Pulse/scripts/tests
courtmanr@gmail.com 5c1c89a363 Guard auto-update asset staging and migrate stale update units
Adversarial review of 9db25ba60 found four residual defects in the
auto-update asset install, plus a doc line it left contradicting itself.

- install_auto_update_assets copied the bundled helper into the staged
  mktemp file with an unchecked cp, and both call sites invoke the
  function under `if !`, which suppresses errexit for its whole body. A
  failed copy (ENOSPC, EIO) fell through to
  configure_auto_update_script_repo, whose awk emits a lone GITHUB_REPO=
  line for empty input, so a shebang-less one-line stub replaced the
  working helper with a "script" that only ever exits 0 - silently
  disabling unattended updates. Check the cp, and refuse the swap unless
  the staged helper is non-empty and starts with #!.
- Both units were rendered with a bare truncating `cat > "$unit"` whose
  status was never checked, and the function's last statement is
  safe_systemctl daemon-reload, which returns 0 by design. A failing
  write truncated a working unit and still reported success. Render each
  unit to ${path}.tmp and commit it with a checked rename, so a failure
  leaves the installed unit byte-identical.
- The widened ReadWritePaths could not reach deployed boxes: the unit
  that grants the write access is itself the file that has to be
  rewritten, and on an existing install the sandbox running the
  installer excludes /etc/systemd/system and /usr/local/bin (EROFS). The
  Go update pipeline cannot carry it either - pulse.service runs as
  User=pulse with its own ProtectSystem=strict over the install and
  config dirs only. So probe each destination directory up front and,
  when one is blocked, re-exec this already-signature-verified installer
  through systemd-run with a new internal --repair-auto-update-units
  entry point: PID 1 forks the transient unit, so it starts in the host
  mount namespace instead of inheriting the sandbox. The installer is
  copied into the install dir first because the calling unit's
  PrivateTmp=yes hides its /tmp copy from PID 1. The escape needs root
  and systemd-run, and never recurses.
- Keep the ReadWritePaths entries as directory grants: every write now
  commits with a rename from a sibling staging file, and rename needs
  write access on the containing directory, so the file-level entries
  systemd would otherwise accept cannot work. Document the tradeoff in
  the unit and the subsystem contract instead.

The deployment-installability contract still claimed the update sandbox
leaves "only the install dir, config dir and /tmp" writable, which the
paragraph the same file gained in 9db25ba60 contradicts; the same stale
rationale had been copied into two test comments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 11:56:52 +01:00
..
integration feat: Pulse v6 release 2026-03-18 16:06:30 +00:00
run.sh test(scripts): run python smoke tests 2026-03-28 21:53:09 +00:00
test-cloud-public-signup-smoke.sh Add Pulse Cloud public signup smoke 2026-04-23 23:09:10 +01:00
test-common-lib.sh feat: add shared script library system and refactor docker-agent installer 2025-10-20 15:13:38 +00:00
test-hot-dev-auth.sh Stabilize script smoke checks 2026-05-28 12:55:33 +01:00
test-hot-dev-bg.sh backend and governance: MCP contract, agent capabilities, API, and release-control 2026-06-23 17:26:15 +01:00
test-hot-dev-runtime.sh Build canonical Patrol attention workbench 2026-07-19 15:11:01 +01:00
test-install-ps1-parser.sh Tighten unified agent hardening proof 2026-04-23 23:37:25 +01:00
test-install-update-resilience.sh Guard auto-update asset staging and migrate stale update units 2026-07-28 11:56:52 +01:00
test-pulse-auto-update.sh Fix auto-update leaving service down after installer failure 2026-07-27 09:38:37 +01:00
test-retired-trial-acquisition-docs.sh Rename retired trial acquisition proof assets 2026-04-28 18:38:10 +01:00
test-script-reference-integrity.sh Switch script-reference integrity test from rg to git grep for portable CI 2026-05-12 00:30:43 +01:00
test-toggle-mock.sh Recover managed mock-mode restart handoffs 2026-06-01 08:33:18 +01:00
test_localized_public_docs.py Add localized public docs entry points 2026-06-13 08:07:46 +01:00
test_repo_docs_link_drift.py test(repo): generalize docs drift guard 2026-03-28 22:15:12 +00:00
test_sync_chart_release_metadata.py fix(deploy): pin docs links to release refs 2026-03-28 21:32:11 +00:00
test_telemetry_adoption_report.py Measure verified telemetry outcomes 2026-07-27 10:15:48 +01:00
test_telemetry_schema_parity.py Add privacy-safe telemetry lifecycle and outcome signals 2026-07-23 01:10:38 +01:00