1 name: SDK PyPI Release
2
3 # Publishes smbcloud-sdk-auth (sdk/python) to PyPI.
4 # Triggered by the crates release workflow when wrapped Rust crates change,
5 # or manually via workflow_dispatch.
6 #
7 # Windows arm64 wheels are intentionally not built here. GitHub's hosted
8 # Windows runners provide x64 Python, and maturin refuses to build an arm64
9 # wheel when the interpreter reports win-amd64.
10
11 on:
12 workflow_dispatch:
13 inputs:
14 tag:
15 description: "Release tag (e.g. v0.3.35)"
16 required: true
17
18 permissions:
19 id-token: write
20 contents: read
21
22 env:
23 CARGO_TERM_COLOR: always
24 CLI_CLIENT_SECRET: ${{ secrets.CLI_CLIENT_SECRET }}
25
26 jobs:
27 build-wheels:
28 name: Build PyPI wheels
29 runs-on: ${{ matrix.build.OS }}
30 strategy:
31 fail-fast: false
32 matrix:
33 build:
34 - {
35 NAME: linux-x64-glibc,
36 OS: ubuntu-latest,
37 TARGET: x86_64-unknown-linux-gnu,
38 MANYLINUX: "2014",
39 }
40 - {
41 NAME: linux-arm64-glibc,
42 OS: ubuntu-24.04-arm,
43 TARGET: aarch64-unknown-linux-gnu,
44 MANYLINUX: "2014",
45 }
46 - {
47 NAME: win32-x64-msvc,
48 OS: windows-2022,
49 TARGET: x86_64-pc-windows-msvc,
50 }
51 - {
52 NAME: darwin-x64,
53 OS: macos-15-intel,
54 TARGET: x86_64-apple-darwin,
55 }
56 - {
57 NAME: darwin-arm64,
58 OS: macos-latest,
59 TARGET: aarch64-apple-darwin,
60 }
61 steps:
62 - name: Checkout
63 uses: actions/checkout@v6
64 with:
65 ref: ${{ github.event.inputs.tag || github.ref }}
66
67 - name: Set the release version
68 shell: bash
69 run: |
70 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
71 release_version="${GITHUB_REF_NAME#v}"
72 else
73 release_version="${{ github.event.inputs.tag }}"
74 release_version="${release_version#v}"
75 fi
76
77 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
78
79 - name: Read Rust toolchain
80 shell: bash
81 run: |
82 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
83 if [ -z "$rust_toolchain" ]; then
84 echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
85 exit 1
86 fi
87
88 echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
89
90 - name: Install toolkit
91 uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
92 with:
93 toolchain: ${{ env.RUST_TOOLCHAIN }}
94
95 - name: Install Rust target
96 shell: bash
97 run: |
98 rustup target add ${{ matrix.build.TARGET }} --toolchain ${{ env.RUST_TOOLCHAIN }}
99 rustup target list --installed
100
101 - name: Verify Cargo.toml version matches tag
102 shell: bash
103 run: |
104 CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 \
105 | jq -r '.packages[] | select(.name == "smbcloud-auth-sdk-py") | .version')
106
107 echo "Cargo.toml version : ${CARGO_VERSION}"
108 echo "Release tag version: ${RELEASE_VERSION}"
109
110 if [[ "${CARGO_VERSION}" != "${RELEASE_VERSION}" ]]; then
111 echo "::error::Version mismatch — bump version in crates/smbcloud-auth-sdk-py/Cargo.toml before releasing."
112 exit 1
113 fi
114
115 - name: Setup Rust cache
116 uses: Swatinem/rust-cache@v2
117 with:
118 key: sdk-pypi-${{ matrix.build.NAME }}
119
120 # manylinux containers are CentOS-based and use yum; perl-core is required
121 # by the openssl-sys vendored build to compile OpenSSL from source.
122 - name: Clean dist directory
123 shell: bash
124 run: rm -rf sdk/python/dist
125
126 - name: Build wheel
127 uses: PyO3/maturin-action@v1
128 with:
129 target: ${{ matrix.build.TARGET }}
130 manylinux: ${{ matrix.build.MANYLINUX || 'off' }}
131 working-directory: sdk/python
132 args: --release --locked --compatibility pypi --out dist
133 before-script-linux: yum install -y perl-core
134 docker-options: -e CLI_CLIENT_SECRET
135
136 - name: Upload wheel artifact
137 uses: actions/upload-artifact@v7
138 with:
139 name: wheels-${{ matrix.build.NAME }}
140 path: sdk/python/dist/*
141
142 build-sdist:
143 name: Build source distribution
144 runs-on: ubuntu-latest
145 steps:
146 - name: Checkout
147 uses: actions/checkout@v6
148 with:
149 ref: ${{ github.event.inputs.tag || github.ref }}
150
151 - name: Set the release version
152 shell: bash
153 run: |
154 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
155 release_version="${GITHUB_REF_NAME#v}"
156 else
157 release_version="${{ github.event.inputs.tag }}"
158 release_version="${release_version#v}"
159 fi
160
161 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
162
163 - name: Read Rust toolchain
164 shell: bash
165 run: |
166 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
167 if [ -z "$rust_toolchain" ]; then
168 echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
169 exit 1
170 fi
171
172 echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
173
174 - name: Install toolkit
175 uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
176 with:
177 toolchain: ${{ env.RUST_TOOLCHAIN }}
178
179 - name: Clean dist directory
180 run: rm -rf sdk/python/dist
181
182 - name: Build sdist
183 uses: PyO3/maturin-action@v1
184 with:
185 command: sdist
186 working-directory: sdk/python
187 args: --out dist
188
189 - name: Upload sdist artifact
190 uses: actions/upload-artifact@v7
191 with:
192 name: sdist
193 path: sdk/python/dist/*
194
195 publish-pypi:
196 name: Publish to PyPI
197 runs-on: ubuntu-latest
198 needs:
199 - build-wheels
200 - build-sdist
201
202 steps:
203 - name: Checkout
204 uses: actions/checkout@v6
205 with:
206 ref: ${{ github.event.inputs.tag || github.ref }}
207
208 - name: Set the release version
209 shell: bash
210 run: |
211 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
212 release_version="${GITHUB_REF_NAME#v}"
213 else
214 release_version="${{ github.event.inputs.tag }}"
215 release_version="${release_version#v}"
216 fi
217
218 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
219
220 - name: Download distribution artifacts
221 uses: actions/download-artifact@v7
222 with:
223 pattern: "*"
224 path: dist
225 merge-multiple: true
226
227 - name: Check whether release already exists on PyPI
228 id: pypi-check
229 shell: bash
230 run: |
231 if curl -fsS "https://pypi.org/pypi/smbcloud-sdk-auth/${RELEASE_VERSION}/json" >/dev/null 2>&1; then
232 echo "smbcloud-sdk-auth ${RELEASE_VERSION} already exists on PyPI, skipping publish"
233 echo "exists=true" >> "$GITHUB_OUTPUT"
234 else
235 echo "exists=false" >> "$GITHUB_OUTPUT"
236 fi
237
238 - name: Publish distribution
239 if: steps.pypi-check.outputs.exists != 'true'
240 uses: pypa/gh-action-pypi-publish@release/v1
241 with:
242 skip-existing: true