1
name: Release crates.io
2
3
+# This is the only workflow that reacts to a tag push. It verifies the tag,
4
+# publishes the crate, and then dispatches every distribution workflow with the
5
+# same tag. Wrappers never publish for a crate release that failed.
6
+
7
on:
8
push:
9
tags:
6
- - "v*"
10
+ - "v*.*.*"
11
workflow_dispatch:
12
+ inputs:
13
+ tag:
14
+ description: "Release tag (e.g. v0.4.0)"
15
+ required: true
16
17
permissions:
18
contents: read
19
+ actions: write
20
21
concurrency:
22
group: release-crates-${{ github.ref }}
35
name: Verify release
36
runs-on: ubuntu-latest
37
outputs:
29
- version: ${{ steps.package.outputs.version }}
30
- tag: ${{ steps.tag.outputs.tag }}
38
+ version: ${{ steps.release.outputs.version }}
39
+ tag: ${{ steps.release.outputs.tag }}
40
steps:
41
- name: Checkout
42
uses: actions/checkout@v6
43
+ with:
44
+ ref: ${{ github.event.inputs.tag || github.ref }}
45
+
46
+ - name: Read Rust toolchain
47
+ shell: bash
48
+ run: |
49
+ rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
50
+ if [ -z "$rust_toolchain" ]; then
51
+ echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
52
+ exit 1
53
+ fi
54
+ echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
55
56
- name: Install Rust toolchain
36
- uses: dtolnay/rust-toolchain@stable
57
+ uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
58
with:
59
+ toolchain: ${{ env.RUST_TOOLCHAIN }}
60
components: clippy, rustfmt
61
62
- name: Cache cargo registry and build artifacts
63
uses: Swatinem/rust-cache@v2
64
43
- - name: Read package version
44
- id: package
65
+ - name: Set the release version
66
+ id: release
67
shell: bash
68
run: |
47
- version="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n 1)"
48
- if [ -z "$version" ]; then
49
- echo "Failed to read package version from Cargo.toml" >&2
50
- exit 1
69
+ if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
70
+ release_tag="${GITHUB_REF_NAME}"
71
+ else
72
+ release_tag="${{ github.event.inputs.tag }}"
73
+ release_tag="${release_tag#refs/tags/}"
74
fi
52
- echo "version=$version" >> "$GITHUB_OUTPUT"
75
+ release_version="${release_tag#v}"
76
54
- - name: Read git tag version
55
- id: tag
77
+ echo "tag=${release_tag}" >> "$GITHUB_OUTPUT"
78
+ echo "version=${release_version}" >> "$GITHUB_OUTPUT"
79
+
80
+ - name: Ensure tag matches Cargo.toml version
81
shell: bash
82
run: |
58
- ref="${GITHUB_REF_NAME}"
59
- tag="${ref#v}"
60
- echo "tag=$tag" >> "$GITHUB_OUTPUT"
83
+ version="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n 1)"
84
+ if [ -z "$version" ]; then
85
+ echo "::error::Failed to read package version from Cargo.toml"
86
+ exit 1
87
+ fi
88
+ if [ "$version" != "${{ steps.release.outputs.version }}" ]; then
89
+ echo "::error::Cargo.toml is ${version} but the tag is ${{ steps.release.outputs.version }}"
90
+ exit 1
91
+ fi
92
62
- - name: Ensure tag matches Cargo.toml version
63
- if: github.event_name == 'push'
93
+ - name: Ensure the pub.dev wrapper matches the tag
94
shell: bash
95
run: |
66
- test "${{ steps.package.outputs.version }}" = "${{ steps.tag.outputs.tag }}"
96
+ pub_version="$(sed -n 's/^version: \(.*\)/\1/p' pub/onde_cli/pubspec.yaml | head -n 1)"
97
+ if [ "$pub_version" != "${{ steps.release.outputs.version }}" ]; then
98
+ echo "::error::pubspec.yaml is ${pub_version} but the tag is ${{ steps.release.outputs.version }}"
99
+ exit 1
100
+ fi
101
102
- name: Check formatting
103
run: cargo fmt --all -- --check
125
steps:
126
- name: Checkout
127
uses: actions/checkout@v6
128
+ with:
129
+ ref: ${{ github.event.inputs.tag || github.ref }}
130
+
131
+ - name: Read Rust toolchain
132
+ shell: bash
133
+ run: |
134
+ rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
135
+ echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
136
137
- name: Install Rust toolchain
96
- uses: dtolnay/rust-toolchain@stable
138
+ uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
139
+ with:
140
+ toolchain: ${{ env.RUST_TOOLCHAIN }}
141
142
- name: Cache cargo registry and build artifacts
143
uses: Swatinem/rust-cache@v2
145
- name: Publish crate
146
env:
147
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
104
- run: cargo publish --locked
148
+ shell: bash
149
+ run: |
150
+ version="${{ needs.verify.outputs.version }}"
151
+ if curl -sSf "https://crates.io/api/v1/crates/onde-cli/${version}" >/dev/null 2>&1; then
152
+ echo "onde-cli ${version} is already on crates.io, skipping publish"
153
+ exit 0
154
+ fi
155
+ cargo publish --locked
156
+
157
+ dispatch-distributions:
158
+ name: Trigger distribution workflows
159
+ needs: [verify, publish]
160
+ runs-on: ubuntu-latest
161
+
162
+ steps:
163
+ - name: Trigger GitHub release
164
+ uses: actions/github-script@v7
165
+ with:
166
+ script: |
167
+ await github.rest.actions.createWorkflowDispatch({
168
+ owner: context.repo.owner,
169
+ repo: context.repo.repo,
170
+ workflow_id: 'release-github.yml',
171
+ ref: '${{ needs.verify.outputs.tag }}',
172
+ inputs: {
173
+ tag: '${{ needs.verify.outputs.tag }}'
174
+ }
175
+ })
176
+
177
+ - name: Trigger npm release
178
+ uses: actions/github-script@v7
179
+ with:
180
+ script: |
181
+ await github.rest.actions.createWorkflowDispatch({
182
+ owner: context.repo.owner,
183
+ repo: context.repo.repo,
184
+ workflow_id: 'release-npm.yml',
185
+ ref: '${{ needs.verify.outputs.tag }}',
186
+ inputs: {
187
+ tag: '${{ needs.verify.outputs.tag }}'
188
+ }
189
+ })
190
+
191
+ - name: Trigger NuGet release
192
+ uses: actions/github-script@v7
193
+ with:
194
+ script: |
195
+ await github.rest.actions.createWorkflowDispatch({
196
+ owner: context.repo.owner,
197
+ repo: context.repo.repo,
198
+ workflow_id: 'release-nuget.yml',
199
+ ref: '${{ needs.verify.outputs.tag }}',
200
+ inputs: {
201
+ tag: '${{ needs.verify.outputs.tag }}'
202
+ }
203
+ })
204
+
205
+ - name: Trigger PyPI release
206
+ uses: actions/github-script@v7
207
+ with:
208
+ script: |
209
+ await github.rest.actions.createWorkflowDispatch({
210
+ owner: context.repo.owner,
211
+ repo: context.repo.repo,
212
+ workflow_id: 'release-pypi.yml',
213
+ ref: '${{ needs.verify.outputs.tag }}',
214
+ inputs: {
215
+ tag: '${{ needs.verify.outputs.tag }}'
216
+ }
217
+ })