Skip to content

The macOS lane signs, and takes a set in each language - #5

Open
anderix wants to merge 3 commits into
mainfrom
macos-lane
Open

anderix wants to merge 3 commits into
mainfrom
macos-lane

Conversation

@anderix

@anderix anderix commented Sep 18, 2026

Copy link
Copy Markdown
Member

Three fixes to packaging/macos, found while making the first Mac App Store
submission: the signing refused, the capture had no way to be told a language,
and the size check would refuse a correct Retina capture.

One certificate in two keychains is one identity, not two

find_identity counted lines from security find-identity, which searches
every keychain in the search list. A Mac set up to sign over ssh has a signing
keychain holding a copy of what the login keychain already had — which is what
mac-signing-keychain.sh puts there on purpose — so every identity is reported
at least twice. Here it was four times, and the check read that as four Apple
Distribution certificates and signed nothing. The setup for ssh signing is what
broke signing over ssh.

It now counts distinct SHA-1s. The case the check was written for is untouched:
two genuinely different certificates sharing a display name, an expiring one
beside its replacement, still refuses, and both directions were tried before the
change went in.

This one is not odox's alone. flyleaf, segler and duckling carry the
same line-counting check, and each will refuse the same way the first time it
signs from that Mac.

A capture is checked against the sizes App Store Connect takes

The window is sized in points and screencapture writes pixels, so a display
with a backing scale of 2 hands back twice what was asked for. Both are right:
Connect takes 1280x800, 1440x900, 2560x1600 and 2880x1800, and the larger pair
is the smaller pair doubled. Comparing the file against the number asked for
would call a correct Retina capture wrong.

Not reached on the Mac this was written on, whose display is 1x. It is the
refusal a Retina machine meets on its first run.

The set is taken in a language, and a recipe layer takes it

screenshot.sh gains --lang, handed to the application through open --env
as POTEXT_LANG. It cannot be exported by the calling shell: open hands the
process to launchd, and launchd does not pass that environment on. It is the
shape flyleaf's driver already uses, and this is the last window lane to get it.

It also gains --settle, because a deck whose slides carry pictures is still
decoding them when a document of a few pages has settled — the same reason
shots.ps1 gives xodp eight seconds.

shots.sh is the half that is odox's: which application, which document, which
locale directory. It is the counterpart of packaging/windows/shots.ps1,
carries the same table in this platform's spellings, and writes
dist/screenshots/<locale>/, the layout ship --shots reads. It photographs a
development-signed bundle, because a Store build cannot be launched on the
machine that made it.

Measured

  • The three Store packages build and sign, each carrying
    Authority=Apple Distribution: Excelano LLC (9K6W5PMFYP), universal
    x86_64 arm64, profile embedded, and exactly two entitlements.
  • altool --validate-app returns VERIFY SUCCEEDED for all three, and all three
    uploaded and processed to VALID as build 80.
  • Six frames: three applications in each of en-US and de-DE, 1440x900, with
    German chrome in the German set — Datei, Ansicht, Gliederung, Folien, and
    100 % with the German space.

🤖 Generated with Claude Code

anderix and others added 3 commits September 17, 2026 22:15
`find_identity` counted lines from `security find-identity`, which searches
every keychain in the search list. A Mac set up to sign over ssh has a signing
keychain holding a copy of what the login keychain already had, which
`mac-signing-keychain.sh` puts there deliberately, so every identity is
reported at least twice. Here it was four times, and the check read that as
four Apple Distribution certificates and refused to sign anything.

It now counts distinct SHA-1s. The case the check was written for is untouched:
two genuinely different certificates sharing a display name, an expiring one
beside its replacement, still refuses, and that was tried both ways before the
change went in.

Measured: the three Store packages build and sign, each carrying
`Authority=Apple Distribution: Excelano LLC (9K6W5PMFYP)`, and `altool
--validate-app` returns VERIFY SUCCEEDED for all three.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The window is sized in points and `screencapture` writes pixels, so a display
with a backing scale of 2 hands back twice what was asked for. Both are right:
Connect takes 1280x800, 1440x900, 2560x1600 and 2880x1800, and the larger pair
is the smaller pair doubled. Comparing the file against the number asked for
would call a correct Retina capture wrong and refuse it.

Not reached on this Mac, whose display is 1x and whose captures come back at
the 1440x900 the window was sized to. It is the refusal a Retina machine would
have met on its first run, and the message named the sizes without saying what
they are, which is the other half of why it would have been puzzling.

The second condition keeps what the first one gave up: an accepted size that is
not this window at either scale is some other window, and still refuses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`screenshot.sh` gains `--lang`, passed to the application through `open --env`
as POTEXT_LANG. It cannot be exported by the calling shell: `open` hands the
process to launchd, and launchd does not pass this shell environment on. That
is the shape flyleaf's driver already uses, and this is the last of the four
window lanes to get it.

It also gains `--settle`. A deck whose slides carry pictures is still decoding
them when a document of a few pages has settled, and `shots.ps1` gives xodp
eight seconds for the same reason.

`shots.sh` is the half that is odox's: which application, which document, and
which locale directory. It is the counterpart of `packaging/windows/shots.ps1`
and carries the same table in this platform's spellings, writing
`dist/screenshots/<locale>/` - the layout `ship --shots` reads and the Windows
lane already writes. It photographs a development-signed bundle, because a
Store build cannot be launched on the machine that made it.

Measured: six frames, three applications in each of en-US and de-DE, 1440x900,
with German chrome in the German set - Datei, Ansicht, Gliederung, Folien, and
100 % with the German space.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@anderix

anderix commented Sep 18, 2026

Copy link
Copy Markdown
Member Author

Read all three files. The find_identity fix is right and the reasoning behind it holds: one certificate in two keychains is listed under one SHA-1 twice, so keying on the hash and sort -u collapses it where counting lines could not. Three notes, none of them blocking.

--only is not validated, and its Windows counterpart is. shots.ps1 declares [ValidateSet('xodt', 'xods', 'xodp')][string] $Only, so a typo there is refused by name. Here any value is accepted: --only xdot matches no application, the loop body never runs, $outdir is never created, and the failure surfaces at find "$outdir" under set -eu as a bare "No such file or directory" rather than as the wrong argument. The usage text already promises the constraint the code does not enforce. A case beside the --lang one would close it, and --settle has the same shape — a non-numeric value reaches sleep and fails there instead.

The identity sed assumes uppercase hex. \([0-9A-F]*\) matches security find-identity's output as macOS prints it today, but if that ever came back lowercase the pattern would not match at all, the line would pass through unchanged, and ${matches#* } would strip one leading space and hand 1) <hash> "Apple Distribution: ..." to codesign -s. It fails either way, just at codesign with a confusing message rather than at the check written to catch exactly this. [0-9A-Fa-f] costs nothing.

The scale check is correct, and for a reason worth writing down. Comparing only got_w against width and width * 2 is sufficient here solely because the four sizes App Store Connect accepts have four distinct widths — 1280, 1440, 2560, 2880 — so the case above already pins the height. That is load-bearing and invisible: a fifth accepted size sharing a width with an existing one would make a mismatched capture pass silently. One line saying so would keep the next reader from "simplifying" the case.

Checked and found correct: the deck's eight seconds do match $DECK_SETTLE = 8 on the Windows lane, and the un-localed output directory when --lang is absent is fine rather than a gap — ship calls takeShots(r, fleet.Mac, "en de") and never uses the bare form, so that path is local preview only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant