inkrs 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- inkrs-0.2.0/.gitattributes +7 -0
- inkrs-0.2.0/.github/workflows/ci.yml +148 -0
- inkrs-0.2.0/.github/workflows/package.yml +471 -0
- inkrs-0.2.0/.gitignore +3 -0
- inkrs-0.2.0/API.md +265 -0
- inkrs-0.2.0/CHANGELOG.md +36 -0
- inkrs-0.2.0/Cargo.lock +1175 -0
- inkrs-0.2.0/Cargo.toml +58 -0
- inkrs-0.2.0/LICENSE-APACHE +202 -0
- inkrs-0.2.0/LICENSE-MIT +21 -0
- inkrs-0.2.0/PKG-INFO +145 -0
- inkrs-0.2.0/README.md +144 -0
- inkrs-0.2.0/THIRD_PARTY_NOTICES +159 -0
- inkrs-0.2.0/bench/p0-tiles-s1.json +50 -0
- inkrs-0.2.0/bench/p0h-tiles-1447.json +10 -0
- inkrs-0.2.0/bench/pairdiff_inksweep.py +63 -0
- inkrs-0.2.0/deny.toml +28 -0
- inkrs-0.2.0/docs/FAST.md +134 -0
- inkrs-0.2.0/docs/MODELS.md +192 -0
- inkrs-0.2.0/docs/WRITEUP.md +56 -0
- inkrs-0.2.0/fixtures/hann128.f32 +0 -0
- inkrs-0.2.0/fixtures/hann384.f32 +1786 -4
- inkrs-0.2.0/fixtures/v8-gauss1.f32 +0 -0
- inkrs-0.2.0/fixtures/v8-gauss3.f32 +0 -0
- inkrs-0.2.0/inkrs/__init__.py +860 -0
- inkrs-0.2.0/inkrs/_cuda.py +390 -0
- inkrs-0.2.0/inkrs/_inkrs.pyi +146 -0
- inkrs-0.2.0/inkrs/export_weights.py +180 -0
- inkrs-0.2.0/inkrs/py.typed +0 -0
- inkrs-0.2.0/licenses/cuda-rust-LICENSE +203 -0
- inkrs-0.2.0/metal/coreml_body.py +104 -0
- inkrs-0.2.0/ptx/inkrs.sm120.ptx +48387 -0
- inkrs-0.2.0/ptx/inkrs.sm120.sm_120.cubin +0 -0
- inkrs-0.2.0/ptx/inkrs.sm75.ptx +20432 -0
- inkrs-0.2.0/ptx/inkrs.sm75.sm_75.cubin +0 -0
- inkrs-0.2.0/ptx/inkrs.sm80.ptx +48534 -0
- inkrs-0.2.0/ptx/inkrs.sm80.sm_100.cubin +0 -0
- inkrs-0.2.0/ptx/inkrs.sm80.sm_80.cubin +0 -0
- inkrs-0.2.0/ptx/inkrs.sm80.sm_86.cubin +0 -0
- inkrs-0.2.0/ptx/inkrs.sm80.sm_89.cubin +0 -0
- inkrs-0.2.0/ptx/inkrs.sm80.sm_90.cubin +0 -0
- inkrs-0.2.0/ptx/libdevice.txt +8 -0
- inkrs-0.2.0/ptx/ptxas-sm120.txt +366 -0
- inkrs-0.2.0/ptx/ptxas-sm75.txt +231 -0
- inkrs-0.2.0/ptx/ptxas-sm80.txt +366 -0
- inkrs-0.2.0/ptx/ptxas.txt +1 -0
- inkrs-0.2.0/ptx/sources.sha256 +36 -0
- inkrs-0.2.0/ptx/tools.txt +4 -0
- inkrs-0.2.0/pyproject.toml +63 -0
- inkrs-0.2.0/python/API.md +265 -0
- inkrs-0.2.0/python/Cargo.toml +20 -0
- inkrs-0.2.0/python/LICENSE-APACHE +202 -0
- inkrs-0.2.0/python/LICENSE-MIT +21 -0
- inkrs-0.2.0/python/README.md +104 -0
- inkrs-0.2.0/python/THIRD_PARTY_NOTICES +159 -0
- inkrs-0.2.0/python/build-wheel.sh +46 -0
- inkrs-0.2.0/python/src/lib.rs +608 -0
- inkrs-0.2.0/python/src/v8in.rs +117 -0
- inkrs-0.2.0/python/tests/gpu_parity.py +207 -0
- inkrs-0.2.0/python/tests/test_api.py +651 -0
- inkrs-0.2.0/results/README.md +213 -0
- inkrs-0.2.0/results/accuracy-speed.json +15848 -0
- inkrs-0.2.0/results/coverage-mutations-s1.json +50 -0
- inkrs-0.2.0/results/fast-mode-timing.json +337 -0
- inkrs-0.2.0/results/first-run.json +238 -0
- inkrs-0.2.0/results/hecate-inksweep-timing.json +64 -0
- inkrs-0.2.0/results/hecate-kernel-timing.json +56 -0
- inkrs-0.2.0/results/hecate-maps-crop.json +335 -0
- inkrs-0.2.0/results/hecate-p0h.json +812 -0
- inkrs-0.2.0/results/hecate-timing.json +812 -0
- inkrs-0.2.0/results/ink3d-gates.json +40 -0
- inkrs-0.2.0/results/inkrs-report-r343-gate.json +486 -0
- inkrs-0.2.0/results/inkrs-report-r343.json +698 -0
- inkrs-0.2.0/results/inkrs-report-s1.json +698 -0
- inkrs-0.2.0/results/inksweep-passes-r343.json +1170 -0
- inkrs-0.2.0/results/inksweep-passes-s1.json +1035 -0
- inkrs-0.2.0/results/inksweep-vs-villa-r343-calibrated.json +790 -0
- inkrs-0.2.0/results/inksweep-vs-villa-r343-raw.json +197 -0
- inkrs-0.2.0/results/inksweep-vs-villa-s1-calibrated.json +790 -0
- inkrs-0.2.0/results/integration-v0.1.json +259 -0
- inkrs-0.2.0/results/integration-v0.2.json +245 -0
- inkrs-0.2.0/results/letters-r343-multiscale.json +258 -0
- inkrs-0.2.0/results/metal-gates.json +190 -0
- inkrs-0.2.0/results/metal-s1.json +129 -0
- inkrs-0.2.0/results/normaliser-gpu.json +4 -0
- inkrs-0.2.0/results/other-gpu-a10.json +1608 -0
- inkrs-0.2.0/results/other-gpu-tesla-t4.json +738 -0
- inkrs-0.2.0/results/other-gpus-v0.2.json +2231 -0
- inkrs-0.2.0/results/p0-s1.json +1326 -0
- inkrs-0.2.0/results/p1-r343-multiscale.json +276 -0
- inkrs-0.2.0/results/p1-r343.json +276 -0
- inkrs-0.2.0/results/p1-s1.json +276 -0
- inkrs-0.2.0/results/prize-models-gates.json +594 -0
- inkrs-0.2.0/results/ptx-tiers-models.json +494 -0
- inkrs-0.2.0/results/ptx-tiers-s1.json +228 -0
- inkrs-0.2.0/results/public-ports.json +686 -0
- inkrs-0.2.0/results/r343-map-figure.json +28 -0
- inkrs-0.2.0/results/r343-map-figure.png +0 -0
- inkrs-0.2.0/results/reader-v2-p1.json +308 -0
- inkrs-0.2.0/results/reader-v2-published.json +56 -0
- inkrs-0.2.0/results/reader-v2-timing.json +179 -0
- inkrs-0.2.0/results/reader-v2-villa-engine.json +169 -0
- inkrs-0.2.0/results/reader-v3-norm.json +29 -0
- inkrs-0.2.0/results/reader-v3-p0.json +16 -0
- inkrs-0.2.0/results/reader-v3-p1.json +82 -0
- inkrs-0.2.0/results/reader-v3-timing.json +146 -0
- inkrs-0.2.0/results/recipe343-timing.json +78 -0
- inkrs-0.2.0/results/release-checks.json +235 -0
- inkrs-0.2.0/results/release-timing.json +875 -0
- inkrs-0.2.0/results/repeatability.json +77 -0
- inkrs-0.2.0/results/software.json +16 -0
- inkrs-0.2.0/results/t4-gates.json +119 -0
- inkrs-0.2.0/results/t4-models.json +436 -0
- inkrs-0.2.0/results/t4-speed.json +29 -0
- inkrs-0.2.0/results/timing-matched.json +324 -0
- inkrs-0.2.0/results/timing-villa-fast-flags.json +51 -0
- inkrs-0.2.0/results/tta-306b-timing.json +60 -0
- inkrs-0.2.0/results/v8in-306b.json +230 -0
- inkrs-0.2.0/results/windows-checks.json +62 -0
- inkrs-0.2.0/rustfmt.toml +3 -0
- inkrs-0.2.0/scripts/build-cubin.sh +30 -0
- inkrs-0.2.0/scripts/build-ptx.sh +147 -0
- inkrs-0.2.0/scripts/c_letters.py +152 -0
- inkrs-0.2.0/scripts/check-ptx.sh +52 -0
- inkrs-0.2.0/scripts/export_hecate.py +106 -0
- inkrs-0.2.0/scripts/export_unet3d.py +276 -0
- inkrs-0.2.0/scripts/export_v8in.py +182 -0
- inkrs-0.2.0/scripts/export_weights.py +8 -0
- inkrs-0.2.0/scripts/fingerprint.py +17 -0
- inkrs-0.2.0/scripts/gate_mutations.py +101 -0
- inkrs-0.2.0/scripts/hecate_mapgate.py +315 -0
- inkrs-0.2.0/scripts/inksweep_matched.py +135 -0
- inkrs-0.2.0/scripts/make_hecate_inputs.py +134 -0
- inkrs-0.2.0/scripts/make_pyramid.py +94 -0
- inkrs-0.2.0/scripts/make_s1.py +22 -0
- inkrs-0.2.0/scripts/mapdiff.py +99 -0
- inkrs-0.2.0/scripts/mapgate3d.py +163 -0
- inkrs-0.2.0/scripts/norm_fixture.py +134 -0
- inkrs-0.2.0/scripts/p0_compare.py +103 -0
- inkrs-0.2.0/scripts/p0_hecate.py +402 -0
- inkrs-0.2.0/scripts/p0_ink3d.py +182 -0
- inkrs-0.2.0/scripts/p0_torch.py +112 -0
- inkrs-0.2.0/scripts/p1_gate.py +123 -0
- inkrs-0.2.0/scripts/p1_mutate.py +107 -0
- inkrs-0.2.0/scripts/recipe.py +695 -0
- inkrs-0.2.0/scripts/recipe343_gate.py +240 -0
- inkrs-0.2.0/scripts/v8_windows.py +26 -0
- inkrs-0.2.0/scripts/villa_coverage.py +113 -0
- inkrs-0.2.0/scripts/villa_map3d.py +116 -0
- inkrs-0.2.0/src/checks.rs +616 -0
- inkrs-0.2.0/src/conv.rs +425 -0
- inkrs-0.2.0/src/dll.rs +627 -0
- inkrs-0.2.0/src/dnn/algo_cache.rs +723 -0
- inkrs-0.2.0/src/dnn/bias_act.rs +129 -0
- inkrs-0.2.0/src/dnn/fused.rs +637 -0
- inkrs-0.2.0/src/dnn/gemm_bf16.rs +50 -0
- inkrs-0.2.0/src/dnn.rs +604 -0
- inkrs-0.2.0/src/engine/tta.rs +159 -0
- inkrs-0.2.0/src/engine.rs +1083 -0
- inkrs-0.2.0/src/error.rs +165 -0
- inkrs-0.2.0/src/fast.rs +211 -0
- inkrs-0.2.0/src/gpu.rs +637 -0
- inkrs-0.2.0/src/hecate/engine.rs +406 -0
- inkrs-0.2.0/src/hecate/model.rs +997 -0
- inkrs-0.2.0/src/hecate/p0.rs +104 -0
- inkrs-0.2.0/src/hecate.rs +287 -0
- inkrs-0.2.0/src/lib.rs +42 -0
- inkrs-0.2.0/src/main.rs +1064 -0
- inkrs-0.2.0/src/metal/common.metal +55 -0
- inkrs-0.2.0/src/metal/conv.metal +304 -0
- inkrs-0.2.0/src/metal/coreml.rs +504 -0
- inkrs-0.2.0/src/metal/engine.rs +869 -0
- inkrs-0.2.0/src/metal/glue.metal +603 -0
- inkrs-0.2.0/src/metal/gpu.rs +699 -0
- inkrs-0.2.0/src/metal/hecate/engine.rs +365 -0
- inkrs-0.2.0/src/metal/hecate/model.rs +1006 -0
- inkrs-0.2.0/src/metal/hecate.metal +600 -0
- inkrs-0.2.0/src/metal/hecate.rs +5 -0
- inkrs-0.2.0/src/metal/model.rs +1114 -0
- inkrs-0.2.0/src/metal/v8in/engine.rs +262 -0
- inkrs-0.2.0/src/metal/v8in/model.rs +1291 -0
- inkrs-0.2.0/src/metal/v8in.metal +493 -0
- inkrs-0.2.0/src/metal/v8in.rs +5 -0
- inkrs-0.2.0/src/metal.rs +53 -0
- inkrs-0.2.0/src/model.rs +1034 -0
- inkrs-0.2.0/src/probe.rs +716 -0
- inkrs-0.2.0/src/sched.rs +884 -0
- inkrs-0.2.0/src/tiff.rs +199 -0
- inkrs-0.2.0/src/tta.rs +67 -0
- inkrs-0.2.0/src/unet3d/engine.rs +476 -0
- inkrs-0.2.0/src/unet3d/model.rs +874 -0
- inkrs-0.2.0/src/unet3d/p0.rs +157 -0
- inkrs-0.2.0/src/unet3d/zio.rs +215 -0
- inkrs-0.2.0/src/unet3d.rs +305 -0
- inkrs-0.2.0/src/v8in/engine.rs +263 -0
- inkrs-0.2.0/src/v8in/host.rs +384 -0
- inkrs-0.2.0/src/v8in/model.rs +582 -0
- inkrs-0.2.0/src/v8in.rs +518 -0
- inkrs-0.2.0/src/weights.rs +396 -0
- inkrs-0.2.0/src/zarr.rs +401 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# LF in every checkout, Windows included: the PTX tiers are embedded byte for byte (include_str!) and checked
|
|
2
|
+
# against hashes, and the villa patches must apply as written, so CRLF would change what a build ships
|
|
3
|
+
* text=auto eol=lf
|
|
4
|
+
# float fixtures are raw little-endian bytes
|
|
5
|
+
*.f32 binary
|
|
6
|
+
# compiled kernels, embedded byte for byte (include_bytes!)
|
|
7
|
+
*.cubin binary
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Builds and checks inkrs on Linux and Windows x86_64: formatting, clippy, the CPU tests and the CLI, plus the
|
|
2
|
+
# dependency policy and the Python lints. package.yml builds and tests the Python wheels and sdist. A GitHub runner
|
|
3
|
+
# has no GPU, so nothing here runs a model; the GPU checks are in results/README.md. CUDA loads at run time, so no
|
|
4
|
+
# job installs a CUDA toolkit
|
|
5
|
+
|
|
6
|
+
name: CI
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
workflow_dispatch: {}
|
|
10
|
+
push:
|
|
11
|
+
branches:
|
|
12
|
+
- master
|
|
13
|
+
pull_request:
|
|
14
|
+
branches:
|
|
15
|
+
- master
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
# a new push to a pull request supersedes its running checks
|
|
21
|
+
concurrency:
|
|
22
|
+
group: ci-${{ github.ref }}
|
|
23
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
24
|
+
|
|
25
|
+
env:
|
|
26
|
+
CARGO_TERM_COLOR: always
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
rust:
|
|
30
|
+
name: Rust (${{ matrix.name }})
|
|
31
|
+
# MSVC links slower
|
|
32
|
+
timeout-minutes: ${{ matrix.os == 'windows-2025' && 45 || 30 }}
|
|
33
|
+
strategy:
|
|
34
|
+
fail-fast: false
|
|
35
|
+
matrix:
|
|
36
|
+
include:
|
|
37
|
+
- name: Linux x86_64
|
|
38
|
+
os: ubuntu-24.04
|
|
39
|
+
rustflags: -D warnings
|
|
40
|
+
# the C runtime is linked statically, so inkrs.exe runs without the Visual C++ redistributable. RUSTFLAGS
|
|
41
|
+
# replaces the per-target flags rather than adding to them, so each target spells out its full set
|
|
42
|
+
- name: Windows x86_64
|
|
43
|
+
os: windows-2025
|
|
44
|
+
rustflags: -D warnings -C target-feature=+crt-static
|
|
45
|
+
runs-on: ${{ matrix.os }}
|
|
46
|
+
env:
|
|
47
|
+
RUSTFLAGS: ${{ matrix.rustflags }}
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
50
|
+
|
|
51
|
+
- name: Install Rust toolchain
|
|
52
|
+
uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable
|
|
53
|
+
with:
|
|
54
|
+
toolchain: stable
|
|
55
|
+
components: rustfmt, clippy
|
|
56
|
+
|
|
57
|
+
- uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
|
|
58
|
+
|
|
59
|
+
# the PTX tiers are embedded byte for byte, so a checkout must not change a line ending; .gitattributes keeps
|
|
60
|
+
# every file LF, and renormalising must find nothing to change
|
|
61
|
+
- name: Line endings
|
|
62
|
+
shell: bash
|
|
63
|
+
run: |
|
|
64
|
+
set -euo pipefail
|
|
65
|
+
git add --renormalize .
|
|
66
|
+
git diff --cached --exit-code
|
|
67
|
+
|
|
68
|
+
- name: Format
|
|
69
|
+
if: runner.os == 'Linux'
|
|
70
|
+
run: cargo fmt --all -- --check
|
|
71
|
+
|
|
72
|
+
# hashes and file lists only; the PTX itself is built with cuda-oxide and the CUDA toolkit
|
|
73
|
+
- name: Committed PTX matches the kernel sources
|
|
74
|
+
if: runner.os == 'Linux'
|
|
75
|
+
run: scripts/check-ptx.sh
|
|
76
|
+
|
|
77
|
+
- name: Clippy
|
|
78
|
+
run: cargo clippy --workspace --all-targets --locked -- -D warnings
|
|
79
|
+
|
|
80
|
+
# the tests that need a GPU open one and fail without it, so only the CPU tests run here; the Windows leg also
|
|
81
|
+
# runs the DLL search's own tests
|
|
82
|
+
- name: Tests
|
|
83
|
+
run: cargo test --workspace --exclude inkrs-python --release --locked
|
|
84
|
+
|
|
85
|
+
- name: Build the CLI
|
|
86
|
+
run: cargo build --release --locked --bin inkrs
|
|
87
|
+
|
|
88
|
+
# a static C runtime keeps vcruntime140.dll and the UCRT out of the imports, and CUDA must load at run time,
|
|
89
|
+
# so the binary may import only Windows system DLLs
|
|
90
|
+
- name: Check the CLI's imports and that it starts
|
|
91
|
+
if: runner.os == 'Windows'
|
|
92
|
+
shell: bash
|
|
93
|
+
run: |
|
|
94
|
+
set -euo pipefail
|
|
95
|
+
bin=target/release/inkrs.exe
|
|
96
|
+
rustup component add llvm-tools
|
|
97
|
+
readobj="$(cygpath -u "$(rustc --print sysroot)")/lib/rustlib/x86_64-pc-windows-msvc/bin/llvm-readobj.exe"
|
|
98
|
+
# a Windows tool's output ends lines with CRLF
|
|
99
|
+
imports="$("$readobj" --coff-imports "$bin" | tr -d '\r' | sed -n 's/^ *Name: //p' | sort -u)"
|
|
100
|
+
echo "$imports"
|
|
101
|
+
if grep -Eiv '^(kernel32|ntdll|advapi32|bcrypt|bcryptprimitives|ws2_32|userenv|secur32|crypt32|ole32|oleaut32|shell32|user32|synchronization|api-ms-win-core-[a-z0-9-]+)\.dll$' <<< "$imports"; then
|
|
102
|
+
echo "the binary imports a DLL outside Windows itself" >&2
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
"$bin" --help
|
|
106
|
+
# the runner has no NVIDIA driver, so opening the GPU must fail naming the Windows driver, not panic
|
|
107
|
+
if out="$("$bin" probe 2>&1)"; then
|
|
108
|
+
echo "probe succeeded without a GPU" >&2
|
|
109
|
+
exit 1
|
|
110
|
+
fi
|
|
111
|
+
echo "$out"
|
|
112
|
+
grep -q 'nvcuda.dll' <<< "$out"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# advisories, licences and sources (deny.toml): every dependency comes from crates.io
|
|
116
|
+
deny:
|
|
117
|
+
name: Dependency policy
|
|
118
|
+
runs-on: ubuntu-24.04
|
|
119
|
+
steps:
|
|
120
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
121
|
+
|
|
122
|
+
- name: Install Rust toolchain
|
|
123
|
+
uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable
|
|
124
|
+
with:
|
|
125
|
+
toolchain: stable
|
|
126
|
+
|
|
127
|
+
# a prebuilt binary instead of cargo-deny-action, whose Docker build fails whenever Docker Hub rate-limits the
|
|
128
|
+
# runner
|
|
129
|
+
- name: Install cargo-deny
|
|
130
|
+
uses: taiki-e/install-action@86f7a0ffb25f40114606501a311ebcfaf882ea32 # v2.88.0
|
|
131
|
+
with:
|
|
132
|
+
tool: cargo-deny@0.20.2
|
|
133
|
+
|
|
134
|
+
- name: Check dependency policy
|
|
135
|
+
run: cargo deny --workspace --all-features check
|
|
136
|
+
|
|
137
|
+
python-lint:
|
|
138
|
+
name: Python lints
|
|
139
|
+
runs-on: ubuntu-24.04
|
|
140
|
+
steps:
|
|
141
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
142
|
+
|
|
143
|
+
- name: Install uv
|
|
144
|
+
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
145
|
+
|
|
146
|
+
# pinned, because a ruff release that adds lints would otherwise fail an unchanged tree
|
|
147
|
+
- name: Ruff
|
|
148
|
+
run: uvx ruff@0.17.4 check python
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
# Builds and tests the inkrs Python package, its wheels and sdist, on every push and pull request, and publishes
|
|
2
|
+
# them from a version tag
|
|
3
|
+
#
|
|
4
|
+
# To release:
|
|
5
|
+
# 1. set the version in Cargo.toml and python/Cargo.toml; pyproject.toml takes it from python/Cargo.toml
|
|
6
|
+
# 2. in CHANGELOG.md, give the version's entry its release date ("## [X.Y.Z] - YYYY-MM-DD", replacing
|
|
7
|
+
# "Unreleased") and its link at the bottom
|
|
8
|
+
# 3. merge, then tag the merge commit and push the tag:
|
|
9
|
+
# git tag vX.Y.Z
|
|
10
|
+
# git push origin vX.Y.Z
|
|
11
|
+
#
|
|
12
|
+
# The tag publishes to TestPyPI and installs that release on each platform. The pypi environment then waits for a
|
|
13
|
+
# reviewer, and once approved the workflow publishes to PyPI, then a GitHub release with the tag's CHANGELOG.md
|
|
14
|
+
# entry as its notes and the wheels and sdist attached. A tag whose version or changelog entry is wrong fails
|
|
15
|
+
# before anything is published. PyPI and TestPyPI trust this workflow and its environments through OIDC, so no
|
|
16
|
+
# token is stored. Every publish skips files already uploaded, so "Re-run failed jobs" finishes a partial release.
|
|
17
|
+
# Third-party actions are pinned to commit SHAs, since these jobs build and publish the release files; the trailing
|
|
18
|
+
# comment names the version
|
|
19
|
+
|
|
20
|
+
name: Package
|
|
21
|
+
|
|
22
|
+
on:
|
|
23
|
+
workflow_dispatch: {}
|
|
24
|
+
push:
|
|
25
|
+
branches:
|
|
26
|
+
- master
|
|
27
|
+
tags:
|
|
28
|
+
- "v*"
|
|
29
|
+
pull_request:
|
|
30
|
+
branches:
|
|
31
|
+
- master
|
|
32
|
+
|
|
33
|
+
permissions:
|
|
34
|
+
contents: read
|
|
35
|
+
|
|
36
|
+
# a new push to a pull request supersedes its running checks; a tag's run is never cancelled
|
|
37
|
+
concurrency:
|
|
38
|
+
group: package-${{ github.ref }}
|
|
39
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
40
|
+
|
|
41
|
+
env:
|
|
42
|
+
CARGO_TERM_COLOR: always
|
|
43
|
+
|
|
44
|
+
jobs:
|
|
45
|
+
# one abi3 wheel per platform with the backend that platform runs, installed and tested on the oldest and a
|
|
46
|
+
# current CPython. The Linux and Windows wheels build without a GPU or CUDA toolkit because CUDA loads at run time
|
|
47
|
+
# and the kernels ship compiled; the macOS wheel needs no Metal toolchain because the shaders compile when the
|
|
48
|
+
# engine opens. A runner has no GPU, so nothing here runs a model; the GPU checks are in results/README.md
|
|
49
|
+
wheels:
|
|
50
|
+
name: Wheel (${{ matrix.name }})
|
|
51
|
+
timeout-minutes: 45
|
|
52
|
+
strategy:
|
|
53
|
+
fail-fast: false
|
|
54
|
+
matrix:
|
|
55
|
+
include:
|
|
56
|
+
- name: Linux x86_64, CUDA
|
|
57
|
+
os: ubuntu-24.04
|
|
58
|
+
target: x86_64-unknown-linux-gnu
|
|
59
|
+
manylinux: "2_28"
|
|
60
|
+
rustflags: -D warnings
|
|
61
|
+
- name: macOS arm64, Metal
|
|
62
|
+
os: macos-26
|
|
63
|
+
target: aarch64-apple-darwin
|
|
64
|
+
manylinux: "off"
|
|
65
|
+
rustflags: -D warnings
|
|
66
|
+
# the C runtime is linked statically, so the extension runs without the Visual C++ redistributable whichever
|
|
67
|
+
# Python loads it. RUSTFLAGS replaces the per-target flags rather than adding to them, so each target spells
|
|
68
|
+
# out its full set
|
|
69
|
+
- name: Windows x86_64, CUDA
|
|
70
|
+
os: windows-2025
|
|
71
|
+
target: x86_64-pc-windows-msvc
|
|
72
|
+
manylinux: "off"
|
|
73
|
+
rustflags: -D warnings -C target-feature=+crt-static
|
|
74
|
+
runs-on: ${{ matrix.os }}
|
|
75
|
+
env:
|
|
76
|
+
RUSTFLAGS: ${{ matrix.rustflags }}
|
|
77
|
+
# the Metal engine needs macOS 26, but the wheel keeps the arm64 floor so pip installs it on any Apple silicon
|
|
78
|
+
# Mac, where an older macOS gets the engine's UnsupportedError naming the version instead of a failed sdist
|
|
79
|
+
# build; other platforms ignore this
|
|
80
|
+
MACOSX_DEPLOYMENT_TARGET: "11.0"
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
83
|
+
|
|
84
|
+
# a Windows checkout without symlink support writes each licence symlink in python/ as a text file holding its
|
|
85
|
+
# target, which maturin would ship in the wheel as the licence; removing it first keeps cp from writing through
|
|
86
|
+
# a real symlink onto itself
|
|
87
|
+
- name: Resolve the licence symlinks
|
|
88
|
+
if: runner.os == 'Windows'
|
|
89
|
+
shell: bash
|
|
90
|
+
run: |
|
|
91
|
+
set -euo pipefail
|
|
92
|
+
for f in LICENSE-MIT LICENSE-APACHE THIRD_PARTY_NOTICES; do
|
|
93
|
+
rm -f "python/$f"
|
|
94
|
+
cp "$f" "python/$f"
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
- name: Build wheel
|
|
98
|
+
uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
|
|
99
|
+
with:
|
|
100
|
+
command: build
|
|
101
|
+
target: ${{ matrix.target }}
|
|
102
|
+
manylinux: ${{ matrix.manylinux }}
|
|
103
|
+
args: --release --locked --out dist -m python/Cargo.toml
|
|
104
|
+
# tag builds are published, so they never reuse objects from the shared cache
|
|
105
|
+
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
|
106
|
+
|
|
107
|
+
- name: Check wheel size
|
|
108
|
+
shell: bash
|
|
109
|
+
run: |
|
|
110
|
+
set -euo pipefail
|
|
111
|
+
# PyPI rejects files over 100 MB unless the project's limit is raised
|
|
112
|
+
limit=$((90 * 1000 * 1000))
|
|
113
|
+
for wheel in dist/*.whl; do
|
|
114
|
+
size="$(wc -c < "$wheel")"
|
|
115
|
+
echo "$(basename "$wheel"): $size bytes"
|
|
116
|
+
test "$size" -le "$limit"
|
|
117
|
+
done
|
|
118
|
+
|
|
119
|
+
# pip on macOS before 26 skips a wheel tagged for a newer macOS and tries to build the sdist instead
|
|
120
|
+
- name: Check the macOS platform tag
|
|
121
|
+
if: runner.os == 'macOS'
|
|
122
|
+
shell: bash
|
|
123
|
+
run: |
|
|
124
|
+
set -euo pipefail
|
|
125
|
+
ls dist/*-macosx_11_0_arm64.whl
|
|
126
|
+
|
|
127
|
+
# CUDA must load at run time and the C runtime is static, so the extension may import only Python's stable ABI
|
|
128
|
+
# and Windows system DLLs
|
|
129
|
+
- name: Check the extension's imports
|
|
130
|
+
if: runner.os == 'Windows'
|
|
131
|
+
shell: bash
|
|
132
|
+
run: |
|
|
133
|
+
set -euo pipefail
|
|
134
|
+
unpacked="$RUNNER_TEMP/wheel"
|
|
135
|
+
mkdir -p "$unpacked"
|
|
136
|
+
# 7-Zip ships on the runner image, and a wheel is a zip
|
|
137
|
+
7z x -y dist/*.whl -o"$unpacked" > /dev/null
|
|
138
|
+
ext="$(find "$unpacked" -name '_inkrs*.pyd' -print -quit)"
|
|
139
|
+
test -n "$ext"
|
|
140
|
+
rustup component add llvm-tools
|
|
141
|
+
readobj="$(cygpath -u "$(rustc --print sysroot)")/lib/rustlib/x86_64-pc-windows-msvc/bin/llvm-readobj.exe"
|
|
142
|
+
# a Windows tool's output ends lines with CRLF
|
|
143
|
+
imports="$("$readobj" --coff-imports "$ext" | tr -d '\r' | sed -n 's/^ *Name: //p' | sort -u)"
|
|
144
|
+
echo "$imports"
|
|
145
|
+
if grep -Eiv '^(python3|kernel32|ntdll|advapi32|bcrypt|bcryptprimitives|ws2_32|userenv|secur32|crypt32|ole32|oleaut32|shell32|user32|synchronization|api-ms-win-core-[a-z0-9-]+)\.dll$' <<< "$imports"; then
|
|
146
|
+
echo "the extension imports a DLL outside Python and Windows itself" >&2
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
- name: Install uv
|
|
151
|
+
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
152
|
+
|
|
153
|
+
# the oldest and a current CPython, since the wheel is abi3; the tests need no GPU, cuDNN or checkpoint
|
|
154
|
+
- name: Install the wheel and run its CPU tests
|
|
155
|
+
shell: bash
|
|
156
|
+
run: |
|
|
157
|
+
set -euo pipefail
|
|
158
|
+
for python in 3.9 3.13; do
|
|
159
|
+
venv="$RUNNER_TEMP/venv-$python"
|
|
160
|
+
uv venv --python "$python" "$venv"
|
|
161
|
+
# Windows venvs keep the interpreter in Scripts, not bin
|
|
162
|
+
py="$venv/bin/python"
|
|
163
|
+
if [ "$RUNNER_OS" = Windows ]; then py="$venv/Scripts/python.exe"; fi
|
|
164
|
+
uv pip install --python "$py" dist/*.whl pytest tifffile imagecodecs
|
|
165
|
+
"$py" -c "import inkrs; print(inkrs.__version__, inkrs.build_info())"
|
|
166
|
+
"$py" -m pytest -q -rs python/tests/test_api.py
|
|
167
|
+
done
|
|
168
|
+
|
|
169
|
+
- name: Upload wheel
|
|
170
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
171
|
+
with:
|
|
172
|
+
name: python-wheel-${{ matrix.target }}
|
|
173
|
+
path: dist/*.whl
|
|
174
|
+
|
|
175
|
+
sdist:
|
|
176
|
+
name: Sdist
|
|
177
|
+
runs-on: ubuntu-24.04
|
|
178
|
+
timeout-minutes: 30
|
|
179
|
+
env:
|
|
180
|
+
RUSTFLAGS: -D warnings
|
|
181
|
+
steps:
|
|
182
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
183
|
+
|
|
184
|
+
# the package takes its version from python/Cargo.toml and release tags are named after the engine's, so a
|
|
185
|
+
# bindings version that lags behind fails here on the pull request instead of at publish time
|
|
186
|
+
- name: Check the bindings version matches inkrs
|
|
187
|
+
run: |
|
|
188
|
+
set -euo pipefail
|
|
189
|
+
engine=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
|
|
190
|
+
bindings=$(sed -n 's/^version = "\(.*\)"/\1/p' python/Cargo.toml | head -1)
|
|
191
|
+
if [ "$engine" != "$bindings" ]; then
|
|
192
|
+
echo "python/Cargo.toml is $bindings but inkrs is $engine; bump python/Cargo.toml"
|
|
193
|
+
exit 1
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
- name: Build sdist
|
|
197
|
+
uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
|
|
198
|
+
with:
|
|
199
|
+
command: sdist
|
|
200
|
+
args: --out dist -m python/Cargo.toml
|
|
201
|
+
|
|
202
|
+
- name: Install Rust toolchain
|
|
203
|
+
uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable
|
|
204
|
+
with:
|
|
205
|
+
toolchain: stable
|
|
206
|
+
|
|
207
|
+
- name: Install uv
|
|
208
|
+
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
209
|
+
|
|
210
|
+
# pip builds the sdist where no wheel fits, so build it the same way, outside the checkout. The engine embeds
|
|
211
|
+
# the kernels from ptx/ at compile time, so a build that finds them all proves the sdist carries them; the
|
|
212
|
+
# runner has no GPU, so the check stops at what the import reports
|
|
213
|
+
- name: Install sdist into a fresh environment
|
|
214
|
+
shell: bash
|
|
215
|
+
working-directory: ${{ runner.temp }}
|
|
216
|
+
run: |
|
|
217
|
+
set -euo pipefail
|
|
218
|
+
uv venv --python 3.12 sdist-venv
|
|
219
|
+
uv pip install --python sdist-venv "$GITHUB_WORKSPACE"/dist/*.tar.gz
|
|
220
|
+
version=$(sed -n 's/^version = "\(.*\)"/\1/p' "$GITHUB_WORKSPACE/python/Cargo.toml" | head -1)
|
|
221
|
+
sdist-venv/bin/python -I - "$version" <<'EOF'
|
|
222
|
+
import sys
|
|
223
|
+
|
|
224
|
+
import inkrs
|
|
225
|
+
|
|
226
|
+
info = inkrs.build_info()
|
|
227
|
+
print(inkrs.__version__, info)
|
|
228
|
+
assert inkrs.__version__ == info["version"] == sys.argv[1], (inkrs.__version__, sys.argv[1])
|
|
229
|
+
assert [p["target"] for p in info["ptx"]] == ["sm_75", "sm_80", "sm_120"], info["ptx"]
|
|
230
|
+
# the sdist must carry the cubins too, or a wheel built from it JITs on every GPU
|
|
231
|
+
assert len(info["cubins"]) == 7, info["cubins"]
|
|
232
|
+
EOF
|
|
233
|
+
|
|
234
|
+
- name: Upload sdist
|
|
235
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
236
|
+
with:
|
|
237
|
+
name: python-sdist
|
|
238
|
+
path: dist/*.tar.gz
|
|
239
|
+
|
|
240
|
+
# checks a tag before anything is published: both crates' versions must match it, and CHANGELOG.md needs a dated
|
|
241
|
+
# entry for it, which becomes the GitHub release notes
|
|
242
|
+
release-check:
|
|
243
|
+
name: Check the tag
|
|
244
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
245
|
+
runs-on: ubuntu-24.04
|
|
246
|
+
steps:
|
|
247
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
248
|
+
|
|
249
|
+
# the GitHub release reads its notes with parse-changelog, so the same parser checks the entry here
|
|
250
|
+
- name: Install parse-changelog
|
|
251
|
+
uses: taiki-e/install-action@86f7a0ffb25f40114606501a311ebcfaf882ea32 # v2.88.0
|
|
252
|
+
with:
|
|
253
|
+
tool: parse-changelog
|
|
254
|
+
|
|
255
|
+
- name: Check the version and changelog
|
|
256
|
+
shell: bash
|
|
257
|
+
run: |
|
|
258
|
+
set -euo pipefail
|
|
259
|
+
version="${GITHUB_REF_NAME#v}"
|
|
260
|
+
for manifest in Cargo.toml python/Cargo.toml; do
|
|
261
|
+
crate=$(sed -n 's/^version = "\(.*\)"/\1/p' "$manifest" | head -1)
|
|
262
|
+
if [ "$crate" != "$version" ]; then
|
|
263
|
+
echo "$manifest is $crate but the tag is $GITHUB_REF_NAME"
|
|
264
|
+
exit 1
|
|
265
|
+
fi
|
|
266
|
+
done
|
|
267
|
+
# an entry still marked "Unreleased" has not been given its date
|
|
268
|
+
title="$(parse-changelog CHANGELOG.md --title "$version")"
|
|
269
|
+
if ! [[ "$title" =~ ^\[${version//./\\.}\]\ -\ [0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
|
270
|
+
echo "the CHANGELOG.md heading for $version is \"$title\"; it needs \"[$version] - YYYY-MM-DD\""
|
|
271
|
+
exit 1
|
|
272
|
+
fi
|
|
273
|
+
notes="$(parse-changelog CHANGELOG.md "$version")"
|
|
274
|
+
if [ -z "$notes" ]; then
|
|
275
|
+
echo "the CHANGELOG.md entry for $version is empty"
|
|
276
|
+
exit 1
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
# a v* tag publishes to TestPyPI, installs that release, and only then publishes to PyPI; each index trusts this
|
|
280
|
+
# workflow and environment, so no token is stored
|
|
281
|
+
testpypi-publish:
|
|
282
|
+
name: Publish to TestPyPI
|
|
283
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
284
|
+
needs: [release-check, wheels, sdist]
|
|
285
|
+
runs-on: ubuntu-24.04
|
|
286
|
+
environment:
|
|
287
|
+
name: testpypi
|
|
288
|
+
url: https://test.pypi.org/project/inkrs/
|
|
289
|
+
permissions:
|
|
290
|
+
id-token: write
|
|
291
|
+
steps:
|
|
292
|
+
- name: Download wheels and sdist
|
|
293
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
294
|
+
with:
|
|
295
|
+
pattern: python-*
|
|
296
|
+
path: dist
|
|
297
|
+
merge-multiple: true
|
|
298
|
+
|
|
299
|
+
- name: Check files match the tag
|
|
300
|
+
shell: bash
|
|
301
|
+
run: |
|
|
302
|
+
set -euo pipefail
|
|
303
|
+
version="${GITHUB_REF_NAME#v}"
|
|
304
|
+
ls -la dist
|
|
305
|
+
# the Linux, macOS and Windows wheels and the sdist
|
|
306
|
+
test "$(find dist -type f | wc -l)" -eq 4
|
|
307
|
+
for file in dist/*; do
|
|
308
|
+
case "$(basename "$file")" in
|
|
309
|
+
"inkrs-${version}"-*.whl | "inkrs-${version}.tar.gz") ;;
|
|
310
|
+
*) echo "$file does not match tag $GITHUB_REF_NAME"; exit 1 ;;
|
|
311
|
+
esac
|
|
312
|
+
done
|
|
313
|
+
|
|
314
|
+
- name: Publish
|
|
315
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
316
|
+
with:
|
|
317
|
+
repository-url: https://test.pypi.org/legacy/
|
|
318
|
+
skip-existing: true
|
|
319
|
+
|
|
320
|
+
testpypi-install:
|
|
321
|
+
name: Install from TestPyPI (${{ matrix.name }})
|
|
322
|
+
needs: testpypi-publish
|
|
323
|
+
runs-on: ${{ matrix.os }}
|
|
324
|
+
strategy:
|
|
325
|
+
fail-fast: false
|
|
326
|
+
matrix:
|
|
327
|
+
include:
|
|
328
|
+
- name: Linux x86_64
|
|
329
|
+
os: ubuntu-24.04
|
|
330
|
+
backend: cuda
|
|
331
|
+
- name: macOS arm64
|
|
332
|
+
os: macos-26
|
|
333
|
+
backend: metal
|
|
334
|
+
- name: Windows x86_64
|
|
335
|
+
os: windows-2025
|
|
336
|
+
backend: cuda
|
|
337
|
+
steps:
|
|
338
|
+
- name: Install uv
|
|
339
|
+
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
340
|
+
|
|
341
|
+
- name: Install the release and import it
|
|
342
|
+
shell: bash
|
|
343
|
+
working-directory: ${{ runner.temp }}
|
|
344
|
+
env:
|
|
345
|
+
BACKEND: ${{ matrix.backend }}
|
|
346
|
+
run: |
|
|
347
|
+
set -euo pipefail
|
|
348
|
+
version="${GITHUB_REF_NAME#v}"
|
|
349
|
+
uv venv --python 3.12 venv-release
|
|
350
|
+
# Windows venvs keep the interpreter in Scripts, not bin
|
|
351
|
+
py=venv-release/bin/python
|
|
352
|
+
if [ "$RUNNER_OS" = Windows ]; then py=venv-release/Scripts/python.exe; fi
|
|
353
|
+
# anyone can upload to TestPyPI, so only inkrs comes from it, as a wheel; numpy comes from PyPI
|
|
354
|
+
uv pip install --python "$py" "numpy>=1.22"
|
|
355
|
+
# a new release can take a few minutes to appear in the index
|
|
356
|
+
for attempt in 1 2 3 4 5 6 7 8 9 10; do
|
|
357
|
+
if uv pip install --python "$py" --no-cache --no-deps --only-binary inkrs \
|
|
358
|
+
--index-url https://test.pypi.org/simple/ "inkrs==${version}"; then
|
|
359
|
+
break
|
|
360
|
+
fi
|
|
361
|
+
test "$attempt" -lt 10
|
|
362
|
+
sleep 30
|
|
363
|
+
done
|
|
364
|
+
"$py" -I - "$version" "$BACKEND" <<'EOF'
|
|
365
|
+
import sys
|
|
366
|
+
|
|
367
|
+
import inkrs
|
|
368
|
+
|
|
369
|
+
info = inkrs.build_info()
|
|
370
|
+
print(inkrs.__version__, info)
|
|
371
|
+
assert inkrs.__version__ == sys.argv[1], (inkrs.__version__, sys.argv[1])
|
|
372
|
+
assert info["backend"] == sys.argv[2], info
|
|
373
|
+
EOF
|
|
374
|
+
|
|
375
|
+
# the pypi environment's reviewer approves the release here, before anything outside TestPyPI is published
|
|
376
|
+
pypi-publish:
|
|
377
|
+
name: Publish to PyPI
|
|
378
|
+
needs: testpypi-install
|
|
379
|
+
runs-on: ubuntu-24.04
|
|
380
|
+
environment:
|
|
381
|
+
name: pypi
|
|
382
|
+
url: https://pypi.org/project/inkrs/
|
|
383
|
+
permissions:
|
|
384
|
+
id-token: write
|
|
385
|
+
steps:
|
|
386
|
+
- name: Download wheels and sdist
|
|
387
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
388
|
+
with:
|
|
389
|
+
pattern: python-*
|
|
390
|
+
path: dist
|
|
391
|
+
merge-multiple: true
|
|
392
|
+
|
|
393
|
+
- name: Publish
|
|
394
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
395
|
+
with:
|
|
396
|
+
skip-existing: true
|
|
397
|
+
|
|
398
|
+
# the release starts as a draft and is published only once its files are attached
|
|
399
|
+
github-release:
|
|
400
|
+
name: GitHub release
|
|
401
|
+
needs: pypi-publish
|
|
402
|
+
runs-on: ubuntu-24.04
|
|
403
|
+
permissions:
|
|
404
|
+
contents: write
|
|
405
|
+
steps:
|
|
406
|
+
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
|
407
|
+
with:
|
|
408
|
+
persist-credentials: false
|
|
409
|
+
|
|
410
|
+
- name: Download wheels and sdist
|
|
411
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
412
|
+
with:
|
|
413
|
+
pattern: python-*
|
|
414
|
+
path: dist
|
|
415
|
+
merge-multiple: true
|
|
416
|
+
|
|
417
|
+
# create-gh-release-action deletes a release that already exists, so a rerun leaves a published release alone
|
|
418
|
+
# and finishes a draft instead of recreating it
|
|
419
|
+
- name: Check for an existing release
|
|
420
|
+
id: existing
|
|
421
|
+
env:
|
|
422
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
423
|
+
shell: bash
|
|
424
|
+
run: |
|
|
425
|
+
set -euo pipefail
|
|
426
|
+
if draft="$(gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" --json isDraft --jq .isDraft 2> view.err)"; then
|
|
427
|
+
if [ "$draft" = true ]; then state=draft; else state=published; fi
|
|
428
|
+
elif grep -q 'release not found' view.err; then
|
|
429
|
+
state=none
|
|
430
|
+
else
|
|
431
|
+
# any other failure could hide a release the next step would delete
|
|
432
|
+
cat view.err >&2
|
|
433
|
+
exit 1
|
|
434
|
+
fi
|
|
435
|
+
echo "the $GITHUB_REF_NAME release is $state"
|
|
436
|
+
echo "state=$state" >> "$GITHUB_OUTPUT"
|
|
437
|
+
|
|
438
|
+
- name: Create a draft release
|
|
439
|
+
if: steps.existing.outputs.state == 'none'
|
|
440
|
+
uses: taiki-e/create-gh-release-action@eba8ea96c86cca8a37f1b56e94b4d13301fba651 # v1.11.0
|
|
441
|
+
with:
|
|
442
|
+
draft: true
|
|
443
|
+
# the tag's CHANGELOG.md entry becomes the release notes
|
|
444
|
+
changelog: CHANGELOG.md
|
|
445
|
+
|
|
446
|
+
# --clobber replaces files a failed run already attached. The release becomes latest only when no published
|
|
447
|
+
# stable release has a higher version, so a maintenance release of an older line leaves the newest one as latest
|
|
448
|
+
- name: Attach the wheels and sdist, then publish
|
|
449
|
+
if: steps.existing.outputs.state != 'published'
|
|
450
|
+
env:
|
|
451
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
452
|
+
shell: bash
|
|
453
|
+
run: |
|
|
454
|
+
set -euo pipefail
|
|
455
|
+
gh release upload "$GITHUB_REF_NAME" dist/* --clobber --repo "$GITHUB_REPOSITORY"
|
|
456
|
+
|
|
457
|
+
version="${GITHUB_REF_NAME#v}"
|
|
458
|
+
latest=false
|
|
459
|
+
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
460
|
+
# a plain assignment, so set -e stops the step when the lookup fails instead of marking this release
|
|
461
|
+
# latest against an empty list
|
|
462
|
+
releases="$(gh release list --repo "$GITHUB_REPOSITORY" --exclude-drafts --exclude-pre-releases \
|
|
463
|
+
--limit 1000 --json tagName --jq '.[].tagName')"
|
|
464
|
+
newest="$(
|
|
465
|
+
printf '%s\n' "$releases" "$GITHUB_REF_NAME" \
|
|
466
|
+
| sed 's/^v//' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1
|
|
467
|
+
)"
|
|
468
|
+
if [ "$newest" = "$version" ]; then latest=true; fi
|
|
469
|
+
fi
|
|
470
|
+
echo "marking $GITHUB_REF_NAME latest: $latest"
|
|
471
|
+
gh release edit "$GITHUB_REF_NAME" --draft=false --latest="$latest" --repo "$GITHUB_REPOSITORY"
|
inkrs-0.2.0/.gitignore
ADDED