Compare commits

..

4 Commits

Author SHA1 Message Date
chen jing
d06db5f30c test: skip webFrameMain.copyVideoFrameAt on win32 CI due Chromium DCHECK 2026-03-19 14:02:07 +08:00
dodola
67eec6ceb3 Merge branch 'electron:main' into videoframe 2026-03-17 19:27:30 +08:00
dodola
ea6da976eb Merge branch 'electron:main' into videoframe 2026-02-24 10:57:39 +08:00
chenjing
b568504d8d feat: add copyVideoFrameAt and saveVideoFrameAs Method on Webcontent
chore: change the description of savevideoframe api

chore: add the description of the restrictive elements for using the APIs.

move to webframemain

fixed mediaPlayerAction to kSaveVideoFrameAs

Update spec/api-web-frame-main-spec.ts

Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>

Update spec/api-web-frame-main-spec.ts

Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>

fixed clipboard tests for video frame copying

fixed test for copying video frame to clipboard. check video loaded before copy video frame in test.

chore: try non-proprietary video format

Revert "chore: try non-proprietary video format"

This reverts commit ef085f88a1af53b6408a7af695cc60b8681398cf.

fix: format video as file url
2025-12-10 18:25:52 +08:00
869 changed files with 17293 additions and 30533 deletions

View File

@@ -11,7 +11,6 @@
"Bash(e patches:*)",
"Bash(e sync:*)",
"Skill(electron-chromium-upgrade)",
"Skill(electron-node-upgrade)",
"Read(*)",
"Bash(echo:*)",
"Bash(e build:*)",

View File

@@ -1,106 +0,0 @@
---
name: chrome-release-cls
description: Given a Chrome Releases blog post URL (chromereleases.googleblog.com), extract every CVE/bug and find the underlying Gerrit CL that fixed it by searching the local Chromium checkout and sub-repos. Use when asked to map Chrome security release notes to fixing CLs, or to find which commits correspond to CVEs in a Chrome stable update.
---
# Chrome Release → Fixing CL Mapper
Maps every security fix in a Chrome Releases blog post to the Gerrit CL(s) that fixed it.
## Input
`$ARGUMENTS` — a `https://chromereleases.googleblog.com/...` URL. If empty, ask the user for one.
## Procedure
### 1. Extract CVE → bug ID pairs from the blog post
The blog HTML buries bug IDs inside `<a>` tags, so strip tags first. Run:
```bash
curl -sL "$URL" | python3 -c '
import sys, re, html
t = re.sub(r"<[^>]+>", " ", sys.stdin.read())
t = re.sub(r"\s+", " ", html.unescape(t))
seen = set()
for m in re.finditer(r"\[\s*(\d{6,})\s*\]\s*(Critical|High|Medium|Low)\s*(CVE-\d{4}-\d+):\s*([^.]+?)\.", t):
if m.group(3) in seen: continue
seen.add(m.group(3))
print(f"{m.group(3)}|{m.group(1)}|{m.group(2)}|{m.group(4).strip()}")
' > /tmp/cve_bugs.txt
cat /tmp/cve_bugs.txt
```
If this yields nothing, the page may have changed format — fall back to `grep -oE 'CVE-[0-9]{4}-[0-9]+'` and `grep -oE 'crbug\.com/[0-9]+'` and pair them by order.
### 2. Find the fixing CL for each bug
Search git history in the Chromium checkout and relevant sub-repos for commits whose `Bug:` or `Fixed:` footer references the bug ID, then extract the `Reviewed-on:` Gerrit URL.
Repo selection by component keyword:
- ANGLE → `third_party/angle`
- Skia, Graphite → `third_party/skia`
- PDFium → `third_party/pdfium`
- Dawn → `third_party/dawn`
- V8, Turbofan, Maglev, Turboshaft → `v8`
- everything else → `.` (chromium/src)
Always also fall back to `.` if the hinted repo has no match.
```bash
cd /root/src/electron/src # chromium root (parent of electron/)
lookup() {
local bug="$1" repos="$2"
for repo in $repos . v8 third_party/skia third_party/angle third_party/pdfium third_party/dawn; do
local hits
hits=$(git -C "$repo" log --all --since='6 months ago' -E \
--grep="(Bug|Fixed):.*\\b${bug}\\b" --format='%H' 2>/dev/null | sort -u)
[[ -z "$hits" ]] && continue
while read -r h; do
git -C "$repo" log -1 --format='%B' "$h" | grep '^Reviewed-on:' | sed 's/^/ /'
echo "$(git -C "$repo" log -1 --format='%s' "$h")"
done <<<"$hits"
return 0
done
echo " (not found locally)"
}
```
Drive it from `/tmp/cve_bugs.txt`. Prefer the **non-`[M1xx]`-prefixed** commit subject as the canonical main CL; the `[M1xx]` ones are branch cherry-picks.
### 3. Handle misses
For any bug with no local hit:
- `git -C <repo> fetch origin` then re-search `--remotes` (fix may be newer than the checkout).
- Query Gerrit directly: `curl -s "https://chromium-review.googlesource.com/changes/?q=bug:${BUG}&n=10" | tail -n +2 | python3 -m json.tool` (also try `skia-review`, `pdfium-review`, `dawn-review`, `aomedia-review`).
- **`b/` bug format (Skia, Graphite, Dawn):** These repos reference bugs as `b/<id>` in commit messages rather than `Bug: <id>` footers. The Gerrit `bug:` query will return nothing. Use `message:<id>` search instead:
```bash
curl -s "https://skia-review.googlesource.com/changes/?q=message:${BUG}&n=5" | tail -n +2
```
Apply the same pattern for `dawn-review.googlesource.com` when the component is Dawn.
- **Tracing main CLs from merges:** When only `[M1xx]` merge CLs are found, query the CL detail for `cherry_pick_of_change` to find the original main CL number:
```bash
curl -s "https://chromium-review.googlesource.com/changes/${CL_NUM}?o=CURRENT_REVISION" | tail -n +2 | python3 -c "
import sys, json
d = json.load(sys.stdin)
print(d.get('cherry_pick_of_change', 'none'))
"
```
- If still nothing and the bug was reported very recently (especially by "Google Threat Intelligence" or marked in-the-wild), the CL is likely still access-restricted — report it as such rather than guessing.
### 4. Special cases
- **Roll CLs — skip and find the upstream fix:** For components whose fixes land in upstream repos (PDFium, Dawn, Skia, Graphite, libaom, libvpx, ffmpeg), the chromium-review hit will be a `Roll src/third_party/...` commit. Do not report the roll CL as the fix. Instead, query the component's own Gerrit instance directly for the actual fixing CL:
- PDFium → `pdfium-review.googlesource.com` (use `bug:` or `message:` query)
- Dawn → `dawn-review.googlesource.com` (use `message:` query — uses `b/` format)
- Skia / Graphite → `skia-review.googlesource.com` (use `message:` query — uses `b/` format)
- libaom → `aomedia-review.googlesource.com`
Only if the upstream Gerrit instance returns no results should you fall back to reporting the roll CL — in that case, include the roll CL and note that the actual fix is upstream but the specific CL could not be identified.
- Multiple `Reviewed-on:` lines in one commit body: cherry-picks keep the original line plus a new one. The **first** `Reviewed-on:` is the original CL.
- A bug may have multiple distinct fix CLs (fix + follow-up hardening) — list all of them.
### 5. Output
Produce a markdown table per severity level: `CVE | Bug | Component | Fix CL (main)`. Link bugs as `https://crbug.com/<id>`. Save raw output (including all branch merges) to `/tmp/cve_cls.txt` and mention the path.

View File

