Commit Graph
2 Commits
Author SHA1 Message Date
l0ng-ai 47e25ef854 fix(ci): judge a Mach-O's signature by codesign's exit status (#696)
`codesign -dv` spells its signature line differently per posture:
`Signature=adhoc` for an ad-hoc or linker signature, `Signature size=8968`
for a Developer ID one with a timestamp. The check matched the literal
`Signature=`, which the second spelling does not contain.

While the script only pointed at the standalone tty7-server, which is
ad-hoc signed, that was invisible. #692 pointed it at the bundle's
tty7-app, tty7 and tty7-updater as well, and those are Developer ID
signed whenever the signing secrets are present. Pull requests do not see
the secrets, so every PR run took the ad-hoc branch and passed; the first
build that signed for real — the nightly — failed on all three binaries,
printing `CodeDirectory`, `Signature size=8968` and a Developer ID
`TeamIdentifier` as its proof they carried no signature. The binaries
were signed, notarized and stapled; only the assertion was wrong.

Exit status has no such split: 0 for anything signed, 1 with `code object
is not signed at all` for anything not, verified against all three
postures. The output is still captured so the failure message carries it.
2026-08-20 14:00:40 +08:00
l0ng-aiandl0ng-ai 6c26b35acc fix(control): bump the dialect to v6, and publish a tty7-server for macOS (#605)
* fix(control): bump the dialect to v6 so an out-of-date server says so

The control dialect has been renamed, extended and cut since it was
last numbered, all of it against CONTROL_VERSION 5: the machine tree
replaced WorkspaceList/Get/Put/Delete with WorkspaceTree, MachineGet
and the tab/pane verbs, GitStream arrived with its chunk and end
events, and ReplyOk::Attached and FileMeta went away.

A peer left behind by any of that still answers the hello, because the
number it answers with still matches. It is also still sitting at the
path the installer looks for, tty7-server-c5p5, so a client decides it
already has the server it needs. Then the first call reaches a variant
the peer has never heard of, the frame fails to decode, and the read
loop takes the whole link down with it. What the user sees is a remote
workspace that opens with no tabs and a git detail pane that never
fills, with nothing anywhere saying why.

Moving the number puts all three guards back: the hello is refused with
the message that names the old build, the remote binary is looked for
at c6p5 and installed rather than trusted, and a stale local daemon
gets the restart prompt it should have been getting all along.

Document the rule next to the constant while it is fresh: move it when
a variant is added or removed. The feature strings only cover what a
peer can safely ignore, and a request it cannot decode is not that.

* feat(remote): publish a tty7-server for macOS hosts

A remote workspace has been Linux-only for no reason anyone chose: the
installer derives the asset name from `uname -sm`, and the only names it
knew were the two musl builds. A Mac on the other end of an SSH profile
got "a remote tty7 workspace needs a Linux host" and stopped there.

Publish the two Apple slices alongside them and teach the installer to
ask for them. `Darwin arm64` and `Darwin x86_64` now map to
tty7-server-macos-aarch64 and tty7-server-macos-x86_64; everything past
that point already worked, because nothing under it was ever Linux-
specific — the install path is POSIX, the upload is SFTP, and the
dialect probe runs the binary before trusting it.

The machine names are matched per system rather than by architecture
alone. Linux says aarch64 on one distribution and arm64 on the next,
while a Mac only ever says arm64, so honouring Linux's spellings under
Darwin would be guessing at output no Mac produces.

Static linking is not the instrument on macOS — Apple ships no static
libSystem — so assert-macho.sh stands in for assert-static.sh with the
guarantee that actually matters: every dependency resolves under
/usr/lib or /System/Library, so nothing the destination Mac lacks can be
picked up from a build runner, and the binary carries the signature
arm64 refuses to run without.

Not signed or notarized beyond that, deliberately. The binary is never
downloaded by the Mac that runs it: the client fetches it, verifies it
against checksums.txt and writes it over SFTP, which sets no quarantine
attribute, so Gatekeeper is not in the path.

ASSET_X86_64 and ASSET_AARCH64 become ASSET_LINUX_*, which is what they
always meant and could not keep meaning next to a macOS pair.

* fix(ci): sign the x86_64 macOS server, and stop the guard flaking on it

Two faults the first green run hid from each other.

The linker ad-hoc signs the arm64 slice because Apple Silicon will not
execute anything unsigned, and leaves x86_64 bare. That is fine on an
Intel Mac, but the x86_64 server is also what an Apple Silicon box gets
when it asks through a Rosetta shell, and handing that machine an
unsigned binary is a guess about Rosetta nobody needs to make. Sign both
slices ad-hoc in the workflow — no identity, no secrets, nothing to do
with the notarized signing the GUI bundles get.

The guard that caught it was itself unreliable: `codesign -dv | grep -q`
under `pipefail` reports failure whenever grep wins the race, because -q
exits on the first match and the writer takes SIGPIPE. Small output means
the writer usually finishes first, which is why the arm64 job passed and
x86_64 failed on the same signed-or-not question. Capture into a variable
and match afterwards, the way the release workflow already does it.

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-08-13 11:47:27 +08:00