diff --git a/.github/workflows/forge-mirror.yml b/.github/workflows/forge-mirror.yml new file mode 100644 index 0000000..00d98c9 --- /dev/null +++ b/.github/workflows/forge-mirror.yml @@ -0,0 +1,48 @@ +name: Mirror canonical main to Gitea + +# GitHub is the canonical line. This workflow mirrors main and tags to the +# Gitea mirror and then verifies that .github/ and scripts/ are byte-identical +# on both forges, so the #342 reconciliation invariant is enforced by CI +# rather than by hand. + +# Requires a repository secret: +# GITEA_MIRROR_TOKEN - a Gitea token with Contents: write on +# terraphim/terraphim-clients. + +on: + push: + branches: [main] + tags: ["v*"] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: forge-mirror-terraphim-clients + cancel-in-progress: false + +jobs: + mirror: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + - name: Mirror main and tags to Gitea + env: + GITEA_MIRROR_TOKEN: ${{ secrets.GITEA_MIRROR_TOKEN }} + run: | + set -euo pipefail + if [ -z "${GITEA_MIRROR_TOKEN:-}" ]; then + echo "::error::GITEA_MIRROR_TOKEN secret is not configured; cannot mirror to Gitea." + exit 1 + fi + git remote add gitea "https://x-access-token:${GITEA_MIRROR_TOKEN}@git.terraphim.cloud/terraphim/terraphim-clients.git" + git push gitea "refs/remotes/origin/main:refs/heads/main" + git push gitea --tags + - name: Verify .github and scripts are identical on both forges + run: | + set -euo pipefail + git fetch --no-tags gitea "main:refs/remotes/gitea/main" + scripts/ci/check-forge-drift.sh origin/main gitea/main .github scripts diff --git a/scripts/ci/check-forge-drift.sh b/scripts/ci/check-forge-drift.sh new file mode 100755 index 0000000..b7bd0ee --- /dev/null +++ b/scripts/ci/check-forge-drift.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Fail when two git refs differ on a set of paths. +# +# Usage: check-forge-drift.sh REF_A REF_B [PATH...] +# +# Defaults to the reconciliation scope (.github scripts). Used by the +# forge-mirror workflow to prove GitHub and the Gitea mirror are identical +# (Gitea terraphim-clients#342). +set -euo pipefail + +if [ "$#" -lt 2 ]; then + echo "usage: check-forge-drift.sh REF_A REF_B [PATH...]" >&2 + exit 2 +fi + +ref_a="$1"; shift +ref_b="$1"; shift +paths=("$@") +if [ "${#paths[@]}" -eq 0 ]; then + paths=(.github scripts) +fi + +tmp_a="$(mktemp)"; tmp_b="$(mktemp)" +trap 'rm -f "$tmp_a" "$tmp_b"' EXIT + +git ls-tree -r "$ref_a" -- "${paths[@]}" | sort > "$tmp_a" +git ls-tree -r "$ref_b" -- "${paths[@]}" | sort > "$tmp_b" + +if diff -u "$tmp_a" "$tmp_b"; then + echo "forge drift: ${ref_a} == ${ref_b} on: ${paths[*]}" + exit 0 +fi + +echo "forge drift: ${ref_a} and ${ref_b} differ on: ${paths[*]}" >&2 +echo "A Gitea-side change must be mirrored to GitHub first (or vice versa)." >&2 +exit 1