Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

Commit

Permalink
Add applyPatches script and some basic patch documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
baparham committed Jun 8, 2022
1 parent a9f9604 commit 45a4b8d
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 31 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,32 @@ This project deploys multiple defense measures to ensure that the safe binaries
- Only authorized Vercel employees can push new revisions to npm.

Report to [[email protected]](mailto:[email protected]), if you noticed a disparity between (hashes of) binaries.

## Contributing Updates to Patches

The expectation is that a patch applies cleanly, with no delta or offsets from
the source repo.

When making a change to a patch file, it is possible to apply that patch without
building by running

`yarn applyPatches --node-range node18`

where the `--node-range` can be specified to apply patches for the version of
node for which you are updating patches. If unspecified, the latest node version
in [patches.json](./patches/patches.json) will be used.

Ultimately, the patch should result in fully functional node binary, but the
`applyPatches` script can be used to quickly iterate just the application of
the patches you are updating without needing to wait for the full build to
complete.

## Building a Binary Locally

You can use the `yarn start` script to build the binary locally, which is helpful
when updating patches to ensure functionality before pushing patch updates for
review.

For example:

`yarn start --node-range node18 --arch x64 --output dist`
35 changes: 35 additions & 0 deletions lib/apply-patches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env node

import yargs from 'yargs';

import { log } from './log';
import { getNodeVersion } from './index';
import { version } from '../package.json';
import { fetchExtractApply, prepBuildPath } from './build';

async function applyPatchesOnVersion(nodeRange: string, quietExtraction = false) {
await prepBuildPath();
await fetchExtractApply(getNodeVersion(nodeRange), quietExtraction);
}

async function main() {
const { argv } = yargs
.option('node-range', { alias: 'n', default: 'latest', type: 'string' })
.option('quiet-extraction', { alias: 'q', type: 'boolean' })
.version(version)
.alias('v', 'version')
.help()
.alias('h', 'help');

const {
'node-range': nodeRange,
'quiet-extraction': quietExtraction,
} = argv;

await applyPatchesOnVersion(nodeRange, quietExtraction);
}

main().catch((error) => {
if (!error.wasReported) log.error(error);
process.exit(2);
});
27 changes: 18 additions & 9 deletions lib/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async function tarFetch(nodeVersion: string) {
await downloadUrl(`${distUrl}/${tarName}`, archivePath);
}

async function tarExtract(nodeVersion: string) {
async function tarExtract(nodeVersion: string, suppressTarOutput: boolean) {
log.info('Extracting Node.js source archive...');

const tarName = `node-${nodeVersion}.tar.gz`;
Expand All @@ -130,7 +130,9 @@ async function tarExtract(nodeVersion: string) {
const extract = tar.extract(nodePath, {
strip: 1,
map: (header) => {
log.info(header.name);
if (!suppressTarOutput) {
log.info(header.name);
}
return header;
},
});
Expand Down Expand Up @@ -159,6 +161,12 @@ async function applyPatches(nodeVersion: string) {
}
}

export async function fetchExtractApply(nodeVersion: string, quietExtraction: boolean) {
await tarFetch(nodeVersion);
await tarExtract(nodeVersion, quietExtraction);
await applyPatches(nodeVersion);
}

async function compileOnWindows(
nodeVersion: string,
targetArch: string,
Expand Down Expand Up @@ -284,19 +292,20 @@ async function compile(
return compileOnUnix(nodeVersion, targetArch, targetPlatform);
}

export async function prepBuildPath() {
await fs.remove(buildPath);
await fs.mkdirp(nodePath);
await fs.mkdirp(nodeArchivePath);
}

export default async function build(
nodeVersion: string,
targetArch: string,
targetPlatform: string,
local: string
) {
await fs.remove(buildPath);
await fs.mkdirp(nodePath);
await fs.mkdirp(nodeArchivePath);

await tarFetch(nodeVersion);
await tarExtract(nodeVersion);
await applyPatches(nodeVersion);
await prepBuildPath();
await fetchExtractApply(nodeVersion, false);

const output = await compile(nodeVersion, targetArch, targetPlatform);
const outputHash = await hash(output);
Expand Down
50 changes: 29 additions & 21 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,23 @@ interface NeedOptions {
arch: string;
}

export async function need(opts: NeedOptions) {
// eslint-disable-line complexity
const { forceFetch, forceBuild, dryRun, output } = opts || {};
let { nodeRange, platform, arch } = opts || {};
export function satisfyingNodeVersion(nodeRange: string) {
const versions = Object.keys(patchesJson)
.filter((nv) => semver.satisfies(nv, nodeRange) || nodeRange === 'latest')
.sort((nv1, nv2) => (semver.gt(nv1, nv2) ? 1 : -1));

if (!nodeRange) throw wasReported('nodeRange not specified');
if (!platform) throw wasReported('platform not specified');
if (!arch) throw wasReported('arch not specified');
const nodeVersion = versions.pop();

if (!nodeVersion) {
throw wasReported(
`No available node version satisfies '${nodeRange}'`
);
}

return nodeVersion;
}

export function getNodeVersion(nodeRange: string) {
nodeRange = abiToNodeRange(nodeRange); // 'm48' -> 'node6'

if (!isValidNodeRange(nodeRange)) {
Expand All @@ -73,24 +81,24 @@ export async function need(opts: NeedOptions) {
nodeRange = `v${nodeRange.slice(4)}`; // 'node6' -> 'v6' for semver
}

platform = toFancyPlatform(platform); // win32 -> win
arch = toFancyArch(arch); // ia32 -> x86
const nodeVersion = satisfyingNodeVersion(nodeRange);
return nodeVersion;
}

function satisfyingNodeVersion() {
const versions = Object.keys(patchesJson)
.filter((nv) => semver.satisfies(nv, nodeRange) || nodeRange === 'latest')
.sort((nv1, nv2) => (semver.gt(nv1, nv2) ? 1 : -1));

return versions.pop();
}
export async function need(opts: NeedOptions) {
// eslint-disable-line complexity
const { forceFetch, forceBuild, dryRun, output, nodeRange } = opts || {};
let { platform, arch } = opts || {};

if (!nodeRange) throw wasReported('nodeRange not specified');
if (!platform) throw wasReported('platform not specified');
if (!arch) throw wasReported('arch not specified');

const nodeVersion = satisfyingNodeVersion();
platform = toFancyPlatform(platform); // win32 -> win
arch = toFancyArch(arch); // ia32 -> x86

if (!nodeVersion) {
throw wasReported(
`No available node version satisfies '${opts.nodeRange}'`
);
}
const nodeVersion = getNodeVersion(nodeRange);

const fetched = localPlace({
from: 'fetched',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"lint": "eslint lib",
"prepare": "npm run build",
"prepublishOnly": "npm run lint",
"start": "node lib-es5/bin.js"
"start": "node lib-es5/bin.js",
"applyPatches": "node lib-es5/apply-patches.js"
},
"prettier": {
"singleQuote": true
Expand Down

0 comments on commit 45a4b8d

Please sign in to comment.