Skip to content

Commit a02b508

Browse files
committed
Simplify CI and dependency management
Rework the dependency flow to use a dedicated `dependencies` branch with lite CI, while the main CI runs the full test matrix only on `master`. Add a weekly security audit and branch sync workflow. Remove branch protection ruleset documentation in favor of simpler setup steps.
1 parent e2bc558 commit a02b508

1 file changed

Lines changed: 80 additions & 80 deletions

File tree

README.md

Lines changed: 80 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,70 @@
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

89
1. Click "Use this template" -> "Create a new repository" on GitHub.
910
2. 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

126111
Each publish workflow is triggered manually with "Use workflow from:
127112
tags/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

134121
Publish is manual on purpose, so a tag without a published artifact is allowed.
135122
If you switch to auto-publishing on tag push, you will need a PAT: a tag pushed
136123
with `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

147141
Add 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

156156
The GitHub Release step and GHCR publish use the built-in `GITHUB_TOKEN`
157157
(`contents: write` and `packages: write`). No extra secret.

0 commit comments

Comments
 (0)