build: update to yarn v4 (#48994)

* build: update to yarn v4

(cherry picked from commit 6adec744f3411240a63fd194a319b510872eca93)

* chore: fixup types after yarn v4 migration

* chore: update nan yarn.lock patch

* build: automatically install git for dugite
This commit is contained in:
John Kleinschmidt 2025-11-19 17:32:30 -05:00 committed by GitHub
commit b2e73d28e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 18341 additions and 11314 deletions

View file

@ -184,7 +184,7 @@ runs:
shell: bash shell: bash
run: | run: |
cd src/electron cd src/electron
node script/yarn create-typescript-definitions node script/yarn.js create-typescript-definitions
- name: Publish Electron Dist ${{ inputs.step-suffix }} - name: Publish Electron Dist ${{ inputs.step-suffix }}
if: ${{ inputs.is-release == 'true' }} if: ${{ inputs.is-release == 'true' }}
shell: bash shell: bash

View file

@ -152,7 +152,7 @@ runs:
if: ${{ inputs.target-platform == 'linux' && github.ref == 'refs/heads/main' }} if: ${{ inputs.target-platform == 'linux' && github.ref == 'refs/heads/main' }}
shell: bash shell: bash
run: | run: |
npx node src/electron/script/patches-stats.mjs --upload-stats || true node src/electron/script/patches-stats.mjs --upload-stats || true
# delete all .git directories under src/ except for # delete all .git directories under src/ except for
# third_party/angle/ and third_party/dawn/ because of build time generation of files # third_party/angle/ and third_party/dawn/ because of build time generation of files
# gen/angle/commit.h depends on third_party/angle/.git/HEAD # gen/angle/commit.h depends on third_party/angle/.git/HEAD

View file

@ -13,12 +13,16 @@ runs:
- name: Generating Types for SHA in ${{ inputs.sha-file }} - name: Generating Types for SHA in ${{ inputs.sha-file }}
shell: bash shell: bash
run: | run: |
git checkout $(cat ${{ inputs.sha-file }}) export ELECTRON_DIR=$(pwd)
rm -rf node_modules if [ "${{ inputs.sha-file }}" == ".dig-old" ]; then
yarn install --frozen-lockfile --ignore-scripts cd /tmp
git clone https://github.com/electron/electron.git
cd electron
fi
git checkout $(cat $ELECTRON_DIR/${{ inputs.sha-file }})
node script/yarn.js install --immutable
echo "#!/usr/bin/env node\nglobal.x=1" > node_modules/typescript/bin/tsc echo "#!/usr/bin/env node\nglobal.x=1" > node_modules/typescript/bin/tsc
node node_modules/.bin/electron-docs-parser --dir=./ --outDir=./ --moduleVersion=0.0.0-development node node_modules/.bin/electron-docs-parser --dir=./ --outDir=./ --moduleVersion=0.0.0-development
node node_modules/.bin/electron-typescript-definitions --api=electron-api.json --outDir=artifacts node node_modules/.bin/electron-typescript-definitions --api=electron-api.json --outDir=artifacts
mv artifacts/electron.d.ts artifacts/${{ inputs.filename }} mv artifacts/electron.d.ts $ELECTRON_DIR/artifacts/${{ inputs.filename }}
git checkout .
working-directory: ./electron working-directory: ./electron

View file

@ -6,7 +6,7 @@ runs:
- name: Get yarn cache directory path - name: Get yarn cache directory path
shell: bash shell: bash
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "dir=$(node src/electron/script/yarn cache dir)" >> $GITHUB_OUTPUT run: echo "dir=$(node src/electron/script/yarn.js config get cacheFolder)" >> $GITHUB_OUTPUT
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
id: yarn-cache id: yarn-cache
with: with:
@ -18,4 +18,7 @@ runs:
shell: bash shell: bash
run: | run: |
cd src/electron cd src/electron
node script/yarn install --frozen-lockfile --prefer-offline if [ "$TARGET_ARCH" = "x86" ]; then
export npm_config_arch="ia32"
fi
node script/yarn.js install --immutable

View file

@ -333,7 +333,7 @@ jobs:
build-runs-on: electron-arc-centralus-linux-amd64-32core build-runs-on: electron-arc-centralus-linux-amd64-32core
test-runs-on: electron-arc-centralus-linux-arm64-4core test-runs-on: electron-arc-centralus-linux-arm64-4core
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}' build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init","volumes":["/home/runner/externals:/mnt/runner-externals"]}' test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init --memory=12g","volumes":["/home/runner/externals:/mnt/runner-externals"]}'
target-platform: linux target-platform: linux
target-arch: arm target-arch: arm
is-release: false is-release: false

View file

