pg-embed-setup-unpriv 0.6.2

Initializes postgresql_embedded clusters with platform-appropriate setup
Documentation
name: Release

'on':
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      tag:
        description: Existing Git tag to publish, for example v0.5.0
        required: true
        type: string

permissions:
  contents: write

jobs:
  prepare:
    name: Resolve release tag
    runs-on: ubuntu-24.04
    outputs:
      tag: ${{ steps.resolve.outputs.tag }}
      version: ${{ steps.resolve.outputs.version }}
    steps:
      - name: Resolve tag and version
        id: resolve
        env:
          EVENT_NAME: ${{ github.event_name }}
          INPUT_TAG: ${{ inputs.tag }}
          REF_NAME: ${{ github.ref_name }}
        run: |
          set -euo pipefail

          if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
            tag="$INPUT_TAG"
          else
            tag="$REF_NAME"
          fi

          if ! [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            echo "Release tags must match vMAJOR.MINOR.PATCH: $tag" >&2
            exit 1
          fi

          echo "tag=$tag" >> "$GITHUB_OUTPUT"
          echo "version=${tag#v}" >> "$GITHUB_OUTPUT"

  create-release:
    name: Create draft release
    needs: prepare
    runs-on: ubuntu-24.04
    permissions:
      contents: write
    steps:
      # This job deliberately skips actions/checkout: it only calls the GitHub
      # API. GH_REPO gives gh the repository it would otherwise infer from a
      # git remote, and the API tag check replaces `--verify-tag`, which needs
      # a local clone.
      - name: Create draft release when missing
        env:
          GH_REPO: ${{ github.repository }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG: ${{ needs.prepare.outputs.tag }}
        run: |
          set -euo pipefail

          if gh release view "$TAG" >/dev/null 2>&1; then
            echo "Release $TAG already exists in $GH_REPO"
            exit 0
          fi

          if ! gh api "repos/$GH_REPO/git/ref/tags/$TAG" >/dev/null 2>&1; then
            echo "Tag $TAG does not exist in $GH_REPO" >&2
            exit 1
          fi

          gh release create "$TAG" \
            --draft \
            --generate-notes \
            --title "$TAG"

  build-assets:
    name: Build ${{ matrix.target }}
    needs: [prepare, create-release]
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - runner: ubuntu-24.04
            target: x86_64-unknown-linux-gnu
          - runner: ubuntu-24.04-arm
            target: aarch64-unknown-linux-gnu
          - runner: windows-latest
            target: x86_64-pc-windows-msvc
          - runner: macos-latest
            target: aarch64-apple-darwin
          - runner: macos-15-intel
            target: x86_64-apple-darwin
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
        with:
          persist-credentials: false
          ref: ${{ needs.prepare.outputs.tag }}
      - name: Setup Rust
        uses: leynos/shared-actions/.github/actions/setup-rust@074f7d8ba75a6e5d18532b72cbe38fccbda4e9c6
      - name: Setup uv
        uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990
        with:
          enable-cache: false
      - name: Build release archive
        shell: bash
        env:
          RELEASE_VERSION: ${{ needs.prepare.outputs.version }}
          TARGET: ${{ matrix.target }}
        run: >-
          uv run --script scripts/release_archive.py
          "$TARGET"
          --release-version "$RELEASE_VERSION"
      # build-assets checks out the release tag, so the packaging script comes
      # from the tagged tree while this workflow comes from the default branch.
      # Tags cut before the script wrote sidecars still need one, so compute
      # any that are missing here.
      - name: Ensure checksum sidecars
        shell: bash
        working-directory: dist
        run: |
          set -euo pipefail

          # Arrays are avoided: expanding an empty array under `set -u`
          # fails on the Bash 3.2 that macOS runners provide.
          shopt -s nullglob
          archives=0
          for archive in *.tgz; do
            archives=$((archives + 1))
            if [ -f "${archive}.sha256" ]; then
              continue
            fi
            if command -v sha256sum >/dev/null 2>&1; then
              sha256sum "$archive" > "${archive}.sha256"
            else
              shasum --algorithm 256 "$archive" > "${archive}.sha256"
            fi
          done

          if [ "$archives" -eq 0 ]; then
            echo "release_archive.py produced no .tgz archive" >&2
            exit 1
          fi
      - name: Upload release archive and checksum sidecar
        shell: bash
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG: ${{ needs.prepare.outputs.tag }}
        run: gh release upload "$TAG" dist/*.tgz dist/*.tgz.sha256 --clobber

  audit-draft-assets:
    name: Audit draft assets (${{ matrix.target }})
    needs: [prepare, build-assets]
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - runner: ubuntu-24.04
            target: x86_64-unknown-linux-gnu
          - runner: ubuntu-24.04-arm
            target: aarch64-unknown-linux-gnu
          - runner: windows-latest
            target: x86_64-pc-windows-msvc
          - runner: macos-latest
            target: aarch64-apple-darwin
          - runner: macos-15-intel
            target: x86_64-apple-darwin
    permissions:
      # Draft releases are invisible to read-scoped tokens, so downloading the
      # draft assets requires write scope even though this job only reads.
      contents: write
    env:
      CARGO_TERM_COLOR: always
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      RELEASE_PORT: "8765"
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
        with:
          persist-credentials: false
          ref: ${{ needs.prepare.outputs.tag }}
      - name: Setup Rust
        uses: leynos/shared-actions/.github/actions/setup-rust@074f7d8ba75a6e5d18532b72cbe38fccbda4e9c6
      - name: Download draft release assets
        shell: bash
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG: ${{ needs.prepare.outputs.tag }}
        run: |
          set -euo pipefail

          rm -rf release-dist
          mkdir -p release-dist
          gh release download "$TAG" \
            --dir release-dist \
            --pattern "*.tgz" \
            --pattern "*.tgz.sha256"
      - name: Verify draft asset checksums
        shell: bash
        working-directory: release-dist
        run: |
          set -euo pipefail

          # Arrays are avoided: expanding an empty array under `set -u`
          # fails on the Bash 3.2 that macOS runners provide.
          shopt -s nullglob
          verified=0
          for sidecar in *.tgz.sha256; do
            if command -v sha256sum >/dev/null 2>&1; then
              sha256sum --check --strict "$sidecar"
            else
              shasum --algorithm 256 --check --strict "$sidecar"
            fi
            verified=$((verified + 1))
          done

          if [ "$verified" -eq 0 ]; then
            echo "No .sha256 sidecars were published alongside the archives" >&2
            exit 1
          fi
      - name: Start local HTTPS archive server
        id: archive-server
        uses: ./.github/actions/setup-local-https-server
        with:
          port: ${{ env.RELEASE_PORT }}
          cert-dir: ${{ runner.temp }}/binstall-cert
          serve-dir: release-dist
      - name: Audit cargo-binstall assets
        shell: bash
        env:
          ROOT_CERTIFICATE: ${{ steps.archive-server.outputs.ca-certificate }}
          RUNNER_OS: ${{ runner.os }}
          SERVER_PID: ${{ steps.archive-server.outputs.server-pid }}
          TARGET: ${{ matrix.target }}
        run: |
          set -euo pipefail
          trap 'kill "$SERVER_PID" 2>/dev/null || true' EXIT

          install_dir="$PWD/.binstall-bin"
          rm -rf "$install_dir"
          mkdir -p "$install_dir"

          pkg_url="https://127.0.0.1:${RELEASE_PORT}/{ name }-{ target }-v{ version }{ archive-suffix }"
          for target in \
            x86_64-unknown-linux-gnu \
            aarch64-unknown-linux-gnu \
            x86_64-pc-windows-msvc \
            aarch64-apple-darwin \
            x86_64-apple-darwin
          do
            cargo binstall -y \
              --manifest-path Cargo.toml \
              --targets "$target" \
              --strategies crate-meta-data \
              --pkg-url "$pkg_url" \
              --root-certificates "$ROOT_CERTIFICATE" \
              --install-path "$RUNNER_TEMP/binstall-dry-run" \
              --force \
              --dry-run \
              pg-embed-setup-unpriv
          done

          cargo binstall -y \
            --manifest-path Cargo.toml \
            --targets "$TARGET" \
            --strategies crate-meta-data \
            --pkg-url "$pkg_url" \
            --root-certificates "$ROOT_CERTIFICATE" \
            --install-path "$install_dir" \
            --force \
            pg-embed-setup-unpriv

          binary_ext=""
          if [[ "$RUNNER_OS" == "Windows" ]]; then
            binary_ext=".exe"
          fi

          test -f "$install_dir/pg_embedded_setup_unpriv${binary_ext}"
          test -f "$install_dir/pg_worker${binary_ext}"
          "$install_dir/pg_embedded_setup_unpriv${binary_ext}" --version

  publish-release:
    name: Publish release
    needs: [prepare, audit-draft-assets]
    runs-on: ubuntu-24.04
    permissions:
      contents: write
    steps:
      # Like create-release, this job has no checkout, so gh needs GH_REPO.
      - name: Publish draft release
        env:
          GH_REPO: ${{ github.repository }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG: ${{ needs.prepare.outputs.tag }}
        run: |
          set -euo pipefail

          gh release edit "$TAG" --draft=false

  verify-published-assets:
    name: Verify published asset URLs
    needs: [prepare, publish-release]
    runs-on: ubuntu-24.04
    permissions:
      contents: read
    env:
      CARGO_TERM_COLOR: always
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
        with:
          persist-credentials: false
          ref: ${{ needs.prepare.outputs.tag }}
      - name: Setup Rust
        uses: leynos/shared-actions/.github/actions/setup-rust@074f7d8ba75a6e5d18532b72cbe38fccbda4e9c6
      - name: Dry-run published release asset URLs
        run: |
          set -euo pipefail

          for target in \
            x86_64-unknown-linux-gnu \
            aarch64-unknown-linux-gnu \
            x86_64-pc-windows-msvc \
            aarch64-apple-darwin \
            x86_64-apple-darwin
          do
            cargo binstall -y \
              --manifest-path Cargo.toml \
              --targets "$target" \
              --strategies crate-meta-data \
              --install-path "$RUNNER_TEMP/binstall-url-dry-run" \
              --force \
              --dry-run \
              pg-embed-setup-unpriv
          done