pg-embed-setup-unpriv 0.6.2

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

'on':
  pull_request:
    types: [opened, synchronize, reopened]
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
jobs:
  build-test:
    name: Linux Build and Test (${{ matrix.privilege }})
    # Ubicloud, to avoid the hosted queue. A pull request from a fork cannot
    # obtain an Ubicloud runner, so it falls back to the hosted pool. Main's
    # Ubicloud coverage-upload writes the cache scope this reads. standard-4,
    # not standard-2: the unprivileged leg ran out of disk on standard-2
    # (`No space left on device`, run 36565376334).
    runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubicloud-standard-4' }}
    # Above the 1,800 s cargo watchdog on the coverage step plus the work
    # either side of it, which reached 830 s on run 33982759833. The job
    # had no ceiling before this and so inherited GitHub's six-hour
    # default; a ceiling merely above the watchdog would cancel the job
    # before the watchdog could report an overrun, and a cancellation
    # discards the log that would have explained it.
    timeout-minutes: 66
    strategy:
      fail-fast: false
      matrix:
        privilege: [unprivileged, root]
    permissions:
      contents: read
    env:
      CARGO_TERM_COLOR: always
      BUILD_PROFILE: debug
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      MERMAN_CLI_VERSION: '0.7.0'
      WHITAKER_INSTALLER_VERSION: '0.2.6'
      # Embedded-Postgres tests run slower under cold coverage
      # instrumentation than the default 600s cargo wall-clock cap.
      RUN_RUST_CARGO_WAIT_TIMEOUT: '1800'
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
        with:
          fetch-depth: 0
          persist-credentials: false
      - name: Setup Rust
        uses: leynos/shared-actions/.github/actions/setup-rust@074f7d8ba75a6e5d18532b72cbe38fccbda4e9c6
      - name: Install mdtablefix
        # `make check-fmt` runs `mdtablefix --check --git`; 0.6.0 is the
        # first release with `--check` and `--git`.
        if: ${{ matrix.privilege == 'unprivileged' }}
        uses: leynos/shared-actions/.github/actions/install-mdtablefix@774cc5a01e457714aede66e14a97b3e257812fc2
        with:
          version: "0.6.0"

      - name: Format
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: make check-fmt
      - name: Markdown lint
        if: ${{ matrix.privilege == 'unprivileged' }}
        uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff
        with:
          globs: '**/*.md'
      - name: Setup uv
        if: ${{ matrix.privilege == 'unprivileged' }}
        uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990
        with:
          enable-cache: false
      - name: Enforce en-GB-oxendict spelling
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: make spelling
      - name: Release tooling tests
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: make test-scripts
      - name: Install interrogate
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: uv tool install interrogate==1.7.0
      # The shared action resolves a pinned, checksum-verified installer
      # release archive. The previous inline step fell back to
      # `cargo install --locked whitaker-installer`, which built the tool from
      # source in CI and verified nothing.
      - name: Install Whitaker Dylint suite
        if: ${{ matrix.privilege == 'unprivileged' }}
        uses: leynos/shared-actions/.github/actions/install-whitaker@e041cb75c35c3524201a32d5e57c87408fbd5874
        with:
          installer-version: ${{ env.WHITAKER_INSTALLER_VERSION }}
      - name: Lint
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: make lint
      - name: Set up isolated Rust for Merman
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: rustup toolchain install 1.95.0 --profile minimal
      - name: Cache Merman
        if: ${{ matrix.privilege == 'unprivileged' }}
        id: cache-merman
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
        with:
          path: ~/.cargo/bin/merman-cli
          key: merman-cli-${{ runner.os }}-${{ runner.arch }}-${{ env.MERMAN_CLI_VERSION }}
      - name: Install Mermaid validation tools
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: |
          uv tool install nixie-cli==1.1.0
          if [[ "${{ steps.cache-merman.outputs.cache-hit }}" != 'true' ]]; then
            cargo +1.95.0 install merman-cli --version "=${MERMAN_CLI_VERSION}" --locked
          fi
      - name: Validate Mermaid diagrams
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: make nixie
      - name: Install cargo-nextest
        run: cargo binstall -y cargo-nextest@0.9.122
      - name: Test with all features (unprivileged)
        if: ${{ matrix.privilege == 'unprivileged' }}
        run: >-
          RUSTFLAGS="-D warnings"
          cargo nextest run --all-targets --all-features
      - name: Run Loom model tests (unprivileged)
        if: ${{ matrix.privilege == 'unprivileged' }}
        timeout-minutes: 10
        run: make test-loom
      - name: Test and Measure Coverage (unprivileged)
        if: ${{ matrix.privilege == 'unprivileged' }}
        uses: leynos/shared-actions/.github/actions/generate-coverage@d4d248bbbecdcf7b4f5bc79ffd4d6caee370bd79
        with:
          output-path: lcov.info
          format: lcov
          features: dev-worker
          with-default-features: "false"
          # Compare changed-line coverage against the baseline
          # `coverage-main.yml` writes. Caches saved on main are readable by
          # every pull-request run, so the comparison happens here, on the
          # runner, with no call to any external service.
          with-ratchet: 'true'
          # This lane measures and compares; it does not publish. The action
          # archives the report it generated under a step of its own, which no
          # scanner over this file's steps can see, so declining that archive
          # is the only way the boundary is observable here. `coverage-main.yml`
          # keeps the default and uploads.
          publish-artefact: 'false'
      - name: Install cargo-nextest and test (root)
        if: ${{ matrix.privilege == 'root' }}
        run: |
          # Worker binary path - staging logic will copy it to /tmp for nobody access
          export PG_EMBEDDED_WORKER="$PWD/target/debug/pg_worker"

          run_as_root() {
            : "${RUSTUP_HOME:=$HOME/.rustup}"
            : "${CARGO_HOME:=$HOME/.cargo}"
            sudo -E env \
              HOME="$HOME" \
              RUSTUP_HOME="$RUSTUP_HOME" \
              CARGO_HOME="$CARGO_HOME" \
              GITHUB_TOKEN="$GITHUB_TOKEN" \
              PG_EMBEDDED_WORKER="$PG_EMBEDDED_WORKER" \
              PATH="$CARGO_HOME/bin:$PATH" \
              "$@"
          }
          run_as_root make test
  msrv:
    name: Build at the declared rust-version
    # The same placement as `build-test`: a fork cannot obtain an Ubicloud
    # runner and falls back to the hosted pool.
    runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubicloud-standard-4' }}
    # A cold build of every target on a stable toolchain, with no coverage
    # instrumentation, takes about five minutes.
    timeout-minutes: 30
    permissions:
      contents: read
    env:
      CARGO_TERM_COLOR: always
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
        with:
          persist-credentials: false
      - 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
      # `scripts/msrv_check.py` reads `rust-version` from Cargo.toml,
      # installs that toolchain, resolves a lockfile as a project on it
      # would, and builds every target against it.
      - name: Build at the declared rust-version
        run: make msrv
  cross-platform-tests:
    name: Test (${{ matrix.target }})
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - runner: macos-latest
            target: aarch64-apple-darwin
          - runner: windows-latest
            target: x86_64-pc-windows-msvc
    permissions:
      contents: read
    env:
      CARGO_TERM_COLOR: always
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      PG_BINARY_CACHE_DIR: ${{ github.workspace }}/.postgres-binary-cache
      PG_TEST_BACKEND: postgresql_embedded
      PG_VERSION_REQ: "=17.4.0"
      RUSTFLAGS: -D warnings
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
        with:
          persist-credentials: false
      - name: Setup Rust
        uses: leynos/shared-actions/.github/actions/setup-rust@074f7d8ba75a6e5d18532b72cbe38fccbda4e9c6
      - name: Restore PostgreSQL binary cache
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
        with:
          path: ${{ env.PG_BINARY_CACHE_DIR }}
          key: pg-embedded-postgresql-${{ env.PG_VERSION_REQ }}-${{ runner.os }}-${{ runner.arch }}
      - name: Install cargo-nextest
        shell: bash
        run: cargo binstall -y cargo-nextest@0.9.122
      - name: Build binaries
        shell: bash
        run: >-
          cargo build
          --no-default-features
          --features cluster-unit-tests,async-api
          --bins
      - name: Run CLI version smoke test
        shell: bash
        env:
          RUNNER_OS: ${{ runner.os }}
        run: |
          set -euo pipefail

          binary=target/debug/pg_embedded_setup_unpriv
          if [[ "$RUNNER_OS" == "Windows" ]]; then
            binary="${binary}.exe"
          fi

          "$binary" --version
      - name: Test unprivileged surface
        shell: bash
        run: >-
          cargo nextest run
          --all-targets
          --no-default-features
          --features cluster-unit-tests,async-api

  binstall-packaging:
    name: Binstall (${{ matrix.target }})
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - runner: ubuntu-24.04
            target: x86_64-unknown-linux-gnu
          - runner: macos-latest
            target: aarch64-apple-darwin
          - runner: windows-latest
            target: x86_64-pc-windows-msvc
    permissions:
      contents: read
    env:
      CARGO_TERM_COLOR: always
      RELEASE_PORT: "8765"
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
        with:
          persist-credentials: false
      - 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 cargo-binstall archive
        shell: bash
        env:
          TARGET: ${{ matrix.target }}
        run: >-
          uv run --script scripts/release_archive.py
          "$TARGET"
      - name: Verify archive checksum sidecar
        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
          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 "release_archive.py wrote no .sha256 sidecar" >&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: dist
      - name: Install from local cargo-binstall archive
        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"

          cargo binstall -y \
            --manifest-path Cargo.toml \
            --targets "$TARGET" \
            --strategies crate-meta-data \
            --pkg-url "https://127.0.0.1:${RELEASE_PORT}/{ name }-{ target }-v{ version }{ archive-suffix }" \
            --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