1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
name: Publish on crates.io
on:
release:
types:
# A release only emits `published` once, so a job that failed on an
# infrastructure problem could not be retried without deleting and recreating
# the release. Manual dispatch makes the publication replayable; pick the
# release tag as the ref so the guard below still runs.
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
env:
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
steps:
- name: Checkout sources
uses: actions/checkout@v5
with:
# Every tag is needed: the semver baseline below is the previous release.
fetch-depth: 0
# `Cargo.toml` is the version's source of truth: the bump is a reviewable
# commit, and the tag only confirms it.
#
# Skipped when dispatched from a branch instead of a tag: there is no tag to
# confirm anything, and comparing the manifest against a branch name would
# fail on every such run.
- name: Verify the tag matches Cargo.toml
if: github.ref_type == 'tag'
run: |
# A command substitution that fails does not stop the script under
# `set -e`, so an unreadable manifest would leave MANIFEST empty and
# report a mismatch against nothing. `pipefail` and the emptiness test
# make the guard say which of the two actually went wrong.
set -o pipefail
TAG=${{github.ref_name}}
MANIFEST=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[0].version')
test -n "$MANIFEST" || { echo "::error::could not read the version from Cargo.toml"; exit 1; }
if [ "$TAG" != "$MANIFEST" ]; then
echo "::error::tag $TAG does not match Cargo.toml version $MANIFEST — bump the manifest and re-tag"
exit 1
fi
- name: Install cargo-semver-checks
if: github.ref_type == 'tag'
uses: taiki-e/install-action@v2
with:
tool: cargo-semver-checks
# Whether the bump covers what changed in the public API. This is the release
# question -- the version is raised here, not on a branch -- so it is asked
# here rather than on every pull request, where it could only compare against
# a version nobody had raised yet and failed for reasons no author could act
# on. `compile_and_test.yml` reports the per-branch breaks; this decides
# whether the number is allowed to carry them.
#
# In 0.x cargo reads the minor field as the major one, so a break requires
# 0.(x+1).0 and a compatible change 0.x.(y+1). The feature set matches the
# report's, which is the one docs.rs builds.
- name: Verify the version bump covers the API changes
if: github.ref_type == 'tag'
run: |
set -o pipefail
BASELINE=$(git tag --list '[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname \
| grep -vFx "${{github.ref_name}}" | head -n 1)
if [ -z "$BASELINE" ]; then
echo "no previous release tag to compare against, nothing to verify"
exit 0
fi
echo "baseline: $BASELINE"
cargo semver-checks --baseline-rev "$BASELINE" \
--only-explicit-features \
--features tokio-runtime,tokio-rustls,pool,json,client-cache
- name: Publish crate
run: cargo publish --token ${{secrets.CRATES_IO_API_TOKEN}}