# Auto-sync this Gitea repo to its public GitHub mirror on every push to main. # # Design (deliberate trade-offs): # - Push-driven from Gitea (this repo IS the source of truth); GitHub is a mirror. # - Each sync = one snapshot commit on GitHub referencing the source Gitea SHA. # GitHub gets a real, growing history (one commit per Gitea push that changed # the mirrored tree); NO force-push, NO history rewrites. # - The mirror tree is FILTERED: `blog/`, `handoff.md`, `.claude/`, `.gitea/`, # and the usual generated junk are never copied. They are not in the GitHub # history either (per the original "do not push these" rule). # - If a Gitea push touches only excluded paths, the rsync produces no diff and # the workflow exits clean (no empty commit on GitHub). # # Prereqs (one-time): # - Repo secret GH_MIRROR_TOKEN holds a GitHub PAT with `repo` scope (push to # recklessop/ai-workflow-course). Name avoids the GITHUB_ reserved prefix. # - The GitHub mirror exists at github.com/recklessop/ai-workflow-course. name: Sync to GitHub mirror on: push: branches: [main] workflow_dispatch: {} concurrency: group: sync-github-mirror cancel-in-progress: false # serialize; never cancel a sync mid-push jobs: sync: runs-on: docker steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Sync filtered tree to GitHub shell: bash env: GH_MIRROR_TOKEN: ${{ secrets.GH_MIRROR_TOKEN }} run: | set -euo pipefail if [ -z "${GH_MIRROR_TOKEN:-}" ]; then echo "::error::GH_MIRROR_TOKEN secret not set; see this workflow's header." exit 1 fi command -v rsync >/dev/null || { apt-get update && apt-get install -y --no-install-recommends rsync; } GH_REPO="recklessop/ai-workflow-course" SRC_SHA="$(git rev-parse --short HEAD)" # Clone the GitHub mirror into a sibling working dir GH_DIR="${RUNNER_TEMP:-/tmp}/awc-gh-mirror"; rm -rf "$GH_DIR"; git clone --depth=1 "https://x-access-token:${GH_MIRROR_TOKEN}@github.com/${GH_REPO}.git" "$GH_DIR" # Mirror this checkout's tree into gh-mirror/ with the exclusions. # --delete drops files removed on the source; --exclude='.git' protects # both repos' .git dirs from rsync touching them. # belt-and-suspenders exclude of the clone dir name in case anyone re-uses ./gh-mirror. rsync -a --delete \ --exclude='.git' \ --exclude='.gitea/' \ --exclude='.claude/' \ --exclude='blog/' \ --exclude='handoff.md' \ --exclude='gh-mirror/' \ --exclude='__pycache__/' \ --exclude='*.pyc' \ --exclude='tasks.json' \ --exclude='.DS_Store' \ ./ "$GH_DIR"/ cd "$GH_DIR" git add -A if git diff --cached --quiet; then echo "no relevant changes for the mirror (source push only touched excluded paths); skipping" exit 0 fi git config user.name "Justin Paul" git config user.email "justin@jpaul.me" git commit -m "sync from gitea @ ${SRC_SHA}" # Plain push: each sync is a fast-forward append (no rewrites). If a # stranger pushed to GitHub main between clone and push, --force-with-lease # would tell us; here we let it fail loudly so we notice the divergence. git push origin HEAD:main