Skip to content

Build DSi Wi-Fi Manager release #9

Build DSi Wi-Fi Manager release

Build DSi Wi-Fi Manager release #9

Workflow file for this run

name: Build DSi Wi-Fi Manager release
on:
release:
types: [published]
workflow_dispatch:
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
container: skylyrac/blocksds:slim-latest
env:
BLOCKSDS: /opt/wonderful/thirdparty/blocksds/core
name: Build with BlocksDS toolchain using official Docker image
steps:
- name: Checkout repo
uses: actions/checkout@v7.0.1
- name: Build app
id: build
# ROM is overridden to the bare name so every release carries an identically-named
# asset, keeping the /releases/latest/download/dsi_wifi_manager.dsi URL permanent.
#
# A release shows its tag on the banner rather than a commit hash: the tag is what a user
# can read back to you, and the release itself already pins the exact commit. Nightlies
# have no tag and keep the hash. Fed through DSIWIFI_COMMIT because that is the field the
# banner prints; a long tag is cut with a '~' rather than overflowing the row.
#
# DSIWIFI_BUILD_KIND=Release also turns DSIWIFI_DEBUG off, which removes the
# flash-layout screen and the ability to force a write that programs nothing.
run: |
# No pipefail: the container's shell is dash, which does not have it, and `make | tee`
# would then report tee's status and a broken build would look clean. Redirecting and
# replaying the log keeps make's own exit status without needing bash.
release_ref="${{ github.event.release.tag_name }}"
if [ -z "$release_ref" ]; then
release_ref="$(git rev-parse --short=7 HEAD)"
fi
make ROM=dsi_wifi_manager.dsi DSIWIFI_BUILD_KIND=Release DSIWIFI_COMMIT="$release_ref" > build.log 2>&1 || { cat build.log; echo "::error::build failed"; exit 1; }
cat build.log
- name: Check the debug affordances are off
# A Release that still offered the forced write would put a developer affordance in front
# of somebody restoring their home network. Checked here rather than by looking for debug
# strings in the ROM: those are compiled into every build, because the view layer takes a
# runtime flag so the offline harness can draw both variants. Only the flag differs.
run: |
grep -q "kind=Release debug=0" build.log || {
echo "::error::release build has debug affordances on"; grep "BUILD kind=" build.log; exit 1; }
echo "debug affordances off"
- name: Fail on any compiler warning
run: |
if grep -qi "warning" build.log; then
echo "::error::compiler warnings in build.log"
grep -i warning build.log
exit 1
fi
echo "no warnings"
- name: Keep the ROM for the verify job
uses: actions/upload-artifact@v7.0.1
with:
name: release-rom
path: dsi_wifi_manager.dsi
retention-days: 1
verify:
# A separate job on the runner, not in the build container: the BlocksDS image has no
# python3, and this check has to decode a little-endian offset and a UTF-16 banner. Doing
# that in the container's dash with od and awk would be fragile in a step whose whole job
# is to be trustworthy.
#
# The asset is only attached once this passes, so a ROM that fails verification is never
# published.
needs: build
runs-on: ubuntu-latest
name: Verify the ROM and attach it to the release
steps:
- name: Fetch the ROM
uses: actions/download-artifact@v8.0.1
with:
name: release-rom
- name: Check the ROM really is a Release DSi build
# Reads the banner and the unitcode back out of the ROM, which is the only way to know
# DSIWIFI_BUILD_KIND reached the binary -- and DEBUG follows from the kind.
#
# Not checked by looking for debug strings: they are present in every build. The view
# layer takes a runtime flag so the offline harness can draw both variants, so the
# text compiles in either way and only its reachability changes.
env:
TAG: ${{ github.event.release.tag_name }}
COMMIT: ${{ github.sha }}
run: |
python3 - <<'EOF'
import os, struct, sys
d = open("dsi_wifi_manager.dsi", "rb").read()
off = struct.unpack("<I", d[0x68:0x6C])[0]
banner = d[off+0x240:off+0x240+256].decode("utf-16-le", "replace").split("\x00")[0]
lines = banner.split("\n")
tag = os.environ["TAG"] or os.environ["COMMIT"][:7]
print("banner:", lines)
print("unitcode: 0x%02X" % d[0x12])
bad = []
# Match top_banner(): it keeps a version that fits beside the app name and marks an
# overlong tag with '~'. This still rejects a leaked build kind while allowing the
# deliberate, visible truncation required by the 51-column screen.
room = 51 - len("DSi Wi-Fi Manager") - 1
expected = tag if len(tag) <= room else tag[:room - 1] + "~"
if lines[-1] != expected:
bad.append(f"version line is {lines[-1]!r}, expected {expected!r}")
if d[0x12] != 0x02:
bad.append("unitcode is not 0x02, DSi-only")
if bad:
print("::error::" + "; ".join(bad)); sys.exit(1)
EOF
- name: Verify GitHub CLI authentication
if: github.event_name == 'workflow_dispatch'
env:
GH_TOKEN: ${{ github.token }}
run: gh api "repos/${{ github.repository }}" --jq .full_name
- name: Upload dsi_wifi_manager.dsi
if: github.event_name == 'release'
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ github.event.release.tag_name }}" dsi_wifi_manager.dsi --clobber