1 name: SDK Ruby Gem Release
2
3 # Publishes the smbcloud-auth Ruby gem (sdk/gems/auth) to RubyGems.
4 # Builds pre-compiled native gems for the supported Unix platforms, plus a
5 # source gem as a fallback for unsupported environments.
6 #
7 # Required secrets:
8 # RUBYGEMS_API_KEY — RubyGems.org API key with push access to smbcloud-auth
9
10 on:
11 workflow_dispatch:
12 inputs:
13 tag:
14 description: "Release tag (e.g. v0.3.35)"
15 required: true
16
17 permissions:
18 contents: read
19
20 env:
21 CARGO_TERM_COLOR: always
22
23 jobs:
24 # ── Build native gems ──────────────────────────────────────────────────────
25 build-gems:
26 name: Build gem (${{ matrix.name }})
27 runs-on: ${{ matrix.runner }}
28 strategy:
29 fail-fast: false
30 matrix:
31 include:
32 - { name: linux-x64, runner: ubuntu-latest, platform: x86_64-linux }
33 - {
34 name: linux-arm64,
35 runner: ubuntu-24.04-arm,
36 platform: aarch64-linux,
37 }
38 - { name: darwin-arm64, runner: macos-latest, platform: arm64-darwin }
39 - {
40 name: darwin-x64,
41 runner: macos-15-intel,
42 platform: x86_64-darwin,
43 }
44 # Windows is excluded: rb-sys bindgen fails because RubyInstaller is
45 # MinGW-based and its rbconfig emits GCC flags that are incompatible
46 # with the MSVC clang used by bindgen. The source gem provides a
47 # fallback for Windows users (compiled at install time).
48
49 steps:
50 - name: Checkout
51 uses: actions/checkout@v6
52 with:
53 ref: ${{ github.event.inputs.tag || github.ref }}
54
55 - name: Set the release version
56 shell: bash
57 run: |
58 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
59 release_version="${GITHUB_REF_NAME#v}"
60 else
61 release_version="${{ github.event.inputs.tag }}"
62 release_version="${release_version#v}"
63 fi
64 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
65
66 - name: Wait for Rust crate dependencies on crates.io
67 shell: bash
68 run: |
69 for crate_name in smbcloud-auth-sdk smbcloud-model smbcloud-network; do
70 for attempt in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
71 if curl -fsS -H "User-Agent: smbcloud-cli-release" "https://crates.io/api/v1/crates/${crate_name}/${RELEASE_VERSION}" >/dev/null 2>&1; then
72 echo "${crate_name} ${RELEASE_VERSION} is available on crates.io"
73 break
74 fi
75
76 if [ "$attempt" -eq 20 ]; then
77 echo "Timed out waiting for ${crate_name} ${RELEASE_VERSION} on crates.io" >&2
78 exit 1
79 fi
80
81 echo "Waiting for ${crate_name} ${RELEASE_VERSION} to appear on crates.io..."
82 sleep 15
83 done
84 done
85
86 - name: Install system dependencies (Linux)
87 if: runner.os == 'Linux'
88 run: sudo apt-get update -q && sudo apt-get install -y libclang-dev
89
90 - name: Read Rust toolchain
91 shell: bash
92 run: |
93 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
94 if [ -z "$rust_toolchain" ]; then
95 echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
96 exit 1
97 fi
98 echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
99
100 - name: Install toolkit
101 uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
102 with:
103 toolchain: ${{ env.RUST_TOOLCHAIN }}
104
105 - name: Resolve the published Rust crate dependencies
106 shell: bash
107 run: cargo generate-lockfile --manifest-path sdk/gems/auth/Cargo.toml
108
109 # Scope the Rust cache to the gem's own Cargo workspace so its registry
110 # deps are cached independently from the main workspace.
111 - name: Setup Rust cache
112 uses: Swatinem/rust-cache@v2
113 with:
114 key: sdk-gem-${{ matrix.name }}
115 workspaces: sdk/gems/auth
116
117 - name: Setup Ruby
118 uses: ruby/setup-ruby@v1
119 with:
120 ruby-version: .ruby-version
121 bundler-cache: true
122 working-directory: sdk/gems/auth
123
124 - name: Build native gem
125 working-directory: sdk/gems/auth
126 shell: bash
127 run: bundle exec rake native gem
128
129 - name: Upload gem artifact
130 uses: actions/upload-artifact@v7
131 with:
132 name: gem-${{ matrix.name }}
133 path: sdk/gems/auth/pkg/*.gem
134
135 # ── Build source gem (fallback for unsupported platforms) ──────────────────
136 build-source-gem:
137 name: Build source gem
138 runs-on: ubuntu-latest
139 steps:
140 - name: Checkout
141 uses: actions/checkout@v6
142 with:
143 ref: ${{ github.event.inputs.tag || github.ref }}
144
145 - name: Set the release version
146 shell: bash
147 run: |
148 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
149 release_version="${GITHUB_REF_NAME#v}"
150 else
151 release_version="${{ github.event.inputs.tag }}"
152 release_version="${release_version#v}"
153 fi
154 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
155
156 - name: Verify gem version matches tag
157 shell: bash
158 run: |
159 GEM_VERSION=$(sed -n "s/.*VERSION = '\(.*\)'/\1/p" sdk/gems/auth/lib/auth/version.rb)
160
161 echo "Gem version : ${GEM_VERSION}"
162 echo "Tag version : ${RELEASE_VERSION}"
163
164 if [[ "${GEM_VERSION}" != "${RELEASE_VERSION}" ]]; then
165 echo "::error::Version mismatch — bump Auth::VERSION in sdk/gems/auth/lib/auth/version.rb before releasing."
166 exit 1
167 fi
168
169 - name: Setup Ruby
170 uses: ruby/setup-ruby@v1
171 with:
172 ruby-version: .ruby-version
173 bundler-cache: true
174 working-directory: sdk/gems/auth
175
176 - name: Build source gem
177 working-directory: sdk/gems/auth
178 run: bundle exec rake build
179
180 - name: Upload source gem artifact
181 uses: actions/upload-artifact@v7
182 with:
183 name: gem-source
184 path: sdk/gems/auth/pkg/*.gem
185
186 # ── Push all gems to RubyGems ──────────────────────────────────────────────
187 push-gems:
188 name: Push to RubyGems
189 needs: [build-gems, build-source-gem]
190 runs-on: ubuntu-latest
191
192 steps:
193 - name: Checkout
194 uses: actions/checkout@v6
195 with:
196 ref: ${{ github.event.inputs.tag || github.ref }}
197
198 - name: Set the release version
199 shell: bash
200 run: |
201 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
202 release_version="${GITHUB_REF_NAME#v}"
203 else
204 release_version="${{ github.event.inputs.tag }}"
205 release_version="${release_version#v}"
206 fi
207 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
208
209 - name: Check whether release already exists on RubyGems
210 id: rubygems-check
211 shell: bash
212 run: |
213 if curl -fsS \
214 "https://rubygems.org/api/v2/rubygems/smbcloud-auth/versions/${RELEASE_VERSION}.json" \
215 >/dev/null 2>&1; then
216 echo "smbcloud-auth ${RELEASE_VERSION} already exists on RubyGems, skipping publish"
217 echo "exists=true" >> "$GITHUB_OUTPUT"
218 else
219 echo "exists=false" >> "$GITHUB_OUTPUT"
220 fi
221
222 - name: Download all gem artifacts
223 if: steps.rubygems-check.outputs.exists != 'true'
224 uses: actions/download-artifact@v7
225 with:
226 pattern: "gem-*"
227 path: gems
228 merge-multiple: true
229
230 - name: Setup Ruby
231 if: steps.rubygems-check.outputs.exists != 'true'
232 uses: ruby/setup-ruby@v1
233 with:
234 ruby-version: .ruby-version
235
236 - name: Configure RubyGems credentials
237 if: steps.rubygems-check.outputs.exists != 'true'
238 shell: bash
239 run: |
240 mkdir -p "${HOME}/.gem"
241 echo ":rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }}" > "${HOME}/.gem/credentials"
242 chmod 0600 "${HOME}/.gem/credentials"
243
244 # Push each .gem file individually. Platform-specific gems that are already
245 # published (e.g. from a re-run) emit a warning and continue rather than
246 # failing the whole job.
247 - name: Push gems to RubyGems
248 if: steps.rubygems-check.outputs.exists != 'true'
249 shell: bash
250 run: |
251 for gem_file in gems/*.gem; do
252 echo "Pushing $(basename "${gem_file}")…"
253 gem push "${gem_file}" \
254 || echo "::warning::$(basename "${gem_file}") may already exist for this version — skipping."
255 done