metadata-gen 0.0.8

A powerful Rust library for extracting, validating, and processing metadata in YAML, TOML, and JSON formats from any content or data file.
Documentation
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# Tag-driven release. Pushing a signed, annotated `vX.Y.Z` tag that passed
# scripts/release-preflight.sh locally produces the GitHub release:
# the packaged crate, a CycloneDX SBOM, build provenance for both, and
# release notes in the repository-standard shape (hand-written Highlights
# from docs/releases/vX.Y.Z.md, generated What's Changed, checksums,
# Full Changelog).
#
# Publishing to crates.io stays a maintainer action after the published
# release audit; this workflow never runs `cargo publish`.

name: Release

on:
  push:
    tags: ["v[0-9]+.[0-9]+.[0-9]+"]

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always

jobs:
  verify:
    name: verify tag and versions
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
          fetch-depth: 0
      - uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067 # master
        with:
          toolchain: stable
      - uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
      - name: Tag is annotated, named for the manifest, and has release notes
        run: |
          set -euo pipefail
          tag="${GITHUB_REF_NAME}"
          version="${tag#v}"
          [ "$(git cat-file -t "refs/tags/${tag}")" = "tag" ] \
            || { echo "::error::${tag} is a lightweight tag; releases need a signed annotated tag"; exit 1; }
          subject="$(git tag -l --format='%(contents:subject)' "${tag}")"
          [ "${subject}" = "metadata-gen v${version}" ] \
            || { echo "::error::tag subject is '${subject}', expected 'metadata-gen v${version}'"; exit 1; }
          manifest="$(grep -m1 '^version = ' Cargo.toml | cut -d'"' -f2)"
          [ "${manifest}" = "${version}" ] \
            || { echo "::error::Cargo.toml says ${manifest}, tag says ${version}"; exit 1; }
          grep -q '^## Highlights' "docs/releases/${tag}.md" \
            || { echo "::error::docs/releases/${tag}.md is missing or has no '## Highlights' section"; exit 1; }
      - run: ./scripts/verify-release-versions.sh
      - run: cargo test --all-features --locked

  release:
    name: package, attest, publish release
    needs: verify
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: write
      id-token: write
      attestations: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
          fetch-depth: 0
      - uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067 # master
        with:
          toolchain: stable
      - uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
      - uses: taiki-e/cache-cargo-install-action@9ee83daaa7b96a6fab930949ecf1122bba04a389 # v3.0.8
        with:
          tool: cargo-cyclonedx
      - name: Package the crate and generate the SBOM
        run: |
          set -euo pipefail
          version="${GITHUB_REF_NAME#v}"
          mkdir -p dist
          cargo package --locked
          cp "target/package/metadata-gen-${version}.crate" dist/
          cargo cyclonedx --format json --all-features --override-filename "metadata-gen-${version}.cdx"
          mv "metadata-gen-${version}.cdx.json" dist/
          (cd dist && sha256sum -- * > ../SHA256SUMS)
          cat SHA256SUMS
      - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
        with:
          subject-path: dist/*
      - name: Compose release notes
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          set -euo pipefail
          tag="${GITHUB_REF_NAME}"
          prev="$(git describe --tags --abbrev=0 "${tag}^" 2>/dev/null || true)"
          args=(-f "tag_name=${tag}")
          [ -n "${prev}" ] && args+=(-f "previous_tag_name=${prev}")
          generated="$(gh api -X POST "repos/${GITHUB_REPOSITORY}/releases/generate-notes" "${args[@]}" --jq .body)"
          full="$(printf '%s\n' "${generated}" | grep '^\*\*Full Changelog\*\*' || true)"
          [ -n "${full}" ] || full="**Full Changelog**: https://github.com/${GITHUB_REPOSITORY}/compare/${prev}...${tag}"
          {
            sed -n '/^## Highlights/,$p' "docs/releases/${tag}.md"
            echo
            printf '%s\n' "${generated}" | grep -v '^\*\*Full Changelog\*\*'
            echo
            echo '## Checksums'
            echo
            echo 'SHA-256 of every asset attached to this release:'
            echo
            echo '```text'
            cat SHA256SUMS
            echo '```'
            echo
            printf '%s\n' "${full}"
          } > release-notes.md
          cat release-notes.md
      - name: Publish the GitHub release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          set -euo pipefail
          tag="${GITHUB_REF_NAME}"
          gh release create "${tag}" \
            --verify-tag \
            --title "metadata-gen ${tag#v}" \
            --notes-file release-notes.md \
            dist/*