close
Skip to content

Tags: electron/packager

Tags

v20.3.0

Toggle v20.3.0's commit message

Partially verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
feat: asar integrity digest (#1890)

* feat: asar integrity digest

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>

* test: assert sentinel exists in asar-disabled digest test

The "does not write digest when asar is disabled" test wrapped its
only assertion in `if (sentinelIndex !== -1)`, silently passing with
zero assertions if the sentinel was missing. Both sibling tests in the
same describe block explicitly assert the sentinel exists — this one
drifted. With test/config.json on 41.1.0 the sentinel is guaranteed
present, so the guard only masked failures.

* perf: use chunked scan + positional writes for integrity digest

Previously setIntegrityDigest loaded the entire Electron Framework
binary (150-500 MB) into memory, scanned it, modified 34 bytes, and
wrote the whole thing back. Now it scans in 4 MB chunks with two
concurrent workers (I/O overlaps Buffer.indexOf CPU) and patches only
the 34-byte digest slot(s) via handle.write at the found position(s).
Each chunk overreads sentinel.length-1 bytes so a sentinel straddling
a boundary is still detected. Peak memory drops from ~binary-size to
~8 MB. Same approach as electron/fuses#96, which benched 10-15x faster
on 150-300 MB binaries.

Adds two synthetic-binary tests: one plants the sentinel across a 4 MB
boundary (verified load-bearing via mutation), one plants a sentinel in
each of two chunks to cover the universal-binary multi-write path. Both
skip packager() and run in ~10ms.

* refactor: extract writeIntegrityDigest helper

setIntegrityDigest had grown to ~155 lines after the chunked-scan
change. Pull the scan/validate/write block out to a module-level
helper alongside isAsarIntegrity; the method now just gates on
version, resolves integrity, computes the hash, and calls through.
Sentinel is passed as a parameter rather than referenced via
MacApp.INTEGRITY_DIGEST_SENTINEL to avoid a no-use-before-define
disable comment.

* fix lint

* fix lint 2

* chore: debug logging and comments

* fix(mac): restore ad-hoc framework signature after writing integrity digest

Patching the integrity digest into the Electron Framework binary
invalidates the ad-hoc code signature that official Electron builds ship
with, so unsigned darwin/mas arm64 apps were killed at launch on Apple
Silicon ("code signature invalid"). Re-sign the framework with
codesign --force --sign - --deep after the patch, mirroring
@electron/fuses' resetAdHocDarwinSignature. The re-sign also runs when
osxSign is configured, since osx-sign failures can be reduced to a
warning via continueOnError and would otherwise ship a broken framework.

Universal builds skip the digest + re-sign for the intermediate x64 and
arm64 slices: bundle re-signing creates a per-arch
_CodeSignature/CodeResources file that @electron/universal rejects when
merging, and the slice frameworks are replaced by the lipo merge anyway.
The merged app gets its own digest + re-sign instead.

--deep is required because Electron's darwin-x64 dists ship unsigned
binaries, which otherwise fail nested code validation when sealing the
framework bundle. On non-darwin hosts, where codesign is unavailable, a
warning is emitted when no osxSign option is set.

* fix: skip asar integrity digest on non-darwin hosts

Patching the Electron Framework binary invalidates the ad-hoc signature
official Electron builds ship with, and only codesign (macOS-only) can
restore it. Apple Silicon refuses to launch binaries with an invalid
signature, so cross-packaged unsigned apps would be killed at startup
where previous Packager versions produced launchable apps.

Electron fails open when the digest slot is unwritten, so skipping the
patch off-macOS preserves existing behavior at no security cost — those
apps simply don't get digest enforcement, and a warning says so.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177rcB5TuvnmWgHiy5AYUH7

* test: read framework binary via Versions/A so digest test passes on Windows

The non-darwin integrity digest test read the Electron Framework binary
through the bundle root's 'Electron Framework' symlink. On Windows hosts
the extracted bundle's 'Versions/Current' link is a file-type symlink
that cannot be traversed as a directory, so resolving the root symlink
fails with ENOENT. Read the concrete binary at Versions/A instead, which
exists as a regular file on every host platform.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SWNEg8hKKnkdBJw5Phj6Dh

* fix: review feedback on integrity digest diagnostics and validation

- Check process.platform before any filesystem work in setIntegrityDigest,
  so non-darwin hosts get the accurate 'package on macOS' warning instead
  of a misleading 'binary not found' one (the framework path isn't even
  resolvable on Windows).
- Make sentinel slot validation all-or-nothing as the comment promises:
  throw on any slot without room for the payload instead of patching the
  remaining slices, which would leave one arch enforcing the digest while
  another fails open.
- Drop the dead 'renamedAppPath ?? electronAppPath' fallback (the getter
  is never nullish) and document the after-rename precondition instead.
- Deduplicate the expected-digest computation in tests into a
  computeExpectedDigest helper in test/utils.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmT8nnHCKN6H87SVD5kinq

* feat: add asarIntegrityDigest option to opt out of digest embedding

Embedding the digest ad-hoc re-signs the Electron Framework bundle, which
adds a per-arch _CodeSignature/CodeResources seal that Electron dists
don't ship. That breaks users who package x64 and arm64 separately and
merge them with @electron/universal themselves (makeUniversalApp requires
non-binary files to be identical across arches), and surprises pipelines
that modify app.asar after packaging.

asarIntegrityDigest (CLI: --no-asar-integrity-digest) defaults to true
and, when false, skips the digest patch and re-sign entirely, leaving the
framework byte-identical to what Electron ships. The unwritten slot fails
open at runtime: apps stay launchable and per-file asar integrity checks
still apply; only the plist tamper-protection layer is absent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmT8nnHCKN6H87SVD5kinq

* docs: restructure asarIntegrityDigest option docs

Lead with what the option does, list the exact conditions under which the
digest is embedded, and give concrete guidance on when to disable it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmT8nnHCKN6H87SVD5kinq

---------

Co-authored-by: Noah Gregory <noahmgregory@gmail.com>
Co-authored-by: Samuel Attard <sattard@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>

v20.2.1

Toggle v20.2.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: declare osxSign.continueOnError in PackagerOsxSignOptions (#1951)

createSignOpts has read osxSign.continueOnError since #1579 (and defaults
it to true), but PackagerOsxSignOptions is a straight Omit of osx-sign's
options so TypeScript users can only set it through a cast. Declare it,
mirroring PackagerWindowsSignOptions, and document the (different) default
on both.

No-Verification-Needed: type declarations and docs only

v20.2.0

Toggle v20.2.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: set mtimes to extraction time after extracting Electron zips (#1940

)

Electron's dist zips have every entry's timestamp zeroed to the DOS epoch
(1980-01-01). The Rust-based extractor used since v20.0.1 faithfully
restores archive mtimes, so packaged apps ended up with every file dated
1980. Reset timestamps to the extraction time after extracting, matching
the observable behavior of packager 18 and earlier, and preserve
timestamps in the EXDEV copy fallbacks so both staging paths behave like
rename.

Co-authored-by: Claude <noreply@anthropic.com>

v20.1.1

Toggle v20.1.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: throw a descriptive error when the out dir would exclude the app…

… from packaging (#1928)

* fix: throw a descriptive error when the out dir would exclude the app from packaging

Out directories are automatically excluded from the packaged app. When
the out dir is the app dir itself, or is a subdirectory of the app dir
that contains the app's main entry point, packaging failed with a
misleading "Application manifest was not found" or "The main entry
point to your app was not found" error that blamed the ignore option.

validateElectronApp now receives the resolved ignored out dirs and
throws a targeted error explaining that the out dir is excluded from
packaging and must live outside the app source.

Fixes #1685

* fix: canonicalize paths when detecting an out dir that excludes the app

Use realpath-canonicalized paths and a path.relative-based containment
check so the out-dir detection works across symlinked paths (e.g. /var
vs /private/var in macOS tmpdirs) and case-insensitive filesystems.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RX3wGLrrpx1fBfqXCZt1Tx

---------

Co-authored-by: Claude <noreply@anthropic.com>

v20.1.0

Toggle v20.1.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: support tmpdir: false when the out dir is inside the project dir (

…#1927)

Node's fs.cp rejects copying a directory into a subdirectory of itself
before the copy filter runs, so packaging with tmpdir: false and an out
dir inside the project dir (e.g. Electron Forge's default ./out) failed
with ERR_FS_CP_EINVAL. When the staging path is inside the app dir, copy
each top-level entry individually instead, still applying the same
filter so ignored paths (including the out dir itself) are skipped.

Fixes #1679

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: ianho <anho.zhang@gmail.com>

v20.0.4

Toggle v20.0.4's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: add Electron 44 version gates for win32/ia32 and linux/armv7l (#…

…1935)

* fix: add Electron 44 version gates for win32/ia32 and linux/armv7l

Electron removed win32/ia32 and linux/armv7l builds starting with
v44.0.0-alpha.4 (electron/electron#51816). Add upper bounds to the
buildVersions map so that:

- arch=all no longer expands to ia32/armv7l for Electron >= 44.0.0-alpha.4
- an explicit ia32/armv7l target produces the friendly "official
  support only exists" warning instead of a download 404

The bound is anchored at 44.0.0-alpha.4 (not 44.0.0) because
officialBuildExists uses semver.satisfies with includePrerelease: true,
so '<44.0.0' would still match the 44.0.0 prereleases that no longer
ship these builds. Electron <= 43 behavior is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEEZg64JKFrSDAc7u4V4Na

* docs: use <= instead of ≤ in README note

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEEZg64JKFrSDAc7u4V4Na

* docs: use GFM footnote syntax in README

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEEZg64JKFrSDAc7u4V4Na

---------

Co-authored-by: Claude <noreply@anthropic.com>

v20.0.3

Toggle v20.0.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: embed custom application manifests byte-for-byte instead of thei…

…r entire backing ArrayBuffer (#1934)

* test: verify custom application manifests are embedded byte-for-byte

Adds regression tests for #1933: builds a minimal fixture executable
in-memory with pe-library/resedit, runs the resedit() step with
win32metadata's application-manifest and requested-execution-level
options, and reads the RT_MANIFEST resource back out of the executable.

The manifest read is forced to be a Buffer view at a nonzero byteOffset
into a larger backing ArrayBuffer (the normal shape for pooled Buffers
in Node), so these tests fail on the current implementation, which
embeds the entire backing ArrayBuffer instead of the Buffer's actual
byte range.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016E9GNG9AcxUyvtEmRzovbj

* fix: slice manifest ArrayBuffers to the Buffer's actual byte range

Buffer.buffer returns the entire backing ArrayBuffer. Node Buffers are
frequently views into a larger shared ArrayBuffer (e.g. the internal
buffer pool), so embedding buffer.buffer as the RT_MANIFEST resource
can embed unrelated garbage bytes around the manifest, producing
executables that fail to launch with side-by-side configuration errors.

Slice the backing ArrayBuffer with byteOffset/length, mirroring the
existing ASAR integrity resource handling in the same file. Applies to
both the application-manifest and requested-execution-level branches.

Fixes #1933

Diagnosis and fix proposed by @cristiammercado in the issue report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016E9GNG9AcxUyvtEmRzovbj

* refactor: use TextEncoder for execution-level manifest bytes

* test: simplify readFile mock typing

---------

Co-authored-by: Claude <noreply@anthropic.com>

v20.0.2

Toggle v20.0.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
ci: pin macOS test runners to macos-15 (#1929)

GitHub is rolling out a new macos-26-arm64 image behind the
macos-latest label. On that image, `codesign --verify` rejects app
bundles signed by the test suite's self-signed certificate with
"code has no resources but signature indicates they must be present",
so the `packager > macOS > codesign > can sign the app` test fails
deterministically. The same commits pass on macos-15-arm64.

Pin the test matrix to macos-15 until @electron/osx-sign is compatible
with macOS 26's codesign behavior.

Co-authored-by: Claude <noreply@anthropic.com>

v20.0.1

Toggle v20.0.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: swap `extract-zip` with `@electron-internal/extract-zip` (#1917)

v20.0.0

Toggle v20.0.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat!: update @electron/get to v5 (#1907)

BREAKING CHANGE: Updated @electron/get to v5, download options have been migrated from "got" to built-in "fetch". `download` param is now a different type.