@@ -1,123 +0,0 @@
---
name: chrome-release-verify
description: End-to-end Chrome security backport for an Electron release branch. Given a Chrome Releases blog URL and a branch (e.g. 41-x-y), determines which CVE fixes are missing from the *actual synced source*, writes the cherry-pick patches locally, validates them with `e sync --3` + `lint --patches`, then pushes a single PR. Use when asked to backport a Chrome security release to N-x-y, "is CVE-X already in N-x-y?", or to produce/validate the cherry-pick set for a release branch.
---
# Chrome Release → Validated Backport PR
Input: `$ARGUMENTS` = `<release-branch> <chrome-releases-blog-url>` (e.g. `41-x-y https://chromereleases.googleblog.com/2026/04/stable-channel-update-for-desktop_15.html`). Ask if either is missing.
The flow is **local-first**: nothing is pushed until every patch applies via `e sync --3` and passes `lint --patches`.
## 1. Map CVE → bug → fix CL
Run `/chrome-release-cls <blog-url>` (or its inline procedure) to produce `/tmp/cve_bugs.txt` (`CVE|bug|severity|desc`) and a per-bug canonical fix CL. For each CL also note `repo` (path under `src/`: `.`, `v8`, `third_party/{skia,angle,pdfium,dawn}`, `third_party/libaom/source/libaom`) and `gerrit-host`.
**Prefer the target-milestone merge CL** if one exists (e.g. on `41-x-y` ≈ M146, prefer the `[M146]` cherry-pick over the main CL) — it's already rebased and far less likely to conflict. Find it via `git log --all --grep` on the Change-Id, or Gerrit `?q=bug:<n>`. If Chrome did *not* merge a fix to the target milestone, that's a strong signal the vulnerable code doesn't exist there — flag it for skip rather than forcing a port.
## 2. Prepare a synced worktree
Reuse `bp-<NN>` from `e show configs` if present, else `e worktree add bp-<NN> ~/src/electron-bp-<NN> --source <current> --no-sync`.
```bash
cd <root>/src/electron
git fetch origin <branch>
git checkout -B security-backport/<branch>/<short-date> origin/<branch>
e use bp-<NN>
e sync 2>&1 | tee /tmp/bp_sync.log
```
If sync fails with `NotADirectoryError: '<root>/src/.git/objects/info/alternates'`, remove `GIT_CACHE_PATH` from the bp config's `env` and retry.
## 3. Verify IN-TREE vs NEEDS-BACKPORT
For each bug, three checks against the **synced** repo:
1. `git -C "$repo" log HEAD --since='1 year ago' -E --grep="\b${bug}\b" --format='%h %s'`
2. Fetch Change-Id from Gerrit, then `git log HEAD --grep="^Change-Id: ${cid}$"`
3. `grep -rlE "(\b${bug}\b|${cid})" <root>/src/electron/patches/`
Any hit ⇒ IN-TREE. All empty ⇒ NEEDS-BACKPORT.
For each NEEDS-BACKPORT CL, also fetch its file list (`/changes/<proj>~<cl>/revisions/current/files`) and **skip** if every file is under `chrome/browser/`, `chrome/android/`, `ios/`, or `components/**/android/` — Electron doesn't compile those.
Report the table now (`CVE | Sev | Bug | Component | Verdict | CL`) and the proposed backport set; get user sign-off before continuing.
## 4. Write patches locally (no push yet)
For each backport CL, fetch the raw patch and write it into `patches/<dir>/`:
```bash
curl -s "https://${host}.googlesource.com/changes/${proj//\//%2F}~${cl}/revisions/current/patch" \
| base64 -d > "patches/${dir}/cherry-pick-${short}.patch"
echo "cherry-pick-${short}.patch" >> "patches/${dir}/.patches"
```
For repos with no Gerrit host `e cherry-pick` supports (e.g. **libaom** on aomedia), instead `git cherry-pick` the upstream commits onto the synced sub-repo HEAD and `git format-patch` the result.
For any newly-created `patches/<dir>/`, append to `patches/config.json` **preserving the compact one-line-per-entry style**:
```json
{ "patch_dir": "src/electron/patches/<dir>", "repo": "src/third_party/<dir-or-nested-path>" }
```
## 5. Validate with `e sync --3`
```bash
e sync --3 2>&1 | tee /tmp/bp_sync3.log
```
On `Patch failed at NNNN <subject>`:
- `cd` into the failing repo, inspect `git diff` for conflict markers.
- **Test-only files** (e.g. `web_tests/VirtualTestSuites`, `*_unittest.cc` context drift): take ours (`git checkout --ours -- <file>`) if the security-relevant hunks merged cleanly.
- **Substantive code conflicts**: check whether a target-milestone merge CL exists and swap to it. If none exists upstream and the surrounding code is structurally different, **drop the patch** (delete the file, remove from `.patches` and `config.json`) and note it for a separate manual-port PR — do not improvise security-fix semantics.
- After resolving: `git add <files> && git -c commit.gpgsign=false am --continue`, then `e patches <repo>` to export the resolved patch, then re-run `e sync --3`. Repeat until clean.
## 6. Export → lint → re-apply loop
```bash
e patches all
node script/lint.js --patches # must exit 0
```
If lint reports findings (typically trailing whitespace on `+` content lines), fixing them **changes the bytes the patch writes**, which invalidates the `index <old>..<new>` blob hashes that `e patches` baked in. Hand-editing a `.patch` and pushing it as-is will pass lint locally but fail CI's Apply Patches re-export check with a one-line `index` hash diff.
So whenever lint (or you) modifies any `.patch` file after export, round-trip once more:
```bash
# fix the lint findings in patches/**/*.patch, then:
e sync # re-apply the edited patches (no --3 needed; they applied cleanly last time)
e patches all # re-export so index blob hashes match the edited content
node script/lint.js --patches # must now exit 0
git diff --quiet -- patches/ || { echo "patches changed again — repeat the loop"; }
```
Repeat until `lint --patches` exits 0 **and** `git diff -- patches/` is empty after the final `e patches all`. Only then is the patch set CI-stable.
## 7. Commit, push, PR
```bash
git add patches/
git commit -m "chore: cherry-pick <N> changes from <dirs>"
git push origin HEAD
gh pr create --repo electron/electron --base <branch> --head <this-branch> \
--title "chore: cherry-pick <N> changes from <dirs>" \
--label "<branch>" --label backport-check-skip --label semver/patch --label "security 🔒" \
--body-file /tmp/pr_body.md
```
PR body format:
```markdown
Backports the following changes:
* [`<shortCommit>`](<gerrit-CL-url>) from <patchDir> — <subject> ([<bug>](https://crbug.com/<bug>), CVE-YYYY-NNNN)
* ...
Notes: Security: backported fixes for CVE-YYYY-NNNN, CVE-YYYY-NNNN, ....
```
Short commit links to the **Gerrit CL**; bug links to `crbug.com`; CVE comes from the blog mapping (the patch's own `Bug:` footer may differ); `Notes:` is the last line. Mention any dropped patches (with reason) above the `Notes:` line.
Restore `e use <previous>` when done.

View File

@@ -1,323 +0,0 @@
---
name: electron-node-upgrade
description: Guide for performing Node.js version upgrades in the Electron project. Use when working on the roller/node/main branch to fix patch conflicts during `e sync --3`. Covers the patch application workflow, conflict resolution, analyzing upstream Node.js changes, building, running the Node.js test suite, and proper commit formatting for patch fixes.
---
# Electron Node.js Upgrade: Phase One
## Summary
Run `e sync --3` repeatedly, fixing patch conflicts as they arise, until it succeeds. Then export patches and commit changes atomically.
## Success Criteria
Phase One is complete when:
- `e sync --3` exits with code 0 (no patch failures)
- All changes are committed per the commit guidelines
Do not stop until these criteria are met.
**CRITICAL** Do not delete or skip patches unless 100% certain the patch is no longer needed. For major version upgrades, patches that shim deprecated V8 APIs or backport upstream changes are often deletable because the new Node.js version already incorporates them — but verify before removing. Complicated conflicts or hard to resolve issues should be presented to the user after you have exhausted all other options. Do not delete the patch just because you can't solve it.
**CRITICAL** Never use `git am --skip` and then manually recreate a patch by making a new commit. This destroys the original patch's authorship, commit message, and position in the series. If `git am --continue` reports "No changes", investigate why — the changes were likely absorbed by a prior conflict resolution's 3-way merge. Present this situation to the user rather than skipping and recreating.
## Context
The `roller/node/main` branch is created by automation to update Electron's Node.js dependency version in `DEPS`. No work has been done to handle breaking changes between the old and new versions.
There are two types of Node.js version updates:
- **Bumps** (patch/minor): Automated by `electron-roller[bot]` with commit title `chore: bump node to v{version}`. Trivial patch index updates are handled automatically by `patchup[bot]`. These often land cleanly, but may require manual patch fixes.
- **Major upgrades** (e.g., v22 → v24): Manual, large PRs with commit title `chore: upgrade Node.js to v{X}.{Y}.{Z}`. These typically involve deleting obsolete patches, adapting many others, and updating `@types/node` in `package.json`.
**Key directories:**
- Current directory: Electron repo (always run `e` commands here)
- `../third_party/electron_node`: Node.js repo (where patches apply)
- `patches/node/`: Patch files for Node.js
- `docs/development/patches.md`: Patch system documentation
## Pre-flight Checks
Run these once at the start of each upgrade session:
1. **Clear rerere cache** (if enabled): `git rerere clear` in both the electron and `../third_party/electron_node` repos. Stale recorded resolutions from a prior attempt can silently apply wrong merges.
2. **Ensure pre-commit hooks are installed**: Check that `.git/hooks/pre-commit` exists. If not, run `yarn husky` to install it. The hook runs `lint-staged` which handles clang-format for C++ files.
## Workflow
1. Run `e sync --3` (the `--3` flag enables 3-way merge, always required)
2. If succeeds → skip to step 5
3. If patch fails:
- Identify target repo and patch from error output
- Analyze failure (see references/patch-analysis.md)
- Fix conflict in `../third_party/electron_node` working directory
- Run `git am --continue` in `../third_party/electron_node`
- Repeat until all patches for that repo apply
- IMPORTANT: Once `git am --continue` succeeds you MUST run `e patches node` to export fixes
- Return to step 1
4. When `e sync --3` succeeds, run `e patches all`
5. **Read `references/phase-one-commit-guidelines.md` NOW**, then commit changes following those instructions exactly.
## Commands Reference
| Command | Purpose |
|---------|---------|
| `e sync --3` | Clone deps and apply patches with 3-way merge |
| `git am --continue` | Continue after resolving conflict (run in node repo) |
| `e patches node` | Export commits from node repo to patch files |
| `e patches all` | Export all patches from all targets |
| `e patches node --commit-updates` | Export patches and auto-commit trivial changes |
| `e patches --list-targets` | List targets and config paths |
## Patch System Mental Model
```
patches/node/*.patch → [e sync --3] → ../third_party/electron_node commits
← [e patches] ←
```
## When to Edit Patches
| Situation | Action |
|-----------|--------|
| During active `git am` conflict | Fix in node repo, then `git am --continue` |
| Modifying patch outside conflict | Edit `.patch` file directly |
| Creating new patch (rare, avoid) | Commit in node repo, then `e patches node` |
Fix existing patches 99% of the time rather than creating new ones.
## Patch Fixing Rules
1. **Preserve authorship**: Keep original author in TODO comments (from patch `From:` field)
2. **Never change TODO assignees**: `TODO(name)` must retain original name
3. **Update descriptions**: If upstream changed APIs or macros, update patch commit message to reflect current state
4. **Never skip-and-recreate a patch**: If `git am --continue` says "No changes — did you forget to use 'git add'?", do NOT run `git am --skip` and create a replacement commit. The patch's changes were already absorbed by a prior 3-way merge resolution. This means an earlier conflict resolution pulled in too many changes. Present the situation to the user for guidance — the correct fix may require re-doing an earlier resolution more carefully to keep each patch's changes separate.
# Electron Node.js Upgrade: Phase Two
## Summary
Run `e build -k 999 -- --quiet` repeatedly, fixing build issues as they arise, until it succeeds. Then run `e start --version` to validate Electron launches and commit changes atomically.
Run Phase Two immediately after Phase One is complete.
## Success Criteria
Phase Two is complete when:
- `e build -k 999 -- --quiet` exits with code 0 (no build failures)
- `e start --version` has been run to check Electron launches
- All changes are committed per the commit guidelines
Do not stop until these criteria are met. Do not delete code or features, never comment out code in order to take short cut. Make all existing code, logic and intention work.
## Context
The `roller/node/main` branch is created by automation to update Electron's Node.js dependency version in `DEPS`. No work has been done to handle breaking changes between the old and new versions. Node.js APIs (especially internal V8 integration, OpenSSL/BoringSSL compatibility, and build system files) frequently change between versions. In every case the code in Electron must be updated to account for the change in Node.js, strongly avoid making changes to the code in Node.js to fix Electron's build.
**Key directories:**
- Current directory: Electron repo (always run `e` commands here)
- `../third_party/electron_node`: Node.js repo (do not touch this code to fix build issues, just read it to obtain context)
## Workflow
1. Run `e build -k 999 -- --quiet` (the `--quiet` flag suppresses per-target status lines, showing only errors and the final result)
2. If succeeds → skip to step 6
3. If build fails:
- Identify underlying file in "electron" from the compilation error message
- Analyze failure
- Fix build issue by adapting Electron's code for the change in Node.js
- Run `e build -t {target_that_failed}.o` to build just the failed target we were specifically fixing
- You can identify the target_that_failed from the failure line in the build log. E.g. `FAILED: 2e506007-8d5d-4f38-bdd1-b5cd77999a77 "./obj/electron/shell/browser/api/electron_api_utility_process.o" CXX obj/electron/shell/browser/api/electron_api_utility_process.o` the target name is `obj/electron/shell/browser/api/electron_api_utility_process.o`
- **Read `references/phase-two-commit-guidelines.md` NOW**, then commit changes following those instructions exactly.
- Return to step 1
4. **CRITICAL**: After ANY commit (especially patch commits), immediately run `git status` in the electron repo
- Look for other modified `.patch` files that only have index/hunk header changes
- These are dependent patches affected by your fix
- Commit them immediately with: `git commit -am "chore: update patches (trivial only)"`
5. Return to step 1
6. When `e build` succeeds, run `e start --version`
7. Check if you have any pending changes in the Node.js repo by running `git status` in `../third_party/electron_node`
- If you have changes follow the instructions below in "A. Patch Fixes" to correctly commit those modifications into the appropriate patch file
## Commands Reference
| Command | Purpose |
|---------|---------|
| `e build -k 999 -- --quiet` | Build Electron, continue on errors, suppress status lines |
| `e build -t {target}.o` | Build just one specific target to verify a fix |
| `e start --version` | Validate Electron launches after successful build |
## Two Types of Build Fixes
### A. Patch Fixes (for files in patched Node.js files)
When the error is in a file that Electron patches (check with `grep -l "filename" patches/node/*.patch`):
1. Edit the file in the Node.js source tree (`../third_party/electron_node/...`)
2. Create a fixup commit targeting the original patch commit:
```bash
cd ../third_party/electron_node
git add <modified-file>
git commit --fixup=<original-patch-commit-hash>
GIT_SEQUENCE_EDITOR=: git rebase --autosquash --autostash -i <commit>^
```
3. Export the updated patch: `e patches node`
4. Commit the updated patch file following `references/phase-one-commit-guidelines.md`.
To find the original patch commit to fixup: `git log --oneline | grep -i "keyword from patch name"`
The base commit for rebase is the Node.js commit before patches were applied. Find it by checking the `refs/patches/upstream-head` ref.
### B. Electron Code Fixes (for files in shell/, electron/, etc.)
When the error is in Electron's own source code:
1. Edit files directly in the electron repo
2. Commit directly (no patch export needed)
# Electron Node.js Upgrade: Phase Three
## Summary
Run the Node.js test suite via `script/node-spec-runner.js`, fix failing tests, and commit fixes until all tests pass. Certain tests are permanently disabled (listed in `script/node-disabled-tests.json`) and should not be run.
Run Phase Three immediately after Phase Two is complete.
## Success Criteria
Phase Three is complete when:
- `node script/node-spec-runner.js --default` exits with zero failures
- All changes are committed per the commit guidelines
Do not stop until these criteria are met.
## Context
Electron runs a subset of Node.js's upstream test suite using a custom runner (`script/node-spec-runner.js`). Tests are executed with the built Electron binary via `ELECTRON_RUN_AS_NODE=true`. Many tests need adaptation because Electron uses BoringSSL (not OpenSSL) and Chromium's V8 (which may differ from Node.js's bundled V8).
**Key files:**
- `script/node-spec-runner.js` — Test runner script
- `script/node-disabled-tests.json` — Permanently disabled tests (do not try to fix these)
- `../third_party/electron_node/test/` — Node.js test files (where patches apply)
- `patches/node/fix_crypto_tests_to_run_with_bssl.patch` — BoringSSL crypto test adaptations
- `patches/node/test_formally_mark_some_tests_as_flaky.patch` — Flaky test list
## Workflow
1. Run `node script/node-spec-runner.js --default` from the electron repo
2. If all tests pass → Phase Three is complete
3. If tests fail:
- Identify the failing test file(s) from the output
- Analyze each failure (see "Common Failure Patterns" below)
- Fix the test in `../third_party/electron_node/test/...`
- Re-run the specific failing test to verify: `node script/node-spec-runner.js {test-path}`
- The test path is relative to the node `test/` directory, e.g. `test/parallel/test-crypto-key-objects-raw.js`
- Do NOT use `--default` when running specific tests — it adds the full suite flags
- Do NOT run tests directly with `ELECTRON_RUN_AS_NODE` — the runner handles environment setup (e.g. temporarily switching `package.json` from ESM to CommonJS)
- Commit the fix using the fixup workflow and commit guidelines
- Return to step 1
## Commands Reference
| Command | Purpose |
|---------|---------|
| `node script/node-spec-runner.js --default` | Run full Node.js test suite |
| `node script/node-spec-runner.js test/parallel/test-foo.js` | Run a single test |
| `NODE_REGENERATE_SNAPSHOTS=1 node script/node-spec-runner.js test/test-runner/test-foo.mjs` | Regenerate snapshot for a snapshot-based test |
## Common Failure Patterns
### BoringSSL incompatibilities
Electron uses BoringSSL (via Chromium) instead of OpenSSL. Many crypto features are missing or behave differently:
| Unsupported in BoringSSL | Guard pattern |
|--------------------------|---------------|
| ChaCha20-Poly1305 | `if (!process.features.openssl_is_boringssl)` |
| AES-CCM (aes-128-ccm, aes-256-ccm) | `if (ciphers.includes('aes-128-ccm'))` |
| AES-KW (key wrapping) | `if (!process.features.openssl_is_boringssl)` |
| DSA keys | `if (!process.features.openssl_is_boringssl)` |
| Ed448 / X448 curves | `if (!process.features.openssl_is_boringssl)` |
| DH key PEM loading | `if (!process.features.openssl_is_boringssl)` |
| PQC algorithms (ML-KEM, ML-DSA, SLH-DSA) | `if (hasOpenSSL(3, 5))` (already guards these) |
When guarding tests, prefer checking cipher availability (`ciphers.includes(algo)`) over blanket BoringSSL checks where possible, as it's more precise and self-documenting.
New upstream tests that exercise these features will need guards added to the `fix_crypto_tests_to_run_with_bssl` patch.
### Snapshot test mismatches
Some tests compare output against committed `.snapshot` files using `assert.strictEqual` — these are NOT wildcard comparisons. When Chromium's V8 produces different output (e.g. different stack traces due to V8 enhancements), the snapshot must be regenerated:
```bash
NODE_REGENERATE_SNAPSHOTS=1 node script/node-spec-runner.js test/test-runner/test-foo.mjs
```
Then inspect the diff to verify the changes are expected, and commit the updated snapshot into the appropriate patch.
### V8 behavioral differences
Chromium's V8 may be ahead of Node.js's bundled V8. This can cause:
- Different stack trace formats (e.g. thenable async stack frames)
- Different error messages
- Features available in Chromium V8 that aren't in stock Node.js V8 (or vice versa)
## Two Types of Test Fixes
### A. Patch Fixes (most common for test failures)
Most test fixes go into existing patches in `patches/node/`. Use the fixup workflow:
1. Edit the test file in `../third_party/electron_node/test/...`
2. Find the relevant patch commit: `git log --oneline | grep -i "keyword"`
- Crypto/BoringSSL tests → `fix crypto tests to run with bssl`
- Snapshot tests → the specific snapshot patch (e.g. `test: accomodate V8 thenable`)
- Flaky tests → `test: formally mark some tests as flaky`
3. Create a fixup commit:
```bash
cd ../third_party/electron_node
git add test/path/to/test.js
git commit --fixup=<patch-commit-hash>
GIT_SEQUENCE_EDITOR=: git rebase --autosquash --autostash -i <commit>^
```
4. Export: `e patches node`
5. **Read `references/phase-three-commit-guidelines.md` NOW**, then commit the updated patch file.
### B. New Patches (rare)
Only create a new patch when the fix doesn't belong in any existing patch. The new patch commit in `../third_party/electron_node` must include a description explaining why the patch exists and when it can be removed — the lint check enforces this.
## Adding to Disabled Tests
Only add a test to `script/node-disabled-tests.json` as a **last resort** — when the test is fundamentally incompatible with Electron's architecture (not just a BoringSSL difference that can be guarded). Tests disabled here are completely skipped and never run.
# Critical: Read Before Committing
- Before ANY Phase One commits: Read `references/phase-one-commit-guidelines.md`
- Before ANY Phase Two commits: Read `references/phase-two-commit-guidelines.md`
- Before ANY Phase Three commits: Read `references/phase-three-commit-guidelines.md`
# High-Churn Patches
These patches consistently require the most work during Node.js upgrades:
- **`fix_handle_boringssl_and_openssl_incompatibilities.patch`** — Electron uses BoringSSL (via Chromium) while Node.js expects OpenSSL. This patch is large and complex, and upstream OpenSSL API changes frequently break it.
- **`fix_crypto_tests_to_run_with_bssl.patch`** — Companion to the above; adapts Node.js crypto tests for BoringSSL. Can grow significantly during major upgrades.
- **`support_v8_sandboxed_pointers.patch`** — V8 sandbox pointer support requires careful adaptation when V8 APIs change.
- **`build_add_gn_build_files.patch`** — The GN build file patch is large and touches many build targets. Upstream build system changes frequently conflict.
# Major Version Upgrades
Major Node.js version transitions (e.g., v22 → v24) are significantly more involved than patch bumps:
1. **Expect patch deletions.** Electron uses Chromium's V8, which is often ahead of the V8 version bundled in Node.js. Many patches exist to bridge this gap — shimming newer V8 APIs that Chromium's V8 has but Node.js' older V8 doesn't. When Node.js bumps to a newer major version, its V8 catches up to Chromium's, and those bridge patches can be deleted. In the v22 → v24 upgrade, 17 patches were deleted for this reason.
2. **Update `@types/node`** in `package.json` to match the new major version.
3. **Post-upgrade regressions are expected.** Even after the upgrade lands, follow-up fix PRs for edge cases (ESM path handling, certificate loading, platform-specific issues) are normal.
# Skill Directory Structure
This skill has additional reference files in `references/`:
- patch-analysis.md - How to analyze patch failures
- phase-one-commit-guidelines.md - Commit format for Phase One
- phase-two-commit-guidelines.md - Commit format for Phase Two
- phase-three-commit-guidelines.md - Commit format for Phase Three
Read these when referenced in the workflow steps.

View File

@@ -1,112 +0,0 @@
# Analyzing Patch Failures
## Investigation Steps
1. **Read the patch file** at `patches/node/{patch_name}.patch`
2. **Examine current state** of the file in the Node.js repo at mentioned line numbers
3. **Check recent upstream changes:**
```bash
cd ../third_party/electron_node
git log --oneline -10 -- {file}
```
4. **Find Node.js PR** in commit messages:
```
PR-URL: https://github.com/nodejs/node/pull/{PR_NUMBER}
```
## Critical: Resolve by Intent, Not by Mechanical Merge
When resolving a patch conflict, do NOT blindly preserve the patch's old code. Instead:
1. **Understand the upstream commit's full scope** — not just the conflicting hunk.
Run `git show <commit> --stat` and read diffs for all affected files.
Upstream may have removed structs, members, or methods that the patch
references in other hunks or files.
2. **Re-read the patch commit message** to understand its *intent* — what
behavior does it need to preserve or add?
3. **Implement the intent against the new upstream code.** If the patch's
purpose is "add BoringSSL compatibility", add only the compatibility
layer — don't also restore old code that upstream separately removed.
### Lesson: Upstream Removals Break Patch References
- **Trigger:** Patch conflict involves an upstream refactor (not just context drift)
- **Strategy:** After identifying the upstream commit, check its full diff for
removed types, members, and methods. If the patch's old code references
something removed, the resolution must use the new upstream mechanism.
### Lesson: Separate Patch Purpose from Patch Implementation
- **Trigger:** Conflict between "upstream simplified code" vs "patch has older code"
- **Strategy:** Identify the *minimal* change the patch needs. If the patch
wraps code in a conditional, only add the conditional — don't restore old
code that was inside the conditional but was separately cleaned up upstream.
### Lesson: Finish the Adaptation at Conflict Time
- **Trigger:** A patch conflict involves an upstream API removal or replacement
- **Strategy:** When resolving the conflict, fully adapt the patch to use the
new API in the same commit. Don't remove the old code and leave behind stale
references that will "be fixed in Phase Two." Each patch fix commit should be
a complete resolution.
## Common Failure Patterns
| Pattern | Cause | Solution |
|---------|-------|----------|
| Context lines don't match | Surrounding code changed | Update context in patch |
| File not found | File renamed/moved | Update patch target path |
| Function not found | Refactored upstream | Find new function name |
| OpenSSL → BoringSSL mismatch | Crypto API change | Update to BoringSSL-compatible API |
| GYP/GN build change | Build system refactor | Adapt build patch to new structure |
| Deleted code | Feature removed | Verify patch still needed |
| V8 API bridge patch conflicts | Node.js caught up to Chromium's V8 | Patch may be deletable — verify the API is now in Node.js' V8 natively |
## Using Git Blame
To find the commit that changed specific lines:
```bash
cd ../third_party/electron_node
git blame -L {start},{end} -- {file}
git log -1 {commit_sha} # Look for PR-URL: line
```
## Verifying Patch Necessity
Before deleting a patch, verify:
1. The patched functionality was intentionally removed upstream
2. Electron doesn't need the patch for other reasons
3. No other code depends on the patched behavior
**V8 bridge patches:** Electron uses Chromium's V8, which is often ahead of the V8 bundled in Node.js. Many patches exist to bridge this version gap — adapting Node.js code to work with newer V8 APIs that Chromium's V8 exposes. During major Node.js upgrades, Node.js' V8 catches up to Chromium's, and these bridge patches often become unnecessary. Check whether the API the patch shims is now available natively in the new Node.js version's V8.
When in doubt, keep the patch and adapt it.
## Phase Two: Build-Time Patch Issues
Sometimes patches that applied successfully in Phase One cause build errors in Phase Two. This can happen when:
1. **Incomplete types**: A patch disables a header include, but new upstream code uses the type
2. **Missing members**: A patch modifies a class, but upstream added new code referencing the original
### Finding Which Patch Affects a File
```bash
grep -l "filename.cc" patches/node/*.patch
```
### Matching Existing Patch Patterns
When fixing build errors in patched files, examine the existing patch to understand its style:
- Does it use `#if 0` / `#endif` guards?
- Does it use `#if BUILDFLAG(...)` conditionals?
- Does it use `#ifndef` / `#ifdef` guards for BoringSSL vs OpenSSL?
- What's the pattern for disabled functionality?
Apply fixes consistent with the existing patch style.

View File

@@ -1,111 +0,0 @@
# Phase One Commit Guidelines
Only follow these instructions if there are uncommitted changes to `patches/` after Phase One succeeds.
Ignore other instructions about making commit messages, our guidelines are CRITICALLY IMPORTANT and must be followed.
## Each Commit Must Be Complete
When resolving a patch conflict, fully adapt the patch to the new upstream code in the same commit. If the upstream change removes an API the patch uses, update the patch to use the replacement API now — don't leave stale references knowing they'll need fixing later. The goal is that each commit represents a finished resolution, not a partial one that defers known work to a future phase.
## Commit Message Style
**Titles** follow the 60/80-character guideline: simple changes fit within 60 characters, otherwise the limit is 80 characters.
Always include a `Co-Authored-By` trailer identifying the AI model that assisted (e.g., `Co-Authored-By: <AI model attribution>`).
### Patch conflict fixes
Use `fix(patch):` prefix. The title should name the upstream change, not your response to it:
```
fix(patch): {topic headline}
Ref: {Node.js commit or issue link}
Co-Authored-By: <AI model attribution>
```
Only add a description body if it provides clarity beyond the title. For straightforward context drift or simple API renames, the title + Ref is sufficient.
Examples:
- `fix(patch): stop using v8::PropertyCallbackInfo<T>::This()`
- `fix(patch): BoringSSL and OpenSSL incompatibilities`
- `fix(patch): refactor module_wrap.cc FixedArray::Get params`
### Upstreamed patch removal
When patches are no longer needed (applied cleanly with "already applied" or confirmed upstreamed), group ALL removals into a single commit:
```
chore: remove upstreamed patch
```
or (if multiple):
```
chore: remove upstreamed patches
```
Most Node.js patches in Electron are Electron-authored (no upstream `PR-URL:`). If the patch originated from an upstream Node.js PR, no extra `Ref:` is needed. Otherwise, add a `Ref:` pointing to the relevant Node.js issue or commit if one exists.
### Trivial patch updates
After all fix commits, stage remaining trivial changes (index, line numbers, context only):
```bash
git add patches
git commit -m "chore: update patches (trivial only)"
```
**Conflict resolution can produce trivial results.** A `git am` conflict doesn't always mean the patch content changed — context drift alone can cause a conflict. After resolving and exporting, inspect the patch diff: if only index hashes, line numbers, and context lines changed (not the patch's own `+`/`-` lines), it's trivial and belongs here, not in a `fix(patch):` commit.
## Atomic Commits
Each patch conflict fix gets its own commit with its own Ref.
IMPORTANT: Try really hard to find the PR or commit reference per the instructions below. Each change you made should in theory have been in response to a change made in Node.js that you identified or can identify. Try for a while to identify and include the ref in the commit message. Do not give up easily.
## Finding Commit/Issue References
Use `git log` or `git blame` on Node.js source files in `../third_party/electron_node`. Look for:
```
PR-URL: https://github.com/nodejs/node/pull/XXXXX
```
or issue references in the patch itself:
```
Refs: https://github.com/nodejs/node/issues/XXXXX
```
Note: Most Node.js patches in Electron are Electron-authored and won't have upstream references. In that case, check `git log` in the Node.js repo to find which upstream commit caused the conflict.
If no reference found after searching: `Ref: Unable to locate reference`
## Example Commits
### Patch conflict fix (simple — title is sufficient)
```
fix(patch): stop using v8::PropertyCallbackInfo<T>::This()
Ref: https://github.com/nodejs/node/issues/60616
Co-Authored-By: <AI model attribution>
```
### Patch conflict fix (complex — description adds value)
```
fix(patch): BoringSSL and OpenSSL incompatibilities
Upstream updated OpenSSL APIs that diverge from BoringSSL. Adapted
the compatibility shims in crypto patches to use the BoringSSL
equivalents.
Ref: Unable to locate reference
Co-Authored-By: <AI model attribution>
```

View File

@@ -1,80 +0,0 @@
# Phase Three Commit Guidelines
Only follow these instructions if there are uncommitted changes after fixing a test failure during Phase Three.
Ignore other instructions about making commit messages, our guidelines are CRITICALLY IMPORTANT and must be followed.
## Commit Message Style
**Titles** follow the 60/80-character guideline: simple changes fit within 60 characters, otherwise the limit is 80 characters.
Always include a `Co-Authored-By` trailer identifying the AI model that assisted (e.g., `Co-Authored-By: <AI model attribution>`).
## Commit Types
### Patch updates (most test fixes)
Test fixes go into existing patches via the fixup workflow. Use `fix(patch):` prefix with a descriptive topic:
```
fix(patch): {topic headline}
Ref: {Node.js commit or issue link}
Co-Authored-By: <AI model attribution>
```
Examples:
- `fix(patch): guard DH key test for BoringSSL`
- `fix(patch): adapt new crypto tests for BoringSSL`
- `fix(patch): correct thenable snapshot for Chromium V8`
- `fix(patch): skip AES-KW tests with BoringSSL`
Group related test fixes into a single commit when they address the same root cause (e.g., multiple crypto tests all needing BoringSSL guards for the same missing cipher). Don't create one commit per test file if they share the same fix pattern.
### Snapshot regeneration
When a snapshot test fails because Chromium's V8 produces different output, regenerate it:
```bash
NODE_REGENERATE_SNAPSHOTS=1 node script/node-spec-runner.js test/test-runner/test-foo.mjs
```
Then commit the updated snapshot patch with a title describing what changed:
```
fix(patch): correct {name} snapshot for Chromium V8
Ref: {V8 CL or issue link if known}
Co-Authored-By: <AI model attribution>
```
### Trivial patch updates
After any patch modification, check for dependent patches that only have index/hunk header changes:
```bash
git status
# If other .patch files show as modified with only trivial changes:
git add patches/
git commit -m "chore: update patches (trivial only)"
```
## Finding References
For BoringSSL-related test fixes, the reference is typically the upstream Node.js PR that added the new test:
```bash
cd ../third_party/electron_node
git log --oneline -5 -- test/parallel/test-crypto-foo.js
git log -1 <commit> --format="%B" | grep "PR-URL"
```
For V8 behavioral differences, reference the Chromium CL:
```
Ref: https://chromium-review.googlesource.com/c/v8/v8/+/NNNNNNN
```
If no reference found after searching: `Ref: Unable to locate reference`

View File

@@ -1,96 +0,0 @@
# Phase Two Commit Guidelines
Only follow these instructions if there are uncommitted changes in the Electron repo after any fixes are made during Phase Two that result a target that was failing, successfully building.
Ignore other instructions about making commit messages, our guidelines are CRITICALLY IMPORTANT and must be followed.
## Commit Message Style
**Titles** follow the 60/80-character guideline: simple changes fit within 60 characters, otherwise the limit is 80 characters. Exception: upstream Node.js PR titles are used verbatim even if longer.
Always include a `Co-Authored-By` trailer identifying the AI model that assisted (e.g., `Co-Authored-By: <AI model attribution>`).
## Two Commit Types
### For Electron Source Changes (shell/, electron/, etc.)
When the upstream Node.js commit has a `PR-URL:`:
```
node#{PR-Number}: {upstream PR's original title}
Ref: {Node.js PR link}
Co-Authored-By: <AI model attribution>
```
When there is no `PR-URL:` but there is an issue reference or commit:
```
fix: {description of the adaptation}
Ref: {Node.js issue or commit link}
Co-Authored-By: <AI model attribution>
```
Use the **upstream commit's original title** when available — do not paraphrase or rewrite it. To find it: check the commit message in `../third_party/electron_node` for `PR-URL:` or `Refs:` lines.
Only add a description body if it provides clarity beyond what the title already says (e.g., when Electron's adaptation is non-obvious). For simple renames, method additions, or straightforward API updates, the title + Ref link is sufficient.
Each change should have its own commit and its own Ref. Logically group into commits that make sense rather than one giant commit. You may include multiple "Ref" links if required.
IMPORTANT: Try really hard to find a reference. Each change you made should in theory have been in response to a change in Node.js. Check `git log` and `git blame` in the Node.js repo. Do not give up easily.
### For Patch Updates (patches/node/*.patch)
Use the same fixup workflow as Phase One and follow `references/phase-one-commit-guidelines.md` for the commit message format (`fix(patch):` prefix, topic style).
## Dependent Patch Header Updates
After any patch modification, check for other affected patches:
```bash
git status
# If other .patch files show as modified with only index, line number, and context changes:
git add patches/
git commit -m "chore: update patches (trivial only)"
```
## Finding References
Use `git log` or `git blame` on Node.js source files in `../third_party/electron_node`. Look for:
```
PR-URL: https://github.com/nodejs/node/pull/XXXXX
Refs: https://github.com/nodejs/node/issues/XXXXX
```
Note: Many Node.js patches in Electron are Electron-authored and won't have upstream `PR-URL:` lines. Check the patch's own commit message for `Refs:` lines, or use `git log` in the Node.js repo to find which upstream commit caused the build break.
If no reference found after searching: `Ref: Unable to locate reference`
## Example Commits
### Electron Source Fix (with upstream PR)
```
node#61898: src: stop using v8::PropertyCallbackInfo<T>::This()
Ref: https://github.com/nodejs/node/pull/61898
Co-Authored-By: <AI model attribution>
```
### Electron Source Fix (with issue reference, no PR)
```
fix: adapt to v8::PropertyCallbackInfo<T>::This() removal
Updated NodeBindings to use HolderV2() after upstream Node.js
stopped using the deprecated This() API.
Ref: https://github.com/nodejs/node/issues/60616
Co-Authored-By: <AI model attribution>
```

View File

@@ -35,7 +35,7 @@
"ms-vscode.cpptools",
"mutantdino.resourcemonitor",
"dsanders11.vscode-electron-build-tools",
"oxc.oxc-vscode",
"dbaeumer.vscode-eslint",
"shakram02.bash-beautify",
"marshallofsound.gnls-electron"
],

79
.eslintrc.json Normal file
View File

@@ -0,0 +1,79 @@
{
"root": true,
"extends": "standard",
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"env": {
"browser": true
},
"rules": {
"semi": ["error", "always"],
"no-var": "error",
"no-unused-vars": "off",
"guard-for-in": "error",
"@typescript-eslint/no-unused-vars": ["error", {
"vars": "all",
"args": "after-used",
"ignoreRestSiblings": true
}],
"prefer-const": ["error", {
"destructuring": "all"
}],
"n/no-callback-literal": "off",
"import/newline-after-import": "error",
"import/order": ["error", {
"alphabetize": {
"order": "asc"
},
"newlines-between": "always",
"pathGroups": [
{
"pattern": "@electron/internal/**",
"group": "external",
"position": "before"
},
{
"pattern": "@electron/**",
"group": "external",
"position": "before"
},
{
"pattern": "{electron,electron/**}",
"group": "external",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": [],
"distinctGroup": true,
"groups": [
"external",
"builtin",
["sibling", "parent"],
"index",
"type"
]
}]
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"overrides": [
{
"files": "*.ts",
"rules": {
"no-undef": "off",
"no-redeclare": "off",
"@typescript-eslint/no-redeclare": ["error"],
"no-use-before-define": "off"
}
},
{
"files": "*.d.ts",
"rules": {
"no-useless-constructor": "off",
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}

1
.github/CODEOWNERS vendored
View File

@@ -19,7 +19,6 @@ DEPS @electron/wg-upgrades
/lib/renderer/security-warnings.ts @electron/wg-security
# Infra WG
/.claude/ @electron/wg-infra
/.github/actions/ @electron/wg-infra
/.github/workflows/*-publish.yml @electron/wg-infra
/.github/workflows/build.yml @electron/wg-infra

View File

@@ -0,0 +1,14 @@
name: Maintainer Issue (not for public use)
description: Only to be created by Electron maintainers
body:
- type: checkboxes
attributes:
label: Confirmation
options:
- label: I am a [maintainer](https://github.com/orgs/electron/people) of the Electron project. (If not, please create a [different issue type](https://github.com/electron/electron/issues/new/).)
required: true
- type: textarea
attributes:
label: Description
validations:
required: true

View File

@@ -5,18 +5,13 @@ Thank you for your Pull Request. Please provide a description above and review
the requirements below.
Contributors guide: https://github.com/electron/electron/blob/main/CONTRIBUTING.md
Using a coding agent / AI? Read the policy: https://github.com/electron/governance/blob/main/policy/ai.md
NOTE: PRs submitted that do not follow this template will be automatically closed.
-->
#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->
- [ ] I have built and tested this change
- [ ] I have filled out the PR description
- [ ] [I have reviewed and verified the changes](https://github.com/electron/governance/blob/main/policy/ai.md)
- [ ] PR description included
- [ ] I have built and tested this PR
- [ ] `npm test` passes
- [ ] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md)
- [ ] relevant API documentation, tutorials, and examples are updated and follow the [documentation style guide](https://github.com/electron/electron/blob/main/docs/development/style-guide.md)

View File

@@ -47,16 +47,6 @@ runs:
- name: Add Clang problem matcher
shell: bash
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
- name: Download previous object checksums
shell: bash
if: ${{ (github.event_name == 'push' || github.event_name == 'pull_request') && inputs.is-asan != 'true' }}
env:
GITHUB_TOKEN: ${{ github.token }}
ARTIFACT_NAME: object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json
SEARCH_BRANCH: ${{ case(github.event_name == 'push', github.ref_name, github.event.pull_request.base.ref) }}
REPO: ${{ github.repository }}
OUTPUT_PATH: src/previous-object-checksums.json
run: node src/electron/.github/actions/build-electron/download-previous-object-checksums.mjs
- name: Build Electron ${{ inputs.step-suffix }}
if: ${{ inputs.target-platform != 'win' }}
shell: bash
@@ -82,17 +72,12 @@ runs:
cp out/Default/.ninja_log out/electron_ninja_log
node electron/script/check-symlinks.js
# Build stats and object checksums
BUILD_STATS_ARGS="out/Default/siso.INFO --out-dir out/Default --output-object-checksums object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json"
if [ -f previous-object-checksums.json ]; then
BUILD_STATS_ARGS="$BUILD_STATS_ARGS --input-object-checksums previous-object-checksums.json"
fi
if ! [ -z "$DD_API_KEY" ]; then
BUILD_STATS_ARGS="$BUILD_STATS_ARGS --upload-stats"
# Upload build stats to Datadog
if ! [ -z $DD_API_KEY ]; then
npx node electron/script/build-stats.mjs out/Default/siso.INFO --upload-stats || true
else
echo "Skipping build-stats.mjs upload because DD_API_KEY is not set"
fi
node electron/script/build-stats.mjs $BUILD_STATS_ARGS || true
- name: Build Electron (Windows) ${{ inputs.step-suffix }}
if: ${{ inputs.target-platform == 'win' }}
shell: powershell
@@ -107,28 +92,19 @@ runs:
} else {
e build --target electron:testing_build
}
if ($LASTEXITCODE -ne 0) {
Write-Host "e build failed with exit code $LASTEXITCODE"
exit $LASTEXITCODE
}
Copy-Item out\Default\.ninja_log out\electron_ninja_log
node electron\script\check-symlinks.js
# Build stats and object checksums
$statsArgs = @("out\Default\siso.exe.INFO", "--out-dir", "out\Default", "--output-object-checksums", "object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json")
if (Test-Path previous-object-checksums.json) {
$statsArgs += @("--input-object-checksums", "previous-object-checksums.json")
}
# Upload build stats to Datadog
if ($env:DD_API_KEY) {
$statsArgs += "--upload-stats"
try {
npx node electron\script\build-stats.mjs out\Default\siso.exe.INFO --upload-stats ; $LASTEXITCODE = 0
} catch {
Write-Host "Build stats upload failed, continuing..."
}
} else {
Write-Host "Skipping build-stats.mjs upload because DD_API_KEY is not set"
}
try {
& node electron\script\build-stats.mjs @statsArgs ; $LASTEXITCODE = 0
} catch {
Write-Host "Build stats failed, continuing..."
}
- name: Verify dist.zip ${{ inputs.step-suffix }}
shell: bash
run: |
@@ -152,9 +128,6 @@ runs:
fi
sed $SEDOPTION '/.*builtins-pgo/d' out/Default/mksnapshot_args
sed $SEDOPTION '/--turbo-profiling-input/d' out/Default/mksnapshot_args
sed $SEDOPTION '/--reorder-builtins/d' out/Default/mksnapshot_args
sed $SEDOPTION '/--warn-about-builtin-profile-data/d' out/Default/mksnapshot_args
sed $SEDOPTION '/--abort-on-bad-builtin-profile-data/d' out/Default/mksnapshot_args
if [ "${{ inputs.target-platform }}" = "win" ]; then
cd out/Default
@@ -232,17 +205,7 @@ runs:
if: ${{ inputs.is-release == 'true' }}
run: |
cd src
# Reuse the hermetic mac_sdk_path that `e build` wrote for out/Default so
# out/ffmpeg builds against the same SDK instead of the runner's system Xcode.
# The path has to live under root_build_dir, so copy the symlink tree and
# rewrite Default -> ffmpeg.
MAC_SDK_ARG=""
if [ "$(uname)" = "Darwin" ]; then
mkdir -p out/ffmpeg
cp -a out/Default/xcode_links out/ffmpeg/
MAC_SDK_ARG=$(sed -n 's|^\(mac_sdk_path = "//out/\)Default/|\1ffmpeg/|p' out/Default/args.gn)
fi
gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true use_siso=true $MAC_SDK_ARG $GN_EXTRA_ARGS"
gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true use_siso=true $GN_EXTRA_ARGS"
e build --target electron:electron_ffmpeg_zip -C ../../out/ffmpeg
- name: Remove Clang problem matcher
shell: bash
@@ -311,25 +274,18 @@ runs:
run: ./src/electron/script/actions/move-artifacts.sh
- name: Upload Generated Artifacts ${{ inputs.step-suffix }}
if: always() && !cancelled()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f #v7.0.0
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
with:
name: generated_artifacts_${{ env.ARTIFACT_KEY }}
path: ./generated_artifacts_${{ inputs.artifact-platform }}_${{ inputs.target-arch }}
- name: Upload Src Artifacts ${{ inputs.step-suffix }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f #v7.0.0
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
with:
name: src_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src_artifacts_${{ inputs.artifact-platform }}_${{ inputs.target-arch }}
- name: Upload Out Gen Artifacts ${{ inputs.step-suffix }}
if: ${{ inputs.upload-out-gen-artifacts == 'true' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f #v7.0.0
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
with:
name: out_gen_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src/out/Default/gen
- name: Upload Object Checksums ${{ inputs.step-suffix }}
if: ${{ always() && !cancelled() && inputs.is-asan != 'true' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: object_checksums_${{ inputs.artifact-platform }}_${{ inputs.target-arch }}
path: ./src/object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json
archive: false

View File

@@ -1,82 +0,0 @@
import { Octokit } from '@octokit/rest';
import { writeFileSync } from 'node:fs';
const token = process.env.GITHUB_TOKEN;
const repo = process.env.REPO;
const artifactName = process.env.ARTIFACT_NAME;
const branch = process.env.SEARCH_BRANCH;
const outputPath = process.env.OUTPUT_PATH;
const required = { GITHUB_TOKEN: token, REPO: repo, ARTIFACT_NAME: artifactName, SEARCH_BRANCH: branch, OUTPUT_PATH: outputPath };
const missing = Object.entries(required).filter(([, v]) => !v).map(([k]) => k);
if (missing.length > 0) {
console.error(`Missing required environment variables: ${missing.join(', ')}`);
process.exit(1);
}
const [owner, repoName] = repo.split('/');
const octokit = new Octokit({ auth: token });
async function main () {
console.log(`Searching for artifact '${artifactName}' on branch '${branch}'...`);
// Resolve the "Build" workflow name to an ID, mirroring how `gh run list --workflow` works
// under the hood (it uses /repos/{owner}/{repo}/actions/workflows/{id}/runs).
const { data: workflows } = await octokit.actions.listRepoWorkflows({ owner, repo: repoName });
const buildWorkflow = workflows.workflows.find((w) => w.name === 'Build');
if (!buildWorkflow) {
console.log('Could not find "Build" workflow, continuing without previous checksums');
return;
}
const { data: runs } = await octokit.actions.listWorkflowRuns({
owner,
repo: repoName,
workflow_id: buildWorkflow.id,
branch,
status: 'completed',
event: 'push',
per_page: 20,
exclude_pull_requests: true
});
for (const run of runs.workflow_runs) {
const { data: artifacts } = await octokit.actions.listWorkflowRunArtifacts({
owner,
repo: repoName,
run_id: run.id,
name: artifactName
});
if (artifacts.artifacts.length > 0) {
const artifact = artifacts.artifacts[0];
console.log(`Found artifact in run ${run.id} (artifact ID: ${artifact.id}), downloading...`);
// Non-archived artifacts are still downloaded from the /zip endpoint
const response = await octokit.actions.downloadArtifact({
owner,
repo: repoName,
artifact_id: artifact.id,
archive_format: 'zip'
});
if (response.headers['content-type'] !== 'application/json') {
console.error(`Unexpected content type for artifact download: ${response.headers['content-type']}`);
console.error('Expected application/json, continuing without previous checksums');
return;
}
writeFileSync(outputPath, JSON.stringify(response.data));
console.log('Downloaded previous object checksums successfully');
return;
}
}
console.log(`No previous object checksums found in last ${runs.workflow_runs.length} runs, continuing without them`);
}
main().catch((err) => {
console.error('Failed to download previous object checksums, continuing without them:', err.message);
process.exit(0);
});

View File

@@ -1,24 +0,0 @@
name: 'Build Image SHA'
description: 'Single source of truth for the ghcr.io/electron/build image SHA'
inputs:
override:
description: 'Optional override SHA (e.g. from a workflow_dispatch input)'
required: false
default: ''
outputs:
build-image-sha:
description: 'The electron/build image SHA to use'
value: ${{ steps.set.outputs.build-image-sha }}
runs:
using: 'composite'
steps:
- id: set
shell: bash
env:
OVERRIDE: ${{ inputs.override }}
run: |
if [ -n "$OVERRIDE" ]; then
echo "build-image-sha=$OVERRIDE" >> "$GITHUB_OUTPUT"
else
echo "build-image-sha=daad061f4b99a0ae1c841be4aa09188280a9c8a4" >> "$GITHUB_OUTPUT"
fi

View File

@@ -28,7 +28,7 @@ runs:
shell: bash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH="v2-src-cache-$(cat src/electron/.depshash)"
DEPSHASH="v1-src-cache-$(cat src/electron/.depshash)"
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_FILE=$DEPSHASH.tar" >> $GITHUB_ENV
if [ "${{ inputs.target-platform }}" = "win" ]; then
@@ -43,7 +43,7 @@ runs:
curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}&getAccountName=true" > sas-token
- name: Save SAS Key
if: ${{ inputs.generate-sas-token == 'true' }}
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: sas-token
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}
@@ -109,7 +109,7 @@ runs:
echo "target_os=['$TARGET_OS']" >> ./.gclient
fi
ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN=0 DEPOT_TOOLS_WIN_TOOLCHAIN=0 ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags -vv
if [[ "${{ inputs.is-release }}" != "true" ]]; then
# Re-export all the patches to check if there were changes.
python3 src/electron/script/export_all_patches.py src/electron/patches/config.json
@@ -187,35 +187,21 @@ runs:
shell: bash
run: |
echo "Uncompressed src size: $(du -sh src | cut -f1 -d' ')"
# Named .tar but zstd-compressed; the sas-sidecar's filename allowlist
# only permits .tar/.tgz so we keep the extension and decode on restore.
tar -cf - src | zstd -T0 --long=30 -f -o $CACHE_FILE
tar -cf $CACHE_FILE src
echo "Compressed src to $(du -sh $CACHE_FILE | cut -f1 -d' ')"
cp ./$CACHE_FILE $CACHE_DRIVE/
- name: Persist Src Cache
if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }}
shell: bash
run: |
final_cache_path=$CACHE_DRIVE/$CACHE_FILE
# Upload to a run-unique temp name first so concurrent readers never
# observe a partially-written file, and an interrupted copy can't leave
# a truncated file at the final path. Orphaned temp files get swept by
# the clean-orphaned-cache-uploads workflow.
tmp_cache_path=$final_cache_path.upload-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}
echo "Uploading to temp path: $tmp_cache_path"
cp ./$CACHE_FILE $tmp_cache_path
echo "Using cache key: $DEPSHASH"
if [ -f "$final_cache_path" ]; then
echo "Cache already persisted at $final_cache_path by a concurrent run; discarding ours"
rm -f $tmp_cache_path
else
mv -f $tmp_cache_path $final_cache_path
echo "Cache key persisted in $final_cache_path"
fi
echo "Checking path: $final_cache_path"
if [ ! -f "$final_cache_path" ]; then
echo "Cache key not found"
exit 1
else
echo "Cache key persisted in $final_cache_path"
fi
- name: Wait for active SSH sessions
shell: bash

View File

@@ -22,50 +22,30 @@ runs:
steps:
- name: Delete wrong ${{ inputs.dependency }}
shell: bash
env:
CIPD_ROOT_PREFIX: ${{ inputs.cipd-root-prefix-path }}
INSTALLATION_DIR: ${{ inputs.installation-dir }}
run : |
rm -rf "${CIPD_ROOT_PREFIX}${INSTALLATION_DIR}"
rm -rf ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }}
- name: Create ensure file for ${{ inputs.dependency }}
if: ${{ inputs.dependency-version == '' }}
shell: bash
env:
PACKAGE: ${{ inputs.package }}
DEPS_FILE: ${{ inputs.deps-file }}
INSTALLATION_DIR: ${{ inputs.installation-dir }}
DEPENDENCY: ${{ inputs.dependency }}
run: |
echo "$PACKAGE" $(e d gclient getdep --deps-file="$DEPS_FILE" -r "${INSTALLATION_DIR}:${PACKAGE}") > "${DEPENDENCY}_ensure_file"
cat "${DEPENDENCY}_ensure_file"
echo '${{ inputs.package }}' `e d gclient getdep --deps-file=${{ inputs.deps-file }} -r '${{ inputs.installation-dir }}:${{ inputs.package }}'` > ${{ inputs.dependency }}_ensure_file
cat ${{ inputs.dependency }}_ensure_file
- name: Create ensure file for ${{ inputs.dependency }} from dependency-version
if: ${{ inputs.dependency-version != '' }}
shell: bash
env:
PACKAGE: ${{ inputs.package }}
DEPENDENCY_VERSION: ${{ inputs.dependency-version }}
DEPENDENCY: ${{ inputs.dependency }}
run: |
echo "$PACKAGE $DEPENDENCY_VERSION" > "${DEPENDENCY}_ensure_file"
cat "${DEPENDENCY}_ensure_file"
echo '${{ inputs.package }} ${{ inputs.dependency-version }}' > ${{ inputs.dependency }}_ensure_file
cat ${{ inputs.dependency }}_ensure_file
- name: CIPD installation of ${{ inputs.dependency }} (macOS)
if: ${{ inputs.target-platform != 'win' }}
shell: bash
env:
CIPD_ROOT_PREFIX: ${{ inputs.cipd-root-prefix-path }}
INSTALLATION_DIR: ${{ inputs.installation-dir }}
DEPENDENCY: ${{ inputs.dependency }}
run: |
echo "ensuring $DEPENDENCY"
e d cipd ensure --root "${CIPD_ROOT_PREFIX}${INSTALLATION_DIR}" -ensure-file "${DEPENDENCY}_ensure_file"
echo "ensuring ${{ inputs.dependency }}"
e d cipd ensure --root ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} -ensure-file ${{ inputs.dependency }}_ensure_file
- name: CIPD installation of ${{ inputs.dependency }} (Windows)
if: ${{ inputs.target-platform == 'win' }}
shell: powershell
env:
CIPD_ROOT_PREFIX: ${{ inputs.cipd-root-prefix-path }}
INSTALLATION_DIR: ${{ inputs.installation-dir }}
DEPENDENCY: ${{ inputs.dependency }}
run: |
echo "ensuring $env:DEPENDENCY on Windows"
e d cipd ensure --root "$env:CIPD_ROOT_PREFIX$env:INSTALLATION_DIR" -ensure-file "$($env:DEPENDENCY)_ensure_file"
echo "ensuring ${{ inputs.dependency }} on Windows"
e d cipd ensure --root ${{ inputs.cipd-root-prefix-path }}${{ inputs.installation-dir }} -ensure-file ${{ inputs.dependency }}_ensure_file

View File

@@ -27,7 +27,6 @@ runs:
python3 src/tools/clang/scripts/update.py
# Refs https://chromium-review.googlesource.com/c/chromium/src/+/6667681
python3 src/tools/clang/scripts/update.py --package objdump
python3 src/tools/clang/scripts/update.py --package clang-tidy
- name: Fix esbuild
if: ${{ inputs.target-platform != 'linux' }}
uses: ./src/electron/.github/actions/cipd-install
@@ -133,7 +132,7 @@ runs:
run : |
cd src/third_party/angle
rm -f .git/objects/info/alternates
git remote set-url origin https://github.com/google/angle.git
git remote set-url origin https://chromium.googlesource.com/angle/angle.git
cp .git/config .git/config.backup
git remote remove origin
mv .git/config.backup .git/config

View File

@@ -15,7 +15,7 @@ runs:
git config --global core.preloadindex true
git config --global core.longpaths true
fi
export BUILD_TOOLS_SHA=1b7bd25dae4a780bb3170fff56c9327b53aaf7eb
export BUILD_TOOLS_SHA=a0cc95a1884a631559bcca0c948465b725d9295a
npm i -g @electron/build-tools
# Update depot_tools to ensure python
e d update_depot_tools
@@ -29,4 +29,4 @@ runs:
else
echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH
echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH
fi
fi

View File

@@ -7,7 +7,7 @@ runs:
shell: bash
id: yarn-cache-dir-path
run: echo "dir=$(node src/electron/script/yarn.js config get cacheFolder)" >> $GITHUB_OUTPUT
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -21,28 +21,11 @@ runs:
if [ "$TARGET_ARCH" = "x86" ]; then
export npm_config_arch="ia32"
fi
ARCH=$(uname -m)
node script/yarn.js install --immutable --mode=skip-build
# if running on linux arm skip yarn Builds
ARCH=$(uname -m)
if [ "$ARCH" = "armv7l" ]; then
echo "Skipping yarn build on linux arm"
node script/yarn.js install --immutable --mode=skip-build
else
# Pre-seed the node-gyp header cache so the parallel native-addon
# builds below don't race on a cold cache. Linux build containers
# already ship a warm cache (electron/build-images#68), so only do
# this on macOS / Windows runners.
if [ "$(uname -s)" != "Linux" ]; then
for i in 1 2 3; do
if node node_modules/node-gyp/bin/node-gyp.js install; then
break
fi
if [ "$i" = "3" ]; then
echo "node-gyp header pre-seed failed after 3 attempts" >&2
exit 1
fi
echo "node-gyp header pre-seed failed (attempt $i), retrying in 5s..." >&2
sleep 5
done
fi
node script/yarn.js install --immutable
fi

View File

@@ -31,7 +31,7 @@ runs:
fi
mkdir temp-cache
zstd -d --long=30 -c $cache_path | tar -xf - -C temp-cache
tar -xf $cache_path -C temp-cache
echo "Unzipped cache is $(du -sh temp-cache/src | cut -f1)"
if [ -d "temp-cache/src" ]; then

View File

@@ -8,14 +8,14 @@ runs:
steps:
- name: Obtain SAS Key
continue-on-error: true
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: sas-token
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-1
enableCrossOsArchive: true
- name: Obtain SAS Key
continue-on-error: true
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: sas-token
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}
@@ -24,7 +24,7 @@ runs:
# The cache will always exist here as a result of the checkout job
# Either it was uploaded to Azure in the checkout job for this commit
# or it was uploaded in the checkout job for a previous commit.
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0
with:
timeout_minutes: 30
max_attempts: 3
@@ -61,9 +61,9 @@ runs:
echo "Cache is empty - exiting"
exit 1
fi
mkdir temp-cache
zstd -d --long=30 -c $DEPSHASH.tar | tar -xf - -C temp-cache
tar -xf $DEPSHASH.tar -C temp-cache
echo "Unzipped cache is $(du -sh temp-cache/src | cut -f1)"
if [ -d "temp-cache/src" ]; then
@@ -85,21 +85,23 @@ runs:
- name: Unzip and Ensure Src Cache (Windows)
if: ${{ inputs.target-platform == 'win' }}
shell: bash
shell: powershell
run: |
echo "Downloaded cache is $(du -sh $DEPSHASH.tar | cut -f1)"
if [ `du $DEPSHASH.tar | cut -f1` = "0" ]; then
echo "Cache is empty - exiting"
$src_cache = "$env:DEPSHASH.tar"
$cache_size = $(Get-Item $src_cache).length
Write-Host "Downloaded cache is $cache_size"
if ($cache_size -eq 0) {
Write-Host "Cache is empty - exiting"
exit 1
fi
}
mkdir temp-cache
zstd -d --long=30 -c $DEPSHASH.tar | tar -xf - -C temp-cache
rm -f $DEPSHASH.tar
$TEMP_DIR=New-Item -ItemType Directory -Path temp-cache
$TEMP_DIR_PATH = $TEMP_DIR.FullName
C:\ProgramData\Chocolatey\bin\7z.exe -y -snld20 x $src_cache -o"$TEMP_DIR_PATH"
- name: Move Src Cache (Windows)
if: ${{ inputs.target-platform == 'win' }}
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0
with:
timeout_minutes: 30
max_attempts: 3
@@ -110,6 +112,9 @@ runs:
Write-Host "Relocating Cache"
Remove-Item -Recurse -Force src
Move-Item temp-cache\src src
Write-Host "Deleting zip file"
Remove-Item -Force $src_cache
}
if (-Not (Test-Path "src\third_party\blink")) {
Write-Host "Cache was not correctly restored - exiting"

View File

@@ -7,7 +7,7 @@ runs:
if: ${{ runner.os != 'Windows' }}
shell: bash
run: |
if [[ -z "$CHROMIUM_GIT_COOKIE" ]]; then
if [[ -z "${{ env.CHROMIUM_GIT_COOKIE }}" ]]; then
echo "CHROMIUM_GIT_COOKIE is not set - cannot authenticate."
exit 0
fi
@@ -18,7 +18,9 @@ runs:
git config --global http.cookiefile ~/.gitcookies
echo "$CHROMIUM_GIT_COOKIE" | tr , \\t >>~/.gitcookies
tr , \\t <<\__END__ >>~/.gitcookies
${{ env.CHROMIUM_GIT_COOKIE }}
__END__
eval 'set -o history' 2>/dev/null || unsetopt HIST_IGNORE_SPACE 2>/dev/null
RESPONSE=$(curl -s -b ~/.gitcookies https://chromium-review.googlesource.com/a/accounts/self)
@@ -40,7 +42,7 @@ runs:
)
git config --global http.cookiefile "%USERPROFILE%\.gitcookies"
powershell -noprofile -nologo -command Write-Output $env:CHROMIUM_GIT_COOKIE_WINDOWS_STRING >>"%USERPROFILE%\.gitcookies"
powershell -noprofile -nologo -command Write-Output "${{ env.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}" >>"%USERPROFILE%\.gitcookies"
curl -s -b "%USERPROFILE%\.gitcookies" https://chromium-review.googlesource.com/a/accounts/self > response.txt

View File

@@ -5,7 +5,7 @@
"fromPath": "src/out/Default/args.gn",
"pattern": [
{
"regexp": "^(.+)[(:](\\d+)[:,](\\d+)\\)?:\\s+(warning|fatal error|error):\\s+(.*)$",
"regexp": "^(.+)[(:](\\d+)[:,](\\d+)\\)?:\\s+(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,

View File

@@ -0,0 +1,22 @@
{
"problemMatcher": [
{
"owner": "eslint-stylish",
"pattern": [
{
"regexp": "^\\s*([^\\s].*)$",
"file": 1
},
{
"regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
"line": 1,
"column": 2,
"severity": 3,
"message": 4,
"code": 5,
"loop": true
}
]
}
]
}

View File

@@ -1,47 +0,0 @@
From 85b561ea4dbc76ba98af020b970f3aa6b20fdb9e Mon Sep 17 00:00:00 2001
From: Samuel Attard <sam@electronjs.org>
Date: Wed, 8 Apr 2026 23:24:15 -0700
Subject: [PATCH] siso: reuse the outer *os.File for chunked ReadAt in
fileParser.readFile
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The per-chunk goroutine currently re-opens fname to get its own handle
for ReadAt. (*os.File).ReadAt is documented as safe for concurrent
calls on the same File (on Windows it is ReadFile with an OVERLAPPED
offset, so there is no shared seek state), so the extra open is
redundant — the goroutines can share the outer f.
Besides halving the CreateFileW calls per subninja, this avoids an
intermittent 'The parameter is incorrect.' (ERROR_INVALID_PARAMETER)
from bindflt.sys when out/ is a mapped directory inside a Windows
container: bindflt's handle-relative NtCreateFile path races when a
second relative open arrives while the first handle to the same target
is still being set up. Absolute paths and single opens do not trigger
it; see microsoft/Windows-Containers#<tbd>.
---
siso/toolsupport/ninjautil/file_parser.go | 7 -------
1 file changed, 7 deletions(-)
diff --git a/siso/toolsupport/ninjautil/file_parser.go b/siso/toolsupport/ninjautil/file_parser.go
index 8c18d084..63116662 100644
--- a/siso/toolsupport/ninjautil/file_parser.go
+++ b/siso/toolsupport/ninjautil/file_parser.go
@@ -111,13 +111,6 @@ func (p *fileParser) readFile(ctx context.Context, fname string) ([]byte, error)
eg.Go(func() error {
p.sema <- struct{}{}
defer func() { <-p.sema }()
- f, err := os.Open(fname)
- if err != nil {
- return err
- }
- defer func() {
- _ = f.Close()
- }()
for len(chunkBuf) > 0 {
n, err := f.ReadAt(chunkBuf, pos)
if err != nil {
--
2.53.0

View File

@@ -18,7 +18,6 @@ jobs:
pull-requests: read
outputs:
has-patches: ${{ steps.filter.outputs.patches }}
build-image-sha: ${{ steps.build-image-sha.outputs.build-image-sha }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
@@ -27,16 +26,13 @@ jobs:
# Use dorny/paths-filter instead of the path filter under the on: pull_request: block
# so that the output can be used to conditionally run the apply-patches job, which lets
# the job be marked as a required status check (conditional skip counts as a success).
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
patches:
- DEPS
- 'patches/**'
- name: Set Build Image SHA
id: build-image-sha
uses: ./.github/actions/build-image-sha
apply-patches:
needs: setup
@@ -45,7 +41,7 @@ jobs:
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
@@ -77,7 +73,7 @@ jobs:
target-platform: linux
- name: Upload Patch Conflict Fix
if: ${{ failure() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: update-patches
path: patches/update-patches.patch

View File

@@ -17,25 +17,21 @@ jobs:
with:
fetch-depth: 0
- name: Setup Node.js/npm
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f
with:
node-version: 24.12.x
- name: Setting Up Dig Site
env:
CLONE_URL: ${{ github.event.pull_request.head.repo.clone_url }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
echo "remote: $CLONE_URL"
echo "sha $HEAD_SHA"
echo "base ref $BASE_REF"
git clone https://github.com/electron/electron.git electron
echo "remote: ${{ github.event.pull_request.head.repo.clone_url }}"
echo "sha ${{ github.event.pull_request.head.sha }}"
echo "base ref ${{ github.event.pull_request.base.ref }}"
git clone https://github.com/electron/electron.git electron
cd electron
mkdir -p artifacts
git remote add fork "$CLONE_URL" && git fetch fork
git checkout "$HEAD_SHA"
git merge-base "origin/$BASE_REF" HEAD > .dig-old
echo "$HEAD_SHA" > .dig-new
git remote add fork ${{ github.event.pull_request.head.repo.clone_url }} && git fetch fork
git checkout ${{ github.event.pull_request.head.sha }}
git merge-base origin/${{ github.event.pull_request.base.ref }} HEAD > .dig-old
echo ${{ github.event.pull_request.head.sha }} > .dig-new
cp .dig-old artifacts
- name: Generating Types for SHA in .dig-new
@@ -49,7 +45,7 @@ jobs:
sha-file: .dig-old
filename: electron.old.d.ts
- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f #v7.0.0
with:
name: artifacts
path: electron/artifacts

View File

@@ -17,7 +17,7 @@ jobs:
contents: read
steps:
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 22.17.x
- name: Sparse checkout repository
@@ -28,7 +28,7 @@ jobs:
.github
.yarn
- run: yarn workspaces focus @electron/gha-workflows
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
id: audit-errors
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -87,8 +87,6 @@ jobs:
!message.startsWith("The hosted runner lost communication with the server") &&
!message.startsWith("Dependabot encountered an error performing the update") &&
!message.startsWith("The action 'Run Electron Tests' has timed out") &&
!message.startsWith("The operation was canceled") &&
!message.startsWith("Canceling since") &&
!/Unable to make request/.test(message) &&
!/The requested URL returned error/.test(message),
)

View File

@@ -105,7 +105,7 @@ jobs:
org: electron
- name: Generate Release Project Board Metadata
if: ${{ steps.check-major-version.outputs.MAJOR }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
id: generate-project-metadata
env:
MAJOR: ${{ steps.check-major-version.outputs.MAJOR }}
@@ -157,7 +157,7 @@ jobs:
}))
- name: Create Release Project Board
if: ${{ steps.check-major-version.outputs.MAJOR }}
uses: dsanders11/project-actions/copy-project@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/copy-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
id: create-release-board
with:
drafts: true
@@ -170,60 +170,6 @@ jobs:
template-view: ${{ steps.generate-project-metadata.outputs.template-view }}
title: ${{ steps.generate-project-metadata.outputs.major }}-x-y
token: ${{ steps.generate-token.outputs.token }}
- name: Randomly Assign Draft Issues to Release WG Members
if: ${{ steps.check-major-version.outputs.MAJOR }}
uses: dsanders11/project-actions/github-script@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
env:
PROJECT_ID: ${{ steps.create-release-board.outputs.id }}
with:
token: ${{ steps.generate-token.outputs.token }}
script: |
const { data: members } = await github.rest.teams.listMembersInOrg({
org: 'electron',
team_slug: 'wg-releases',
});
const excludedLogins = ['nikwen'];
const memberLogins = new Set(members.map(m => m.login));
for (const login of excludedLogins) {
if (!memberLogins.has(login)) {
core.warning(`Excluded member "${login}" is not in @electron/wg-releases`);
}
}
const eligible = members.filter(m => !excludedLogins.includes(m.login));
if (eligible.length === 0) {
core.warning('No eligible members found in @electron/wg-releases team');
return;
}
const projectId = process.env.PROJECT_ID;
const draftIssues = await actions.getDraftIssues(projectId);
if (draftIssues.length === 0) {
core.info('No draft issues found in the project');
return;
}
// Fisher-Yates shuffle for uniform random assignment
const shuffled = [...eligible];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
// Assign draft issues round-robin across team members
for (let i = 0; i < draftIssues.length; i++) {
const member = shuffled[i % shuffled.length];
const draftIssue = draftIssues[i];
core.info(`Assigning "${draftIssue.content.title}" to ${member.login}`);
await actions.editItem(projectId, draftIssue.content.id, {
assignees: [member.login],
});
}
core.info(`Assigned ${draftIssues.length} draft issues to ${eligible.length} team members`);
- name: Dump Release Project Board Contents
if: ${{ steps.check-major-version.outputs.MAJOR }}
run: gh project item-list ${{ steps.create-release-board.outputs.number }} --owner electron --format json | jq
@@ -231,7 +177,7 @@ jobs:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
- name: Find Previous Release Project Board
if: ${{ steps.check-major-version.outputs.MAJOR }}
uses: dsanders11/project-actions/find-project@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/find-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
id: find-prev-release-board
with:
fail-if-project-not-found: false
@@ -239,7 +185,7 @@ jobs:
token: ${{ steps.generate-token.outputs.token }}
- name: Close Previous Release Project Board
if: ${{ steps.find-prev-release-board.outputs.number }}
uses: dsanders11/project-actions/close-project@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/close-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
project-number: ${{ steps.find-prev-release-board.outputs.number }}
token: ${{ steps.generate-token.outputs.token }}

View File

@@ -2,38 +2,25 @@ name: Build Git Cache
# This workflow updates git cache on the cross-instance cache volumes
# It runs daily at midnight.
on:
on:
schedule:
- cron: "0 0 * * *"
permissions: {}
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-slim
permissions:
contents: read
outputs:
build-image-sha: ${{ steps.build-image-sha.outputs.build-image-sha }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set Build Image SHA
id: build-image-sha
uses: ./.github/actions/build-image-sha
build-git-cache-linux:
needs: setup
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
env:
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
steps:
- name: Checkout Electron
@@ -47,12 +34,12 @@ jobs:
target-platform: linux
build-git-cache-windows:
needs: setup
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
volumes:
- /mnt/win-cache:/mnt/win-cache
@@ -72,13 +59,14 @@ jobs:
target-platform: win
build-git-cache-macos:
# This job updates the same git cache as linux, so it needs to run after the linux one.
needs: [setup, build-git-cache-linux]
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
# This job updates the same git cache as linux, so it needs to run after the linux one.
needs: build-git-cache-linux
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
@@ -94,4 +82,4 @@ jobs:
- name: Build Git Cache
uses: ./src/electron/.github/actions/build-git-cache
with:
target-platform: macos
target-platform: macos

View File

@@ -6,8 +6,8 @@ on:
build-image-sha:
type: string
description: 'SHA for electron/build image'
default: ''
required: false
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
required: true
skip-macos:
type: boolean
description: 'Skip macOS builds'
@@ -48,40 +48,39 @@ permissions: {}
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-slim
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
docs: ${{ steps.filter.outputs.docs }}
src: ${{ steps.filter.outputs.src }}
build-image-sha: ${{ steps.build-image-sha.outputs.build-image-sha }}
build-image-sha: ${{ steps.set-output.outputs.build-image-sha }}
docs-only: ${{ steps.set-output.outputs.docs-only }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
docs:
- 'docs/**'
- '.claude/**'
- README.md
- SECURITY.md
- CONTRIBUTING.md
- CODE_OF_CONDUCT.md
src:
- '!{docs,.claude}/**'
- name: Set Build Image SHA
id: build-image-sha
uses: ./.github/actions/build-image-sha
with:
override: ${{ inputs.build-image-sha }}
- name: Set Docs Only
- '!docs/**'
- name: Set Outputs for Build Image SHA & Docs Only
id: set-output
run: |
if [ -z "${{ inputs.build-image-sha }}" ]; then
echo "build-image-sha=eac3529546ea8f3aa356d31e345715eef342233b" >> "$GITHUB_OUTPUT"
else
echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT"
fi
echo "docs-only=${{ steps.filter.outputs.docs == 'true' && steps.filter.outputs.src == 'false' }}" >> "$GITHUB_OUTPUT"
# Lint Jobs
@@ -201,15 +200,6 @@ jobs:
generate-sas-token: 'true'
target-platform: win
# Build a patched siso binary for Windows CI in parallel with checkout-windows.
# The Windows build jobs download the resulting artifact and use it via SISO_PATH.
build-siso-windows:
needs: setup
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
uses: ./.github/workflows/pipeline-segment-build-siso-windows.yml
permissions:
contents: read
# GN Check Jobs
macos-gn-check:
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
@@ -275,11 +265,10 @@ jobs:
contents: read
issues: read
pull-requests: read
uses: ./.github/workflows/pipeline-electron-build-and-tidy-and-test.yml
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
needs: checkout-macos
with:
build-runs-on: macos-15-xlarge
clang-tidy-runs-on: macos-15-large
test-runs-on: macos-15
target-platform: macos
target-arch: arm64
@@ -376,32 +365,18 @@ jobs:
generate-symbols: false
upload-to-storage: '0'
secrets: inherit
test-linux-arm64-64k:
uses: ./.github/workflows/pipeline-segment-electron-test-64k.yml
permissions:
contents: read
issues: read
pull-requests: read
needs: [checkout-linux, linux-arm64]
with:
test-runs-on: ubuntu-22.04-arm
test-container: '{"image":"ghcr.io/electron/test:arm64v8-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init"}'
secrets: inherit
windows-x64:
permissions:
contents: read
issues: read
pull-requests: read
uses: ./.github/workflows/pipeline-electron-build-and-tidy-and-test.yml
needs: [checkout-windows, build-siso-windows]
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
needs: checkout-windows
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
with:
build-runs-on: electron-arc-centralus-windows-amd64-16core
clang-tidy-runs-on: electron-arc-centralus-linux-amd64-8core
test-runs-on: windows-latest
clang-tidy-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-windows.outputs.build-image-sha }}","options":"--user root --device /dev/fuse --cap-add SYS_ADMIN","volumes":["/mnt/win-cache:/mnt/win-cache"]}'
target-platform: win
target-arch: x64
is-release: false
@@ -416,7 +391,7 @@ jobs:
issues: read
pull-requests: read
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
needs: [checkout-windows, build-siso-windows]
needs: checkout-windows
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
with:
build-runs-on: electron-arc-centralus-windows-amd64-16core
@@ -435,7 +410,7 @@ jobs:
issues: read
pull-requests: read
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
needs: [checkout-windows, build-siso-windows]
needs: checkout-windows
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
with:
build-runs-on: electron-arc-centralus-windows-amd64-16core
@@ -453,12 +428,9 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, build-siso-windows, windows-x64, windows-x86, windows-arm64]
if: always() && github.repository == 'electron/electron'
steps:
- name: Fail if any needed job failed or was cancelled
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, windows-x64, windows-x86, windows-arm64]
if: always() && github.repository == 'electron/electron' && !contains(needs.*.result, 'failure')
steps:
- name: GitHub Actions Jobs Done
run: |
echo "All GitHub Actions Jobs are done"

View File

@@ -1,45 +0,0 @@
name: Clean Orphaned Cache Uploads
# Description:
# Sweeps orphaned in-flight upload temp files left on the src-cache volumes
# by checkout/action.yml when its cp-to-share step dies before the rename.
# A successful upload finishes in minutes, so anything older than 4h is dead.
on:
schedule:
- cron: "0 */4 * * *"
workflow_dispatch:
permissions: {}
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-slim
permissions:
contents: read
outputs:
build-image-sha: ${{ steps.build-image-sha.outputs.build-image-sha }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set Build Image SHA
id: build-image-sha
uses: ./.github/actions/build-image-sha
clean-orphaned-uploads:
needs: setup
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
- /mnt/win-cache:/mnt/win-cache
steps:
- name: Remove Orphaned Upload Temp Files
shell: bash
run: |
find /mnt/cross-instance-cache -maxdepth 1 -type f -name '*.tar.upload-*' -mmin +240 -print -delete
find /mnt/win-cache -maxdepth 1 -type f -name '*.tar.upload-*' -mmin +240 -print -delete

View File

@@ -7,162 +7,28 @@ name: Clean Source Cache
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions: {}
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-slim
permissions:
contents: read
outputs:
build-image-sha: ${{ steps.build-image-sha.outputs.build-image-sha }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set Build Image SHA
id: build-image-sha
uses: ./.github/actions/build-image-sha
clean-src-cache:
needs: setup
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
env:
DD_API_KEY: ${{ secrets.DD_API_KEY }}
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
- /mnt/win-cache:/mnt/win-cache
steps:
- name: Get Disk Space Before Cleanup
id: disk-before
shell: bash
run: |
echo "Disk space before cleanup:"
df -h /mnt/cross-instance-cache
df -h /mnt/win-cache
CROSS_FREE_BEFORE=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $4}')
CROSS_TOTAL=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $2}')
WIN_FREE_BEFORE=$(df -k /mnt/win-cache | tail -1 | awk '{print $4}')
WIN_TOTAL=$(df -k /mnt/win-cache | tail -1 | awk '{print $2}')
echo "cross_free_kb=$CROSS_FREE_BEFORE" >> $GITHUB_OUTPUT
echo "cross_total_kb=$CROSS_TOTAL" >> $GITHUB_OUTPUT
echo "win_free_kb=$WIN_FREE_BEFORE" >> $GITHUB_OUTPUT
echo "win_total_kb=$WIN_TOTAL" >> $GITHUB_OUTPUT
- name: Cleanup Source Cache
shell: bash
run: |
df -h /mnt/cross-instance-cache
find /mnt/cross-instance-cache -type f -mtime +15 -delete
find /mnt/win-cache -type f -mtime +15 -delete
- name: Get Disk Space After Cleanup
id: disk-after
shell: bash
run: |
echo "Disk space after cleanup:"
df -h /mnt/cross-instance-cache
df -h /mnt/win-cache
CROSS_FREE_AFTER=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $4}')
WIN_FREE_AFTER=$(df -k /mnt/win-cache | tail -1 | awk '{print $4}')
echo "cross_free_kb=$CROSS_FREE_AFTER" >> $GITHUB_OUTPUT
echo "win_free_kb=$WIN_FREE_AFTER" >> $GITHUB_OUTPUT
- name: Log Disk Space to Datadog
if: ${{ env.DD_API_KEY != '' }}
shell: bash
env:
CROSS_FREE_BEFORE: ${{ steps.disk-before.outputs.cross_free_kb }}
CROSS_FREE_AFTER: ${{ steps.disk-after.outputs.cross_free_kb }}
CROSS_TOTAL: ${{ steps.disk-before.outputs.cross_total_kb }}
WIN_FREE_BEFORE: ${{ steps.disk-before.outputs.win_free_kb }}
WIN_FREE_AFTER: ${{ steps.disk-after.outputs.win_free_kb }}
WIN_TOTAL: ${{ steps.disk-before.outputs.win_total_kb }}
run: |
TIMESTAMP=$(date +%s)
CROSS_FREE_BEFORE_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_FREE_BEFORE / 1024 / 1024}")
CROSS_FREE_AFTER_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_FREE_AFTER / 1024 / 1024}")
CROSS_FREED_GB=$(awk "BEGIN {printf \"%.2f\", ($CROSS_FREE_AFTER - $CROSS_FREE_BEFORE) / 1024 / 1024}")
CROSS_TOTAL_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_TOTAL / 1024 / 1024}")
WIN_FREE_BEFORE_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_FREE_BEFORE / 1024 / 1024}")
WIN_FREE_AFTER_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_FREE_AFTER / 1024 / 1024}")
WIN_FREED_GB=$(awk "BEGIN {printf \"%.2f\", ($WIN_FREE_AFTER - $WIN_FREE_BEFORE) / 1024 / 1024}")
WIN_TOTAL_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_TOTAL / 1024 / 1024}")
echo "cross-instance-cache: free before=${CROSS_FREE_BEFORE_GB}GB, after=${CROSS_FREE_AFTER_GB}GB, freed=${CROSS_FREED_GB}GB, total=${CROSS_TOTAL_GB}GB"
echo "win-cache: free before=${WIN_FREE_BEFORE_GB}GB, after=${WIN_FREE_AFTER_GB}GB, freed=${WIN_FREED_GB}GB, total=${WIN_TOTAL_GB}GB"
curl -s -X POST "https://api.datadoghq.com/api/v2/series" \
-H "Content-Type: application/json" \
-H "DD-API-KEY: ${DD_API_KEY}" \
-d @- << EOF
{
"series": [
{
"metric": "electron.src_cache.disk.free_space_before_cleanup_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREE_BEFORE_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:cross-instance-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.free_space_after_cleanup_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREE_AFTER_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:cross-instance-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.space_freed_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREED_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:cross-instance-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.total_space_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_TOTAL_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:cross-instance-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.free_space_before_cleanup_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREE_BEFORE_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:win-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.free_space_after_cleanup_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREE_AFTER_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:win-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.space_freed_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREED_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:win-cache", "platform:linux"]
},
{
"metric": "electron.src_cache.disk.total_space_gb",
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_TOTAL_GB}}],
"type": 3,
"unit": "gigabyte",
"tags": ["volume:win-cache", "platform:linux"]
}
]
}
EOF
echo "Disk space metrics logged to Datadog"
find /mnt/win-cache -type f -mtime +15 -delete
df -h /mnt/win-cache

