From f1934e1a7df67e1cd23c3b853721e49704f97864 Mon Sep 17 00:00:00 2001 From: Daniel Bachler Date: Mon, 14 Sep 2026 15:02:47 +0200 Subject: [PATCH 1/2] Add a workflow that keeps owid/public-skills in sync owid/public-skills is a private mirror of this repo, republished inside the org so the skills can be installed as an org-level plugin in Claude Desktop/Web. Until now it had to be synced by hand. This workflow checks the mirror out on every push to main and runs the mirror's own sync-upstream.sh, which merges this repo, regenerates the mirror README (upstream README + a "this is a mirror" notice) and pushes. The sync logic stays in the mirror; this only triggers it. A weekly cron repairs drift if a run ever fails, and it can be re-run by hand via workflow_dispatch. Needs a MIRROR_SYNC_TOKEN secret: a fine-grained PAT scoped to owid/public-skills with Contents: read and write. Without it the first step fails with an explanation rather than a confusing checkout error. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/mirror-to-public-skills.yml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .github/workflows/mirror-to-public-skills.yml diff --git a/.github/workflows/mirror-to-public-skills.yml b/.github/workflows/mirror-to-public-skills.yml new file mode 100644 index 0000000..7a45865 --- /dev/null +++ b/.github/workflows/mirror-to-public-skills.yml @@ -0,0 +1,65 @@ +name: Mirror to owid/public-skills + +# owid/public-skills is a private mirror of this repo. It exists because an +# org-level plugin in Claude Desktop / Claude Web has to be installed from a +# repository inside the owid organization, so this repo's content is +# republished there. +# +# This job keeps the mirror current: it checks the mirror out and runs the +# mirror's own sync-upstream.sh, which merges this repo's main, regenerates the +# mirror's README (upstream README + a "this is a mirror" notice) and pushes. +# All the sync logic lives in the mirror; this workflow only triggers it. +# +# Setup (one-time): a fine-grained PAT scoped to owid/public-skills with +# "Contents: read and write", stored as the MIRROR_SYNC_TOKEN secret on this +# repo. The default GITHUB_TOKEN cannot reach another repository. + +on: + push: + branches: [main] + # Safety net: if a push-triggered run ever fails, this repairs the drift + # without anyone noticing it. The sync is idempotent, so a no-op run is free. + schedule: + - cron: "0 7 * * 1" + workflow_dispatch: + +# Two syncs pushing to the mirror at once would race; queue them instead. +concurrency: + group: mirror-to-public-skills + cancel-in-progress: false + +permissions: {} + +jobs: + mirror: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check the mirror token is configured + env: + TOKEN: ${{ secrets.MIRROR_SYNC_TOKEN }} + run: | + if [ -z "$TOKEN" ]; then + echo "MIRROR_SYNC_TOKEN is not set on this repository." >&2 + echo "Create a fine-grained PAT scoped to owid/public-skills with" >&2 + echo "Contents: read and write, and add it as a repository secret." >&2 + exit 1 + fi + + # Full history: the sync is a real merge of this repo into the mirror. + - name: Check out the mirror + uses: actions/checkout@v4 + with: + repository: owid/public-skills + token: ${{ secrets.MIRROR_SYNC_TOKEN }} + fetch-depth: 0 + + - name: Configure git identity + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # Fails loudly on any conflict outside the mirror's README, which is the + # only file the mirror is allowed to diverge in. + - name: Sync the mirror with this repo + run: ./sync-upstream.sh From 1d6e0845ecac70506980af3267f920e62352ecaf Mon Sep 17 00:00:00 2001 From: Daniel Bachler Date: Tue, 15 Sep 2026 17:00:38 +0200 Subject: [PATCH 2/2] Diagnose token problems up front and move to actions/checkout@v5 The first live run failed with a bare "Not Found" from actions/checkout, which reads as though the mirror repo did not exist when in fact the token could not see it. A preflight call names the likely cause instead. checkout@v5 also drops the Node 20 deprecation warning. --- .github/workflows/mirror-to-public-skills.yml | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/.github/workflows/mirror-to-public-skills.yml b/.github/workflows/mirror-to-public-skills.yml index 7a45865..f43edbf 100644 --- a/.github/workflows/mirror-to-public-skills.yml +++ b/.github/workflows/mirror-to-public-skills.yml @@ -35,20 +35,41 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - name: Check the mirror token is configured + # A missing, expired or unapproved token otherwise surfaces as a bare + # "Not Found" from actions/checkout, which reads like the repo is gone. + - name: Check the token can reach the mirror env: TOKEN: ${{ secrets.MIRROR_SYNC_TOKEN }} run: | if [ -z "$TOKEN" ]; then - echo "MIRROR_SYNC_TOKEN is not set on this repository." >&2 - echo "Create a fine-grained PAT scoped to owid/public-skills with" >&2 - echo "Contents: read and write, and add it as a repository secret." >&2 + echo "::error::MIRROR_SYNC_TOKEN is not set on this repository." \ + "Create a fine-grained PAT owned by the owid org, scoped to" \ + "owid/public-skills with Contents: read and write." exit 1 fi + code=$(curl -sS -o /dev/null -w '%{http_code}' \ + -H "Authorization: Bearer $TOKEN" \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/repos/owid/public-skills) + + case "$code" in + 200) echo "MIRROR_SYNC_TOKEN can reach owid/public-skills." ;; + 401) echo "::error::MIRROR_SYNC_TOKEN is invalid or expired. Mint a new one and update the secret." + exit 1 ;; + 403|404) + echo "::error::MIRROR_SYNC_TOKEN cannot see owid/public-skills." \ + "Usual causes: the token expired, its resource owner is a personal" \ + "account rather than the owid org, org approval is still pending, or" \ + "public-skills is not among its selected repositories." + exit 1 ;; + *) echo "::error::Unexpected HTTP $code from the GitHub API." + exit 1 ;; + esac + # Full history: the sync is a real merge of this repo into the mirror. - name: Check out the mirror - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: owid/public-skills token: ${{ secrets.MIRROR_SYNC_TOKEN }}