Skip to content

Add standalone native binaries for Windows/macOS/Linux - #54

Open
sugatoray wants to merge 7 commits into
masterfrom
claude/pptxdiff-installers-m3mym2
Open

Add standalone native binaries for Windows/macOS/Linux#54
sugatoray wants to merge 7 commits into
masterfrom
claude/pptxdiff-installers-m3mym2

Conversation

@sugatoray

Copy link
Copy Markdown
Owner

Adds src/packages/binaries/ (@pptxdiff/binaries): builds a standalone
pptxdiff executable per OS via Node's Single Executable Applications
feature, so a user can download one artifact and run it without
installing Node.js. Deliberately standalone binaries, not signed OS
installers — asked directly, matching a prior explicit decision to
avoid Electron/Tauri-style installer/signing overhead.

  • bin/cli.js: startServer() gains a backward-compatible optional
    root param so the packaged binary can serve assets from next to
    itself instead of the npm package's own directory.
  • sea-entry.cjs + build.mjs: bundle via esbuild, generate the SEA
    blob, inject via postject, copy static app assets alongside the
    binary, zip as the downloadable artifact.
  • .github/workflows/binaries.yml: 3-OS CI matrix (SEA has no
    cross-compile mode, so each OS's binary is built on that OS).
  • make pkg.binaries.build / npm run build:binary for local builds.

Verified end-to-end on Linux: built, ran the actual packaged binary,
confirmed it serves index.html/support.js/vendor/* correctly.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01BxwMTp6RQJ6j6K5K8Jjdpm

@socket-security

socket-security Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​yao-pkg/​pkg@​6.22.0911009293100

View full report

claude added 7 commits August 9, 2026 03:07
Adds src/packages/binaries/ (@pptxdiff/binaries): builds a standalone
pptxdiff executable per OS via Node's Single Executable Applications
feature, so a user can download one artifact and run it without
installing Node.js. Deliberately standalone binaries, not signed OS
installers — asked directly, matching a prior explicit decision to
avoid Electron/Tauri-style installer/signing overhead.

- bin/cli.js: startServer() gains a backward-compatible optional
  `root` param so the packaged binary can serve assets from next to
  itself instead of the npm package's own directory.
- sea-entry.cjs + build.mjs: bundle via esbuild, generate the SEA
  blob, inject via postject, copy static app assets alongside the
  binary, zip as the downloadable artifact.
- .github/workflows/binaries.yml: 3-OS CI matrix (SEA has no
  cross-compile mode, so each OS's binary is built on that OS).
- make pkg.binaries.build / npm run build:binary for local builds.

Verified end-to-end on Linux: built, ran the actual packaged binary,
confirmed it serves index.html/support.js/vendor/* correctly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxwMTp6RQJ6j6K5K8Jjdpm
build.mjs gains an entrypoint guard (matching capture_screenshots.mjs's
existing pattern) exporting PLATFORM_MAP/ASSET_ENTRIES/resolveTarget/
buildBinary for testability.

- test_build_config.mjs (fast, pure, `npm test`): 17 assertions,
  including a static regression guard on bin/cli.js's
  startServer(root = ROOT) signature. Demonstrated genuine RED->GREEN
  by temporarily reverting that signature and confirming the test
  catches it.
- test_build_e2e.mjs (slow, real, `npm run test:e2e`): builds and
  runs the actual packaged binary, verifying real HTTP responses and
  path-traversal rejection. 11/11 against a real build in this
  sandbox.
- Fixed a real bug found while writing the e2e test: the "clean the
  output dir" step was a blind rm -rf that would have deleted each
  OS folder's tracked README.md/CHANGELOG.md on every build. Replaced
  with a targeted cleanup that only removes generated entries;
  verified by running the real build twice and confirming the docs
  survive both times.
- Added CHANGELOG.md to each pptxdiff-{win,mac,linux} folder and
  filled in the root CHANGELOG.md's previously-empty [Unreleased]
  section.
- .github/workflows/binaries.yml now runs both test suites before
  building each OS's release artifact.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxwMTp6RQJ6j6K5K8Jjdpm
Direct follow-up to "why aren't you using yao-pkg/pkg?" — investigated
hands-on and confirmed two real advantages over Node's SEA feature:

- Genuine cross-compilation: built real Windows (PE32+) and macOS
  (Mach-O) binaries from this Linux sandbox, something SEA cannot do
  at all (it can only build for its own currently-running platform).
- Built-in asset embedding needing zero bin/cli.js changes: pkg's
  snapshot filesystem preserves the real project's relative directory
  layout, so pointing pkg directly at the unmodified bin/cli.js with
  src/pptxdiff/** as assets makes the existing ROOT computation just
  resolve correctly. Reverted the SEA-era startServer(root) parameter
  entirely — bin/cli.js is now byte-identical to before this feature.

Found and fixed a real, silently-failing gotcha mid-switch: pkg's
"assets" glob paths resolve relative to wherever the config file
itself lives, not cwd or the entry file's directory. Confirmed via a
controlled A/B test; fixed by writing the temp pkg config directly at
the repo root (removed in a finally block). New WISDOM.md trap entry
with the full reproduction.

macOS is deliberately NOT cross-compiled even though pkg technically
can: codesign only exists on macOS, and an unsigned binary may not
launch at all on Apple Silicon. .github/workflows/binaries.yml now
runs 2 jobs instead of 3: linux+win build together on ubuntu-latest
(genuine cross-compile), mac stays on its own macos-latest runner.

Both test files rewritten and re-verified with genuine RED->GREEN on
the config-colocation guard. Output is now a true single file per OS
(no more assets/ folder, no more zip wrapper).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxwMTp6RQJ6j6K5K8Jjdpm
Direct follow-up to "does the mac binary work for Apple Silicon
MacBooks?" - the honest answer was yes, but only via Rosetta 2
translation, since only the Intel (x64) target existed.

TARGET_MAP gains an outDirKey field, separate from the map's own
key, so `mac` and the new `mac-arm64` (node22-macos-arm64) share one
output folder (pptxdiff-mac/) while keeping distinct binary names.
buildOne() now computes outDir from target.outDirKey rather than the
osKey argument.

.github/workflows/binaries.yml's build-mac job now builds both mac
targets; test_build_e2e.mjs picks mac vs mac-arm64 based on the
host's actual os.arch(), so GitHub's Apple Silicon macos-latest
runners genuinely exercise the native build.

Verified for real in this sandbox: built the arm64 target directly,
confirmed via `file` a genuine Mach-O 64-bit arm64 executable,
confirmed it lands in the shared folder without disturbing the
tracked README.md/CHANGELOG.md there. pkg's own error output
independently confirmed the Apple Silicon signing requirement this
project's reasoning already relied on.

Windows and Linux stay x64-only - not asked about, and arm64 desktop
usage is a smaller fraction of their likely audience than Apple
Silicon is of the Mac audience.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxwMTp6RQJ6j6K5K8Jjdpm
Direct follow-up to "can we support arm64 for windows and linux as
well?" - immediately after the mac-only arm64 addition.

Unlike macOS, neither Windows nor Linux needs a signing step, so
both new targets (node22-win-arm64, node22-linux-arm64) fold into
the existing cross-compiled build-linux-win CI job rather than
needing a new one.

Found and fixed a real build failure while verifying pkg actually
supports these targets: cross-arch builds need to execute a
matching-arch helper binary to generate V8 bytecode, which fails
with a genuine exec-format error on a host with no QEMU/binfmt
emulation for that arch (confirmed absent in this sandbox, not
assumed). pkg's own warning named the fix: --fallback-to-source,
now applied unconditionally in buildOne() (a no-op for same-arch
builds).

Verified via the real production build path: real ELF aarch64 and
PE32+ Aarch64 executables, binary size consistent with real assets
being embedded, landing correctly in each OS's existing shared
output folder without disturbing the tracked docs there. Neither
binary was run (no arm64 execution emulation in this sandbox) - CI
is what actually executes them for the first time.

Genuine RED->GREEN demonstrated on the new --fallback-to-source
regression guard, which protects against a class of failure that
would silently break every arm64 target while leaving x64 green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxwMTp6RQJ6j6K5K8Jjdpm
The public MkDocs docs-site had never been touched across any of the
@pptxdiff/binaries commits, even though docs/.scrolls was kept
current throughout - confirmed via git log rather than assumed.

- getting-started.md: new "Option D - standalone binary" install
  option, with a matching doc_coverage id (native-binaries, partial).
- cli.md: cross-link to Option D. index.md: updated install card.
- limitations.md: new row for the binaries' own limitations, and
  reworded the existing "browser tab, not native window" row so it
  doesn't read like the binaries are an exception to it.
- New changelog subpage changelogs/pptxdiff-binaries.md, transcluding
  all three per-OS CHANGELOG.md files (this package has no single
  package-level one) - added to nav and the changelog index.
- coverage_registry.yml: two new ids, native-binaries and
  native-binaries-limitations; sync_doc_coverage.py --write/--check
  re-run (36 complete, 4 partial, 0 missing).

Found and fixed a real mkdocs build --strict failure: the three
per-OS CHANGELOG.md files' relative link back to the root
CHANGELOG.md was correct on GitHub but broke once transcluded into
the new subpage at a different path. Fixed at the source with an
absolute GitHub URL, matching every other changelog subpage's
existing pattern.

Verified for real: mkdocs build --strict clean, and directly grepped
the built HTML to confirm the new anchor matches exactly between the
defining page and both pages that link to it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxwMTp6RQJ6j6K5K8Jjdpm
Rebasing the binaries branch onto master (which landed Homebrew and
Chocolatey packaging work in the interim) required renumbering the
binaries SPEC.md section from the collided §32 to §36, since master's
Homebrew formula legitimately owns §32. A few self-references to the
binaries feature's own section number, written before the rebase,
still said §32 in HANDOFF.md, PLAN.md, and the per-OS CHANGELOG.md
files - fixed to §36 (verified every remaining §32 reference in the
repo is the real, correct Homebrew one).

Also regenerates documentation-coverage.md via sync_doc_coverage.py
--write, since the getting-started.md "Option D" naming collision
(both this branch and master's Homebrew work independently claimed
it) was resolved by renaming this branch's option to "Option E".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxwMTp6RQJ6j6K5K8Jjdpm
@sugatoray
sugatoray force-pushed the claude/pptxdiff-installers-m3mym2 branch from 66072eb to 50fe345 Compare August 9, 2026 03:17
@sugatoray sugatoray self-assigned this Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants