|
1 | 1 | # ---------------------------------------------------------------------------- |
2 | | -# TODO(org-move): re-create secrets on the destination org before this repo |
3 | | -# is transferred to `pilot-protocol/`. None of the secrets below survive a |
4 | | -# repo transfer — they live on the SOURCE org (TeoSlayer) and need to be |
5 | | -# created fresh on `pilot-protocol` so this workflow keeps working. |
| 2 | +# Releases continue from this repo as normal — both before and after the |
| 3 | +# planned move to `pilot-protocol/`. This comment is just a reminder for |
| 4 | +# migration day; it does NOT gate any release. |
6 | 5 | # |
7 | | -# Secrets currently expected by `release.yml` and other workflows in this |
8 | | -# repo (`ci.yml`, `nightly.yml`, `architecture.yml`, `codeql.yml`): |
| 6 | +# Why the reminder: GitHub repo transfers do not carry secrets across orgs. |
| 7 | +# The current setup only needs GITHUB_TOKEN (auto-issued), so a transfer |
| 8 | +# today would not break anything. But future work will re-introduce |
| 9 | +# secrets, and they must be re-created on the destination org BEFORE the |
| 10 | +# transfer flips DNS — otherwise the first release after the move silently |
| 11 | +# falls back to no-op or fails. |
9 | 12 | # |
10 | | -# GITHUB_TOKEN - auto-issued, no action needed |
| 13 | +# Pending (not blocking releases now — will block IF the linked work |
| 14 | +# lands before the org migration): |
11 | 15 | # |
12 | | -# Secrets that need to be added BACK once the matching workflows are |
13 | | -# restored (we removed `update-homebrew.yml` in PR #122 to drop the |
14 | | -# cross-org PAT; auto-publish workflows for npm/PyPI never landed): |
15 | | -# |
16 | | -# HOMEBREW_TAP_TOKEN - PAT or GitHub App token with `contents:write` |
17 | | -# on `pilot-protocol/homebrew-pilot`. |
18 | | -# Prefer a GitHub App over a PAT — see |
19 | | -# `actions/create-github-app-token@v1`. |
| 16 | +# HOMEBREW_TAP_TOKEN - re-introduced when Homebrew auto-publish |
| 17 | +# returns. Prefer a GitHub App over a PAT |
| 18 | +# via `actions/create-github-app-token@v1`. |
20 | 19 | # NPM_TOKEN - if PILOT-203 lands sdk-node auto-publish. |
21 | 20 | # PYPI_TOKEN - if PILOT-203 lands sdk-python auto-publish. |
22 | 21 | # COSIGN_KEY / COSIGN_PASS - if PILOT-114 lands updater binary signing. |
23 | 22 | # |
24 | | -# When the migration happens, mirror the secrets via: |
25 | | -# gh secret set HOMEBREW_TAP_TOKEN --repo pilot-protocol/<new-repo> --body <value> |
26 | | -# (Reading the value from the old org first; GitHub never exposes secrets, |
27 | | -# so the original cleartext source is required.) |
| 23 | +# Migration steps (when the day comes): |
| 24 | +# 1. List secrets on the source org with `gh secret list --repo <src>`. |
| 25 | +# 2. For each non-auto-issued secret, recreate it on the destination |
| 26 | +# using the original cleartext value (GitHub never reveals existing |
| 27 | +# secret values). |
| 28 | +# 3. Transfer the repo via Settings → "Transfer ownership" or |
| 29 | +# `gh api repos/<src>/transfer -f new_owner=<dst>`. |
| 30 | +# 4. Re-verify a release tag triggers this workflow successfully. |
28 | 31 | # |
29 | | -# Track the migration in the org-move runbook; do not delete this comment |
30 | | -# until every workflow file that previously referenced a secret has either |
31 | | -# (a) been re-wired against the new secret, or (b) been confirmed retired. |
| 32 | +# Track the migration in the org-move runbook. Do NOT delete this comment |
| 33 | +# until either: (a) the migration has completed and every reintroduced |
| 34 | +# secret is wired against the destination org, or (b) auto-publish and |
| 35 | +# binary signing have been formally retired. |
32 | 36 | # ---------------------------------------------------------------------------- |
33 | 37 |
|
34 | 38 | name: Release |
@@ -273,3 +277,166 @@ jobs: |
273 | 277 | generate_release_notes: true |
274 | 278 | draft: false |
275 | 279 | prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }} |
| 280 | + |
| 281 | + # ---------------------------------------------------------------------------- |
| 282 | + # publish-manifest |
| 283 | + # |
| 284 | + # Regenerates `pilotprotocol.network/.well-known/latest.json` from the tag we |
| 285 | + # just shipped, then hands it off to `pilot-protocol/website` via |
| 286 | + # `repository_dispatch`. The website side commits the JSON to main, which |
| 287 | + # triggers the Cloudflare Pages deploy. |
| 288 | + # |
| 289 | + # The manifest is the single source of truth that every install surface |
| 290 | + # (install.sh, Homebrew formula, SDK release helpers) reads to decide which |
| 291 | + # version is current. Failing here does NOT roll the release back — the |
| 292 | + # GitHub release is already live — but it does mean install.sh will keep |
| 293 | + # serving the old tag until the manifest is republished. The step is best- |
| 294 | + # effort and prints a clear hint when the dispatch token is missing. |
| 295 | + # ---------------------------------------------------------------------------- |
| 296 | + publish-manifest: |
| 297 | + name: Publish version manifest |
| 298 | + needs: release |
| 299 | + runs-on: ubuntu-latest |
| 300 | + steps: |
| 301 | + - name: Build manifest JSON from this release |
| 302 | + id: build |
| 303 | + env: |
| 304 | + TAG: ${{ github.ref_name }} |
| 305 | + IS_PRE: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }} |
| 306 | + run: | |
| 307 | + # Pull the just-published checksums.txt directly from the GitHub |
| 308 | + # release (it landed there in the previous job). |
| 309 | + curl -fsSL -o checksums.txt \ |
| 310 | + "https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/checksums.txt" |
| 311 | +
|
| 312 | + sha_for() { |
| 313 | + grep " $1\$" checksums.txt | awk '{print $1}' |
| 314 | + } |
| 315 | +
|
| 316 | + DARWIN_AMD64=$(sha_for "pilot-darwin-amd64.tar.gz") |
| 317 | + DARWIN_ARM64=$(sha_for "pilot-darwin-arm64.tar.gz") |
| 318 | + LINUX_AMD64=$(sha_for "pilot-linux-amd64.tar.gz") |
| 319 | + LINUX_ARM64=$(sha_for "pilot-linux-arm64.tar.gz") |
| 320 | +
|
| 321 | + # Refuse to publish a manifest with missing checksums — install.sh |
| 322 | + # would silently skip verification. |
| 323 | + for v in "$DARWIN_AMD64" "$DARWIN_ARM64" "$LINUX_AMD64" "$LINUX_ARM64"; do |
| 324 | + if [ -z "$v" ]; then |
| 325 | + echo "error: checksums.txt missing one or more platform entries" |
| 326 | + cat checksums.txt |
| 327 | + exit 1 |
| 328 | + fi |
| 329 | + done |
| 330 | +
|
| 331 | + # When the new tag is a prerelease, leave latest_stable alone and |
| 332 | + # bump only latest_prerelease + channels.edge. The website receiver |
| 333 | + # merges into the existing manifest. |
| 334 | + if [ "$IS_PRE" = "true" ]; then |
| 335 | + STABLE="" ; EDGE="$TAG" |
| 336 | + else |
| 337 | + STABLE="$TAG" ; EDGE="$TAG" |
| 338 | + fi |
| 339 | +
|
| 340 | + UPDATED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ) |
| 341 | + OWNER_REPO="${GITHUB_REPOSITORY}" |
| 342 | +
|
| 343 | + cat > manifest.json <<EOF |
| 344 | + { |
| 345 | + "\$schema": "https://pilotprotocol.network/.well-known/latest.schema.json", |
| 346 | + "updated_at": "${UPDATED_AT}", |
| 347 | + "tag": "${TAG}", |
| 348 | + "is_prerelease": ${IS_PRE}, |
| 349 | + "proposed_stable": "${STABLE}", |
| 350 | + "proposed_edge": "${EDGE}", |
| 351 | + "release_notes_url": "https://github.com/${OWNER_REPO}/releases/tag/${TAG}", |
| 352 | + "homebrew_formula_url": "https://github.com/TeoSlayer/homebrew-pilot/raw/main/Formula/pilotprotocol.rb", |
| 353 | + "platforms": { |
| 354 | + "darwin-amd64": { "url": "https://github.com/${OWNER_REPO}/releases/download/${TAG}/pilot-darwin-amd64.tar.gz", "sha256": "${DARWIN_AMD64}" }, |
| 355 | + "darwin-arm64": { "url": "https://github.com/${OWNER_REPO}/releases/download/${TAG}/pilot-darwin-arm64.tar.gz", "sha256": "${DARWIN_ARM64}" }, |
| 356 | + "linux-amd64": { "url": "https://github.com/${OWNER_REPO}/releases/download/${TAG}/pilot-linux-amd64.tar.gz", "sha256": "${LINUX_AMD64}" }, |
| 357 | + "linux-arm64": { "url": "https://github.com/${OWNER_REPO}/releases/download/${TAG}/pilot-linux-arm64.tar.gz", "sha256": "${LINUX_ARM64}" } |
| 358 | + } |
| 359 | + } |
| 360 | + EOF |
| 361 | + cat manifest.json |
| 362 | +
|
| 363 | + # Escape for JSON embedding in the dispatch payload. |
| 364 | + PAYLOAD=$(jq -c '{event_type:"manifest-update", client_payload:{manifest:.}}' manifest.json) |
| 365 | + echo "payload=$PAYLOAD" >> "$GITHUB_OUTPUT" |
| 366 | +
|
| 367 | + - name: Dispatch to website |
| 368 | + env: |
| 369 | + # SHOCKWAVE_DISPATCH_TOKEN must have `repository_dispatch` scope on |
| 370 | + # pilot-protocol/website. Prefer a GitHub App token over a PAT — |
| 371 | + # see actions/create-github-app-token@v1. |
| 372 | + SHOCKWAVE_TOKEN: ${{ secrets.SHOCKWAVE_DISPATCH_TOKEN }} |
| 373 | + run: | |
| 374 | + if [ -z "$SHOCKWAVE_TOKEN" ]; then |
| 375 | + echo "::warning::SHOCKWAVE_DISPATCH_TOKEN secret unset — skipping manifest dispatch." |
| 376 | + echo "Action required: set the secret and re-run this workflow to publish ${{ github.ref_name }}." |
| 377 | + exit 0 |
| 378 | + fi |
| 379 | + HTTP_CODE=$(curl -sS -o /tmp/resp -w '%{http_code}' \ |
| 380 | + -X POST \ |
| 381 | + -H "Accept: application/vnd.github+json" \ |
| 382 | + -H "Authorization: Bearer ${SHOCKWAVE_TOKEN}" \ |
| 383 | + "https://api.github.com/repos/pilot-protocol/website/dispatches" \ |
| 384 | + -d '${{ steps.build.outputs.payload }}') |
| 385 | + if [ "$HTTP_CODE" != "204" ]; then |
| 386 | + echo "dispatch failed (HTTP $HTTP_CODE):" |
| 387 | + cat /tmp/resp |
| 388 | + exit 1 |
| 389 | + fi |
| 390 | + echo "manifest dispatch accepted (HTTP 204)" |
| 391 | +
|
| 392 | + # ---------------------------------------------------------------------------- |
| 393 | + # shockwave |
| 394 | + # |
| 395 | + # Notify every package that derives from web4 (Homebrew formula + SDKs) that |
| 396 | + # a new release exists. Each downstream repo runs its own bump workflow on |
| 397 | + # receiving `repository_dispatch` event_type=upstream-release. |
| 398 | + # |
| 399 | + # Receivers (each must have a workflow listening for `upstream-release`): |
| 400 | + # - pilot-protocol/homebrew-pilot → bump Formula/pilot.rb |
| 401 | + # - pilot-protocol/sdk-node → bump pkg version + npm publish |
| 402 | + # - pilot-protocol/sdk-python → bump pyproject + PyPI publish |
| 403 | + # - pilot-protocol/sdk-swift → bump Package.swift binaryTarget |
| 404 | + # |
| 405 | + # Soft-fail per receiver: a missing token or a 404 on one repo MUST NOT block |
| 406 | + # the others. The job summary at the end lists which targets succeeded so a |
| 407 | + # missed dispatch is visible without grepping logs. |
| 408 | + # ---------------------------------------------------------------------------- |
| 409 | + shockwave: |
| 410 | + name: Shockwave fan-out |
| 411 | + needs: release |
| 412 | + runs-on: ubuntu-latest |
| 413 | + steps: |
| 414 | + - name: Dispatch to downstream consumers |
| 415 | + env: |
| 416 | + SHOCKWAVE_TOKEN: ${{ secrets.SHOCKWAVE_DISPATCH_TOKEN }} |
| 417 | + TAG: ${{ github.ref_name }} |
| 418 | + IS_PRE: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') }} |
| 419 | + run: | |
| 420 | + if [ -z "$SHOCKWAVE_TOKEN" ]; then |
| 421 | + echo "::warning::SHOCKWAVE_DISPATCH_TOKEN secret unset — skipping fan-out." |
| 422 | + exit 0 |
| 423 | + fi |
| 424 | + summary="" |
| 425 | + for repo in homebrew-pilot sdk-node sdk-python sdk-swift; do |
| 426 | + payload=$(jq -nc --arg tag "$TAG" --argjson pre "$IS_PRE" \ |
| 427 | + '{event_type:"upstream-release", client_payload:{tag:$tag, is_prerelease:$pre}}') |
| 428 | + HTTP_CODE=$(curl -sS -o /tmp/resp -w '%{http_code}' \ |
| 429 | + -X POST \ |
| 430 | + -H "Accept: application/vnd.github+json" \ |
| 431 | + -H "Authorization: Bearer ${SHOCKWAVE_TOKEN}" \ |
| 432 | + "https://api.github.com/repos/pilot-protocol/${repo}/dispatches" \ |
| 433 | + -d "$payload") |
| 434 | + if [ "$HTTP_CODE" = "204" ]; then |
| 435 | + summary="${summary} ✓ ${repo}\n" |
| 436 | + else |
| 437 | + summary="${summary} ✗ ${repo} (HTTP ${HTTP_CODE})\n" |
| 438 | + echo "::warning::shockwave dispatch failed for ${repo}: HTTP ${HTTP_CODE}" |
| 439 | + cat /tmp/resp |
| 440 | + fi |
| 441 | + done |
| 442 | + printf "Shockwave fan-out summary:\n${summary}" >> "$GITHUB_STEP_SUMMARY" |
0 commit comments