Skip to content

Commit

Permalink
feat: merge update-v8 in the project (nodejs#235)
Browse files Browse the repository at this point in the history
* feat: merge update-v8 in the project

* move to git-node
  • Loading branch information
targos authored and joyeecheung committed Apr 19, 2018
1 parent 67bc061 commit 969987b
Show file tree
Hide file tree
Showing 15 changed files with 1,652 additions and 195 deletions.
96 changes: 96 additions & 0 deletions components/git/v8.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict';

const path = require('path');

const execa = require('execa');
const logSymbols = require('log-symbols');

const updateV8 = require('../../lib/update-v8');
const constants = require('../../lib/update-v8/constants');
const common = require('../../lib/update-v8/common');

module.exports = {
command: 'v8 [major|minor|backport]',
describe: 'Update or patch the V8 engine',
builder: (yargs) => {
yargs
.command({
command: 'major',
desc: 'Do a major upgrade. Replaces the whole deps/v8 directory',
handler: main,
builder: (yargs) => {
yargs.option('branch', {
describe: 'Branch of the V8 repository to use for the upgrade',
default: 'lkgr'
});
}
})
.command({
command: 'minor',
desc: 'Do a minor patch of the current V8 version',
handler: main
})
.command({
command: 'backport <sha>',
desc: 'Backport a single commit from the V8 repository',
handler: main,
builder: (yargs) => {
yargs.option('bump', {
describe: 'Bump V8 embedder version number or patch version',
default: true
});
}
})
.demandCommand(1, 'Please provide a valid command')
.option('node-dir', {
describe: 'Directory of a Node.js clone',
default: process.cwd()
})
.option('base-dir', {
describe: 'Directory where V8 should be cloned',
default: constants.defaultBaseDir
})
.option('verbose', {
describe: 'Enable verbose output',
boolean: true,
default: false
});
},
handler: main
};

function main(argv) {
const options = Object.assign({}, argv);
options.nodeDir = path.resolve(options.nodeDir);
options.baseDir = path.resolve(options.baseDir);
options.v8CloneDir = path.join(options.baseDir, 'v8');

options.execGitNode = function execGitNode(...args) {
return execa('git', args, { cwd: options.nodeDir });
};
options.execGitV8 = function execGitV8(...args) {
return execa('git', args, { cwd: options.v8CloneDir });
};

Promise.resolve()
.then(async() => {
await common.checkCwd(options);
const kind = argv._[0];
options[kind] = true;
switch (kind) {
case 'minor':
return updateV8.minor(options);
case 'major':
return updateV8.major(options);
case 'backport':
return updateV8.backport(options);
}
})
.catch((err) => {
console.error(
logSymbols.error,
options.verbose ? err.stack : err.message
);
process.exitCode = 1;
});
}
55 changes: 55 additions & 0 deletions docs/git-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Commands:
new one for a pull request
git-node metadata <identifier> Retrieves metadata for a PR and validates them
against nodejs/node PR rules
git-node v8 [major|minor|backport] Update or patch the V8 engine
Options:
--version Show version number [boolean]
Expand Down Expand Up @@ -145,3 +146,57 @@ If you are using `git bash` and having trouble with output use
current known issues with git bash:
- git bash Lacks colors.
- git bash output duplicates metadata.

### `git node v8`

Update or patch the V8 engine.
This tool will maintain a clone of the V8 repository in `~/.update-v8/v8`.

#### `git node v8 major`

* Replaces `deps/v8` with a newer major version.
* Resets the embedder version number to `-node.0`.
* Updates `NODE_MODULE_VERSION` according to the V8 version.

##### Options

###### `--branch=branchName`

Branch of the V8 repository to use for the upgrade.
Defaults to `lkgr`.

#### `git node v8 minor`

Compare current V8 version with latest upstream of the same major. Applies a
patch if necessary.
If the `git apply` command fails, a patch file will be written in the Node.js
clone directory.

#### `git node v8 backport <sha>`

Fetches and applies the patch corresponding to `sha`. Increments the V8
embedder version number or patch version and commits the changes.
If the `git apply` command fails, a patch file will be written in the Node.js
clone directory.

##### Options

###### `--no-bump`

Set this flag to skip bumping the V8 embedder version number or patch version.

#### General options

##### `--node-dir=/path/to/node`

Specify the path to the Node.js git repository.
Defaults to current working directory.

##### `--base-dir=/path/to/base/dir`

Specify the path where V8 the clone will be maintained.
Defaults to `~/.update-v8`.

##### `--verbose`

Enable verbose output.
126 changes: 126 additions & 0 deletions lib/update-v8/backport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
'use strict';

const path = require('path');

const execa = require('execa');
const fs = require('fs-extra');
const Listr = require('listr');

const common = require('./common');

exports.doBackport = function doBackport(options) {
const todo = [common.getCurrentV8Version(), generatePatch(), applyPatch()];
if (options.bump !== false) {
if (options.nodeMajorVersion < 9) {
todo.push(incrementV8Version());
} else {
todo.push(incrementEmbedderVersion());
}
}
return {
title: 'V8 commit backport',
task: () => {
return new Listr(todo);
}
};
};

exports.commitBackport = function commitBackport() {
return {
title: 'Commit patch',
task: async(ctx) => {
const messageTitle = `deps: cherry-pick ${ctx.sha.substring(
0,
7
)} from upstream V8`;
const indentedMessage = ctx.message.replace(/\n/g, '\n ');
const messageBody =
'Original commit message:\n\n' +
` ${indentedMessage}\n\n` +
`Refs: https://github.com/v8/v8/commit/${ctx.sha}`;

await ctx.execGitNode('add', 'deps/v8');
await ctx.execGitNode('commit', '-m', messageTitle, '-m', messageBody);
}
};
};

function generatePatch() {
return {
title: 'Generate patch',
task: async(ctx) => {
const sha = ctx.sha;
if (!sha || sha.length !== 40) {
throw new Error(
'--sha option is required and must be 40 characters long'
);
}
try {
const [patch, message] = await Promise.all([
ctx.execGitV8('format-patch', '--stdout', `${sha}^..${sha}`),
ctx.execGitV8('log', '--format=%B', '-n', '1', sha)
]);
ctx.patch = patch.stdout;
ctx.message = message.stdout;
} catch (e) {
throw new Error(e.stderr);
}
}
};
}

function applyPatch() {
return {
title: 'Apply patch to deps/v8',
task: async(ctx) => {
const patch = ctx.patch;
try {
await execa('git', ['apply', '-3', '--directory=deps/v8'], {
cwd: ctx.nodeDir,
input: patch
});
} catch (e) {
const file = path.join(ctx.nodeDir, `${ctx.sha}.diff`);
await fs.writeFile(file, ctx.patch);
throw new Error(
`Could not apply patch.\n${e}\nDiff was stored in ${file}`
);
}
}
};
}

function incrementV8Version() {
return {
title: 'Increment V8 version',
task: async(ctx) => {
const incremented = ctx.currentVersion[3] + 1;
const versionHPath = `${ctx.nodeDir}/deps/v8/include/v8-version.h`;
let versionH = await fs.readFile(versionHPath, 'utf8');
versionH = versionH.replace(
/V8_PATCH_LEVEL (\d+)/,
`V8_PATCH_LEVEL ${incremented}`
);
await fs.writeFile(versionHPath, versionH);
}
};
}

const embedderRegex = /'v8_embedder_string': '-node\.(\d+)'/;
function incrementEmbedderVersion() {
return {
title: 'Increment embedder version number',
task: async(ctx) => {
const commonGypiPath = path.join(ctx.nodeDir, 'common.gypi');
const commonGypi = await fs.readFile(commonGypiPath, 'utf8');
const embedderValue = parseInt(embedderRegex.exec(commonGypi)[1], 10);
const embedderString = `'v8_embedder_string': '-node.${embedderValue +
1}'`;
await fs.writeFile(
commonGypiPath,
commonGypi.replace(embedderRegex, embedderString)
);
await ctx.execGitNode('add', 'common.gypi');
}
};
}
28 changes: 28 additions & 0 deletions lib/update-v8/commitUpdate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const util = require('./util');

module.exports = function() {
return {
title: 'Commit V8 update',
task: async(ctx) => {
const newV8Version = util.getNodeV8Version(ctx.nodeDir).join('.');
await ctx.execGitNode('add', 'deps/v8');
const moreArgs = [];
let message;
if (ctx.minor) {
const prev = ctx.currentVersion.join('.');
const next = ctx.latestVersion.join('.');
moreArgs.push(
'-m',
`Refs: https://github.com/v8/v8/compare/${prev}...${next}`
);
message = `deps: patch V8 to ${newV8Version}`;
} else {
message = `deps: update V8 to ${newV8Version}`;
}
await ctx.execGitNode('commit', '-m', message, ...moreArgs);
},
skip: (ctx) => ctx.skipped
};
};
38 changes: 38 additions & 0 deletions lib/update-v8/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';

const path = require('path');

const fs = require('fs-extra');

const util = require('./util');

exports.getCurrentV8Version = function getCurrentV8Version() {
return {
title: 'Get current V8 version',
task: (ctx) => {
ctx.currentVersion = util.getNodeV8Version(ctx.nodeDir);
}
};
};

exports.checkCwd = async function checkCwd(ctx) {
let isNode = false;
try {
const nodeVersion = await fs.readFile(
path.join(ctx.nodeDir, 'src/node_version.h')
);
const match = /#define NODE_MAJOR_VERSION (\d+)/.exec(nodeVersion);
if (match) {
isNode = true;
ctx.nodeMajorVersion = parseInt(match[1], 10);
}
} catch (e) {
// ignore
}
if (!isNode) {
throw new Error(
'This does not seem to be the Node.js repository.\n' +
`node-dir: ${ctx.nodeDir}`
);
}
};
Loading

0 comments on commit 969987b

Please sign in to comment.