Build & Release Bangen #44
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 & Release Bangen" | |
| on: | |
| workflow_dispatch: | |
| push: | |
| paths: | |
| - "pyproject.toml" | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| tag: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| outputs: | |
| version: ${{ steps.extract.outputs.version }} | |
| tag: ${{ steps.extract.outputs.tag }} | |
| exists: ${{ steps.check.outputs.exists }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - id: extract | |
| run: | | |
| VERSION=$(python3 - <<'EOF' | |
| import tomllib | |
| with open("pyproject.toml", "rb") as f: | |
| print(tomllib.load(f)["project"]["version"]) | |
| EOF | |
| ) | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "tag=$VERSION" >> $GITHUB_OUTPUT | |
| - id: check | |
| run: | | |
| if git rev-parse "refs/tags/${{ steps.extract.outputs.tag }}" >/dev/null 2>&1; then | |
| echo "exists=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "exists=false" >> $GITHUB_OUTPUT | |
| fi | |
| - if: steps.check.outputs.exists == 'false' | |
| run: | | |
| git config user.name github-actions[bot] | |
| git config user.email github-actions[bot]@users.noreply.github.com | |
| git tag "${{ steps.extract.outputs.tag }}" | |
| git push origin "${{ steps.extract.outputs.tag }}" | |
| build: | |
| needs: tag | |
| if: needs.tag.outputs.exists == 'false' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| platform: linux | |
| bin: bangen | |
| - os: windows-latest | |
| platform: windows | |
| bin: bangen.exe | |
| - os: macos-latest | |
| platform: macos | |
| bin: bangen | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Windows & macOS native Python setup (Pinned to 3.11 for parity) | |
| - if: matrix.platform != 'linux' | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| # Core Build step handling logic per platform | |
| - name: Build Executable | |
| shell: bash | |
| env: | |
| VERSION: ${{ needs.tag.outputs.version }} | |
| PLATFORM: ${{ matrix.platform }} | |
| run: | | |
| printf 'from bangen.app import main\nif __name__=="__main__": main()\n' > _entry.py | |
| # 2. Extract dependencies dynamically using standardized PEP 508 parsing | |
| GENERATE_FLAGS_SCRIPT=$(cat <<'PYTHON_SCRIPT' | |
| import tomllib | |
| from packaging.requirements import Requirement | |
| pkg_to_module = {"Pillow": "PIL"} | |
| modules = ["bangen"] | |
| with open("pyproject.toml", "rb") as f: | |
| deps = tomllib.load(f).get("project", {}).get("dependencies", []) | |
| for dep in deps: | |
| name = Requirement(dep).name | |
| modules.append(pkg_to_module.get(name, name)) | |
| print(" ".join(f"--collect-all {m}" for m in sorted(set(modules)))) | |
| PYTHON_SCRIPT | |
| ) | |
| # 3. Compile executable | |
| if [[ "$PLATFORM" == "linux" ]]; then | |
| # Run inside modern manylinux container with Python 3.11 to avoid native compilation errors. | |
| # NOTE: "py3.11" is only a shell alias defined via /root/.bashrc on this image, which is | |
| # never sourced by a non-interactive `bash -c` shell (and isn't sourced at all once HOME | |
| # is overridden to /tmp for the unprivileged -u user). Use the real interpreter path instead. | |
| docker run --rm \ | |
| -e HOME=/root \ | |
| -v "${{ github.workspace }}:/workspace" \ | |
| -w /workspace \ | |
| ghcr.io/yt-dlp/manylinux2014_x86_64-shared \ | |
| bash -c " | |
| set -euo pipefail | |
| # Pillow >= 11 only ships manylinux_2_27/2_28 wheels for cp311, which this | |
| # manylinux2014 (CentOS7/glibc2.17) image can't use, so pip falls back to | |
| # building the latest Pillow from source. That needs libjpeg's headers, | |
| # which this image doesn't ship by default. Install them via yum (root is | |
| # required for this, hence no -u/-e HOME=/tmp on this docker run). | |
| yum install -y libjpeg-turbo-devel zlib-devel >/dev/null | |
| PY=/opt/python/cp311-cp311/bin/python3.11 | |
| \$PY -m pip install -U pip wheel pyinstaller packaging --no-cache-dir | |
| \$PY -m pip install . --no-cache-dir | |
| COLLECT_FLAGS=\$(\$PY -c '$GENERATE_FLAGS_SCRIPT') | |
| \$PY -m PyInstaller --onefile --noconfirm --strip --noupx --name bangen --distpath dist --workpath build \$COLLECT_FLAGS --optimize 2 _entry.py | |
| strings dist/bangen | grep GLIBC_ | sort -V | uniq | |
| " | |
| else | |
| python -m pip install -U pip wheel pyinstaller packaging | |
| pip install . | |
| COLLECT_FLAGS=$(python -c "$GENERATE_FLAGS_SCRIPT") | |
| python -m PyInstaller --onefile --noconfirm --noupx --name bangen --distpath dist --workpath build $COLLECT_FLAGS --optimize 2 _entry.py | |
| fi | |
| # 4. Standardized artifact compression and packaging | |
| - name: Package Output | |
| shell: bash | |
| run: | | |
| mkdir -p release | |
| NAME="bangen-${{ matrix.platform }}-${{ needs.tag.outputs.version }}" | |
| if [[ "${{ matrix.platform }}" == "windows" ]]; then | |
| mv dist/${{ matrix.bin }} release/$NAME.exe | |
| powershell -Command "Compress-Archive -Path release\\$NAME.exe -DestinationPath release\\$NAME.zip" | |
| rm release/$NAME.exe | |
| else | |
| mv dist/${{ matrix.bin }} release/$NAME | |
| tar -czf "release/$NAME.tar.gz" -C release "$NAME" | |
| rm release/$NAME | |
| fi | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.platform }}-build | |
| path: release/* | |
| retention-days: 1 | |
| release: | |
| needs: [tag, build] | |
| if: needs.tag.outputs.exists == 'false' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| merge-multiple: true | |
| - name: Checksums | |
| run: | | |
| cd artifacts | |
| sha256sum * > SHA256SUMS.txt | |
| - uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.tag.outputs.tag }} | |
| name: "Bangen ${{ needs.tag.outputs.version }}" | |
| files: artifacts/* | |