Skip to content

Commit ad69f14

Browse files
authored
[install] Name quarantine as the wedge cause, not /Applications (#27)
The message a user sees when the installed bundle never starts told them the wrong thing. It blamed "Gatekeeper assessment on a bundle in /Applications" and called ~/Applications "known-good", which is backwards: a quarantined bundle hangs in ~/Applications identically, and an unquarantined one runs fine in /Applications. The variable is the xattr, not the directory. Isolating #25 established that. Acting on the old advice also cost you the recovery. It sent people to reinstall elsewhere without saying that the first blocked launch wedges the path, so deleting the xattr or replacing the bundle in place looks like it should work and does not. The detail block bypasses err(), which prefixes every line with ERROR:. Fifteen of those is a wall, and this is read by someone already stuck. Solves: #25 follow-up — the diagnostic contradicted the isolation Tests: new assertion pins the message to com.apple.quarantine and a working recovery, and rejects the disproved advice returning; mutation-checked by restoring the old wording, which fails it
1 parent 77a1708 commit ad69f14

2 files changed

Lines changed: 46 additions & 10 deletions

File tree

install-server.sh

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,11 @@ fi
527527
# no matter what grep says, so the check condemned a working binary.
528528
#
529529
# BOUNDED, because "does not run" and "does not finish" are different failures
530-
# and only one of them used to be handled. A bundle can block in dyld before
531-
# reaching main — Gatekeeper assessment on a bundle in /Applications does
532-
# exactly this (issue #25) — and an unbounded check then hangs the installer
533-
# forever with no output, no error, and no service. A verification step that
534-
# can wedge is worse than no verification step.
530+
# and only one of them used to be handled. A bundle carrying
531+
# com.apple.quarantine blocks in dyld before reaching main (issue #25), and an
532+
# unbounded check then hangs the installer forever with no output, no error,
533+
# and no service. A verification step that can wedge is worse than no
534+
# verification step.
535535
# run_bounded is defined above the source guard so the tests can reach it.
536536

537537
# --help, NOT a bare invocation. `tacet` with no arguments now runs the agent
@@ -544,10 +544,31 @@ verify_rc=$?
544544
set -e
545545

546546
if [[ "$verify_rc" -eq 124 ]]; then
547+
# The headline goes through err(); the rest does not. err() prefixes every
548+
# line with ERROR:, and fifteen of those is a wall nobody reads at the moment
549+
# they are already stuck.
547550
err "installed ${APP_DST} but it did not finish starting within 20s."
548-
err "The process blocks before reaching main — nothing it logs will say so."
549-
err "A bundle in /Applications does this under Gatekeeper assessment (#25);"
550-
err "installing to ~/Applications (no cask, no TACET_APP_DIR) is known-good."
551+
cat >&2 <<EOF
552+
553+
The process blocks before reaching main, so nothing it logs will say why.
554+
555+
Almost always this is com.apple.quarantine on the bundle (issue #25). Nothing
556+
launches tacet through LaunchServices — launchd starts it and this script execs
557+
it — so the first-launch consent gate has nobody to answer it and the process
558+
waits forever. Notarization does not help; the gate is consent, not assessment.
559+
560+
xattr -p com.apple.quarantine ${APP_DST}
561+
562+
Removing the attribute does NOT recover this path. The first blocked launch
563+
wedges the path itself, and neither deleting the xattr nor replacing the bundle
564+
clears it — only a path that has never wedged:
565+
566+
rm -rf ${APP_DST}
567+
TACET_APP_DIR=~/Applications ./install-server.sh
568+
569+
The Homebrew cask strips quarantine from 0.1.2 on, so a current
570+
'brew install --cask drycodeworks/tap/tacet' should not reach this message.
571+
EOF
551572
exit 1
552573
fi
553574
if [[ "$usage_out" != *"usage: tacet"* ]]; then

tests/test_install_server_args.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ def test_the_usage_text_documents_the_env_overrides(self, tmp_path):
5959
class TestVerificationIsBounded:
6060
"""The installed binary is executed to prove it works. That must not hang.
6161
62-
A bundle can block in dyld before reaching main — Gatekeeper assessment on
63-
a bundle in /Applications does exactly that (#25). Unbounded, the installer
62+
A bundle carrying com.apple.quarantine blocks in dyld before reaching main
63+
(#25) — the consent gate has no UI to answer it. Unbounded, the installer
6464
waits forever: no output, no error, no service, nothing in any log.
6565
"""
6666

@@ -78,6 +78,21 @@ def test_a_timeout_is_reported_differently_from_a_broken_binary(self):
7878
code = SCRIPT.read_text()
7979
assert 'verify_rc" -eq 124' in code, "the timeout status must be handled"
8080

81+
def test_the_timeout_message_names_quarantine_and_the_real_recovery(self):
82+
# This message is read by someone already stuck, so being wrong here is
83+
# expensive. It used to blame /Applications and call ~/Applications
84+
# "known-good", which is backwards: a quarantined bundle hangs in
85+
# ~/Applications too, and an unquarantined one runs fine in
86+
# /Applications. The variable is the xattr, not the directory.
87+
code = SCRIPT.read_text()
88+
timeout_branch = code[code.index('verify_rc" -eq 124'):code.index("Shared secret")]
89+
assert "com.apple.quarantine" in timeout_branch, \
90+
"the message must name the actual cause"
91+
assert "TACET_APP_DIR" in timeout_branch, \
92+
"the message must give a recovery that works"
93+
assert "known-good" not in timeout_branch, \
94+
"the disproved /Applications-vs-~/Applications advice is back"
95+
8196
def test_the_fallback_exists_because_macos_ships_no_timeout(self):
8297
# A stock macOS has neither timeout(1) nor gtimeout. Relying on
8398
# coreutils would leave the check silently unbounded on exactly the

0 commit comments

Comments
 (0)