diff --git a/docs/ci/BUNDLED_RELEASES.md b/docs/ci/BUNDLED_RELEASES.md index 795769ca..7b09ae27 100644 --- a/docs/ci/BUNDLED_RELEASES.md +++ b/docs/ci/BUNDLED_RELEASES.md @@ -49,7 +49,8 @@ Template: [`templates/game-release.yml`](templates/game-release.yml). as `licenses/recomp-ui-LICENSE` and its font/image notices as `assets/{fonts,img}/NOTICE.md`); refuse if anything kit-shaped is in the stage; sign on Windows; zip. -6. Verify the zip: executable + OpenBIOS + catalog present; no `psxrecomp/`, +6. Verify the zip: executable + OpenBIOS + catalog + recomp-ui license and + font/image notices present; no `psxrecomp/`, `recomp-ui/`, CLI, emitters at the root, sources, generated C, dumps, per-machine mod state. diff --git a/docs/ci/templates/game-release.yml b/docs/ci/templates/game-release.yml index 03bcef5b..d664a398 100644 --- a/docs/ci/templates/game-release.yml +++ b/docs/ci/templates/game-release.yml @@ -598,7 +598,7 @@ jobs: LIST="$(unzip -Z1 "${ZIP}")" # 1. It is a game: the executable, the bundled OpenBIOS, and the # runtime data sit at the root. - for must in 'bios/openbios.bin' 'bios/OpenBIOS.LICENSE' 'game.toml' 'psx_game_version.txt' 'assets/fonts/' 'mods/bundled/'; do + for must in 'bios/openbios.bin' 'bios/OpenBIOS.LICENSE' 'game.toml' 'psx_game_version.txt' 'assets/fonts/' 'licenses/recomp-ui-LICENSE' 'assets/fonts/NOTICE.md' 'assets/img/NOTICE.md' 'mods/bundled/'; do if ! grep -q "^${must}" <<<"${LIST}"; then echo "::error::release zip lacks ${must}" >&2 printf '%s\n' "${LIST}" | head -40 >&2 diff --git a/tools/package_game_release.sh b/tools/package_game_release.sh index 733fd6ca..7a8490d6 100755 --- a/tools/package_game_release.sh +++ b/tools/package_game_release.sh @@ -384,16 +384,14 @@ fi if ! grep -qs '^PSX_RECOMP_UI:BOOL=OFF' "${BUILD_DIR}/CMakeCache.txt"; then UI_ROOT="$(sed -n 's/^RECOMP_UI_ROOT:[A-Z]*=//p' "${BUILD_DIR}/CMakeCache.txt" 2>/dev/null | head -n1 || true)" [[ -n "${UI_ROOT}" && -f "${UI_ROOT}/recomp_ui.cmake" ]] || UI_ROOT="${ROOT}/recomp-ui" - if [[ -f "${UI_ROOT}/recomp_ui.cmake" ]]; then - [[ -f "${UI_ROOT}/LICENSE" ]] || { echo "error: ${UI_ROOT}/LICENSE missing" >&2; exit 1; } - cp -a "${UI_ROOT}/LICENSE" "${STAGE}/licenses/recomp-ui-LICENSE" - for sub in fonts img; do - notice="${UI_ROOT}/assets/common/${sub}/NOTICE.md" - if [[ -f "${notice}" && -d "${STAGE}/assets/${sub}" ]]; then - cp -a "${notice}" "${STAGE}/assets/${sub}/NOTICE.md" - fi - done - fi + [[ -f "${UI_ROOT}/recomp_ui.cmake" ]] || { echo "error: recomp-ui assets are staged but no recomp-ui tree found (RECOMP_UI_ROOT / ${ROOT}/recomp-ui)" >&2; exit 1; } + [[ -f "${UI_ROOT}/LICENSE" ]] || { echo "error: ${UI_ROOT}/LICENSE missing" >&2; exit 1; } + cp -a "${UI_ROOT}/LICENSE" "${STAGE}/licenses/recomp-ui-LICENSE" + for sub in fonts img; do + notice="${UI_ROOT}/assets/common/${sub}/NOTICE.md" + [[ -f "${notice}" ]] || { echo "error: ${notice} missing" >&2; exit 1; } + cp -a "${notice}" "${STAGE}/assets/${sub}/NOTICE.md" + done fi if [[ -z "$(ls -A "${STAGE}/licenses" 2>/dev/null)" ]]; then echo "error: no third-party notices staged (${FRAMEWORK}/runtime/licenses empty?)" >&2 diff --git a/tools/tests/test_bundled_release_layout.py b/tools/tests/test_bundled_release_layout.py index b42c1b2c..df41bb26 100644 --- a/tools/tests/test_bundled_release_layout.py +++ b/tools/tests/test_bundled_release_layout.py @@ -317,6 +317,15 @@ class PackagerScriptContract(unittest.TestCase): self.assertIn('assets/common/${sub}/NOTICE.md', text) self.assertIn('"${STAGE}/assets/${sub}/NOTICE.md"', text) self.assertIn("RECOMP_UI_ROOT", text, "must follow the tree the build linked") + self.assertIn('[[ -f "${UI_ROOT}/recomp_ui.cmake" ]] ||', text, + "missing recomp-ui tree must fail packaging") + self.assertIn('[[ -f "${notice}" ]] ||', text, + "missing asset notices must fail packaging") + template = TEMPLATE.read_text(encoding="utf-8") + for path in ("licenses/recomp-ui-LICENSE", "assets/fonts/NOTICE.md", + "assets/img/NOTICE.md"): + self.assertIn(f"'{path}'", template, + f"the CI zip verifier must require {path}") def test_scripts_are_executable_and_parse(self): for rel in ("tools/package_game_release.sh", "tools/ci/check_generated.sh"):