@ -54,12 +54,12 @@ jobs:
shell: bash shell: bash
run: | run: |
cd src/electron cd src/electron
node script/yarn create-typescript-definitions node script/yarn.js create-typescript-definitions
node script/yarn tsc -p tsconfig.default_app.json --noEmit node script/yarn.js tsc -p tsconfig.default_app.json --noEmit
for f in build/webpack/*.js for f in build/webpack/*.js
do do
out="${f:29}" out="${f:29}"
if [ "$out" != "base.js" ]; then if [ "$out" != "base.js" ]; then
node script/yarn webpack --config $f --output-filename=$out --output-path=./.tmp --env mode=development node script/yarn.js webpack --config $f --output-filename=$out --output-path=./.tmp --env mode=development
fi fi
done done

View file

@ -78,11 +78,11 @@ jobs:
# but then we would lint its contents (at least gn format), and it doesn't pass it. # but then we would lint its contents (at least gn format), and it doesn't pass it.
cd src/electron cd src/electron
node script/yarn install --frozen-lockfile node script/yarn.js install --immutable
node script/yarn lint node script/yarn.js lint
- name: Run Script Typechecker - name: Run Script Typechecker
shell: bash shell: bash
run: | run: |
cd src/electron cd src/electron
node script/yarn tsc -p tsconfig.script.json node script/yarn.js tsc -p tsconfig.script.json

View file

@ -196,10 +196,7 @@ jobs:
# sudo security authorizationdb write com.apple.trust-settings.admin allow # sudo security authorizationdb write com.apple.trust-settings.admin allow
# cd src/electron # cd src/electron
# ./script/codesign/generate-identity.sh # ./script/codesign/generate-identity.sh
- name: Install Datadog CLI
run: |
cd src/electron
node script/yarn global add @datadog/datadog-ci
- name: Run Electron Tests - name: Run Electron Tests
shell: bash shell: bash
env: env:
@ -225,7 +222,7 @@ jobs:
export ELECTRON_FORCE_TEST_SUITE_EXIT="true" export ELECTRON_FORCE_TEST_SUITE_EXIT="true"
fi fi
fi fi
node script/yarn test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files node script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
else else
chown :builduser .. && chmod g+w .. chown :builduser .. && chmod g+w ..
chown -R :builduser . && chmod -R g+w . chown -R :builduser . && chmod -R g+w .
@ -242,9 +239,14 @@ jobs:
export MOCHA_TIMEOUT=180000 export MOCHA_TIMEOUT=180000
echo "Piping output to ASAN_SYMBOLIZE ($ASAN_SYMBOLIZE)" echo "Piping output to ASAN_SYMBOLIZE ($ASAN_SYMBOLIZE)"
cd electron cd electron
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn test --runners=main --trace-uncaught --enable-logging --files $tests_files | $ASAN_SYMBOLIZE runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --trace-uncaught --enable-logging --files $tests_files | $ASAN_SYMBOLIZE
else else
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn test --runners=main --trace-uncaught --enable-logging --files $tests_files if [ "${{ inputs.target-arch }}" = "arm" ]; then
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --skipYarnInstall --runners=main --trace-uncaught --enable-logging --files $tests_files
else
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --trace-uncaught --enable-logging --files $tests_files
fi
fi fi
fi fi
- name: Upload Test results to Datadog - name: Upload Test results to Datadog
@ -256,8 +258,9 @@ jobs:
DD_TAGS: "os.architecture:${{ inputs.target-arch }},os.family:${{ inputs.target-platform }},os.platform:${{ inputs.target-platform }},asan:${{ inputs.is-asan }}" DD_TAGS: "os.architecture:${{ inputs.target-arch }},os.family:${{ inputs.target-platform }},os.platform:${{ inputs.target-platform }},asan:${{ inputs.is-asan }}"
run: | run: |
if ! [ -z $DD_API_KEY ] && [ -f src/electron/junit/test-results-main.xml ]; then if ! [ -z $DD_API_KEY ] && [ -f src/electron/junit/test-results-main.xml ]; then
export DATADOG_PATH=`node src/electron/script/yarn global bin` cd src/electron
$DATADOG_PATH/datadog-ci junit upload src/electron/junit/test-results-main.xml export DATADOG_PATH=`node script/yarn.js bin datadog-ci`
$DATADOG_PATH junit upload junit/test-results-main.xml
fi fi
if: always() && !cancelled() if: always() && !cancelled()
- name: Upload Test Artifacts - name: Upload Test Artifacts

2
.gitignore vendored
View file

@ -53,3 +53,5 @@ ts-gen
patches/mtime-cache.json patches/mtime-cache.json
spec/fixtures/logo.png spec/fixtures/logo.png
.yarn/install-state.gz

942
.yarn/releases/yarn-4.11.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

12
.yarnrc.yml Normal file
View file

@ -0,0 +1,12 @@
enableScripts: false
nmHoistingLimits: workspaces
nodeLinker: node-modules
npmMinimalAgeGate: 10080
npmPreapprovedPackages:
- "@electron/*"
yarnPath: .yarn/releases/yarn-4.11.0.cjs

5
DEPS
View file

@ -30,9 +30,6 @@ vars = {
# The path of the sysroots.json file. # The path of the sysroots.json file.
'sysroots_json_path': 'electron/script/sysroots.json', 'sysroots_json_path': 'electron/script/sysroots.json',
# KEEP IN SYNC WITH utils.js FILE
'yarn_version': '1.22.22',
# To be able to build clean Chromium from sources. # To be able to build clean Chromium from sources.
'apply_patches': True, 'apply_patches': True,
@ -155,7 +152,7 @@ hooks = [
'action': [ 'action': [
'python3', 'python3',
'-c', '-c',
'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["python3", "script/lib/npx.py", "yarn@' + (Var("yarn_version")) + '", "install", "--frozen-lockfile"]);', 'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["node", ".yarn/releases/yarn-4.11.0.cjs", "install", "--immutable"]);',
], ],
}, },
{ {

View file

@ -119,7 +119,10 @@ export function fetchWithSession (input: RequestInfo, init: (RequestInit & {bypa
p.reject(err); p.reject(err);
}); });
if (!req.body?.pipeTo(Writable.toWeb(r as unknown as Writable)).then(() => r.end())) { r.end(); } // pipeTo expects a WritableStream<Uint8Array>. Node.js' Writable.toWeb returns WritableStream<any>,
// which causes a TS structural mismatch.
const writable = Writable.toWeb(r as unknown as Writable) as unknown as WritableStream<Uint8Array>;
if (!req.body?.pipeTo(writable).then(() => r.end())) { r.end(); }
return p.promise; return p.promise;
} }

View file

@ -4,6 +4,8 @@ import { createReadStream } from 'fs';
import { Readable } from 'stream'; import { Readable } from 'stream';
import { ReadableStream } from 'stream/web'; import { ReadableStream } from 'stream/web';
import type { ReadableStreamDefaultReader } from 'stream/web';
// Global protocol APIs. // Global protocol APIs.
const { registerSchemesAsPrivileged, getStandardSchemes, Protocol } = process._linkedBinding('electron_browser_protocol'); const { registerSchemesAsPrivileged, getStandardSchemes, Protocol } = process._linkedBinding('electron_browser_protocol');
@ -12,7 +14,7 @@ const ERR_UNEXPECTED = -9;
const isBuiltInScheme = (scheme: string) => ['http', 'https', 'file'].includes(scheme); const isBuiltInScheme = (scheme: string) => ['http', 'https', 'file'].includes(scheme);
function makeStreamFromPipe (pipe: any): ReadableStream { function makeStreamFromPipe (pipe: any): ReadableStream<Uint8Array> {
const buf = new Uint8Array(1024 * 1024 /* 1 MB */); const buf = new Uint8Array(1024 * 1024 /* 1 MB */);
return new ReadableStream({ return new ReadableStream({
async pull (controller) { async pull (controller) {
@ -38,21 +40,26 @@ function makeStreamFromFileInfo ({
filePath: string; filePath: string;
offset?: number; offset?: number;
length?: number; length?: number;
}): ReadableStream { }): ReadableStream<Uint8Array> {
// Node's Readable.toWeb produces a WHATWG ReadableStream whose chunks are Uint8Array.
return Readable.toWeb(createReadStream(filePath, { return Readable.toWeb(createReadStream(filePath, {
start: offset, start: offset,
end: length >= 0 ? offset + length : undefined end: length >= 0 ? offset + length : undefined
})); })) as ReadableStream<Uint8Array>;
} }
function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): RequestInit['body'] { function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): RequestInit['body'] {
if (!uploadData) return null; if (!uploadData) return null;
// Optimization: skip creating a stream if the request is just a single buffer. // Optimization: skip creating a stream if the request is just a single buffer.
if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') return uploadData[0].bytes; if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') {
return uploadData[0].bytes as any;
}
const chunks = [...uploadData] as any[]; // TODO: types are wrong const chunks = [...uploadData] as any[]; // TODO: refine ProtocolRequest types
let current: ReadableStreamDefaultReader | null = null; // Use Node's web stream types explicitly to avoid DOM lib vs Node lib structural mismatches.
return new ReadableStream({ // Generic <Uint8Array> ensures reader.read() returns value?: Uint8Array consistent with enqueue.
let current: ReadableStreamDefaultReader<Uint8Array> | null = null;
return new ReadableStream<Uint8Array>({
async pull (controller) { async pull (controller) {
if (current) { if (current) {
const { done, value } = await current.read(); const { done, value } = await current.read();
@ -67,7 +74,7 @@ function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): Reque
if (!chunks.length) { return controller.close(); } if (!chunks.length) { return controller.close(); }
const chunk = chunks.shift()!; const chunk = chunks.shift()!;
if (chunk.type === 'rawData') { if (chunk.type === 'rawData') {
controller.enqueue(chunk.bytes); controller.enqueue(chunk.bytes as Uint8Array);
} else if (chunk.type === 'file') { } else if (chunk.type === 'file') {
current = makeStreamFromFileInfo(chunk).getReader(); current = makeStreamFromFileInfo(chunk).getReader();
return this.pull!(controller); return this.pull!(controller);

View file

@ -40,7 +40,7 @@ process.on('uncaughtException', function (error) {
// Emit 'exit' event on quit. // Emit 'exit' event on quit.
const { app } = require('electron'); const { app } = require('electron');
app.on('quit', (_event, exitCode) => { app.on('quit', (_event: any, exitCode: number) => {
process.emit('exit', exitCode); process.emit('exit', exitCode);
}); });

View file

@ -5,6 +5,7 @@
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": { "devDependencies": {
"@azure/storage-blob": "^12.28.0", "@azure/storage-blob": "^12.28.0",
"@datadog/datadog-ci": "^4.1.2",
"@electron/asar": "^3.2.13", "@electron/asar": "^3.2.13",
"@electron/docs-parser": "^2.0.0", "@electron/docs-parser": "^2.0.0",
"@electron/fiddle-core": "^1.3.4", "@electron/fiddle-core": "^1.3.4",
@ -40,6 +41,7 @@
"lint-staged": "^16.1.0", "lint-staged": "^16.1.0",
"markdownlint-cli2": "^0.18.0", "markdownlint-cli2": "^0.18.0",
"minimist": "^1.2.8", "minimist": "^1.2.8",
"node-gyp": "^11.4.2",
"null-loader": "^4.0.1", "null-loader": "^4.0.1",
"pre-flight": "^2.0.0", "pre-flight": "^2.0.0",
"process": "^0.11.10", "process": "^0.11.10",
@ -133,7 +135,14 @@
"node script/gen-libc++-filenames.js" "node script/gen-libc++-filenames.js"
] ]
}, },
"resolutions": { "packageManager": "yarn@4.11.0",
"nan": "nodejs/nan#e14bdcd1f72d62bca1d541b66da43130384ec213" "workspaces": [
"spec",
"spec/fixtures/native-addon/*"
],
"dependenciesMeta": {
"dugite": {
"built": true
}
} }
} }

View file

@ -7,3 +7,4 @@ fix_support_new_variant_of_namedpropertyhandlerconfiguration_and.patch
fix_correct_usages_of_v8_returnvalue_void_set_nonempty_for_new.patch fix_correct_usages_of_v8_returnvalue_void_set_nonempty_for_new.patch
chore_remove_deprecated_functioncallbackinfo_holder.patch chore_remove_deprecated_functioncallbackinfo_holder.patch
fix_replace_deprecated_get_setprototype.patch fix_replace_deprecated_get_setprototype.patch
chore_add_yarnrc_yml_and_yarn_lock_file_to_use_yarn_v4.patch

File diff suppressed because it is too large Load diff

View file

@ -40,6 +40,7 @@ DevToolsSecurity -enable
# security import "$dir"/public.key -k $KEY_CHAIN # security import "$dir"/public.key -k $KEY_CHAIN
# Generate Trust Settings # Generate Trust Settings
# TODO: Remove NPX
npm_config_yes=true npx ts-node "$(dirname $0)"/gen-trust.ts "$dir"/certificate.cer "$dir"/trust.xml npm_config_yes=true npx ts-node "$(dirname $0)"/gen-trust.ts "$dir"/certificate.cer "$dir"/trust.xml
# Import Trust Settings # Import Trust Settings

View file

@ -153,7 +153,7 @@ const LINTERS = [{
}, { }, {
key: 'javascript', key: 'javascript',
roots: ['build', 'default_app', 'lib', 'npm', 'script', 'spec'], roots: ['build', 'default_app', 'lib', 'npm', 'script', 'spec'],
ignoreRoots: ['spec/node_modules'], ignoreRoots: ['spec/node_modules', 'spec/fixtures/native-addon'],
test: filename => filename.endsWith('.js') || filename.endsWith('.ts') || filename.endsWith('.mjs'), test: filename => filename.endsWith('.js') || filename.endsWith('.ts') || filename.endsWith('.mjs'),
run: async (opts, filenames) => { run: async (opts, filenames) => {
const eslint = new ESLint({ const eslint = new ESLint({
@ -282,7 +282,7 @@ const LINTERS = [{
}, { }, {
key: 'md', key: 'md',
roots: ['.'], roots: ['.'],
ignoreRoots: ['.git', 'node_modules', 'spec/node_modules'], ignoreRoots: ['.git', 'node_modules', 'spec/node_modules', 'spec/fixtures/native-addon'],
test: filename => filename.endsWith('.md'), test: filename => filename.endsWith('.md'),
run: async (opts, filenames) => { run: async (opts, filenames) => {
const { getCodeBlocks } = await import('@electron/lint-roller/dist/lib/markdown.js'); const { getCodeBlocks } = await import('@electron/lint-roller/dist/lib/markdown.js');

View file

@ -9,7 +9,7 @@ const NAN_DIR = path.resolve(BASE, 'third_party', 'nan');
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
const utils = require('./lib/utils'); const utils = require('./lib/utils');
const { YARN_VERSION } = require('./yarn'); const { YARN_SCRIPT_PATH } = require('./yarn');
if (!require.main) { if (!require.main) {
throw new Error('Must call the nan spec runner directly'); throw new Error('Must call the nan spec runner directly');
@ -106,13 +106,12 @@ async function main () {
stdio: 'inherit', stdio: 'inherit',
shell: process.platform === 'win32' shell: process.platform === 'win32'
}); });
if (buildStatus !== 0 || signal != null) { if (buildStatus !== 0 || signal != null) {
console.error('Failed to build nan test modules'); console.error('Failed to build nan test modules');
return process.exit(buildStatus !== 0 ? buildStatus : signal); return process.exit(buildStatus !== 0 ? buildStatus : signal);
} }
const { status: installStatus, signal: installSignal } = cp.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install'], { const { status: installStatus, signal: installSignal } = cp.spawnSync(process.execPath, [YARN_SCRIPT_PATH, 'install'], {
env, env,
cwd: NAN_DIR, cwd: NAN_DIR,
stdio: 'inherit', stdio: 'inherit',

View file

@ -211,6 +211,7 @@ new Promise<string>((resolve, reject) => {
}); });
}) })
.then((tarballPath) => { .then((tarballPath) => {
// TODO: Remove NPX
const existingVersionJSON = childProcess.execSync(`npx npm@7 view ${rootPackageJson.name}@${currentElectronVersion} --json`).toString('utf-8'); const existingVersionJSON = childProcess.execSync(`npx npm@7 view ${rootPackageJson.name}@${currentElectronVersion} --json`).toString('utf-8');
// It's possible this is a re-run and we already have published the package, if not we just publish like normal // It's possible this is a re-run and we already have published the package, if not we just publish like normal
if (!existingVersionJSON) { if (!existingVersionJSON) {

View file

@ -21,6 +21,7 @@ const fail = chalk.red('✗');
const FAILURE_STATUS_KEY = 'Electron_Spec_Runner_Failures'; const FAILURE_STATUS_KEY = 'Electron_Spec_Runner_Failures';
const args = minimist(process.argv, { const args = minimist(process.argv, {
boolean: ['skipYarnInstall'],
string: ['runners', 'target', 'electronVersion'], string: ['runners', 'target', 'electronVersion'],
number: ['enableRerun'], number: ['enableRerun'],
unknown: arg => unknownFlags.push(arg) unknown: arg => unknownFlags.push(arg)
@ -36,10 +37,9 @@ for (const flag of unknownFlags) {
} }
const utils = require('./lib/utils'); const utils = require('./lib/utils');
const { YARN_VERSION } = require('./yarn'); const { YARN_SCRIPT_PATH } = require('./yarn');
const BASE = path.resolve(__dirname, '../..'); const BASE = path.resolve(__dirname, '../..');
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
const runners = new Map([ const runners = new Map([
['main', { description: 'Main process specs', run: runMainProcessElectronTests }] ['main', { description: 'Main process specs', run: runMainProcessElectronTests }]
@ -96,7 +96,7 @@ async function main () {
const somethingChanged = (currentSpecHash !== lastSpecHash) || const somethingChanged = (currentSpecHash !== lastSpecHash) ||
(lastSpecInstallHash !== currentSpecInstallHash); (lastSpecInstallHash !== currentSpecInstallHash);
if (somethingChanged) { if (somethingChanged && !args.skipYarnInstall) {
await installSpecModules(path.resolve(__dirname, '..', 'spec')); await installSpecModules(path.resolve(__dirname, '..', 'spec'));
await getSpecHash().then(saveSpecHash); await getSpecHash().then(saveSpecHash);
} }
@ -419,7 +419,8 @@ async function installSpecModules (dir) {
if (fs.existsSync(path.resolve(dir, 'node_modules'))) { if (fs.existsSync(path.resolve(dir, 'node_modules'))) {
await fs.promises.rm(path.resolve(dir, 'node_modules'), { force: true, recursive: true }); await fs.promises.rm(path.resolve(dir, 'node_modules'), { force: true, recursive: true });
} }
const { status } = childProcess.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install', '--frozen-lockfile'], { const yarnArgs = [YARN_SCRIPT_PATH, 'install', '--immutable'];
const { status } = childProcess.spawnSync(process.execPath, yarnArgs, {
env, env,
cwd: dir, cwd: dir,
stdio: 'inherit', stdio: 'inherit',
@ -435,8 +436,8 @@ function getSpecHash () {
return Promise.all([ return Promise.all([
(async () => { (async () => {
const hasher = crypto.createHash('SHA256'); const hasher = crypto.createHash('SHA256');
hasher.update(fs.readFileSync(path.resolve(__dirname, '../yarn.lock')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json'))); hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/yarn.lock')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../script/spec-runner.js'))); hasher.update(fs.readFileSync(path.resolve(__dirname, '../script/spec-runner.js')));
return hasher.digest('hex'); return hasher.digest('hex');
})(), })(),

18
script/yarn.js Normal file → Executable file
View file

@ -1,21 +1,7 @@
const cp = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path'); const path = require('node:path');
const YARN_VERSION = /'yarn_version': '(.+?)'/.exec(fs.readFileSync(path.resolve(__dirname, '../DEPS'), 'utf8'))[1]; exports.YARN_SCRIPT_PATH = path.resolve(__dirname, '..', '.yarn/releases/yarn-4.11.0.cjs');
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
if (require.main === module) { if (require.main === module) {
const child = cp.spawn(NPX_CMD, [`yarn@${YARN_VERSION}`, ...process.argv.slice(2)], { require(exports.YARN_SCRIPT_PATH);
stdio: 'inherit',
env: {
...process.env,
npm_config_yes: 'true'
},
shell: process.platform === 'win32'
});
child.on('exit', code => process.exit(code));
} }
exports.YARN_VERSION = YARN_VERSION;

2
spec/ambient.d.ts vendored
View file

@ -1,4 +1,2 @@
declare let standardScheme: string; declare let standardScheme: string;
declare let serviceWorkerScheme: string; declare let serviceWorkerScheme: string;
declare module 'dbus-native';

View file

@ -671,7 +671,7 @@ describe('contextBridge', () => {
it('should release the global hold on methods sent across contexts', async () => { it('should release the global hold on methods sent across contexts', async () => {
await makeBindingWindow(() => { await makeBindingWindow(() => {
const trackedValues: WeakRef<object>[] = []; const trackedValues: WeakRef<object>[] = [];
require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length })); require('electron').ipcRenderer.on('get-gc-info', (e: any) => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
contextBridge.exposeInMainWorld('example', { contextBridge.exposeInMainWorld('example', {
getFunction: () => () => 123, getFunction: () => () => 123,
track: (value: object) => { trackedValues.push(new WeakRef(value)); } track: (value: object) => { trackedValues.push(new WeakRef(value)); }
@ -699,7 +699,7 @@ describe('contextBridge', () => {
it('should not leak the global hold on methods sent across contexts when reloading a sandboxed renderer', async () => { it('should not leak the global hold on methods sent across contexts when reloading a sandboxed renderer', async () => {
await makeBindingWindow(() => { await makeBindingWindow(() => {
const trackedValues: WeakRef<object>[] = []; const trackedValues: WeakRef<object>[] = [];
require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length })); require('electron').ipcRenderer.on('get-gc-info', (e: any) => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
contextBridge.exposeInMainWorld('example', { contextBridge.exposeInMainWorld('example', {
getFunction: () => () => 123, getFunction: () => () => 123,
track: (value: object) => { trackedValues.push(new WeakRef(value)); } track: (value: object) => { trackedValues.push(new WeakRef(value)); }

View file

@ -243,7 +243,6 @@ describe('ipc module', () => {
await w.webContents.executeJavaScript(`(${function () { await w.webContents.executeJavaScript(`(${function () {
try { try {
const buffer = new ArrayBuffer(10); const buffer = new ArrayBuffer(10);
// @ts-expect-error
require('electron').ipcRenderer.postMessage('port', '', [buffer]); require('electron').ipcRenderer.postMessage('port', '', [buffer]);
} catch (e) { } catch (e) {
require('electron').ipcRenderer.postMessage('port', { error: (e as Error).message }); require('electron').ipcRenderer.postMessage('port', { error: (e as Error).message });
@ -323,7 +322,7 @@ describe('ipc module', () => {
w.loadURL('about:blank'); w.loadURL('about:blank');
await w.webContents.executeJavaScript(`(${function () { await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron'); const { ipcRenderer } = require('electron');
ipcRenderer.on('port', e => { ipcRenderer.on('port', (e: any) => {
const [port] = e.ports; const [port] = e.ports;
port.start(); port.start();
port.onclose = () => { port.onclose = () => {
@ -480,8 +479,8 @@ describe('ipc module', () => {
w.loadURL('about:blank'); w.loadURL('about:blank');
await w.webContents.executeJavaScript(`(${function () { await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron'); const { ipcRenderer } = require('electron');
ipcRenderer.on('port', ev => { ipcRenderer.on('port', (e: any) => {
const [port] = ev.ports; const [port] = e.ports;
port.onmessage = () => { port.onmessage = () => {
ipcRenderer.send('done'); ipcRenderer.send('done');
}; };
@ -498,9 +497,9 @@ describe('ipc module', () => {
w.loadURL('about:blank'); w.loadURL('about:blank');
await w.webContents.executeJavaScript(`(${function () { await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron'); const { ipcRenderer } = require('electron');
ipcRenderer.on('port', e1 => { ipcRenderer.on('port', (e1: any) => {
e1.ports[0].onmessage = e2 => { e1.ports[0].onmessage = (e2: any) => {
e2.ports[0].onmessage = e3 => { e2.ports[0].onmessage = (e3: any) => {
ipcRenderer.send('done', e3.data); ipcRenderer.send('done', e3.data);
}; };
}; };
@ -587,7 +586,7 @@ describe('ipc module', () => {
w.loadURL('about:blank'); w.loadURL('about:blank');
await w.webContents.executeJavaScript(`(${function () { await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron'); const { ipcRenderer } = require('electron');
ipcRenderer.on('foo', (_e, msg) => { ipcRenderer.on('foo', (_e: Event, msg: string) => {
ipcRenderer.send('bar', msg); ipcRenderer.send('bar', msg);
}); });
}})()`); }})()`);

View file

@ -10,7 +10,7 @@ import { nativeImage } from 'electron/common';
import { app } from 'electron/main'; import { app } from 'electron/main';
import { expect } from 'chai'; import { expect } from 'chai';
import * as dbus from 'dbus-native'; import * as dbus from 'dbus-ts';
import * as path from 'node:path'; import * as path from 'node:path';
import { promisify } from 'node:util'; import { promisify } from 'node:util';
@ -40,10 +40,9 @@ ifdescribe(!skip)('Notification module (dbus)', () => {
const path = '/org/freedesktop/Notifications'; const path = '/org/freedesktop/Notifications';
const iface = 'org.freedesktop.DBus.Mock'; const iface = 'org.freedesktop.DBus.Mock';
console.log(`session bus: ${process.env.DBUS_SESSION_BUS_ADDRESS}`); console.log(`session bus: ${process.env.DBUS_SESSION_BUS_ADDRESS}`);
const bus = dbus.sessionBus(); const bus = await dbus.sessionBus();
const service = bus.getService(serviceName); const service = bus.getService(serviceName);
const getInterface = promisify(service.getInterface.bind(service)); mock = await service.getInterface(path, iface);
mock = await getInterface(path, iface);
getCalls = promisify(mock.GetCalls.bind(mock)); getCalls = promisify(mock.GetCalls.bind(mock));
reset = promisify(mock.Reset.bind(mock)); reset = promisify(mock.Reset.bind(mock));
}); });

View file

@ -7,7 +7,7 @@
// See https://pypi.python.org/pypi/python-dbusmock for more information about // See https://pypi.python.org/pypi/python-dbusmock for more information about
// python-dbusmock. // python-dbusmock.
import { expect } from 'chai'; import { expect } from 'chai';
import * as dbus from 'dbus-native'; import * as dbus from 'dbus-ts';
import { once } from 'node:events'; import { once } from 'node:events';
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
@ -20,10 +20,9 @@ describe('powerMonitor', () => {
ifdescribe(process.platform === 'linux' && process.env.DBUS_SYSTEM_BUS_ADDRESS != null)('when powerMonitor module is loaded with dbus mock', () => { ifdescribe(process.platform === 'linux' && process.env.DBUS_SYSTEM_BUS_ADDRESS != null)('when powerMonitor module is loaded with dbus mock', () => {
before(async () => { before(async () => {
const systemBus = dbus.systemBus(); const systemBus = await dbus.systemBus();
const loginService = systemBus.getService('org.freedesktop.login1'); const loginService = systemBus.getService('org.freedesktop.login1');
const getInterface = promisify(loginService.getInterface.bind(loginService)); logindMock = await loginService.getInterface('/org/freedesktop/login1', 'org.freedesktop.DBus.Mock');
logindMock = await getInterface('/org/freedesktop/login1', 'org.freedesktop.DBus.Mock');
getCalls = promisify(logindMock.GetCalls.bind(logindMock)); getCalls = promisify(logindMock.GetCalls.bind(logindMock));
emitSignal = promisify(logindMock.EmitSignal.bind(logindMock)); emitSignal = promisify(logindMock.EmitSignal.bind(logindMock));
reset = promisify(logindMock.Reset.bind(logindMock)); reset = promisify(logindMock.Reset.bind(logindMock));

View file

@ -2687,7 +2687,13 @@ describe('webContents module', () => {
const errMsg = Buffer.concat(stderr).toString().trim(); const errMsg = Buffer.concat(stderr).toString().trim();
console.error(`Error parsing PDF file, exit code was ${code}; signal was ${signal}, error: ${errMsg}`); console.error(`Error parsing PDF file, exit code was ${code}; signal was ${signal}, error: ${errMsg}`);
} }
return JSON.parse(Buffer.concat(stdout).toString().trim()); try {
return JSON.parse(Buffer.concat(stdout).toString().trim());
} catch (err) {
console.error('Error parsing PDF file:', err);
console.error('Raw output:', Buffer.concat(stdout).toString().trim());
throw err;
}
}; };
let w: BrowserWindow; let w: BrowserWindow;

View file

@ -1,5 +1,8 @@
{ {
"main": "./lib/echo.js",
"name": "@electron-ci/echo", "name": "@electron-ci/echo",
"version": "0.0.1" "version": "0.0.1",
"main": "./lib/echo.js",
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
} }

View file

@ -1,5 +1,8 @@
{ {
"main": "./lib/test-array-buffer.js",
"name": "@electron-ci/external-ab", "name": "@electron-ci/external-ab",
"version": "0.0.1" "version": "0.0.1",
"main": "./lib/test-array-buffer.js",
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
} }

View file

@ -0,0 +1,13 @@
{
"name": "@electron-ci/is-valid-window",
"version": "0.0.5",
"main": "./lib/is-valid-window.js",
"private": true,
"licenses": "MIT",
"dependencies": {
"nan": "2.x"
},
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
}

View file

@ -1,5 +1,9 @@
{ {
"main": "./lib/osr-gpu.js",
"name": "@electron-ci/osr-gpu", "name": "@electron-ci/osr-gpu",
"version": "0.0.1" "version": "0.0.1",
"main": "./lib/osr-gpu.js",
"private": true,
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
} }

View file

@ -1,5 +1,9 @@
{ {
"name": "@electron-ci/uv-dlopen", "name": "@electron-ci/uv-dlopen",
"version": "0.0.1", "version": "0.0.1",
"main": "index.js" "main": "index.js",
"private": true,
"scripts": {
"install": "node-gyp configure && node-gyp build"
}
} }

View file

@ -1,9 +0,0 @@
{
"main": "./lib/is-valid-window.js",
"name": "is-valid-window",
"version": "0.0.5",
"licenses": "Public Domain",
"dependencies": {
"nan": "2.x"
}
}

View file

@ -7,6 +7,13 @@
"node-gyp-install": "node-gyp install" "node-gyp-install": "node-gyp install"
}, },
"devDependencies": { "devDependencies": {
"@electron-ci/echo": "*",
"@electron-ci/external-ab": "*",
"@electron-ci/is-valid-window": "*",
"@electron-ci/osr-gpu": "*",
"@electron-ci/uv-dlopen": "*",
"@electron/fuses": "^1.8.0",
"@electron/packager": "^18.3.2",
"@types/basic-auth": "^1.1.8", "@types/basic-auth": "^1.1.8",
"@types/busboy": "^1.5.4", "@types/busboy": "^1.5.4",
"@types/chai": "^4.3.19", "@types/chai": "^4.3.19",
@ -15,32 +22,25 @@
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/mocha": "^7.0.2", "@types/mocha": "^7.0.2",
"@types/send": "^0.14.5", "@types/send": "^0.14.5",
"@types/sinon": "^9.0.4",
"@types/split": "^1.0.5", "@types/split": "^1.0.5",
"@types/uuid": "^3.4.6", "@types/uuid": "^3.4.6",
"@types/w3c-web-serial": "^1.0.7", "@types/w3c-web-serial": "^1.0.7",
"express": "^4.20.0",
"@electron-ci/echo": "file:./fixtures/native-addon/echo",
"@electron-ci/is-valid-window": "file:./is-valid-window",
"@electron-ci/uv-dlopen": "file:./fixtures/native-addon/uv-dlopen/",
"@electron-ci/osr-gpu": "file:./fixtures/native-addon/osr-gpu/",
"@electron-ci/external-ab": "file:./fixtures/native-addon/external-ab/",
"@electron/fuses": "^1.8.0",
"@electron/packager": "^18.3.2",
"@types/sinon": "^9.0.4",
"@types/ws": "^7.2.0", "@types/ws": "^7.2.0",
"basic-auth": "^2.0.1", "basic-auth": "^2.0.1",
"busboy": "^1.6.0", "busboy": "^1.6.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"coffeescript": "^2.4.1", "coffeescript": "^2.4.1",
"dbus-native": "github:nornagon/dbus-native#master", "dbus-ts": "^0.0.7",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"express": "^4.20.0",
"graceful-fs": "^4.1.15", "graceful-fs": "^4.1.15",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^10.0.0", "mocha": "^10.0.0",
"mocha-junit-reporter": "^1.18.0", "mocha-junit-reporter": "^1.18.0",
"mocha-multi-reporters": "^1.1.7", "mocha-multi-reporters": "^1.1.7",
"pdfjs-dist": "^4.2.67", "pdfjs-dist": "4.2.67",
"ps-list": "^7.0.0", "ps-list": "^7.0.0",
"q": "^1.5.1", "q": "^1.5.1",
"send": "^0.19.0", "send": "^0.19.0",
@ -50,11 +50,5 @@
"winreg": "1.2.4", "winreg": "1.2.4",
"ws": "^7.5.10", "ws": "^7.5.10",
"yargs": "^16.0.3" "yargs": "^16.0.3"
},
"resolutions": {
"nan": "file:../../third_party/nan",
"dbus-native/optimist/minimist": "1.2.7",
"dbus-native/xml2js": "0.5.0",
"abstract-socket": "github:deepak1556/node-abstractsocket#928cc591decd12aff7dad96449da8afc29832c19"
} }
} }

File diff suppressed because it is too large Load diff

22962
yarn.lock

File diff suppressed because it is too large Load diff