name: Build & Release on: push: branches: [main] paths: ["bios/**", "platforms/**"] workflow_dispatch: inputs: force_release: description: "Force release even if rate limited" type: boolean default: false permissions: {} concurrency: group: build cancel-in-progress: true jobs: release: if: false # disabled until pack generation is validated in production runs-on: ubuntu-latest permissions: contents: write pages: write steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: "3.12" - run: pip install pyyaml - name: Run tests run: python -m unittest tests.test_e2e -v - 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 for f in .cache/large/*; do [ -f "$f" ] || continue name=$(basename "$f") target=$(grep "$name" .gitignore | head -1) if [ -n "$target" ] && [ ! -f "$target" ]; then mkdir -p "$(dirname "$target")" cp "$f" "$target" echo "Restored: $target" fi done 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: Deploy site to GitHub Pages if: steps.rate.outputs.skip != 'true' run: | pip install mkdocs-material python scripts/generate_readme.py --db database.json --platforms-dir platforms python scripts/generate_site.py mkdocs gh-deploy --force --clean - 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/ | 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 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. | Platform | Pack | Path | |----------|------|------| | RetroArch / Lakka | RetroArch_Lakka_BIOS_Pack.zip | system/ | | Batocera | Batocera_BIOS_Pack.zip | /userdata/bios/ | | Recalbox | Recalbox_BIOS_Pack.zip | /recalbox/share/bios/ | | RetroBat | RetroBat_BIOS_Pack.zip | bios/ | | RetroDECK | RetroDECK_BIOS_Pack.zip | ~/retrodeck/bios/ | | EmuDeck | EmuDeck_BIOS_Pack.zip | Emulation/bios/ | ### 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 }}