1212
1313permissions :
1414 contents : read
15+ actions : write
1516
1617env :
1718 CARGO_TERM_COLOR : always
1819 CLI_CLIENT_SECRET : ${{ secrets.CLI_CLIENT_SECRET }}
1920
2021jobs :
2122 publish :
22- name : Publish to crates.io
23+ name : Publish workspace crates to crates.io
2324 runs-on : ubuntu-latest
25+ outputs :
26+ tag : ${{ steps.release.outputs.tag }}
27+ published_count : ${{ steps.plan.outputs.count }}
28+ publish_cli_sdk_pypi : ${{ steps.downstream.outputs.publish_cli_sdk_pypi }}
29+ publish_cli_sdk_npm : ${{ steps.downstream.outputs.publish_cli_sdk_npm }}
30+ publish_cli_sdk_gem : ${{ steps.downstream.outputs.publish_cli_sdk_gem }}
2431
2532 steps :
2633 - name : Checkout
2734 uses : actions/checkout@v6
2835 with :
36+ fetch-depth : 0
2937 ref : ${{ github.event.inputs.tag || github.ref }}
3038
3139 - name : Set the release version
40+ id : release
3241 shell : bash
3342 run : |
3443 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
44+ release_tag="${GITHUB_REF_NAME}"
3545 release_version="${GITHUB_REF_NAME#v}"
3646 else
37- release_version="${{ github.event.inputs.tag }}"
38- release_version="${release_version#v}"
47+ release_tag="${{ github.event.inputs.tag }}"
48+ release_tag="${release_tag#refs/tags/}"
49+ release_version="${release_tag#v}"
3950 fi
4051
52+ echo "RELEASE_TAG=${release_tag}" >> "$GITHUB_ENV"
4153 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
54+ echo "tag=${release_tag}" >> "$GITHUB_OUTPUT"
55+ echo "version=${release_version}" >> "$GITHUB_OUTPUT"
4256
4357 - name : Read Rust toolchain
4458 shell : bash
@@ -61,37 +75,207 @@ jobs:
6175 with :
6276 key : publish
6377
64- - name : Verify Cargo.toml version matches tag
78+ - name : Install cargo-workspaces
79+ shell : bash
80+ run : cargo install cargo-workspaces --version 0.4.2 --locked
81+
82+ - name : Verify workspace crate versions match tag
6583 shell : bash
6684 run : |
67- CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 \
68- | jq -r '.packages[] | select(.name == "smbcloud-cli") | .version')
85+ cargo metadata --no-deps --format-version 1 > metadata.json
86+
87+ crate_count="$(jq '[.packages[] | select(.manifest_path | contains("/crates/"))] | length' metadata.json)"
88+ if [[ "$crate_count" == "0" ]]; then
89+ echo "::error::No workspace crates found under /crates."
90+ exit 1
91+ fi
6992
70- echo "Cargo.toml version : ${CARGO_VERSION}"
71- echo "Release tag version: ${RELEASE_VERSION}"
93+ mismatches="$({
94+ jq -r --arg release_version "${RELEASE_VERSION}" '
95+ .packages[]
96+ | select(.manifest_path | contains("/crates/"))
97+ | select(.version != $release_version)
98+ | "\(.name) \(.version)"' metadata.json
99+ } || true)"
72100
73- if [[ "${CARGO_VERSION}" != "${RELEASE_VERSION}" ]]; then
74- echo "::error::Version mismatch — bump version in Cargo.toml before releasing."
101+ if [[ -n "$mismatches" ]]; then
102+ echo "::error::These crates do not match tag version ${RELEASE_VERSION}:"
103+ echo "$mismatches"
75104 exit 1
76105 fi
77106
78- - name : Check whether release already exists on crates.io
79- id : crates-check
107+ - name : Build unpublished crate plan
108+ id : plan
80109 shell : bash
110+ env :
111+ CARGO_REGISTRY_TOKEN : ${{ secrets.CARGO_REGISTRY_TOKEN }}
81112 run : |
82- if curl -fsS \
83- -H "User-Agent: smbcloud-cli-release-workflow" \
84- "https://crates.io/api/v1/crates/smbcloud-cli/${RELEASE_VERSION}" \
85- >/dev/null 2>&1; then
86- echo "smbcloud-cli ${RELEASE_VERSION} already exists on crates.io, skipping publish"
87- echo "exists=true" >> "$GITHUB_OUTPUT"
88- else
89- echo "exists=false" >> "$GITHUB_OUTPUT"
113+ if [[ -z "${CARGO_REGISTRY_TOKEN}" ]]; then
114+ echo "::error::CARGO_REGISTRY_TOKEN is required."
115+ exit 1
90116 fi
91117
92- - name : Publish to crates.io
93- if : steps.crates-check.outputs.exists != 'true'
118+ cargo workspaces plan --skip-published --json --token "${CARGO_REGISTRY_TOKEN}" > plan.json
119+ cat plan.json
120+
121+ plan_count="$(jq 'length' plan.json)"
122+ echo "count=${plan_count}" >> "$GITHUB_OUTPUT"
123+
124+ - name : Derive downstream workflow triggers
125+ id : downstream
126+ shell : bash
127+ run : |
128+ publish_cli_sdk_pypi="$(jq -r 'map(.name) | any(. == "smbcloud-auth-sdk-py" or . == "smbcloud-auth-sdk" or . == "smbcloud-model" or . == "smbcloud-network")' plan.json)"
129+ publish_cli_sdk_npm="$(jq -r 'map(.name) | any(. == "smbcloud-auth-sdk-wasm" or . == "smbcloud-auth-sdk" or . == "smbcloud-network" or . == "smbcloud-networking")' plan.json)"
130+ publish_cli_sdk_gem="$(jq -r 'map(.name) | any(. == "smbcloud-auth-sdk" or . == "smbcloud-model" or . == "smbcloud-network")' plan.json)"
131+
132+ echo "publish_cli_sdk_pypi=${publish_cli_sdk_pypi}" >> "$GITHUB_OUTPUT"
133+ echo "publish_cli_sdk_npm=${publish_cli_sdk_npm}" >> "$GITHUB_OUTPUT"
134+ echo "publish_cli_sdk_gem=${publish_cli_sdk_gem}" >> "$GITHUB_OUTPUT"
135+
136+ echo "SDK PyPI trigger: ${publish_cli_sdk_pypi}"
137+ echo "SDK npm trigger: ${publish_cli_sdk_npm}"
138+ echo "SDK gem trigger: ${publish_cli_sdk_gem}"
139+
140+ - name : Publish workspace crates to crates.io
141+ if : steps.plan.outputs.count != '0'
94142 shell : bash
95143 env :
96144 CARGO_REGISTRY_TOKEN : ${{ secrets.CARGO_REGISTRY_TOKEN }}
97- run : cargo publish --locked --package smbcloud-cli
145+ run : |
146+ cargo workspaces publish \
147+ --publish-as-is \
148+ --yes \
149+ --locked \
150+ --allow-branch "*" \
151+ --publish-interval 15 \
152+ --token "${CARGO_REGISTRY_TOKEN}"
153+
154+ - name : Nothing to publish
155+ if : steps.plan.outputs.count == '0'
156+ run : echo "All workspace crate versions in this tag are already published."
157+
158+ dispatch-distributions :
159+ name : Trigger CLI distribution workflows
160+ needs : publish
161+ runs-on : ubuntu-latest
162+
163+ steps :
164+ - name : Trigger GitHub CLI release
165+ uses : actions/github-script@v7
166+ with :
167+ script : |
168+ await github.rest.actions.createWorkflowDispatch({
169+ owner: context.repo.owner,
170+ repo: context.repo.repo,
171+ workflow_id: 'release-github.yml',
172+ ref: '${{ needs.publish.outputs.tag }}',
173+ inputs: {
174+ tag: '${{ needs.publish.outputs.tag }}'
175+ }
176+ })
177+
178+ - name : Trigger npm CLI release
179+ uses : actions/github-script@v7
180+ with :
181+ script : |
182+ await github.rest.actions.createWorkflowDispatch({
183+ owner: context.repo.owner,
184+ repo: context.repo.repo,
185+ workflow_id: 'release-npm.yml',
186+ ref: '${{ needs.publish.outputs.tag }}',
187+ inputs: {
188+ tag: '${{ needs.publish.outputs.tag }}'
189+ }
190+ })
191+
192+ - name : Trigger NuGet CLI release
193+ uses : actions/github-script@v7
194+ with :
195+ script : |
196+ await github.rest.actions.createWorkflowDispatch({
197+ owner: context.repo.owner,
198+ repo: context.repo.repo,
199+ workflow_id: 'release-nuget.yml',
200+ ref: '${{ needs.publish.outputs.tag }}',
201+ inputs: {
202+ tag: '${{ needs.publish.outputs.tag }}'
203+ }
204+ })
205+
206+ - name : Trigger PyPI CLI release
207+ uses : actions/github-script@v7
208+ with :
209+ script : |
210+ await github.rest.actions.createWorkflowDispatch({
211+ owner: context.repo.owner,
212+ repo: context.repo.repo,
213+ workflow_id: 'release-pypi.yml',
214+ ref: '${{ needs.publish.outputs.tag }}',
215+ inputs: {
216+ tag: '${{ needs.publish.outputs.tag }}'
217+ }
218+ })
219+
220+ dispatch-sdk-pypi :
221+ name : Trigger SDK PyPI release
222+ needs : publish
223+ if : needs.publish.outputs.publish_cli_sdk_pypi == 'true'
224+ runs-on : ubuntu-latest
225+
226+ steps :
227+ - name : Trigger SDK PyPI release workflow
228+ uses : actions/github-script@v7
229+ with :
230+ script : |
231+ await github.rest.actions.createWorkflowDispatch({
232+ owner: context.repo.owner,
233+ repo: context.repo.repo,
234+ workflow_id: 'release-sdk-pypi.yml',
235+ ref: '${{ needs.publish.outputs.tag }}',
236+ inputs: {
237+ tag: '${{ needs.publish.outputs.tag }}'
238+ }
239+ })
240+
241+ dispatch-sdk-npm :
242+ name : Trigger SDK npm release
243+ needs : publish
244+ if : needs.publish.outputs.publish_cli_sdk_npm == 'true'
245+ runs-on : ubuntu-latest
246+
247+ steps :
248+ - name : Trigger SDK npm release workflow
249+ uses : actions/github-script@v7
250+ with :
251+ script : |
252+ await github.rest.actions.createWorkflowDispatch({
253+ owner: context.repo.owner,
254+ repo: context.repo.repo,
255+ workflow_id: 'release-sdk-npm.yml',
256+ ref: '${{ needs.publish.outputs.tag }}',
257+ inputs: {
258+ tag: '${{ needs.publish.outputs.tag }}'
259+ }
260+ })
261+
262+ dispatch-sdk-gem :
263+ name : Trigger SDK Ruby gem release
264+ needs : publish
265+ if : needs.publish.outputs.publish_cli_sdk_gem == 'true'
266+ runs-on : ubuntu-latest
267+
268+ steps :
269+ - name : Trigger SDK Ruby gem release workflow
270+ uses : actions/github-script@v7
271+ with :
272+ script : |
273+ await github.rest.actions.createWorkflowDispatch({
274+ owner: context.repo.owner,
275+ repo: context.repo.repo,
276+ workflow_id: 'release-sdk-gem.yml',
277+ ref: '${{ needs.publish.outputs.tag }}',
278+ inputs: {
279+ tag: '${{ needs.publish.outputs.tag }}'
280+ }
281+ })
0 commit comments