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 - { name: win-x64, runner: windows-2022, platform: x64-mingw-ucrt }
45
46 steps:
47 - name: Checkout
48 uses: actions/checkout@v6
49 with:
50 ref: ${{ github.event.inputs.tag || github.ref }}
51
52 - name: Set the release version
53 shell: bash
54 run: |
55 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
56 release_version="${GITHUB_REF_NAME#v}"
57 else
58 release_version="${{ github.event.inputs.tag }}"
59 release_version="${release_version#v}"
60 fi
61 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
62
63 - name: Wait for Rust crate dependencies on crates.io
64 shell: bash
65 run: |
66 for crate_name in smbcloud-auth-sdk smbcloud-model smbcloud-network; do
67 for attempt in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
68 if curl -fsS "https://crates.io/api/v1/crates/${crate_name}/${RELEASE_VERSION}" >/dev/null 2>&1; then
69 echo "${crate_name} ${RELEASE_VERSION} is available on crates.io"
70 break
71 fi
72
73 if [ "$attempt" -eq 20 ]; then
74 echo "Timed out waiting for ${crate_name} ${RELEASE_VERSION} on crates.io" >&2
75 exit 1
76 fi
77
78 echo "Waiting for ${crate_name} ${RELEASE_VERSION} to appear on crates.io..."
79 sleep 15
80 done
81 done
82
83 - name: Read Rust toolchain
84 shell: bash
85 run: |
86 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
87 if [ -z "$rust_toolchain" ]; then
88 echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
89 exit 1
90 fi
91 echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
92
93 - name: Install toolkit
94 uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
95 with:
96 toolchain: ${{ env.RUST_TOOLCHAIN }}
97
98 # Scope the Rust cache to the gem's own Cargo workspace so its registry
99 # deps are cached independently from the main workspace.
100 - name: Setup Rust cache
101 uses: Swatinem/rust-cache@v2
102 with:
103 key: sdk-gem-${{ matrix.name }}
104 workspaces: sdk/gems/auth
105
106 - name: Setup Ruby
107 uses: ruby/setup-ruby@v1
108 with:
109 ruby-version: "3.4.2"
110 bundler-cache: true
111 working-directory: sdk/gems/auth
112
113 - name: Build native gem
114 working-directory: sdk/gems/auth
115 shell: bash
116 run: bundle exec rake native gem
117
118 - name: Upload gem artifact
119 uses: actions/upload-artifact@v7
120 with:
121 name: gem-${{ matrix.name }}
122 path: sdk/gems/auth/pkg/*.gem
123
124 # ── Build source gem (fallback for unsupported platforms) ──────────────────
125 build-source-gem:
126 name: Build source gem
127 runs-on: ubuntu-latest
128 steps:
129 - name: Checkout
130 uses: actions/checkout@v6
131 with:
132 ref: ${{ github.event.inputs.tag || github.ref }}
133
134 - name: Set the release version
135 shell: bash
136 run: |
137 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
138 release_version="${GITHUB_REF_NAME#v}"
139 else
140 release_version="${{ github.event.inputs.tag }}"
141 release_version="${release_version#v}"
142 fi
143 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
144
145 - name: Verify gem version matches tag
146 shell: bash
147 run: |
148 GEM_VERSION=$(sed -n "s/.*VERSION = '\(.*\)'/\1/p" sdk/gems/auth/lib/auth/version.rb)
149
150 echo "Gem version : ${GEM_VERSION}"
151 echo "Tag version : ${RELEASE_VERSION}"
152
153 if [[ "${GEM_VERSION}" != "${RELEASE_VERSION}" ]]; then
154 echo "::error::Version mismatch — bump Auth::VERSION in sdk/gems/auth/lib/auth/version.rb before releasing."
155 exit 1
156 fi
157
158 - name: Setup Ruby
159 uses: ruby/setup-ruby@v1
160 with:
161 ruby-version: "3.4.2"
162 bundler-cache: true
163 working-directory: sdk/gems/auth
164
165 - name: Build source gem
166 working-directory: sdk/gems/auth
167 run: bundle exec rake build
168
169 - name: Upload source gem artifact
170 uses: actions/upload-artifact@v7
171 with:
172 name: gem-source
173 path: sdk/gems/auth/pkg/*.gem
174
175 # ── Push all gems to RubyGems ──────────────────────────────────────────────
176 push-gems:
177 name: Push to RubyGems
178 needs: [build-gems, build-source-gem]
179 runs-on: ubuntu-latest
180
181 steps:
182 - name: Checkout
183 uses: actions/checkout@v6
184 with:
185 ref: ${{ github.event.inputs.tag || github.ref }}
186
187 - name: Set the release version
188 shell: bash
189 run: |
190 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
191 release_version="${GITHUB_REF_NAME#v}"
192 else
193 release_version="${{ github.event.inputs.tag }}"
194 release_version="${release_version#v}"
195 fi
196 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
197
198 - name: Check whether release already exists on RubyGems
199 id: rubygems-check
200 shell: bash
201 run: |
202 if curl -fsS \
203 "https://rubygems.org/api/v2/rubygems/smbcloud-auth/versions/${RELEASE_VERSION}.json" \
204 >/dev/null 2>&1; then
205 echo "smbcloud-auth ${RELEASE_VERSION} already exists on RubyGems, skipping publish"
206 echo "exists=true" >> "$GITHUB_OUTPUT"
207 else
208 echo "exists=false" >> "$GITHUB_OUTPUT"
209 fi
210
211 - name: Download all gem artifacts
212 if: steps.rubygems-check.outputs.exists != 'true'
213 uses: actions/download-artifact@v7
214 with:
215 pattern: "gem-*"
216 path: gems
217 merge-multiple: true
218
219 - name: Setup Ruby
220 if: steps.rubygems-check.outputs.exists != 'true'
221 uses: ruby/setup-ruby@v1
222 with:
223 ruby-version: "3.4.2"
224
225 - name: Configure RubyGems credentials
226 if: steps.rubygems-check.outputs.exists != 'true'
227 shell: bash
228 run: |
229 mkdir -p "${HOME}/.gem"
230 echo ":rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }}" > "${HOME}/.gem/credentials"
231 chmod 0600 "${HOME}/.gem/credentials"
232
233 # Push each .gem file individually. Platform-specific gems that are already
234 # published (e.g. from a re-run) emit a warning and continue rather than
235 # failing the whole job.
236 - name: Push gems to RubyGems
237 if: steps.rubygems-check.outputs.exists != 'true'
238 shell: bash
239 run: |
240 for gem_file in gems/*.gem; do
241 echo "Pushing $(basename "${gem_file}")…"
242 gem push "${gem_file}" \
243 || echo "::warning::$(basename "${gem_file}") may already exist for this version — skipping."
244 done