Skip to content

Build & Release Bangen #44

Build & Release Bangen

Build & Release Bangen #44

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/*