Skip to content

Commit aadc4fc

Browse files
authored
feat(v8): add command to sync V8 deps (#1016)
Add `git node v8 deps` to (re)sync V8 dependencies from V8's `DEPS` file.
1 parent d05ef11 commit aadc4fc

File tree

5 files changed

+127
-72
lines changed

5 files changed

+127
-72
lines changed

components/git/v8.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import path from 'node:path';
22

33
import logSymbols from 'log-symbols';
44

5-
import { minor, major, backport } from '../../lib/update-v8/index.js';
5+
import { minor, major, backport, deps } from '../../lib/update-v8/index.js';
66
import { defaultBaseDir } from '../../lib/update-v8/constants.js';
77
import { checkCwd } from '../../lib/update-v8/common.js';
88
import { forceRunAsync } from '../../lib/run.js';
99

10-
export const command = 'v8 [major|minor|backport]';
10+
export const command = 'v8 [major|minor|backport|deps]';
1111
export const describe = 'Update or patch the V8 engine';
1212

1313
export function builder(yargs) {
@@ -26,6 +26,11 @@ export function builder(yargs) {
2626
describe: 'Bump the NODE_MODULE_VERSION constant',
2727
default: true
2828
});
29+
yargs.option('concurrent', {
30+
type: 'boolean',
31+
describe: 'Update dependencies concurrently',
32+
default: true,
33+
});
2934
}
3035
})
3136
.command({
@@ -64,6 +69,19 @@ export function builder(yargs) {
6469
});
6570
}
6671
})
72+
.command({
73+
command: 'deps',
74+
desc: 'Update V8 dependencies from the DEPS file',
75+
handler,
76+
builder: (yargs) => {
77+
yargs
78+
.option('concurrent', {
79+
type: 'boolean',
80+
describe: 'Update dependencies concurrently',
81+
default: true,
82+
});
83+
}
84+
})
6785
.demandCommand(1, 'Please provide a valid command')
6886
.option('node-dir', {
6987
describe: 'Directory of a Node.js clone',
@@ -126,6 +144,8 @@ export function handler(argv) {
126144
return major(options);
127145
case 'backport':
128146
return backport(options);
147+
case 'deps':
148+
return deps(options);
129149
}
130150
})
131151
.catch((err) => {

lib/update-v8/deps.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import path from 'node:path';
2+
import { promises as fs } from 'node:fs';
3+
4+
import { chromiumGit, v8Deps } from './constants.js';
5+
import { forceRunAsync } from '../run.js';
6+
import {
7+
addToGitignore,
8+
filterForVersion,
9+
getNodeV8Version,
10+
removeDirectory,
11+
replaceGitignore,
12+
} from './util.js';
13+
14+
async function fetchFromGit(cwd, repo, commit) {
15+
await removeDirectory(cwd);
16+
await fs.mkdir(cwd, { recursive: true });
17+
await exec('init');
18+
await exec('remote', 'add', 'origin', repo);
19+
await exec('fetch', 'origin', commit);
20+
await exec('reset', '--hard', 'FETCH_HEAD');
21+
await removeDirectory(path.join(cwd, '.git'));
22+
23+
function exec(...options) {
24+
return forceRunAsync('git', options, {
25+
ignoreFailure: false,
26+
spawnArgs: { cwd, stdio: 'ignore' }
27+
});
28+
}
29+
}
30+
31+
async function readDeps(nodeDir) {
32+
const depsStr = await fs.readFile(path.join(nodeDir, 'deps/v8/DEPS'), 'utf8');
33+
const start = depsStr.indexOf('deps = {');
34+
const end = depsStr.indexOf('\n}', start) + 2;
35+
const depsDeclaration = depsStr.substring(start, end).replace(/^ *#.*/gm, '');
36+
const Var = () => chromiumGit; // eslint-disable-line no-unused-vars
37+
let deps;
38+
eval(depsDeclaration); // eslint-disable-line no-eval
39+
return deps;
40+
}
41+
42+
async function lookupDep(depsTable, depName) {
43+
const dep = depsTable[depName];
44+
if (!dep) {
45+
throw new Error(`V8 dep "${depName}" not found in DEPS file`);
46+
}
47+
if (typeof dep === 'object') {
48+
return dep.url.split('@');
49+
}
50+
return dep.split('@');
51+
}
52+
53+
export default function updateV8Deps() {
54+
return {
55+
title: 'Update V8 DEPS',
56+
task: async(ctx, task) => {
57+
const newV8Version = await getNodeV8Version(ctx.nodeDir);
58+
const repoPrefix = newV8Version.majorMinor >= 86 ? '' : 'v8/';
59+
const deps = filterForVersion(v8Deps.map((v8Dep) => ({
60+
...v8Dep,
61+
repo: `${repoPrefix}${v8Dep.repo}`,
62+
path: v8Dep.repo
63+
})), newV8Version);
64+
if (deps.length === 0) return;
65+
const depsTable = await readDeps(ctx.nodeDir);
66+
const subtasks = [];
67+
for (const dep of deps) {
68+
// Update .gitignore sequentially to avoid races
69+
if (dep.gitignore) {
70+
if (typeof dep.gitignore === 'string') {
71+
await addToGitignore(ctx.nodeDir, dep.gitignore);
72+
} else {
73+
await replaceGitignore(ctx.nodeDir, dep.gitignore);
74+
}
75+
}
76+
subtasks.push({
77+
title: `Update ${dep.path}`,
78+
task: async(ctx) => {
79+
const [repo, commit] = await lookupDep(depsTable, dep.repo);
80+
const thePath = path.join(ctx.nodeDir, 'deps/v8', dep.path);
81+
await fetchFromGit(thePath, repo, commit);
82+
}
83+
});
84+
}
85+
return task.newListr(subtasks, { concurrent: ctx.concurrent });
86+
}
87+
};
88+
};

lib/update-v8/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import updateVersionNumbers from './updateVersionNumbers.js';
55
import commitUpdate from './commitUpdate.js';
66
import majorUpdate from './majorUpdate.js';
77
import minorUpdate from './minorUpdate.js';
8+
import updateDeps from './deps.js';
89
import updateV8Clone from './updateV8Clone.js';
910

1011
export function major(options) {
@@ -34,6 +35,14 @@ export async function backport(options) {
3435
return tasks.run(options);
3536
};
3637

38+
export async function deps(options) {
39+
const tasks = new Listr(
40+
[updateDeps()],
41+
getOptions(options)
42+
);
43+
return tasks.run(options);
44+
};
45+
3746
/**
3847
* Get the listr2 options.
3948
* @param {{ verbose?: boolean }} options The original options.

lib/update-v8/majorUpdate.js

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import path from 'node:path';
2-
import { promises as fs } from 'node:fs';
32

43
import { getCurrentV8Version } from './common.js';
4+
import updateV8Deps from './deps.js';
55
import {
6-
getNodeV8Version,
7-
filterForVersion,
8-
addToGitignore,
9-
replaceGitignore,
106
removeDirectory,
117
isVersionString
128
} from './util.js';
139
import applyNodeChanges from './applyNodeChanges.js';
14-
import { chromiumGit, v8Deps } from './constants.js';
1510
import { forceRunAsync } from '../run.js';
1611

1712
export default function majorUpdate() {
@@ -106,65 +101,3 @@ function addDepsV8() {
106101
})
107102
};
108103
}
109-
110-
function updateV8Deps() {
111-
return {
112-
title: 'Update V8 DEPS',
113-
task: async(ctx) => {
114-
const newV8Version = await getNodeV8Version(ctx.nodeDir);
115-
const repoPrefix = newV8Version.majorMinor >= 86 ? '' : 'v8/';
116-
const deps = filterForVersion(v8Deps.map((v8Dep) => ({
117-
...v8Dep,
118-
repo: `${repoPrefix}${v8Dep.repo}`,
119-
path: v8Dep.repo
120-
})), newV8Version);
121-
if (deps.length === 0) return;
122-
for (const dep of deps) {
123-
if (dep.gitignore) {
124-
if (typeof dep.gitignore === 'string') {
125-
await addToGitignore(ctx.nodeDir, dep.gitignore);
126-
} else {
127-
await replaceGitignore(ctx.nodeDir, dep.gitignore);
128-
}
129-
}
130-
const [repo, commit] = await readDeps(ctx.nodeDir, dep.repo);
131-
const thePath = path.join(ctx.nodeDir, 'deps/v8', dep.path);
132-
await fetchFromGit(thePath, repo, commit);
133-
}
134-
}
135-
};
136-
}
137-
138-
async function readDeps(nodeDir, depName) {
139-
const depsStr = await fs.readFile(path.join(nodeDir, 'deps/v8/DEPS'), 'utf8');
140-
const start = depsStr.indexOf('deps = {');
141-
const end = depsStr.indexOf('\n}', start) + 2;
142-
const depsDeclaration = depsStr.substring(start, end).replace(/^ *#.*/gm, '');
143-
const Var = () => chromiumGit; // eslint-disable-line no-unused-vars
144-
let deps;
145-
eval(depsDeclaration); // eslint-disable-line no-eval
146-
const dep = deps[depName];
147-
if (!dep) {
148-
throw new Error(`V8 dep "${depName}" not found in DEPS file`);
149-
}
150-
if (typeof dep === 'object') {
151-
return dep.url.split('@');
152-
}
153-
return dep.split('@');
154-
}
155-
156-
async function fetchFromGit(cwd, repo, commit) {
157-
await fs.mkdir(cwd, { recursive: true });
158-
await exec('init');
159-
await exec('remote', 'add', 'origin', repo);
160-
await exec('fetch', 'origin', commit);
161-
await exec('reset', '--hard', 'FETCH_HEAD');
162-
await removeDirectory(path.join(cwd, '.git'));
163-
164-
function exec(...options) {
165-
return forceRunAsync('git', options, {
166-
ignoreFailure: false,
167-
spawnArgs: { cwd, stdio: 'ignore' }
168-
});
169-
}
170-
}

lib/update-v8/util.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,18 @@ export function filterForVersion(list, version) {
3737

3838
export async function addToGitignore(nodeDir, value) {
3939
const gitignorePath = path.join(nodeDir, 'deps/v8/.gitignore');
40-
await fs.appendFile(gitignorePath, `${value}\n`);
40+
const gitignore = await fs.readFile(gitignorePath, 'utf8');
41+
if (!gitignore.includes(value)) {
42+
await fs.appendFile(gitignorePath, `${value}\n`);
43+
}
4144
}
4245

4346
export async function replaceGitignore(nodeDir, options) {
4447
const gitignorePath = path.join(nodeDir, 'deps/v8/.gitignore');
4548
let gitignore = await fs.readFile(gitignorePath, 'utf8');
46-
gitignore = gitignore.replace(options.match, options.replace);
49+
if (!gitignore.includes(options.replace)) {
50+
gitignore = gitignore.replace(options.match, options.replace);
51+
}
4752
await fs.writeFile(gitignorePath, gitignore);
4853
}
4954

0 commit comments

Comments
 (0)