Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions .github/workflows/mirror-sync.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: mirror-sync

# =============================================================================
# Test-mirror syncing for upstream-bound PR branches
# =============================================================================
#
# Branches named `upstream-pr/<topic>` are reserved for clean contributions
# to kubescape/node-agent — we cannot touch their `.github/` without
# polluting the upstream diff. This workflow reacts to each push by creating
# (or force-updating) `test-mirror/<same-topic>`: same code, but with the
# fork's working CI files overlaid from the `fork-ci/` directory on `main`.
#
# At the time of introduction `fork-ci/` here is empty (only a README) — the
# existing `.github/workflows/build.yaml` + `component-tests.yaml` already
# behave correctly on any ref. The machinery exists so that the moment a
# fork-only workflow tweak is needed, it has a home that does not leak into
# the upstream diff.
#
# This keeps the upstream-pr branch pristine for the reviewer while letting
# our internal CI exercise it on every push.
# =============================================================================

on:
push:
branches:
- 'upstream-pr/**'
workflow_dispatch:
inputs:
source_branch:
description: 'Source branch to mirror (defaults to the one this runs on)'
required: false
type: string

concurrency:
group: mirror-sync-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: write

jobs:
mirror:
runs-on: ubuntu-latest
steps:
- name: Resolve source branch
id: src
env:
DISPATCH_SOURCE: ${{ github.event.inputs.source_branch }}
run: |
set -euo pipefail
if [ -n "${DISPATCH_SOURCE:-}" ]; then
echo "branch=${DISPATCH_SOURCE}" >> "$GITHUB_OUTPUT"
else
# For push events, GITHUB_REF_NAME is the branch that received the push.
echo "branch=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
fi

- name: Fail fast if the branch is not an upstream-pr/* branch
env:
BRANCH: ${{ steps.src.outputs.branch }}
run: |
set -euo pipefail
case "$BRANCH" in
upstream-pr/*) echo "ok — mirroring $BRANCH" ;;
*) echo "refusing to mirror non-upstream-pr branch: $BRANCH"; exit 1 ;;
esac

- name: Checkout full history
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: true
token: ${{ secrets.CROSS_REPO_PAT }}

- name: Configure bot identity
run: |
git config user.name "k8sstorm-mirror-bot"
git config user.email "noreply@k8sstormcenter.io"

- name: Build + push test-mirror branch
env:
BRANCH: ${{ steps.src.outputs.branch }}
run: |
set -euo pipefail
MIRROR="test-mirror/${BRANCH}"

# Start from the pushed branch tip.
git checkout "$BRANCH"

# Overlay fork-ci/ onto the working tree. fork-ci/ lives on main and
# holds any workflow files that must appear on test-mirror branches
# but not on the upstream-pr branch itself. Today it is empty beyond
# a README — the overlay is then a no-op and the mirror branch is
# byte-identical to the source.
git checkout main -- fork-ci || {
echo "fork-ci/ missing on main; nothing to overlay"
exit 1
}

if [ ! -d fork-ci ]; then
echo "fork-ci/ directory missing after checkout; abort"
exit 1
fi

# Copy fork-ci/ payload into the tree, skipping the README which is
# documentation for the directory itself and not intended to land
# on the mirror branch.
if [ -d fork-ci/.github ] || [ -n "$(ls -A fork-ci 2>/dev/null | grep -v '^README.md$' || true)" ]; then
# shellcheck disable=SC2010
find fork-ci -mindepth 1 -maxdepth 1 ! -name 'README.md' -exec cp -r {} . \;
fi
rm -rf fork-ci
git add -A

# Only commit if something actually differs. With an empty fork-ci/
# (README only) this is a no-op and we push the source tip unchanged.
if git diff --cached --quiet; then
echo "no changes after overlay — pushing source branch unchanged as mirror"
else
git commit -m "test-mirror: overlay fork-ci workflows (NOT FOR UPSTREAM)"
fi

# Force-push to the test-mirror branch. It's a mirror: force is
# deliberate and safe because the branch is only ever written by
# this workflow.
git push --force origin "HEAD:refs/heads/${MIRROR}"
echo "pushed ${MIRROR} from $(git rev-parse HEAD)"
26 changes: 26 additions & 0 deletions fork-ci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# fork-ci/ — workflow overlays for test-mirror branches

This directory is a plug-in point for **fork-specific CI files that must
appear on `test-mirror/**` branches but not on `upstream-pr/**` branches**.

At the time of introduction it is intentionally empty (beyond this README):
node-agent's existing `.github/workflows/` — `build.yaml` (workflow_dispatch
only) and `component-tests.yaml` (internally dispatched by `build.yaml`) —
already work correctly when dispatched on any ref, so the mirror branch can
use the upstream-pr branch's files as-is.

## When to add files here

If you ever need a workflow file that must behave differently on
test-mirror branches than on upstream-pr branches (e.g. a push trigger with
`test-mirror/**` in its branch list, or a different image registry), drop
it into `fork-ci/.github/workflows/`. The `mirror-sync.yaml` workflow on
`main` will overlay everything under `fork-ci/` onto the mirror branch at
sync time. Do **not** add such files to the upstream-pr branch directly —
they would contaminate the upstream PR diff.

## Why the shape exists even when empty

Matches the `fork-ci/` layout on `k8sstormcenter/storage` so there's a
single mental model across both forks. Anyone looking at the mirror-sync
workflow on either fork sees the same template.
Loading