Skip to content

Quit the installed macOS tray as macOS does, a logout's quit among them - #1031

Merged
SimonCropp merged 4 commits into
mainfrom
mac-tray-lifecycle
Nov 5, 2026
Merged

SimonCropp merged 4 commits into
mainfrom
mac-tray-lifecycle

Conversation

@SimonCropp

@SimonCropp SimonCropp commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

This adds the lifecycle coverage the review of #991 asked for.

Until now the tray-install job started the installed tray, waited for it to listen on 3492 and then killed it. Nothing ever asked it to quit the way macOS does.

What's new. On macOS, a new step starts the installed tray twice and quits it with an Apple event each time, sent by pid from a small Swift script (.github/scripts/send-quit.swift, compiled with xcrun swiftc).

  • An ordinary quit, as the Dock or AppleScript sends it. The tray has to refuse it, answering userCanceledErr (-128), and exit through its own loop.
  • A logout's quit: the same event with kAEQuitReason set to kAELogOut. It must not be answered -128, because that refusal is what cancels a logout.
  • After each quit, the tray has to exit with status 0 and leave no version marker ($TMPDIR/DiffEngineTray/version.txt). After a logout's quit, only the session end can remove the marker, because AppKit ends the process without going through the loop's own exit.
  • The step fails early and says why if the tray has no status item. Without one, nothing pumps the events sent to it.

CI can't log out, but the event is what a logout sends, so the whole of TerminateGuard's session path runs. That covers reading the reason, calling back into managed code through detray_on_session_end, and returning .terminateNow.

Not covered. No inline snapshot is in the queue, so this doesn't show a trio being staged. Getting one into the tray's queue means driving a client, and the tray then opens a viewer window. TraySessionEnd is shared with every other way a session ends, and its own tests cover staging.

Tests. Nothing here can be run on Windows; this PR's macOS tray-install job is the test. It shows the following:

  • A quit Apple event reaches an unbundled accessory process by pid with no Automation prompt.
  • A plain quit is answered -128.
  • A logout's quit is answered 0.
  • After both, the tray exits with status 0 and its marker is gone.

The logout reason has to go on the event as an attribute, which is where loginwindow puts it. A first version of the script sent it as a parameter, and the tray rightly took that for an ordinary quit.

The tray-install job started the tray and waited for it to listen, and
nothing ever asked it to quit. On macOS it now sends the tray the quit
Apple events by pid: an ordinary one, which the tray refuses (-128) and
turns into its own exit, and a logout's, kAEQuitReason set to kAELogOut,
which it has to let through. Each has to end the tray with status 0 and
its version marker gone. CI cannot log out; the event is what a logout
sends, so the whole of TerminateGuard's session path runs.
On macOS a connection to 3492 was taken before the installed tray had
started, so the install step never waited for it, and the quit events
went to a tray that was still starting. Ask lsof whether the tray's own
process id is listening, and list whatever else holds the port first.
send-quit set kAEQuitReason as a parameter of the quit event, and
TerminateGuard reads it as an attribute, so the logout's quit read as an
ordinary one and was refused. The install step's connection check was
never wrong: the tray listens within a second, and its first line of
output only arrives late because it is buffered. So it is back as it was,
and only the quit step asks lsof, for the process the events go to.
A tray that starts and quits cleanly logs nothing, so there is no
log.txt, and under bash -e the cat of it ended the step that had just
passed, and would have ended each failing path before its error line.
@SimonCropp
SimonCropp merged commit b946e00 into main Nov 5, 2026
14 checks passed
@SimonCropp
SimonCropp deleted the mac-tray-lifecycle branch November 5, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant