@procedures/snapshot 0.0.0-stage → 0.2.0
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/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +737 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +21 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +117 -0
- package/dist/module.js.map +1 -0
- package/dist/procedures/snapshot/archive-io.d.ts +12 -0
- package/dist/procedures/snapshot/archive-io.d.ts.map +1 -0
- package/dist/procedures/snapshot/archive-io.js +29 -0
- package/dist/procedures/snapshot/archive-io.js.map +1 -0
- package/dist/procedures/snapshot/commands.d.ts +15 -0
- package/dist/procedures/snapshot/commands.d.ts.map +1 -0
- package/dist/procedures/snapshot/commands.js +32 -0
- package/dist/procedures/snapshot/commands.js.map +1 -0
- package/dist/procedures/snapshot/create.d.ts +12 -0
- package/dist/procedures/snapshot/create.d.ts.map +1 -0
- package/dist/procedures/snapshot/create.js +279 -0
- package/dist/procedures/snapshot/create.js.map +1 -0
- package/dist/procedures/snapshot/delete.d.ts +12 -0
- package/dist/procedures/snapshot/delete.d.ts.map +1 -0
- package/dist/procedures/snapshot/delete.js +31 -0
- package/dist/procedures/snapshot/delete.js.map +1 -0
- package/dist/procedures/snapshot/diff.d.ts +12 -0
- package/dist/procedures/snapshot/diff.d.ts.map +1 -0
- package/dist/procedures/snapshot/diff.js +113 -0
- package/dist/procedures/snapshot/diff.js.map +1 -0
- package/dist/procedures/snapshot/index.d.ts +9 -0
- package/dist/procedures/snapshot/index.d.ts.map +1 -0
- package/dist/procedures/snapshot/index.js +9 -0
- package/dist/procedures/snapshot/index.js.map +1 -0
- package/dist/procedures/snapshot/list.d.ts +12 -0
- package/dist/procedures/snapshot/list.d.ts.map +1 -0
- package/dist/procedures/snapshot/list.js +57 -0
- package/dist/procedures/snapshot/list.js.map +1 -0
- package/dist/procedures/snapshot/names.d.ts +10 -0
- package/dist/procedures/snapshot/names.d.ts.map +1 -0
- package/dist/procedures/snapshot/names.js +20 -0
- package/dist/procedures/snapshot/names.js.map +1 -0
- package/dist/procedures/snapshot/restore.d.ts +12 -0
- package/dist/procedures/snapshot/restore.d.ts.map +1 -0
- package/dist/procedures/snapshot/restore.js +131 -0
- package/dist/procedures/snapshot/restore.js.map +1 -0
- package/dist/procedures/snapshot/s3-lookup.d.ts +39 -0
- package/dist/procedures/snapshot/s3-lookup.d.ts.map +1 -0
- package/dist/procedures/snapshot/s3-lookup.js +57 -0
- package/dist/procedures/snapshot/s3-lookup.js.map +1 -0
- package/dist/register.d.ts +7 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +9 -0
- package/dist/register.js.map +1 -0
- package/dist/types.d.ts +193 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +168 -0
- package/dist/types.js.map +1 -0
- package/package.json +87 -3
- package/src/index.ts +51 -0
- package/src/module.ts +155 -0
- package/src/procedures/snapshot/archive-io.ts +31 -0
- package/src/procedures/snapshot/commands.ts +34 -0
- package/src/procedures/snapshot/create.ts +354 -0
- package/src/procedures/snapshot/delete.ts +40 -0
- package/src/procedures/snapshot/diff.ts +147 -0
- package/src/procedures/snapshot/index.ts +9 -0
- package/src/procedures/snapshot/list.ts +80 -0
- package/src/procedures/snapshot/names.ts +24 -0
- package/src/procedures/snapshot/restore.ts +166 -0
- package/src/procedures/snapshot/s3-lookup.ts +88 -0
- package/src/register.ts +11 -0
- package/src/types.ts +381 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File access for snapshot archives without the whole archive in memory.
|
|
3
|
+
*
|
|
4
|
+
* Before, create and restore read each archive into memory (for the checksum and the upload).
|
|
5
|
+
* An archive over 2 GiB failed, and the peak was about 2.3 times the archive (deep dive DATA-19).
|
|
6
|
+
*/
|
|
7
|
+
import type { FileHandle } from "node:fs/promises";
|
|
8
|
+
/** The SHA-256 of a file, read as a stream. */
|
|
9
|
+
export declare function sha256File(path: string): Promise<string>;
|
|
10
|
+
/** The bytes of a file from `offset`, at most `size`. */
|
|
11
|
+
export declare function readPart(file: FileHandle, offset: number, size: number): Promise<Buffer>;
|
|
12
|
+
//# sourceMappingURL=archive-io.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-io.d.ts","sourceRoot":"","sources":["../../../src/procedures/snapshot/archive-io.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,+CAA+C;AAC/C,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9D;AAED,yDAAyD;AACzD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS9F"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File access for snapshot archives without the whole archive in memory.
|
|
3
|
+
*
|
|
4
|
+
* Before, create and restore read each archive into memory (for the checksum and the upload).
|
|
5
|
+
* An archive over 2 GiB failed, and the peak was about 2.3 times the archive (deep dive DATA-19).
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { createReadStream } from "node:fs";
|
|
9
|
+
/** The SHA-256 of a file, read as a stream. */
|
|
10
|
+
export async function sha256File(path) {
|
|
11
|
+
const hash = createHash("sha256");
|
|
12
|
+
for await (const chunk of createReadStream(path)) {
|
|
13
|
+
hash.update(chunk);
|
|
14
|
+
}
|
|
15
|
+
return hash.digest("hex");
|
|
16
|
+
}
|
|
17
|
+
/** The bytes of a file from `offset`, at most `size`. */
|
|
18
|
+
export async function readPart(file, offset, size) {
|
|
19
|
+
const buffer = Buffer.alloc(size);
|
|
20
|
+
let filled = 0;
|
|
21
|
+
while (filled < size) {
|
|
22
|
+
const { bytesRead } = await file.read(buffer, filled, size - filled, offset + filled);
|
|
23
|
+
if (bytesRead === 0)
|
|
24
|
+
break;
|
|
25
|
+
filled += bytesRead;
|
|
26
|
+
}
|
|
27
|
+
return buffer.subarray(0, filled);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=archive-io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-io.js","sourceRoot":"","sources":["../../../src/procedures/snapshot/archive-io.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG3C,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,KAAe,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,yDAAyD;AACzD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAgB,EAAE,MAAc,EAAE,IAAY;IAC3E,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,MAAM,GAAG,IAAI,EAAE,CAAC;QACrB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;QACtF,IAAI,SAAS,KAAK,CAAC;YAAE,MAAM;QAC3B,MAAM,IAAI,SAAS,CAAC;IACtB,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The programs that the snapshot procedures run: git and pnpm, through `runCommand`.
|
|
3
|
+
*
|
|
4
|
+
* `runCommand` is async, kills the process tree when the signal aborts, and runs no shell.
|
|
5
|
+
* Before, the procedures used `execSync`: it blocked the event loop of the host for each call,
|
|
6
|
+
* ignored the signal, and passed the command line to a shell.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* This function runs `git <args>` in `cwd` and gives its trimmed output. It gives `undefined`
|
|
10
|
+
* when git fails, when it cannot start, or when the signal aborts.
|
|
11
|
+
*/
|
|
12
|
+
export declare function gitText(args: readonly string[], cwd: string, signal?: AbortSignal): Promise<string | undefined>;
|
|
13
|
+
/** This function gives the version of pnpm, or "unknown" when pnpm cannot run. */
|
|
14
|
+
export declare function pnpmVersion(signal?: AbortSignal): Promise<string>;
|
|
15
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../src/procedures/snapshot/commands.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH;;;GAGG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGrH;AAED,kFAAkF;AAClF,wBAAsB,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CASvE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The programs that the snapshot procedures run: git and pnpm, through `runCommand`.
|
|
3
|
+
*
|
|
4
|
+
* `runCommand` is async, kills the process tree when the signal aborts, and runs no shell.
|
|
5
|
+
* Before, the procedures used `execSync`: it blocked the event loop of the host for each call,
|
|
6
|
+
* ignored the signal, and passed the command line to a shell.
|
|
7
|
+
*/
|
|
8
|
+
import { runCommand } from "@procedures/shell/command";
|
|
9
|
+
import { resolvePnpm } from "@procedures/pnpm";
|
|
10
|
+
/** The longest time of one git or pnpm query, in milliseconds. */
|
|
11
|
+
const QUERY_TIMEOUT_MS = 30_000;
|
|
12
|
+
/**
|
|
13
|
+
* This function runs `git <args>` in `cwd` and gives its trimmed output. It gives `undefined`
|
|
14
|
+
* when git fails, when it cannot start, or when the signal aborts.
|
|
15
|
+
*/
|
|
16
|
+
export async function gitText(args, cwd, signal) {
|
|
17
|
+
const result = await runCommand("git", { args, cwd, signal, timeout: QUERY_TIMEOUT_MS });
|
|
18
|
+
return result.success ? result.stdout.trim() : undefined;
|
|
19
|
+
}
|
|
20
|
+
/** This function gives the version of pnpm, or "unknown" when pnpm cannot run. */
|
|
21
|
+
export async function pnpmVersion(signal) {
|
|
22
|
+
let pnpm;
|
|
23
|
+
try {
|
|
24
|
+
pnpm = resolvePnpm();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return "unknown";
|
|
28
|
+
}
|
|
29
|
+
const result = await runCommand(pnpm.command, { args: [...pnpm.prefix, "--version"], signal, timeout: QUERY_TIMEOUT_MS });
|
|
30
|
+
return result.success ? result.stdout.trim() : "unknown";
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../../src/procedures/snapshot/commands.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,kEAAkE;AAClE,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAuB,EAAE,GAAW,EAAE,MAAoB;IACtF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACzF,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAoB;IACpD,IAAI,IAA2C,CAAC;IAChD,IAAI,CAAC;QACH,IAAI,GAAG,WAAW,EAAE,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC1H,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snapshot.create procedure
|
|
3
|
+
*
|
|
4
|
+
* Create an environment snapshot and upload to S3.
|
|
5
|
+
*/
|
|
6
|
+
import type { ProcedureContext } from "@procedures/client";
|
|
7
|
+
import type { SnapshotCreateInput, SnapshotCreateOutput } from "../../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Create a snapshot of the environment
|
|
10
|
+
*/
|
|
11
|
+
export declare function snapshotCreate(input: SnapshotCreateInput, ctx: ProcedureContext): Promise<SnapshotCreateOutput>;
|
|
12
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/procedures/snapshot/create.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EAIrB,MAAM,gBAAgB,CAAC;AAKxB;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,oBAAoB,CAAC,CAgG/B"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snapshot.create procedure
|
|
3
|
+
*
|
|
4
|
+
* Create an environment snapshot and upload to S3.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdtempSync, rmSync, readFileSync, writeFileSync, statSync } from "node:fs";
|
|
7
|
+
import { open } from "node:fs/promises";
|
|
8
|
+
import { join, basename, dirname, resolve } from "node:path";
|
|
9
|
+
import { tmpdir, hostname } from "node:os";
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
import * as tar from "tar";
|
|
12
|
+
import { assertSnapshotName, isExcluded } from "./names.js";
|
|
13
|
+
import { readPart, sha256File } from "./archive-io.js";
|
|
14
|
+
import { gitText, pnpmVersion } from "./commands.js";
|
|
15
|
+
/** The size of each part of a multipart upload (the S3 minimum) */
|
|
16
|
+
const PART_SIZE = 5 * 1024 * 1024;
|
|
17
|
+
/**
|
|
18
|
+
* Create a snapshot of the environment
|
|
19
|
+
*/
|
|
20
|
+
export async function snapshotCreate(input, ctx) {
|
|
21
|
+
// A plain name: it goes into S3 keys and the snapshot id (a "/" made ids that restore,
|
|
22
|
+
// diff and delete never found: deep dive DATA-1, DATA-16)
|
|
23
|
+
assertSnapshotName("name", input.name);
|
|
24
|
+
const id = `${input.name}-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
25
|
+
const workDir = mkdtempSync(join(tmpdir(), "snapshot-"));
|
|
26
|
+
try {
|
|
27
|
+
// Get repository paths to snapshot
|
|
28
|
+
const repoPaths = input.paths || (await getEcosystemPaths(ctx));
|
|
29
|
+
// Gather repository info
|
|
30
|
+
const repositories = await Promise.all(repoPaths.map((path) => getRepositoryInfo(path, ctx)));
|
|
31
|
+
// Get environment info
|
|
32
|
+
const environment = await getEnvironmentInfo(ctx.signal);
|
|
33
|
+
// Create metadata
|
|
34
|
+
const metadata = {
|
|
35
|
+
id,
|
|
36
|
+
name: input.name,
|
|
37
|
+
preset: input.preset,
|
|
38
|
+
createdAt: new Date().toISOString(),
|
|
39
|
+
environment,
|
|
40
|
+
repositories,
|
|
41
|
+
checksum: "", // Will be calculated after archive creation
|
|
42
|
+
archiveSize: 0,
|
|
43
|
+
description: input.description,
|
|
44
|
+
};
|
|
45
|
+
// Write metadata to work directory
|
|
46
|
+
const metadataPath = join(workDir, "metadata.json");
|
|
47
|
+
writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
48
|
+
// Create archive based on preset
|
|
49
|
+
const archivePath = join(workDir, `${id}.tar.gz`);
|
|
50
|
+
await createArchive(archivePath, repoPaths, input.preset, workDir);
|
|
51
|
+
// Calculate checksum (a stream: the archive is never in memory as a whole)
|
|
52
|
+
const checksum = await sha256File(archivePath);
|
|
53
|
+
const archiveSize = statSync(archivePath).size;
|
|
54
|
+
// Update metadata with checksum
|
|
55
|
+
metadata.checksum = checksum;
|
|
56
|
+
metadata.archiveSize = archiveSize;
|
|
57
|
+
writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
58
|
+
// Upload to S3
|
|
59
|
+
const uploadStart = Date.now();
|
|
60
|
+
const s3Key = `snapshots/${input.name}/${id}.tar.gz`;
|
|
61
|
+
// Use multipart upload for large files (> 5MB). The parts are read from the file, one at a
|
|
62
|
+
// time (deep dive DATA-19).
|
|
63
|
+
if (archiveSize > PART_SIZE) {
|
|
64
|
+
await multipartUpload(ctx, input.bucket, s3Key, archivePath, archiveSize);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
await ctx.client.call(["s3", "upload"], {
|
|
68
|
+
bucket: input.bucket,
|
|
69
|
+
key: s3Key,
|
|
70
|
+
body: readFileSync(archivePath).toString("base64"),
|
|
71
|
+
base64: true,
|
|
72
|
+
contentType: "application/gzip",
|
|
73
|
+
metadata: {
|
|
74
|
+
snapshotId: id,
|
|
75
|
+
snapshotName: input.name,
|
|
76
|
+
preset: input.preset,
|
|
77
|
+
checksum,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Also upload metadata separately for quick listing
|
|
82
|
+
await ctx.client.call(["s3", "upload"], {
|
|
83
|
+
bucket: input.bucket,
|
|
84
|
+
key: `snapshots/${input.name}/${id}.metadata.json`,
|
|
85
|
+
body: JSON.stringify(metadata, null, 2),
|
|
86
|
+
contentType: "application/json",
|
|
87
|
+
});
|
|
88
|
+
const uploadDuration = Date.now() - uploadStart;
|
|
89
|
+
return {
|
|
90
|
+
id,
|
|
91
|
+
location: `s3://${input.bucket}/${s3Key}`,
|
|
92
|
+
metadata,
|
|
93
|
+
uploadDuration,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
// Cleanup work directory
|
|
98
|
+
if (existsSync(workDir)) {
|
|
99
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get ecosystem repository paths from manifest
|
|
105
|
+
*/
|
|
106
|
+
async function getEcosystemPaths(_ctx) {
|
|
107
|
+
// TODO: Read from ecosystem manifest
|
|
108
|
+
// For now, return current working directory
|
|
109
|
+
return [process.cwd()];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get repository info for a path
|
|
113
|
+
*/
|
|
114
|
+
async function getRepositoryInfo(repoPath, ctx) {
|
|
115
|
+
try {
|
|
116
|
+
const result = await ctx.client.call(["git", "status"], { cwd: repoPath });
|
|
117
|
+
// The commit hash ("" for a repository with no commit) and the remote URL (none without "origin")
|
|
118
|
+
const commit = (await gitText(["rev-parse", "HEAD"], repoPath, ctx.signal)) ?? "";
|
|
119
|
+
const remoteUrl = await gitText(["remote", "get-url", "origin"], repoPath, ctx.signal);
|
|
120
|
+
// Get stash count
|
|
121
|
+
let stashCount = 0;
|
|
122
|
+
try {
|
|
123
|
+
const stashList = await ctx.client.call(["git", "stash", "list"], { cwd: repoPath });
|
|
124
|
+
stashCount = stashList.count;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// No stashes
|
|
128
|
+
}
|
|
129
|
+
// Get package name from package.json
|
|
130
|
+
let name = basename(repoPath);
|
|
131
|
+
try {
|
|
132
|
+
const pkgPath = join(repoPath, "package.json");
|
|
133
|
+
if (existsSync(pkgPath)) {
|
|
134
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
135
|
+
name = pkg.name || name;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Use directory name
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
path: repoPath,
|
|
143
|
+
name,
|
|
144
|
+
branch: result.branch,
|
|
145
|
+
commit,
|
|
146
|
+
dirty: !result.clean,
|
|
147
|
+
stashCount,
|
|
148
|
+
remoteUrl,
|
|
149
|
+
ahead: result.ahead,
|
|
150
|
+
behind: result.behind,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// Not a git repo or error
|
|
155
|
+
return {
|
|
156
|
+
path: repoPath,
|
|
157
|
+
name: basename(repoPath),
|
|
158
|
+
branch: "",
|
|
159
|
+
commit: "",
|
|
160
|
+
dirty: false,
|
|
161
|
+
stashCount: 0,
|
|
162
|
+
ahead: 0,
|
|
163
|
+
behind: 0,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get environment info
|
|
169
|
+
*/
|
|
170
|
+
async function getEnvironmentInfo(signal) {
|
|
171
|
+
return {
|
|
172
|
+
os: process.platform,
|
|
173
|
+
nodeVersion: process.version,
|
|
174
|
+
pnpmVersion: await pnpmVersion(signal),
|
|
175
|
+
username: process.env["USER"] || process.env["USERNAME"] || "unknown",
|
|
176
|
+
hostname: hostname(),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Create tar archive based on preset
|
|
181
|
+
*/
|
|
182
|
+
async function createArchive(archivePath, repoPaths, preset, workDir) {
|
|
183
|
+
// Only archive repositories that actually exist on disk.
|
|
184
|
+
const existingRepos = repoPaths.filter((p) => existsSync(p));
|
|
185
|
+
// tar.create accepts a single `cwd`, and each entry is stored relative to it.
|
|
186
|
+
// To store each repository as a top-level `<basename>/...` entry we must run
|
|
187
|
+
// with `cwd` set to the directory that contains the repositories (the
|
|
188
|
+
// ecosystem root), not the fresh temp `workDir` the repos are not inside.
|
|
189
|
+
const parents = new Set(existingRepos.map((p) => resolve(dirname(p))));
|
|
190
|
+
if (parents.size > 1) {
|
|
191
|
+
throw new Error(`snapshot.create requires all repositories to share a parent directory; got multiple roots: ${[
|
|
192
|
+
...parents,
|
|
193
|
+
].join(", ")}`);
|
|
194
|
+
}
|
|
195
|
+
const cwd = existingRepos.length > 0 ? resolve(dirname(existingRepos[0])) : workDir;
|
|
196
|
+
// Create gzip options based on preset
|
|
197
|
+
const gzipOpts = { level: preset === "heavy" ? 6 : 9 };
|
|
198
|
+
// Build exclude patterns based on preset
|
|
199
|
+
const excludePatterns = [];
|
|
200
|
+
if (preset === "light") {
|
|
201
|
+
// Exclude node_modules and pnpm store
|
|
202
|
+
excludePatterns.push("node_modules", ".pnpm-store", "dist", "*.log");
|
|
203
|
+
}
|
|
204
|
+
else if (preset === "medium") {
|
|
205
|
+
// Include node_modules but exclude pnpm store
|
|
206
|
+
excludePatterns.push(".pnpm-store", "*.log");
|
|
207
|
+
}
|
|
208
|
+
// heavy preset includes everything
|
|
209
|
+
await tar.create({
|
|
210
|
+
gzip: gzipOpts,
|
|
211
|
+
file: archivePath,
|
|
212
|
+
cwd,
|
|
213
|
+
// Whole path segments, and "*.ext" for file endings. (Before, `path.includes("dist")`
|
|
214
|
+
// left out src/distance.ts, and "*.log" matched nothing: deep dive DATA-3.)
|
|
215
|
+
filter: (path) => !isExcluded(path, excludePatterns),
|
|
216
|
+
}, existingRepos.map((p) => basename(p)));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Multipart upload for large files. Each part is read from the file when it goes up.
|
|
220
|
+
*/
|
|
221
|
+
async function multipartUpload(ctx, bucket, key, archivePath, archiveSize) {
|
|
222
|
+
// Initialize multipart upload
|
|
223
|
+
const initResult = await ctx.client.call(["s3", "multipart", "init"], {
|
|
224
|
+
bucket,
|
|
225
|
+
key,
|
|
226
|
+
contentType: "application/gzip",
|
|
227
|
+
});
|
|
228
|
+
const uploadId = initResult.uploadId;
|
|
229
|
+
const parts = [];
|
|
230
|
+
const file = await open(archivePath, "r");
|
|
231
|
+
try {
|
|
232
|
+
// Upload parts
|
|
233
|
+
let partNumber = 1;
|
|
234
|
+
for (let offset = 0; offset < archiveSize; offset += PART_SIZE) {
|
|
235
|
+
const chunk = await readPart(file, offset, PART_SIZE);
|
|
236
|
+
const partResult = await ctx.client.call(["s3", "multipart", "upload"], {
|
|
237
|
+
bucket,
|
|
238
|
+
key,
|
|
239
|
+
uploadId,
|
|
240
|
+
partNumber,
|
|
241
|
+
body: chunk.toString("base64"),
|
|
242
|
+
});
|
|
243
|
+
parts.push({
|
|
244
|
+
etag: partResult.etag,
|
|
245
|
+
partNumber: partResult.partNumber,
|
|
246
|
+
});
|
|
247
|
+
partNumber++;
|
|
248
|
+
}
|
|
249
|
+
// Complete multipart upload
|
|
250
|
+
await ctx.client.call(["s3", "multipart", "complete"], {
|
|
251
|
+
bucket,
|
|
252
|
+
key,
|
|
253
|
+
uploadId,
|
|
254
|
+
parts,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
// Abort on failure. The error of the upload stays the error: before, a failed abort
|
|
259
|
+
// replaced it.
|
|
260
|
+
try {
|
|
261
|
+
await ctx.client.call(["s3", "multipart", "abort"], {
|
|
262
|
+
bucket,
|
|
263
|
+
key,
|
|
264
|
+
uploadId,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
catch (abortError) {
|
|
268
|
+
throw new Error(`${messageOf(error)} (the abort of upload ${uploadId} also failed: ${messageOf(abortError)})`, { cause: error });
|
|
269
|
+
}
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
await file.close();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function messageOf(error) {
|
|
277
|
+
return error instanceof Error ? error.message : String(error);
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/procedures/snapshot/create.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snapshot.delete procedure
|
|
3
|
+
*
|
|
4
|
+
* Delete a snapshot from S3.
|
|
5
|
+
*/
|
|
6
|
+
import type { ProcedureContext } from "@procedures/client";
|
|
7
|
+
import type { SnapshotDeleteInput, SnapshotDeleteOutput } from "../../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Delete a snapshot from S3
|
|
10
|
+
*/
|
|
11
|
+
export declare function snapshotDelete(input: SnapshotDeleteInput, ctx: ProcedureContext): Promise<SnapshotDeleteOutput>;
|
|
12
|
+
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/procedures/snapshot/delete.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGhF;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,oBAAoB,CAAC,CAuB/B"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snapshot.delete procedure
|
|
3
|
+
*
|
|
4
|
+
* Delete a snapshot from S3.
|
|
5
|
+
*/
|
|
6
|
+
import { listAllObjects, findSnapshotKeys } from "./s3-lookup.js";
|
|
7
|
+
/**
|
|
8
|
+
* Delete a snapshot from S3
|
|
9
|
+
*/
|
|
10
|
+
export async function snapshotDelete(input, ctx) {
|
|
11
|
+
// Find all files for this snapshot ID (paginated listing, exact basename
|
|
12
|
+
// match). Exact matching is critical here: a substring match could delete a
|
|
13
|
+
// different snapshot whose id contains this id as a substring.
|
|
14
|
+
const objects = await listAllObjects(ctx, input.bucket, "snapshots/");
|
|
15
|
+
const snapshotKeys = findSnapshotKeys(objects, input.id);
|
|
16
|
+
if (snapshotKeys.length === 0) {
|
|
17
|
+
throw new Error(`Snapshot not found: ${input.id}`);
|
|
18
|
+
}
|
|
19
|
+
// Delete each file
|
|
20
|
+
for (const key of snapshotKeys) {
|
|
21
|
+
await ctx.client.call(["s3", "delete"], {
|
|
22
|
+
bucket: input.bucket,
|
|
23
|
+
key,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
deleted: true,
|
|
28
|
+
id: input.id,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../../src/procedures/snapshot/delete.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAA0B,EAC1B,GAAqB;IAErB,yEAAyE;IACzE,4EAA4E;IAC5E,+DAA+D;IAC/D,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtE,MAAM,YAAY,GAAG,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IAEzD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,uBAAuB,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,mBAAmB;IACnB,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE;YACtC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG;SACJ,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAI;QACb,EAAE,EAAE,KAAK,CAAC,EAAE;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snapshot.diff procedure
|
|
3
|
+
*
|
|
4
|
+
* Compare current state against a snapshot.
|
|
5
|
+
*/
|
|
6
|
+
import type { ProcedureContext } from "@procedures/client";
|
|
7
|
+
import type { SnapshotDiffInput, SnapshotDiffOutput } from "../../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Compare current state against a snapshot
|
|
10
|
+
*/
|
|
11
|
+
export declare function snapshotDiff(input: SnapshotDiffInput, ctx: ProcedureContext): Promise<SnapshotDiffOutput>;
|
|
12
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/procedures/snapshot/diff.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAGnB,MAAM,gBAAgB,CAAC;AAGxB;;GAEG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,iBAAiB,EACxB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,kBAAkB,CAAC,CA0H7B"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snapshot.diff procedure
|
|
3
|
+
*
|
|
4
|
+
* Compare current state against a snapshot.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { basename } from "node:path";
|
|
8
|
+
import { gitText } from "./commands.js";
|
|
9
|
+
import { listAllObjects, findSnapshotKey } from "./s3-lookup.js";
|
|
10
|
+
/**
|
|
11
|
+
* Compare current state against a snapshot
|
|
12
|
+
*/
|
|
13
|
+
export async function snapshotDiff(input, ctx) {
|
|
14
|
+
// Download snapshot metadata (paginated listing, exact basename match).
|
|
15
|
+
const objects = await listAllObjects(ctx, input.bucket, "snapshots/");
|
|
16
|
+
const metadataKey = findSnapshotKey(objects, input.id, ".metadata.json");
|
|
17
|
+
if (!metadataKey) {
|
|
18
|
+
throw new Error(`Snapshot not found: ${input.id}`);
|
|
19
|
+
}
|
|
20
|
+
const metadataResult = await ctx.client.call(["s3", "download"], {
|
|
21
|
+
bucket: input.bucket,
|
|
22
|
+
key: metadataKey,
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
});
|
|
25
|
+
const snapshotMetadata = JSON.parse(metadataResult.body);
|
|
26
|
+
// Compare each repository
|
|
27
|
+
const repositories = [];
|
|
28
|
+
let totalFilesChanged = 0;
|
|
29
|
+
let reposChanged = 0;
|
|
30
|
+
const pathsToCheck = input.paths || snapshotMetadata.repositories.map((r) => r.path);
|
|
31
|
+
for (const repoPath of pathsToCheck) {
|
|
32
|
+
const snapshotRepo = snapshotMetadata.repositories.find((r) => r.path === repoPath || r.name === basename(repoPath));
|
|
33
|
+
if (!snapshotRepo)
|
|
34
|
+
continue;
|
|
35
|
+
const diff = {
|
|
36
|
+
path: repoPath,
|
|
37
|
+
filesChanged: 0,
|
|
38
|
+
newStashes: 0,
|
|
39
|
+
};
|
|
40
|
+
if (!existsSync(repoPath)) {
|
|
41
|
+
// Repository was deleted
|
|
42
|
+
diff.filesChanged = -1; // Indicates missing
|
|
43
|
+
repositories.push(diff);
|
|
44
|
+
reposChanged++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
// Get current branch (a failure is an error of this repository: the catch below)
|
|
49
|
+
const currentBranch = await gitText(["rev-parse", "--abbrev-ref", "HEAD"], repoPath, ctx.signal);
|
|
50
|
+
if (currentBranch === undefined)
|
|
51
|
+
throw new Error(`git cannot read the branch of ${repoPath}`);
|
|
52
|
+
if (currentBranch !== snapshotRepo.branch) {
|
|
53
|
+
diff.branchDiff = {
|
|
54
|
+
current: currentBranch,
|
|
55
|
+
snapshot: snapshotRepo.branch,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Get current commit
|
|
59
|
+
const currentCommit = await gitText(["rev-parse", "HEAD"], repoPath, ctx.signal);
|
|
60
|
+
if (currentCommit === undefined)
|
|
61
|
+
throw new Error(`git cannot read the commit of ${repoPath}`);
|
|
62
|
+
if (currentCommit !== snapshotRepo.commit) {
|
|
63
|
+
diff.commitDiff = {
|
|
64
|
+
current: currentCommit,
|
|
65
|
+
snapshot: snapshotRepo.commit,
|
|
66
|
+
};
|
|
67
|
+
// Count files changed since snapshot commit.
|
|
68
|
+
// The commit comes from snapshot metadata downloaded from S3: accept only a hex
|
|
69
|
+
// object name, and pass it as an argument (no shell), so it cannot inject a command.
|
|
70
|
+
try {
|
|
71
|
+
if (!/^[0-9a-f]{7,64}$/i.test(snapshotRepo.commit)) {
|
|
72
|
+
throw new Error(`Not a commit hash: ${snapshotRepo.commit}`);
|
|
73
|
+
}
|
|
74
|
+
const diffStat = await gitText(["diff", "--stat", `${snapshotRepo.commit}..HEAD`], repoPath, ctx.signal);
|
|
75
|
+
if (diffStat === undefined)
|
|
76
|
+
throw new Error(`git cannot compare ${snapshotRepo.commit} with HEAD`);
|
|
77
|
+
// Count lines that represent files (contain |)
|
|
78
|
+
diff.filesChanged = diffStat.split("\n").filter((l) => l.includes("|")).length;
|
|
79
|
+
totalFilesChanged += diff.filesChanged;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Snapshot commit may not exist locally
|
|
83
|
+
diff.filesChanged = -1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Check for new stashes
|
|
87
|
+
const stashResult = await ctx.client.call(["git", "stash", "list"], { cwd: repoPath });
|
|
88
|
+
diff.newStashes = stashResult.count - snapshotRepo.stashCount;
|
|
89
|
+
if (diff.branchDiff ||
|
|
90
|
+
diff.commitDiff ||
|
|
91
|
+
diff.filesChanged > 0 ||
|
|
92
|
+
diff.newStashes > 0) {
|
|
93
|
+
reposChanged++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Not a git repo or error
|
|
98
|
+
diff.filesChanged = -1;
|
|
99
|
+
reposChanged++;
|
|
100
|
+
}
|
|
101
|
+
repositories.push(diff);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
snapshotMetadata,
|
|
105
|
+
repositories,
|
|
106
|
+
summary: {
|
|
107
|
+
reposChanged,
|
|
108
|
+
totalFilesChanged,
|
|
109
|
+
isMatch: reposChanged === 0,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../src/procedures/snapshot/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot Procedures Index
|
|
3
|
+
*/
|
|
4
|
+
export { snapshotCreate } from "./create.js";
|
|
5
|
+
export { snapshotList } from "./list.js";
|
|
6
|
+
export { snapshotRestore } from "./restore.js";
|
|
7
|
+
export { snapshotDiff } from "./diff.js";
|
|
8
|
+
export { snapshotDelete } from "./delete.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/procedures/snapshot/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|