Refactor release workflows for tag-based and manual releases
- Update release-crate.yml to support both tag and manual dispatch - Ensure correct version is checked out and verified before publishing - Add crates.io version existence check to avoid duplicate publishes - Use explicit Rust toolchain and cache setup for reproducible builds - Align release-npm and release-pypi workflows to accept release tag input and check out correct ref - Always build and publish only the smbcloud-cli package
Seto Elkahfi committed
Apr 6, 2026 at 11:46 UTC
16ffe217a81a69222cba47a1b4e3d3ba2e51ff71
3 files changed
+93
-11
.github/workflows/release-crate.yml
+80
-7
@@ -1,7 +1,17 @@
1
name: Crates.io Release
2
3
on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
workflow_dispatch:
8
+ inputs:
9
+ tag:
10
+ description: "Release tag (e.g. v0.3.35)"
11
+ required: true
12
+
13
+permissions:
14
+ contents: read
15
16
env:
17
CARGO_TERM_COLOR: always
@@ -15,15 +25,48 @@ env:
25
GH_OAUTH_REDIRECT_PORT: ${{ secrets.GH_OAUTH_REDIRECT_PORT }}
26
27
jobs:
18
- build:
28
+ publish:
29
+ name: Publish to crates.io
30
runs-on: ubuntu-latest
31
32
steps:
33
- name: Checkout
23
- uses: actions/checkout@v3
34
+ uses: actions/checkout@v6
35
+ with:
36
+ ref: ${{ github.event.inputs.tag || github.ref }}
37
+
38
+ - name: Set the release version
39
+ shell: bash
40
+ run: |
41
+ if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
42
+ release_version="${GITHUB_REF_NAME#v}"
43
+ else
44
+ release_version="${{ github.event.inputs.tag }}"
45
+ release_version="${release_version#v}"
46
+ fi
47
+
48
+ echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
49
+
50
+ - name: Read Rust toolchain
51
+ shell: bash
52
+ run: |
53
+ rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
54
+ if [ -z "$rust_toolchain" ]; then
55
+ echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
56
+ exit 1
57
+ fi
58
+
59
+ echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
60
+
61
+ - name: Install toolkit
62
+ uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
63
+ with:
64
+ toolchain: ${{ env.RUST_TOOLCHAIN }}
65
25
- - name: Setup Rust toolchain
26
- uses: dtolnay/rust-toolchain@stable
66
+ - name: Setup Rust cache
67
+ uses: Swatinem/rust-cache@v2
68
+ with:
69
+ key: publish
70
71
- name: Create .env file
72
run: |
@@ -31,7 +74,37 @@ jobs:
74
echo CLI_CLIENT_SECRET=${{ secrets.CLI_CLIENT_SECRET }} >> .env
75
cat .env
76
77
+ - name: Verify Cargo.toml version matches tag
78
+ shell: bash
79
+ run: |
80
+ CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 \
81
+ | jq -r '.packages[] | select(.name == "smbcloud-cli") | .version')
82
+
83
+ echo "Cargo.toml version : ${CARGO_VERSION}"
84
+ echo "Release tag version: ${RELEASE_VERSION}"
85
+
86
+ if [[ "${CARGO_VERSION}" != "${RELEASE_VERSION}" ]]; then
87
+ echo "::error::Version mismatch — bump version in Cargo.toml before releasing."
88
+ exit 1
89
+ fi
90
+
91
+ - name: Check whether release already exists on crates.io
92
+ id: crates-check
93
+ shell: bash
94
+ run: |
95
+ if curl -fsS \
96
+ -H "User-Agent: smbcloud-cli-release-workflow" \
97
+ "https://crates.io/api/v1/crates/smbcloud-cli/${RELEASE_VERSION}" \
98
+ >/dev/null 2>&1; then
99
+ echo "smbcloud-cli ${RELEASE_VERSION} already exists on crates.io, skipping publish"
100
+ echo "exists=true" >> "$GITHUB_OUTPUT"
101
+ else
102
+ echo "exists=false" >> "$GITHUB_OUTPUT"
103
+ fi
104
+
105
- name: Publish to crates.io
35
- uses: katyo/publish-crates@v2
36
- with:
37
- registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
106
+ if: steps.crates-check.outputs.exists != 'true'
107
+ shell: bash
108
+ env:
109
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
110
+ run: cargo publish --locked --package smbcloud-cli
.github/workflows/release-npm.yml
+1
-1
@@ -115,7 +115,7 @@ jobs:
115
GH_OAUTH_REDIRECT_URI: ${{ secrets.GH_OAUTH_REDIRECT_URI }}
116
shell: bash
117
run: |
118
- cargo build --release --locked --target ${{ matrix.build.TARGET }}
118
+ cargo build --release --locked --target ${{ matrix.build.TARGET }} --package smbcloud-cli
119
120
- name: Install node
121
uses: actions/setup-node@v6
.github/workflows/release-pypi.yml
+12
-3
@@ -60,6 +60,8 @@ jobs:
60
steps:
61
- name: Checkout
62
uses: actions/checkout@v6
63
+ with:
64
+ ref: ${{ github.event.inputs.tag || github.ref }}
65
66
- name: Set the release version
67
shell: bash
@@ -67,7 +69,8 @@ jobs:
69
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
70
release_version="${GITHUB_REF_NAME#v}"
71
else
70
- release_version="$(sed -n 's/^version = "\(.*\)"/\1/p' crates/cli/Cargo.toml | head -n 1)"
72
+ release_version="${{ github.event.inputs.tag }}"
73
+ release_version="${release_version#v}"
74
fi
75
76
echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
@@ -121,6 +124,8 @@ jobs:
124
steps:
125
- name: Checkout
126
uses: actions/checkout@v6
127
+ with:
128
+ ref: ${{ github.event.inputs.tag || github.ref }}
129
130
- name: Set the release version
131
shell: bash
@@ -128,7 +133,8 @@ jobs:
133
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
134
release_version="${GITHUB_REF_NAME#v}"
135
else
131
- release_version="$(sed -n 's/^version = "\(.*\)"/\1/p' crates/cli/Cargo.toml | head -n 1)"
136
+ release_version="${{ github.event.inputs.tag }}"
137
+ release_version="${release_version#v}"
138
fi
139
140
echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
@@ -178,6 +184,8 @@ jobs:
184
steps:
185
- name: Checkout
186
uses: actions/checkout@v6
187
+ with:
188
+ ref: ${{ github.event.inputs.tag || github.ref }}
189
190
- name: Set the release version
191
shell: bash
@@ -185,7 +193,8 @@ jobs:
193
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
194
release_version="${GITHUB_REF_NAME#v}"
195
else
188
- release_version="$(sed -n 's/^version = "\(.*\)"/\1/p' crates/cli/Cargo.toml | head -n 1)"
196
+ release_version="${{ github.event.inputs.tag }}"
197
+ release_version="${release_version#v}"
198
fi
199
200
echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"