@bravemobile/react-native-code-push 13.1.0-beta.5 → 13.1.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/buildPatchToolsCommand/buildPatchTools.test.ts +118 -0
- package/cli/commands/buildPatchToolsCommand/buildPatchTools.ts +46 -0
- package/cli/commands/buildPatchToolsCommand/index.ts +44 -0
- package/cli/dist/commands/buildPatchToolsCommand/buildPatchTools.js +34 -0
- package/cli/dist/commands/buildPatchToolsCommand/buildPatchTools.test.js +88 -0
- package/cli/dist/commands/buildPatchToolsCommand/index.js +32 -0
- package/cli/dist/index.js +4 -0
- package/cli/dist/utils/binaryPatch.js +8 -5
- package/cli/dist/utils/binaryPatch.test.js +1 -1
- package/cli/index.ts +5 -0
- package/cli/utils/binaryPatch.test.ts +1 -1
- package/cli/utils/binaryPatch.ts +8 -5
- package/package.json +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
|
|
5
|
+
import { buildPatchTools, hashBuildScript } from "./buildPatchTools.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The command is a thin wrapper around the build script this package ships. What it has
|
|
9
|
+
* to get right is what it hands the script: the directory the CLI will later look in, and
|
|
10
|
+
* `--force` when asked. A script that records what it was handed proves both without
|
|
11
|
+
* cloning and compiling HDiffPatch. The shipped script is run once at the end, against an
|
|
12
|
+
* install it already finds complete, to show the wrapper speaks its actual interface.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const REPO_ROOT = path.resolve(__dirname, "..", "..", "..");
|
|
16
|
+
const SHIPPED_BUILD_SCRIPT = path.join(REPO_ROOT, "scripts", "binary-patch", "build-hdiffpatch.sh");
|
|
17
|
+
/** Where `jest.globalSetup.ts` built the tools for this run. */
|
|
18
|
+
const INSTALLED_TOOLS_DIR = process.env.HDIFFPATCH_TOOLS_DIR || path.join(REPO_ROOT, ".hdiffpatch-tools");
|
|
19
|
+
|
|
20
|
+
let workDir: string;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
workDir = fs.mkdtempSync(path.join(os.tmpdir(), "build-patch-tools-"));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
fs.rmSync(workDir, { recursive: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function writeScript(body: string): string {
|
|
31
|
+
const scriptPath = path.join(workDir, "build.sh");
|
|
32
|
+
fs.writeFileSync(scriptPath, `#!/bin/sh\n${body}\n`, { mode: 0o755 });
|
|
33
|
+
return scriptPath;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A script that writes the install directory it was given, then each argument, one per line. */
|
|
37
|
+
function writeRecordingScript(): string {
|
|
38
|
+
return writeScript('printf \'%s\\n\' "$HDIFFPATCH_TOOLS_DIR" "$@" > "$HDIFFPATCH_TOOLS_DIR/invocation"');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readInvocation(toolsDir: string): string[] {
|
|
42
|
+
return fs.readFileSync(path.join(toolsDir, "invocation"), "utf8").trimEnd().split("\n");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("buildPatchTools", () => {
|
|
46
|
+
it("runs the build script against the tools directory it was given, without forcing a rebuild", () => {
|
|
47
|
+
const toolsDir = path.join(workDir, "tools");
|
|
48
|
+
fs.mkdirSync(toolsDir);
|
|
49
|
+
|
|
50
|
+
buildPatchTools({ buildScriptPath: writeRecordingScript(), toolsDir, force: false });
|
|
51
|
+
|
|
52
|
+
expect(readInvocation(toolsDir)).toEqual([toolsDir]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("asks the build script to rebuild when forced", () => {
|
|
56
|
+
const toolsDir = path.join(workDir, "tools");
|
|
57
|
+
fs.mkdirSync(toolsDir);
|
|
58
|
+
|
|
59
|
+
buildPatchTools({ buildScriptPath: writeRecordingScript(), toolsDir, force: true });
|
|
60
|
+
|
|
61
|
+
expect(readInvocation(toolsDir)).toEqual([toolsDir, "--force"]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("fails with the exit code when the build script fails", () => {
|
|
65
|
+
const buildScriptPath = writeScript("exit 3");
|
|
66
|
+
|
|
67
|
+
expect(() => buildPatchTools({ buildScriptPath, toolsDir: workDir, force: false })).toThrow(/exit code 3/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("fails naming the script when it cannot be started", () => {
|
|
71
|
+
const buildScriptPath = path.join(workDir, "no-such-script.sh");
|
|
72
|
+
|
|
73
|
+
expect(() => buildPatchTools({ buildScriptPath, toolsDir: workDir, force: false })).toThrow(
|
|
74
|
+
/no-such-script\.sh/,
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("succeeds against an install the shipped script already finds complete", () => {
|
|
79
|
+
// Without an install the shipped script clones and compiles for minutes, and
|
|
80
|
+
// `spawnSync` holds the worker past any jest timeout. `jest.globalSetup.ts`
|
|
81
|
+
// provides the install; this says so when it did not.
|
|
82
|
+
const missing = ["hdiffz", "hpatchz"].filter((tool) => !fs.existsSync(path.join(INSTALLED_TOOLS_DIR, tool)));
|
|
83
|
+
if (missing.length > 0) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`${missing.join(", ")} not installed in ${INSTALLED_TOOLS_DIR}: jest.globalSetup.ts should have built the tools first`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
expect(() =>
|
|
90
|
+
buildPatchTools({ buildScriptPath: SHIPPED_BUILD_SCRIPT, toolsDir: INSTALLED_TOOLS_DIR, force: false }),
|
|
91
|
+
).not.toThrow();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A CI cache of the installed tools is keyed by this hash, so it has to stay the same for as
|
|
97
|
+
* long as the script would build the same tools, and change as soon as it would not.
|
|
98
|
+
*/
|
|
99
|
+
describe("hashBuildScript", () => {
|
|
100
|
+
it("hashes two scripts with the same contents to the same hex digest", () => {
|
|
101
|
+
const first = path.join(workDir, "first.sh");
|
|
102
|
+
const second = path.join(workDir, "second.sh");
|
|
103
|
+
fs.writeFileSync(first, "#!/bin/sh\nHDIFFPATCH_TAG=v5.1.3\n");
|
|
104
|
+
fs.writeFileSync(second, "#!/bin/sh\nHDIFFPATCH_TAG=v5.1.3\n");
|
|
105
|
+
|
|
106
|
+
expect(hashBuildScript(first)).toBe(hashBuildScript(second));
|
|
107
|
+
expect(hashBuildScript(first)).toMatch(/^[0-9a-f]{64}$/);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("hashes a script whose pinned version changed to a different value", () => {
|
|
111
|
+
const before = path.join(workDir, "before.sh");
|
|
112
|
+
const after = path.join(workDir, "after.sh");
|
|
113
|
+
fs.writeFileSync(before, "#!/bin/sh\nHDIFFPATCH_TAG=v5.1.3\n");
|
|
114
|
+
fs.writeFileSync(after, "#!/bin/sh\nHDIFFPATCH_TAG=v5.1.4\n");
|
|
115
|
+
|
|
116
|
+
expect(hashBuildScript(after)).not.toBe(hashBuildScript(before));
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { TOOLS_DIR_ENV_NAME } from "../../utils/binaryPatch.js";
|
|
6
|
+
|
|
7
|
+
interface BuildPatchToolsOptions {
|
|
8
|
+
/** The `build-hdiffpatch.sh` this package ships. */
|
|
9
|
+
buildScriptPath: string;
|
|
10
|
+
/** Where the script installs `hdiffz` and `hpatchz`. It has to be a place the CLI looks in. */
|
|
11
|
+
toolsDir: string;
|
|
12
|
+
/** Rebuild even when both tools are already there. */
|
|
13
|
+
force: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Runs the build script with the install directory the caller chose.
|
|
18
|
+
*
|
|
19
|
+
* The script's output goes straight to the terminal: a first build clones and compiles
|
|
20
|
+
* HDiffPatch, which takes minutes, and a silent wait would look like a hang. So when the
|
|
21
|
+
* script fails, its own message has already been shown, and the error raised here only
|
|
22
|
+
* has to say that it did.
|
|
23
|
+
*/
|
|
24
|
+
export function buildPatchTools({ buildScriptPath, toolsDir, force }: BuildPatchToolsOptions): void {
|
|
25
|
+
const result = spawnSync(buildScriptPath, force ? ['--force'] : [], {
|
|
26
|
+
stdio: 'inherit',
|
|
27
|
+
env: { ...process.env, [TOOLS_DIR_ENV_NAME]: toolsDir },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (result.error) {
|
|
31
|
+
throw new Error(`failed to run ${buildScriptPath}: ${result.error.message}`);
|
|
32
|
+
}
|
|
33
|
+
if (result.status !== 0) {
|
|
34
|
+
const reason = result.status === null ? `signal ${result.signal}` : `exit code ${result.status}`;
|
|
35
|
+
throw new Error(`${path.basename(buildScriptPath)} failed with ${reason}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* SHA-256 of the build script's bytes, for keying a CI cache of the installed tools. The
|
|
41
|
+
* script pins the sources and the build flags, so it changes whenever the tools it would
|
|
42
|
+
* build do.
|
|
43
|
+
*/
|
|
44
|
+
export function hashBuildScript(buildScriptPath: string): string {
|
|
45
|
+
return crypto.createHash('sha256').update(fs.readFileSync(buildScriptPath)).digest('hex');
|
|
46
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { program } from "commander";
|
|
4
|
+
import { TOOLS_DIR_ENV_NAME, TOOLS_DIR_NAME } from "../../utils/binaryPatch.js";
|
|
5
|
+
import { buildPatchTools, hashBuildScript } from "./buildPatchTools.js";
|
|
6
|
+
|
|
7
|
+
type Options = {
|
|
8
|
+
toolsDir: string;
|
|
9
|
+
force: boolean;
|
|
10
|
+
printHash: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The CLI runs compiled, from `cli/dist/commands/buildPatchToolsCommand/`, which puts the
|
|
15
|
+
* package root - and the script shipped under it - four levels up.
|
|
16
|
+
*/
|
|
17
|
+
const BUILD_SCRIPT_PATH = fileURLToPath(
|
|
18
|
+
new URL("../../../../scripts/binary-patch/build-hdiffpatch.sh", import.meta.url),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Installs where `release` will look: `HDIFFPATCH_TOOLS_DIR` when it is set, and otherwise
|
|
23
|
+
* a `.hdiffpatch-tools` directory in the working directory, the first place the lookup
|
|
24
|
+
* checks before walking up.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_TOOLS_DIR = process.env[TOOLS_DIR_ENV_NAME] || path.resolve(process.cwd(), TOOLS_DIR_NAME);
|
|
27
|
+
|
|
28
|
+
program.command('build-patch-tools')
|
|
29
|
+
.description('Builds hdiffz and hpatchz from source and installs them where `release --binary-bundle-path` looks for them.\nThe build clones the pinned HDiffPatch sources, so it needs git, make, a C/C++ compiler and network access. It does nothing when the tools are already installed.')
|
|
30
|
+
.option('--tools-dir <path>', 'directory to install the tools into', DEFAULT_TOOLS_DIR)
|
|
31
|
+
.option('--force', 'rebuild even when the tools are already installed', false)
|
|
32
|
+
.option('--print-hash', 'print a hash of the build script, for keying a CI cache of the tools, and exit without building', false)
|
|
33
|
+
.action((options: Options) => {
|
|
34
|
+
if (options.printHash) {
|
|
35
|
+
console.log(hashBuildScript(BUILD_SCRIPT_PATH));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
buildPatchTools({
|
|
40
|
+
buildScriptPath: BUILD_SCRIPT_PATH,
|
|
41
|
+
toolsDir: path.resolve(options.toolsDir),
|
|
42
|
+
force: options.force,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { TOOLS_DIR_ENV_NAME } from "../../utils/binaryPatch.js";
|
|
6
|
+
/**
|
|
7
|
+
* Runs the build script with the install directory the caller chose.
|
|
8
|
+
*
|
|
9
|
+
* The script's output goes straight to the terminal: a first build clones and compiles
|
|
10
|
+
* HDiffPatch, which takes minutes, and a silent wait would look like a hang. So when the
|
|
11
|
+
* script fails, its own message has already been shown, and the error raised here only
|
|
12
|
+
* has to say that it did.
|
|
13
|
+
*/
|
|
14
|
+
export function buildPatchTools({ buildScriptPath, toolsDir, force }) {
|
|
15
|
+
const result = spawnSync(buildScriptPath, force ? ['--force'] : [], {
|
|
16
|
+
stdio: 'inherit',
|
|
17
|
+
env: { ...process.env, [TOOLS_DIR_ENV_NAME]: toolsDir },
|
|
18
|
+
});
|
|
19
|
+
if (result.error) {
|
|
20
|
+
throw new Error(`failed to run ${buildScriptPath}: ${result.error.message}`);
|
|
21
|
+
}
|
|
22
|
+
if (result.status !== 0) {
|
|
23
|
+
const reason = result.status === null ? `signal ${result.signal}` : `exit code ${result.status}`;
|
|
24
|
+
throw new Error(`${path.basename(buildScriptPath)} failed with ${reason}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* SHA-256 of the build script's bytes, for keying a CI cache of the installed tools. The
|
|
29
|
+
* script pins the sources and the build flags, so it changes whenever the tools it would
|
|
30
|
+
* build do.
|
|
31
|
+
*/
|
|
32
|
+
export function hashBuildScript(buildScriptPath) {
|
|
33
|
+
return crypto.createHash('sha256').update(fs.readFileSync(buildScriptPath)).digest('hex');
|
|
34
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
|
|
5
|
+
import { buildPatchTools, hashBuildScript } from "./buildPatchTools.js";
|
|
6
|
+
/**
|
|
7
|
+
* The command is a thin wrapper around the build script this package ships. What it has
|
|
8
|
+
* to get right is what it hands the script: the directory the CLI will later look in, and
|
|
9
|
+
* `--force` when asked. A script that records what it was handed proves both without
|
|
10
|
+
* cloning and compiling HDiffPatch. The shipped script is run once at the end, against an
|
|
11
|
+
* install it already finds complete, to show the wrapper speaks its actual interface.
|
|
12
|
+
*/
|
|
13
|
+
const REPO_ROOT = path.resolve(__dirname, "..", "..", "..");
|
|
14
|
+
const SHIPPED_BUILD_SCRIPT = path.join(REPO_ROOT, "scripts", "binary-patch", "build-hdiffpatch.sh");
|
|
15
|
+
/** Where `jest.globalSetup.ts` built the tools for this run. */
|
|
16
|
+
const INSTALLED_TOOLS_DIR = process.env.HDIFFPATCH_TOOLS_DIR || path.join(REPO_ROOT, ".hdiffpatch-tools");
|
|
17
|
+
let workDir;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
workDir = fs.mkdtempSync(path.join(os.tmpdir(), "build-patch-tools-"));
|
|
20
|
+
});
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
fs.rmSync(workDir, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
function writeScript(body) {
|
|
25
|
+
const scriptPath = path.join(workDir, "build.sh");
|
|
26
|
+
fs.writeFileSync(scriptPath, `#!/bin/sh\n${body}\n`, { mode: 0o755 });
|
|
27
|
+
return scriptPath;
|
|
28
|
+
}
|
|
29
|
+
/** A script that writes the install directory it was given, then each argument, one per line. */
|
|
30
|
+
function writeRecordingScript() {
|
|
31
|
+
return writeScript('printf \'%s\\n\' "$HDIFFPATCH_TOOLS_DIR" "$@" > "$HDIFFPATCH_TOOLS_DIR/invocation"');
|
|
32
|
+
}
|
|
33
|
+
function readInvocation(toolsDir) {
|
|
34
|
+
return fs.readFileSync(path.join(toolsDir, "invocation"), "utf8").trimEnd().split("\n");
|
|
35
|
+
}
|
|
36
|
+
describe("buildPatchTools", () => {
|
|
37
|
+
it("runs the build script against the tools directory it was given, without forcing a rebuild", () => {
|
|
38
|
+
const toolsDir = path.join(workDir, "tools");
|
|
39
|
+
fs.mkdirSync(toolsDir);
|
|
40
|
+
buildPatchTools({ buildScriptPath: writeRecordingScript(), toolsDir, force: false });
|
|
41
|
+
expect(readInvocation(toolsDir)).toEqual([toolsDir]);
|
|
42
|
+
});
|
|
43
|
+
it("asks the build script to rebuild when forced", () => {
|
|
44
|
+
const toolsDir = path.join(workDir, "tools");
|
|
45
|
+
fs.mkdirSync(toolsDir);
|
|
46
|
+
buildPatchTools({ buildScriptPath: writeRecordingScript(), toolsDir, force: true });
|
|
47
|
+
expect(readInvocation(toolsDir)).toEqual([toolsDir, "--force"]);
|
|
48
|
+
});
|
|
49
|
+
it("fails with the exit code when the build script fails", () => {
|
|
50
|
+
const buildScriptPath = writeScript("exit 3");
|
|
51
|
+
expect(() => buildPatchTools({ buildScriptPath, toolsDir: workDir, force: false })).toThrow(/exit code 3/);
|
|
52
|
+
});
|
|
53
|
+
it("fails naming the script when it cannot be started", () => {
|
|
54
|
+
const buildScriptPath = path.join(workDir, "no-such-script.sh");
|
|
55
|
+
expect(() => buildPatchTools({ buildScriptPath, toolsDir: workDir, force: false })).toThrow(/no-such-script\.sh/);
|
|
56
|
+
});
|
|
57
|
+
it("succeeds against an install the shipped script already finds complete", () => {
|
|
58
|
+
// Without an install the shipped script clones and compiles for minutes, and
|
|
59
|
+
// `spawnSync` holds the worker past any jest timeout. `jest.globalSetup.ts`
|
|
60
|
+
// provides the install; this says so when it did not.
|
|
61
|
+
const missing = ["hdiffz", "hpatchz"].filter((tool) => !fs.existsSync(path.join(INSTALLED_TOOLS_DIR, tool)));
|
|
62
|
+
if (missing.length > 0) {
|
|
63
|
+
throw new Error(`${missing.join(", ")} not installed in ${INSTALLED_TOOLS_DIR}: jest.globalSetup.ts should have built the tools first`);
|
|
64
|
+
}
|
|
65
|
+
expect(() => buildPatchTools({ buildScriptPath: SHIPPED_BUILD_SCRIPT, toolsDir: INSTALLED_TOOLS_DIR, force: false })).not.toThrow();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* A CI cache of the installed tools is keyed by this hash, so it has to stay the same for as
|
|
70
|
+
* long as the script would build the same tools, and change as soon as it would not.
|
|
71
|
+
*/
|
|
72
|
+
describe("hashBuildScript", () => {
|
|
73
|
+
it("hashes two scripts with the same contents to the same hex digest", () => {
|
|
74
|
+
const first = path.join(workDir, "first.sh");
|
|
75
|
+
const second = path.join(workDir, "second.sh");
|
|
76
|
+
fs.writeFileSync(first, "#!/bin/sh\nHDIFFPATCH_TAG=v5.1.3\n");
|
|
77
|
+
fs.writeFileSync(second, "#!/bin/sh\nHDIFFPATCH_TAG=v5.1.3\n");
|
|
78
|
+
expect(hashBuildScript(first)).toBe(hashBuildScript(second));
|
|
79
|
+
expect(hashBuildScript(first)).toMatch(/^[0-9a-f]{64}$/);
|
|
80
|
+
});
|
|
81
|
+
it("hashes a script whose pinned version changed to a different value", () => {
|
|
82
|
+
const before = path.join(workDir, "before.sh");
|
|
83
|
+
const after = path.join(workDir, "after.sh");
|
|
84
|
+
fs.writeFileSync(before, "#!/bin/sh\nHDIFFPATCH_TAG=v5.1.3\n");
|
|
85
|
+
fs.writeFileSync(after, "#!/bin/sh\nHDIFFPATCH_TAG=v5.1.4\n");
|
|
86
|
+
expect(hashBuildScript(after)).not.toBe(hashBuildScript(before));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { program } from "commander";
|
|
4
|
+
import { TOOLS_DIR_ENV_NAME, TOOLS_DIR_NAME } from "../../utils/binaryPatch.js";
|
|
5
|
+
import { buildPatchTools, hashBuildScript } from "./buildPatchTools.js";
|
|
6
|
+
/**
|
|
7
|
+
* The CLI runs compiled, from `cli/dist/commands/buildPatchToolsCommand/`, which puts the
|
|
8
|
+
* package root - and the script shipped under it - four levels up.
|
|
9
|
+
*/
|
|
10
|
+
const BUILD_SCRIPT_PATH = fileURLToPath(new URL("../../../../scripts/binary-patch/build-hdiffpatch.sh", import.meta.url));
|
|
11
|
+
/**
|
|
12
|
+
* Installs where `release` will look: `HDIFFPATCH_TOOLS_DIR` when it is set, and otherwise
|
|
13
|
+
* a `.hdiffpatch-tools` directory in the working directory, the first place the lookup
|
|
14
|
+
* checks before walking up.
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_TOOLS_DIR = process.env[TOOLS_DIR_ENV_NAME] || path.resolve(process.cwd(), TOOLS_DIR_NAME);
|
|
17
|
+
program.command('build-patch-tools')
|
|
18
|
+
.description('Builds hdiffz and hpatchz from source and installs them where `release --binary-bundle-path` looks for them.\nThe build clones the pinned HDiffPatch sources, so it needs git, make, a C/C++ compiler and network access. It does nothing when the tools are already installed.')
|
|
19
|
+
.option('--tools-dir <path>', 'directory to install the tools into', DEFAULT_TOOLS_DIR)
|
|
20
|
+
.option('--force', 'rebuild even when the tools are already installed', false)
|
|
21
|
+
.option('--print-hash', 'print a hash of the build script, for keying a CI cache of the tools, and exit without building', false)
|
|
22
|
+
.action((options) => {
|
|
23
|
+
if (options.printHash) {
|
|
24
|
+
console.log(hashBuildScript(BUILD_SCRIPT_PATH));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
buildPatchTools({
|
|
28
|
+
buildScriptPath: BUILD_SCRIPT_PATH,
|
|
29
|
+
toolsDir: path.resolve(options.toolsDir),
|
|
30
|
+
force: options.force,
|
|
31
|
+
});
|
|
32
|
+
});
|
package/cli/dist/index.js
CHANGED
|
@@ -26,6 +26,10 @@ import "./commands/showHistoryCommand/index.js";
|
|
|
26
26
|
* npx code-push init
|
|
27
27
|
*/
|
|
28
28
|
import "./commands/initCommand/index.js";
|
|
29
|
+
/**
|
|
30
|
+
* npx code-push build-patch-tools
|
|
31
|
+
*/
|
|
32
|
+
import "./commands/buildPatchToolsCommand/index.js";
|
|
29
33
|
shell.set("-e");
|
|
30
34
|
shell.set("+v");
|
|
31
35
|
program
|
|
@@ -31,14 +31,17 @@ export const BINARY_PATCH_ALGORITHM = 'hdiffpatch-m-zstd';
|
|
|
31
31
|
export const BINARY_PATCH_FORMAT_VERSION = 1;
|
|
32
32
|
const HDIFFZ_OPTIONS = ['-f', '-m-6', '-c-zstd-21-24'];
|
|
33
33
|
const HPATCHZ_OPTIONS = ['-f', '-m'];
|
|
34
|
-
const TOOLS_DIR_ENV_NAME = 'HDIFFPATCH_TOOLS_DIR';
|
|
35
|
-
const TOOLS_DIR_NAME = '.hdiffpatch-tools';
|
|
36
|
-
const
|
|
34
|
+
export const TOOLS_DIR_ENV_NAME = 'HDIFFPATCH_TOOLS_DIR';
|
|
35
|
+
export const TOOLS_DIR_NAME = '.hdiffpatch-tools';
|
|
36
|
+
const BUILD_COMMAND = 'npx code-push build-patch-tools';
|
|
37
37
|
/**
|
|
38
38
|
* Finds the hdiffz/hpatchz executable, looking at `HDIFFPATCH_TOOLS_DIR` first and
|
|
39
39
|
* then at a `.hdiffpatch-tools` directory in the working directory or any directory
|
|
40
40
|
* above it. The tools are built from source rather than installed as a package
|
|
41
41
|
* dependency, so the error explains how to get them.
|
|
42
|
+
*
|
|
43
|
+
* `build-patch-tools` (`cli/commands/buildPatchToolsCommand/index.ts`) installs into the
|
|
44
|
+
* first place this looks; a change to the lookup order has to be mirrored there.
|
|
42
45
|
*/
|
|
43
46
|
export function resolveBinaryPatchTool(tool) {
|
|
44
47
|
const configuredDir = process.env[TOOLS_DIR_ENV_NAME];
|
|
@@ -48,7 +51,7 @@ export function resolveBinaryPatchTool(tool) {
|
|
|
48
51
|
return configured;
|
|
49
52
|
}
|
|
50
53
|
throw new Error(`${TOOLS_DIR_ENV_NAME} is set to '${configuredDir}' but it does not contain '${tool}'. ` +
|
|
51
|
-
`Build the tools with '${
|
|
54
|
+
`Build the tools with '${BUILD_COMMAND}'.`);
|
|
52
55
|
}
|
|
53
56
|
let directory = path.resolve(process.cwd());
|
|
54
57
|
for (;;) {
|
|
@@ -63,7 +66,7 @@ export function resolveBinaryPatchTool(tool) {
|
|
|
63
66
|
directory = parent;
|
|
64
67
|
}
|
|
65
68
|
throw new Error(`'${tool}' not found in any '${TOOLS_DIR_NAME}' directory at or above '${process.cwd()}'. ` +
|
|
66
|
-
`Build it with '${
|
|
69
|
+
`Build it with '${BUILD_COMMAND}', or set ${TOOLS_DIR_ENV_NAME} to a directory that contains it.`);
|
|
67
70
|
}
|
|
68
71
|
/** Writes the patch that turns the bundle at `basePath` into the one at `targetPath`. */
|
|
69
72
|
export function generatePatch(basePath, targetPath, patchPath) {
|
|
@@ -82,7 +82,7 @@ describe("generatePatch/applyPatch", () => {
|
|
|
82
82
|
const previous = process.env.HDIFFPATCH_TOOLS_DIR;
|
|
83
83
|
process.env.HDIFFPATCH_TOOLS_DIR = workPath("no-such-dir");
|
|
84
84
|
try {
|
|
85
|
-
expect(() => resolveBinaryPatchTool("hdiffz")).toThrow(/build-
|
|
85
|
+
expect(() => resolveBinaryPatchTool("hdiffz")).toThrow(/npx code-push build-patch-tools/);
|
|
86
86
|
}
|
|
87
87
|
finally {
|
|
88
88
|
if (previous === undefined) {
|
package/cli/index.ts
CHANGED
|
@@ -34,6 +34,11 @@ import "./commands/showHistoryCommand/index.js";
|
|
|
34
34
|
*/
|
|
35
35
|
import "./commands/initCommand/index.js";
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* npx code-push build-patch-tools
|
|
39
|
+
*/
|
|
40
|
+
import "./commands/buildPatchToolsCommand/index.js";
|
|
41
|
+
|
|
37
42
|
shell.set("-e");
|
|
38
43
|
shell.set("+v");
|
|
39
44
|
|
|
@@ -101,7 +101,7 @@ describe("generatePatch/applyPatch", () => {
|
|
|
101
101
|
const previous = process.env.HDIFFPATCH_TOOLS_DIR;
|
|
102
102
|
process.env.HDIFFPATCH_TOOLS_DIR = workPath("no-such-dir");
|
|
103
103
|
try {
|
|
104
|
-
expect(() => resolveBinaryPatchTool("hdiffz")).toThrow(/build-
|
|
104
|
+
expect(() => resolveBinaryPatchTool("hdiffz")).toThrow(/npx code-push build-patch-tools/);
|
|
105
105
|
} finally {
|
|
106
106
|
if (previous === undefined) {
|
|
107
107
|
delete process.env.HDIFFPATCH_TOOLS_DIR;
|
package/cli/utils/binaryPatch.ts
CHANGED
|
@@ -37,15 +37,18 @@ export type BinaryPatchTool = 'hdiffz' | 'hpatchz';
|
|
|
37
37
|
const HDIFFZ_OPTIONS = ['-f', '-m-6', '-c-zstd-21-24'];
|
|
38
38
|
const HPATCHZ_OPTIONS = ['-f', '-m'];
|
|
39
39
|
|
|
40
|
-
const TOOLS_DIR_ENV_NAME = 'HDIFFPATCH_TOOLS_DIR';
|
|
41
|
-
const TOOLS_DIR_NAME = '.hdiffpatch-tools';
|
|
42
|
-
const
|
|
40
|
+
export const TOOLS_DIR_ENV_NAME = 'HDIFFPATCH_TOOLS_DIR';
|
|
41
|
+
export const TOOLS_DIR_NAME = '.hdiffpatch-tools';
|
|
42
|
+
const BUILD_COMMAND = 'npx code-push build-patch-tools';
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Finds the hdiffz/hpatchz executable, looking at `HDIFFPATCH_TOOLS_DIR` first and
|
|
46
46
|
* then at a `.hdiffpatch-tools` directory in the working directory or any directory
|
|
47
47
|
* above it. The tools are built from source rather than installed as a package
|
|
48
48
|
* dependency, so the error explains how to get them.
|
|
49
|
+
*
|
|
50
|
+
* `build-patch-tools` (`cli/commands/buildPatchToolsCommand/index.ts`) installs into the
|
|
51
|
+
* first place this looks; a change to the lookup order has to be mirrored there.
|
|
49
52
|
*/
|
|
50
53
|
export function resolveBinaryPatchTool(tool: BinaryPatchTool): string {
|
|
51
54
|
const configuredDir = process.env[TOOLS_DIR_ENV_NAME];
|
|
@@ -56,7 +59,7 @@ export function resolveBinaryPatchTool(tool: BinaryPatchTool): string {
|
|
|
56
59
|
}
|
|
57
60
|
throw new Error(
|
|
58
61
|
`${TOOLS_DIR_ENV_NAME} is set to '${configuredDir}' but it does not contain '${tool}'. ` +
|
|
59
|
-
`Build the tools with '${
|
|
62
|
+
`Build the tools with '${BUILD_COMMAND}'.`,
|
|
60
63
|
);
|
|
61
64
|
}
|
|
62
65
|
|
|
@@ -75,7 +78,7 @@ export function resolveBinaryPatchTool(tool: BinaryPatchTool): string {
|
|
|
75
78
|
|
|
76
79
|
throw new Error(
|
|
77
80
|
`'${tool}' not found in any '${TOOLS_DIR_NAME}' directory at or above '${process.cwd()}'. ` +
|
|
78
|
-
`Build it with '${
|
|
81
|
+
`Build it with '${BUILD_COMMAND}', or set ${TOOLS_DIR_ENV_NAME} to a directory that contains it.`,
|
|
79
82
|
);
|
|
80
83
|
}
|
|
81
84
|
|
package/package.json
CHANGED