fix: repair PortMaster package compatibility

This commit is contained in:
Alexander Sellite 2026-08-16 08:56:03 -04:00
parent 6175ebd686
commit a170922588
3 changed files with 108 additions and 26 deletions

View file

@ -7,9 +7,20 @@ version-named generic ZIP. The installer-facing archive must be named
```text ```text
NETfishing.sh NETfishing.sh
netfishing/ netfishing/
port.json
``` ```
`port.json` belongs at `netfishing/port.json` in the distributable archive.
The upstream source-submission layout keeps it beside the launcher and port
directory, but PortMaster's release builder relocates it into the declared
port directory. Do not copy the source-submission layout directly into a
release ZIP.
HarbourMaster's installer also relocates recognized non-script files found at
an archive root into the detected port directory. That compatibility behavior
masked NETfishing's former root-level `port.json` during auto-install tests.
Directly extracting the same archive into `/ports` did not perform the repair.
Do not rely on installer relocation; build the distributable layout correctly.
Do not derive a release package from an older local `portmaster-stage` Do not derive a release package from an older local `portmaster-stage`
directory. The templates in `scripts/portmaster/` are authoritative. directory. The templates in `scripts/portmaster/` are authoritative.
@ -30,9 +41,10 @@ directory. The templates in `scripts/portmaster/` are authoritative.
`netfishing/conf/config`, and `netfishing/conf/cache`. `netfishing/conf/config`, and `netfishing/conf/cache`.
- The archive must not contain `conf/`, saves, identities, logs, source files, - The archive must not contain `conf/`, saves, identities, logs, source files,
`.git`, or `.godot` content. `.git`, or `.godot` content.
- Release downloads must publish `netfishing.zip`. A renamed versioned ZIP is - Release downloads must publish only the canonical `netfishing.zip` for this
not a substitute because HarbourMaster identifies the canonical port by platform. Do not also publish a byte-identical versioned PortMaster ZIP;
archive name. HarbourMaster identifies the port by its canonical archive name and the
Forgejo release tag already supplies the version.
## Build ## Build
@ -53,6 +65,18 @@ builds/v<project-version>/netfishing.zip
`--package-only` is reserved for repackaging an already validated ARM64 export. `--package-only` is reserved for repackaging an already validated ARM64 export.
It does not rebuild game content. It does not rebuild game content.
For a packaging-only correction to an already published game release, commit
only PortMaster launcher, packaging-script, or PortMaster documentation changes
after the immutable release tag, then run:
```bash
bash scripts/build_portmaster.sh --package-only --hotfix
```
The hotfix keeps the game executable and PCK tied to the release tag while
recording the newer packaging commit separately in `BUILD-INFO.txt`. Replace
the release's existing `netfishing.zip`; never move the game release tag.
## Local muOS installation ## Local muOS installation
The muOS auto-install directory is: The muOS auto-install directory is:
@ -128,11 +152,13 @@ The device-verified Godot mapping is:
`scripts/portmaster/NETfishing.sh` must replace the incompatible SDL2 entry `scripts/portmaster/NETfishing.sh` must replace the incompatible SDL2 entry
with the Godot entry when that SDL2 GUID is selected. Do not append the two with the Godot entry when that SDL2 GUID is selected. Do not append the two
entries with a newline: WestonPack evaluates launcher arguments through a entries with a newline. WestonPack reconstructs the wrapped game command
shell and treats the second line as a command. Pass the single selected mapping through a shell, so values passed in that command can be split into unintended
as `SDL_GAMECONTROLLERCONFIG` in the game command after Weston initializes; commands. Controller names may legally contain spaces, which caused this
Weston sources PortMaster's control file internally and otherwise restores the failure with the `GO-Super Gamepad` mapping. Export the single selected
SDL2 value. `SDL_GAMECONTROLLERCONFIG` value through the outer `env` invocation, before
`westonwrap.sh`, so Weston and the game inherit it without tokenizing the
mapping text.
Do not pass a `.gptk` controller mapping to GPTOKEYB. Its exit-only process Do not pass a `.gptk` controller mapping to GPTOKEYB. Its exit-only process
must retain PortMaster's controller configuration, while the verified Godot must retain PortMaster's controller configuration, while the verified Godot

View file

