meterstore 0.4.0

Hot/cold tiered store for metering time series — PostgreSQL for the recent window, Apache Iceberg for history.
Documentation
name: Release

on:
  push:
    tags: ["v*"]
  # Lets a failed publish be retried without moving the tag.
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to release (e.g. v0.1.0)"
        required: true
        type: string

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always

jobs:
  # A tag that disagrees with Cargo.toml publishes the wrong version under the
  # right name, and crates.io versions cannot be replaced. Check it first, before
  # anything expensive runs.
  verify:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
    steps:
      - uses: actions/checkout@v4

      - name: Tag must match Cargo.toml version
        id: version
        run: |
          tag="${{ github.event.inputs.tag || github.ref_name }}"
          tag_version="${tag#v}"
          crate_version=$(cargo metadata --no-deps --format-version 1 \
            | jq -r '.packages[0].version')

          if [ "$tag_version" != "$crate_version" ]; then
            echo "::error::tag $tag implies version $tag_version, but Cargo.toml says $crate_version"
            exit 1
          fi

          echo "version=$crate_version" >> "$GITHUB_OUTPUT"
          echo "releasing $crate_version"

      - name: Reject a pre-release version on a stable tag
        run: |
          tag="${{ github.event.inputs.tag || github.ref_name }}"
          if [[ "${{ steps.version.outputs.version }}" == *-* && "$tag" != *-* ]]; then
            echo "::error::Cargo.toml holds a pre-release version but the tag does not"
            exit 1
          fi

  # The same gate CI runs. A tag must not be able to skip it.
  check:
    needs: verify
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with: { components: rustfmt, clippy }
      - uses: Swatinem/rust-cache@v2

      - run: cargo fmt --all -- --check
      - run: cargo clippy --all-targets --all-features -- -D warnings
      # The same gate CI runs, verbatim. A tag must not be able to skip it.
      # Container concurrency is bounded inside the suite itself
      # (`tests/it/containers.rs`), so neither job needs a thread cap.
      - run: cargo test --all-features
      - run: cargo doc --no-deps --all-features
        env:
          RUSTDOCFLAGS: -D warnings

      # Catches packaging problems — a file excluded from the crate that the
      # build needs — which only appear once published.
      - name: Verify the packaged crate builds
        run: cargo publish --dry-run --all-features

  publish:
    needs: [verify, check]
    runs-on: ubuntu-latest
    environment: crates-io
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2

      - name: Publish to crates.io
        run: cargo publish --all-features
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

  github-release:
    needs: [verify, publish]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Build release notes
        id: notes
        run: |
          tag="${{ github.event.inputs.tag || github.ref_name }}"
          previous=$(git describe --tags --abbrev=0 "${tag}^" 2>/dev/null || true)

          {
            echo "## meterstore ${{ needs.verify.outputs.version }}"
            echo
            if [ -n "$previous" ]; then
              echo "### Changes since $previous"
              echo
              git log --no-merges --pretty='- %s (%h)' "$previous..$tag"
            else
              echo "First release."
            fi
            echo
            echo '```toml'
            echo 'meterstore = "${{ needs.verify.outputs.version }}"'
            echo '```'
          } > notes.md

      - name: Create GitHub release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ github.event.inputs.tag || github.ref_name }}
          body_path: notes.md
          # Any tag carrying a hyphen (v0.1.0-rc.1) is a pre-release.
          prerelease: ${{ contains(needs.verify.outputs.version, '-') }}