Preserve Unix execute bit in Linux/macOS release zips#373
Merged
Conversation
Compress-Archive on the windows-latest runner can't store Unix file-mode bits, so the self-contained apphost (PlanViewer.App) extracted as non-executable on Linux/macOS and the app would not launch. .NET's ZipArchive is no better: on Windows it stamps the archive host byte as Windows (create_system=0), so unzip/macOS ignore the mode even when ExternalAttributes carries 0755. Add .github/scripts/zip_with_exec.py (Python zipfile with create_system=3) to zip the Linux flat output and both macOS .app bundles, marking the apphost (and createdump if present) 0755. It hard-fails if the apphost is missing so a broken archive fails the release loudly. Wire it into release.yml and nightly.yml; Windows stays on Compress-Archive (no Unix bit needed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add CFBundleDocumentTypes to Info.plist so Finder shows the app icon for .sqlplan files and routes them to Performance Studio via the Open With menu. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ":P0" format is culture-sensitive in the space before the percent sign (en-US -> "100%", InvariantCulture -> "100 %"). Warning text therefore differed between en-US dev machines and CI runners that default to InvariantCulture, breaking the golden-master WarningCharacterizationTests (baseline recorded "100%", ubuntu produced "100 %").
Format the three percents as "{x * 100:N0}%" with a literal percent sign. N0 preserves the digits and group separators :P0 produced on en-US, so the committed baseline stays valid; the literal % drops the culture-dependent space, making output identical on every platform. Verified: full Core suite passes under default and DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Linux and macOS release artifacts were built with PowerShell's
Compress-Archiveon thewindows-latestrunner, which cannot store Unix file-mode bits. The self-contained apphostPlanViewer.Apptherefore extracted as non-executable, and the app failed to launch ("permission denied") on Linux/macOS — the blocker reported by a tester.What changed
.github/scripts/zip_with_exec.py(new): zips a directory withcreate_system=3(Unix) so extractors honor the mode, marking the apphost — andcreatedumpif present —0755, everything else0644. Hard-fails if the apphost is missing, so a broken archive fails the release loudly instead of shipping.release.yml/nightly.yml: the Linux flat zip and both macOS.appbundles now go through the helper. Windows stays onCompress-Archive(no Unix execute bit; signed-binary path untouched).Info.plist: addedCFBundleDocumentTypesso Finder shows the app icon for.sqlplanfiles and offers Performance Studio in "Open With".PlanAnalyzer.Node.cs: fixed culture-dependent percent formatting (:P0→{x * 100:N0}%). Pre-existing bug ondevunrelated to packaging, but it blocks CI::P0renders100%on en-US and100 %on InvariantCulture, soWarningCharacterizationTestsfailed on the ubuntu runner (baseline was recorded on en-US). Had to fix it here to get this PR green. Verified the full Core suite passes under both default andDOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1.Why not just use .NET / pwsh?
ZipArchivecan setExternalAttributes, but on Windows it stamps the archive host byte as Windows (create_system=0), sounzip/macOS ignore the Unix mode. Verified empirically — onlycreate_system=3(Python'szipfile) yields an executable apphost on extraction.Test plan
create_system=3on every entry, missing-apphost guard fires, optional-createdump skipped cleanly when absent, archive round-trips intact.Info.plistvalidated withplistlib; both workflow YAMLs parse.release.ymlrun on merge to main (produces the v1.14.0 Linux/macOS zips).Known follow-up (not in this PR)
On macOS the
.sqlplanassociation registers the icon and launches the app, but the app discovers the file to open fromargv/named-pipe only; macOS delivers it via akAEOpenDocumentsApple Event (IActivatableApplicationLifetime). Double-click-to-open the plan needs that handler wired up — tracked separately.🤖 Generated with Claude Code