@ -8,12 +8,25 @@ readonly TEMPLATE_ROOT="${SCRIPT_DIR}/portmaster"
readonly GODOT_BIN="${GODOT_BIN:-godot}" readonly GODOT_BIN="${GODOT_BIN:-godot}"
PACKAGE_ONLY=0 PACKAGE_ONLY=0
if [[ "${1:-}" == "--package-only" ]]; then HOTFIX=0
PACKAGE_ONLY=1 while [[ $# -gt 0 ]]; do
case "$1" in
--package-only)
PACKAGE_ONLY=1
;;
--hotfix)
HOTFIX=1
;;
*)
echo "Usage: $0 [--package-only] [--hotfix]" >&2
exit 2
;;
esac
shift shift
fi done
if [[ $# -ne 0 ]]; then if [[ ${HOTFIX} -eq 1 && ${PACKAGE_ONLY} -ne 1 ]]; then
echo "Usage: $0 [--package-only]" >&2 echo "PortMaster hotfixes must use an existing pinned ARM64 export." >&2
echo "Run this script with both --package-only and --hotfix." >&2
exit 2 exit 2
fi fi
@ -33,11 +46,30 @@ readonly ARM64_PCK="${ARM64_ROOT}/NETfishing.pck"
readonly STAGE_ROOT="${RELEASE_ROOT}/portmaster-stage" readonly STAGE_ROOT="${RELEASE_ROOT}/portmaster-stage"
readonly GAME_ROOT="${STAGE_ROOT}/netfishing" readonly GAME_ROOT="${STAGE_ROOT}/netfishing"
readonly ARCHIVE="${RELEASE_ROOT}/netfishing.zip" readonly ARCHIVE="${RELEASE_ROOT}/netfishing.zip"
readonly LEGACY_ARCHIVE="${RELEASE_ROOT}/netfishing-${RELEASE_TAG}-portmaster-arm64.zip" readonly OBSOLETE_VERSIONED_ARCHIVE="${RELEASE_ROOT}/NETfishing-${RELEASE_TAG}-portmaster-arm64.zip"
readonly OBSOLETE_LOWERCASE_ARCHIVE="${RELEASE_ROOT}/netfishing-${RELEASE_TAG}-portmaster-arm64.zip"
readonly HEAD_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse HEAD)" readonly HEAD_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse HEAD)"
readonly TAG_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse "${RELEASE_TAG}^{commit}")" readonly TAG_COMMIT="$(git -C "${PROJECT_ROOT}" rev-parse "${RELEASE_TAG}^{commit}")"
if [[ "${HEAD_COMMIT}" != "${TAG_COMMIT}" ]]; then if [[ ${HOTFIX} -eq 1 ]]; then
if ! git -C "${PROJECT_ROOT}" merge-base --is-ancestor \
"${TAG_COMMIT}" "${HEAD_COMMIT}"; then
echo "${RELEASE_TAG} is not an ancestor of HEAD. Refusing hotfix package." >&2
exit 1
fi
while IFS= read -r changed_path; do
case "${changed_path}" in
docs/PORTMASTER.md|scripts/build_portmaster.sh|scripts/portmaster/*)
;;
*)
echo "Hotfix contains a non-PortMaster change: ${changed_path}" >&2
exit 1
;;
esac
done < <(git -C "${PROJECT_ROOT}" diff --name-only \
"${TAG_COMMIT}..${HEAD_COMMIT}")
elif [[ "${HEAD_COMMIT}" != "${TAG_COMMIT}" ]]; then
echo "HEAD does not match ${RELEASE_TAG}. Refusing to package mutable source." >&2 echo "HEAD does not match ${RELEASE_TAG}. Refusing to package mutable source." >&2
exit 1 exit 1
fi fi
@ -75,11 +107,14 @@ test -s "${ARM64_PCK}"
file "${ARM64_EXECUTABLE}" | grep -q "ARM aarch64" file "${ARM64_EXECUTABLE}" | grep -q "ARM aarch64"
rm -rf -- "${STAGE_ROOT}" rm -rf -- "${STAGE_ROOT}"
rm -f -- "${ARCHIVE}" "${LEGACY_ARCHIVE}" rm -f -- \
"${ARCHIVE}" \
"${OBSOLETE_VERSIONED_ARCHIVE}" \
"${OBSOLETE_LOWERCASE_ARCHIVE}"
mkdir -p -- "${GAME_ROOT}/licenses" mkdir -p -- "${GAME_ROOT}/licenses"
install -m 0755 "${TEMPLATE_ROOT}/NETfishing.sh" "${STAGE_ROOT}/NETfishing.sh" install -m 0755 "${TEMPLATE_ROOT}/NETfishing.sh" "${STAGE_ROOT}/NETfishing.sh"
install -m 0644 "${TEMPLATE_ROOT}/port.json" "${STAGE_ROOT}/port.json" install -m 0644 "${TEMPLATE_ROOT}/port.json" "${GAME_ROOT}/port.json"
install -m 0755 "${ARM64_EXECUTABLE}" "${GAME_ROOT}/NETfishing.aarch64" install -m 0755 "${ARM64_EXECUTABLE}" "${GAME_ROOT}/NETfishing.aarch64"
install -m 0644 "${ARM64_PCK}" "${GAME_ROOT}/NETfishing.pck" install -m 0644 "${ARM64_PCK}" "${GAME_ROOT}/NETfishing.pck"
install -m 0644 "${TEMPLATE_ROOT}/gameinfo.xml" "${GAME_ROOT}/gameinfo.xml" install -m 0644 "${TEMPLATE_ROOT}/gameinfo.xml" "${GAME_ROOT}/gameinfo.xml"
@ -107,7 +142,9 @@ cat >"${GAME_ROOT}/BUILD-INFO.txt" <<EOF
NETfishing PortMaster ARM64 build NETfishing PortMaster ARM64 build
Project version: ${PROJECT_VERSION} Project version: ${PROJECT_VERSION}
Source commit: ${TAG_COMMIT} Game source commit: ${TAG_COMMIT}
Packaging source commit: ${HEAD_COMMIT}
Packaging mode: $([[ ${HOTFIX} -eq 1 ]] && printf 'PortMaster hotfix' || printf 'release')
Engine/export template: $("${GODOT_BIN}" --version) Engine/export template: $("${GODOT_BIN}" --version)
Architecture: AArch64 Architecture: AArch64
Minimum linked GLIBC symbol version: GLIBC_2.28 Minimum linked GLIBC symbol version: GLIBC_2.28
@ -125,6 +162,7 @@ NETfishing code is licensed under GPL-3.0-or-later.
Source repository: https://forge.makearmy.io/woofmeow/netfishing Source repository: https://forge.makearmy.io/woofmeow/netfishing
Exact source revision: ${TAG_COMMIT} Exact source revision: ${TAG_COMMIT}
Release tag: ${RELEASE_TAG} Release tag: ${RELEASE_TAG}
PortMaster packaging revision: ${HEAD_COMMIT}
EOF EOF
cat >"${GAME_ROOT}/README.md" <<EOF cat >"${GAME_ROOT}/README.md" <<EOF
@ -153,28 +191,45 @@ EOF
( (
cd "${STAGE_ROOT}" cd "${STAGE_ROOT}"
zip -qry "${ARCHIVE}" NETfishing.sh netfishing port.json zip -qry "${ARCHIVE}" NETfishing.sh netfishing
) )
unzip -tq "${ARCHIVE}" >/dev/null unzip -tq "${ARCHIVE}" >/dev/null
readonly TOP_LEVELS="$( readonly TOP_LEVELS="$(
unzip -Z1 "${ARCHIVE}" | cut -d/ -f1 | sort -u unzip -Z1 "${ARCHIVE}" | cut -d/ -f1 | sort -u
)" )"
readonly EXPECTED_TOP_LEVELS="$(printf '%s\n' NETfishing.sh netfishing port.json)" readonly EXPECTED_TOP_LEVELS="$(printf '%s\n' NETfishing.sh netfishing)"
if [[ "${TOP_LEVELS}" != "${EXPECTED_TOP_LEVELS}" ]]; then if [[ "${TOP_LEVELS}" != "${EXPECTED_TOP_LEVELS}" ]]; then
echo "Unexpected PortMaster archive roots:" >&2 echo "Unexpected PortMaster archive roots:" >&2
printf '%s\n' "${TOP_LEVELS}" >&2 printf '%s\n' "${TOP_LEVELS}" >&2
exit 1 exit 1
fi fi
grep -q '"version": 4' "${STAGE_ROOT}/port.json" grep -q '"version": 4' "${GAME_ROOT}/port.json"
grep -q '"name": "netfishing.zip"' "${STAGE_ROOT}/port.json" grep -q '"name": "netfishing.zip"' "${GAME_ROOT}/port.json"
grep -q '"rtr": true' "${STAGE_ROOT}/port.json" grep -q '"rtr": true' "${GAME_ROOT}/port.json"
if unzip -Z1 "${ARCHIVE}" | grep -qx 'port.json'; then
echo "port.json must be inside netfishing/, not at the archive root." >&2
exit 1
fi
grep -q '^# PORTMASTER: netfishing.zip, NETfishing.sh$' \ grep -q '^# PORTMASTER: netfishing.zip, NETfishing.sh$' \
"${STAGE_ROOT}/NETfishing.sh" "${STAGE_ROOT}/NETfishing.sh"
grep -Fq 'PROFILE_PATH="$CONFDIR/performance_profile"' \ grep -Fq 'PROFILE_PATH="$CONFDIR/performance_profile"' \
"${STAGE_ROOT}/NETfishing.sh" "${STAGE_ROOT}/NETfishing.sh"
grep -Fq 'NETFISHING_PERFORMANCE_PROFILE=light' \ grep -Fq 'NETFISHING_PERFORMANCE_PROFILE=light' \
"${STAGE_ROOT}/NETfishing.sh" "${STAGE_ROOT}/NETfishing.sh"
readonly CONTROLLER_ENV_LINE="$(
grep -nF 'SDL_GAMECONTROLLERCONFIG="$netfishing_controllerconfig"' \
"${STAGE_ROOT}/NETfishing.sh" | cut -d: -f1
)"
readonly WESTON_LAUNCH_LINE="$(
grep -nF '"$WESTON_DIR/westonwrap.sh" headless noop kiosk crusty_x11egl' \
"${STAGE_ROOT}/NETfishing.sh" | cut -d: -f1
)"
if [[ -z "${CONTROLLER_ENV_LINE}" || -z "${WESTON_LAUNCH_LINE}" || \
"${CONTROLLER_ENV_LINE}" -ge "${WESTON_LAUNCH_LINE}" ]]; then
echo "SDL_GAMECONTROLLERCONFIG must be exported before westonwrap.sh." >&2
exit 1
fi
grep -Fq '$GPTOKEYB "NETfishing.aarch64" &' \ grep -Fq '$GPTOKEYB "NETfishing.aarch64" &' \
"${STAGE_ROOT}/NETfishing.sh" "${STAGE_ROOT}/NETfishing.sh"
grep -Fq 'pm_platform_helper "$GAME_EXECUTABLE"' \ grep -Fq 'pm_platform_helper "$GAME_EXECUTABLE"' \

View file

@ -123,12 +123,13 @@ esac
$GPTOKEYB "NETfishing.aarch64" & $GPTOKEYB "NETfishing.aarch64" &
pm_platform_helper "$GAME_EXECUTABLE" pm_platform_helper "$GAME_EXECUTABLE"
$ESUDO env CRUSTY_RESOLUTION="${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}" \ $ESUDO env \
CRUSTY_RESOLUTION="${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}" \
SDL_GAMECONTROLLERCONFIG="$netfishing_controllerconfig" \
"$WESTON_DIR/westonwrap.sh" headless noop kiosk crusty_x11egl \ "$WESTON_DIR/westonwrap.sh" headless noop kiosk crusty_x11egl \
XDG_DATA_HOME="$CONFDIR/data" \ XDG_DATA_HOME="$CONFDIR/data" \
XDG_CONFIG_HOME="$CONFDIR/config" \ XDG_CONFIG_HOME="$CONFDIR/config" \
XDG_CACHE_HOME="$CONFDIR/cache" \ XDG_CACHE_HOME="$CONFDIR/cache" \
SDL_GAMECONTROLLERCONFIG="$netfishing_controllerconfig" \
GODOT_SILENCE_ROOT_WARNING=1 \ GODOT_SILENCE_ROOT_WARNING=1 \
"${NETFISHING_GAME_ENVIRONMENT[@]}" \ "${NETFISHING_GAME_ENVIRONMENT[@]}" \
"$GAME_EXECUTABLE" \ "$GAME_EXECUTABLE" \