pgctl: Postgres backup, restore, and verification #1
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: Release | |
| # commit -> release-please PR -> merge -> tag vX.Y.Z -> GoReleaser -> binaries + ghcr image | |
| # | |
| # One source of version truth: release-please owns the number, GoReleaser only | |
| # builds what the tag says. Nothing here publishes on an ordinary push to main -- | |
| # a release is the act of merging the release PR. | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| rebuild-tag: | |
| description: "Re-run GoReleaser against an existing tag (e.g. v0.1.0). Leave empty for a normal run." | |
| required: false | |
| type: string | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Keeps a release PR open against main, and cuts the tag when it is merged. | |
| release-please: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| outputs: | |
| released: ${{ steps.rp.outputs.release_created }} | |
| tag: ${{ steps.rp.outputs.tag_name }} | |
| steps: | |
| # A GitHub App token rather than the | |
| # default GITHUB_TOKEN, because a tag pushed with GITHUB_TOKEN does not | |
| # trigger other workflows -- so anything we later hang off the release tag | |
| # would silently never fire. It also means the release PR and the tag are | |
| # authored by the bot rather than github-actions[bot]. | |
| # | |
| # RELEASE_APP_ID / RELEASE_APP_PRIVATE_KEY are organisation secrets, so | |
| # this repo needs nothing added to use them. | |
| - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 | |
| id: app-token | |
| with: | |
| app-id: ${{ secrets.RELEASE_APP_ID }} | |
| private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| # Without a checkout, release-please has no local history and backfills the | |
| # file list for every commit through the API. Cheap on a small repo, but | |
| # This step can die partway through and strand | |
| # a release PR with a manifest conflict nothing heals. Give it the history. | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4 | |
| id: rp | |
| with: | |
| token: ${{ steps.app-token.outputs.token }} | |
| config-file: release-please-config.json | |
| manifest-file: .release-please-manifest.json | |
| # Builds the tag. Normally that is the tag release-please just cut; rebuild-tag | |
| # lets it re-run against an existing one, for when the publish failed but the | |
| # tag is already out and burning a version number to retry would be silly. | |
| goreleaser: | |
| needs: release-please | |
| if: needs.release-please.outputs.released == 'true' || inputs.rebuild-tag != '' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: write # upload the release artifacts | |
| packages: write # push to ghcr | |
| steps: | |
| # metsareleasebot, to publish the GitHub release. | |
| - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 | |
| id: app-token | |
| with: | |
| app-id: ${{ secrets.RELEASE_APP_ID }} | |
| private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| owner: ${{ github.repository_owner }} | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| # GoReleaser needs the tag release-please just pushed, and the history | |
| # to build a changelog from. | |
| fetch-depth: 0 | |
| ref: ${{ inputs.rebuild-tag || needs.release-please.outputs.tag }} | |
| - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 | |
| with: | |
| go-version: "1.25" | |
| - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3 | |
| # Log in to GHCR to pull the base image and push the recovery kit. | |
| - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # Pin the recovery image's base by digest, not by the moving :18 tag. | |
| # | |
| # This is the difference between "v1.2.0 contains postgres-barman as it was | |
| # when we released and tested it" and "v1.2.0 contains whatever | |
| # postgres-barman happens to be today". In an incident you are betting on | |
| # the former. Resolve it once, here, and stamp it into the build. | |
| - name: Pin postgres-barman by digest | |
| id: base | |
| run: | | |
| set -euo pipefail | |
| digest=$(docker buildx imagetools inspect \ | |
| ghcr.io/metsaapp/postgres-barman:18 \ | |
| --format '{{.Manifest.Digest}}') | |
| echo "image=ghcr.io/metsaapp/postgres-barman@${digest}" >> "$GITHUB_OUTPUT" | |
| echo "recovery image will be built FROM ghcr.io/metsaapp/postgres-barman@${digest}" | |
| # On a rebuild, skip the archives and checksums: they were published when the | |
| # tag was first cut and GitHub rejects re-uploading an asset that already | |
| # exists (422 already_exists). GoReleaser uploads archives BEFORE it pushes | |
| # images, so that 422 is what stopped the recovery image ever reaching GHCR. | |
| # Skipping them lets the rebuild get to the part that is actually missing. | |
| - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6 | |
| with: | |
| version: "~> v2" | |
| args: >- | |
| release --clean | |
| ${{ inputs.rebuild-tag && '--skip=archive' || '' }} | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| POSTGRES_BARMAN_IMAGE: ${{ steps.base.outputs.image }} |