From e181fd040f72becd135db1fa977622b81da21643 Mon Sep 17 00:00:00 2001 From: John Kleinschmidt Date: Fri, 12 Dec 2025 13:43:12 -0500 Subject: [PATCH] build: drop dugite as a dependency (#49194) --- package.json | 4 ---- script/lib/utils.js | 17 ++++++++++------- script/lint.js | 11 +++++++---- script/release/notes/index.ts | 10 +++++++--- script/release/notes/notes.ts | 10 +++++++--- script/release/prepare-release.ts | 25 +++++++++++++------------ script/release/version-utils.ts | 29 +++++++++++++++++++++++------ spec/release-notes-spec.ts | 22 ++++++++++++++-------- spec/version-bump-spec.ts | 19 +++++++++++++------ yarn.lock | 13 ------------- 10 files changed, 94 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index a8f5c02938..e3294ccfb5 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "buffer": "^6.0.3", "chalk": "^4.1.0", "check-for-leaks": "^1.2.1", - "dugite": "^2.7.1", "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.32.0", @@ -149,9 +148,6 @@ "dependenciesMeta": { "abstract-socket": { "built": true - }, - "dugite": { - "built": true } } } diff --git a/script/lib/utils.js b/script/lib/utils.js index 25521af9b4..140850fc87 100644 --- a/script/lib/utils.js +++ b/script/lib/utils.js @@ -1,6 +1,6 @@ const chalk = require('chalk'); -const { GitProcess } = require('dugite'); +const childProcess = require('node:child_process'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); @@ -61,13 +61,16 @@ function getAbsoluteElectronExec () { return path.resolve(SRC_DIR, getElectronExec()); } -async function handleGitCall (args, gitDir) { - const details = await GitProcess.exec(args, gitDir); - if (details.exitCode === 0) { - return details.stdout.replace(/^\*|\s+|\s+$/, ''); +function handleGitCall (args, gitDir) { + const result = childProcess.spawnSync('git', args, { + cwd: gitDir, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); + if (result.status === 0) { + return result.stdout.replace(/^\*|\s+|\s+$/, ''); } else { - const error = GitProcess.parseError(details.stderr); - console.log(`${fail} couldn't parse git process call: `, error); + console.log(`${fail} couldn't parse git process call: `, result.stderr); process.exit(1); } } diff --git a/script/lint.js b/script/lint.js index 1bc38e3cd3..356122ca73 100755 --- a/script/lint.js +++ b/script/lint.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -const { GitProcess } = require('dugite'); const { ESLint } = require('eslint'); const minimist = require('minimist'); @@ -431,9 +430,13 @@ function populateLinterWithArgs (linter, opts) { } async function findChangedFiles (top) { - const result = await GitProcess.exec(['diff', '--name-only', '--cached'], top); - if (result.exitCode !== 0) { - console.log('Failed to find changed files', GitProcess.parseError(result.stderr)); + const result = childProcess.spawnSync('git', ['diff', '--name-only', '--cached'], { + cwd: top, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); + if (result.status !== 0) { + console.log('Failed to find changed files', result.stderr); process.exit(1); } const relativePaths = result.stdout.split(/\r\n|\r|\n/g); diff --git a/script/release/notes/index.ts b/script/release/notes/index.ts index a6e7ebf01c..04d268caa5 100755 --- a/script/release/notes/index.ts +++ b/script/release/notes/index.ts @@ -1,9 +1,9 @@ #!/usr/bin/env node import { Octokit } from '@octokit/rest'; -import { GitProcess } from 'dugite'; import { valid, compare, gte, lte } from 'semver'; +import { spawnSync } from 'node:child_process'; import { basename } from 'node:path'; import { parseArgs } from 'node:util'; @@ -20,8 +20,12 @@ const semverify = (version: string) => version.replace(/^origin\//, '').replace( const runGit = async (args: string[]) => { console.info(`Running: git ${args.join(' ')}`); - const response = await GitProcess.exec(args, ELECTRON_DIR); - if (response.exitCode !== 0) { + const response = spawnSync('git', args, { + cwd: ELECTRON_DIR, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); + if (response.status !== 0) { throw new Error(response.stderr.trim()); } return response.stdout.trim(); diff --git a/script/release/notes/notes.ts b/script/release/notes/notes.ts index 04a2030bd1..0da214361c 100644 --- a/script/release/notes/notes.ts +++ b/script/release/notes/notes.ts @@ -1,8 +1,8 @@ #!/usr/bin/env node import { Octokit } from '@octokit/rest'; -import { GitProcess } from 'dugite'; +import { spawnSync } from 'node:child_process'; import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'; import { resolve as _resolve } from 'node:path'; @@ -105,8 +105,12 @@ class Pool { **/ const runGit = async (dir: string, args: string[]) => { - const response = await GitProcess.exec(args, dir); - if (response.exitCode !== 0) { + const response = spawnSync('git', args, { + cwd: dir, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); + if (response.status !== 0) { throw new Error(response.stderr.trim()); } return response.stdout.trim(); diff --git a/script/release/prepare-release.ts b/script/release/prepare-release.ts index ffe763f988..63e6c38f1a 100755 --- a/script/release/prepare-release.ts +++ b/script/release/prepare-release.ts @@ -1,8 +1,7 @@ import { Octokit } from '@octokit/rest'; import * as chalk from 'chalk'; -import { GitProcess } from 'dugite'; -import { execSync } from 'node:child_process'; +import { execSync, spawnSync } from 'node:child_process'; import { join } from 'node:path'; import { createGitHubTokenStrategy } from './github-token'; @@ -166,11 +165,12 @@ async function createRelease ( } async function pushRelease (branch: string) { - const pushDetails = await GitProcess.exec( - ['push', 'origin', `HEAD:${branch}`, '--follow-tags'], - ELECTRON_DIR - ); - if (pushDetails.exitCode === 0) { + const pushDetails = spawnSync('git', ['push', 'origin', `HEAD:${branch}`, '--follow-tags'], { + cwd: ELECTRON_DIR, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); + if (pushDetails.status === 0) { console.log( `${pass} Successfully pushed the release. Wait for ` + 'release builds to finish before running "npm run release".' @@ -191,11 +191,12 @@ async function runReleaseBuilds (branch: string, newVersion: string) { async function tagRelease (version: string) { console.log(`Tagging release ${version}.`); - const checkoutDetails = await GitProcess.exec( - ['tag', '-a', '-m', version, version], - ELECTRON_DIR - ); - if (checkoutDetails.exitCode === 0) { + const checkoutDetails = spawnSync('git', ['tag', '-a', '-m', version, version], { + cwd: ELECTRON_DIR, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); + if (checkoutDetails.status === 0) { console.log(`${pass} Successfully tagged ${version}.`); } else { console.log( diff --git a/script/release/version-utils.ts b/script/release/version-utils.ts index 67ffac650d..25c6941d58 100644 --- a/script/release/version-utils.ts +++ b/script/release/version-utils.ts @@ -1,6 +1,7 @@ -import { GitProcess } from 'dugite'; import * as semver from 'semver'; +import { spawnSync } from 'node:child_process'; + import { ELECTRON_DIR } from '../lib/utils'; export enum PreType { @@ -27,7 +28,11 @@ export const isStable = (v: string) => { export async function nextAlpha (v: string) { const next = semver.coerce(semver.clean(v)); - const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-alpha.*`], ELECTRON_DIR); + const tagBlob = spawnSync('git', ['tag', '--list', '-l', `v${next}-alpha.*`], { + cwd: ELECTRON_DIR, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); const tags = tagBlob.stdout.split('\n').filter(e => e !== ''); tags.sort((t1, t2) => { const a = parseInt(t1.split('.').pop()!, 10); @@ -41,7 +46,11 @@ export async function nextAlpha (v: string) { export async function nextBeta (v: string) { const next = semver.coerce(semver.clean(v)); - const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-beta.*`], ELECTRON_DIR); + const tagBlob = spawnSync('git', ['tag', '--list', '-l', `v${next}-beta.*`], { + cwd: ELECTRON_DIR, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); const tags = tagBlob.stdout.split('\n').filter(e => e !== ''); tags.sort((t1, t2) => { const a = parseInt(t1.split('.').pop()!, 10); @@ -57,7 +66,11 @@ export async function nextNightly (v: string) { let next = semver.valid(semver.coerce(v)); const pre = `nightly.${getCurrentDate()}`; - const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim(); + const branch = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { + cwd: ELECTRON_DIR, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }).stdout.trim(); if (branch === 'main') { next = semver.inc(await getLastMajorForMain(), 'major'); } else if (isStable(v)) { @@ -69,8 +82,12 @@ export async function nextNightly (v: string) { async function getLastMajorForMain () { let branchNames; - const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], ELECTRON_DIR); - if (result.exitCode === 0) { + const result = spawnSync('git', ['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], { + cwd: ELECTRON_DIR, + encoding: 'utf8', + stdio: ['inherit', 'pipe', 'pipe'] + }); + if (result.status === 0) { branchNames = result.stdout.trim().split('\n'); const filtered = branchNames.map(b => b.replace('origin/', '')); return getNextReleaseBranch(filtered); diff --git a/spec/release-notes-spec.ts b/spec/release-notes-spec.ts index be34d0608c..482b101638 100644 --- a/spec/release-notes-spec.ts +++ b/spec/release-notes-spec.ts @@ -1,12 +1,12 @@ import { expect } from 'chai'; -import { GitProcess, IGitExecutionOptions, IGitResult } from 'dugite'; import * as sinon from 'sinon'; +import { SpawnSyncReturns } from 'node:child_process'; import * as path from 'node:path'; import * as notes from '../script/release/notes/notes'; -/* Fake a Dugite GitProcess that only returns the specific +/* Fake a git spawnSync that only returns the specific commits that we want to test */ class Commit { @@ -43,10 +43,10 @@ class GitFake { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - exec (args: string[], path: string, options?: IGitExecutionOptions | undefined): Promise { + exec (command: string, args: string[], options?: any): SpawnSyncReturns { let stdout = ''; const stderr = ''; - const exitCode = 0; + const status = 0; if (args.length === 3 && args[0] === 'merge-base') { // expected form: `git merge-base branchName1 branchName2` @@ -78,10 +78,10 @@ class GitFake { // git branch --all --contains ${ref} --sort version:refname stdout = args[3]; } else { - console.error('unhandled GitProcess.exec():', args); + console.error('unhandled git spawnSync():', args); } - return Promise.resolve({ exitCode, stdout, stderr }); + return { status, stdout, stderr, pid: 0, output: [null, stdout, stderr], signal: null }; } } @@ -118,8 +118,14 @@ describe('release notes', () => { }); beforeEach(() => { - const wrapper = (args: string[], path: string, options?: IGitExecutionOptions | undefined) => gitFake.exec(args, path, options); - sandbox.replace(GitProcess, 'exec', wrapper); + const wrapper = (command: unknown, args: unknown, options?: unknown) => { + if (command === 'git' && Array.isArray(args)) { + return gitFake.exec(command as string, args as string[], options); + } + // Default behavior for non-git commands + return { status: 0, stdout: '', stderr: '', pid: 0, output: [null, '', ''], signal: null }; + }; + sandbox.stub(require('node:child_process'), 'spawnSync').callsFake(wrapper); gitFake.setBranch(oldBranch, [...sharedHistory, oldFix]); }); diff --git a/spec/version-bump-spec.ts b/spec/version-bump-spec.ts index e84d4a82ac..8a71b63463 100644 --- a/spec/version-bump-spec.ts +++ b/spec/version-bump-spec.ts @@ -1,7 +1,8 @@ import { expect } from 'chai'; -import { GitProcess, IGitExecutionOptions, IGitResult } from 'dugite'; import * as sinon from 'sinon'; +import { SpawnSyncReturns } from 'node:child_process'; + import { ifdescribe } from './lib/spec-helpers'; import { nextVersion } from '../script/release/version-bumper'; @@ -33,10 +34,10 @@ class GitFake { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - exec (args: string[], path: string, options?: IGitExecutionOptions | undefined): Promise { + exec (command: string, args: string[], options?: any): SpawnSyncReturns { let stdout = ''; const stderr = ''; - const exitCode = 0; + const status = 0; // handle for promoting from current master HEAD let branch = 'stable'; @@ -48,7 +49,7 @@ class GitFake { if (!this.branches[branch]) this.setBranch(branch); stdout = this.branches[branch].join('\n'); - return Promise.resolve({ exitCode, stdout, stderr }); + return { status, stdout, stderr, pid: 0, output: [null, stdout, stderr], signal: null }; } } @@ -163,8 +164,14 @@ describe('version-bumper', () => { const gitFake = new GitFake(); beforeEach(() => { - const wrapper = (args: string[], path: string, options?: IGitExecutionOptions | undefined) => gitFake.exec(args, path, options); - sandbox.replace(GitProcess, 'exec', wrapper); + const wrapper = (command: unknown, args: unknown, options?: unknown) => { + if (command === 'git' && Array.isArray(args)) { + return gitFake.exec(command as string, args as string[], options); + } + // Default behavior for non-git commands + return { status: 0, stdout: '', stderr: '', pid: 0, output: [null, '', ''], signal: null }; + }; + sandbox.stub(require('node:child_process'), 'spawnSync').callsFake(wrapper); }); afterEach(() => { diff --git a/yarn.lock b/yarn.lock index 9fba955666..0b110f565e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -607,7 +607,6 @@ __metadata: buffer: "npm:^6.0.3" chalk: "npm:^4.1.0" check-for-leaks: "npm:^1.2.1" - dugite: "npm:^2.7.1" eslint: "npm:^8.57.1" eslint-config-standard: "npm:^17.1.0" eslint-plugin-import: "npm:^2.32.0" @@ -644,8 +643,6 @@ __metadata: dependenciesMeta: abstract-socket: built: true - dugite: - built: true languageName: unknown linkType: soft @@ -4867,16 +4864,6 @@ __metadata: languageName: node linkType: hard -"dugite@npm:^2.7.1": - version: 2.7.1 - resolution: "dugite@npm:2.7.1" - dependencies: - progress: "npm:^2.0.3" - tar: "npm:^6.1.11" - checksum: 10c0/8b7992b91abc2473e43a4900c5b100f5a4b98785b9955275c72b1a621152b5df9ff794cda50daced5ee452f4dc95b313ce9c30af5dc9e302241ef9f661a593df - languageName: node - linkType: hard - "dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": version: 1.0.1 resolution: "dunder-proto@npm:1.0.1"