From 15fc97a9963f7a9c15000ab1c6ca58b8730345b4 Mon Sep 17 00:00:00 2001 From: Andrew Quenehen Date: Thu, 6 Aug 2026 08:28:31 -0300 Subject: [PATCH] Unify --fused with OTA launcher and require DEVKITPRO preflight for release builds. --- docs/switch-build.md | 78 ++++++++------ docs/switch-install.md | 6 +- native/switch-ota-launcher/README.md | 10 +- scripts/build_switch.sh | 66 ++++++------ scripts/switch/build_ota_launcher.sh | 27 +++-- scripts/switch/common.sh | 127 ++++++++++++++++++++++- scripts/switch/install_devkitpro_deps.sh | 20 +++- scripts/switch/selftest_build_switch.sh | 52 ++++++++-- test/switch-nro-ota.spec.test.js | 14 ++- 9 files changed, 289 insertions(+), 111 deletions(-) diff --git a/docs/switch-build.md b/docs/switch-build.md index 2cc3d59a..0f1cd3be 100644 --- a/docs/switch-build.md +++ b/docs/switch-build.md @@ -22,19 +22,25 @@ WSL — not cmd.exe or PowerShell (AD-008). ### macOS / Linux 1. Install [devkitPro pacman](https://devkitpro.org/wiki/devkitPro_pacman). -2. Install Switch tools: +2. Install Switch tools (**required for `--fused`**): ```sh sudo dkp-pacman -S switch-dev ``` -3. Ensure `nacptool` and `elf2nro` are on `PATH` (or under - `$DEVKITPRO/tools/bin` — the fused script prepends that when set). +3. OTA launcher toolchain — **native or Docker** (either is fine): -**Optional:** Install [Docker](https://docs.docker.com/get-docker/) so fused -builds can fall back to the pinned image when native tools are missing. + ```sh + bash scripts/switch/install_devkitpro_deps.sh # native + # or install Docker (same pin as fused builds) + ``` -### Native OTA launcher (optional but required for in-console updates) +4. Ensure `DEVKITPRO` is exported (typical macOS: `/opt/devkitpro`) and + `nacptool` / `elf2nro` are on `PATH` (or under `$DEVKITPRO/tools/bin`). + +Fused game builds can also use Docker when native `nacptool`/`elf2nro` are absent. + +### Native OTA launcher (included in `--fused`) In-console OTA uses a **separate DEVKITPRO NRO** (not LÖVE). Source: `native/switch-ota-launcher/`. Host protocol tests (no toolchain): @@ -45,26 +51,14 @@ make -C native/switch-ota-launcher host-test scripts/switch/build_ota_launcher.sh # host-test first; NRO needs DEVKITPRO/Docker ``` -Extra packages beyond `switch-dev`: +`--fused` always builds the fused game, native OTA launcher, and dual-NRO SD +zip. The same `*-switch.zip` is the OTA download asset. **DEVKITPRO is +required.** OTA launcher: native packages **or** Docker — both are supported. + +Release-like build from repo root: ```sh -sudo dkp-pacman -S --noconfirm --needed switch-curl switch-mbedtls switch-zlib switch-zziplib -# or: bash scripts/switch/install_devkitpro_deps.sh -``` - -Packaging dual-NRO SD zip (launcher entry + fused game): - -```sh -scripts/switch/pack_sd_zip.sh dist/switch/game.nro VERSION out.zip dist/switch/gen1recomp-launcher.nro -``` - -One-shot OTA release build (fused game + launcher + dual-NRO SD zip; -the same `*-switch.zip` is the OTA download asset): - -```sh -# once: bash scripts/switch/install_devkitpro_deps.sh -export DEVKITPRO=/opt/devkitpro -scripts/build_switch.sh --fetch --ota --version X.Y.Z +scripts/build_switch.sh --fetch --fused --version X.Y.Z ``` See `native/switch-ota-launcher/README.md` and @@ -86,8 +80,7 @@ See `native/switch-ota-launcher/README.md` and | You install | Script does **not** install | | ----------- | --------------------------- | | bash, git, zip tooling the repo already expects | — | -| `dkp-pacman` + `switch-dev` (native fused) | `dkp-pacman -S …` | -| Docker (optional fused fallback) | Docker Engine | +| `dkp-pacman` + `switch-dev` + OTA packages **or** Docker | `dkp-pacman -S …` | | A legal `.gb` ROM (to play) | Any ROM or game data | --- @@ -100,7 +93,7 @@ See `native/switch-ota-launcher/README.md` and | ---- | ------------ | | `--fetch` | Downloads pinned **love.nro** + **love.elf** into `.bazinga/love-nx/11.5-nx1/` and verifies SHA-256 against `scripts/switch/love-nx-11.5-nx1.sha256`. | | `--loose` | Packs `game.love`, copies pinned `love.nro` → `dist/switch/loose/` as `gen1recomp.nro` + `game.love` side by side. Needs the pin. | -| `--fused` | Builds `dist/switch/gen1recomp--switch.nro` (game in romfs) via `nacptool` + `elf2nro`, then packs `dist/switch/gen1recomp--switch.zip` (SD-ready tree). Needs the pin + toolchain (native or Docker). GitHub Releases publish the **zip only**. | +| `--fused` | Builds fused game NRO, OTA launcher NRO, and dual-NRO SD zip. **Requires DEVKITPRO** + `switch-dev`. OTA launcher: native packages or Docker. GitHub Releases publish the **zip only**. | Rules: @@ -145,13 +138,15 @@ scripts/build_switch.sh --fetch # Loose pair for iteration (fetch + assemble) scripts/build_switch.sh --fetch --loose -# Single fused NRO + SD-ready zip for a release-like artifact +# Fused game + OTA launcher + dual-NRO SD zip for a release-like artifact scripts/build_switch.sh --fetch --fused --version 0.2.0 ``` Outputs land under `dist/switch/` (and `dist/switch/loose/` for loose mode). -The fused path also writes `gen1recomp--switch.nro.sha256` and -`gen1recomp--switch.zip` (+ `.sha256` sidecar for the zip). +The fused path also writes `gen1recomp--switch.nro` (game), +`gen1recomp--launcher.nro`, `gen1recomp--game.nro`, +`gen1recomp--switch.nro.sha256`, and `gen1recomp--switch.zip` +(+ `.sha256` sidecar for the zip). Offline packaging smoke (no network, no nacptool required): @@ -217,9 +212,26 @@ A Switch packaging failure fails the entire release job. The release asset is ### Runner provisioning -The self-hosted Mac runner must have **native switch-tools** (`nacptool` / -`elf2nro`) **and/or Docker** available. CI and release do not silently run -`dkp-pacman -S`; keep the runner image/host provisioned per this guide. +The self-hosted Mac runner **must** have **DEVKITPRO** installed and exported. +`--fused` preflight fails early with setup steps if it is missing. + +**One-time setup on the runner** (if not already present): + +```sh +# devkitPro pacman installer from https://devkitpro.org/wiki/devkitPro_pacman +sudo dkp-pacman -S switch-dev +export DEVKITPRO=/opt/devkitpro +export PATH="$DEVKITPRO/tools/bin:$PATH" + +# OTA launcher — pick one: +bash scripts/switch/install_devkitpro_deps.sh # native +# or ensure Docker is installed (same pin as fused builds) +``` + +CI and release still run `scripts/build_switch.sh --fetch --fused`. Preflight +requires DEVKITPRO and either native OTA packages or Docker. Without all of +that, the job fails with the setup steps above. Scripts never auto-run +`dkp-pacman -S` during CI. --- diff --git a/docs/switch-install.md b/docs/switch-install.md index 48d4cea0..00b03cae 100644 --- a/docs/switch-install.md +++ b/docs/switch-install.md @@ -30,15 +30,15 @@ Extract the zip at the **root** of the microSD so you get: ```text sdmc:/switch/gen1recomp/gen1recomp.nro # native OTA launcher (hbmenu entry) -sdmc:/switch/gen1recomp/gen1recomp-game.nro # fused LÖVE game (when OTA layout ships) +sdmc:/switch/gen1recomp/gen1recomp-game.nro # fused LÖVE game sdmc:/switch/gen1recomp/version.txt sdmc:/switch/gen1recomp/pokemon-love2d/imports/ sdmc:/switch/gen1recomp/pokemon-love2d/imports/mods/ sdmc:/switch/gen1recomp/pokemon-love2d/imports/saves/... ``` -Older single-NRO zips only had `gen1recomp.nro` (the fused game). New OTA-ready -zips use the dual-NRO layout above — open `gen1recomp` in hbmenu (the launcher). +Older single-NRO zips only had `gen1recomp.nro` (the fused game). Current +releases use the dual-NRO layout above — open `gen1recomp` in hbmenu (the launcher). Merge folders if your OS asks. Any method works: **MTP** (DBI → Run MTP responder + a client), **direct SD** (Hekate UMS or a card reader), or **FTP**. diff --git a/native/switch-ota-launcher/README.md b/native/switch-ota-launcher/README.md index f664131f..6d777508 100644 --- a/native/switch-ota-launcher/README.md +++ b/native/switch-ota-launcher/README.md @@ -45,7 +45,13 @@ make # → gen1recomp.nro ``` -Or from repo root: +Or from repo root (as part of `--fused`): + +```bash +scripts/build_switch.sh --fetch --fused --version X.Y.Z +``` + +Standalone launcher build: ```bash scripts/switch/build_ota_launcher.sh @@ -64,4 +70,4 @@ Manifest: `scripts/switch/ota_launcher.manifest`. - Zip extraction uses `switch-zziplib` (`ota_unzip.c`) on device. - OTA replaces launcher + game from the unified zip (NACP versions stay aligned). - Sphaira HOME forwarders cache metadata until reinstalled (see docs/switch-install.md). -- Install toolchain deps once: `bash scripts/switch/install_devkitpro_deps.sh` +- Release runner: `switch-dev` + (`install_devkitpro_deps.sh` **or** Docker) diff --git a/scripts/build_switch.sh b/scripts/build_switch.sh index 8fbc86b6..f9c62e05 100755 --- a/scripts/build_switch.sh +++ b/scripts/build_switch.sh @@ -5,10 +5,8 @@ # scripts/build_switch.sh --fetch # scripts/build_switch.sh --loose [path/to/game.love] # scripts/build_switch.sh --fused [--version X.Y.Z] -# scripts/build_switch.sh --ota [--version X.Y.Z] # scripts/build_switch.sh --fetch --loose # scripts/build_switch.sh --fetch --fused [--version X.Y.Z] -# scripts/build_switch.sh --fetch --ota [--version X.Y.Z] # # Modes: # --fetch Download pinned love.nro + love.elf into @@ -21,22 +19,19 @@ # (gen1recomp.nro + game.love side by side). Requires the pin # (run --fetch first, or combine --fetch --loose). # -# --fused Build a single gen1recomp--switch.nro via nacptool+elf2nro, -# then pack dist/switch/gen1recomp--switch.zip (SD-ready -# tree under switch/gen1recomp/). Uses native tools (PATH or -# $DEVKITPRO/tools/bin) first; else Docker from -# scripts/switch/dkp-docker.image (override GEN1_DKP_IMAGE). -# Requires the pin (run --fetch or combine). GitHub Releases -# publish the zip only; the versioned .nro stays local / PR CI. +# --fused Build fused gen1recomp--switch.nro (game in romfs), native +# OTA launcher NRO, and dual-NRO SD zip (gen1recomp.nro launcher + +# gen1recomp-game.nro under switch/gen1recomp/). The same +# gen1recomp--switch.zip is the OTA download asset. +# Requires DEVKITPRO + switch-dev; OTA launcher via native packages +# (install_devkitpro_deps.sh) or Docker. +# Uses native tools (PATH or $DEVKITPRO/tools/bin) first; else Docker +# from scripts/switch/dkp-docker.image (override GEN1_DKP_IMAGE). +# Requires the pin (run --fetch or combine). GitHub Releases publish +# the zip only; versioned .nro files stay local / PR CI. # -# --ota Like --fused, plus native OTA launcher NRO and dual-NRO SD zip -# (gen1recomp.nro launcher + gen1recomp-game.nro). The same -# gen1recomp--switch.zip is the OTA download asset. Needs -# DEVKITPRO switch-curl/mbedtls/zlib/zziplib — see -# scripts/switch/install_devkitpro_deps.sh. -# -# Combinable: --fetch alone, or --fetch with --loose / --fused / --ota. -# XOR: --loose, --fused, and --ota cannot be combined with each other. +# Combinable: --fetch alone, or --fetch with --loose / --fused. +# XOR: --loose and --fused cannot be combined with each other. # # Non-goals (never done by this script): # MTP push, ROM install, dkp-pacman auto-install (use install_devkitpro_deps.sh). @@ -49,7 +44,6 @@ DIST="$ROOT/dist/switch" LOOSE=0 FUSED=0 FETCH=0 -OTA=0 GAME_LOVE="" VERSION="$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo dev)" @@ -58,14 +52,13 @@ say() { printf '\033[1;32m==>\033[0m %s\n' "$*" >&2; } fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } usage() { - sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//' + sed -n '2,38p' "$0" | sed 's/^# \{0,1\}//' } while [ $# -gt 0 ]; do case "$1" in --loose) LOOSE=1; shift ;; --fused) FUSED=1; shift ;; - --ota) OTA=1; shift ;; --fetch) FETCH=1; shift ;; --version) VERSION="$2"; shift 2 ;; -h|--help) @@ -83,13 +76,12 @@ while [ $# -gt 0 ]; do esac done -MODE_COUNT=$((LOOSE + FUSED + OTA)) -if [ "$MODE_COUNT" -gt 1 ]; then - fail "choose one of --loose, --fused, or --ota (XOR)" +if [ "$LOOSE" -eq 1 ] && [ "$FUSED" -eq 1 ]; then + fail "choose one of --loose or --fused (XOR)" fi -if [ "$FETCH" -eq 0 ] && [ "$LOOSE" -eq 0 ] && [ "$FUSED" -eq 0 ] && [ "$OTA" -eq 0 ]; then - fail "specify --fetch, --loose, --fused, and/or --ota (see --help)" +if [ "$FETCH" -eq 0 ] && [ "$LOOSE" -eq 0 ] && [ "$FUSED" -eq 0 ]; then + fail "specify --fetch, --loose, and/or --fused (see --help)" fi if [ "$FETCH" -eq 1 ]; then @@ -139,22 +131,22 @@ if [ "$LOOSE" -eq 1 ]; then exec "$ROOT/scripts/switch/assemble_loose.sh" "$GAME_LOVE" fi -if [ "$FUSED" -eq 1 ] || [ "$OTA" -eq 1 ]; then +if [ "$FUSED" -eq 1 ]; then + # shellcheck source=scripts/switch/common.sh + . "$ROOT/scripts/switch/common.sh" + preflight_fused_build + GAME_LOVE="$(pack_game_love)" OUT_NRO="$DIST/gen1recomp-${VERSION}-switch.nro" OUT_ZIP="$DIST/gen1recomp-${VERSION}-switch.zip" "$ROOT/scripts/switch/build_fused.sh" "$GAME_LOVE" "$VERSION" "$OUT_NRO" - if [ "$OTA" -eq 1 ]; then - LAUNCHER_NRO="$DIST/gen1recomp-${VERSION}-launcher.nro" - say "building native OTA launcher" - "$ROOT/scripts/switch/build_ota_launcher.sh" "$LAUNCHER_NRO" "$VERSION" - say "packing dual-NRO SD zip (also the OTA download asset)" - "$ROOT/scripts/switch/pack_sd_zip.sh" "$OUT_NRO" "$VERSION" "$OUT_ZIP" "$LAUNCHER_NRO" - cp "$OUT_NRO" "$DIST/gen1recomp-${VERSION}-game.nro" - else - "$ROOT/scripts/switch/pack_sd_zip.sh" "$OUT_NRO" "$VERSION" "$OUT_ZIP" - fi + LAUNCHER_NRO="$DIST/gen1recomp-${VERSION}-launcher.nro" + say "building native OTA launcher" + "$ROOT/scripts/switch/build_ota_launcher.sh" "$LAUNCHER_NRO" "$VERSION" + say "packing dual-NRO SD zip (also the OTA download asset)" + "$ROOT/scripts/switch/pack_sd_zip.sh" "$OUT_NRO" "$VERSION" "$OUT_ZIP" "$LAUNCHER_NRO" + cp "$OUT_NRO" "$DIST/gen1recomp-${VERSION}-game.nro" cp "$WORK/build-info.json" "$DIST/gen1recomp-${VERSION}-build-info.json" say "done. See $DIST/" @@ -167,4 +159,4 @@ if [ "$FETCH" -eq 1 ]; then exit 0 fi -fail "specify --fetch, --loose, --fused, and/or --ota (see --help)" +fail "specify --fetch, --loose, and/or --fused (see --help)" diff --git a/scripts/switch/build_ota_launcher.sh b/scripts/switch/build_ota_launcher.sh index c1ce80dd..c8a8facd 100755 --- a/scripts/switch/build_ota_launcher.sh +++ b/scripts/switch/build_ota_launcher.sh @@ -22,15 +22,21 @@ DKP_IMAGE_FILE="$ROOT/scripts/switch/dkp-docker.image" [ -d "$LAUNCHER_DIR" ] || fail "missing $LAUNCHER_DIR" [ -f "$LAUNCHER_DIR/Makefile" ] || fail "missing Makefile" +if ! devkitpro_ready; then + fail_missing_devkitpro +fi + say "host-test ota_protocol (no DEVKITPRO required)" make -C "$LAUNCHER_DIR" host-test mkdir -p "$(dirname "$OUT_NRO")" -if [ -n "${DEVKITPRO:-}" ] && [ -f "$DEVKITPRO/libnx/switch_rules" ]; then +if ota_launcher_deps_ready; then say "building launcher NRO with native DEVKITPRO (NACP $APP_VERSION)" make -C "$LAUNCHER_DIR" clean || true - make -C "$LAUNCHER_DIR" all APP_VERSION="$APP_VERSION" + if ! make -C "$LAUNCHER_DIR" all APP_VERSION="$APP_VERSION"; then + fail_ota_launcher_toolchain + fi cp "$LAUNCHER_DIR/gen1recomp.nro" "$OUT_NRO" say "wrote $OUT_NRO" exit 0 @@ -51,24 +57,17 @@ resolve_dkp_image() { if command -v docker >/dev/null 2>&1; then image="$(resolve_dkp_image)" say "building launcher NRO via Docker ($image, NACP $APP_VERSION)" - docker run --rm \ + if ! docker run --rm \ -v "$ROOT:/work" \ -w /work/native/switch-ota-launcher \ -e "APP_VERSION=$APP_VERSION" \ "$image" \ - bash -lc 'pacman -Sy --noconfirm switch-curl switch-mbedtls switch-zlib switch-zziplib >/dev/null 2>&1 || true; make clean; make all APP_VERSION="$APP_VERSION"' + bash -lc 'pacman -Sy --noconfirm switch-curl switch-mbedtls switch-zlib switch-zziplib >/dev/null 2>&1 || true; make clean; make all APP_VERSION="$APP_VERSION"'; then + fail_ota_launcher_toolchain + fi cp "$LAUNCHER_DIR/gen1recomp.nro" "$OUT_NRO" say "wrote $OUT_NRO" exit 0 fi -fail "$(cat </dev/null 2>&1 || return 1 + local pkg + for pkg in "${OTA_LAUNCHER_PKGS[@]}"; do + dkp-pacman -Q "$pkg" >/dev/null 2>&1 || return 1 + done +} + +# Space-separated list of missing OTA launcher packages (empty when all installed). +ota_launcher_missing_pkgs() { + command -v dkp-pacman >/dev/null 2>&1 || return 0 + local pkg missing="" + for pkg in "${OTA_LAUNCHER_PKGS[@]}"; do + if ! dkp-pacman -Q "$pkg" >/dev/null 2>&1; then + missing="${missing} ${pkg}" + fi + done + printf '%s' "$missing" +} + +devkitpro_ready() { + [ -n "${DEVKITPRO:-}" ] && [ -f "$DEVKITPRO/libnx/switch_rules" ] +} + +fail_missing_devkitpro() { + fail "$(cat <<'EOF' +--fused requires DEVKITPRO (release builds ship the OTA launcher for in-console updates). + +One-time setup on the Mac self-hosted runner (or your dev machine): + + 1. Install devkitPro pacman: https://devkitpro.org/wiki/devkitPro_pacman + 2. Install Switch tools: sudo dkp-pacman -S switch-dev + 3. Export DEVKITPRO (typical macOS): + export DEVKITPRO=/opt/devkitpro + export PATH="$DEVKITPRO/tools/bin:$PATH" + 4. OTA launcher toolchain (native or Docker — pick one): + bash scripts/switch/install_devkitpro_deps.sh + or install Docker (same pin as fused builds) + +Then re-run: scripts/build_switch.sh --fetch --fused + +See docs/switch-build.md (Runner provisioning). +EOF +)" +} + +fail_missing_ota_deps() { + local missing + missing="$(ota_launcher_missing_pkgs)" + fail "$(cat </dev/null 2>&1 && command -v elf2nro >/dev/null 2>&1; then + game_ok=1 + elif command -v docker >/dev/null 2>&1; then + game_ok=1 + fi + if [ "$game_ok" -eq 0 ]; then + fail_fused_toolchain + fi + + if ota_launcher_deps_ready; then + return 0 + fi + if command -v docker >/dev/null 2>&1; then + return 0 + fi + fail_missing_ota_deps +} + # Fused mode needs nacptool/elf2nro (native or Docker). Rich multi-OS hint. fail_fused_toolchain() { fail "$(cat <<'EOF' -fused packaging needs nacptool and elf2nro (devkitPro switch-dev). +fused packaging needs DEVKITPRO, nacptool, and elf2nro (devkitPro switch-dev). Install options: - macOS: https://devkitpro.org/wiki/devkitPro_pacman (installer / pacman) + macOS: https://devkitpro.org/wiki/devkitPro_pacman + sudo dkp-pacman -S switch-dev + OTA launcher (native or Docker): + bash scripts/switch/install_devkitpro_deps.sh + or install Docker Linux: https://devkitpro.org/wiki/devkitPro_pacman Windows: Git Bash / MSYS2 / WSL with devkitPro tools on PATH - Docker: install Docker; build_fused.sh falls back to the pinned image + Docker: install Docker; fused game and OTA launcher can build in-container + +Export DEVKITPRO (typical): export DEVKITPRO=/opt/devkitpro See docs/switch-build.md for details. EOF diff --git a/scripts/switch/install_devkitpro_deps.sh b/scripts/switch/install_devkitpro_deps.sh index 65c4bac2..64bfc2d5 100755 --- a/scripts/switch/install_devkitpro_deps.sh +++ b/scripts/switch/install_devkitpro_deps.sh @@ -4,8 +4,15 @@ # # bash scripts/switch/install_devkitpro_deps.sh # +# Optional: speeds up local native OTA launcher builds. CI/release can fall back +# to Docker when these packages are not installed. +# set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=common.sh +. "$SCRIPT_DIR/common.sh" + if [ "$(id -u)" -ne 0 ]; then echo "==> re-running with sudo…" exec sudo -E bash "$0" "$@" @@ -25,14 +32,17 @@ if ! command -v dkp-pacman >/dev/null 2>&1; then exit 1 fi +if ! dkp-pacman -Q switch-dev >/dev/null 2>&1; then + echo "error: switch-dev is not installed (needed for nacptool/elf2nro)." >&2 + echo " sudo dkp-pacman -S switch-dev" >&2 + echo "Then re-run: bash scripts/switch/install_devkitpro_deps.sh" >&2 + exit 1 +fi + echo "==> DEVKITPRO=$DEVKITPRO" # Named packages only (avoid interactive switch-dev group member prompt). # ZIP reading uses switch-zziplib (dkp has no minizip port for Switch). -dkp-pacman -Sy --noconfirm --needed \ - switch-curl \ - switch-mbedtls \ - switch-zlib \ - switch-zziplib +dkp-pacman -Sy --noconfirm --needed "${OTA_LAUNCHER_PKGS[@]}" echo "==> installed packages:" dkp-pacman -Q | grep -E 'switch-(curl|mbedtls|zlib|zziplib|tools)|libnx|devkitA64' || true diff --git a/scripts/switch/selftest_build_switch.sh b/scripts/switch/selftest_build_switch.sh index 9bcfbc5b..b1634c0b 100755 --- a/scripts/switch/selftest_build_switch.sh +++ b/scripts/switch/selftest_build_switch.sh @@ -51,17 +51,17 @@ MISSING="" printf '%s' "$HELP_LC" | grep -q 'fetch' || MISSING="${MISSING} fetch" printf '%s' "$HELP_LC" | grep -q 'loose' || MISSING="${MISSING} loose" printf '%s' "$HELP_LC" | grep -q 'fused' || MISSING="${MISSING} fused" -printf '%s' "$HELP_LC" | grep -q 'ota' || MISSING="${MISSING} ota" +printf '%s' "$HELP_LC" | grep -Eq 'devkitpro' || MISSING="${MISSING} DEVKITPRO" printf '%s' "$HELP_LC" | grep -Eq 'auto-download|downloads' || MISSING="${MISSING} auto-download" printf '%s' "$HELP_LC" | grep -Eq 'non-goal|does not|never' || MISSING="${MISSING} non-goals" if [ -z "$MISSING" ]; then - ok "build_switch.sh --help mentions fetch, loose, fused, ota (+ auto-download/non-goals)" + ok "build_switch.sh --help mentions fetch, loose, fused, DEVKITPRO (+ auto-download/non-goals)" else bad "build_switch.sh --help missing:$MISSING" fi # --------------------------------------------------------------------------- -# 3. XOR --loose --fused / --ota exits non-zero +# 3. XOR --loose --fused exits non-zero # --------------------------------------------------------------------------- XOR_RC=0 "$ROOT/scripts/build_switch.sh" --loose --fused >/dev/null 2>&1 || XOR_RC=$? @@ -70,13 +70,6 @@ if [ "$XOR_RC" -ne 0 ]; then else bad "build_switch.sh --loose --fused should exit non-zero" fi -XOR_OTA_RC=0 -"$ROOT/scripts/build_switch.sh" --fused --ota >/dev/null 2>&1 || XOR_OTA_RC=$? -if [ "$XOR_OTA_RC" -ne 0 ]; then - ok "build_switch.sh --fused --ota exits non-zero ($XOR_OTA_RC)" -else - bad "build_switch.sh --fused --ota should exit non-zero" -fi # --------------------------------------------------------------------------- # 4. assemble_loose without pin → stderr contains --fetch @@ -188,7 +181,41 @@ else fi # --------------------------------------------------------------------------- -# 5c. fail_fused_toolchain mentions docs/switch-build.md +# 5c2. fail_missing_devkitpro cites install_devkitpro_deps.sh +# --------------------------------------------------------------------------- +MDK_ERR="$STAGING/missing-dkp.err" +MDK_RC=0 +( + . "$SCRIPT_DIR/common.sh" + fail_missing_devkitpro +) >"$STAGING/missing-dkp.out" 2>"$MDK_ERR" || MDK_RC=$? + +if [ "$MDK_RC" -ne 0 ] \ + && grep -q 'install_devkitpro_deps.sh' "$MDK_ERR" \ + && grep -q 'DEVKITPRO' "$MDK_ERR" \ + && grep -q 'switch-dev' "$MDK_ERR"; then + ok "fail_missing_devkitpro cites DEVKITPRO + switch-dev + install_devkitpro_deps.sh" +else + bad "fail_missing_devkitpro should cite setup steps (rc=$MDK_RC err=$(cat "$MDK_ERR"))" +fi + +OTA_ERR="$STAGING/missing-ota.err" +OTA_RC=0 +( + . "$SCRIPT_DIR/common.sh" + fail_missing_ota_deps +) >"$STAGING/missing-ota.out" 2>"$OTA_ERR" || OTA_RC=$? + +if [ "$OTA_RC" -ne 0 ] \ + && grep -q 'install_devkitpro_deps.sh' "$OTA_ERR" \ + && grep -qi 'docker' "$OTA_ERR"; then + ok "fail_missing_ota_deps cites native or Docker options" +else + bad "fail_missing_ota_deps should cite native + Docker (rc=$OTA_RC err=$(cat "$OTA_ERR"))" +fi + +# --------------------------------------------------------------------------- +# 5d. fail_fused_toolchain mentions docs/switch-build.md # --------------------------------------------------------------------------- FT_ERR="$STAGING/fused-toolchain.err" FT_RC=0 @@ -365,6 +392,9 @@ else fi if [ -f "$ROOT/scripts/switch/build_ota_launcher.sh" ] \ + && grep -q 'ota_launcher_deps_ready' "$ROOT/scripts/switch/build_ota_launcher.sh" \ + && grep -q 'fail_missing_devkitpro' "$ROOT/scripts/switch/build_ota_launcher.sh" \ + && grep -q 'preflight_fused_build' "$ROOT/scripts/build_switch.sh" \ && [ -f "$ROOT/native/switch-ota-launcher/Makefile" ] then ok "native OTA launcher sources + build_ota_launcher.sh present" diff --git a/test/switch-nro-ota.spec.test.js b/test/switch-nro-ota.spec.test.js index 3c56cdd9..ff1bd95f 100644 --- a/test/switch-nro-ota.spec.test.js +++ b/test/switch-nro-ota.spec.test.js @@ -529,12 +529,15 @@ test('AC-010: Fonte do launcher e Makefile DEVKITPRO existem @spec:AC-010', () = assert.match(read('native/switch-ota-launcher/src/ota_fs.c'), /ota_fs_atomic_replace_nro/); assert.doesNotMatch(read('scripts/switch/install_devkitpro_deps.sh'), /switch-minizip/); assert.match(read('scripts/switch/install_devkitpro_deps.sh'), /switch-zziplib/); + assert.match(read('scripts/switch/install_devkitpro_deps.sh'), /switch-dev/); const buildDoc = read('docs/switch-build.md'); assert.match(buildDoc, /native\/switch-ota-launcher|build_ota_launcher/); - assert.match(buildDoc, /switch-curl/); - assert.match(buildDoc, /--ota|install_devkitpro_deps/); + assert.match(buildDoc, /native packages.*or.*Docker|native or Docker/i); + assert.match(buildDoc, /--fused|install_devkitpro_deps/); + assert.match(buildDoc, /Requires DEVKITPRO|DEVKITPRO is[\s\S]*required/i); assert.doesNotMatch(buildDoc, /switch-ota\.zip/); + assert.doesNotMatch(buildDoc, /--ota\b/); const readme = read('native/switch-ota-launcher/README.md'); assert.match(readme, /DEVKITPRO|devkitPro/i); @@ -555,11 +558,16 @@ test('AC-011: Empacotamento dual-NRO e selftest @spec:AC-011', () => { ); const buildSwitch = read('scripts/build_switch.sh'); - assert.match(buildSwitch, /--ota/); + assert.doesNotMatch(buildSwitch, /--ota\b/); assert.doesNotMatch(buildSwitch, /pack_ota_zip\.sh/); assert.match(buildSwitch, /build_ota_launcher\.sh/); assert.match(buildSwitch, /dual-NRO SD zip|OTA download asset/i); + const buildOtaLauncher = read('scripts/switch/build_ota_launcher.sh'); + assert.match(buildOtaLauncher, /ota_launcher_deps_ready/); + assert.match(buildOtaLauncher, /fail_missing_devkitpro/); + assert.match(buildSwitch, /preflight_fused_build/); + const selftest = read('scripts/switch/selftest_build_switch.sh'); assert.match(selftest, /dual-NRO|gen1recomp-game\.nro/); assert.match(selftest, /ota_launcher\.manifest/);