diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc1edd3c..911fc6a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,3 +149,22 @@ jobs: with: token: ${{ secrets.QLTY_COVERAGE_TOKEN }} files: lcov.info + + # Single required status check for branch protection. + # Passes when all jobs pass OR when jobs are skipped (no Rust changes). + ci-pass: + name: CI + runs-on: ubuntu-latest + if: always() + needs: [quality, test, test-cross-platform, coverage] + steps: + - name: Check results + run: | + results=("${{ needs.quality.result }}" "${{ needs.test.result }}" "${{ needs.test-cross-platform.result }}" "${{ needs.coverage.result }}") + for result in "${results[@]}"; do + if [[ "$result" != "success" && "$result" != "skipped" ]]; then + echo "Job failed with result: $result" + exit 1 + fi + done + echo "All jobs passed or were skipped" diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml new file mode 100644 index 00000000..7a2a02b8 --- /dev/null +++ b/.github/workflows/release-plz.yml @@ -0,0 +1,54 @@ +name: release-plz + +on: + push: + branches: [main] + +jobs: + release-plz-release: + name: Release-plz release + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + # persist-credentials required for pushing signed tags via git CLI + persist-credentials: true + - uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1 + with: + install: true + - name: Run release-plz + uses: release-plz/action@52440b50d383aa252927de395c8b2c1e0a9cf8e9 # v0.5 + with: + command: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + release-plz-pr: + name: Release-plz PR + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + concurrency: + group: release-plz-${{ github.ref }} + cancel-in-progress: false + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + - uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1 + with: + install: true + - name: Run release-plz + uses: release-plz/action@52440b50d383aa252927de395c8b2c1e0a9cf8e9 # v0.5 + with: + command: release-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index e0657783..b3e89c8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,37 +17,24 @@ All notable changes to this project will be documented in this file. - Evaluation enhancements with confidence, MIME, tags, metadata ([#29](https://github.com/EvilBit-Labs/libmagic-rs/pull/29)) - Strength calculation & documentation improvements ([#21](https://github.com/EvilBit-Labs/libmagic-rs/pull/21)) ([#30](https://github.com/EvilBit-Labs/libmagic-rs/pull/30)) +### Bug Fixes + +- **ci**: Enable persist-credentials for signed tag pushing +- **ci**: Pin GitHub Actions to commit SHAs for supply chain security + +### Refactor + +- **ci**: Use mise-action instead of dtolnay/rust-toolchain + ### Documentation - Comprehensive mdbook rewrite, rustdoc fixes, and test stability ([#33](https://github.com/EvilBit-Labs/libmagic-rs/pull/33)) -- **agents**: Update AGENTS.md to reflect implemented features and future plans -- **agents**: Fix inaccurate feature claims in AGENTS.md -- **readme**: Update README.md with project badges for visibility -- Improve SECURITY.md with scope, safe harbor, and responsible disclosure -- Add PGP key for encrypted vulnerability reporting -- Use support@evilbitlabs.io for security contact to match PGP key -- Add code review requirements to AGENTS.md and CONTRIBUTING.md -- Add quick reference and OSSF quality standards to CLAUDE.md -- Add DCO requirement to CONTRIBUTING.md -- Add project governance model and update development guide -- Update roadmap to match milestones and add non-goals -- Add release verification guide for signed artifacts -- Add security assurance case for OSSF gold criteria -- **readme**: Add OpenSSF Best Practices badge -- Move OSSF standards from CLAUDE.md to AGENTS.md -- Extract roadmap into ROADMAP.md to keep README concise -- Add project-level Claude Code skills for Rust development +- Comprehensive documentation, security, and CI hardening ([#58](https://github.com/EvilBit-Labs/libmagic-rs/pull/58)) ### Miscellaneous Tasks - **ci**: Add GitHub workflows for auditing, security, and documentation - Add Contributor Covenant Code of Conduct ([#2](https://github.com/EvilBit-Labs/libmagic-rs/pull/2)) -- Pin all GitHub Actions to SHA hashes for supply chain security -- Revert SHA pins in release.yml (managed by cargo-dist) -- Add git-cliff changelog generation and simplify justfile -- Commit Cargo.lock for reproducible binary builds -- Commit mise.lock for reproducible dev tool versions -- Add SPDX license and copyright headers to all source files -- Add Claude Code hook to enforce DCO sign-off on commits -- Add hookify rules for Rust code safety warnings +- Add release-plz for crates.io publishing with trusted publishing +- Add merge gate job for branch protection diff --git a/docs/src/release-process.md b/docs/src/release-process.md index 64bd47e5..06844944 100644 --- a/docs/src/release-process.md +++ b/docs/src/release-process.md @@ -297,74 +297,41 @@ git push origin --delete hotfix/v0.2.1 ## Release Automation -### GitHub Actions Workflow - -```yaml -# .github/workflows/release.yml -name: Release - -on: - push: - tags: - - v* - -jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Setup Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - - name: Run tests - run: cargo test --all-features - - - name: Build release - run: cargo build --release - - - name: Create release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false -``` +Releases are automated by two complementary tools: -### Automated Checks +- **release-plz**: Manages crates.io publishing, version bumping, changelog generation, and git tagging +- **cargo-dist**: Builds cross-platform binaries, Homebrew formulas, SBOM, and GitHub Releases -```bash -#!/bin/bash -# scripts/pre_release_check.sh +### How It Works -set -e +1. Every push to `main` triggers release-plz, which opens (or updates) a **release PR** with: + - Version bump in `Cargo.toml` + - Updated `CHANGELOG.md` (generated via git-cliff) + - Semantic versioning based on conventional commits -echo "Running pre-release checks..." +2. When the release PR is **merged**, release-plz: + - Publishes the crate to **crates.io** + - Creates a **git tag** (e.g., `v0.2.0`) -# Code quality -cargo fmt -- --check -cargo clippy -- -D warnings +3. The git tag triggers **cargo-dist**, which: + - Builds binaries for all target platforms + - Generates SLSA attestations and SBOM + - Publishes the Homebrew formula + - Creates the **GitHub Release** with all artifacts -# Tests -cargo test --all-features -cargo test --doc +### Configuration Files -# Security -cargo audit - -# Performance -cargo bench --bench evaluation_bench +| File | Purpose | +|------|---------| +| `release-plz.toml` | release-plz configuration (crates.io, tags, changelog) | +| `dist-workspace.toml` | cargo-dist configuration (binaries, Homebrew, SBOM) | +| `cliff.toml` | git-cliff changelog template (shared by both tools) | -# Documentation -cargo doc --document-private-items +### Authentication -echo "All pre-release checks passed!" -``` +- **crates.io**: Uses [trusted publishing](https://doc.rust-lang.org/cargo/reference/registry-authentication.html#oidc-token-exchange) (OIDC) -- no token secret needed. Requires configuring the trusted publisher on crates.io and `id-token: write` permission in the workflow. Note: the first publish of a new crate must be done manually with `cargo publish`. +- **Homebrew tap**: Requires a `HOMEBREW_TAP_TOKEN` secret with write access to the tap repository. +- **GitHub Releases**: Uses the automatic `GITHUB_TOKEN`. ## Release Schedule diff --git a/mise.lock b/mise.lock index 83ae4cb2..aa249306 100644 --- a/mise.lock +++ b/mise.lock @@ -86,6 +86,10 @@ backend = "cargo:mdbook-tabs" version = "0.15.3" backend = "cargo:mdbook-toc" +[[tools."cargo:release-plz"]] +version = "0.3.155" +backend = "cargo:release-plz" + [[tools.just]] version = "1.46.0" backend = "aqua:casey/just" diff --git a/mise.toml b/mise.toml index 603a0db1..6f741445 100644 --- a/mise.toml +++ b/mise.toml @@ -30,3 +30,4 @@ pre-commit = "latest" "cargo:cargo-machete" = "0.9.1" "cargo:git-cliff" = "2.12.0" scorecard = "5.4.0" +"cargo:release-plz" = "0.3.155" diff --git a/release-plz.toml b/release-plz.toml new file mode 100644 index 00000000..183b4109 --- /dev/null +++ b/release-plz.toml @@ -0,0 +1,8 @@ +[workspace] +# release-plz handles crates.io publish + git tags +# cargo-dist handles GitHub Releases (triggered by the tag) +git_release_enable = false +git_tag_enable = true +publish = true +semver_check = true +changelog_config = "cliff.toml"