reconcile manifests #18
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
| # Reconcile downstream manifests against an existing release — WITHOUT rebuilding | |
| # or cutting a new release. | |
| # | |
| # Why this exists: every publish-* job in release.yml pins a sha256 read from the | |
| # release's SHA256SUMS (the single source of truth) and verifies the bytes match. | |
| # But if a publish job was skipped/failed on the original run, or a binary was | |
| # re-uploaded (`--clobber`) afterwards, a downstream manifest (Homebrew formula, | |
| # Scoop manifest, AUR PKGBUILD) can drift out of sync with the release. | |
| # | |
| # This workflow re-renders + re-pushes those manifests from the CURRENT | |
| # SHA256SUMS of a chosen release. It never builds anything, so it's fast and | |
| # cannot itself introduce new drift. Safe to run any time; idempotent (each job | |
| # no-ops if its manifest already matches). | |
| # | |
| # Actions ▸ reconcile manifests ▸ Run workflow ▸ tag (blank → v<CMake version>) | |
| name: reconcile manifests | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Release tag to reconcile against (blank → v<CMake project version>)' | |
| required: false | |
| default: '' | |
| # Self-heal automatically. Every publish-* job in release.yml is gated behind | |
| # a build leg (needs: build-linux-*/build-windows), so ONE failed/slow build | |
| # leg silently SKIPS its downstream publisher — the public tag exists but the | |
| # package (AUR/Homebrew/scoop/…) stays stale and users file out-of-date flags. | |
| # These triggers re-pin every manifest from the release's SHA256SUMS without | |
| # rebuilding, so a package can never stay behind the latest release for long: | |
| # * after every release run completes (even partially), reconcile the | |
| # latest tag — fills in whatever the release run skipped. | |
| # * weekly, as a belt-and-suspenders backstop against any later drift | |
| # (re-uploaded asset, manual tag move, transient token failure). | |
| workflow_run: | |
| workflows: ["release"] | |
| types: [completed] | |
| schedule: | |
| - cron: '17 6 * * 1' # Mondays 06:17 UTC | |
| permissions: | |
| contents: write | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| jobs: | |
| meta: | |
| name: resolve tag + version | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.m.outputs.version }} | |
| tag: ${{ steps.m.outputs.tag }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - id: m | |
| run: | | |
| # Priority: explicit dispatch input > CMake project version > the | |
| # repo's latest published release. Auto triggers (schedule / | |
| # workflow_run) have no input, so they reconcile whatever release is | |
| # current. We prefer the LATEST published release tag over the CMake | |
| # version so a mid-development bump (CMake ahead of any release) can't | |
| # point reconcile at a tag that doesn't exist yet. | |
| TAG="${{ inputs.tag }}" | |
| if [ -z "$TAG" ]; then | |
| LATEST=$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName -q .tagName 2>/dev/null || true) | |
| if [ -n "$LATEST" ]; then | |
| TAG="$LATEST" | |
| else | |
| V=$(grep -m1 'project(agentty' CMakeLists.txt | sed -E 's/.*VERSION ([0-9.]+).*/\1/') | |
| TAG="v$V" | |
| fi | |
| fi | |
| # Derive the version FROM the tag being reconciled (a manifest for an | |
| # old tag must pin that tag's version, not HEAD's CMake version). | |
| TV="${TAG#v}" | |
| echo "version=$TV" >> "$GITHUB_OUTPUT" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "reconciling $TAG (version $TV)" | |
| # Shared helper is inlined per job (composite actions would need a checkout of | |
| # this repo in each; the duplication is tiny and keeps each job self-contained). | |
| homebrew: | |
| name: reconcile homebrew tap | |
| needs: meta | |
| runs-on: ubuntu-latest | |
| env: | |
| V: ${{ needs.meta.outputs.version }} | |
| TAG: ${{ needs.meta.outputs.tag }} | |
| TAP_TOKEN: ${{ secrets.TAP_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Re-pin formula from SHA256SUMS | |
| if: ${{ env.TAP_TOKEN != '' }} | |
| run: | | |
| set -eu | |
| gh release download "$TAG" --repo "$GITHUB_REPOSITORY" \ | |
| --pattern 'agentty-linux-x86_64' --pattern 'agentty-linux-aarch64' \ | |
| --pattern 'agentty-macos-x86_64' --pattern 'agentty-macos-arm64' \ | |
| --pattern 'SHA256SUMS' --skip-existing | |
| sum_for() { | |
| want=$(awk -v f="$1" '$2==f {print $1}' SHA256SUMS) | |
| [ -n "$want" ] || { echo "::error::$1 missing from SHA256SUMS"; exit 1; } | |
| got=$(sha256sum "$1" | awk '{print $1}') | |
| [ "$want" = "$got" ] || { echo "::error::$1 drift: SHA256SUMS=$want bytes=$got"; exit 1; } | |
| printf '%s' "$want" | |
| } | |
| sed -e "s/^ version \".*\"/ version \"$V\"/" \ | |
| -e "s|@LINUX_X86_64_SHA256@|$(sum_for agentty-linux-x86_64)|g" \ | |
| -e "s|@LINUX_AARCH64_SHA256@|$(sum_for agentty-linux-aarch64)|g" \ | |
| -e "s|@MACOS_X86_64_SHA256@|$(sum_for agentty-macos-x86_64)|g" \ | |
| -e "s|@MACOS_ARM64_SHA256@|$(sum_for agentty-macos-arm64)|g" \ | |
| packaging/homebrew/agentty.rb > agentty.rb | |
| git clone "https://x-access-token:${TAP_TOKEN}@github.com/1ay1/homebrew-tap.git" tap | |
| mkdir -p tap/Formula && cp agentty.rb tap/Formula/agentty.rb | |
| cd tap | |
| git config user.name agentty-ci; git config user.email noreply@github.com | |
| git add Formula/agentty.rb | |
| git commit -m "reconcile agentty $V" || { echo "already in sync"; exit 0; } | |
| git push origin HEAD:master | |
| scoop: | |
| name: reconcile scoop bucket | |
| needs: meta | |
| runs-on: ubuntu-latest | |
| env: | |
| V: ${{ needs.meta.outputs.version }} | |
| TAG: ${{ needs.meta.outputs.tag }} | |
| SCOOP_TOKEN: ${{ secrets.SCOOP_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Re-pin manifest from SHA256SUMS | |
| if: ${{ env.SCOOP_TOKEN != '' }} | |
| run: | | |
| set -eu | |
| gh release download "$TAG" --repo "$GITHUB_REPOSITORY" \ | |
| --pattern 'agentty-windows-x86_64.exe' --pattern 'SHA256SUMS' --skip-existing | |
| win=$(awk '$2=="agentty-windows-x86_64.exe" {print $1}' SHA256SUMS) | |
| [ -n "$win" ] || { echo "::error::windows .exe missing from SHA256SUMS"; exit 1; } | |
| got=$(sha256sum agentty-windows-x86_64.exe | awk '{print $1}') | |
| [ "$win" = "$got" ] || { echo "::error::windows .exe drift: SHA256SUMS=$win bytes=$got"; exit 1; } | |
| sed -e "s/\"version\": \".*\"/\"version\": \"$V\"/" \ | |
| -e "s|/download/v[0-9.]*/|/download/v$V/|g" \ | |
| -e "s|@WINDOWS_X86_64_SHA256@|$win|g" \ | |
| packaging/scoop/agentty.json > agentty.json | |
| git clone "https://x-access-token:${SCOOP_TOKEN}@github.com/1ay1/scoop-bucket.git" bucket | |
| mkdir -p bucket/bucket && cp agentty.json bucket/bucket/agentty.json | |
| cd bucket | |
| git config user.name agentty-ci; git config user.email noreply@github.com | |
| git add bucket/agentty.json | |
| git commit -m "reconcile agentty $V" || { echo "already in sync"; exit 0; } | |
| git push origin HEAD:master | |
| aur: | |
| name: reconcile AUR | |
| needs: meta | |
| runs-on: ubuntu-latest | |
| env: | |
| V: ${{ needs.meta.outputs.version }} | |
| TAG: ${{ needs.meta.outputs.tag }} | |
| AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Re-pin PKGBUILD from SHA256SUMS | |
| if: ${{ env.AUR_SSH_KEY != '' }} | |
| run: | | |
| set -eu | |
| gh release download "$TAG" --repo "$GITHUB_REPOSITORY" \ | |
| --pattern 'agentty-linux-x86_64' --pattern 'agentty-linux-aarch64' \ | |
| --pattern 'SHA256SUMS' --skip-existing | |
| sum_for() { | |
| want=$(awk -v f="$1" '$2==f {print $1}' SHA256SUMS) | |
| [ -n "$want" ] || { echo "::error::$1 missing from SHA256SUMS"; exit 1; } | |
| got=$(sha256sum "$1" | awk '{print $1}') | |
| [ "$want" = "$got" ] || { echo "::error::$1 drift: SHA256SUMS=$want bytes=$got"; exit 1; } | |
| printf '%s' "$want" | |
| } | |
| x64=$(sum_for agentty-linux-x86_64) | |
| arm=$(sum_for agentty-linux-aarch64) | |
| sed -e "s/^pkgver=.*/pkgver=$V/" \ | |
| -e "s/sha256sums_x86_64=.*/sha256sums_x86_64=('$x64')/" \ | |
| -e "s/sha256sums_aarch64=.*/sha256sums_aarch64=('$arm')/" \ | |
| packaging/arch/PKGBUILD > PKGBUILD | |
| mkdir -p ~/.ssh | |
| printf '%s\n' "$AUR_SSH_KEY" > ~/.ssh/aur; chmod 600 ~/.ssh/aur | |
| ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null | |
| export GIT_SSH_COMMAND="ssh -i ~/.ssh/aur" | |
| git clone ssh://aur@aur.archlinux.org/agentty-bin.git aur | |
| cp PKGBUILD aur/PKGBUILD | |
| cd aur | |
| printf 'pkgbase = agentty-bin\n\tpkgdesc = Blazing-fast Claude in your terminal\n\tpkgver = %s\n\tpkgrel = 1\n\turl = https://github.com/1ay1/agentty\n\tarch = x86_64\n\tarch = aarch64\n\tlicense = MIT\n\tprovides = agentty\n\tconflicts = agentty\n\tsource_x86_64 = agentty-%s-x86_64::https://github.com/1ay1/agentty/releases/download/v%s/agentty-linux-x86_64\n\tsha256sums_x86_64 = %s\n\tsource_aarch64 = agentty-%s-aarch64::https://github.com/1ay1/agentty/releases/download/v%s/agentty-linux-aarch64\n\tsha256sums_aarch64 = %s\n\npkgname = agentty-bin\n' \ | |
| "$V" "$V" "$V" "$x64" "$V" "$V" "$arm" > .SRCINFO | |
| git config user.name agentty-ci; git config user.email noreply@github.com | |
| git add PKGBUILD .SRCINFO | |
| git commit -m "reconcile agentty $V" || { echo "already in sync"; exit 0; } | |
| git push origin HEAD:master |