1 name: PyPI Release
2
3 on:
4 push:
5 tags:
6 - "v*.*.*"
7 workflow_dispatch:
8 inputs:
9 tag:
10 description: "Release tag (e.g. v0.1.1)"
11 required: true
12
13 permissions:
14 id-token: write
15 contents: read
16
17 env:
18 CARGO_TERM_COLOR: always
19
20 jobs:
21 build-wheels:
22 name: Build PyPI wheels
23 runs-on: ${{ matrix.build.OS }}
24 strategy:
25 fail-fast: false
26 matrix:
27 build:
28 - {
29 NAME: linux-x64-glibc,
30 OS: ubuntu-latest,
31 TARGET: x86_64-unknown-linux-gnu,
32 MANYLINUX: "2014",
33 }
34 - {
35 NAME: linux-arm64-glibc,
36 OS: ubuntu-24.04-arm,
37 TARGET: aarch64-unknown-linux-gnu,
38 MANYLINUX: "2014",
39 }
40 - {
41 NAME: win32-x64-msvc,
42 OS: windows-2022,
43 TARGET: x86_64-pc-windows-msvc,
44 }
45 - {
46 NAME: win32-arm64-msvc,
47 OS: windows-2022,
48 TARGET: aarch64-pc-windows-msvc,
49 }
50 - { NAME: darwin-x64, OS: macos-26, TARGET: x86_64-apple-darwin }
51 - { NAME: darwin-arm64, OS: macos-26, TARGET: aarch64-apple-darwin }
52 steps:
53 - name: Checkout
54 uses: actions/checkout@v6
55 with:
56 ref: ${{ github.event.inputs.tag || github.ref }}
57
58 - name: Set the release version
59 shell: bash
60 run: |
61 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
62 release_version="${GITHUB_REF_NAME#v}"
63 else
64 release_version="${{ github.event.inputs.tag }}"
65 release_version="${release_version#v}"
66 fi
67 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
68
69 - name: Read Rust toolchain
70 shell: bash
71 run: |
72 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
73 if [ -z "$rust_toolchain" ]; then
74 echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
75 exit 1
76 fi
77 echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
78
79 - name: Install Rust toolchain
80 uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
81 with:
82 toolchain: ${{ env.RUST_TOOLCHAIN }}
83
84 - name: Install Rust target
85 shell: bash
86 run: |
87 rustup target add ${{ matrix.build.TARGET }} --toolchain ${{ env.RUST_TOOLCHAIN }}
88 rustup target list --installed
89
90 - name: Build wheel
91 uses: PyO3/maturin-action@v1
92 with:
93 target: ${{ matrix.build.TARGET }}
94 manylinux: ${{ matrix.build.MANYLINUX || 'off' }}
95 working-directory: pypi
96 args: --release --locked --compatibility pypi --out dist
97 before-script-linux: yum install -y perl-core
98
99 - name: Upload wheel artifact
100 uses: actions/upload-artifact@v4
101 with:
102 name: wheels-${{ matrix.build.NAME }}
103 path: pypi/dist/*
104
105 build-sdist:
106 name: Build source distribution
107 runs-on: ubuntu-latest
108 steps:
109 - name: Checkout
110 uses: actions/checkout@v6
111 with:
112 ref: ${{ github.event.inputs.tag || github.ref }}
113
114 - name: Read Rust toolchain
115 shell: bash
116 run: |
117 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
118 if [ -z "$rust_toolchain" ]; then
119 echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
120 exit 1
121 fi
122 echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
123
124 - name: Install Rust toolchain
125 uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
126 with:
127 toolchain: ${{ env.RUST_TOOLCHAIN }}
128
129 - name: Build sdist
130 uses: PyO3/maturin-action@v1
131 with:
132 command: sdist
133 working-directory: pypi
134 args: --out dist
135
136 - name: Upload sdist artifact
137 uses: actions/upload-artifact@v4
138 with:
139 name: sdist
140 path: pypi/dist/*
141
142 publish-pypi:
143 name: Publish to PyPI
144 runs-on: ubuntu-latest
145 needs:
146 - build-wheels
147 - build-sdist
148 steps:
149 - name: Checkout
150 uses: actions/checkout@v6
151 with:
152 ref: ${{ github.event.inputs.tag || github.ref }}
153
154 - name: Set the release version
155 shell: bash
156 run: |
157 if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
158 release_version="${GITHUB_REF_NAME#v}"
159 else
160 release_version="${{ github.event.inputs.tag }}"
161 release_version="${release_version#v}"
162 fi
163 echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
164
165 - name: Download distribution artifacts
166 uses: actions/download-artifact@v4
167 with:
168 pattern: "*"
169 path: dist
170 merge-multiple: true
171
172 - name: Check whether release already exists on PyPI
173 id: pypi-check
174 shell: bash
175 run: |
176 if curl -fsS "https://pypi.org/pypi/sigit-code/${RELEASE_VERSION}/json" >/dev/null 2>&1; then
177 echo "sigit-code ${RELEASE_VERSION} already exists on PyPI, skipping publish"
178 echo "exists=true" >> "$GITHUB_OUTPUT"
179 else
180 echo "exists=false" >> "$GITHUB_OUTPUT"
181 fi
182
183 - name: Publish distribution
184 if: steps.pypi-check.outputs.exists != 'true'
185 uses: pypa/gh-action-pypi-publish@release/v1
186 with:
187 skip-existing: true