From 05c93777d62243d7d413c10584daf7f3a4620c5a Mon Sep 17 00:00:00 2001 From: fy59 Date: Thu, 17 Sep 2026 16:09:58 +0200 Subject: [PATCH] fix(sync): resolve versioned launcher symlinks --- CHANGELOG.md | 3 +++ docs/sync_rollout_readiness.md | 2 ++ tests/test_sync_deployment_tools.py | 10 ++++++++++ tools/package_sync_candidate.py | 7 ++++--- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f0c804..cf47ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ SQLite value whether a real backup contains an active draft or legitimately contains none. +- Made relocatable launchers resolve stable user-bin symlinks to their physical + versioned bundle before loading matching binaries and helpers. + - Closed the native candidate warning gate with checked path and HTTP response bounds, strict GCC and Clang builds, full Clang ASan/UBSan validation, and a stable concurrent object-I/O MTP double. Rebuilt the desktop, current Android diff --git a/docs/sync_rollout_readiness.md b/docs/sync_rollout_readiness.md index 3651dba..345c61c 100644 --- a/docs/sync_rollout_readiness.md +++ b/docs/sync_rollout_readiness.md @@ -26,6 +26,8 @@ the separately authorized rollout are complete. `trainlog-syncd`, the production generation MTP adapter and their matching Python codecs. User units must point only into one versioned bundle; they must not combine a packaged binary with helpers from a live checkout. + Stable user-bin symlinks are supported: each launcher resolves the link to + its physical bundle before locating `libexec` and `tools`. 2. `assembleDebug` is development evidence only. Rollout requires the existing release signing identity, verification of its public certificate and a valid increasing `versionCode`. A bounded `TRAINLOG_ANDROID_VERSION_CODE` build diff --git a/tests/test_sync_deployment_tools.py b/tests/test_sync_deployment_tools.py index 5c9b099..f7e3873 100644 --- a/tests/test_sync_deployment_tools.py +++ b/tests/test_sync_deployment_tools.py @@ -66,6 +66,16 @@ class DeploymentToolsTest(unittest.TestCase): self.assertTrue((output / "bin/trainlog-sync-once").is_file()) self.assertTrue((output / "bin/trainlog-syncd").is_file()) self.assertTrue((output / "libexec/trainlog-sync-once").is_file()) + stable_bin = Path(directory) / "stable-bin" + stable_bin.mkdir() + (stable_bin / "trainlog").symlink_to(output / "bin/trainlog") + linked = subprocess.run( + [str(stable_bin / "trainlog"), "--version"], + capture_output=True, + text=True, + ) + self.assertEqual(linked.returncode, 0, linked.stderr) + self.assertIn("trainlog 0.1.2", linked.stdout) self.assertTrue( any( row["path"] == "tools/trainlog_syncd.py" diff --git a/tools/package_sync_candidate.py b/tools/package_sync_candidate.py index 0327007..889fed5 100644 --- a/tools/package_sync_candidate.py +++ b/tools/package_sync_candidate.py @@ -71,15 +71,15 @@ def main() -> int: shutil.copy2(source, args.output / "catalog" / source.name) launcher = args.output / "bin/trainlog" launcher.write_text( - '#!/bin/sh\nset -eu\nroot=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)\nexport TRAINLOG_SYNC_TOOLS_DIR="$root/tools"\nexport TRAINLOG_SYNC_MTP_ADAPTER="$root/libexec/trainlog-generation-mtp-adapter"\nexec "$root/libexec/trainlog" "$@"\n' + '#!/bin/sh\nset -eu\nscript=$(readlink -f -- "$0")\nroot=$(CDPATH= cd -- "$(dirname -- "$script")/.." && pwd)\nexport TRAINLOG_SYNC_TOOLS_DIR="$root/tools"\nexport TRAINLOG_SYNC_MTP_ADAPTER="$root/libexec/trainlog-generation-mtp-adapter"\nexec "$root/libexec/trainlog" "$@"\n' ) sync_once_launcher = args.output / "bin/trainlog-sync-once" sync_once_launcher.write_text( - '#!/bin/sh\nset -eu\nroot=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)\nexec "$root/libexec/trainlog-sync-once" "$@"\n' + '#!/bin/sh\nset -eu\nscript=$(readlink -f -- "$0")\nroot=$(CDPATH= cd -- "$(dirname -- "$script")/.." && pwd)\nexec "$root/libexec/trainlog-sync-once" "$@"\n' ) syncd_launcher = args.output / "bin/trainlog-syncd" syncd_launcher.write_text( - '#!/bin/sh\nset -eu\nroot=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)\nexec python3 "$root/tools/trainlog_syncd.py" --sync-once "$root/bin/trainlog-sync-once" "$@"\n' + '#!/bin/sh\nset -eu\nscript=$(readlink -f -- "$0")\nroot=$(CDPATH= cd -- "$(dirname -- "$script")/.." && pwd)\nexec python3 "$root/tools/trainlog_syncd.py" --sync-once "$root/bin/trainlog-sync-once" "$@"\n' ) files = sorted(path for path in args.output.rglob("*") if path.is_file()) inventory = { @@ -114,6 +114,7 @@ def main() -> int: "libudev", "libmtp", "notcurses", + "coreutils-readlink", ], "files": [ {