1- name : Release Binaries
1+ name : Build Bangen (Nuitka)
22
33on :
44 push :
55 tags :
66 - " v*"
77 workflow_dispatch :
88
9- permissions :
10- contents : write
11-
129jobs :
13- metadata :
14- runs-on : ubuntu-latest
15- outputs :
16- version : ${{ steps.meta.outputs.version }}
17- tag : ${{ steps.meta.outputs.tag }}
18- steps :
19- - uses : actions/checkout@v4
20-
21- - name : Read version
22- id : meta
23- run : |
24- python - <<'PY' >> "$GITHUB_OUTPUT"
25- import tomllib, pathlib
26- v = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]
27- print(f"version={v}")
28- print(f"tag=v{v}")
29- PY
30-
31- - name : Create GitHub release
32- uses : softprops/action-gh-release@v2
33- with :
34- tag_name : ${{ steps.meta.outputs.tag }}
35- name : ${{ steps.meta.outputs.tag }}
36- draft : false
37- prerelease : false
38-
3910 build :
40- needs : metadata
41- runs-on : ${{ matrix.os }}
4211 strategy :
43- fail-fast : false
4412 matrix :
45- include :
46- - os : ubuntu-latest
47- platform : linux-x86_64
48-
49- - os : macos-latest
50- platform : macos-universal2
13+ os : [ubuntu-latest, macos-latest]
5114
52- - os : windows-latest
53- platform : windows-x86_64
15+ runs-on : ${{ matrix.os }}
5416
5517 steps :
56- - uses : actions/checkout@v4
18+ - name : Checkout
19+ uses : actions/checkout@v4
5720
58- - uses : actions/setup-python@v5
21+ - name : Setup Python
22+ uses : actions/setup-python@v5
5923 with :
6024 python-version : " 3.11"
6125
62- # ── Layer 1: installed Python environment ────────────────────────────
63- - name : Cache pip environment
64- id : pip-cache
65- uses : actions/cache@v4
66- with :
67- path : ${{ env.pythonLocation }}
68- key : pip-env-${{ runner.os }}-py3.11-${{ hashFiles('pyproject.toml') }}
69-
70- - name : Install dependencies
71- if : steps.pip-cache.outputs.cache-hit != 'true'
72- run : |
73- python -m pip install --upgrade pip
74- pip install -e . "nuitka[onefile]"
75-
76- # ── Layer 2: sccache (C compiler object file cache) ──────────────────
77- # Intercepts clang calls on Linux/macOS via a shim in /usr/local/bin.
78- # Windows MSVC (cl.exe) is called through Nuitka's Scons backend in a
79- # way that cannot be shimmed — Layer 3 handles Windows compilation cost.
80- - uses : mozilla-actions/sccache-action@v0.0.5
81-
82- - name : Install clang (Linux)
26+ - name : Install system dependencies (Linux)
8327 if : runner.os == 'Linux'
84- run : sudo apt-get install -y clang
85-
86- - name : Configure sccache + compiler shim
87- shell : bash
8828 run : |
89- echo "SCCACHE_GHA_ENABLED=true" >> "$GITHUB_ENV"
90- echo "SCCACHE_CACHE_SIZE=2G" >> "$GITHUB_ENV"
91-
92- if [ "${{ runner.os }}" = "Linux" ]; then
93- REAL_CLANG="$(which clang)"
94- REAL_CLANGXX="$(which clang++)"
95- printf '#!/bin/sh\nexec sccache %s "$@"\n' "$REAL_CLANG" | sudo tee /usr/local/bin/clang > /dev/null
96- printf '#!/bin/sh\nexec sccache %s "$@"\n' "$REAL_CLANGXX" | sudo tee /usr/local/bin/clang++ > /dev/null
97- sudo chmod +x /usr/local/bin/clang /usr/local/bin/clang++
98-
99- elif [ "${{ runner.os }}" = "macOS" ]; then
100- REAL_CLANG="$(xcrun -f clang)"
101- REAL_CLANGXX="$(xcrun -f clang++)"
102- printf '#!/bin/sh\nexec sccache %s "$@"\n' "$REAL_CLANG" | sudo tee /usr/local/bin/clang > /dev/null
103- printf '#!/bin/sh\nexec sccache %s "$@"\n' "$REAL_CLANGXX" | sudo tee /usr/local/bin/clang++ > /dev/null
104- sudo chmod +x /usr/local/bin/clang /usr/local/bin/clang++
105- fi
29+ sudo apt-get update
30+ sudo apt-get install -y build-essential
10631
107- # ── Layer 3: Nuitka bytecode + download cache ─────────────────────────
108- - name : Set Nuitka cache dir
109- shell : bash
32+ - name : Ensure Xcode CLI tools (macOS)
33+ if : runner.os == 'macOS'
11034 run : |
111- if [ "${{ runner.os }}" = "Windows" ]; then
112- echo "NUITKA_CACHE_DIR=${LOCALAPPDATA}/Nuitka" >> "$GITHUB_ENV"
113- else
114- echo "NUITKA_CACHE_DIR=${HOME}/.cache/Nuitka" >> "$GITHUB_ENV"
115- fi
35+ xcode-select -p || xcode-select --install || true
11636
117- - name : Cache Nuitka artifacts
37+ - name : Cache Nuitka
11838 uses : actions/cache@v4
11939 with :
120- path : |
121- ~/.cache/Nuitka
122- ~/AppData/Local/Nuitka
123- key : >-
124- nuitka-${{ runner.os }}-py3.11-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('bangen/**/*.py') }}
125- restore-keys : |
126- nuitka-${{ runner.os }}-py3.11-${{ hashFiles('pyproject.toml') }}-
127- nuitka-${{ runner.os }}-py3.11-
128-
129- # ─────────────────────────────────────────────────────────────────────
130-
131- - name : Generate entry script
132- shell : bash
133- run : |
134- cat > _entry.py << 'EOF'
135- from bangen.app import main
136- if __name__ == "__main__":
137- main()
138- EOF
139-
140- # Shared Nuitka flags, extracted to an env var to avoid duplication
141- # across the two macOS arch passes and the single Linux/Windows pass.
142- - name : Set common Nuitka flags
143- shell : bash
144- run : |
145- cat >> "$GITHUB_ENV" << 'EOF'
146- NUITKA_COMMON_FLAGS=--mode=onefile --assume-yes-for-downloads --output-dir=build --follow-imports --include-package=bangen --include-package=rich --include-package-data=rich --include-package=pyfiglet --include-package-data=pyfiglet --include-package=PIL --include-package=typer --include-package=click --nofollow-import-to=tkinter --nofollow-import-to=unittest --nofollow-import-to=test --nofollow-import-to=distutils --nofollow-import-to=setuptools --nofollow-import-to=pkg_resources --lto=yes --python-flag=no_asserts --python-flag=no_docstrings --python-flag=isolated --onefile-tempdir-spec={CACHE_DIR}/bangen/${{ needs.metadata.outputs.version }}
147- EOF
148-
149- # ── macOS: two separate arch passes + lipo fat binary ─────────────────
150- # Nuitka onefile cannot produce a universal2 binary in a single pass
151- # (fatal error). Build arm64 and x86_64 separately, then lipo-merge.
152- - name : Build arm64 (macOS)
153- if : runner.os == 'macOS'
154- shell : bash
155- run : |
156- python -m nuitka \
157- $NUITKA_COMMON_FLAGS \
158- --output-filename=bangen-arm64 \
159- --clang \
160- --macos-target-arch=arm64 \
161- _entry.py
40+ path : ~/.cache/Nuitka
41+ key : nuitka-${{ runner.os }}-${{ hashFiles('**/*.py') }}
16242
163- - name : Build x86_64 (macOS)
164- if : runner.os == 'macOS'
165- shell : bash
166- run : |
167- python -m nuitka \
168- $NUITKA_COMMON_FLAGS \
169- --output-filename=bangen-x86_64 \
170- --clang \
171- --macos-target-arch=x86_64 \
172- _entry.py
173-
174- - name : Lipo merge → universal2 (macOS)
175- if : runner.os == 'macOS'
176- shell : bash
177- run : |
178- lipo -create \
179- -output build/bangen \
180- build/bangen-arm64 \
181- build/bangen-x86_64
182-
183- # ── Linux / Windows: single pass ──────────────────────────────────────
184- - name : Build (Linux)
185- if : runner.os == 'Linux'
186- shell : bash
43+ - name : Install Python dependencies
18744 run : |
188- python -m nuitka \
189- $NUITKA_COMMON_FLAGS \
190- --output-filename=bangen \
191- --clang \
192- _entry.py
45+ python -m pip install --upgrade pip
46+ pip install nuitka
19347
194- - name : Build (Windows)
195- if : runner.os == 'Windows'
196- shell : bash
48+ - name : Build with Nuitka
19749 run : |
19850 python -m nuitka \
199- $NUITKA_COMMON_FLAGS \
51+ --mode=onefile \
52+ --assume-yes-for-downloads \
53+ --output-dir=build \
20054 --output-filename=bangen \
55+ --include-package=bangen \
56+ --include-package=rich \
57+ --include-package-data=rich \
58+ --include-package=pyfiglet \
59+ --include-package-data=pyfiglet \
60+ --include-package=PIL \
61+ --include-package=typer \
62+ --include-package=click \
63+ --nofollow-import-to=tkinter \
64+ --nofollow-import-to=unittest \
65+ --nofollow-import-to=test \
66+ --nofollow-import-to=distutils \
67+ --nofollow-import-to=setuptools \
68+ --nofollow-import-to=pkg_resources \
69+ --python-flag=no_asserts \
70+ --python-flag=no_docstrings \
71+ --python-flag=isolated \
72+ --onefile-tempdir-spec={CACHE_DIR}/bangen/2.2.1 \
73+ --static-libpython=yes \
20174 _entry.py
20275
203- # ─────────────────────────────────────────────────────────────────────
204-
205- - name : Prepare release asset
206- shell : bash
76+ - name : Prepare artifacts
20777 run : |
208- mkdir -p release-assets
209-
210- if [ "${{ runner.os }}" = "Windows" ]; then
211- BIN="build/bangen.exe"
212- EXT=".exe"
78+ mkdir -p dist
79+ if [[ "$RUNNER_OS" == "Linux" ]]; then
80+ mv build/bangen dist/bangen-linux
21381 else
214- BIN="build/bangen"
215- EXT=""
216- chmod +x "$BIN"
82+ mv build/bangen dist/bangen-macos
21783 fi
21884
219- cp "$BIN" \
220- "release-assets/bangen-${{ needs.metadata.outputs.tag }}-${{ matrix.platform }}${EXT}"
85+ - name : Strip binary (Linux only)
86+ if : runner.os == 'Linux'
87+ run : strip dist/bangen-linux
22188
222- - name : Upload artifact
89+ - name : Upload artifacts
22390 uses : actions/upload-artifact@v4
22491 with :
225- name : bangen-${{ needs.metadata.outputs.tag }}-${{ matrix.platform }}
226- path : release-assets/*
92+ name : bangen-${{ runner.os }}
93+ path : dist/*
94+
95+ release :
96+ needs : build
97+ runs-on : ubuntu-latest
98+ if : startsWith(github.ref, 'refs/tags/')
99+
100+ steps :
101+ - name : Download artifacts
102+ uses : actions/download-artifact@v4
103+ with :
104+ path : artifacts
227105
228- - name : Upload to GitHub release
106+ - name : Create GitHub Release
229107 uses : softprops/action-gh-release@v2
230108 with :
231- tag_name : ${{ needs.metadata.outputs.tag }}
232- files : release-assets/*
109+ files : artifacts/**/*
110+ env :
111+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
0 commit comments