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