-
Notifications
You must be signed in to change notification settings - Fork 0
181 lines (165 loc) · 7.24 KB
/
Copy pathbuild.yml
File metadata and controls
181 lines (165 loc) · 7.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# Builds the standalone application - the download for people who do not
# have Python. Windows and macOS (Apple Silicon and Intel).
#
# Pushing a version tag (v*) additionally publishes a GitHub Release with
# every binary attached: git tag -a v1.1.0 -m "..." && git push origin v1.1.0
# Otherwise the results are under the run's "Artifacts" section.
#
# There is no Linux build on purpose: a PyInstaller bundle is pinned to the
# glibc of the machine that built it, so the runner's Ubuntu would decide
# which distributions it runs on - and Linux users have a working Python and
# "pip install spwb[gui]" already.
#
# Correctness is tests.yml's job, which gates every push to main. What this
# workflow has to prove is different and packaging-specific: that the built
# bundle still has its icons, its Qt platform plugin, a working h5py and a
# scipy that produces the right numbers. That is "spwb --selftest", run
# below against the executable itself rather than against the source tree.
name: build
on:
push:
tags:
- "v*"
branches: [main]
# Only when something that can change the bundle changes.
paths:
- "spwb.spec"
- "pyproject.toml"
- "tools/standalone_entry.py"
- "src/spwb/selftest.py"
- ".github/workflows/build.yml"
workflow_dispatch:
jobs:
build:
name: ${{ matrix.label }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
label: windows x64
artifact: spwb-windows-x64
asset: SPWB-windows-x64.zip
- os: macos-latest
label: macOS Apple Silicon
artifact: spwb-macos-apple-silicon
asset: SPWB-macos-apple-silicon.zip
# macos-latest is Apple Silicon only, and a bundle built there
# will not run on an Intel Mac. This is the second half of "any
# Mac", and it is the half on borrowed time: GitHub retired the
# macos-13 image in December 2025 (a job asking for it queues
# forever rather than failing, which is how this was found), and
# drops x86_64 macOS altogether when macos-15 retires in autumn
# 2027. When that happens, delete this entry - Intel Macs will
# then need "pip install spwb[gui]" like Linux does.
- os: macos-15-intel
label: macOS Intel
artifact: spwb-macos-intel
asset: SPWB-macos-intel.zip
steps:
- name: Check out repository
uses: actions/checkout@v7
# Not the newest Python available: the interpreter is bundled, so the
# user never sees it, and a version every wheel we depend on already
# ships binaries for is worth more here than being current.
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.13"
- name: Install the application and the build tools
run: |
python -m pip install --upgrade pip
python -m pip install ".[build]"
- name: Build
run: python -m PyInstaller spwb.spec --noconfirm
# A windowed executable detaches on Windows, so PowerShell has to be
# told to wait for it - otherwise the report is read before it exists
# and the step passes without having checked anything.
- name: Verify the build is complete
if: runner.os == 'Windows'
run: |
$proc = Start-Process -FilePath ".\dist\SPWB\SPWB.exe" `
-ArgumentList "--selftest","selftest.txt" -Wait -PassThru
Get-Content selftest.txt
if ($proc.ExitCode -ne 0) { exit $proc.ExitCode }
- name: Verify the build is complete
if: runner.os == 'macOS'
run: |
./dist/SPWB.app/Contents/MacOS/SPWB --selftest selftest.txt
cat selftest.txt
# One file to download, and - on macOS - the only way to keep the
# bundle intact: upload-artifact does not preserve the .app's symlinks
# or executable bits, while ditto does.
- name: Package (Windows)
if: runner.os == 'Windows'
run: Compress-Archive -Path dist\SPWB -DestinationPath dist\${{ matrix.asset }}
- name: Package (macOS)
if: runner.os == 'macOS'
run: ditto -c -k --keepParent "dist/SPWB.app" "dist/${{ matrix.asset }}"
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.artifact }}
path: dist/${{ matrix.asset }}
if-no-files-found: error
# Kept whether the run passed or failed: when the self-test fails,
# this file is the only account of what the bundle was missing.
- name: Upload the self-test report
if: always()
uses: actions/upload-artifact@v7
with:
name: selftest-${{ matrix.artifact }}
path: selftest.txt
if-no-files-found: warn
# Release assets never expire; workflow artifacts are deleted after 90
# days, so a tagged build has to be copied somewhere permanent.
#
# The release is created as a DRAFT, and that is load-bearing.
# publish.yml uploads to PyPI when a release is *published*, but GitHub
# does not start a workflow from an event raised by the default
# GITHUB_TOKEN - so a release published from here would attach the
# binaries and silently never reach PyPI. Leaving it a draft puts the
# last step back in a person's hands: reviewing the assets and pressing
# Publish is a real user action, so it does trigger publish.yml, and it
# is a human gate on an upload that can never be taken back.
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# v7, not the current v8, deliberately. v8 stopped unzipping
# unconditionally - it reads Content-Type and leaves non-zip
# files alone - and every artifact here IS a single .zip, so
# the risk is being handed an unpacked SPWB folder instead of
# SPWB-windows-x64.zip. This job only runs on a tag, so that
# would surface during a release and nowhere else. v7 clears
# the Node 20 deprecation, which was the whole point.
- name: Download the built applications
uses: actions/download-artifact@v7
with:
path: artifacts
pattern: spwb-*
- name: Attach them to the release
env:
GH_TOKEN: ${{ github.token }}
run: |
tag="${{ github.ref_name }}"
find artifacts -name '*.zip' -exec mv {} . \;
# Unsigned downloads: a checksum is the only way for someone to
# tell a real one from a tampered copy.
sha256sum SPWB-*.zip > SPWB-checksums.txt
cat SPWB-checksums.txt
files=(SPWB-*.zip SPWB-checksums.txt)
if gh release view "$tag" --repo "${{ github.repository }}" >/dev/null 2>&1; then
gh release upload "$tag" "${files[@]}" --repo "${{ github.repository }}" --clobber
else
gh release create "$tag" "${files[@]}" \
--repo "${{ github.repository }}" \
--title "SPWB ${tag#v}" \
--generate-notes \
--draft
echo "::notice::Draft release $tag created with the binaries attached. Review it, then press Publish - that is what uploads to PyPI."
fi