From a2c8d2c02e477d808a67dab40ec0b955ebeaf770 Mon Sep 17 00:00:00 2001 From: RA <70325462+RAprogramm@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:48:07 +0700 Subject: [PATCH] chore(ci): extract cargo steps into composite actions --- .github/actions/cargo-audit/action.yml | 58 +++++++++++++++++++++++++ .github/actions/cargo-clippy/action.yml | 31 +++++++++++++ .github/actions/cargo-fmt/action.yml | 18 ++++++++ .github/actions/cargo-test/action.yml | 44 +++++++++++++++++++ .github/workflows/reusable-ci.yml | 57 ++++++++++-------------- docs/wiki/ci.md | 51 ++++++++++++++++++++++ docs/wiki/index.md | 1 + 7 files changed, 227 insertions(+), 33 deletions(-) create mode 100644 .github/actions/cargo-audit/action.yml create mode 100644 .github/actions/cargo-clippy/action.yml create mode 100644 .github/actions/cargo-fmt/action.yml create mode 100644 .github/actions/cargo-test/action.yml create mode 100644 docs/wiki/ci.md diff --git a/.github/actions/cargo-audit/action.yml b/.github/actions/cargo-audit/action.yml new file mode 100644 index 0000000..1dbe53b --- /dev/null +++ b/.github/actions/cargo-audit/action.yml @@ -0,0 +1,58 @@ +name: "Cargo audit" +description: "Ensure cargo-audit is installed and run it with consistent flags" +inputs: + version: + description: "cargo-audit crate version to install (leave empty for latest)" + required: false + default: "" + deny-warnings: + description: "Whether to pass --deny warnings" + required: false + default: "true" + extra-args: + description: "Additional arguments passed to cargo audit" + required: false + default: "" +runs: + using: "composite" + steps: + - name: Ensure cargo-audit + shell: bash + env: + VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + desired_version="${VERSION}" + if command -v cargo-audit >/dev/null 2>&1; then + if [ -n "${desired_version}" ]; then + current_version="$(cargo-audit --version | awk '{print $2}')" + if [ "${current_version}" = "${desired_version}" ]; then + exit 0 + fi + else + exit 0 + fi + fi + if [ -n "${desired_version}" ]; then + cargo install cargo-audit --locked --force --version "${desired_version}" + else + cargo install cargo-audit --locked --force + fi + - name: Run cargo audit + shell: bash + env: + DENY_WARNINGS: ${{ inputs.deny-warnings }} + EXTRA_ARGS: ${{ inputs.extra-args }} + run: | + set -euo pipefail + args=() + if [ "${DENY_WARNINGS}" = "true" ]; then + args+=("--deny" "warnings") + fi + if [ -n "${EXTRA_ARGS}" ]; then + # shellcheck disable=SC2206 + extra_parts=(${EXTRA_ARGS}) + args+=("${extra_parts[@]}") + fi + cargo audit "${args[@]}" + diff --git a/.github/actions/cargo-clippy/action.yml b/.github/actions/cargo-clippy/action.yml new file mode 100644 index 0000000..dc0fea3 --- /dev/null +++ b/.github/actions/cargo-clippy/action.yml @@ -0,0 +1,31 @@ +name: "Cargo clippy" +description: "Run cargo clippy with workspace defaults and optional all-features" +inputs: + toolchain: + description: "Rust toolchain identifier to use (without the leading +)" + required: true + all-features: + description: "Whether to enable --all-features" + required: false + default: "false" + extra-args: + description: "Additional arguments passed to cargo clippy" + required: false + default: "" +runs: + using: "composite" + steps: + - name: Run cargo clippy + shell: bash + env: + TOOLCHAIN: ${{ inputs.toolchain }} + ALL_FEATURES: ${{ inputs.all-features }} + EXTRA_ARGS: ${{ inputs.extra-args }} + run: | + set -euo pipefail + if [ "${ALL_FEATURES}" = "true" ]; then + cargo +"${TOOLCHAIN}" clippy --workspace --all-targets --all-features ${EXTRA_ARGS} -- -D warnings + else + cargo +"${TOOLCHAIN}" clippy --workspace --all-targets ${EXTRA_ARGS} -- -D warnings + fi + diff --git a/.github/actions/cargo-fmt/action.yml b/.github/actions/cargo-fmt/action.yml new file mode 100644 index 0000000..47ae706 --- /dev/null +++ b/.github/actions/cargo-fmt/action.yml @@ -0,0 +1,18 @@ +name: "Cargo fmt check" +description: "Run cargo fmt in check mode using the provided toolchain" +inputs: + toolchain: + description: "Rust toolchain identifier to use (without the leading +)" + required: false + default: "nightly" +runs: + using: "composite" + steps: + - name: Run cargo fmt + shell: bash + env: + TOOLCHAIN: ${{ inputs.toolchain }} + run: | + set -euo pipefail + cargo +"${TOOLCHAIN}" fmt --all -- --check + diff --git a/.github/actions/cargo-test/action.yml b/.github/actions/cargo-test/action.yml new file mode 100644 index 0000000..84cd77d --- /dev/null +++ b/.github/actions/cargo-test/action.yml @@ -0,0 +1,44 @@ +name: "Cargo test" +description: "Run cargo test for the workspace with consistent flags" +inputs: + toolchain: + description: "Rust toolchain identifier to use (without the leading +)" + required: true + all-features: + description: "Whether to enable --all-features" + required: false + default: "false" + no-fail-fast: + description: "Whether to include --no-fail-fast" + required: false + default: "true" + extra-args: + description: "Additional arguments passed to cargo test" + required: false + default: "" +runs: + using: "composite" + steps: + - name: Run cargo test + shell: bash + env: + TOOLCHAIN: ${{ inputs.toolchain }} + ALL_FEATURES: ${{ inputs.all-features }} + NO_FAIL_FAST: ${{ inputs.no-fail-fast }} + EXTRA_ARGS: ${{ inputs.extra-args }} + run: | + set -euo pipefail + args=("--workspace") + if [ "${ALL_FEATURES}" = "true" ]; then + args+=("--all-features") + fi + if [ "${NO_FAIL_FAST}" = "true" ]; then + args+=("--no-fail-fast") + fi + if [ -n "${EXTRA_ARGS}" ]; then + # shellcheck disable=SC2206 + extra_parts=(${EXTRA_ARGS}) + args+=("${extra_parts[@]}") + fi + cargo +"${TOOLCHAIN}" test "${args[@]}" + diff --git a/.github/workflows/reusable-ci.yml b/.github/workflows/reusable-ci.yml index d84e676..7d88bef 100644 --- a/.github/workflows/reusable-ci.yml +++ b/.github/workflows/reusable-ci.yml @@ -168,39 +168,30 @@ jobs: delete-branch: true # ---------- end README handling ---------- - - name: Check formatting (nightly rustfmt) - run: cargo +nightly fmt --all -- --check - - - name: Clippy (MSRV) - shell: bash - run: | - set -euo pipefail - if [ "${{ inputs.all-features }}" = "true" ]; then - cargo +${{ steps.msrv.outputs.msrv }} clippy --workspace --all-targets --all-features -- -D warnings - else - cargo +${{ steps.msrv.outputs.msrv }} clippy --workspace --all-targets -- -D warnings - fi - - - name: Cargo deny - uses: ./.github/actions/cargo-deny - - - name: Tests (MSRV) - shell: bash - run: | - set -euo pipefail - if [ "${{ inputs.all-features }}" = "true" ]; then - cargo +${{ steps.msrv.outputs.msrv }} test --workspace --all-features --no-fail-fast - else - cargo +${{ steps.msrv.outputs.msrv }} test --workspace --no-fail-fast - fi - - - name: Install cargo-audit - run: cargo install --locked cargo-audit - - - name: Security audit - run: cargo audit --deny warnings - - - name: Auto-commit README changes (any branch) + - name: Check formatting (nightly rustfmt) + uses: ./.github/actions/cargo-fmt + with: + toolchain: nightly + + - name: Clippy (MSRV) + uses: ./.github/actions/cargo-clippy + with: + toolchain: ${{ steps.msrv.outputs.msrv }} + all-features: ${{ inputs.all-features }} + + - name: Cargo deny + uses: ./.github/actions/cargo-deny + + - name: Tests (MSRV) + uses: ./.github/actions/cargo-test + with: + toolchain: ${{ steps.msrv.outputs.msrv }} + all-features: ${{ inputs.all-features }} + + - name: Security audit + uses: ./.github/actions/cargo-audit + + - name: Auto-commit README changes (any branch) if: always() run: | set -euo pipefail diff --git a/docs/wiki/ci.md b/docs/wiki/ci.md new file mode 100644 index 0000000..06429ba --- /dev/null +++ b/docs/wiki/ci.md @@ -0,0 +1,51 @@ +# Continuous integration recipes + +The workspace exposes reusable GitHub composite actions so multiple workflows can +share the same cargo setup without copying command bodies. Each composite lives +under [`.github/actions`](../../.github/actions) and assumes the caller already +prepared the toolchain, caches, and workspace checkout. + +## Available composites + +| Action | Purpose | Required inputs | +| ------ | ------- | ---------------- | +| `./.github/actions/cargo-fmt` | Runs `cargo fmt -- --check` with the requested toolchain. | `toolchain` (defaults to `nightly`). | +| `./.github/actions/cargo-clippy` | Executes `cargo clippy --workspace --all-targets`, optionally enabling `--all-features`. | `toolchain` (MSRV or other installed toolchain). | +| `./.github/actions/cargo-test` | Executes `cargo test --workspace` with optional `--all-features` and `--no-fail-fast`. | `toolchain` (MSRV or other installed toolchain). | +| `./.github/actions/cargo-audit` | Installs (if required) and runs `cargo audit` with `--deny warnings` by default. | None. | + +## Usage example + +After the shared setup steps (checkout, Rust toolchain install, cache restore), +call the composites in sequence: + +```yaml +jobs: + lint-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: 1.70.0 + - name: Cargo cache + uses: Swatinem/rust-cache@v2 + - name: Run fmt + uses: ./.github/actions/cargo-fmt + with: + toolchain: nightly + - name: Run clippy + uses: ./.github/actions/cargo-clippy + with: + toolchain: 1.70.0 + all-features: true + - name: Run tests + uses: ./.github/actions/cargo-test + with: + toolchain: 1.70.0 + - name: Security audit + uses: ./.github/actions/cargo-audit +``` + +Each composite forwards optional inputs like `extra-args` so teams can tailor +command flags without duplicating the shell logic. diff --git a/docs/wiki/index.md b/docs/wiki/index.md index a482f71..2124e70 100644 --- a/docs/wiki/index.md +++ b/docs/wiki/index.md @@ -8,6 +8,7 @@ section that matches your experience level. - [Building applications with `masterror`](masterror-application-guide.md) - [When to reach for `thiserror`, `anyhow`, or `masterror`](error-crate-comparison.md) - [Patterns and troubleshooting](patterns-and-troubleshooting.md) +- [Continuous integration recipes](ci.md) ## How the wiki is organised