@zukhruf/git-hooks 0.3.41 → 0.3.43
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/README.md +157 -3
- package/dist/build.d.ts +15 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +163 -0
- package/dist/build.js.map +1 -0
- package/dist/codegen.d.ts +1 -1
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js.map +1 -1
- package/dist/commit.d.ts.map +1 -1
- package/dist/commit.js +2 -1
- package/dist/commit.js.map +1 -1
- package/dist/format.d.ts +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +5 -2
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/push.d.ts +29 -0
- package/dist/push.d.ts.map +1 -0
- package/dist/push.js +108 -0
- package/dist/push.js.map +1 -0
- package/dist/ref-update.d.ts +17 -0
- package/dist/ref-update.d.ts.map +1 -0
- package/dist/ref-update.js +43 -0
- package/dist/ref-update.js.map +1 -0
- package/dist/scripts/pre-commit.d.ts +2 -1
- package/dist/scripts/pre-commit.d.ts.map +1 -1
- package/dist/scripts/pre-commit.js.map +1 -1
- package/dist/scripts/pre-push.d.ts +4 -0
- package/dist/scripts/pre-push.d.ts.map +1 -0
- package/dist/scripts/pre-push.js +35 -0
- package/dist/scripts/pre-push.js.map +1 -0
- package/dist/snapshot/commit-files.d.ts +5 -0
- package/dist/snapshot/commit-files.d.ts.map +1 -0
- package/dist/snapshot/commit-files.js +101 -0
- package/dist/snapshot/commit-files.js.map +1 -0
- package/dist/snapshot/drift.d.ts +3 -0
- package/dist/snapshot/drift.d.ts.map +1 -0
- package/dist/snapshot/drift.js +72 -0
- package/dist/snapshot/drift.js.map +1 -0
- package/dist/snapshot/installs.d.ts +2 -0
- package/dist/snapshot/installs.d.ts.map +1 -0
- package/dist/snapshot/installs.js +83 -0
- package/dist/snapshot/installs.js.map +1 -0
- package/dist/snapshot/nx.d.ts +7 -0
- package/dist/snapshot/nx.d.ts.map +1 -0
- package/dist/snapshot/nx.js +20 -0
- package/dist/snapshot/nx.js.map +1 -0
- package/dist/snapshot/packages.d.ts +2 -0
- package/dist/snapshot/packages.d.ts.map +1 -0
- package/dist/snapshot/packages.js +72 -0
- package/dist/snapshot/packages.js.map +1 -0
- package/dist/snapshot/workspaces.d.ts +2 -0
- package/dist/snapshot/workspaces.d.ts.map +1 -0
- package/dist/snapshot/workspaces.js +26 -0
- package/dist/snapshot/workspaces.js.map +1 -0
- package/dist/step.d.ts +2 -3
- package/dist/step.d.ts.map +1 -1
- package/dist/step.js +2 -2
- package/dist/step.js.map +1 -1
- package/dist/sync.d.ts +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js.map +1 -1
- package/package.json +4 -1
package/dist/push.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdir, mkdtempDisposable, realpath } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
8
|
+
import { FileLock } from '@zukhruf/fs';
|
|
9
|
+
import { readRefUpdates } from "./ref-update.js";
|
|
10
|
+
import { writeCommitFiles } from "./snapshot/commit-files.js";
|
|
11
|
+
import { hasInstallDrift } from "./snapshot/drift.js";
|
|
12
|
+
import { installFor } from "./snapshot/installs.js";
|
|
13
|
+
import { runNx } from "./snapshot/nx.js";
|
|
14
|
+
import { linkPackages } from "./snapshot/packages.js";
|
|
15
|
+
const execFileAsync = promisify(execFile);
|
|
16
|
+
export class MissingLockfileError extends Error {
|
|
17
|
+
sha;
|
|
18
|
+
constructor(sha) {
|
|
19
|
+
super(`${sha.slice(0, 7)} has no package-lock.json`);
|
|
20
|
+
this.name = 'MissingLockfileError';
|
|
21
|
+
this.sha = sha;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// What `git push` is about to send: the remote and the ref updates. A step
|
|
25
|
+
// judges the pushed commits in snapshots, never the working tree, because
|
|
26
|
+
// other sessions can edit the working tree while the hook runs.
|
|
27
|
+
export class Push {
|
|
28
|
+
remote;
|
|
29
|
+
updates;
|
|
30
|
+
#root;
|
|
31
|
+
#environment;
|
|
32
|
+
#notice;
|
|
33
|
+
constructor({ root, environment, remote, notice }, updates) {
|
|
34
|
+
this.remote = remote;
|
|
35
|
+
this.updates = updates;
|
|
36
|
+
this.#root = root;
|
|
37
|
+
this.#environment = environment;
|
|
38
|
+
this.#notice = notice;
|
|
39
|
+
}
|
|
40
|
+
static async read(options) {
|
|
41
|
+
const git = (...args) => gitIn(options.root, options.environment, ...args);
|
|
42
|
+
return new Push(options, await readRefUpdates(git, options.remote.name, options.input));
|
|
43
|
+
}
|
|
44
|
+
// Runs `work` on the snapshot of one commit: its files in a temporary
|
|
45
|
+
// folder, with a node_modules that a build can use. `environment` makes git
|
|
46
|
+
// see a clean checkout of the commit there, and holds the nx cache of the
|
|
47
|
+
// state folder. One snapshot is open at a time in a repo; the snapshot is
|
|
48
|
+
// deleted after `work`.
|
|
49
|
+
async inSnapshot(sha, work) {
|
|
50
|
+
const state = join((await this.#git('rev-parse', '--path-format=absolute', '--git-common-dir')).trim(), 'git-hooks');
|
|
51
|
+
await mkdir(state, { recursive: true });
|
|
52
|
+
using lock = FileLock.open(join(state, 'lock'));
|
|
53
|
+
if (!lock.tryLock()) {
|
|
54
|
+
this.#notice('waiting for another pre-push build in this repo');
|
|
55
|
+
while (!lock.tryLock())
|
|
56
|
+
await delay(200);
|
|
57
|
+
}
|
|
58
|
+
await using temporary = await mkdtempDisposable(join(tmpdir(), 'git-hooks-push-'));
|
|
59
|
+
const top = await realpath(temporary.path);
|
|
60
|
+
const folder = join(top, 'tree');
|
|
61
|
+
await writeCommitFiles((...args) => this.#git(...args), { cwd: this.#root, env: this.#environment }, sha, folder);
|
|
62
|
+
if (!existsSync(join(folder, 'package-lock.json')))
|
|
63
|
+
throw new MissingLockfileError(sha);
|
|
64
|
+
const installed = (await hasInstallDrift(join(folder, 'package-lock.json'), join(this.#root, 'node_modules/.package-lock.json')))
|
|
65
|
+
? await installFor(state, folder, this.#environment, () => this.#notice(`${sha.slice(0, 7)}: the installed packages differ from package-lock.json; installing it with npm ci (first time for this lockfile)`))
|
|
66
|
+
: this.#root;
|
|
67
|
+
await linkPackages(installed, folder);
|
|
68
|
+
const cache = join(state, 'nx');
|
|
69
|
+
const environment = {
|
|
70
|
+
...this.#environment,
|
|
71
|
+
GIT_DIR: (await this.#git('rev-parse', '--absolute-git-dir')).trim(),
|
|
72
|
+
GIT_WORK_TREE: folder,
|
|
73
|
+
GIT_INDEX_FILE: join(top, 'index'),
|
|
74
|
+
NX_DAEMON: 'false',
|
|
75
|
+
NX_CACHE_DIRECTORY: cache,
|
|
76
|
+
NX_WORKSPACE_DATA_DIRECTORY: join(cache, 'workspace-data'),
|
|
77
|
+
};
|
|
78
|
+
// A temporary index of the commit, refreshed against the snapshot, so git
|
|
79
|
+
// finds no changed and no untracked files.
|
|
80
|
+
await gitIn(folder, environment, 'read-tree', sha);
|
|
81
|
+
await gitIn(folder, environment, 'update-index', '-q', '--refresh');
|
|
82
|
+
// Awaited here, so the snapshot and the lock stay until `work` ends.
|
|
83
|
+
return await work(folder, environment);
|
|
84
|
+
}
|
|
85
|
+
// Runs nx in the snapshot of one commit, and gives its standard output.
|
|
86
|
+
// When nx exits with a code other than 0, it throws.
|
|
87
|
+
nx(sha, ...args) {
|
|
88
|
+
return this.inSnapshot(sha, async (folder, environment) => {
|
|
89
|
+
const { code, stdout, stderr } = await runNx(folder, environment, args);
|
|
90
|
+
if (code !== 0)
|
|
91
|
+
throw new Error(`nx ${args.join(' ')} exited with ${code}:\n${`${stdout}${stderr}`.trimEnd()}`);
|
|
92
|
+
return stdout;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
#git(...args) {
|
|
96
|
+
return gitIn(this.#root, this.#environment, ...args);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function gitIn(cwd, environment, ...args) {
|
|
100
|
+
const { stdout } = await execFileAsync('git', args, {
|
|
101
|
+
cwd,
|
|
102
|
+
env: environment,
|
|
103
|
+
encoding: 'utf8',
|
|
104
|
+
maxBuffer: 2 ** 30,
|
|
105
|
+
});
|
|
106
|
+
return stdout;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=push.js.map
|
package/dist/push.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface RefLine {
|
|
2
|
+
readonly localRef: string;
|
|
3
|
+
readonly localSha: string;
|
|
4
|
+
readonly remoteRef: string;
|
|
5
|
+
readonly remoteSha: string;
|
|
6
|
+
}
|
|
7
|
+
export type RefUpdate = (RefLine & {
|
|
8
|
+
readonly kind: 'create' | 'update';
|
|
9
|
+
readonly head: string;
|
|
10
|
+
readonly base: string | undefined;
|
|
11
|
+
}) | (RefLine & {
|
|
12
|
+
readonly kind: 'delete';
|
|
13
|
+
});
|
|
14
|
+
type Git = (...args: string[]) => Promise<string>;
|
|
15
|
+
export declare function readRefUpdates(git: Git, remote: string, input: string): Promise<RefUpdate[]>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=ref-update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ref-update.d.ts","sourceRoot":"","sources":["../src/ref-update.ts"],"names":[],"mappings":"AAEA,UAAU,OAAO;IACf,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAKD,MAAM,MAAM,SAAS,GACjB,CAAC,OAAO,GAAG;IACT,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,CAAC,GACF,CAAC,OAAO,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC,CAAC;AAE5C,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAIlD,wBAAsB,cAAc,CAClC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,SAAS,EAAE,CAAC,CAoBtB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const absent = /^0+$/;
|
|
2
|
+
export async function readRefUpdates(git, remote, input) {
|
|
3
|
+
const updates = [];
|
|
4
|
+
for (const line of input.split('\n')) {
|
|
5
|
+
if (line.trim() === '')
|
|
6
|
+
continue;
|
|
7
|
+
const [localRef = '', localSha = '', remoteRef = '', remoteSha = ''] = line
|
|
8
|
+
.trim()
|
|
9
|
+
.split(' ');
|
|
10
|
+
const refLine = { localRef, localSha, remoteRef, remoteSha };
|
|
11
|
+
if (absent.test(localSha)) {
|
|
12
|
+
updates.push({ ...refLine, kind: 'delete' });
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const kind = absent.test(remoteSha) ? 'create' : 'update';
|
|
16
|
+
const base = kind === 'update' && (await isLocal(git, remoteSha))
|
|
17
|
+
? remoteSha
|
|
18
|
+
: await firstUnseenParent(git, remote, localSha);
|
|
19
|
+
updates.push({ ...refLine, kind, head: localSha, base });
|
|
20
|
+
}
|
|
21
|
+
return updates;
|
|
22
|
+
}
|
|
23
|
+
async function isLocal(git, sha) {
|
|
24
|
+
try {
|
|
25
|
+
await git('cat-file', '-e', `${sha}^{commit}`);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// The parent of the first commit that no ref of the remote has, as the
|
|
33
|
+
// pre-commit framework finds the "from" ref of its pre-push stage.
|
|
34
|
+
async function firstUnseenParent(git, remote, head) {
|
|
35
|
+
const unseen = await git('rev-list', head, '--topo-order', '--reverse', '--not', `--remotes=${remote}`);
|
|
36
|
+
const [first = ''] = unseen.split('\n', 1);
|
|
37
|
+
if (first === '')
|
|
38
|
+
return head;
|
|
39
|
+
const parents = await git('rev-list', '--parents', '--max-count=1', first);
|
|
40
|
+
const [, parent] = parents.trim().split(' ');
|
|
41
|
+
return parent;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ref-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ref-update.js","sourceRoot":"","sources":["../src/ref-update.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,GAAG,MAAM,CAAC;AAEtB,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAQ,EACR,MAAc,EACd,KAAa;IAEb,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAS;QACjC,MAAM,CAAC,QAAQ,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,SAAS,GAAG,EAAE,EAAE,SAAS,GAAG,EAAE,CAAC,GAAG,IAAI;aACxE,IAAI,EAAE;aACN,KAAK,CAAC,GAAG,CAAC,CAAC;QACd,MAAM,OAAO,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;QAC7D,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7C,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1D,MAAM,IAAI,GACR,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAClD,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,MAAM,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,GAAQ,EAAE,GAAW;IAC1C,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,GAAG,WAAW,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,mEAAmE;AACnE,KAAK,UAAU,iBAAiB,CAC9B,GAAQ,EACR,MAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,MAAM,GAAG,CACtB,UAAU,EACV,IAAI,EACJ,cAAc,EACd,WAAW,EACX,OAAO,EACP,aAAa,MAAM,EAAE,CACtB,CAAC;IACF,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Commit } from '../commit.ts';
|
|
1
2
|
import type { Step } from '../step.ts';
|
|
2
|
-
export declare function preCommit(steps: readonly Step[]): Promise<void>;
|
|
3
|
+
export declare function preCommit(steps: readonly Step<Commit>[]): Promise<void>;
|
|
3
4
|
//# sourceMappingURL=pre-commit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pre-commit.d.ts","sourceRoot":"","sources":["../../src/scripts/pre-commit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pre-commit.d.ts","sourceRoot":"","sources":["../../src/scripts/pre-commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAKvC,wBAAsB,SAAS,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAa7E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pre-commit.js","sourceRoot":"","sources":["../../src/scripts/pre-commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAGtC,+EAA+E;AAC/E,2EAA2E;AAC3E,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"pre-commit.js","sourceRoot":"","sources":["../../src/scripts/pre-commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAGtC,+EAA+E;AAC/E,2EAA2E;AAC3E,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAA8B;IAC5D,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1D,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QACjD,KAAK,MAAM,IAAI,IAAI,IAAI;YAAE,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;QACnE,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;YACzD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-push.d.ts","sourceRoot":"","sources":["../../src/scripts/pre-push.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAOvC,wBAAsB,OAAO,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBzE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { text } from 'node:stream/consumers';
|
|
2
|
+
import { Push } from "../push.js";
|
|
3
|
+
// What a repo's `.husky/pre-push.ts` calls. Git runs the hook at the root of
|
|
4
|
+
// the work tree, with the name and the URL of the remote as arguments and one
|
|
5
|
+
// ref update on each line of the standard input. This is the one module that
|
|
6
|
+
// prints and reads the arguments, the input and the environment, as a script
|
|
7
|
+
// may.
|
|
8
|
+
export async function prePush(steps) {
|
|
9
|
+
let current = '';
|
|
10
|
+
const print = (name, message) => {
|
|
11
|
+
const [first = '', ...rest] = message.split('\n');
|
|
12
|
+
console.error(`${name.padEnd(9)} ${first}`);
|
|
13
|
+
for (const line of rest)
|
|
14
|
+
console.error(`${' '.repeat(10)}${line}`);
|
|
15
|
+
};
|
|
16
|
+
const [name = '', url = ''] = process.argv.slice(2);
|
|
17
|
+
const push = await Push.read({
|
|
18
|
+
root: process.cwd(),
|
|
19
|
+
environment: process.env,
|
|
20
|
+
remote: { name, url },
|
|
21
|
+
input: await text(process.stdin),
|
|
22
|
+
notice: (message) => print(current, message),
|
|
23
|
+
});
|
|
24
|
+
for (const step of steps) {
|
|
25
|
+
current = step.name;
|
|
26
|
+
const outcome = await step.run(push);
|
|
27
|
+
print(step.name, outcome.message);
|
|
28
|
+
if (outcome.kind === 'fail') {
|
|
29
|
+
console.error('✖ push stopped by the pre-push hook');
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=pre-push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-push.js","sourceRoot":"","sources":["../../src/scripts/pre-push.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,6EAA6E;AAC7E,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,OAAO;AACP,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAA4B;IACxD,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,EAAE;QAC9C,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClD,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAC5C,KAAK,MAAM,IAAI,IAAI,IAAI;YAAE,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC;IACF,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC3B,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE;QACnB,WAAW,EAAE,OAAO,CAAC,GAAG;QACxB,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE;QACrB,KAAK,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QAChC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC;KAC7C,CAAC,CAAC;IACH,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;QACpB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;YACrD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-files.d.ts","sourceRoot":"","sources":["../../src/snapshot/commit-files.ts"],"names":[],"mappings":"AAgBA,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,EAC3C,UAAU,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,EACnD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAqDf"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { once } from 'node:events';
|
|
3
|
+
import { chmod, mkdir, stat, symlink, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
// Writes the files of one commit into an empty folder, from the object store:
|
|
6
|
+
// `git ls-tree` names them and one `git cat-file --batch` gives their content.
|
|
7
|
+
// No filter applies (no autocrlf, no smudge, no LFS), and `export-ignore` does
|
|
8
|
+
// not apply. A submodule is not a file, so it is not written.
|
|
9
|
+
export async function writeCommitFiles(git, gitOptions, sha, folder) {
|
|
10
|
+
const blobs = (await git('ls-tree', '-r', '-z', '--full-tree', sha))
|
|
11
|
+
.split('\0')
|
|
12
|
+
.filter((line) => line !== '')
|
|
13
|
+
.map((line) => {
|
|
14
|
+
const tab = line.indexOf('\t');
|
|
15
|
+
const [mode = '', type = '', object = ''] = line.slice(0, tab).split(' ');
|
|
16
|
+
return { mode, type, object, path: line.slice(tab + 1) };
|
|
17
|
+
})
|
|
18
|
+
.filter(({ type }) => type === 'blob');
|
|
19
|
+
const catFile = spawn('git', ['cat-file', '--batch'], {
|
|
20
|
+
...gitOptions,
|
|
21
|
+
stdio: ['pipe', 'pipe', 'inherit'],
|
|
22
|
+
});
|
|
23
|
+
const exited = once(catFile, 'close');
|
|
24
|
+
catFile.stdin.end(blobs.map(({ object }) => `${object}\n`).join(''));
|
|
25
|
+
const records = new Records(catFile.stdout);
|
|
26
|
+
const links = [];
|
|
27
|
+
for (const blob of blobs) {
|
|
28
|
+
const header = (await records.line()).split(' ');
|
|
29
|
+
const size = Number(header[2]);
|
|
30
|
+
if (header[1] !== 'blob' || !Number.isInteger(size))
|
|
31
|
+
throw new Error(`git cat-file gave "${header.join(' ')}" for ${blob.path}`);
|
|
32
|
+
const content = await records.bytes(size + 1);
|
|
33
|
+
const path = join(folder, blob.path);
|
|
34
|
+
await mkdir(dirname(path), { recursive: true });
|
|
35
|
+
if (blob.mode === '120000')
|
|
36
|
+
links.push({ blob, target: content.subarray(0, size).toString() });
|
|
37
|
+
else {
|
|
38
|
+
await writeFile(path, content.subarray(0, size));
|
|
39
|
+
if (blob.mode === '100755')
|
|
40
|
+
await chmod(path, 0o755);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
await exited;
|
|
44
|
+
// The files come first: on Windows, a link becomes a junction when its
|
|
45
|
+
// target is a folder of the commit, or else a text file that holds the
|
|
46
|
+
// target, as git does with core.symlinks=false.
|
|
47
|
+
for (const { blob, target } of links) {
|
|
48
|
+
const path = join(folder, blob.path);
|
|
49
|
+
if (process.platform !== 'win32')
|
|
50
|
+
await symlink(target, path);
|
|
51
|
+
else {
|
|
52
|
+
const absolute = resolve(dirname(path), target);
|
|
53
|
+
const isFolder = await stat(absolute).then((stats) => stats.isDirectory(), () => false);
|
|
54
|
+
if (isFolder)
|
|
55
|
+
await symlink(absolute, path, 'junction');
|
|
56
|
+
else
|
|
57
|
+
await writeFile(path, target);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Reads the records of `git cat-file --batch`: a header line, then the
|
|
62
|
+
// content and a newline.
|
|
63
|
+
class Records {
|
|
64
|
+
#chunks;
|
|
65
|
+
#buffer = Buffer.alloc(0);
|
|
66
|
+
constructor(stream) {
|
|
67
|
+
this.#chunks = stream[Symbol.asyncIterator]();
|
|
68
|
+
}
|
|
69
|
+
async line() {
|
|
70
|
+
let end = this.#buffer.indexOf(0x0a);
|
|
71
|
+
while (end === -1) {
|
|
72
|
+
this.#buffer = Buffer.concat([this.#buffer, await this.#next()]);
|
|
73
|
+
end = this.#buffer.indexOf(0x0a);
|
|
74
|
+
}
|
|
75
|
+
const line = this.#buffer.subarray(0, end).toString();
|
|
76
|
+
this.#buffer = this.#buffer.subarray(end + 1);
|
|
77
|
+
return line;
|
|
78
|
+
}
|
|
79
|
+
async bytes(count) {
|
|
80
|
+
if (this.#buffer.length < count) {
|
|
81
|
+
const parts = [this.#buffer];
|
|
82
|
+
let length = this.#buffer.length;
|
|
83
|
+
while (length < count) {
|
|
84
|
+
const chunk = await this.#next();
|
|
85
|
+
parts.push(chunk);
|
|
86
|
+
length += chunk.length;
|
|
87
|
+
}
|
|
88
|
+
this.#buffer = Buffer.concat(parts, length);
|
|
89
|
+
}
|
|
90
|
+
const bytes = this.#buffer.subarray(0, count);
|
|
91
|
+
this.#buffer = this.#buffer.subarray(count);
|
|
92
|
+
return bytes;
|
|
93
|
+
}
|
|
94
|
+
async #next() {
|
|
95
|
+
const { done, value } = await this.#chunks.next();
|
|
96
|
+
if (done === true)
|
|
97
|
+
throw new Error('git cat-file ended before it gave each file');
|
|
98
|
+
return Buffer.isBuffer(value) ? value : Buffer.from(String(value));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=commit-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-files.js","sourceRoot":"","sources":["../../src/snapshot/commit-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASnD,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAA2C,EAC3C,UAAmD,EACnD,GAAW,EACX,MAAc;IAEd,MAAM,KAAK,GAAW,CAAC,MAAM,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;SACzE,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;SAC7B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1E,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;IAC3D,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAEzC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE;QACpD,GAAG,UAAU;QACb,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC;KACnC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAqC,EAAE,CAAC;IACnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;YACjD,MAAM,IAAI,KAAK,CACb,sBAAsB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE,CAC3D,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YACxB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;aAChE,CAAC;YACJ,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;YACjD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IACD,MAAM,MAAM,CAAC;IACb,uEAAuE;IACvE,uEAAuE;IACvE,gDAAgD;IAChD,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;YAAE,MAAM,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;aACzD,CAAC;YACJ,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CACxC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,EAC9B,GAAG,EAAE,CAAC,KAAK,CACZ,CAAC;YACF,IAAI,QAAQ;gBAAE,MAAM,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;;gBACnD,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,yBAAyB;AACzB,MAAM,OAAO;IACF,OAAO,CAAwB;IACxC,OAAO,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAElC,YAAY,MAAgB;QAC1B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACrC,OAAO,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACjE,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;QACtD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAa;QACvB,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;YAChC,MAAM,KAAK,GAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvC,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YACjC,OAAO,MAAM,GAAG,KAAK,EAAE,CAAC;gBACtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;gBACjC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC;YACzB,CAAC;YACD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAClD,IAAI,IAAI,KAAK,IAAI;YACf,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.d.ts","sourceRoot":"","sources":["../../src/snapshot/drift.ts"],"names":[],"mappings":"AAcA,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC,CAmBlB;AAmCD,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ5E"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
// Whether the installed packages differ from the lockfile of a commit. npm's
|
|
3
|
+
// hidden lockfile, node_modules/.package-lock.json, lists what is installed.
|
|
4
|
+
// Each difference in the third-party packages is drift: a missing package, a
|
|
5
|
+
// different version, or an extra package, which would build here and fail in
|
|
6
|
+
// CI. Links are not compared: a workspace link points into the snapshot.
|
|
7
|
+
export async function hasInstallDrift(lockfile, hiddenLockfile) {
|
|
8
|
+
const wanted = await packagesOf(lockfile);
|
|
9
|
+
let installed;
|
|
10
|
+
try {
|
|
11
|
+
installed = await packagesOf(hiddenLockfile);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
|
|
15
|
+
return true;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
for (const [path, entry] of wanted) {
|
|
19
|
+
const have = installed.get(path);
|
|
20
|
+
if (have === undefined) {
|
|
21
|
+
// npm skips an optional package that does not fit the platform.
|
|
22
|
+
if (!entry.optional)
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
else if (have.version !== entry.version)
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
for (const path of installed.keys())
|
|
29
|
+
if (!wanted.has(path))
|
|
30
|
+
return true;
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
// The third-party packages of a lockfile: the entries in a node_modules
|
|
34
|
+
// folder that are not links.
|
|
35
|
+
async function packagesOf(path) {
|
|
36
|
+
const lockfile = JSON.parse(await readFile(path, 'utf8'));
|
|
37
|
+
const packages = typeof lockfile === 'object' && lockfile !== null && 'packages' in lockfile
|
|
38
|
+
? lockfile.packages
|
|
39
|
+
: undefined;
|
|
40
|
+
if (typeof packages !== 'object' || packages === null)
|
|
41
|
+
throw new TypeError(`${path} has no "packages"; npm 7 or later writes it`);
|
|
42
|
+
const entries = new Map();
|
|
43
|
+
for (const [key, value] of Object.entries(packages)) {
|
|
44
|
+
if (!key.includes('node_modules/'))
|
|
45
|
+
continue;
|
|
46
|
+
const entry = {
|
|
47
|
+
version: field(value, 'version'),
|
|
48
|
+
link: field(value, 'link') === true,
|
|
49
|
+
optional: field(value, 'optional') === true,
|
|
50
|
+
hasInstallScript: field(value, 'hasInstallScript') === true,
|
|
51
|
+
};
|
|
52
|
+
if (!entry.link)
|
|
53
|
+
entries.set(key, entry);
|
|
54
|
+
}
|
|
55
|
+
return entries;
|
|
56
|
+
}
|
|
57
|
+
function field(value, name) {
|
|
58
|
+
return typeof value === 'object' && value !== null && name in value
|
|
59
|
+
? Reflect.get(value, name)
|
|
60
|
+
: undefined;
|
|
61
|
+
}
|
|
62
|
+
// The third-party packages that the lockfile marks with an install script.
|
|
63
|
+
// The root and the workspace packages have the mark too, but their scripts
|
|
64
|
+
// must not run in the state folder.
|
|
65
|
+
export async function withInstallScripts(lockfile) {
|
|
66
|
+
const names = new Set();
|
|
67
|
+
for (const [key, entry] of await packagesOf(lockfile))
|
|
68
|
+
if (entry.hasInstallScript)
|
|
69
|
+
names.add(key.slice(key.lastIndexOf('node_modules/') + 'node_modules/'.length));
|
|
70
|
+
return [...names];
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../../src/snapshot/drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installs.d.ts","sourceRoot":"","sources":["../../src/snapshot/installs.ts"],"names":[],"mappings":"AA8BA,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,CAAC,UAAU,EAC9B,UAAU,EAAE,MAAM,IAAI,GACrB,OAAO,CAAC,MAAM,CAAC,CAejB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { copyFile, mkdir, readFile, readdir, rm, stat, utimes, writeFile, } from 'node:fs/promises';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
import { withInstallScripts } from "./drift.js";
|
|
8
|
+
import { workspaceFolders } from "./workspaces.js";
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
// The installs that the hook keeps: the ones that it used last.
|
|
11
|
+
const kept = 2;
|
|
12
|
+
// Written last, so a folder without it is an install that did not end.
|
|
13
|
+
const marker = 'installed';
|
|
14
|
+
// An install of the lockfile of the snapshot in a folder of the state folder,
|
|
15
|
+
// named by the hash of the lockfile. A second push with the same lockfile
|
|
16
|
+
// uses the same install. `installing` runs before a new install, because a
|
|
17
|
+
// full install takes some time.
|
|
18
|
+
export async function installFor(state, snapshot, environment, installing) {
|
|
19
|
+
const lockfile = await readFile(join(snapshot, 'package-lock.json'));
|
|
20
|
+
const hash = createHash('sha256').update(lockfile).digest('hex');
|
|
21
|
+
const installs = join(state, 'installs');
|
|
22
|
+
const folder = join(installs, hash.slice(0, 16));
|
|
23
|
+
if (existsSync(join(folder, marker))) {
|
|
24
|
+
const now = new Date();
|
|
25
|
+
await utimes(join(folder, marker), now, now);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
installing();
|
|
29
|
+
await rm(folder, { recursive: true, force: true });
|
|
30
|
+
await install(snapshot, folder, environment);
|
|
31
|
+
}
|
|
32
|
+
await prune(installs);
|
|
33
|
+
return folder;
|
|
34
|
+
}
|
|
35
|
+
// `npm ci --ignore-scripts`, and then `npm rebuild` with only the
|
|
36
|
+
// third-party packages that have install scripts, such as esbuild. The
|
|
37
|
+
// scripts of the repo and of its workspace packages, such as `prepare`,
|
|
38
|
+
// would act on the repo from the state folder, so they do not run. Without
|
|
39
|
+
// names, `npm rebuild` runs them too.
|
|
40
|
+
async function install(snapshot, folder, environment) {
|
|
41
|
+
await mkdir(folder, { recursive: true });
|
|
42
|
+
for (const file of ['package.json', 'package-lock.json', '.npmrc'])
|
|
43
|
+
if (existsSync(join(snapshot, file)))
|
|
44
|
+
await copyFile(join(snapshot, file), join(folder, file));
|
|
45
|
+
for (const workspace of await workspaceFolders(snapshot)) {
|
|
46
|
+
await mkdir(join(folder, workspace), { recursive: true });
|
|
47
|
+
await copyFile(join(snapshot, workspace, 'package.json'), join(folder, workspace, 'package.json'));
|
|
48
|
+
}
|
|
49
|
+
await npm(folder, environment, 'ci', '--ignore-scripts', '--no-audit', '--no-fund');
|
|
50
|
+
const names = await withInstallScripts(join(folder, 'package-lock.json'));
|
|
51
|
+
if (names.length > 0)
|
|
52
|
+
await npm(folder, environment, 'rebuild', ...names);
|
|
53
|
+
await writeFile(join(folder, marker), '');
|
|
54
|
+
}
|
|
55
|
+
async function npm(cwd, environment, ...args) {
|
|
56
|
+
try {
|
|
57
|
+
await execFileAsync('npm', args, {
|
|
58
|
+
cwd,
|
|
59
|
+
env: environment,
|
|
60
|
+
encoding: 'utf8',
|
|
61
|
+
maxBuffer: 2 ** 30,
|
|
62
|
+
// npm is a .cmd file on Windows, which only a shell runs. The
|
|
63
|
+
// arguments are npm's own flags and package names, which need no quotes.
|
|
64
|
+
shell: process.platform === 'win32',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (!(error instanceof Error) || !('stderr' in error))
|
|
69
|
+
throw error;
|
|
70
|
+
throw new Error(`npm ${args.join(' ')} failed:\n${String(error.stderr).trimEnd()}`, { cause: error });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function prune(installs) {
|
|
74
|
+
const used = await Promise.all((await readdir(installs)).map(async (name) => ({
|
|
75
|
+
name,
|
|
76
|
+
at: await stat(join(installs, name, marker)).then(({ mtimeMs }) => mtimeMs, () => -1),
|
|
77
|
+
})));
|
|
78
|
+
used.sort((left, right) => right.at - left.at);
|
|
79
|
+
for (const [index, { name, at }] of used.entries())
|
|
80
|
+
if (index >= kept || at < 0)
|
|
81
|
+
await rm(join(installs, name), { recursive: true, force: true });
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=installs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installs.js","sourceRoot":"","sources":["../../src/snapshot/installs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nx.d.ts","sourceRoot":"","sources":["../../src/snapshot/nx.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAID,wBAAgB,KAAK,CACnB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,CAAC,UAAU,EAC9B,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,KAAK,CAAC,CAoBhB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
// Runs the nx of the snapshot's node_modules in the snapshot. On Windows,
|
|
4
|
+
// npm writes `.bin/nx.cmd`, which only a shell runs.
|
|
5
|
+
export function runNx(folder, environment, args) {
|
|
6
|
+
const windows = process.platform === 'win32';
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
execFile(join(folder, 'node_modules/.bin', windows ? 'nx.cmd' : 'nx'), args, {
|
|
9
|
+
cwd: folder,
|
|
10
|
+
env: environment,
|
|
11
|
+
encoding: 'utf8',
|
|
12
|
+
maxBuffer: 2 ** 30,
|
|
13
|
+
shell: windows,
|
|
14
|
+
}, (error, stdout, stderr) => {
|
|
15
|
+
const code = error === null ? 0 : typeof error.code === 'number' ? error.code : 1;
|
|
16
|
+
resolve({ code, stdout, stderr });
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=nx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nx.js","sourceRoot":"","sources":["../../src/snapshot/nx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,0EAA0E;AAC1E,qDAAqD;AACrD,MAAM,UAAU,KAAK,CACnB,MAAc,EACd,WAA8B,EAC9B,IAAuB;IAEvB,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,QAAQ,CACN,IAAI,CAAC,MAAM,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAC5D,IAAI,EACJ;YACE,GAAG,EAAE,MAAM;YACX,GAAG,EAAE,WAAW;YAChB,QAAQ,EAAE,MAAM;YAChB,SAAS,EAAE,CAAC,IAAI,EAAE;YAClB,KAAK,EAAE,OAAO;SACf,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,MAAM,IAAI,GACR,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACpC,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packages.d.ts","sourceRoot":"","sources":["../../src/snapshot/packages.ts"],"names":[],"mappings":"AAsBA,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|