11# rust-template
22
3- GitHub template repository for new Rust projects. Provides CI, dependabot,
4- release tagging and three publish workflows out of the box.
3+ GitHub template repository for new Rust projects. Provides CI, a staging
4+ branch for dependabot updates, a scheduled security audit, release tagging
5+ and three publish workflows out of the box.
56
67## Use it
78
891 . Click "Use this template" -> "Create a new repository" on GitHub.
9102 . Rename the package in ` Cargo.toml ` and update the binary path in ` Dockerfile `
1011 (the ` target/release/rust-template ` line).
11- 3 . Add the secrets listed in [ Secrets] ( #secrets ) .
12- 4 . Protect ` master ` by importing a ruleset - see [ Branch protection] ( #branch-protection ) .
12+ 3 . Adjust the committer identity in ` bump-and-release.yml ` (the "Configure git"
13+ step) - it is hardcoded to the template author.
14+ 4 . Create a ` dependencies ` branch from ` master ` : dependabot targets it, and the
15+ sync workflow requires it to exist.
16+ 5 . Add the secrets listed in [ Secrets] ( #secrets ) .
1317
1418## Dependency flow
1519
16- Dependabot opens PRs straight into ` master ` . Each PR runs the full ` ci.yml `
17- (fmt, clippy, the test matrix and a release build), so a bump is merged only
18- when it is green. Branch protection keeps ` master ` green even when several PRs
19- land close together - see [ Branch protection ] ( #branch-protection ) .
20+ Dependabot opens weekly PRs into the ` dependencies ` branch, where updates
21+ accumulate away from the trunk. Each PR is gated by the lite CI
22+ ( ` ci-dependencies.yml ` ): the reusable checks plus a single test run - enough to
23+ catch a broken bump without spending the full matrix on every bot PR .
2024
21- There is no separate dependencies branch. In Rust the compiler, ` clippy ` and
22- the test matrix catch breakage from a bump at PR time, so a holding branch adds
23- machinery without adding safety.
25+ On every push to ` master ` the sync workflow merges the trunk into
26+ ` dependencies ` ([ branch-sync-action] ): a clean merge is pushed, a lockfile-only
27+ conflict is regenerated automatically, a source conflict opens a PR to resolve
28+ by hand. Delivery back to the trunk is a single reviewed PR
29+ ` dependencies -> master ` , which runs the full ` ci.yml ` .
2430
25- ## Branch protection
26-
27- ` master ` should require a passing CI before a merge. ` ci.yml ` ends with a
28- ` CI success ` job that depends on every other job - require that single check
29- rather than the individual matrix jobs, whose names change with the matrix.
30- The job name ` CI success ` and the ruleset's required-check context are one
31- contract: rename one without the other and the required check stays pending
32- forever.
33-
34- Two ready-to-import rulesets live in ` .github/rulesets/ ` . Import one under
35- Settings -> Rules -> Rulesets -> "Import a ruleset":
36-
37- | Ruleset | Use it for | Catches interaction breakage by |
38- | ---------------------------| -----------------------------------------| ------------------------------------------------------------------------|
39- | ` master.json ` | personal repos, or orgs without a queue | requiring branches to be up to date before merge (re-runs CI on the rebased PR) |
40- | ` master-merge-queue.json ` | organization repos | a merge queue that tests each PR combined with the ones ahead of it |
41-
42- Both rulesets let the repository admin (built-in ` RepositoryRole ` id 5) bypass
43- the rule, so ` bump-and-release ` can push the version commit with
44- ` RELEASE_TOKEN ` . The token must belong to an account with the admin role -
45- otherwise the bump push is blocked by the required check, which never runs on a
46- direct push. On an org with custom roles, verify the bypass actor id.
47-
48- Merge queue is available only on organization-owned repositories, not on
49- personal accounts. ` ci.yml ` already triggers on ` merge_group ` , so enabling the
50- queue on an org repo is a single setting. Required status checks can also be
51- applied org-wide through an organization-level ruleset; the merge queue rule
52- itself is repository-level only.
31+ [ branch-sync-action ] : https://github.com/nerjs/branch-sync-action
5332
5433## Workflows
5534
56- | File | Trigger | Purpose |
57- | ----------------------------| ----------------------------------------| ---------------------------------------------------------|
58- | ` ci.yml ` | push/PR on master, merge queue, manual | fmt, clippy, test matrix (rust x distro), release build |
59- | ` bump-and-release.yml ` | manual | bump version, tag, optional GitHub Release |
60- | ` publish-crates.yml ` | manual on a tag | publish to crates.io |
61- | ` publish-docker-hub.yml ` | manual on a tag | build and push image to Docker Hub, multi-arch |
62- | ` publish-ghcr.yml ` | manual on a tag | build and push image to ghcr.io, multi-arch |
35+ | File | Trigger | Purpose |
36+ | --------------------------| ---------------------------------------| ------------------------------------------------|
37+ | ` ci.yml ` | push/PR on master, manual | checks -> test matrix -> release build |
38+ | ` checks.yml ` | workflow_call | fmt + clippy + ` cargo check ` , reused as a gate |
39+ | ` ci-dependencies.yml ` | push/PR on dependencies, manual | lite CI for dependency bumps: checks + tests |
40+ | ` sync-dependencies.yml ` | push on master, manual | merge master into the dependencies branch |
41+ | ` audit.yml ` | weekly cron, manifest changes, manual | ` cargo audit ` against the RustSec advisory DB |
42+ | ` bump-and-release.yml ` | manual | bump version, tag, GitHub Release |
43+ | ` publish-crates.yml ` | manual on a tag | publish to crates.io |
44+ | ` publish-docker-hub.yml ` | manual on a tag | build and push image to Docker Hub, multi-arch |
45+ | ` publish-ghcr.yml ` | manual on a tag | build and push image to ghcr.io, multi-arch |
6346
64- ### Test matrix
47+ ### CI
6548
66- ` fmt ` and ` clippy ` run in parallel, then the ` test ` matrix, then ` build ` .
67- ` ci.yml ` runs ` cargo test --all-features ` across:
49+ ` checks.yml ` is the shared entry gate: ` fmt ` , ` clippy ` and ` cargo check ` run in
50+ parallel on the toolchain from ` rust-toolchain.toml ` . ` ci.yml ` chains it into
51+ the test matrix and finishes with a release build.
6852
69- - rust: ` stable ` , ` beta ` , ` 1.95 ` , ` 1.94 ` , ` 1.93 ` , ` 1.92 ` - the moving channels
70- plus the dev version and the three below it
71- - env: ` ubuntu-latest ` , ` debian:12 ` , ` archlinux:latest ` , ` rust:1-alpine `
53+ ` cargo test --all-features ` runs across:
7254
73- 24 matrix jobs, ` fail-fast: false ` . Distros run via ` container: ` . The dev
74- version lives in ` rust-toolchain.toml ` (used by local builds, the Dockerfile,
75- ` fmt ` /` clippy ` /` build ` and the publish gate); the matrix pins it plus the three
76- below. Raise the pinned list and ` rust-toolchain.toml ` by hand when the floor
77- moves. The pinned versions test the toolchains you develop and ship on; ` stable `
78- and ` beta ` catch upcoming-release breakage early. ` beta ` is ` continue-on-error `
79- - an upstream regression there does not block the merge queue.
55+ - rust: ` stable ` and ` beta ` ; ` beta ` is ` continue-on-error ` , so an upcoming
56+ release regression is visible without failing the run
57+ - env: ` ubuntu-latest ` and ` archlinux:latest ` (via ` container: ` , with a pacman
58+ prep step before checkout)
8059
81- Keep ` rust-toolchain.toml ` : ` fmt ` /` clippy ` /` build ` , the publish gate and the
82- Dockerfile have no explicit toolchain step and fall back to the runner's
83- bundled Rust if it is removed.
60+ 4 jobs, ` fail-fast: false ` . The job-level ` RUSTUP_TOOLCHAIN ` overrides
61+ ` rust-toolchain.toml ` , and ` dtolnay/rust-toolchain ` installs the requested
62+ version - the arch container has no preinstalled Rust.
63+
64+ Keep ` rust-toolchain.toml ` : every job outside the test matrix (checks, the lite
65+ CI, ` build ` , the bump and publish gates) carries no toolchain step, as does the
66+ Dockerfile. Remove the file and they silently fall back to the runner's bundled
67+ Rust.
8468
8569### Bump and release
8670
@@ -114,30 +98,40 @@ the action:
11498- prerelease vs full GitHub Release is chosen by the ` -beta.N ` suffix in the
11599 tag, not a separate flag.
116100
117- A ` checks ` job (fmt, clippy, tests via the reusable ` checks.yml ` ) runs first;
118- nothing is bumped or tagged if it fails. Then ` cargo set-version ` bumps every
119- crate (Cargo.lock follows), the job commits the changed manifests, tags
120- ` vX.Y.Z[-beta.N] ` and pushes commit and tag to the selected branch. Pushing into
121- a protected ` master ` needs ` RELEASE_TOKEN ` ; ` GITHUB_TOKEN ` cannot bypass branch
122- rules. A second job creates a GitHub Release through ` GITHUB_TOKEN ` .
101+ The checks gate and a test run come first; nothing is bumped or tagged if they
102+ fail. Then ` cargo set-version ` bumps every crate (Cargo.lock follows), the job
103+ commits the changed manifests, tags ` vX.Y.Z[-beta.N] ` and pushes commit and tag
104+ to the selected branch. The push uses ` RELEASE_TOKEN ` : a push made with the
105+ default ` GITHUB_TOKEN ` does not trigger other workflows, so CI and the
106+ dependencies sync would skip the bump commit. A second job creates a GitHub
107+ Release through ` GITHUB_TOKEN ` .
123108
124109### Publish
125110
126111Each publish workflow is triggered manually with "Use workflow from:
127112tags/vX.Y.Z". A guard rejects runs from a branch. Each one first runs the
128- reusable ` checks.yml ` (fmt, clippy, tests on the dev toolchain) against the
129- tagged commit and publishes only if it passes - so a tag that never went
130- through PR CI cannot ship a broken artifact. The full matrix stays on PRs.
131- Docker tags are derived from the tag name; ` latest ` is
132- set only for non-beta tags.
113+ reusable checks against the tagged commit and publishes only if they pass;
114+ tests are not repeated at publish time - the tag is expected to point at a
115+ commit that already went through CI.
116+
117+ ` publish-crates ` takes a ` dry_run ` flag, ` publish-docker-hub ` asks for the
118+ image name (` namespace/name ` ), GHCR derives everything from the repository.
119+ Docker tags come from the tag name; ` latest ` is set only for non-beta tags.
133120
134121Publish is manual on purpose, so a tag without a published artifact is allowed.
135122If you switch to auto-publishing on tag push, you will need a PAT: a tag pushed
136123with ` GITHUB_TOKEN ` does not trigger other workflows.
137124
125+ ### Audit
126+
127+ ` cargo audit ` checks the dependency tree against the RustSec advisory database
128+ every Monday at 06:00 UTC, on any push touching ` Cargo.toml ` /` Cargo.lock ` , and
129+ on demand. The schedule surfaces new advisories even when the repository is
130+ quiet.
131+
138132## Dependabot
139133
140- Two ecosystems, both opening PRs into ` master ` :
134+ Two ecosystems, both opening PRs into the ` dependencies ` branch :
141135
142136- ` cargo ` - weekly, max 5 open PRs, minor+patch grouped, major opens its own PR
143137- ` github-actions ` - weekly, max 5 open PRs, same grouping
@@ -146,12 +140,18 @@ Two ecosystems, both opening PRs into `master`:
146140
147141Add these in repository settings before using the relevant workflow:
148142
149- | Secret | Used by | Notes |
150- | ------------------------| --------------------| ------------------------------------------------------------------------|
151- | ` RELEASE_TOKEN ` | bump-and-release | PAT with ` contents: write ` . Lets the bump commit reach protected ` master ` ; ` GITHUB_TOKEN ` cannot. |
152- | ` CARGO_REGISTRY_TOKEN ` | publish-crates | crates.io API token |
153- | ` DOCKERHUB_USERNAME ` | publish-docker-hub | Docker Hub login |
154- | ` DOCKERHUB_TOKEN ` | publish-docker-hub | Docker Hub access token |
143+ | Secret | Used by | Notes |
144+ | ------------------------| -------------------------------------| -------------------------|
145+ | ` RELEASE_TOKEN ` | bump-and-release, sync-dependencies | PAT, see below |
146+ | ` CARGO_REGISTRY_TOKEN ` | publish-crates | crates.io API token |
147+ | ` DOCKERHUB_USERNAME ` | publish-docker-hub | Docker Hub login |
148+ | ` DOCKERHUB_TOKEN ` | publish-docker-hub | Docker Hub access token |
149+
150+ ` RELEASE_TOKEN ` is a PAT with contents and pull requests read/write (classic:
151+ ` repo ` ), plus the ` workflow ` scope - a sync can carry changes under
152+ ` .github/workflows ` . A PAT rather than ` GITHUB_TOKEN ` because pushes made with
153+ the default token do not trigger the workflows that must run on the pushed
154+ commits: CI on the bump commit, the lite CI on a synced ` dependencies ` .
155155
156156The GitHub Release step and GHCR publish use the built-in ` GITHUB_TOKEN `
157157(` contents: write ` and ` packages: write ` ). No extra secret.
0 commit comments