Build & Release Bangen #14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build Bangen (Nuitka) | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| name: linux | |
| - os: macos-latest | |
| name: macos | |
| - os: windows-latest | |
| name: windows | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build fingerprint + cache key | |
| id: meta | |
| shell: bash | |
| run: | | |
| VERSION=${GITHUB_REF_NAME#v} | |
| SHA=$(git rev-parse --short HEAD) | |
| # sha256sum on Linux, shasum on macOS — both unavailable on Windows | |
| # so we use Python for a cross-platform hash | |
| DEPS_HASH=$(python -c " | |
| import hashlib, pathlib | |
| p = pathlib.Path('pyproject.toml') | |
| print(hashlib.sha256(p.read_bytes()).hexdigest()[:16] if p.exists() else 'nodeps') | |
| ") | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| echo "SHA=$SHA" >> $GITHUB_OUTPUT | |
| echo "DEPS=$DEPS_HASH" >> $GITHUB_OUTPUT | |
| echo "CACHE_KEY=${{ runner.os }}-$VERSION-$DEPS_HASH" >> $GITHUB_OUTPUT | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: "pip" | |
| - name: Cross-workflow cache (Nuitka + deps) | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cache/pip | |
| ~/.cache/Nuitka | |
| ~/Library/Caches/Nuitka | |
| ~\AppData\Local\Nuitka\Nuitka\Cache | |
| key: nuitka-${{ steps.meta.outputs.CACHE_KEY }} | |
| restore-keys: | | |
| nuitka-${{ runner.os }}-${{ steps.meta.outputs.VERSION }}- | |
| nuitka-${{ runner.os }}- | |
| - name: Install system deps (Linux) | |
| if: runner.os == 'Linux' | |
| # build-essential already pulls in binutils (which provides strip). | |
| # Do NOT list "strip" — it is not a package name and will abort apt. | |
| run: sudo apt-get update && sudo apt-get install -y build-essential patchelf | |
| - name: Install system deps (macOS) | |
| if: runner.os == 'macOS' | |
| run: xcode-select -p || xcode-select --install || true | |
| - name: MSVC setup (Windows) | |
| if: runner.os == 'Windows' | |
| uses: ilammy/msvc-dev-cmd@v1 | |
| - name: Install Python deps | |
| run: | | |
| python -m pip install -U pip wheel | |
| # setuptools>=72 removed pkg_resources.DefaultProvider, which | |
| # Nuitka 4.x's bundled Jinja2 copy requires during C code generation. | |
| # Pin below 72 until Nuitka ships its own Jinja2 fix. | |
| pip install "setuptools<72" | |
| pip install nuitka[onefile] | |
| pip install . | |
| - name: Create entrypoint | |
| shell: bash | |
| run: | | |
| cat > _entry.py << 'EOF' | |
| from bangen.app import main | |
| if __name__ == "__main__": | |
| main() | |
| EOF | |
| - name: Build with Nuitka | |
| shell: bash | |
| run: | | |
| python -m nuitka \ | |
| --onefile \ | |
| --lto=yes \ | |
| --jobs=2 \ | |
| --assume-yes-for-downloads \ | |
| --output-dir=build \ | |
| --output-filename=bangen \ | |
| --include-package=bangen \ | |
| --include-package=rich \ | |
| --include-package=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 \ | |
| --python-flag=no_asserts \ | |
| --python-flag=no_docstrings \ | |
| --python-flag=isolated \ | |
| "--onefile-tempdir-spec={CACHE_DIR}/bangen/${{ steps.meta.outputs.VERSION }}" \ | |
| _entry.py | |
| - name: Stage fingerprinted artifact | |
| shell: bash | |
| run: | | |
| mkdir -p dist | |
| NAME="bangen-${{ matrix.name }}-${{ steps.meta.outputs.VERSION }}-${{ steps.meta.outputs.SHA }}" | |
| if [[ "$RUNNER_OS" == "Linux" ]]; then | |
| mv build/bangen dist/$NAME | |
| # strip is provided by binutils (part of build-essential) | |
| strip dist/$NAME | |
| elif [[ "$RUNNER_OS" == "macOS" ]]; then | |
| mv build/bangen dist/$NAME | |
| # strip ships with Xcode Command Line Tools | |
| strip dist/$NAME | |
| else | |
| # Nuitka always appends .exe on Windows regardless of --output-filename | |
| mv build/bangen.exe dist/$NAME.exe | |
| fi | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.name }}-build | |
| path: dist/* | |
| release: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Checkout is required so git log/describe have history to work with | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # full history so git describe can find previous tags | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| merge-multiple: true # flatten into artifacts/ instead of artifacts/<name>/ | |
| - name: Generate changelog | |
| id: changelog | |
| shell: bash | |
| run: | | |
| PREV=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") | |
| { | |
| echo "CHANGELOG<<EOF" | |
| if [[ -n "$PREV" ]]; then | |
| git log "$PREV"..HEAD --pretty=format:"- %s" | |
| else | |
| git log --pretty=format:"- %s" | |
| fi | |
| echo "" | |
| echo "EOF" | |
| } >> $GITHUB_OUTPUT | |
| - name: Create DRAFT release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: Bangen ${{ github.ref_name }} | |
| draft: true | |
| body: | | |
| ## 🚀 Changes | |
| ${{ steps.changelog.outputs.CHANGELOG }} | |
| ## 🧬 Build Info | |
| - Commit: `${{ github.sha }}` | |
| - Matrix build: Linux · macOS · Windows | |
| - Nuitka LTO + onefile | |
| files: artifacts/* |