Build DSi Wi-Fi Manager release #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |