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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
name: Auto Release
# 推送规则:push 到 main 后,读取 Cargo.toml 最新版本,
# 若对应 tag/release 尚不存在则增量创建(已存在则跳过)。
# 不打包应用 —— 仅 tag + release notes。
#
# 创建了新 tag 时,再用同一个版本号发一次 crates.io。
# 需要仓库 secret `CARGO_REGISTRY_TOKEN`(https://crates.io/settings/tokens);
# 未配置则该步骤直接失败 —— 此时 tag 与 release 已创建,属于必须人工补发的状态,
# 不能报绿(重跑本 workflow 也救不回来:tag 已存在,publish job 会被 created=false 跳过)。
#
# 发布前必须先过 `test` job(测试 / clippy / 声明的最低 Rust 版本)。
on:
push:
branches:
permissions:
contents: write
jobs:
# 发布前的唯一验证关卡。此前没有任何 CI 跑过测试,版本号一改就直接 tag + publish。
test:
runs-on: ubuntu-latest
# 只读即可(最小权限)
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Ensure recent stable toolchain
run: rustup update stable --no-self-update && rustup default stable && rustup component add clippy
- name: Test
run: cargo test
- name: Clippy
run: cargo clippy --all-targets -- -D warnings
# 用 Cargo.toml 里声明的 rust-version 再建一次,确保「声明的 MSRV」与「源码
# 实际用到的特性」一致 —— 曾经声明 1.87,而源码用了 1.88 才稳定的 let-chains,
# 导致 1.87 的用户被 cargo 主动选中本 crate 后编译失败。
- name: Check declared MSRV
run: |
MSRV=$(grep -m1 '^rust-version' Cargo.toml | cut -d'"' -f2)
echo "声明的 MSRV:${MSRV}"
rustup toolchain install "${MSRV}" --profile minimal --no-self-update
cargo "+${MSRV}" check --all-targets
release:
runs-on: ubuntu-latest
outputs:
created: ${{ steps.tag.outputs.created }}
tag: ${{ steps.version.outputs.tag }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Read latest version from Cargo.toml
id: version
run: echo "tag=v$(grep -m1 '^version' Cargo.toml | cut -d'"' -f2)" >> "$GITHUB_OUTPUT"
- name: Create tag and release (incremental)
id: tag
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.version.outputs.tag }}
run: |
if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
echo "Tag ${TAG} already exists — skip (incremental)"
echo "created=false" >> "$GITHUB_OUTPUT"
exit 0
fi
git tag "${TAG}"
git push origin "refs/tags/${TAG}"
gh release create "${TAG}" \
--title "${TAG}" \
--generate-notes
echo "created=true" >> "$GITHUB_OUTPUT"
publish:
# 测试没过就不发布。tag/release 由上面的 job 创建(保持原有逻辑不动),
# 但 crates.io 这一步被 test 卡住。
needs:
# 只在本次真的创建了新 tag 时发布,避免每次 push 都重试上传
if: needs.release.outputs.created == 'true'
runs-on: ubuntu-latest
# cargo publish 用不上 GITHUB_TOKEN,只读即可(最小权限)
permissions:
contents: read
steps:
- uses: actions/checkout@v4
# 用 runner 自带的 rustup 装 stable,不引入第三方 action。
# 本 crate 是 edition 2024(rust-version 1.88),必须确保工具链够新。
- name: Ensure recent stable toolchain
run: rustup update stable --no-self-update && rustup default stable
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
TAG: ${{ needs.release.outputs.tag }}
run: |
if [ -z "$CARGO_REGISTRY_TOKEN" ]; then
echo "::error::未配置 CARGO_REGISTRY_TOKEN —— tag ${TAG} 与 GitHub release 已创建,但未发布到 crates.io。请补上 secret 后手动 cargo publish(重跑本 workflow 无效:tag 已存在,publish job 会被 created=false 跳过)。"
exit 1
fi
echo "发布 ${TAG} 到 crates.io"
cargo publish