@smbcloud / cli / commits / 16ffe21

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"