diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 1962c697..11c6b5dd 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -22,6 +22,18 @@ inputs: cli: description: Install CLI dependencies if `true`. Defaults to `false`. required: false + cargo-release: + description: Install cargo-release if `true`. Defaults to `false`. + required: false + cargo-semver-checks: + description: Install cargo-semver-checks if `true`. Defaults to `false`. + required: false + cargo-audit: + description: Install cargo-audit if `true`. Defaults to `false`. + required: false + cargo-spellcheck: + description: Install cargo-spellcheck if `true`. Defaults to `false`. + required: false purge: description: Purge unused directories if `true`. Defaults to `false`. required: false @@ -93,6 +105,30 @@ runs: sudo apt-get update sudo apt-get install -y libudev-dev protobuf-compiler clang libclang-dev + - name: Install cargo-release + if: ${{ inputs.cargo-release == 'true' }} + uses: taiki-e/install-action@v2 + with: + tool: cargo-release + + - name: Install cargo-semver-checks + if: ${{ inputs.cargo-semver-checks == 'true' }} + uses: taiki-e/install-action@v2 + with: + tool: cargo-semver-checks + + - name: Install cargo-audit + if: ${{ inputs.cargo-audit == 'true' }} + uses: taiki-e/install-action@v2 + with: + tool: cargo-audit + + - name: Install cargo-spellcheck + if: ${{ inputs.cargo-spellcheck == 'true' }} + uses: taiki-e/install-action@v2 + with: + tool: cargo-spellcheck + - name: Cache Cargo Dependencies if: ${{ inputs.cargo-cache-key && !inputs.cargo-cache-fallback-key }} uses: actions/cache@v4 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6afbd7e2..1868f497 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,7 +4,7 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] + workflow_call: jobs: format_and_lint_programs: @@ -56,35 +56,11 @@ jobs: uses: ./.github/actions/setup with: cargo-cache-key: cargo-audit - - - name: Install cargo-audit - uses: taiki-e/install-action@v2 - with: - tool: cargo-audit + cargo-audit: true - name: Run cargo-audit run: pnpm rust:audit - semver_rust: - name: Check semver Rust - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - cargo-cache-key: cargo-semver - - - name: Install cargo-audit - uses: taiki-e/install-action@v2 - with: - tool: cargo-semver-checks - - - name: Run semver checks - run: pnpm rust:semver - spellcheck_rust: name: Spellcheck Rust runs-on: ubuntu-latest @@ -96,11 +72,7 @@ jobs: uses: ./.github/actions/setup with: cargo-cache-key: cargo-spellcheck - - - name: Install cargo-spellcheck - uses: taiki-e/install-action@v2 - with: - tool: cargo-spellcheck + cargo-spellcheck: true - name: Run cargo-spellcheck run: pnpm rust:spellcheck diff --git a/.github/workflows/rust-publish.yml b/.github/workflows/rust-publish.yml new file mode 100644 index 00000000..ab903ffd --- /dev/null +++ b/.github/workflows/rust-publish.yml @@ -0,0 +1,153 @@ +name: Publish Rust Crate + +on: + workflow_dispatch: + inputs: + package_path: + description: Path to directory with package to release + required: true + default: 'clients/cli' + type: choice + options: + - clients/cli + - program + level: + description: Level + required: true + default: patch + type: choice + options: + - patch + - minor + - major + - rc + - beta + - alpha + - release + - version + version: + description: Version (used with level "version") + required: false + type: string + dry_run: + description: Dry run + required: true + default: true + type: boolean + create_release: + description: Create a GitHub release + required: true + type: boolean + default: true + +jobs: + main-check: + name: Main check + uses: ./.github/workflows/main.yml + + semver: + name: Check semver after bump + runs-on: ubuntu-latest + steps: + - name: Git Checkout + uses: actions/checkout@v4 + + - name: Setup Environment + uses: ./.github/actions/setup + with: + cargo-cache-key: cargo-semver + cargo-release: true + cargo-semver-checks: true + + - name: Set Git Author (required for cargo-release) + run: | + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + + - name: Set Version + run: | + if [ "${{ inputs.level }}" == "version" ]; then + LEVEL=${{ inputs.version }} + else + LEVEL=${{ inputs.level }} + fi + cargo release $LEVEL --manifest-path "${{ inputs.package_path }}/Cargo.toml" --no-tag --no-publish --no-push --no-confirm --execute + + # only successfully runs if crate is library & is not first publish + - name: Check semver + if: ${{ inputs.package_path != 'clients/cli' && inputs.level != 'release' }} + run: pnpm rust:semver --manifest-path "${{ inputs.package_path }}/Cargo.toml" + + publish: + name: Publish Rust Crate + runs-on: ubuntu-latest + needs: [ main-check, semver ] + permissions: + contents: write + steps: + - name: Git Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.ANZA_TEAM_PAT }} + fetch-depth: 0 # get the whole history for git-cliff + + - name: Setup Environment + uses: ./.github/actions/setup + with: + cli: true + cargo-cache-key: cargo-publish-${{ inputs.package_path }} + cargo-cache-fallback-key: cargo-publish + cargo-release: true + + - name: Ensure CARGO_REGISTRY_TOKEN variable is set + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + if: ${{ env.CARGO_REGISTRY_TOKEN == '' }} + run: | + echo "The CARGO_REGISTRY_TOKEN secret variable is not set" + echo "Go to \"Settings\" -> \"Secrets and variables\" -> \"Actions\" -> \"New repository secret\"." + exit 1 + + - name: Set Git Author + run: | + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + + - name: Publish Crate + id: publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: | + if [ "${{ inputs.level }}" == "version" ]; then + LEVEL=${{ inputs.version }} + else + LEVEL=${{ inputs.level }} + fi + + if [ "${{ inputs.dry_run }}" == "true" ]; then + OPTIONS="--dry-run" + else + OPTIONS="" + fi + + pnpm rust:publish "${{ inputs.package_path }}" $LEVEL $OPTIONS + + - name: Generate a changelog + if: github.event.inputs.create_release == 'true' + uses: orhun/git-cliff-action@v3 + with: + config: "scripts/cliff.toml" + args: | + "${{ steps.publish.outputs.old_git_tag }}"..main + --include-path "${{ inputs.package_path }}/**" + --github-repo "${{ github.repository }}" + env: + OUTPUT: TEMP_CHANGELOG.md + GITHUB_REPO: ${{ github.repository }} + + - name: Create GitHub release + if: github.event.inputs.create_release == 'true' && github.event.inputs.dry_run != 'true' + uses: ncipollo/release-action@v1 + with: + tag: ${{ steps.publish.outputs.new_git_tag }} + bodyFile: TEMP_CHANGELOG.md diff --git a/package.json b/package.json index 32651b42..8577c50e 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "template:upgrade": "zx ./scripts/upgrade-template.mjs", "rust:spellcheck": "cargo spellcheck --code 1", "rust:audit": "zx ./scripts/rust/audit.mjs", + "rust:publish": "zx ./scripts/rust/publish.mjs", "rust:semver": "cargo semver-checks", "clients:cli:test": "zx ./scripts/rust/test.mjs clients/cli", "clients:cli:format": "zx ./scripts/rust/format.mjs clients/cli", diff --git a/scripts/cliff.toml b/scripts/cliff.toml new file mode 100644 index 00000000..db991f1e --- /dev/null +++ b/scripts/cliff.toml @@ -0,0 +1,58 @@ +# git-cliff configuration file +# https://git-cliff.org/docs/configuration +# +# Lines starting with "#" are comments. +# Configuration options are organized into tables and keys. +# See documentation for more information on available options. + +[changelog] +header = """ +## What's new +""" +# template for the changelog body +# https://tera.netlify.app/docs +body = """ +{% for group, commits in commits | group_by(attribute="group") %}\ + {% for commit in commits %} + - {{ commit.message | upper_first | split(pat="\n") | first | trim }}\ + {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif %}\ + {% endfor %}\ +{% endfor %} +""" +# remove the leading and trailing whitespace from the template +trim = true +footer = """ +""" +postprocessors = [] +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = false +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [] +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^build\\(deps\\)", skip = true }, + { message = "^build\\(deps-dev\\)", skip = true }, + { message = "^ci", skip = true }, + { body = ".*", group = "Changes" }, +] +# protect breaking changes from being skipped due to matching a skipping commit_parser +protect_breaking_commits = false +# filter out the commits that are not matched by commit parsers +filter_commits = false +# glob pattern for matching git tags +tag_pattern = "v[0-9]*" +# regex for skipping tags +skip_tags = "" +# regex for ignoring tags +ignore_tags = "" +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "newest" +# limit the number of commits included in the changelog. +# limit_commits = 42