View File

@@ -34,6 +34,30 @@ jobs:
run: |
gh issue edit $ISSUE_URL --remove-label 'blocked/need-repro','blocked/need-info ❌'
pr-needs-signed-commits-commented:
name: Remove needs-signed-commits on comment
if: ${{ github.event.issue.pull_request && (contains(github.event.issue.labels.*.name, 'needs-signed-commits')) && (github.event.comment.user.login == github.event.issue.user.login) }}
runs-on: ubuntu-slim
steps:
- name: Get author association
id: get-author-association
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: *get-author-association
- name: Generate GitHub App token
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
if: ${{ !contains(fromJSON('["MEMBER", "OWNER", "COLLABORATOR"]'), steps.get-author-association.outputs.author_association) }}
id: generate-token
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
- name: Remove label
if: ${{ !contains(fromJSON('["MEMBER", "OWNER", "COLLABORATOR"]'), steps.get-author-association.outputs.author_association) }}
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
ISSUE_URL: ${{ github.event.issue.html_url }}
run: |
gh issue edit $ISSUE_URL --remove-label 'needs-signed-commits'
pr-reviewer-requested:
name: Maintainer requested reviewer on PR
if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/request-review') && github.event.comment.user.type != 'Bot' }}

View File

@@ -21,7 +21,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Set status
uses: dsanders11/project-actions/edit-item@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 90
@@ -42,7 +42,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Set status
uses: dsanders11/project-actions/edit-item@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 90
@@ -61,10 +61,9 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: electron/electron
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
set -eo pipefail
COMMENT_COUNT=$(gh issue view "$ISSUE_NUMBER" --comments --json comments | jq '[ .comments[] | select(.author.login == "electron-issue-triage" or .authorAssociation == "OWNER" or .authorAssociation == "MEMBER") | select(.body | startswith("<!-- blocked/need-repro -->")) ] | length')
COMMENT_COUNT=$(gh issue view ${{ github.event.issue.number }} --comments --json comments | jq '[ .comments[] | select(.author.login == "electron-issue-triage" or .authorAssociation == "OWNER" or .authorAssociation == "MEMBER") | select(.body | startswith("<!-- blocked/need-repro -->")) ] | length')
if [[ $COMMENT_COUNT -eq 0 ]]; then
echo "SHOULD_COMMENT=1" >> "$GITHUB_OUTPUT"
fi
@@ -76,7 +75,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
- name: Create comment
if: ${{ steps.check-for-comment.outputs.SHOULD_COMMENT }}
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
with:
actions: 'create-comment'
token: ${{ steps.generate-token.outputs.token }}

