pagedb 0.1.0-beta.6

Encrypted, portable, embedded page store with B+ tree and segment-file surfaces.
Documentation
# Manual distribution workflow. Push a v* tag first; `Release Prepare` runs the
# test suite and builds the fsck binaries. Then run this workflow with that tag
# and the successful prepare run ID.
#
# The GitHub Release consumes the prepare run's artifacts, so a failed publish
# or release stage is retried by re-dispatching with only that stage enabled —
# the tests and the binaries are never rebuilt to fix a crates.io timeout or a
# bad release body.
#
# Stages:
#   publish_crate   -> crates.io (single crate; verifying publish)
#   github_release  -> GitHub Release with the fsck binaries attached
#
# Each stage is idempotent: an already-indexed version is skipped, the release
# is overwritten in place. Re-running is safe.
#
# Required secret: CARGO_REGISTRY_TOKEN.

name: Release
run-name: Release ${{ inputs.tag }}

on:
  workflow_dispatch:
    inputs:
      tag:
        description: "Release tag to publish, e.g. v0.1.0"
        required: true
        type: string
      prepare_run_id:
        description: "Successful Release Prepare run ID holding the binaries"
        required: true
        type: string
      publish_crate:
        description: "Publish pagedb to crates.io"
        type: boolean
        default: true
      github_release:
        description: "Create the GitHub Release"
        type: boolean
        default: true

concurrency:
  group: release
  cancel-in-progress: false

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always

jobs:
  # Always runs. Cheap, and it guarantees the dispatched tag still means what
  # the prepare run assumed it meant.
  validate-version:
    uses: ./.github/workflows/release-validate.yml
    with:
      ref: ${{ inputs.tag }}

  # ── crates.io ────────────────────────────────────────────────────────────────
  # pagedb is a single leaf crate — no publish-order tiers. A verifying publish
  # (no --no-verify) is the strongest guarantee the uploaded tarball builds from
  # a clean checkout, and there is no inter-crate index race to skip it for.
  publish-crate:
    name: Publish to crates.io
    needs: validate-version
    if: inputs.publish_crate
    runs-on: ubuntu-latest
    environment: crates.io
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          ref: ${{ inputs.tag }}

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable

      # The verifying publish compiles the packaged lib + the pagedb-fsck bin
      # against crates.io deps. The io-uring VFS backend needs nothing extra,
      # but clang/libclang keeps parity with the test image in case a future
      # build-dep needs bindgen.
      - name: Install LLVM/Clang
        run: sudo apt-get update && sudo apt-get install -y --no-install-recommends clang libclang-dev

      - name: Set version from tag
        run: bash scripts/ci/stamp_version.sh "${{ needs.validate-version.outputs.version }}"

      - name: Publish pagedb
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          set -euo pipefail
          VERSION=$(cargo metadata --no-deps --format-version=1 \
            | jq -r '.packages[] | select(.name == "pagedb") | .version')

          is_published() {
            curl -sf \
              -H "User-Agent: pagedb-ci (github.com/nodedb-lab/pagedb)" \
              "https://crates.io/api/v1/crates/pagedb/$1" > /dev/null 2>&1
          }

          if is_published "$VERSION"; then
            echo "pagedb@$VERSION already published — skipping"
            exit 0
          fi

          echo "Publishing pagedb@$VERSION..."
          # --allow-dirty: stamp_version.sh may have edited Cargo.toml for a
          # prerelease tag; the verifying publish still runs.
          cargo publish -p pagedb --allow-dirty

  # ── GitHub Release ───────────────────────────────────────────────────────────
  # Independent of publish-crate: a crates.io outage must not block cutting the
  # release, and re-running this stage alone is the fix for a bad release body.
  github-release:
    name: Create GitHub Release
    needs: validate-version
    if: inputs.github_release
    runs-on: ubuntu-latest
    permissions:
      contents: write
      actions: read
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          ref: ${{ inputs.tag }}
          fetch-depth: 0

      # Runs before the download so a wrong, unfinished, or foreign run is
      # rejected without its archives ever reaching the runner.
      - name: Verify prepare run
        env:
          GH_TOKEN: ${{ github.token }}
          PREPARE_RUN_ID: ${{ inputs.prepare_run_id }}
        run: |
          set -euo pipefail
          [[ "$PREPARE_RUN_ID" =~ ^[0-9]+$ ]] || {
            echo "::error::prepare_run_id must be numeric"
            exit 1
          }
          gh api \
            -H "Accept: application/vnd.github+json" \
            -H "X-GitHub-Api-Version: 2022-11-28" \
            "repos/${GITHUB_REPOSITORY}/actions/runs/${PREPARE_RUN_ID}" \
            >"$RUNNER_TEMP/prepare-run.json"
          bash scripts/ci/verify_prepare_run.sh \
            "$RUNNER_TEMP/prepare-run.json" \
            "$(git rev-parse HEAD)"

      - name: Download fsck artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: "fsck-*"
          run-id: ${{ inputs.prepare_run_id }}
          github-token: ${{ github.token }}
          path: ./artifacts
          merge-multiple: true

      - name: Verify release artifacts
        env:
          VERSION: ${{ needs.validate-version.outputs.version }}
        run: |
          set -euo pipefail
          bash scripts/ci/verify_release_artifacts.sh ./artifacts "$VERSION"

      # The changelog is the release notes. Auto-generated notes list commit
      # subjects, which describe what was done to the code; the changelog
      # describes what changed for the person upgrading. `validate-version`
      # has already proved this section exists and is non-empty.
      - name: Build release notes from the changelog
        env:
          TAG: ${{ inputs.tag }}
          BASE: ${{ needs.validate-version.outputs.base_version }}
        run: |
          set -euo pipefail
          PREVIOUS_TAG=$(git tag --sort=-v:refname 'v*' | grep -v "^${TAG}$" | head -n 1 || true)

          bash scripts/ci/changelog_section.sh "$BASE" /tmp/changelog-section.md

          {
            cat /tmp/changelog-section.md
            echo
            if [[ -n "$PREVIOUS_TAG" ]]; then
              echo "**Full diff:** https://github.com/nodedb-lab/pagedb/compare/${PREVIOUS_TAG}...${TAG}"
            else
              echo "**Tree:** https://github.com/nodedb-lab/pagedb/tree/${TAG}"
            fi
          } > /tmp/release-notes.md

      - name: Create GitHub Release
        uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
        with:
          tag_name: ${{ inputs.tag }}
          name: pagedb ${{ needs.validate-version.outputs.version }}
          body_path: /tmp/release-notes.md
          draft: false
          prerelease: ${{ needs.validate-version.outputs.is_full_release != 'true' }}
          files: artifacts/*
          fail_on_unmatched_files: true