Reconcile apt repo #75
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
| name: Reconcile apt repo | |
| # Rebuilds the SHARED apt index from the full pool and re-signs it — without | |
| # building or releasing anything. Guarantees the index always matches the pool | |
| # (self-heals a partial/failed release publish), and is the seed of the single | |
| # index-builder for when a 2nd package ships (see packaging/README.md). | |
| on: | |
| schedule: | |
| - cron: "17 6 * * *" # daily; pick a quiet hour | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: read | |
| # Same group as release.yml so a reconcile never races a release on the shared index. | |
| concurrency: | |
| group: apt-publish | |
| cancel-in-progress: false | |
| jobs: | |
| reconcile: | |
| runs-on: ubuntu-latest | |
| # Backstop, not the primary guard (apt-publish deadlines each download). A hung | |
| # reconcile holds the shared apt-publish concurrency group, which would block | |
| # releases and every later reconcile behind it. | |
| timeout-minutes: 30 | |
| # Dedicated environment (not `release`): same S3/GPG creds, but a branch | |
| # policy allowing `main` and no required reviewer, so the daily scheduled | |
| # run can publish unattended. `release` stays locked to v* tags + review. | |
| environment: reconcile | |
| env: | |
| APT_S3_ENDPOINT: ${{ vars.APT_S3_ENDPOINT }} | |
| APT_S3_REGION: ${{ vars.APT_S3_REGION }} | |
| APT_S3_BUCKET: ${{ vars.APT_S3_BUCKET }} | |
| APT_S3_PREFIX: ${{ vars.APT_S3_PREFIX }} | |
| AWS_ACCESS_KEY_ID: ${{ secrets.APT_S3_ACCESS_KEY }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.APT_S3_SECRET_KEY }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.24" | |
| # apt-ftparchive ships in apt-utils, which the runner image already carries ("apt-utils | |
| # is already the newest version" in every successful run), so the install is a fallback | |
| # for an image that ever drops it and the apt-get update behind it does not run at all | |
| # in the normal case. It was not a free no-op: on 2026-08-19 the Ubuntu mirror hung | |
| # mid-fetch with no timeout of its own and the step sat silent for 29 minutes, until the | |
| # job timeout killed a reconcile that had not yet reached the pool. A stall here is | |
| # expensive out of proportion to the step, because the job holds the shared apt-publish | |
| # concurrency group while it burns: releases and every later reconcile queue behind it. | |
| # The step timeout bounds the fallback so a bad mirror fails fast and names itself. | |
| - name: Ensure apt-ftparchive is available | |
| timeout-minutes: 3 | |
| run: | | |
| if ! command -v apt-ftparchive >/dev/null; then | |
| sudo apt-get update | |
| sudo apt-get install -y apt-utils | |
| fi | |
| - name: Import GPG signing key | |
| id: gpg | |
| uses: crazy-max/ghaction-import-gpg@v7 | |
| with: | |
| gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} | |
| passphrase: ${{ secrets.GPG_PASSPHRASE }} | |
| - name: Pull full pool from S3 | |
| run: | | |
| mkdir -p aptrepo | |
| go run ./cmd/apt-publish pull aptrepo | |
| - name: Rebuild + sign index from the full pool | |
| env: | |
| APT_GPG_KEY_ID: ${{ steps.gpg.outputs.fingerprint }} | |
| run: ./scripts/build-apt-repo.sh aptrepo | |
| - name: Publish (Release files last) | |
| run: go run ./cmd/apt-publish push aptrepo |