View File

@@ -20,7 +20,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Add to Issue Triage
uses: dsanders11/project-actions/add-item@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/add-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
field: Reporter
field-value: ${{ github.event.issue.user.login }}
@@ -46,7 +46,7 @@ jobs:
.yarn
- run: yarn workspaces focus @electron/gha-workflows
- name: Add labels
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
id: add-labels
env:
ISSUE_BODY: ${{ github.event.issue.body }}
@@ -146,7 +146,7 @@ jobs:
}
- name: Create unsupported major comment
if: ${{ steps.add-labels.outputs.unsupportedMajor }}
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
with:
actions: 'create-comment'
token: ${{ steps.generate-token.outputs.token }}

View File

@@ -20,7 +20,7 @@ jobs:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Remove from issue triage
uses: dsanders11/project-actions/delete-item@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/delete-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 90

View File

@@ -16,11 +16,9 @@ jobs:
steps:
- name: Check for any blocked labels
id: check-for-blocked-labels
env:
LABELS_JSON: ${{ toJSON(github.event.issue.labels.*.name) }}
run: |
set -eo pipefail
BLOCKED_LABEL_COUNT=$(echo "$LABELS_JSON" | jq '[ .[] | select(startswith("blocked/")) ] | length')
BLOCKED_LABEL_COUNT=$(echo '${{ toJSON(github.event.issue.labels.*.name) }}' | jq '[ .[] | select(startswith("blocked/")) ] | length')
if [[ $BLOCKED_LABEL_COUNT -eq 0 ]]; then
echo "NOT_BLOCKED=1" >> "$GITHUB_OUTPUT"
fi
@@ -33,7 +31,7 @@ jobs:
org: electron
- name: Set status
if: ${{ steps.check-for-blocked-labels.outputs.NOT_BLOCKED }}
uses: dsanders11/project-actions/edit-item@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 90

