Files
libretro/.github/workflows/build.yml
T
Abdessamad Derraz 1f33148180 chore: make the release a manual dispatch
The job carried if: false, which blocks workflow_dispatch as well as
push, so there was no way to cut a release through CI at all and the
comment described a temporary state that had become permanent.

Releasing is deliberate: the push trigger is gone and the job runs when
someone dispatches it. The seven-day rate limit stays as the guard
against dispatching twice, and concurrency no longer cancels a run that
may be midway through uploading assets.
2026-08-11 05:08:40 +02:00

157 lines
5.7 KiB
YAML

name: Build & Release
# Releasing is a deliberate act, not a consequence of pushing. Cutting one is
# a manual dispatch: someone decides the collection is in a state worth
# publishing, and the rate limit below still guards against doing it twice by
# accident.
on:
workflow_dispatch:
inputs:
force_release:
description: "Force release even if rate limited"
type: boolean
default: false
permissions: {}
concurrency:
group: build
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# The release notes are built from `git log -15`, which returns a single
# commit on the default shallow clone.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.12"
- run: pip install pyyaml
- name: Run tests
run: python -m unittest discover tests
- name: Rate limit
if: github.event.inputs.force_release != 'true'
id: rate
run: |
LAST=$(gh release list --repo "${{ github.repository }}" --json createdAt -q '.[0].createdAt' 2>/dev/null || echo "")
if [ -n "$LAST" ] && [ "$LAST" != "null" ]; then
LAST_TS=$(date -d "$LAST" +%s 2>/dev/null || echo 0)
DIFF=$(( ($(date +%s) - LAST_TS) / 86400 ))
if [ "$DIFF" -lt 7 ]; then
echo "Skipping: last release ${DIFF} days ago"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Restore large files from release
if: steps.rate.outputs.skip != 'true'
run: |
mkdir -p .cache/large
gh release download large-files -D .cache/large/ 2>/dev/null || true
python scripts/restore_large_files.py
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Refresh data directories
if: steps.rate.outputs.skip != 'true'
run: python scripts/refresh_data_dirs.py
continue-on-error: true
- name: Build packs
if: steps.rate.outputs.skip != 'true'
run: python scripts/generate_pack.py --all --output-dir dist/
- name: Split oversized packs
if: steps.rate.outputs.skip != 'true'
run: |
# GitHub releases cap assets at 2 GB. Use the .zip.001 volume
# convention: 7-Zip and PeaZip open the .001 part directly,
# unlike .partNN names which they reject as corrupt archives.
for f in dist/*.zip; do
size=$(stat -c%s "$f")
if [ "$size" -gt 2000000000 ]; then
split --bytes=1900M --numeric-suffixes=1 --suffix-length=3 "$f" "$f."
rm "$f"
echo "Split $(basename "$f") into $(ls "$f".* | wc -l) parts"
fi
done
- name: Release
if: steps.rate.outputs.skip != 'true'
run: |
DATE=$(date +%Y.%m.%d)
EXISTING=$(gh release list --repo "${{ github.repository }}" \
--json tagName -q ".[].tagName" | grep -c "^v${DATE}" || true)
TAG="v${DATE}"
[ "$EXISTING" -gt 0 ] && TAG="v${DATE}.$((EXISTING+1))"
CHANGES=$(git log --oneline -15 --no-merges \
-- bios/ platforms/ emulators/ | sed 's/^/- /')
TOTAL=$(python3 -c "import json; print(json.load(open('database.json'))['total_files'])")
SIZE=$(python3 -c "import json; print(f'{json.load(open(\"database.json\"))[\"total_size\"]/1024/1024:.0f}')")
PACKS=$(ls dist/*.zip dist/*.zip.001 2>/dev/null | while read f; do echo "- **$(basename $f)** ($(du -m "$f" | cut -f1) MB)"; done)
gh release create "$TAG" dist/*.zip* \
--repo "${{ github.repository }}" \
--title "BIOS Pack $TAG" \
--notes "${TOTAL} files, ${SIZE} MB, verified checksums.
### Packs
${PACKS}
### Install
Download the pack matching your frontend, extract to the BIOS directory.
Packs over 2 GB are split into numbered volumes (.zip.001, .zip.002).
Download every part, then either open the .001 file directly with
7-Zip / PeaZip, or join the parts first:
- Linux/macOS: \`cat PackName.zip.0* > PackName.zip\`
- Windows (cmd): \`copy /b PackName.zip.001+PackName.zip.002 PackName.zip\`
| Platform | Extract to |
|----------|------------|
| RetroArch | system/ |
| Lakka | /storage/system/ |
| RetroPie | ~/RetroPie/BIOS/ |
| Batocera | /userdata/bios/ |
| Recalbox | /recalbox/share/bios/ |
| RetroBat | bios/ |
| RetroDECK | ~/retrodeck/ (the pack carries its own bios/) |
| EmuDeck | ~/Emulation/bios/ |
| ROCKNIX | /storage/roms/bios/ |
| MiSTer FPGA | /media/fat/games/ |
| BizHawk | Firmware/ |
| RomM | bios/{platform_slug}/ |
### Changes
${CHANGES}
" \
--latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Cleanup old releases
if: steps.rate.outputs.skip != 'true'
run: |
gh release list --repo "${{ github.repository }}" --json tagName,createdAt \
--jq 'sort_by(.createdAt) | reverse | .[].tagName' | \
grep -v "^large-files$" | tail -n +4 | while read tag; do
gh release delete "$tag" --repo "${{ github.repository }}" --yes --cleanup-tag
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}