From 4f68df74762360552f8a0354325514c55c6e6e8c Mon Sep 17 00:00:00 2001 From: Teodor Calin Date: Thu, 28 May 2026 12:50:33 -0700 Subject: [PATCH 1/2] ci: document pending org-move secret migration in release.yml header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop a TODO at the top of release.yml enumerating which secrets need to be re-created on `pilot-protocol/` before the repo transfer, since GitHub secrets do not survive a repo transfer. Currently the only expected secret is GITHUB_TOKEN (auto-issued). HOMEBREW_TAP_TOKEN was removed in #122 when update-homebrew.yml was dropped, and NPM_TOKEN / PYPI_TOKEN / COSIGN_KEY are placeholders for the auto-publish (PILOT-203) and binary-signing (PILOT-114) work that hasn't landed yet. This is documentation only — no behavior change. The comment block is load-bearing for the org migration; deleting it before the new org has its secrets configured will silently break the next release. --- .github/workflows/release.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index deabf21e..95579c64 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,36 @@ +# ---------------------------------------------------------------------------- +# TODO(org-move): re-create secrets on the destination org before this repo +# is transferred to `pilot-protocol/`. None of the secrets below survive a +# repo transfer — they live on the SOURCE org (TeoSlayer) and need to be +# created fresh on `pilot-protocol` so this workflow keeps working. +# +# Secrets currently expected by `release.yml` and other workflows in this +# repo (`ci.yml`, `nightly.yml`, `architecture.yml`, `codeql.yml`): +# +# GITHUB_TOKEN - auto-issued, no action needed +# +# Secrets that need to be added BACK once the matching workflows are +# restored (we removed `update-homebrew.yml` in PR #122 to drop the +# cross-org PAT; auto-publish workflows for npm/PyPI never landed): +# +# HOMEBREW_TAP_TOKEN - PAT or GitHub App token with `contents:write` +# on `pilot-protocol/homebrew-pilot`. +# Prefer a GitHub App over a PAT — see +# `actions/create-github-app-token@v1`. +# NPM_TOKEN - if PILOT-203 lands sdk-node auto-publish. +# PYPI_TOKEN - if PILOT-203 lands sdk-python auto-publish. +# COSIGN_KEY / COSIGN_PASS - if PILOT-114 lands updater binary signing. +# +# When the migration happens, mirror the secrets via: +# gh secret set HOMEBREW_TAP_TOKEN --repo pilot-protocol/ --body +# (Reading the value from the old org first; GitHub never exposes secrets, +# so the original cleartext source is required.) +# +# Track the migration in the org-move runbook; do not delete this comment +# until every workflow file that previously referenced a secret has either +# (a) been re-wired against the new secret, or (b) been confirmed retired. +# ---------------------------------------------------------------------------- + name: Release on: From c755a07bfcac0014e1191d53000e7f3f88ae0b25 Mon Sep 17 00:00:00 2001 From: Teodor Calin Date: Thu, 28 May 2026 15:24:29 -0700 Subject: [PATCH 2/2] release: publish manifest + shockwave fan-out on tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new jobs run after the existing release job: publish-manifest builds public/.well-known/latest.json from the tag's checksums.txt, then repository_dispatches it to pilot-protocol/website, which commits the JSON to main and triggers the Cloudflare deploy. The single canonical manifest at pilotprotocol.network/.well-known/latest.json is consumed by install.sh, the Homebrew formula bump workflow, and the SDK release helpers — one shockwave per release. shockwave fans out repository_dispatch(event_type=upstream-release) to homebrew-pilot, sdk-node, sdk-python, and sdk-swift so each consumer can run its own bump workflow. Per-target dispatch is soft-fail with a summary so a missing token on one repo does not block the others. Both jobs require a new SHOCKWAVE_DISPATCH_TOKEN secret with repository_dispatch scope on each downstream repo (prefer a GitHub App token over a PAT). When the secret is absent the steps emit a clear ::warning:: and exit 0 so existing release flow is not broken. --- .github/workflows/release.yml | 211 ++++++++++++++++++++++++++++++---- 1 file changed, 189 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 95579c64..d0c991d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,34 +1,38 @@ # ---------------------------------------------------------------------------- -# TODO(org-move): re-create secrets on the destination org before this repo -# is transferred to `pilot-protocol/`. None of the secrets below survive a -# repo transfer — they live on the SOURCE org (TeoSlayer) and need to be -# created fresh on `pilot-protocol` so this workflow keeps working. +# Releases continue from this repo as normal — both before and after the +# planned move to `pilot-protocol/`. This comment is just a reminder for +# migration day; it does NOT gate any release. # -# Secrets currently expected by `release.yml` and other workflows in this -# repo (`ci.yml`, `nightly.yml`, `architecture.yml`, `codeql.yml`): +# Why the reminder: GitHub repo transfers do not carry secrets across orgs. +# The current setup only needs GITHUB_TOKEN (auto-issued), so a transfer +# today would not break anything. But future work will re-introduce +# secrets, and they must be re-created on the destination org BEFORE the +# transfer flips DNS — otherwise the first release after the move silently +# falls back to no-op or fails. # -# GITHUB_TOKEN - auto-issued, no action needed +# Pending (not blocking releases now — will block IF the linked work +# lands before the org migration): # -# Secrets that need to be added BACK once the matching workflows are -# restored (we removed `update-homebrew.yml` in PR #122 to drop the -# cross-org PAT; auto-publish workflows for npm/PyPI never landed): -# -# HOMEBREW_TAP_TOKEN - PAT or GitHub App token with `contents:write` -# on `pilot-protocol/homebrew-pilot`. -# Prefer a GitHub App over a PAT — see -# `actions/create-github-app-token@v1`. +# HOMEBREW_TAP_TOKEN - re-introduced when Homebrew auto-publish +# returns. Prefer a GitHub App over a PAT +# via `actions/create-github-app-token@v1`. # NPM_TOKEN - if PILOT-203 lands sdk-node auto-publish. # PYPI_TOKEN - if PILOT-203 lands sdk-python auto-publish. # COSIGN_KEY / COSIGN_PASS - if PILOT-114 lands updater binary signing. # -# When the migration happens, mirror the secrets via: -# gh secret set HOMEBREW_TAP_TOKEN --repo pilot-protocol/ --body -# (Reading the value from the old org first; GitHub never exposes secrets, -# so the original cleartext source is required.) +# Migration steps (when the day comes): +# 1. List secrets on the source org with `gh secret list --repo `. +# 2. For each non-auto-issued secret, recreate it on the destination +# using the original cleartext value (GitHub never reveals existing +# secret values). +# 3. Transfer the repo via Settings → "Transfer ownership" or +# `gh api repos//transfer -f new_owner=`. +# 4. Re-verify a release tag triggers this workflow successfully. # -# Track the migration in the org-move runbook; do not delete this comment -# until every workflow file that previously referenced a secret has either -# (a) been re-wired against the new secret, or (b) been confirmed retired. +# Track the migration in the org-move runbook. Do NOT delete this comment +# until either: (a) the migration has completed and every reintroduced +# secret is wired against the destination org, or (b) auto-publish and +# binary signing have been formally retired. # ---------------------------------------------------------------------------- name: Release @@ -273,3 +277,166 @@ jobs: generate_release_notes: true draft: false prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }} + + # ---------------------------------------------------------------------------- + # publish-manifest + # + # Regenerates `pilotprotocol.network/.well-known/latest.json` from the tag we + # just shipped, then hands it off to `pilot-protocol/website` via + # `repository_dispatch`. The website side commits the JSON to main, which + # triggers the Cloudflare Pages deploy. + # + # The manifest is the single source of truth that every install surface + # (install.sh, Homebrew formula, SDK release helpers) reads to decide which + # version is current. Failing here does NOT roll the release back — the + # GitHub release is already live — but it does mean install.sh will keep + # serving the old tag until the manifest is republished. The step is best- + # effort and prints a clear hint when the dispatch token is missing. + # ---------------------------------------------------------------------------- + publish-manifest: + name: Publish version manifest + needs: release + runs-on: ubuntu-latest + steps: + - name: Build manifest JSON from this release + id: build + env: + TAG: ${{ github.ref_name }} + IS_PRE: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }} + run: | + # Pull the just-published checksums.txt directly from the GitHub + # release (it landed there in the previous job). + curl -fsSL -o checksums.txt \ + "https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/checksums.txt" + + sha_for() { + grep " $1\$" checksums.txt | awk '{print $1}' + } + + DARWIN_AMD64=$(sha_for "pilot-darwin-amd64.tar.gz") + DARWIN_ARM64=$(sha_for "pilot-darwin-arm64.tar.gz") + LINUX_AMD64=$(sha_for "pilot-linux-amd64.tar.gz") + LINUX_ARM64=$(sha_for "pilot-linux-arm64.tar.gz") + + # Refuse to publish a manifest with missing checksums — install.sh + # would silently skip verification. + for v in "$DARWIN_AMD64" "$DARWIN_ARM64" "$LINUX_AMD64" "$LINUX_ARM64"; do + if [ -z "$v" ]; then + echo "error: checksums.txt missing one or more platform entries" + cat checksums.txt + exit 1 + fi + done + + # When the new tag is a prerelease, leave latest_stable alone and + # bump only latest_prerelease + channels.edge. The website receiver + # merges into the existing manifest. + if [ "$IS_PRE" = "true" ]; then + STABLE="" ; EDGE="$TAG" + else + STABLE="$TAG" ; EDGE="$TAG" + fi + + UPDATED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ) + OWNER_REPO="${GITHUB_REPOSITORY}" + + cat > manifest.json <> "$GITHUB_OUTPUT" + + - name: Dispatch to website + env: + # SHOCKWAVE_DISPATCH_TOKEN must have `repository_dispatch` scope on + # pilot-protocol/website. Prefer a GitHub App token over a PAT — + # see actions/create-github-app-token@v1. + SHOCKWAVE_TOKEN: ${{ secrets.SHOCKWAVE_DISPATCH_TOKEN }} + run: | + if [ -z "$SHOCKWAVE_TOKEN" ]; then + echo "::warning::SHOCKWAVE_DISPATCH_TOKEN secret unset — skipping manifest dispatch." + echo "Action required: set the secret and re-run this workflow to publish ${{ github.ref_name }}." + exit 0 + fi + HTTP_CODE=$(curl -sS -o /tmp/resp -w '%{http_code}' \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${SHOCKWAVE_TOKEN}" \ + "https://api.github.com/repos/pilot-protocol/website/dispatches" \ + -d '${{ steps.build.outputs.payload }}') + if [ "$HTTP_CODE" != "204" ]; then + echo "dispatch failed (HTTP $HTTP_CODE):" + cat /tmp/resp + exit 1 + fi + echo "manifest dispatch accepted (HTTP 204)" + + # ---------------------------------------------------------------------------- + # shockwave + # + # Notify every package that derives from web4 (Homebrew formula + SDKs) that + # a new release exists. Each downstream repo runs its own bump workflow on + # receiving `repository_dispatch` event_type=upstream-release. + # + # Receivers (each must have a workflow listening for `upstream-release`): + # - pilot-protocol/homebrew-pilot → bump Formula/pilot.rb + # - pilot-protocol/sdk-node → bump pkg version + npm publish + # - pilot-protocol/sdk-python → bump pyproject + PyPI publish + # - pilot-protocol/sdk-swift → bump Package.swift binaryTarget + # + # Soft-fail per receiver: a missing token or a 404 on one repo MUST NOT block + # the others. The job summary at the end lists which targets succeeded so a + # missed dispatch is visible without grepping logs. + # ---------------------------------------------------------------------------- + shockwave: + name: Shockwave fan-out + needs: release + runs-on: ubuntu-latest + steps: + - name: Dispatch to downstream consumers + env: + SHOCKWAVE_TOKEN: ${{ secrets.SHOCKWAVE_DISPATCH_TOKEN }} + TAG: ${{ github.ref_name }} + IS_PRE: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }} + run: | + if [ -z "$SHOCKWAVE_TOKEN" ]; then + echo "::warning::SHOCKWAVE_DISPATCH_TOKEN secret unset — skipping fan-out." + exit 0 + fi + summary="" + for repo in homebrew-pilot sdk-node sdk-python sdk-swift; do + payload=$(jq -nc --arg tag "$TAG" --argjson pre "$IS_PRE" \ + '{event_type:"upstream-release", client_payload:{tag:$tag, is_prerelease:$pre}}') + HTTP_CODE=$(curl -sS -o /tmp/resp -w '%{http_code}' \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${SHOCKWAVE_TOKEN}" \ + "https://api.github.com/repos/pilot-protocol/${repo}/dispatches" \ + -d "$payload") + if [ "$HTTP_CODE" = "204" ]; then + summary="${summary} ✓ ${repo}\n" + else + summary="${summary} ✗ ${repo} (HTTP ${HTTP_CODE})\n" + echo "::warning::shockwave dispatch failed for ${repo}: HTTP ${HTTP_CODE}" + cat /tmp/resp + fi + done + printf "Shockwave fan-out summary:\n${summary}" >> "$GITHUB_STEP_SUMMARY"