View File

@@ -6,8 +6,7 @@ on:
build-image-sha:
type: string
description: 'SHA for electron/build image'
default: ''
required: false
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
upload-to-storage:
description: 'Uploads to Azure storage'
required: false
@@ -21,28 +20,13 @@ on:
permissions: {}
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-slim
permissions:
contents: read
outputs:
build-image-sha: ${{ steps.build-image-sha.outputs.build-image-sha }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set Build Image SHA
id: build-image-sha
uses: ./.github/actions/build-image-sha
with:
override: ${{ inputs.build-image-sha }}
checkout-linux:
needs: setup
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
image: ghcr.io/electron/build:${{ inputs.build-image-sha }}
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
@@ -66,11 +50,11 @@ jobs:
attestations: write
contents: read
id-token: write
needs: [setup, checkout-linux]
needs: checkout-linux
with:
environment: production-release
build-runs-on: electron-arc-centralus-linux-amd64-32core
build-container: '{"image":"ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
target-platform: linux
target-arch: x64
is-release: true
@@ -86,11 +70,11 @@ jobs:
attestations: write
contents: read
id-token: write
needs: [setup, checkout-linux]
needs: checkout-linux
with:
environment: production-release
build-runs-on: electron-arc-centralus-linux-amd64-32core
build-container: '{"image":"ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
target-platform: linux
target-arch: arm
is-release: true
@@ -106,11 +90,11 @@ jobs:
attestations: write
contents: read
id-token: write
needs: [setup, checkout-linux]
needs: checkout-linux
with:
environment: production-release
build-runs-on: electron-arc-centralus-linux-amd64-32core
build-container: '{"image":"ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
build-container: '{"image":"ghcr.io/electron/build:${{ inputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
target-platform: linux
target-arch: arm64
is-release: true

View File

@@ -6,8 +6,8 @@ on:
build-image-sha:
type: string
description: 'SHA for electron/build image'
default: ''
required: false
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
required: true
upload-to-storage:
description: 'Uploads to Azure storage'
required: false
@@ -21,28 +21,13 @@ on:
permissions: {}
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-slim
permissions:
contents: read
outputs:
build-image-sha: ${{ steps.build-image-sha.outputs.build-image-sha }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set Build Image SHA
id: build-image-sha
uses: ./.github/actions/build-image-sha
with:
override: ${{ inputs.build-image-sha }}
checkout-macos:
needs: setup
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
image: ghcr.io/electron/build:${{ inputs.build-image-sha }}
options: --user root
volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache

View File

@@ -10,10 +10,6 @@ on:
- '.yarn/**'
- '.yarnrc.yml'
# SECURITY: This workflow uses pull_request_target and has access to secrets.
# Do NOT checkout or run code from the PR head. All code execution must use
# the base branch only. Adding a ref to PR head would expose secrets to
# untrusted code.
permissions: {}
jobs:
@@ -49,23 +45,5 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
cat <<'REVIEW_EOF' | sed "s/%AUTHOR%/$PR_AUTHOR/g" | gh pr review $PR_URL -r --body-file=-
<!-- disallowed-non-maintainer-change -->
Hello @%AUTHOR%! It looks like this pull request touches one of our dependency or CI files, and per [our contribution policy](https://github.com/electron/electron/blob/main/CONTRIBUTING.md#dependencies-upgrades-policy) we do not accept these types of changes in PRs.
To move this PR forward, please:
1. Revert the dependency/CI file changes from your branch. (e.g. `yarn.lock`, `.yarn/`, `.yarnrc.yml`, `.github/workflows/`, `.github/actions/`)
2. Ensure your branch [allows maintainer commits](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so a maintainer can push the necessary dependency changes on your behalf.
3. Leave a comment letting reviewers know the dependency change is still needed.
<details>
<summary>For maintainers</summary>
To land this PR, push a verified commit to the contributor's branch with the required dependency/CI changes, then dismiss this review.
</details>
REVIEW_EOF
printf "<!-- disallowed-non-maintainer-change -->\n\nHello @${{ github.event.pull_request.user.login }}! It looks like this pull request touches one of our dependency or CI files, and per [our contribution policy](https://github.com/electron/electron/blob/main/CONTRIBUTING.md#dependencies-upgrades-policy) we do not accept these types of changes in PRs." | gh pr review $PR_URL -r --body-file=-

View File

@@ -35,7 +35,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AKS

View File

@@ -46,11 +46,7 @@ jobs:
shell: bash
run: |
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
if [[ ! "$chromium_revision" =~ ^[a-zA-Z0-9._-]+$ ]]; then
echo "::error::Invalid chromium_revision: $chromium_revision"
exit 1
fi
gn_version="$(curl -sL "https://raw.githubusercontent.com/chromium/chromium/refs/tags/${chromium_revision}/DEPS" | grep gn_version | head -n1 | cut -d\' -f4)"
gn_version="$(curl -sL -b ~/.gitcookies "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
cipd ensure -ensure-file - -root . <<-CIPD
\$ServiceURL https://chrome-infra-packages.appspot.com/
@@ -64,18 +60,15 @@ jobs:
shell: bash
run: |
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
if [[ ! "$chromium_revision" =~ ^[a-zA-Z0-9._-]+$ ]]; then
echo "::error::Invalid chromium_revision: $chromium_revision"
exit 1
fi
mkdir -p src/buildtools
curl -sL "https://raw.githubusercontent.com/chromium/chromium/refs/tags/${chromium_revision}/buildtools/DEPS" > src/buildtools/DEPS
curl -sL -b ~/.gitcookies "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > src/buildtools/DEPS
gclient sync --spec="solutions=[{'name':'src/buildtools','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':True},'managed':False}]"
- name: Add problem matchers
shell: bash
run: |
echo "::add-matcher::src/electron/.github/problem-matchers/eslint-stylish.json"
echo "::add-matcher::src/electron/.github/problem-matchers/markdownlint.json"
- name: Run Lint
shell: bash

View File

@@ -1,98 +0,0 @@
name: Pipeline Segment - Build Siso (Windows)
# Builds a patched siso binary for Windows CI. Reads the siso revision from
# the Chromium DEPS file at the pinned chromium_version, shallow-clones
# chromium.googlesource.com/build at that revision, applies the patches under
# .github/siso-patches/, cross-compiles siso.exe for windows/amd64, and
# publishes it as the `siso-windows-amd64` artifact. The Windows build jobs
# download it and use it via SISO_PATH. The built binary is cached keyed on
# the siso revision + sha256 of the patch contents, so subsequent runs just
# restore it.
on:
workflow_call: {}
permissions: {}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
ref: ${{ github.event.pull_request.head.sha }}
sparse-checkout: |
DEPS
.github/siso-patches
- name: Resolve siso revision from Chromium DEPS
id: resolve
run: |
set -euo pipefail
CHROMIUM_VERSION=$(python3 -c "import re; print(re.search(r\"'chromium_version':\s*\n\s*'([^']+)'\", open('DEPS').read()).group(1))")
if ! [[ "$CHROMIUM_VERSION" =~ ^[0-9]+(\.[0-9]+){1,3}$ ]]; then
echo "error: unexpected chromium_version format: $CHROMIUM_VERSION" >&2
exit 1
fi
curl -sfL "https://raw.githubusercontent.com/chromium/chromium/${CHROMIUM_VERSION}/DEPS" -o /tmp/chromium-DEPS
SISO_SHA=$(python3 -c "import re; print(re.search(r\"'siso_version':\s*'git_revision:([0-9a-f]+)'\", open('/tmp/chromium-DEPS').read()).group(1))")
if ! [[ "$SISO_SHA" =~ ^[0-9a-f]{40}$ ]]; then
echo "error: unexpected siso_version SHA: $SISO_SHA" >&2
exit 1
fi
PATCHES_HASH=$(find .github/siso-patches -type f -name '*.patch' | sort | xargs sha256sum | sha256sum | awk '{print $1}')
echo "siso-sha=${SISO_SHA}" >> "$GITHUB_OUTPUT"
echo "patches-hash=${PATCHES_HASH}" >> "$GITHUB_OUTPUT"
echo "Chromium ${CHROMIUM_VERSION} pins siso at ${SISO_SHA}"
echo "Patches hash: ${PATCHES_HASH}"
- name: Restore cached siso binary
id: cache-siso
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: siso-out/siso.exe
key: siso-windows-amd64-${{ steps.resolve.outputs.siso-sha }}-${{ steps.resolve.outputs.patches-hash }}
- name: Shallow clone chromium build repo at pinned revision
if: steps.cache-siso.outputs.cache-hit != 'true'
env:
SISO_SHA: ${{ steps.resolve.outputs.siso-sha }}
run: |
set -euo pipefail
mkdir chromium-build
cd chromium-build
git init -q
git remote add origin https://chromium.googlesource.com/build
git -c protocol.version=2 fetch --depth=1 origin "$SISO_SHA"
git checkout --detach FETCH_HEAD
- name: Apply in-tree siso patches
if: steps.cache-siso.outputs.cache-hit != 'true'
run: |
set -euo pipefail
cd chromium-build
git -c user.name=electron-ci -c user.email=ci@electronjs.org \
am --3way "${GITHUB_WORKSPACE}/.github/siso-patches"/*.patch
- name: Set up Go
if: steps.cache-siso.outputs.cache-hit != 'true'
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: chromium-build/siso/go.mod
cache: false
- name: Build siso (windows/amd64)
if: steps.cache-siso.outputs.cache-hit != 'true'
working-directory: chromium-build/siso
env:
CGO_ENABLED: '0'
GOOS: windows
GOARCH: amd64
run: |
mkdir -p "${GITHUB_WORKSPACE}/siso-out"
go build -trimpath -o "${GITHUB_WORKSPACE}/siso-out/siso.exe" .
- name: Upload siso artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: siso-windows-amd64
path: siso-out/siso.exe
if-no-files-found: error
retention-days: 1

View File

@@ -77,6 +77,7 @@ env:
ELECTRON_ARTIFACTS_BLOB_STORAGE: ${{ secrets.ELECTRON_ARTIFACTS_BLOB_STORAGE }}
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
SUDOWOODO_EXCHANGE_URL: ${{ secrets.SUDOWOODO_EXCHANGE_URL }}
SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }}
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' && '--custom-var=checkout_mac=True --custom-var=host_os=mac' || inputs.target-platform == 'win' && '--custom-var=checkout_win=True' || '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
ELECTRON_OUT_DIR: Default
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
@@ -123,7 +124,7 @@ jobs:
run: df -h
- name: Setup Node.js/npm
if: ${{ inputs.target-platform == 'macos' }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f
with:
node-version: 22.21.x
cache: yarn
@@ -155,7 +156,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy
@@ -194,22 +195,6 @@ jobs:
- name: Free up space (macOS)
if: ${{ inputs.target-platform == 'macos' }}
uses: ./src/electron/.github/actions/free-space-macos
- name: Download custom siso binary (Windows)
if: ${{ inputs.target-platform == 'win' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: siso-windows-amd64
path: ${{ runner.temp }}/siso
- name: Set SISO_PATH (Windows)
if: ${{ inputs.target-platform == 'win' }}
run: |
SISO_BIN="${RUNNER_TEMP}/siso/siso.exe"
if [ ! -f "$SISO_BIN" ]; then
echo "error: expected siso binary at $SISO_BIN" >&2
exit 1
fi
echo "SISO_PATH=$SISO_BIN" >> "$GITHUB_ENV"
echo "Using custom siso binary at $SISO_BIN"
- name: Build Electron
if: ${{ inputs.target-platform != 'macos' || (inputs.target-variant == 'all' || inputs.target-variant == 'darwin') }}
uses: ./src/electron/.github/actions/build-electron

View File

@@ -80,7 +80,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy
@@ -126,7 +126,7 @@ jobs:
cd src/electron
git pack-refs
- name: Download Out Gen Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
with:
name: out_gen_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src/out/${{ env.ELECTRON_OUT_DIR }}/gen
@@ -135,34 +135,15 @@ jobs:
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
- name: Run Clang-Tidy
run: |
e init -f --root=$(pwd) --out=${ELECTRON_OUT_DIR} testing --target-cpu ${TARGET_ARCH} --remote-build none
e init -f --root=$(pwd) --out=${ELECTRON_OUT_DIR} testing --target-cpu ${TARGET_ARCH}
# For macOS use_remoteexec=false will cause GN errors, so even though we're doing no remote build, set it
export GN_EXTRA_ARGS="use_remoteexec=true target_cpu=\"${TARGET_ARCH}\""
export GN_EXTRA_ARGS="target_cpu=\"${TARGET_ARCH}\""
if [ "${{ inputs.target-platform }}" = "win" ]; then
export GN_EXTRA_ARGS="$GN_EXTRA_ARGS use_v8_context_snapshot=true target_os=\"win\""
fi
e build --only-gen
# Copy macOS framework headers so clang-tidy can find them via -F.
# This must happen after e build --only-gen since e init -f may
# recreate the output directory.
if [ "${{ inputs.target-platform }}" = "macos" ]; then
OUT=src/out/${ELECTRON_OUT_DIR}
SQRL=src/third_party/squirrel.mac
mkdir -p ${OUT}/{ReactiveObjC,Squirrel,Mantle}.framework/Headers
cp ${SQRL}/vendor/ReactiveObjC/ReactiveObjC/*.h ${OUT}/ReactiveObjC.framework/Headers/
cp ${SQRL}/vendor/ReactiveObjC/ReactiveObjC/extobjc/*.h ${OUT}/ReactiveObjC.framework/Headers/
cp ${SQRL}/Squirrel/*.h ${OUT}/Squirrel.framework/Headers/
cp ${SQRL}/vendor/Mantle/Mantle/include/*.h ${OUT}/Mantle.framework/Headers/
cp ${SQRL}/vendor/Mantle/Mantle/extobjc/include/*.h ${OUT}/Mantle.framework/Headers/
fi
cd src/electron
node script/yarn.js lint:clang-tidy --jobs 8 --out-dir ../out/${ELECTRON_OUT_DIR}
- name: Remove Clang problem matcher

View File

@@ -81,7 +81,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy
@@ -130,7 +130,7 @@ jobs:
run: |
for target_cpu in ${{ inputs.target-archs }}
do
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu $target_cpu --remote-build none
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu $target_cpu
cd src
export GN_EXTRA_ARGS="target_cpu=\"$target_cpu\""
if [ "${{ inputs.target-platform }}" = "linux" ]; then

View File

@@ -79,6 +79,7 @@ env:
ELECTRON_ARTIFACTS_BLOB_STORAGE: ${{ secrets.ELECTRON_ARTIFACTS_BLOB_STORAGE }}
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
SUDOWOODO_EXCHANGE_URL: ${{ secrets.SUDOWOODO_EXCHANGE_URL }}
SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }}
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' &&
'--custom-var=checkout_mac=True --custom-var=host_os=mac' ||
inputs.target-platform == 'win' && '--custom-var=checkout_win=True' ||
@@ -131,7 +132,7 @@ jobs:
run: df -h
- name: Setup Node.js/npm
if: ${{ inputs.target-platform == 'macos' }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f
with:
node-version: 22.21.x
cache: yarn
@@ -164,7 +165,7 @@ jobs:
- name: Generate DEPS Hash
run: |
node src/electron/script/generate-deps-hash.js
DEPSHASH=v2-src-cache-$(cat src/electron/.depshash)
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
- name: Restore src cache via AZCopy
@@ -207,22 +208,6 @@ jobs:
- name: Free up space (macOS)
if: ${{ inputs.target-platform == 'macos' }}
uses: ./src/electron/.github/actions/free-space-macos
- name: Download custom siso binary (Windows)
if: ${{ inputs.target-platform == 'win' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: siso-windows-amd64
path: ${{ runner.temp }}/siso
- name: Set SISO_PATH (Windows)
if: ${{ inputs.target-platform == 'win' }}
run: |
SISO_BIN="${RUNNER_TEMP}/siso/siso.exe"
if [ ! -f "$SISO_BIN" ]; then
echo "error: expected siso binary at $SISO_BIN" >&2
exit 1
fi
echo "SISO_PATH=$SISO_BIN" >> "$GITHUB_ENV"
echo "Using custom siso binary at $SISO_BIN"
- name: Build Electron
if: ${{ inputs.target-platform != 'macos' || (inputs.target-variant == 'all' ||
inputs.target-variant == 'darwin') }}

View File

@@ -1,67 +0,0 @@
name: Pipeline Segment - Electron Test on Linux ARM64 64k
on:
workflow_call:
inputs:
test-runs-on:
type: string
description: 'What host to run the tests on'
required: true
test-container:
type: string
description: 'JSON container information for aks runs-on'
required: false
default: '{"image":null}'
concurrency:
group: electron-test-linux-64k-${{ github.ref_protected == true && github.run_id || github.ref }}
cancel-in-progress: ${{ github.ref_protected != true }}
permissions: {}
env:
ELECTRON_OUT_DIR: Default
jobs:
test-linux-arm64-64k:
env:
BUILD_TYPE: linux
TARGET_ARCH: arm64
defaults:
run:
shell: bash
runs-on: ${{ inputs.test-runs-on }}
permissions:
contents: read
issues: read
pull-requests: read
steps:
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
path: src/electron
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Download Generated Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: generated_artifacts_linux_arm64
path: ./generated_artifacts_linux_arm64
- name: Restore Generated Artifacts
run: ./src/electron/script/actions/restore-artifacts.sh
- name: Unzip Dist
run: |
cd src/out/Default
unzip -:o dist.zip
- name: Run Electron Tests in QEMU 64k Container
shell: bash
env:
MOCHA_REPORTER: mocha-multi-reporters
MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
DISPLAY: ':99.0'
run: |
container=$(echo '${{ inputs.test-container }}' | jq -r '.image')
src/electron/script/run-qemu-64k.sh --container $container --testfiles "`pwd`/src"

View File

@@ -48,8 +48,6 @@ env:
ELECTRON_OUT_DIR: Default
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
# @sentry/cli is only needed by release upload-symbols.py; skip the ~17MB CDN download on test jobs
SENTRYCLI_SKIP_DOWNLOAD: 1
jobs:
test:
@@ -66,7 +64,7 @@ jobs:
fail-fast: false
matrix:
build-type: ${{ inputs.target-platform == 'macos' && fromJSON('["darwin","mas"]') || (inputs.target-platform == 'win' && fromJSON('["win"]') || fromJSON('["linux"]')) }}
shard: ${{ case(inputs.display-server == 'wayland', fromJSON('[1]'), inputs.target-platform == 'linux', fromJSON('[1, 2, 3]'), inputs.target-platform == 'macos' && inputs.target-arch == 'x64', fromJSON('[1, 2, 3]'), fromJSON('[1, 2]')) }}
shard: ${{ case(inputs.display-server == 'wayland', fromJSON('[1]'), inputs.target-platform == 'linux', fromJSON('[1, 2, 3]'), fromJSON('[1, 2]')) }}
env:
BUILD_TYPE: ${{ matrix.build-type }}
TARGET_ARCH: ${{ inputs.target-arch }}
@@ -79,7 +77,7 @@ jobs:
cp $(which node) /mnt/runner-externals/node24/bin/
- name: Setup Node.js/npm
if: ${{ inputs.target-platform == 'win' }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f
with:
node-version: 22.21.x
- name: Add TCC permissions on macOS
@@ -175,12 +173,12 @@ jobs:
echo "DISABLE_CRASH_REPORTER_TESTS=true" >> $GITHUB_ENV
echo "IS_ASAN=true" >> $GITHUB_ENV
- name: Download Generated Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
with:
name: generated_artifacts_${{ env.ARTIFACT_KEY }}
path: ./generated_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}
- name: Download Src Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
with:
name: src_artifacts_${{ env.ARTIFACT_KEY }}
path: ./src_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}
@@ -216,7 +214,7 @@ jobs:
- name: Run Electron Tests
shell: bash
timeout-minutes: 60
timeout-minutes: 40
env:
MOCHA_REPORTER: mocha-multi-reporters
MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap
@@ -227,7 +225,7 @@ jobs:
cd src/electron
export ELECTRON_TEST_RESULTS_DIR=`pwd`/junit
# Get which tests are on this shard
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ case(inputs.display-server == 'wayland', 1, inputs.target-platform == 'linux', 3, inputs.target-platform == 'macos' && inputs.target-arch == 'x64', 3, 2) }})
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ case(inputs.display-server == 'wayland', 1, inputs.target-platform == 'linux', 3, 2) }})
if [ "${{ inputs.display-server }}" = "wayland" ]; then
allowlist_file=script/wayland-test-allowlist.txt
filtered_tests=""
@@ -315,7 +313,7 @@ jobs:
if: always() && !cancelled()
- name: Upload Test Artifacts
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
with:
name: ${{ inputs.target-platform == 'linux' && format('test_artifacts_{0}_{1}_{2}', env.ARTIFACT_KEY, inputs.display-server, matrix.shard) || format('test_artifacts_{0}_{1}', env.ARTIFACT_KEY, matrix.shard) }}
path: src/electron/spec/artifacts

View File

@@ -36,8 +36,6 @@ env:
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
ELECTRON_OUT_DIR: Default
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
# @sentry/cli is only needed by release upload-symbols.py; skip the ~17MB CDN download on test jobs
SENTRYCLI_SKIP_DOWNLOAD: 1
jobs:
node-tests:
@@ -67,12 +65,12 @@ jobs:
- name: Install Dependencies
uses: ./src/electron/.github/actions/install-dependencies
- name: Download Generated Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
with:
name: generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
path: ./generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
- name: Download Src Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
with:
name: src_artifacts_linux_${{ env.TARGET_ARCH }}
path: ./src_artifacts_linux_${{ env.TARGET_ARCH }}
@@ -123,12 +121,12 @@ jobs:
- name: Install Dependencies
uses: ./src/electron/.github/actions/install-dependencies
- name: Download Generated Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
with:
name: generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
path: ./generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
- name: Download Src Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
with:
name: src_artifacts_linux_${{ env.TARGET_ARCH }}
path: ./src_artifacts_linux_${{ env.TARGET_ARCH }}

View File

@@ -1,64 +0,0 @@
name: PR Template Check
on:
pull_request_target:
types: [opened, ready_for_review]
# SECURITY: This workflow uses pull_request_target and has access to secrets.
# Do NOT checkout or run code from the PR head. All code execution must use
# the base branch only. Adding a ref to PR head would expose secrets to
# untrusted code.
permissions: {}
jobs:
check-pr-template:
if: ${{ github.event.pull_request.head.repo.fork && !github.event.pull_request.draft && !startsWith(github.head_ref, 'roller/') }}
name: Check PR Template
runs-on: ubuntu-slim
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
sparse-checkout: .github/PULL_REQUEST_TEMPLATE.md
sparse-checkout-cone-mode: false
- name: Check for required sections
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const fs = require('fs');
const template = fs.readFileSync('.github/PULL_REQUEST_TEMPLATE.md', 'utf8');
const requiredSections = [...template.matchAll(/^(#{1,4} .+)$/gm)].map(
(m) => m[1],
);
if (requiredSections.length === 0) {
console.log('No heading sections found in PR template');
return;
}
const body = context.payload.pull_request.body || '';
// Allow through if body contains a valid backport line
const backportRegex = /Backport of (?:#|https:\/\/github.com\/electron\/electron\/pull\/)\d+/i;
if (backportRegex.test(body)) {
console.log('Backport PR detected, skipping required section check.');
return;
}
const missingSections = requiredSections.filter(
(section) => !body.includes(section),
);
if (missingSections.length > 0) {
const list = missingSections.map((s) => `- \`${s}\``).join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `This PR was automatically closed because the PR template was not properly filled out. The following required sections are missing:\n\n${list}\n\nPlease update your PR description to include all required sections and reopen the PR.`,
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
state: 'closed',
});
}

View File

@@ -6,25 +6,16 @@ on:
issue_comment:
types: [created]
# SECURITY: This workflow uses pull_request_target and has access to secrets.
# Do NOT checkout or run code from the PR head. All code execution must use
# the base branch only. Adding a ref to PR head would expose secrets to
# untrusted code.
permissions: {}
jobs:
set-needs-review:
name: Set status to Needs Review
if: >-
(github.event_name == 'pull_request_target'
&& github.event.pull_request.state == 'open'
&& github.event.pull_request.draft != true
&& !contains(github.event.pull_request.labels.*.name, 'wip ⚒')
&& (github.event.action == 'synchronize' || github.event.action == 'review_requested'))
(github.event_name == 'pull_request_target' && github.event.action == 'synchronize')
|| (github.event_name == 'pull_request_target' && github.event.action == 'review_requested')
|| (github.event_name == 'issue_comment'
&& github.event.issue.pull_request
&& github.event.issue.state == 'open'
&& !contains(github.event.issue.labels.*.name, 'wip ⚒')
&& github.event.comment.user.login == github.event.issue.user.login)
runs-on: ubuntu-slim
permissions:
@@ -36,18 +27,8 @@ jobs:
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
org: electron
- name: Get project item status
uses: dsanders11/project-actions/get-item@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
id: get-item
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 118
fail-if-item-not-found: false
- name: Set status to Needs Review
if: >-
(steps.get-item.outputs.field-status == '🛑 Needs Submitter Response'
|| steps.get-item.outputs.field-status == '🟡 WIP')
uses: dsanders11/project-actions/edit-item@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 118

View File

@@ -4,10 +4,6 @@ on:
pull_request_target:
types: [labeled]
# SECURITY: This workflow uses pull_request_target and has access to secrets.
# Do NOT checkout or run code from the PR head. All code execution must use
# the base branch only. Adding a ref to PR head would expose secrets to
# untrusted code.
permissions: {}
jobs:
@@ -42,7 +38,7 @@ jobs:
creds: ${{ secrets.RELEASE_BOARD_GH_APP_CREDS }}
org: electron
- name: Set status
uses: dsanders11/project-actions/edit-item@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
token: ${{ steps.generate-token.outputs.token }}
project-number: 94
@@ -50,7 +46,7 @@ jobs:
field-value: ✅ Reviewed
pull-request-labeled-ai-pr:
name: ai-pr label added
if: github.event.label.name == 'ai-pr' && github.event.pull_request.state != 'closed'
if: github.event.label.name == 'ai-pr'
runs-on: ubuntu-latest
permissions: {}
steps:
@@ -60,7 +56,7 @@ jobs:
with:
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
- name: Create comment
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
with:
actions: 'create-comment'
token: ${{ steps.generate-token.outputs.token }}
@@ -72,7 +68,7 @@ jobs:
Hello @${{ github.event.pull_request.user.login }}. Due to the high amount of AI spam PRs we receive, if a PR is detected to be majority AI-generated without disclosure and untested, we will automatically close the PR.
We welcome the use of AI tools, as long as the PR meets our quality standards and has clearly been built and tested. If you believe your PR was closed in error, we welcome you to resubmit. However, please read our [CONTRIBUTING.md](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) and [AI Tool Policy](https://github.com/electron/governance/blob/main/policy/ai.md) carefully before reopening. Thanks for your contribution.
We welcome the use of AI tools, as long as the PR meets our quality standards and has clearly been built and tested. If you believe your PR was closed in error, we welcome you to resubmit. However, please read our [CONTRIBUTING.md](http://contributing.md/) carefully before reopening. Thanks for your contribution.
- name: Close the pull request
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}

View File

@@ -1,46 +0,0 @@
name: Pull Request Opened/Synchronized
on:
pull_request_target:
types: [opened, synchronize]
# SECURITY: This workflow uses pull_request_target and has access to secrets.
# Do NOT checkout or run code from the PR head. All code execution must use
# the base branch only. Adding a ref to PR head would expose secrets to
# untrusted code.
permissions: {}
jobs:
check-signed-commits:
name: Check signed commits in PR
runs-on: ubuntu-slim
permissions:
contents: read
pull-requests: write
steps:
- name: Check signed commits in PR
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
with:
comment: |
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
for all incoming PRs. To get your PR merged, please sign those commits
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
(`git push --force-with-lease`)
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
- name: Add needs-signed-commits label
if: ${{ failure() }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR_URL --add-label needs-signed-commits
- name: Remove needs-signed-commits label
if: ${{ success() && contains(github.event.pull_request.labels.*.name, 'needs-signed-commits') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR_URL --remove-label needs-signed-commits

View File

@@ -43,7 +43,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: SARIF file
path: results.sarif
@@ -51,6 +51,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v3.29.5
uses: github/codeql-action/upload-sarif@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v3.29.5
with:
sarif_file: results.sarif

View File

@@ -29,7 +29,7 @@ jobs:
PROJECT_NUMBER=$(gh project list --owner electron --format json | jq -r '.projects | map(select(.title | test("^[0-9]+-x-y$"))) | max_by(.number) | .number')
echo "PROJECT_NUMBER=$PROJECT_NUMBER" >> "$GITHUB_OUTPUT"
- name: Update Completed Stable Prep Items
uses: dsanders11/project-actions/completed-by@4b06452b0128cf601dac14399aa668a8eed2d684 # v2.0.1
uses: dsanders11/project-actions/completed-by@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
with:
field: Prep Status
field-value: ✅ Complete

View File

@@ -6,8 +6,8 @@ on:
build-image-sha:
type: string
description: 'SHA for electron/build image'
default: ''
required: false
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
required: true
upload-to-storage:
description: 'Uploads to Azure storage'
required: false
@@ -21,28 +21,13 @@ on:
permissions: {}
jobs:
setup:
if: github.repository == 'electron/electron'
runs-on: ubuntu-slim
permissions:
contents: read
outputs:
build-image-sha: ${{ steps.build-image-sha.outputs.build-image-sha }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set Build Image SHA
id: build-image-sha
uses: ./.github/actions/build-image-sha
with:
override: ${{ inputs.build-image-sha }}
checkout-windows:
needs: setup
if: github.repository == 'electron/electron'
runs-on: electron-arc-centralus-linux-amd64-32core
permissions:
contents: read
container:
image: ghcr.io/electron/build:${{ needs.setup.outputs.build-image-sha }}
image: ghcr.io/electron/build:${{ inputs.build-image-sha }}
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
volumes:
- /mnt/win-cache:/mnt/win-cache
@@ -52,6 +37,8 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_win=True'
TARGET_OS: 'win'
ELECTRON_DEPOT_TOOLS_WIN_TOOLCHAIN: '1'
outputs:
build-image-sha: ${{ inputs.build-image-sha }}
steps:
- name: Checkout Electron
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
@@ -64,14 +51,6 @@ jobs:
generate-sas-token: 'true'
target-platform: win
# Build the patched siso binary in parallel with checkout-windows; the
# publish-*-win jobs consume it via SISO_PATH.
build-siso-windows:
needs: setup
uses: ./.github/workflows/pipeline-segment-build-siso-windows.yml
permissions:
contents: read
publish-x64-win:
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
permissions:
@@ -79,7 +58,7 @@ jobs:
attestations: write
contents: read
id-token: write
needs: [checkout-windows, build-siso-windows]
needs: checkout-windows
with:
environment: production-release
build-runs-on: electron-arc-centralus-windows-amd64-16core
@@ -98,7 +77,7 @@ jobs:
attestations: write
contents: read
id-token: write
needs: [checkout-windows, build-siso-windows]
needs: checkout-windows
with:
environment: production-release
build-runs-on: electron-arc-centralus-windows-amd64-16core
@@ -117,7 +96,7 @@ jobs:
attestations: write
contents: read
id-token: write
needs: [checkout-windows, build-siso-windows]
needs: checkout-windows
with:
environment: production-release
build-runs-on: electron-arc-centralus-windows-amd64-16core

1
.gitignore vendored
View File

@@ -42,7 +42,6 @@ spec/.hash
# Generated native addon files
/spec/fixtures/native-addon/echo/build/
/spec/fixtures/native-addon/dialog-helper/build/
# If someone runs tsc this is where stuff will end up
ts-gen

View File

@@ -1,47 +0,0 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"printWidth": 120,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "none",
"sortImports": {
"newlinesBetween": true,
"groups": [
"electron-internal",
"electron-scoped",
"electron",
"external",
"builtin",
["sibling", "parent"],
"index",
"type",
"unknown"
],
"customGroups": [
{
"groupName": "electron-internal",
"elementNamePattern": ["@electron/internal", "@electron/internal/**"]
},
{
"groupName": "electron-scoped",
"elementNamePattern": ["@electron/**"]
},
{
"groupName": "electron",
"elementNamePattern": ["electron", "electron/**"]
}
]
},
"ignorePatterns": [
"node_modules",
"out",
"ts-gen",
"spec/node_modules",
"spec/fixtures/native-addon",
".github/workflows/node_modules",
"docs/fiddles",
"shell/browser/resources/win/resource.h",
"shell/common/node_includes.h",
"spec/fixtures/pages/jquery-3.6.0.min.js"
]
}

View File

@@ -1,329 +0,0 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": [
"typescript",
"import",
"node",
"promise",
"unicorn"
],
"jsPlugins": [
{
"name": "no-only-tests",
"specifier": "./script/lint-plugins/no-only-tests.mjs"
}
],
"categories": {
"correctness": "off"
},
"options": {
"typeAware": false
},
"env": {
"builtin": true,
"browser": true
},
"ignorePatterns": [
".github/workflows/node_modules",
"spec/node_modules",
"spec/fixtures/native-addon",
"shell/browser/resources/win/resource.h",
"shell/common/node_includes.h",
"spec/fixtures/pages/jquery-3.6.0.min.js"
],
"rules": {
"no-var": "error",
"accessor-pairs": [
"error",
{
"setWithoutGet": true,
"enforceForClassMembers": true
}
],
"array-callback-return": [
"error",
{
"allowImplicit": false,
"checkForEach": false
}
],
"constructor-super": "error",
"curly": [
"error",
"multi-line"
],
"default-case-last": "error",
"eqeqeq": [
"error",
"always",
{
"null": "ignore"
}
],
"new-cap": [
"error",
{
"newIsCap": true,
"capIsNew": false,
"properties": true
}
],
"no-array-constructor": "error",
"no-async-promise-executor": "error",
"no-caller": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-class-members": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-useless-backreference": "error",
"no-empty": [
"error",
{
"allowEmptyCatch": true
}
],
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-eval": "error",
"no-ex-assign": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-boolean-cast": "error",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-import-assign": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-iterator": "error",
"no-labels": [
"error",
{
"allowLoop": false,
"allowSwitch": false
}
],
"no-lone-blocks": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-prototype-builtins": "error",
"no-useless-catch": "error",
"no-useless-constructor": "error",
"no-use-before-define": [
"error",
{
"functions": false,
"classes": false,
"variables": false
}
],
"no-multi-str": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-obj-calls": "error",
"no-proto": "error",
"no-redeclare": [
"error"
],
"no-regex-spaces": "error",
"no-return-assign": [
"error",
"except-parens"
],
"no-self-assign": [
"error",
{
"props": true
}
],
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-template-curly-in-string": "error",
"no-this-before-super": "error",
"no-throw-literal": "error",
"no-unexpected-multiline": "error",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": [
"error",
{
"defaultAssignment": false
}
],
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unused-vars": [
"error",
{
"vars": "all",
"args": "after-used",
"argsIgnorePattern": "^_",
"ignoreRestSiblings": true
}
],
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-escape": "error",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-void": "error",
"no-with": "error",
"prefer-const": [
"error",
{
"destructuring": "all"
}
],
"prefer-promise-reject-errors": "error",
"symbol-description": "error",
"unicode-bom": [
"error",
"never"
],
"use-isnan": [
"error",
{
"enforceForSwitchCase": true,
"enforceForIndexOf": true
}
],
"valid-typeof": [
"error",
{
"requireStringLiterals": true
}
],
"yoda": [
"error",
"never"
],
"import/export": "error",
"import/first": "error",
"import/no-absolute-path": [
"error",
{
"esmodule": true,
"commonjs": true,
"amd": false
}
],
"import/no-duplicates": "error",
"import/no-named-default": "error",
"import/no-webpack-loader-syntax": "error",
"promise/param-names": "error",
"guard-for-in": "error",
"node/handle-callback-err": [
"error",
"^(err|error)$"
],
"node/no-exports-assign": "error",
"node/no-new-require": "error",
"node/no-path-concat": "error"
},
"overrides": [
{
"files": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
"rules": {
"no-use-before-define": "off"
}
},
{
"files": ["lib/browser/**", "lib/utility/**"],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": ["electron", "electron/renderer"],
"patterns": [
"./*",
"../*",
"@electron/internal/isolated_renderer/*",
"@electron/internal/renderer/*",
"@electron/internal/sandboxed_worker/*",
"@electron/internal/worker/*"
]
}
]
}
},
{
"files": [
"lib/renderer/**",
"lib/worker/**",
"lib/preload_realm/**",
"lib/sandboxed_renderer/**",
"lib/isolated_renderer/**"
],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": ["electron", "electron/main"],
"patterns": ["./*", "../*", "@electron/internal/browser/*"]
}
]
}
},
{
"files": ["lib/common/**"],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": ["electron", "electron/main", "electron/renderer"],
"patterns": [
"./*",
"../*",
"@electron/internal/browser/*",
"@electron/internal/isolated_renderer/*",
"@electron/internal/renderer/*",
"@electron/internal/sandboxed_worker/*",
"@electron/internal/worker/*"
]
}
]
}
},
{
"files": [
"build/**",
"script/**",
"docs/**",
"default_app/**",
"spec/**"
],
"rules": {
"unicorn/prefer-node-protocol": "error"
}
},
{
"files": ["spec/**/*.ts", "spec/**/*.js", "spec/**/*.mjs"],
"rules": {
"no-only-tests/no-only-tests": "error"
}
},
{
"files": ["**/*.d.ts"],
"rules": {
"no-useless-constructor": "off",
"no-unused-vars": "off"
}
}
]
}

View File

@@ -1,82 +0,0 @@
# Vendored Yarn release
This directory holds the Yarn release used by this repo (`yarnPath` in
`.yarnrc.yml`). The release file is checked in so every contributor and CI job
runs the exact same Yarn, and so we can carry small local patches when needed.
`releases/yarn-4.12.0.cjs` currently carries one such patch, described below.
If you bump the Yarn version, read the **Upgrading Yarn** section first.
## Patch: use `JsZipImpl` for the node-modules link step
### What changed
Two call sites in `releases/yarn-4.12.0.cjs` are modified so the
`node-modules` linker (and the `pnpm`-loose linker) construct their read-only
`ZipOpenFS` with `customZipImplementation: ST` — Yarn's pure-JS `JsZipImpl`
instead of falling through to the default WASM-backed `LibZipImpl`:
```text
new $f({maxOpenFiles:80,readOnlyArchives:!0})
→ new $f({maxOpenFiles:80,readOnlyArchives:!0,customZipImplementation:ST})
```
A comment block at the top of the `.cjs` file marks the file as patched and
points back here.
### Why
On the `linux-arm` CI test shards we run a 32-bit `arm32v7` container. During
`yarn install`'s **Link step**, Yarn opens up to 80 cache zips concurrently.
With `LibZipImpl`, each open zip is `readFileSync`'d into a Node `Buffer`
**and copied again into the WASM linear memory**, and every file read does a
WASM `_malloc(size)` for the entry. The WASM heap has to grow as a single
contiguous region of the 32-bit address space; once enough zips are resident,
the `_malloc` for a large entry — most often `typescript/lib/typescript.js`
(~9 MB inside a ~22 MB zip) — fails.
Yarn's cross-FS `copyFilePromise` swallows the underlying error and re-throws
a generic one, so CI shows:
```text
YN0001: While persisting .../typescript-patch-...zip/node_modules/typescript/
EINVAL: invalid argument, copyfile '/node_modules/typescript/lib/typescript.js' -> '...'
```
The unmasked form (occasionally seen on `pdfjs-dist`) is the WASM-heap failure
string `Couldn't allocate enough memory`. This started failing ~1-in-3
`linux-arm / test` shards at **Install Dependencies** on 2026-04-13, after
[#50692](https://github.com/electron/electron/pull/50692) grew the cache enough
to push the 32-bit process over the edge nondeterministically — e.g.
[run 24739817558](https://github.com/electron/electron/actions/runs/24739817558/job/72380803746).
`JsZipImpl` avoids the problem entirely: it opens the zip by file descriptor,
reads only the central directory into memory, and `readSync`s individual
entries into ordinary Node `Buffer`s — **no WASM heap involved**. It is
read-only and path-based, which is exactly how the linker uses these archives.
There is no `.yarnrc.yml` setting or environment variable to select the zip
implementation (verified against the bundle), so editing the vendored release
is the only way to switch it short of re-implementing the linker in a plugin.
Upstream references:
[yarnpkg/berry#3972](https://github.com/yarnpkg/berry/issues/3972),
[yarnpkg/berry#6722](https://github.com/yarnpkg/berry/issues/6722),
[yarnpkg/berry#6550](https://github.com/yarnpkg/berry/issues/6550).
### Upgrading Yarn
When bumping `releases/yarn-*.cjs`:
1. Check whether upstream now defaults `readOnlyArchives` opens to `JsZipImpl`,
or exposes a config knob for the zip implementation. If so, drop this patch.
2. Otherwise, re-apply: search the new bundle for
`maxOpenFiles:80,readOnlyArchives:!0` (the surrounding minified identifiers
will differ) and add `,customZipImplementation:<JsZipImpl symbol>` — that
symbol is whatever the new bundle exports as `JsZipImpl` from
`@yarnpkg/libzip`.
3. Re-add the header comment pointing back to this README.
4. Verify with
`rm -rf node_modules spec/node_modules && node script/yarn.js install --immutable --mode=skip-build`
and confirm `node_modules/typescript/lib/typescript.js` is byte-identical to
an unpatched install.

File diff suppressed because one or more lines are too long

View File

@@ -9,8 +9,4 @@ npmMinimalAgeGate: 10080
npmPreapprovedPackages:
- "@electron/*"
httpProxy: "${HTTP_PROXY:-}"
httpsProxy: "${HTTPS_PROXY:-}"
yarnPath: .yarn/releases/yarn-4.12.0.cjs

View File

@@ -105,25 +105,21 @@ electron_mac_bundle_id = branding.mac_bundle_id
if (override_electron_version != "") {
electron_version = override_electron_version
} else {
# When building from a source code tarball there is no git tag available and
# When building from source code tarball there is no git tag available and
# builders must explicitly pass override_electron_version in gn args.
#
# Resolve the real locations of packed-refs and HEAD via git so that this
# also works when electron/ is a `git worktree` (where .git is a file, not a
# directory, and GN's read_file cannot follow the gitdir indirection).
electron_git_ref_paths =
exec_script("script/get-git-ref-paths.py", [], "list lines")
# This read_file call will assert if there is no git information, without it
# gn will generate a malformed build configuration and ninja will get into
# infinite loop.
read_file(electron_git_ref_paths[0], "string")
read_file(".git/packed-refs", "string")
# Set electron version from git tag.
electron_version = exec_script("script/get-git-version.py",
[],
"trim string",
electron_git_ref_paths)
[
".git/packed-refs",
".git/HEAD",
])
}
if (is_mas_build) {
@@ -151,25 +147,6 @@ config("branding") {
config("electron_lib_config") {
include_dirs = [ "." ]
cflags = []
if (is_clang && clang_use_chrome_plugins) {
# The plugin is built directly into clang, so there's no need to load it
# dynamically.
cflags += [
"-Xclang",
"-add-plugin",
"-Xclang",
"blink-gc-plugin",
"-Xclang",
"-plugin-arg-blink-gc-plugin",
"-Xclang",
"check-directory=electron/shell/",
"-Xclang",
"-plugin-arg-blink-gc-plugin",
"-Xclang",
"check-directory=gin/",
]
}
}
# We generate the definitions twice here, once in //electron/electron.d.ts
@@ -499,10 +476,8 @@ source_set("electron_lib") {
"//components/certificate_transparency",
"//components/compose:buildflags",
"//components/embedder_support:user_agent",
"//components/heap_profiling/multi_process",
"//components/input",
"//components/language/core/browser",
"//components/memory_system",
"//components/net_log",
"//components/network_hints/browser",
"//components/network_hints/common:mojo_bindings",
@@ -515,7 +490,6 @@ source_set("electron_lib") {
"//components/pref_registry",
"//components/prefs",
"//components/security_state/content",
"//components/tracing:tracing_metrics",
"//components/upload_list",
"//components/user_prefs",
"//components/viz/host",
@@ -801,7 +775,6 @@ source_set("electron_lib") {
"//components/zoom",
"//extensions/browser",
"//extensions/browser/api:api_provider",
"//extensions/browser/mime_handler:stream_info",
"//extensions/browser/updater",
"//extensions/common",
"//extensions/common:core_api_provider",
@@ -1044,17 +1017,7 @@ if (is_mac) {
}
}
# Electron defines its own plugin helper (using CHILD_EMBEDDER_FIRST + 1) to
# allow loading of unsigned or third-party-signed libraries.
_electron_plugin_helper_params = [
"plugin",
".plugin",
" (Plugin)",
]
electron_mac_helpers =
content_mac_helpers + [ _electron_plugin_helper_params ]
foreach(helper_params, electron_mac_helpers) {
foreach(helper_params, content_mac_helpers) {
_helper_target = helper_params[0]
_helper_bundle_id = helper_params[1]
_helper_suffix = helper_params[2]
@@ -1107,7 +1070,7 @@ if (is_mac) {
":stripped_squirrel_framework",
]
foreach(helper_params, electron_mac_helpers) {
foreach(helper_params, content_mac_helpers) {
sources +=
[ "$root_out_dir/${electron_helper_name}${helper_params[2]}.app" ]
public_deps += [ ":electron_helper_app_${helper_params[0]}" ]
@@ -1211,7 +1174,7 @@ if (is_mac) {
deps = [ ":electron_framework" ]
}
foreach(helper_params, electron_mac_helpers) {
foreach(helper_params, content_mac_helpers) {
_helper_target = helper_params[0]
_helper_bundle_id = helper_params[1]
_helper_suffix = helper_params[2]
@@ -1263,7 +1226,7 @@ if (is_mac) {
deps += [ ":crashpad_handler_syms" ]
}
foreach(helper_params, electron_mac_helpers) {
foreach(helper_params, content_mac_helpers) {
_helper_target = helper_params[0]
deps += [ ":electron_helper_syms_${_helper_target}" ]
}
@@ -1670,7 +1633,6 @@ action("node_version_header") {
action("generate_node_headers") {
deps = [ ":generate_config_gypi" ]
script = "script/node/generate_node_headers.py"
args = [ rebase_path("$root_gen_dir") ]
outputs = [ "$root_gen_dir/node_headers.json" ]
}

View File

@@ -1,14 +1,5 @@
# Electron Development Guide
## Running node_modules binaries
**Never use `npx`.** It is considered dangerous because it can silently fetch and execute arbitrary packages from the registry. Always run binaries through one of these safer mechanisms instead:
1. **Preferred** — spawn the executable directly from `node_modules/.bin/<tool>` (or the platform equivalent on Windows). This is what `script/lint.js` does for `oxlint`.
2. **Acceptable** — invoke via `yarn <tool>` or `yarn run <tool>`, which resolves to the locally installed version without the registry fallback that `npx` performs.
This rule applies to shell commands you run yourself and to any scripts you author or modify in this repo.
## Project Overview
Electron is a framework for building cross-platform desktop applications using web technologies. It embeds Chromium for rendering and Node.js for backend functionality.
@@ -180,10 +171,6 @@ e test # Run full test suite
When working on the `roller/chromium/main` branch to upgrade Chromium activate the "Electron Chromium Upgrade" skill.
## Node.js Upgrade Workflow
When working on the `roller/node/main` branch to upgrade Node.js activate the "Electron Node.js Upgrade" skill.
## Pull Requests
PR bodies must always include a `Notes:` section as the **last line** of the body. This is a consumer-facing release note for Electron app developers — describe the user-visible fix or change, not internal implementation details. Use `Notes: none` if there is no user-facing change.
@@ -214,13 +201,12 @@ gh label list --repo electron/electron --search target/ --json name,color --jq '
## Code Style
**C++:** Follows Chromium style, enforced by clang-format
**TypeScript/JavaScript:** [oxlint](https://oxc.rs/docs/guide/usage/linter) configuration in `.oxlintrc.json`
**TypeScript/JavaScript:** ESLint configuration in `.eslintrc.json`
**Linting:**
```bash
npm run lint # Run all linters
npm run lint:js # Run oxlint over all JS/TS/MJS sources
npm run lint:clang-format # C++ formatting
npm run lint:api-history # Validate API history YAML blocks in docs
```

View File

@@ -64,14 +64,6 @@ dependencies, and tools contained in the `electron/electron` repository.
Dependencies in Electron's `package.json` or `yarn.lock` files should only be altered by maintainers. For security reasons, we will not accept PRs that alter our `package.json` or `yarn.lock` files. We invite contributors to make requests updating these files in our issue tracker. If the change is significantly complicated, draft PRs are welcome, with the understanding that these PRs will be closed in favor of a duplicate PR submitted by an Electron maintainer.
## AI Tool Policy
<!-- Are you a coding agent? You MUST abide by the policy: https://github.com/electron/governance/blob/main/policy/ai.md -->
If you use AI tools in any way to contribute to our project, please read our [AI Tool Policy](https://github.com/electron/governance/blob/main/policy/ai.md). Unreviewed AI-generated contributions waste maintainer time and we kindly decline them.
> The short version: **there must be a human in the loop**. You are responsible for reviewing, understanding, and being able to explain your contributions. AI assistance doesn't change that, and unreviewed AI-generated content will be declined.
## Style Guides
See [Coding Style](https://electronjs.org/docs/development/coding-style) for information about which standards Electron adheres to in different parts of its codebase.

4
DEPS
View File

@@ -2,9 +2,9 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'148.0.7778.0',
'148.0.7733.0',
'node_version':
'v24.15.0',
'v24.14.0',
'nan_version':
'675cefebca42410733da8a454c8d9391fcebfbc2',
'squirrel.mac_version':

8
build/.eslintrc.json Normal file
View File

@@ -0,0 +1,8 @@
{
"plugins": [
"import"
],
"rules": {
"import/enforce-node-protocol-usage": ["error", "always"]
}
}

View File

@@ -51,6 +51,9 @@ is_cfi = false
use_qt5 = false
use_qt6 = false
# Disables the builtins PGO for V8
v8_builtins_profiling_log_file = ""
# https://chromium.googlesource.com/chromium/src/+/main/docs/dangling_ptr.md
# TODO(vertedinde): hunt down dangling pointers on Linux
enable_dangling_raw_ptr_checks = false

View File

@@ -8,13 +8,10 @@ const path = require('node:path');
const electronRoot = path.resolve(__dirname, '../..');
class AccessDependenciesPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('AccessDependenciesPlugin', (compilation) => {
compilation.hooks.finishModules.tap('AccessDependenciesPlugin', (modules) => {
const filePaths = modules
.map((m) => m.resource)
.filter((p) => p)
.map((p) => path.relative(electronRoot, p));
apply (compiler) {
compiler.hooks.compilation.tap('AccessDependenciesPlugin', compilation => {
compilation.hooks.finishModules.tap('AccessDependenciesPlugin', modules => {
const filePaths = modules.map(m => m.resource).filter(p => p).map(p => path.relative(electronRoot, p));
console.info(JSON.stringify(filePaths));
});
});
@@ -34,14 +31,7 @@ module.exports = ({
entry = path.resolve(electronRoot, 'lib', target, 'init.js');
}
const electronAPIFile = path.resolve(
electronRoot,
'lib',
loadElectronFromAlternateTarget || target,
'api',
'exports',
'electron.ts'
);
const electronAPIFile = path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts');
return (env = {}, argv = {}) => {
const onlyPrintingGraph = !!env.PRINT_WEBPACK_GRAPH;
@@ -71,59 +61,49 @@ module.exports = ({
}
if (targetDeletesNodeGlobals) {
plugins.push(
new webpack.ProvidePlugin({
Buffer: ['@electron/internal/common/webpack-provider', 'Buffer'],
global: ['@electron/internal/common/webpack-provider', '_global'],
process: ['@electron/internal/common/webpack-provider', 'process']
})
);
plugins.push(new webpack.ProvidePlugin({
Buffer: ['@electron/internal/common/webpack-provider', 'Buffer'],
global: ['@electron/internal/common/webpack-provider', '_global'],
process: ['@electron/internal/common/webpack-provider', 'process']
}));
}
// Webpack 5 no longer polyfills process or Buffer.
if (!alwaysHasNode) {
plugins.push(
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser'
})
);
plugins.push(new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser'
}));
}
plugins.push(
new webpack.ProvidePlugin({
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise']
})
);
plugins.push(new webpack.ProvidePlugin({
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise']
}));
plugins.push(new webpack.DefinePlugin(defines));
if (wrapInitWithProfilingTimeout) {
plugins.push(
new WrapperPlugin({
header: 'function ___electron_webpack_init__() {',
footer: `
plugins.push(new WrapperPlugin({
header: 'function ___electron_webpack_init__() {',
footer: `
};
if ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) {
setTimeout(___electron_webpack_init__, 0);
} else {
___electron_webpack_init__();
}`
})
);
}));
}
if (wrapInitWithTryCatch) {
plugins.push(
new WrapperPlugin({
header: 'try {',
footer: `
plugins.push(new WrapperPlugin({
header: 'try {',
footer: `
} catch (err) {
console.error('Electron ${outputFilename} script failed to run');
console.error(err);
}`
})
);
}));
}
return {
@@ -153,26 +133,23 @@ if ((globalThis.process || binding.process).argv.includes("--profile-electron-in
}
},
module: {
rules: [
{
test: (moduleName) => !onlyPrintingGraph && ignoredModules.includes(moduleName),
loader: 'null-loader'
},
{
test: /\.ts$/,
loader: 'ts-loader',
options: {
configFile: path.resolve(electronRoot, 'tsconfig.electron.json'),
transpileOnly: onlyPrintingGraph,
ignoreDiagnostics: [
// File '{0}' is not under 'rootDir' '{1}'.
6059,
// Private field '{0}' must be declared in an enclosing class.
1111
]
}
rules: [{
test: (moduleName) => !onlyPrintingGraph && ignoredModules.includes(moduleName),
loader: 'null-loader'
}, {
test: /\.ts$/,
loader: 'ts-loader',
options: {
configFile: path.resolve(electronRoot, 'tsconfig.electron.json'),
transpileOnly: onlyPrintingGraph,
ignoreDiagnostics: [
// File '{0}' is not under 'rootDir' '{1}'.
6059,
// Private field '{0}' must be declared in an enclosing class.
1111
]
}
]
}]
},
node: {
__dirname: false,

View File

@@ -144,8 +144,6 @@ static_library("chrome") {
"//chrome/browser/ui/views/overlay/toggle_camera_button.h",
"//chrome/browser/ui/views/overlay/toggle_microphone_button.cc",
"//chrome/browser/ui/views/overlay/toggle_microphone_button.h",
"//chrome/browser/ui/views/overlay/toggle_mute_button.cc",
"//chrome/browser/ui/views/overlay/toggle_mute_button.h",
"//chrome/browser/ui/views/overlay/video_overlay_window_views.cc",
"//chrome/browser/ui/views/overlay/video_overlay_window_views.h",
"//chrome/browser/ui/views/picture_in_picture/picture_in_picture_bounds_change_animation.cc",

View File

@@ -0,0 +1,8 @@
{
"plugins": [
"import"
],
"rules": {
"import/enforce-node-protocol-usage": ["error", "always"]
}
}

View File

@@ -1,5 +1,5 @@
import { shell } from 'electron/common';
import { app, dialog, BrowserWindow, ipcMain, Menu } from 'electron/main';
import { app, dialog, BrowserWindow, ipcMain } from 'electron/main';
import * as path from 'node:path';
import * as url from 'node:url';
@@ -11,62 +11,23 @@ app.on('window-all-closed', () => {
app.quit();
});
const isMac = process.platform === 'darwin';
app.whenReady().then(() => {
const helpMenu: Electron.MenuItemConstructorOptions = {
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
await shell.openExternal('https://electronjs.org');
}
},
{
label: 'Documentation',
click: async () => {
const version = process.versions.electron;
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
}
},
{
label: 'Community Discussions',
click: async () => {
await shell.openExternal('https://discord.gg/electronjs');
}
},
{
label: 'Search Issues',
click: async () => {
await shell.openExternal('https://github.com/electron/electron/issues');
}
}
]
};
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
const template: Electron.MenuItemConstructorOptions[] = [
...(isMac ? [macAppMenu] : []),
{ role: 'fileMenu' },
{ role: 'editMenu' },
{ role: 'viewMenu' },
{ role: 'windowMenu' },
helpMenu
];
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
});
function decorateURL (url: string) {
// safely add `?utm_source=default_app
const parsedUrl = new URL(url);
parsedUrl.searchParams.append('utm_source', 'default_app');
return parsedUrl.toString();
}
// Find the shortest path to the electron binary
const absoluteElectronPath = process.execPath;
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath);
const electronPath =
absoluteElectronPath.length < relativeElectronPath.length ? absoluteElectronPath : relativeElectronPath;
const electronPath = absoluteElectronPath.length < relativeElectronPath.length
? absoluteElectronPath
: relativeElectronPath;
const indexPath = path.resolve(app.getAppPath(), 'index.html');
function isTrustedSender(webContents: Electron.WebContents) {
function isTrustedSender (webContents: Electron.WebContents) {
if (webContents !== (mainWindow && mainWindow.webContents)) {
return false;
}
@@ -82,7 +43,7 @@ ipcMain.handle('bootstrap', (event) => {
return isTrustedSender(event.sender) ? electronPath : null;
});
async function createWindow(backgroundColor?: string) {
async function createWindow (backgroundColor?: string) {
await app.whenReady();
const options: Electron.BrowserWindowConstructorOptions = {
@@ -107,8 +68,8 @@ async function createWindow(backgroundColor?: string) {
mainWindow = new BrowserWindow(options);
mainWindow.on('ready-to-show', () => mainWindow!.show());
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url);
mainWindow.webContents.setWindowOpenHandler(details => {
shell.openExternal(decorateURL(details.url));
return { action: 'deny' };
});

View File

@@ -15,7 +15,7 @@ type DefaultAppOptions = {
interactive: boolean;
abi: boolean;
modules: string[];
};
}
// Parse command line options.
const argv = process.argv.slice(1);
@@ -74,7 +74,7 @@ if (option.modules.length > 0) {
(Module as any)._preloadModules(option.modules);
}
async function loadApplicationPackage(packagePath: string) {
async function loadApplicationPackage (packagePath: string) {
// Add a flag indicating app is started from default app.
Object.defineProperty(process, 'defaultApp', {
configurable: false,
@@ -92,11 +92,9 @@ async function loadApplicationPackage(packagePath: string) {
const emitWarning = process.emitWarning;
try {
process.emitWarning = () => {};
packageJson = (
await import(url.pathToFileURL(packageJsonPath).toString(), {
with: { type: 'json' }
})
).default;
packageJson = (await import(url.pathToFileURL(packageJsonPath).toString(), {
with: { type: 'json' }
})).default;
} catch (e) {
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${(e as Error).message}`);
return;
@@ -145,23 +143,23 @@ async function loadApplicationPackage(packagePath: string) {
}
}
function showErrorMessage(message: string) {
function showErrorMessage (message: string) {
app.focus();
dialog.showErrorBox('Error launching app', message);
process.exit(1);
}
async function loadApplicationByURL(appUrl: string) {
async function loadApplicationByURL (appUrl: string) {
const { loadURL } = await import('./default_app.js');
loadURL(appUrl);
}
async function loadApplicationByFile(appPath: string) {
async function loadApplicationByFile (appPath: string) {
const { loadFile } = await import('./default_app.js');
loadFile(appPath);
}
async function startRepl() {
async function startRepl () {
if (process.platform === 'win32') {
console.error('Electron REPL not currently supported on Windows');
process.exit(1);
@@ -189,7 +187,7 @@ async function startRepl() {
process.exit(0);
});
function defineBuiltin(context: any, name: string, getter: Function) {
function defineBuiltin (context: any, name: string, getter: Function) {
const setReal = (val: any) => {
// Deleting the property before re-assigning it disables the
// getter/setter mechanism.
@@ -227,42 +225,11 @@ async function startRepl() {
// we only trigger custom tab-completion when no common words are
// potentially matches.
const commonWords = [
'async',
'await',
'break',
'case',
'catch',
'const',
'continue',
'debugger',
'default',
'delete',
'do',
'else',
'export',
'false',
'finally',
'for',
'function',
'if',
'import',
'in',
'instanceof',
'let',
'new',
'null',
'return',
'switch',
'this',
'throw',
'true',
'try',
'typeof',
'var',
'void',
'while',
'with',
'yield'
'async', 'await', 'break', 'case', 'catch', 'const', 'continue',
'debugger', 'default', 'delete', 'do', 'else', 'export', 'false',
'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'let',
'new', 'null', 'return', 'switch', 'this', 'throw', 'true', 'try',
'typeof', 'var', 'void', 'while', 'with', 'yield'
];
const electronBuiltins = [...Object.keys(electron), 'original-fs', 'electron'];

View File

@@ -2,10 +2,10 @@ const { ipcRenderer, contextBridge } = require('electron/renderer');
const policy = window.trustedTypes.createPolicy('electron-default-app', {
// we trust the SVG contents
createHTML: (input) => input
createHTML: input => input
});
async function getOcticonSvg(name: string) {
async function getOcticonSvg (name: string) {
try {
const response = await fetch(`octicon/${name}.svg`);
const div = document.createElement('div');
@@ -16,7 +16,7 @@ async function getOcticonSvg(name: string) {
}
}
async function loadSVG(element: HTMLSpanElement) {
async function loadSVG (element: HTMLSpanElement) {
for (const cssClass of element.classList) {
if (cssClass.startsWith('octicon-')) {
const icon = await getOcticonSvg(cssClass.substr(8));
@@ -32,9 +32,9 @@ async function loadSVG(element: HTMLSpanElement) {
}
}
async function initialize() {
async function initialize () {
const electronPath = await ipcRenderer.invoke('bootstrap');
function replaceText(selector: string, text: string, link?: string) {
function replaceText (selector: string, text: string, link?: string) {
const element = document.querySelector<HTMLElement>(selector);
if (element) {
if (link) {
@@ -51,11 +51,7 @@ async function initialize() {
replaceText('.electron-version', `Electron v${process.versions.electron}`, 'https://electronjs.org/docs');
replaceText('.chrome-version', `Chromium v${process.versions.chrome}`, 'https://developer.chrome.com/docs/chromium');
replaceText(
'.node-version',
`Node v${process.versions.node}`,
`https://nodejs.org/docs/v${process.versions.node}/api`
);
replaceText('.node-version', `Node v${process.versions.node}`, `https://nodejs.org/docs/v${process.versions.node}/api`);
replaceText('.v8-version', `v8 v${process.versions.v8}`, 'https://v8.dev/docs');
replaceText('.command-example', `${electronPath} path-to-app`);

35
docs/.eslintrc.json Normal file
View File

@@ -0,0 +1,35 @@
{
"extends": "standard",
"plugins": [
"import",
"markdown"
],
"overrides": [
{
"files": ["*.md", "**/*.md"],
"processor": "markdown/markdown"
}
],
"rules": {
"@typescript-eslint/no-unused-vars": "off",
"import/order": ["error", {
"alphabetize": {
"order": "asc"
},
"newlines-between": "always",
"pathGroups": [
{
"pattern": "{electron,electron/**}",
"group": "builtin",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": []
}],
"n/no-callback-literal": "off",
"no-undef": "off",
"no-unused-expressions": "off",
"no-unused-vars": "off",
"import/enforce-node-protocol-usage": ["error", "always"]
}
}

View File

@@ -615,11 +615,7 @@ Returns `string` - The current application directory.
by default is the `appData` directory appended with your app's name. By
convention files storing user data should be written to this directory, and
it is not recommended to write large files here because some environments
may backup this directory to cloud storage. It is recommended to store
app-specific files within a subdirectory of `userData` (e.g.,
`path.join(app.getPath('userData'), 'my-app-data')`) rather than directly
in `userData` itself, to avoid naming conflicts with Chromium's own
subdirectories (such as `Cache`, `GPUCache`, and `Local Storage`).
may backup this directory to cloud storage.
* `sessionData` The directory for storing data generated by `Session`, such
as localStorage, cookies, disk cache, downloaded dictionaries, network
state, DevTools files. By default this points to `userData`. Chromium may

View File

@@ -124,65 +124,4 @@ Returns `Promise<Object>` - Resolves with an object containing the `value` and `
Get the maximum usage across processes of trace buffer as a percentage of the
full state.
### `contentTracing.enableHeapProfiling([options])` _Experimental_
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/50826
```
-->
* `options` ([EnableHeapProfilingOptions](structures/enable-heap-profiling-options.md)) (optional)
Returns `Promise<void>` - Resolves once heap profiling has been enabled.
Enable [heap profiling](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/memory-infra/heap_profiler.md)
for MemoryInfra traces. Equivalent to the `--memlog` switch in Chrome.
Only takes effect if the `disabled-by-default-memory-infra` category is included.
Needs to be called before `contentTracing.startRecording()`.
Usage:
```js
const { contentTracing } = require('electron')
async function recordTrace () {
await contentTracing.enableHeapProfiling()
await contentTracing.startRecording({
included_categories: ['disabled-by-default-memory-infra'],
excluded_categories: ['*'],
memory_dump_config: {
triggers: [
{ mode: 'detailed', periodic_interval_ms: 1000 }
]
}
})
await new Promise(resolve => setTimeout(resolve, 5000))
const filePath = await contentTracing.stopRecording()
}
```
To view the recorded heap dumps:
1. Download the breakpad symbols for your Electron version from the Electron GitHub
[releases](https://github.com/electron/electron/releases)
2. Clone the [Electron source code](../development/build-instructions-gn.md)
3. In your Chromium checkout for Electron, run this command to symbolicate the heap dump:
```bash
python3 third_party/catapult/tracing/bin/symbolize_trace --use-breakpad-symbols --breakpad-symbols-directory /path/to/breakpad_symbols /path/to/trace.json
```
4. Open the symbolicated trace in `chrome://tracing` (the Perfetto UI does not support memory dumps
yet)
5. Click on one of the `M` symbols
6. Click on a `` triple bar icon (e.g., in the `malloc` column)
<img src="../images/viewing-heap-dumps.png" alt="Screenshot showing how to view a heapdump in Chromium's tracing view" />
[trace viewer]: https://chromium.googlesource.com/catapult/+/HEAD/tracing/README.md

View File

@@ -28,10 +28,7 @@ added:
* `window` [BaseWindow](base-window.md) (optional)
* `options` Object
* `title` string (optional)
* `defaultPath` string (optional) - Absolute directory path, absolute file
path, or file name to use by default. If not provided, the dialog will
default to the user's Downloads folder, or their home directory if Downloads
doesn't exist.
* `defaultPath` string (optional)
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
left empty the default label will be used.
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
@@ -112,10 +109,7 @@ changes:
* `window` [BaseWindow](base-window.md) (optional)
* `options` Object
* `title` string (optional)
* `defaultPath` string (optional) - Absolute directory path, absolute file
path, or file name to use by default. If not provided, the dialog will
default to the user's Downloads folder, or their home directory if Downloads
doesn't exist.
* `defaultPath` string (optional)
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
left empty the default label will be used.
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
@@ -204,9 +198,7 @@ added:
* `options` Object
* `title` string (optional) - The dialog title. Cannot be displayed on some _Linux_ desktop environments.
* `defaultPath` string (optional) - Absolute directory path, absolute file
path, or file name to use by default. If not provided, the dialog will
default to the user's Downloads folder, or their home directory if Downloads
doesn't exist.
path, or file name to use by default.
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
left empty the default label will be used.
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
@@ -246,9 +238,7 @@ changes:
* `options` Object
* `title` string (optional) - The dialog title. Cannot be displayed on some _Linux_ desktop environments.
* `defaultPath` string (optional) - Absolute directory path, absolute file
path, or file name to use by default. If not provided, the dialog will
default to the user's Downloads folder, or their home directory if Downloads
doesn't exist.
path, or file name to use by default.
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
left empty the default label will be used.
* `filters` [FileFilter[]](structures/file-filter.md) (optional)

View File

@@ -56,9 +56,6 @@ Returns `string` - The badge string of the dock.
Hides the dock icon.
> [!IMPORTANT]
> **Known issue:** Calling `dock.hide()` within one second of a previous call will have no effect. As a workaround, ensure at least one second has elapsed between calls — for example, by deferring with a `setTimeout` of 1100ms or more after a previous call.
#### `dock.show()` _macOS_
Returns `Promise<void>` - Resolves when the dock icon is shown.

View File

@@ -203,22 +203,13 @@ the one downloaded by `npm install`. Usage:
export ELECTRON_OVERRIDE_DIST_PATH=/Users/username/projects/electron/out/Testing
```
### `ELECTRON_INSTALL_PLATFORM`
### `ELECTRON_SKIP_BINARY_DOWNLOAD`
Manually overrides platform used by `electron` package during an install.
This can be useful if you are on one platform (e.g macOS) but want to
download binaries for another platform (e.g Windows or Linux). Usage:
If you want to install your project's dependencies but don't need to use Electron functionality,
you can set the `ELECTRON_SKIP_BINARY_DOWNLOAD` environment variable to prevent the binary from being
downloaded. For instance, this feature can be useful in continuous integration environments when
running unit tests that mock out the `electron` module.
```sh
ELECTRON_INSTALL_PLATFORM=darwin npm install
```
### `ELECTRON_INSTALL_ARCH`
Manually overrides architecture used by `electron` package during an install.
This can be useful if you are on one arch (e.g `arm64`) but want to download
binaries meant for another arch. Note that this will not work under Rosetta. Usage:
```sh
ELECTRON_INSTALL_ARCH=arm64 npm install
ELECTRON_SKIP_BINARY_DOWNLOAD=1 npm install
```

View File

@@ -148,34 +148,3 @@ added:
-->
Unregisters all of the global shortcuts.
### `globalShortcut.setSuspended(suspended)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/50425
```
-->
* `suspended` boolean - Whether global shortcut handling should be suspended.
Suspends or resumes global shortcut handling. When suspended, all registered
global shortcuts will stop listening for key presses. When resumed, all
previously registered shortcuts will begin listening again. New shortcut
registrations will fail while handling is suspended.
This can be useful when you want to temporarily allow the user to press key
combinations without your application intercepting them, for example while
displaying a UI to rebind shortcuts.
### `globalShortcut.isSuspended()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/50425
```
-->
Returns `boolean` - Whether global shortcut handling is currently suspended.

View File

@@ -44,8 +44,8 @@ See [`Menu`](menu.md) for examples.
menu items.
* `registerAccelerator` boolean (optional) _Linux_ _Windows_ - If false, the accelerator won't be registered
with the system, but it will still be displayed. Defaults to true.
* `sharingItem` [SharingItem](structures/sharing-item.md) (optional) _macOS_ - The item to share when the `role` is `shareMenu`.
* `submenu` ([MenuItemConstructorOptions](#new-menuitemoptions)[] | [Menu](menu.md)) (optional) - Should be specified
* `sharingItem` SharingItem (optional) _macOS_ - The item to share when the `role` is `shareMenu`.
* `submenu` (MenuItemConstructorOptions[] | [Menu](menu.md)) (optional) - Should be specified
for `submenu` type menu items. If `submenu` is specified, the `type: 'submenu'` can be omitted.
If the value is not a [`Menu`](menu.md) then it will be automatically converted to one using
`Menu.buildFromTemplate`.
@@ -89,7 +89,7 @@ A `Function` that is fired when the MenuItem receives a click event.
It can be called with `menuItem.click(event, focusedWindow, focusedWebContents)`.
* `event` [KeyboardEvent](structures/keyboard-event.md)
* `focusedWindow` [BaseWindow](base-window.md)
* `focusedWindow` [BaseWindow](browser-window.md)
* `focusedWebContents` [WebContents](web-contents.md)
#### `menuItem.submenu`
@@ -110,11 +110,11 @@ A `string` (optional) indicating the item's role, if set. Can be `undo`, `redo`,
#### `menuItem.accelerator`
An [`Accelerator | null`](../tutorial/keyboard-shortcuts.md#accelerators) indicating the item's accelerator, if set.
An `Accelerator | null` indicating the item's accelerator, if set.
#### `menuItem.userAccelerator` _Readonly_ _macOS_
An [`Accelerator | null`](../tutorial/keyboard-shortcuts.md#accelerators) indicating the item's [user-assigned accelerator](https://developer.apple.com/documentation/appkit/nsmenuitem/1514850-userkeyequivalent?language=objc) for the menu item.
An `Accelerator | null` indicating the item's [user-assigned accelerator](https://developer.apple.com/documentation/appkit/nsmenuitem/1514850-userkeyequivalent?language=objc) for the menu item.
> [!NOTE]
> This property is only initialized after the `MenuItem` has been added to a `Menu`. Either via `Menu.buildFromTemplate` or via `Menu.append()/insert()`. Accessing before initialization will just return `null`.
@@ -170,7 +170,7 @@ This property can be dynamically changed.
#### `menuItem.sharingItem` _macOS_
A [`SharingItem`](structures/sharing-item.md) indicating the item to share when the `role` is `shareMenu`.
A `SharingItem` indicating the item to share when the `role` is `shareMenu`.
This property can be dynamically changed.

View File

@@ -46,7 +46,7 @@ this has the additional effect of removing the menu bar from the window.
> [!NOTE]
> The default menu will be created automatically if the app does not set one.
> It contains standard items such as `File`, `Edit`, `View`, and `Window`.
> It contains standard items such as `File`, `Edit`, `View`, `Window` and `Help`.
#### `Menu.getApplicationMenu()`
@@ -70,7 +70,7 @@ for more information on macOS' native actions.
#### `Menu.buildFromTemplate(template)`
- `template` ([MenuItemConstructorOptions](menu-item.md#new-menuitemoptions) | [MenuItem](menu-item.md))[]
- `template` (MenuItemConstructorOptions | [MenuItem](menu-item.md))[]
Returns [`Menu`](menu.md)
@@ -162,7 +162,7 @@ Emitted when a popup is closed either manually or with `menu.closePopup()`.
#### `menu.items`
A [`MenuItem[]`](menu-item.md) array containing the menu's items.
A `MenuItem[]` array containing the menu's items.
Each `Menu` consists of multiple [`MenuItem`](menu-item.md) instances and each `MenuItem`
can nest a `Menu` into its `submenu` property.

View File

@@ -84,7 +84,3 @@ Currently, Windows high contrast is the only system setting that triggers forced
### `nativeTheme.prefersReducedTransparency` _Readonly_
A `boolean` that indicates whether the user has chosen via system accessibility settings to reduce transparency at the OS level.
### `nativeTheme.shouldDifferentiateWithoutColor` _macOS_ _Readonly_
A `boolean` that indicates whether the user prefers UI that differentiates items using something other than color alone (e.g. shapes or labels). This maps to [NSWorkspace.accessibilityDisplayShouldDifferentiateWithoutColor](https://developer.apple.com/documentation/appkit/nsworkspace/accessibilitydisplayshoulddifferentiatewithoutcolor).

View File

@@ -76,69 +76,25 @@ app.whenReady().then(() => {
})
```
#### `Notification.getHistory()` _macOS_
Returns `Promise<Notification[]>` - Resolves with an array of `Notification` objects representing all delivered notifications still present in Notification Center.
Each returned `Notification` is a live object connected to the corresponding delivered notification. Interaction events (`click`, `reply`, `action`, `close`) will fire on these objects when the user interacts with the notification in Notification Center. This is useful after an app restart to re-attach event handlers to notifications from a previous session.
The returned notifications have their `id`, `groupId`, `title`, `subtitle`, and `body` properties populated from information available in the Notification Center. Other properties (e.g., `actions`, `silent`, `icon`) are not available from delivered notifications and will have default values.
> [!NOTE]
> Like all macOS notification APIs, this method requires the application to be
> code-signed. In unsigned development builds, notifications are not delivered
> to Notification Center and this method will resolve with an empty array.
> [!NOTE]
> Unlike notifications created with `new Notification()`, notifications returned
> by `getHistory()` will remain visible in Notification Center when the object
> is garbage collected. Calling `show()` on a restored notification will remove
> the original from Notification Center and post a new one with the same
> properties.
```js
const { Notification, app } = require('electron')
app.whenReady().then(async () => {
// Restore notifications from a previous session
const notifications = await Notification.getHistory()
for (const n of notifications) {
console.log(`Found delivered notification: ${n.id} - ${n.title}`)
n.on('click', () => {
console.log(`User clicked: ${n.id}`)
})
n.on('reply', (event) => {
console.log(`User replied to ${n.id}: ${event.reply}`)
})
}
// Keep references so events continue to fire
})
```
### `new Notification([options])`
* `options` Object (optional)
* `id` string (optional) _macOS_ _Windows_ - A unique identifier for the notification. On macOS, maps to `UNNotificationRequest`'s [`identifier`](https://developer.apple.com/documentation/usernotifications/unnotificationrequest/identifier) property. On Windows, maps to the toast notification's [`Tag`](https://learn.microsoft.com/en-us/uwp/api/windows.ui.notifications.toastnotification.tag) property. Defaults to a random UUID if not provided or if an empty string is passed. This can be used to remove or update previously delivered notifications.
* `groupId` string (optional) _macOS_ _Windows_ - A string identifier used to visually group notifications together in Notification Center / Action Center. On macOS, maps to `UNNotificationContent`'s [`threadIdentifier`](https://developer.apple.com/documentation/usernotifications/unnotificationcontent/threadidentifier) property. On Windows, maps to the toast notification's [`Group`](https://learn.microsoft.com/en-us/uwp/api/windows.ui.notifications.toastnotification.group) property.
* `groupTitle` string (optional) _Windows_ - A title for the notification group header. When both `groupId` and `groupTitle` are specified, Windows will display a header above the notification that groups related notifications together. Maps to the toast notification's [`header`](https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-headers) element.
* `id` string (optional) _macOS_ - A unique identifier for the notification, mapping to `UNNotificationRequest`'s [`identifier`](https://developer.apple.com/documentation/usernotifications/unnotificationrequest/identifier) property. Defaults to a random UUID if not provided or if an empty string is passed. This can be used to remove or update previously delivered notifications.
* `groupId` string (optional) _macOS_ - A string identifier used to visually group notifications together in Notification Center. Maps to `UNNotificationContent`'s [`threadIdentifier`](https://developer.apple.com/documentation/usernotifications/unnotificationcontent/threadidentifier) property.
* `title` string (optional) - A title for the notification, which will be displayed at the top of the notification window when it is shown.
* `subtitle` string (optional) _macOS_ - A subtitle for the notification, which will be displayed below the title.
* `body` string (optional) - The body text of the notification, which will be displayed below the title or subtitle.
* `silent` boolean (optional) - Whether or not to suppress the OS notification noise when showing the notification.
* `icon` (string | [NativeImage](native-image.md)) (optional) - An icon to use in the notification. If a string is passed, it must be a valid path to a local icon file.
* `hasReply` boolean (optional) _macOS_ _Windows_ - Whether or not to add an inline reply option to the notification.
* `hasReply` boolean (optional) _macOS_ - Whether or not to add an inline reply option to the notification.
* `timeoutType` string (optional) _Linux_ _Windows_ - The timeout duration of the notification. Can be 'default' or 'never'.
* `replyPlaceholder` string (optional) _macOS_ _Windows_ - The placeholder to write in the inline reply input field.
* `replyPlaceholder` string (optional) _macOS_ - The placeholder to write in the inline reply input field.
* `sound` string (optional) _macOS_ - The name of the sound file to play when the notification is shown.
* `urgency` string (optional) _Linux_ _Windows_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
* `actions` [NotificationAction[]](structures/notification-action.md) (optional) _macOS_ _Windows_ - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation.
* `urgency` string (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
* `actions` [NotificationAction[]](structures/notification-action.md) (optional) _macOS_ - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation.
* `closeButtonText` string (optional) _macOS_ - A custom title for the close button of an alert. An empty string will cause the default localized text to be used.
* `toastXml` string (optional) _Windows_ - A custom description of the Notification on Windows superseding all properties above. Provides full customization of design and behavior of the notification.
> [!NOTE]
> On Windows, `urgency` type 'critical' sorts the notification higher in Action Center (above default priority notifications), but does not prevent auto-dismissal. To prevent auto-dismissal, you should also set
> `timeoutType` to 'never'.
### Instance Events
Objects created with `new Notification` emit the following events:
@@ -331,10 +287,6 @@ call this method before the OS will display it.
If the notification has been shown before, this method will dismiss the previously
shown notification and create a new one with identical properties.
On macOS, calling `show()` on a notification returned by `Notification.getHistory()` will
remove the original notification from Notification Center and post a new one with the same
properties.
```js
const { Notification, app } = require('electron')
@@ -373,17 +325,13 @@ app.whenReady().then(() => {
### Instance Properties
#### `notification.id` _macOS_ _Windows_ _Readonly_
#### `notification.id` _macOS_ _Readonly_
A `string` property representing the unique identifier of the notification. This is set at construction time — either from the `id` option or as a generated UUID if none was provided.
#### `notification.groupId` _macOS_ _Windows_ _Readonly_
#### `notification.groupId` _macOS_ _Readonly_
A `string` property representing the group identifier of the notification. Notifications with the same `groupId` will be visually grouped together in Notification Center (macOS) or Action Center (Windows).
#### `notification.groupTitle` _Windows_ _Readonly_
A `string` property representing the title of the notification group header.
A `string` property representing the group identifier of the notification. Notifications with the same `groupId` will be visually grouped together in Notification Center.
#### `notification.title`

View File

@@ -56,15 +56,6 @@ app.whenReady().then(() => {
})
```
## Protocol names
[RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) defines what a valid
protocol name is:
> Scheme names consist of a sequence of characters beginning with a letter and followed
> by any combination of letters, digits, plus ("+"), period ("."), or hyphen ("-").
> Although schemes are case-insensitive, the canonical form is lowercase […].
## Methods
The `protocol` module has the following methods:

View File

@@ -59,12 +59,7 @@ On Windows, returns true once the app has emitted the `ready` event.
### `safeStorage.isAsyncEncryptionAvailable()`
Returns `Promise<boolean>` - Resolves with whether encryption is available for
asynchronous safeStorage operations.
The asynchronous encryptor is initialized lazily the first time this method,
`encryptStringAsync`, or `decryptStringAsync` is called after the app is ready.
The returned promise resolves once initialization completes.
Returns `Promise<Boolean>` - Whether encryption is available for asynchronous safeStorage operations.
### `safeStorage.encryptString(plainText)`

View File

@@ -650,7 +650,7 @@ Clears the sessions HTTP cache.
`scheme://host:port`.
* `storages` string[] (optional) - The types of storages to clear, can be
`cookies`, `filesystem`, `indexdb`, `localstorage`,
`shadercache`, `serviceworkers`, `cachestorage`. If not
`shadercache`, `websql`, `serviceworkers`, `cachestorage`. If not
specified, clear all storage types.
Returns `Promise<void>` - resolves when the storage data has been cleared.

View File

@@ -11,5 +11,3 @@
* `stream` boolean (optional) - Default false.
* `codeCache` boolean (optional) - Enable V8 code cache for the scheme, only
works when `standard` is also set to true. Default false.
* `allowExtensions` boolean (optional) - Allow Chrome extensions to be used
on pages served over this protocol. Default false.

View File

@@ -1,26 +0,0 @@
# EnableHeapProfilingOptions Object
* `mode` string (optional) - Controls which processes are profiled. Equivalent to `--memlog` in
Chrome. Default is `all`.
* `all` - Profile all processes.
* `browser` - Profile only the browser process.
* `gpu` - Profile only the GPU process.
* `minimal` - Profile only the browser and GPU processes.
* `renderer-sampling` - Profile at most 1 renderer process. Each renderer process has a fixed
probability of being profiled when the renderer process is started or, for existing processes,
when heap profiling is enabled.
* `all-renderers` - Profile all renderer processes.
* `utility-sampling` - Each utility process has a fixed probability of being profiled.
* `all-utilities` - Profile all utility processes.
* `utility-and-browser` - Profile all utility processes and the browser process.
* `samplingRate` number (optional) - Controls the sampling interval in bytes. The lower the
interval, the more precise the profile is. However it comes at the cost of performance. Default
is `100000` (100KB). That is enough to observe allocation sites that make allocations >500KB
total, where total equals to a single allocation size times the number of such allocations at the
same call site. Equivalent to `--memlog-sampling-rate` in Chrome. Must be an integer between
`1000` and `10000000`.
* `stackMode` string (optional) - Controls the type of metadata recorded for each allocation.
Equivalent to `--memlog-stack-mode` in Chrome. Default is `native`.
* `native` - Instruction addresses from unwinding the stack.
* `native-with-thread-names` - Instruction addresses from unwinding the stack. Includes the thread
name as the first frame.

View File

@@ -5,7 +5,6 @@
* `rgba` - 32bpp RGBA (byte-order), 1 plane.
* `rgbaf16` - Half float RGBA, 1 plane.
* `nv12` - 12bpp with Y plane followed by a 2x2 interleaved UV plane.
* `nv16` - 16bpp with Y plane followed by a 2x1 interleaved UV plane.
* `p010le` - 4:2:0 10-bit YUV (little-endian), Y plane followed by a 2x2 interleaved UV plane.
* `colorSpace` [ColorSpace](color-space.md) (optional) - The color space of the texture.
* `codedSize` [Size](size.md) - The full dimensions of the shared texture.

View File

@@ -94,7 +94,6 @@
The actual output pixel format and color space of the texture should refer to [`OffscreenSharedTexture`](../structures/offscreen-shared-texture.md) object in the `paint` event.
* `argb` - The requested output texture format is 8-bit unorm RGBA, with SRGB SDR color space.
* `rgbaf16` - The requested output texture format is 16-bit float RGBA, with scRGB HDR color space.
* `nv12` - The requested output texture format is 12bpp with Y plane followed by a 2x2 interleaved UV plane, with REC709 color space.
* `deviceScaleFactor` number (optional) _Experimental_ - The device scale factor of the offscreen rendering output. If not set, will use `1` as default.
* `contextIsolation` boolean (optional) - Whether to run Electron APIs and
the specified `preload` script in a separate JavaScript context. Defaults

View File

@@ -2275,20 +2275,6 @@ Returns `Integer` - The Chromium internal `pid` of the associated renderer. Can
be compared to the `frameProcessId` passed by frame specific navigation events
(e.g. `did-frame-navigate`)
#### `contents.clone()`
Returns `WebContents` - A cloned WebContents instance. This method creates a copy
of the WebContents with the following attributes:
* **WebPreferences** - All preferences from the original WebContents are copied
* **SiteInstance** - Uses the same SiteInstance as the original. This means the cloned WebContents will reuse the same render process as the original when loading same-origin pages, and only spawn a new render process for cross-origin navigations. This process allocation behavior is consistent with window.open and tab duplication in Chromium. For more details, see [Chromium's Site Isolation](https://www.chromium.org/developers/design-documents/site-isolation/) design document.
* **Opener relationship** - Inherits the opener (window.opener) relationship
* **Navigation state** - Copies the navigation history and controller state
The cloned WebContents is an independent instance with its own lifecycle that can be destroyed separately and will not contain any open web pages.
This API is useful for use cases where you want to create a new WebContents that shares the same render process with the original for same-origin content, while maintaining full lifecycle independence. Additionally, reusing the existing render process can help optimize memory usage and page load speed to a certain extent, as it eliminates the overhead of spawning and initializing a new render process from scratch.
#### `contents.takeHeapSnapshot(filePath)`
* `filePath` string - Path to the output file.

View File

@@ -33,14 +33,10 @@ because it is invoked in the main process.
Returns [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) | null
`features` is a comma-separated key-value list, following the standard format of
the browser. For convenience, Electron will parse a subset of presentational
[`BrowserWindowConstructorOptions`](structures/browser-window-options.md) out of
this list (such as `width`, `height`, `x`, `y`, `show`, `frame`, `title`,
`backgroundColor`). Because the renderer is untrusted, options that cause the
main process to access the filesystem or that are otherwise privileged (such as
`icon`) are ignored. For full control and better ergonomics, use
`webContents.setWindowOpenHandler` to customize the BrowserWindow creation from
the main process.
the browser. Electron will parse [`BrowserWindowConstructorOptions`](structures/browser-window-options.md) out of this
list where possible, for convenience. For full control and better ergonomics,
consider using `webContents.setWindowOpenHandler` to customize the
BrowserWindow creation.
A subset of [`WebPreferences`](structures/web-preferences.md) can be set directly,
unnested, from the features string: `zoomFactor`, `nodeIntegration`, `javascript`,
@@ -60,10 +56,9 @@ window.open('https://github.com', '_blank', 'top=500,left=200,frame=false,nodeIn
enabled on the parent window.
* JavaScript will always be disabled in the opened `window` if it is disabled on
the parent window.
* Features that are not handled by Chromium and not in Electron's allowlist of
presentational `BrowserWindowConstructorOptions` are ignored. The raw
`features` string is still available to the main process via
`setWindowOpenHandler`.
* Non-standard features (that are not handled by Chromium or Electron) given in
`features` will be passed to any registered `webContents`'s
`did-create-window` event handler in the `options` argument.
* `frameName` follows the specification of `target` located in the [native documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window/open#parameters).
* When opening `about:blank`, the child window's [`WebPreferences`](structures/web-preferences.md) will be copied
from the parent window, and there is no way to override it because Chromium

View File

@@ -12,34 +12,6 @@ This document uses the following convention to categorize breaking changes:
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
## Planned Breaking API Changes (43.0)
### Behavior Changed: Dialog methods default to Downloads directory
The `defaultPath` option for the following methods now defaults to the user's Downloads folder (or their home directory if Downloads doesn't exist) when not explicitly provided:
* `dialog.showOpenDialog`
* `dialog.showOpenDialogSync`
* `dialog.showSaveDialog`
* `dialog.showSaveDialogSync`
Previously, when no `defaultPath` was provided, the underlying OS file dialog would determine the initial directory — typically remembering the last directory the user navigated to, or falling back to an OS-specific default. Now, Electron explicitly sets the initial directory to Downloads, which also means the OS will no longer track and restore the last-used directory between dialog invocations.
To preserve the old behavior, you can track the last-used directory yourself and pass it as `defaultPath`:
```js
const path = require('node:path')
let lastUsedPath
const result = await dialog.showOpenDialog({
defaultPath: lastUsedPath
})
if (!result.canceled && result.filePaths.length > 0) {
lastUsedPath = path.dirname(result.filePaths[0])
}
```
## Planned Breaking API Changes (42.0)
### Behavior Changed: macOS notifications now use `UNNotification` API
@@ -98,9 +70,6 @@ npm install electron --save-dev
ELECTRON_INSTALL_PLATFORM=mas npx electron . --no
```
This also means the `ELECTRON_SKIP_BINARY_DOWNLOAD` environment variable is no
longer supported, as its primary purpose was to prevent the `postinstall` script from running.
### Removed: `quotas` object from `Session.clearStorageData(options)`
When calling `Session.clearStorageData(options)`, the `options.quotas` object is no longer supported because it has been

View File

@@ -3,7 +3,7 @@
These are the style guidelines for coding in Electron.
You can run `npm run lint` to show any style issues detected by `cpplint` and
`oxlint`.
`eslint`.
## General Code

Some files were not shown because too many files have changed in this diff Show More