Skip to main content

Rust CI/CD Workflows

Generated by rust-ci-library, which is composed by all three Rust service archetypes:

  • rust-rest-service-archetype
  • rust-grpc-service-archetype
  • rust-graphql-service-archetype

The protocol has no effect on the pipeline - all three render the same two workflow files.

Build Workflow

.github/workflows/build.yaml, shown as rendered for a project named billing-service:

name: Build

on:
push:
branches: ["**"]
pull_request:

permissions:
contents: write
id-token: write

env:
IMAGE_NAME: billing-service
APPLICATION_NAME: billing-service

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: p6m-actions/rust-setup@v1

- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler

- name: Cut Patch Version
if: github.ref_name == 'main'
id: cut-patch
uses: p6m-actions/rust-cut-tag@v1
with:
version-level: patch

- name: Build
uses: p6m-actions/rust-build@v1

# ... shared container build, release and dispatch steps

Step Chain

#StepScopeRuns on
1CheckoutSharedAlways
2Rust setupRustAlways
3Install protocRust onlyAlways
4Cut Patch VersionRustmain
5BuildRustAlways
6Login to Artifactory Container RegistrySharedmain
7Set up Docker BuildxSharedAlways
8Build and Publish Docker ImageSharedAlways
9Make ArtifactsSharedmain
10Create Github releaseSharedmain
11Update Application ManifestSharedmain

Rust is the only language that installs a system package before building.

Language-Specific Steps

Rust Setup

- uses: p6m-actions/rust-setup@v1

p6m-actions/rust-setup installs a Rust toolchain and restores the cargo registry and target/ caches.

InputDefaultPassed by the workflow
toolchainstable(default)
components(empty)(default)
targets(empty)(default)
cachetrue(default) - cargo registry and build caching
cache-key-suffix(empty)(default)
install-dependenciesfalse(default) - no separate cargo fetch
working-directory.(default)
OutputDescription
rustc-versionThe installed rustc version
cargo-versionThe installed cargo version
cache-hitWhether the cargo cache was restored
clippy and rustfmt are not requested explicitly

The build step runs cargo clippy and cargo fmt by default, which works because the GitHub-hosted runner image ships both with its preinstalled stable toolchain. If you pin toolchain to a specific version, add components: clippy, rustfmt so they are installed for that toolchain.

Install protoc

- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
InputsNone
Outputsprotoc on PATH

prost-build and tonic-build shell out to protoc at compile time, and GitHub's ubuntu-latest runners do not ship it. Without this step, the first CI run of a freshly generated gRPC service fails during cargo build.

The step runs unconditionally in all three Rust archetypes, including REST and GraphQL where the build may not need it. That is deliberate: one workflow file serves all three protocols, and a few seconds of apt is cheaper than a per-protocol divergence.

Cut Patch Version

- name: Cut Patch Version
if: github.ref_name == 'main'
id: cut-patch
uses: p6m-actions/rust-cut-tag@v1
with:
version-level: patch

p6m-actions/rust-cut-tag uses cargo-release to bump Cargo.toml, commit, and tag.

InputRequiredValue
version-levelNopatch in build.yaml; the dispatch input in cut-tag.yaml
working-directoryNo. (default)
commit-changesNotrue (default)
commit-messageNoBump version to {version} [skip ci] (default)
skip-pushNofalse (default)
workspaceNotrue (default) - applies to all workspace members
OutputDescription
versionThe new version, e.g. 1.4.2
tagThe created git tag

The workspace default of true matters here. The Rust archetypes render a Cargo workspace whose members vary by protocol - *_bin, *_core and *_persistence in all three, plus *_client and *_server for gRPC and *_schema for GraphQL, alongside an xtask crate - and all members are bumped together.

The action runs p6m-actions/token-exchange@v2 first for App-token authentication and the [skip ci] commit hook, then installs cargo-release via baptiste0928/cargo-install@v3.

Build

- name: Build
uses: p6m-actions/rust-build@v1

p6m-actions/rust-build runs format check, lint, test, and build. No inputs are overridden, so all defaults apply:

InputDefaultWhat runs
run-format-checktruecargo fmt -- --check
run-linttruecargo clippy -- -D warnings
run-testtruecargo test
run-buildtruecargo build --release
workspacefalseNot passed as --workspace
all-features / no-default-features / featuresoff / off / emptyDefault feature set
archive-coveragefalsetarget/coverage is not uploaded
OutputDescription
build-statussuccess or failure

Rust has the strictest defaults of the six languages. Both the formatting check and clippy -D warnings are hard gates on every branch: a stray unformatted line or a single clippy warning fails the build. Run cargo fmt and cargo clippy -- -D warnings locally before pushing.

Cut Tag Workflow

.github/workflows/cut-tag.yaml:

name: Cut Tag

on:
workflow_dispatch:
inputs:
version-level:
description: "Version bump level"
required: true
default: "patch"
type: choice
options:
- patch
- minor
- major

permissions:
id-token: write
contents: write

jobs:
cut-tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- uses: p6m-actions/rust-setup@v1

- uses: p6m-actions/rust-cut-tag@v1
with:
version-level: ${{ inputs.version-level }}
Inputversion-level - patch, minor, or major
OutputsA [skip ci] version bump commit across the workspace and a git tag, both pushed

Version File

Cargo.toml is the source of truth. In the workspace layout the version lives in the workspace root and members inherit it:

[workspace.package]
version = "1.4.2"