feat: release pplx 2026.07.23.1784784046+c8b317a #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Cuts a public release whenever the Buildkite release-sync job lands a new | |
| # dist/ payload on main, then hands off to release.yml (stage -> smoke -> publish). | |
| # | |
| # Design notes: | |
| # - The human gate is upstream (manual Buildkite trigger); this workflow is fully | |
| # automatic and uses only GITHUB_TOKEN (no PAT, no PRs, no admin). | |
| # - Releases created here are DRAFTS: no events fire, no git tag exists, and | |
| # releases/latest (used by install.sh) is unaffected until release.yml's | |
| # publish job uploads all assets and flips the draft to published. | |
| # - GITHUB_TOKEN-created releases/tags never trigger other workflows, so the | |
| # handoff to release.yml is an explicit `gh workflow run` (workflow_dispatch | |
| # is the documented exception that always starts runs). | |
| # - The `paths:` filter is an economy filter only; correctness comes from the | |
| # in-job idempotency guard (dist-version marker in the release body). Never | |
| # strip that HTML comment when editing release notes, or the version will be | |
| # re-released under a new tag. | |
| # - Stale drafts (superseded calvers) should be DELETED, never published by | |
| # hand: publishing an asset-less draft creates a real tag and can mis-point | |
| # releases/latest. | |
| name: Cut Release | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - dist/metadata.json | |
| # Backfill / recovery: re-run detection for whatever dist/ is at main HEAD. | |
| workflow_dispatch: {} | |
| permissions: {} | |
| # Serialize runs so tag computation + draft creation never race. | |
| # cancel-in-progress must stay false: a running cut is never killed; one newest | |
| # pending run waits and older pending runs are superseded (intermediate | |
| # metadata.json pushes collapse into the newest run, which is intended). | |
| concurrency: | |
| group: cut-release | |
| cancel-in-progress: false | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| cut: | |
| name: Detect dist version and cut draft release | |
| # push is already main-scoped; this extends the same provenance gate to | |
| # manual dispatch (which can otherwise target any ref). | |
| if: github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # list/create/edit releases | |
| actions: write # dispatch release.yml | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd | |
| - name: Verify dist payload | |
| run: | | |
| set -euo pipefail | |
| for f in metadata.json pplx-aarch64-apple-darwin.bin pplx-x86_64-linux-gnu.bin pplx-aarch64-linux-gnu.bin; do | |
| if [[ ! -f "dist/${f}" ]]; then | |
| echo "::error::missing dist/${f} at ${GITHUB_SHA}" | |
| exit 1 | |
| fi | |
| done | |
| - name: Read dist metadata | |
| id: meta | |
| run: | | |
| set -euo pipefail | |
| version="$(jq -er '.version' dist/metadata.json)" | |
| agi_commit="$(jq -er '.agi_commit' dist/metadata.json)" | |
| # Whole-string anchors (no per-line grep) so multi-line values cannot | |
| # smuggle extra $GITHUB_OUTPUT lines; charset matches install.sh's | |
| # VERSION allowlist. | |
| if [[ ! "${version}" =~ ^[0-9A-Za-z][0-9A-Za-z.+-]*$ ]]; then | |
| echo "::error::unexpected version format in dist/metadata.json: ${version}" | |
| exit 1 | |
| fi | |
| if [[ ! "${agi_commit}" =~ ^[0-9a-f]{7,40}$ ]]; then | |
| echo "::error::unexpected agi_commit format in dist/metadata.json: ${agi_commit}" | |
| exit 1 | |
| fi | |
| { | |
| echo "version=${version}" | |
| echo "agi_commit=${agi_commit}" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Resolve release for this version | |
| id: resolve | |
| env: | |
| VERSION: ${{ steps.meta.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| marker="<!-- pplx-dist-version: ${VERSION} -->" | |
| # Draft releases have no git tag, so the REST list (which includes | |
| # drafts for tokens with push access) is the source of truth. | |
| gh api --paginate "repos/${GH_REPO}/releases" > /tmp/releases.json | |
| match="$(jq -cs --arg marker "${marker}" \ | |
| 'first(add[]? | select((.body // "") | contains($marker)) | {id, tag_name, draft}) // empty' \ | |
| /tmp/releases.json)" | |
| if [[ -n "${match}" ]]; then | |
| tag="$(jq -r '.tag_name' <<<"${match}")" | |
| draft="$(jq -r '.draft' <<<"${match}")" | |
| release_id="$(jq -r '.id' <<<"${match}")" | |
| if [[ "${draft}" == "false" ]]; then | |
| echo "Version ${VERSION} already published as ${tag}; nothing to do." | |
| echo "action=skip" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "Version ${VERSION} already has draft ${tag} (a prior run stalled); re-dispatching the pipeline." | |
| { | |
| echo "action=reuse" | |
| echo "tag=${tag}" | |
| echo "release_id=${release_id}" | |
| } >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Next public tag: patch bump of the highest vA.B.C across ALL | |
| # releases (drafts included) and git tags. Minor/major bumps are | |
| # human decisions (cut such a tag manually; the counter continues | |
| # from it). | |
| latest="$( | |
| { | |
| jq -r '.[].tag_name' /tmp/releases.json | |
| git ls-remote --tags origin 'refs/tags/v*' \ | |
| | awk '{print $2}' | sed -e 's#^refs/tags/##' -e 's#\^{}$##' | |
| } | { grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' || true; } | sort -V | tail -n 1 | |
| )" | |
| latest="${latest:-v0.0.0}" | |
| IFS=. read -r major minor patch <<<"${latest#v}" | |
| tag="v${major}.${minor}.$((patch + 1))" | |
| echo "Version ${VERSION} is unreleased; next tag is ${tag} (after ${latest})." | |
| { | |
| echo "action=create" | |
| echo "tag=${tag}" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Create draft release | |
| if: steps.resolve.outputs.action == 'create' | |
| env: | |
| VERSION: ${{ steps.meta.outputs.version }} | |
| AGI_COMMIT: ${{ steps.meta.outputs.agi_commit }} | |
| TAG: ${{ steps.resolve.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| body="$(printf '%s\n' \ | |
| "pplx CLI \`${VERSION}\` (agi commit \`${AGI_COMMIT}\`)." \ | |
| "" \ | |
| "Install via the README one-liner; binaries, manifest.json and SHA256SUMS are attached by the release pipeline." \ | |
| "" \ | |
| "<!-- pplx-dist-version: ${VERSION} -->")" | |
| gh api -X POST "repos/${GH_REPO}/releases" \ | |
| -f tag_name="${TAG}" \ | |
| -f target_commitish="${GITHUB_SHA}" \ | |
| -f name="${TAG}" \ | |
| -f body="${body}" \ | |
| -F draft=true \ | |
| -F generate_release_notes=true \ | |
| --jq '.html_url' | |
| - name: Retarget existing draft | |
| if: steps.resolve.outputs.action == 'reuse' | |
| env: | |
| RELEASE_ID: ${{ steps.resolve.outputs.release_id }} | |
| run: | | |
| set -euo pipefail | |
| gh api -X PATCH "repos/${GH_REPO}/releases/${RELEASE_ID}" \ | |
| -f target_commitish="${GITHUB_SHA}" \ | |
| --jq '.html_url' | |
| - name: Dispatch release pipeline | |
| if: steps.resolve.outputs.action != 'skip' | |
| env: | |
| TAG: ${{ steps.resolve.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -z "${TAG}" ]]; then | |
| echo "::error::resolved tag is empty; refusing to dispatch" | |
| exit 1 | |
| fi | |
| gh workflow run release.yml --ref main \ | |
| -f dry_run=false \ | |
| -f tag_name="${TAG}" \ | |
| -f sha="${GITHUB_SHA}" | |
| echo "Dispatched release.yml for ${TAG} @ ${GITHUB_SHA}" | |