GitHub actions¶
a816 ships the CI steps a ROM hack repository repeats: install, decrypt
the base ROM, lint, build, seal, a pull request's artifact comment, the
nightly prerelease and the tagged release. Each one is a composite action
under manz/a816/actions/ that calls a816 ci <step>, so it runs the a816
the project locks. The actions need a816 1.1.5 or later, and pin the same
tag as the package.
| Action | Does |
|---|---|
setup |
uv sync --locked, then exports A816_BUILD_VERSION (the tag, pr-<n> or nightly-<run>) and SOURCE_DATE_EPOCH (the commit time, unless the job sets it). |
decrypt-rom |
Decrypts x.sfc.gz.gpg with passphrase; available is false, and nothing fails, without one (a fork's pull request). Optional sha1. |
lint |
a816 format --diff, then a816 check; both run. No paths: the project a816.toml describes. |
build |
a816 build to several outputs (one per line, format from the extension), with defines. Output debug-info. |
seal |
a816 bps seal for each x.bps.unsealed, a816 ips seal for each x.ips in patches, against rom: the SNES header checksum is written when a816.toml declares the cartridge (checksum: false to skip). |
pr-artifact |
Uploads files as <name>-pr-<n> and keeps one comment on the PR (marker A816-BUILD-COMMENT, or marker to adopt a repo's old one) with the files, their sizes and a link to the artifact; later builds update it, extras are deleted, and it goes when outcome is not success. |
nightly |
Replaces the one nightly prerelease and its tag with files. |
release |
Creates the tag's release with assets and an optional reproducible zip; prerelease for PEP 440 pre-release tags (v1.2rc1). A re-run re-uploads and keeps the notes. |
Steps with their own build script (build.py, make) keep it as a plain
step: the actions around it do not care how the patch was built.
A project¶
# .github/workflows/ci.yml
name: CI
on:
pull_request:
push:
branches: [master]
concurrency:
group: ci-${{ github.ref }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write # the nightly release
issues: write # the PR comment
pull-requests: write
steps:
- uses: actions/checkout@v7
- uses: manz/a816/actions/[email protected]
- id: rom
uses: manz/a816/actions/[email protected]
with:
passphrase: ${{ secrets.ROM_PASSPHRASE }}
encrypted: game.sfc.gz.gpg
to: build/game.sfc
- if: steps.rom.outputs.available == 'true'
uses: manz/a816/actions/[email protected]
with:
outputs: |
build/game.ips
build/game.bps.unsealed
- uses: manz/a816/actions/[email protected]
- if: steps.rom.outputs.available == 'true'
uses: manz/a816/actions/[email protected]
with:
rom: build/game.sfc
patches: build/game.bps.unsealed
- if: always() && github.event_name == 'pull_request' && steps.rom.outputs.available == 'true'
uses: manz/a816/actions/[email protected]
with:
name: game
files: |
build/game.ips
build/game.bps
outcome: ${{ job.status }}
- if: github.event_name == 'push'
uses: manz/a816/actions/[email protected]
with:
files: |
build/game.ips
build/game.bps
# .github/workflows/release.yml
name: Release
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: manz/a816/actions/[email protected]
- uses: manz/a816/actions/[email protected]
with:
passphrase: ${{ secrets.ROM_PASSPHRASE }}
encrypted: game.sfc.gz.gpg
to: build/game.sfc
- uses: manz/a816/actions/[email protected]
with:
outputs: |
build/game.ips
build/game.bps.unsealed
- uses: manz/a816/actions/[email protected]
with:
rom: build/game.sfc
patches: build/game.bps.unsealed
- uses: manz/a816/actions/[email protected]
with:
zip: game-${{ github.ref_name }}.zip
zip-files: |
build/game.ips
build/game.bps
README.txt
The build sees A816_BUILD_VERSION; pass it on with defines: VERSION=...
or read it in a build script. Lint after any step that generates sources
the project includes.
The base ROM is never committed in clear: commit gzip -9 < game.sfc | gpg
--symmetric > game.sfc.gz.gpg and store the passphrase as a secret.
Trying a step locally¶
Every step is a816 ci <step>, so it runs outside GitHub too: outputs
print as name=value. The publishing steps take --dry-run, which prints
the gh writes instead of running them:
Renovate¶
The actions and the a816 package move together. Extend the preset so Renovate bumps both in one pull request: