@rehearsal-labs/retest 0.2.0 → 0.3.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/README.md +6 -5
- package/dist/api/source-location.d.ts.map +1 -1
- package/dist/api/source-location.js +3 -1
- package/dist/browser/builds.d.ts +35 -6
- package/dist/browser/builds.d.ts.map +1 -1
- package/dist/browser/builds.js +115 -16
- package/dist/browser/cdp/transport.d.ts.map +1 -1
- package/dist/browser/cdp/transport.js +4 -4
- package/dist/browser/chromium-process.d.ts +23 -0
- package/dist/browser/chromium-process.d.ts.map +1 -1
- package/dist/browser/chromium-process.js +78 -13
- package/dist/browser/electron.d.ts +1 -1
- package/dist/browser/electron.d.ts.map +1 -1
- package/dist/browser/electron.js +6 -3
- package/dist/browser/executables.d.ts +16 -1
- package/dist/browser/executables.d.ts.map +1 -1
- package/dist/browser/executables.js +50 -18
- package/dist/browser/firefox/accessible-names.d.ts +5 -4
- package/dist/browser/firefox/accessible-names.d.ts.map +1 -1
- package/dist/browser/firefox/accessible-names.js +11 -6
- package/dist/browser/firefox/bridge.js +3 -2
- package/dist/browser/firefox/executable.d.ts +14 -14
- package/dist/browser/firefox/executable.d.ts.map +1 -1
- package/dist/browser/firefox/executable.js +29 -21
- package/dist/browser/firefox/launch.js +1 -1
- package/dist/browser/firefox/navigation.d.ts +5 -2
- package/dist/browser/firefox/navigation.d.ts.map +1 -1
- package/dist/browser/firefox/navigation.js +48 -14
- package/dist/browser/firefox/orphans.d.ts.map +1 -1
- package/dist/browser/firefox/orphans.js +34 -2
- package/dist/browser/firefox/page.d.ts.map +1 -1
- package/dist/browser/firefox/page.js +113 -19
- package/dist/browser/firefox/process.d.ts +20 -1
- package/dist/browser/firefox/process.d.ts.map +1 -1
- package/dist/browser/firefox/process.js +124 -22
- package/dist/browser/firefox/profile.d.ts +5 -0
- package/dist/browser/firefox/profile.d.ts.map +1 -1
- package/dist/browser/firefox/profile.js +17 -1
- package/dist/browser/firefox/storage.d.ts.map +1 -1
- package/dist/browser/firefox/storage.js +4 -2
- package/dist/browser/firefox/subscriptions.d.ts +26 -0
- package/dist/browser/firefox/subscriptions.d.ts.map +1 -0
- package/dist/browser/firefox/subscriptions.js +10 -0
- package/dist/browser/launch.d.ts +6 -0
- package/dist/browser/launch.d.ts.map +1 -1
- package/dist/browser/launch.js +14 -1
- package/dist/cli/commands/doctor.js +3 -3
- package/dist/cli/commands/inspect.js +3 -2
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +26 -13
- package/dist/cli/commands/licences.d.ts +22 -0
- package/dist/cli/commands/licences.d.ts.map +1 -1
- package/dist/cli/commands/licences.js +62 -20
- package/dist/cli/commands/run.js +2 -2
- package/dist/cli/doctor/checks.js +4 -1
- package/dist/cli/inspect/diagnostics.js +3 -3
- package/dist/cli/inspect/test-timeline.js +10 -10
- package/dist/cli/install/doctor-rows.d.ts.map +1 -1
- package/dist/cli/install/doctor-rows.js +5 -4
- package/dist/cli/install/file-changes.d.ts +44 -0
- package/dist/cli/install/file-changes.d.ts.map +1 -0
- package/dist/cli/install/file-changes.js +81 -0
- package/dist/cli/install/install-archive.d.ts +3 -1
- package/dist/cli/install/install-archive.d.ts.map +1 -1
- package/dist/cli/install/install-archive.js +80 -33
- package/dist/cli/install/lock.d.ts.map +1 -1
- package/dist/cli/install/lock.js +108 -21
- package/dist/cli/install/media-doctor.d.ts.map +1 -1
- package/dist/cli/install/media-doctor.js +2 -1
- package/dist/cli/install/media-install.d.ts.map +1 -1
- package/dist/cli/install/media-install.js +38 -19
- package/dist/cli/install/media-pins.d.ts +7 -1
- package/dist/cli/install/media-pins.d.ts.map +1 -1
- package/dist/cli/install/media-pins.js +23 -12
- package/dist/cli/install/media-record.d.ts.map +1 -1
- package/dist/cli/install/media-record.js +10 -9
- package/dist/cli/install/media-report.d.ts +3 -1
- package/dist/cli/install/media-report.d.ts.map +1 -1
- package/dist/cli/install/media-report.js +7 -5
- package/dist/cli/install/media-tools.d.ts.map +1 -1
- package/dist/cli/install/media-tools.js +6 -4
- package/dist/cli/install/process-start.d.ts +3 -2
- package/dist/cli/install/process-start.d.ts.map +1 -1
- package/dist/cli/install/process-start.js +4 -0
- package/dist/cli/install/report.d.ts.map +1 -1
- package/dist/cli/install/report.js +11 -7
- package/dist/cli/install/supervisor-install.d.ts +33 -0
- package/dist/cli/install/supervisor-install.d.ts.map +1 -0
- package/dist/cli/install/supervisor-install.js +158 -0
- package/dist/cli/install/supervisor-locate.d.ts +47 -0
- package/dist/cli/install/supervisor-locate.d.ts.map +1 -0
- package/dist/cli/install/supervisor-locate.js +113 -0
- package/dist/cli/install/supervisor-pins.d.ts +21 -0
- package/dist/cli/install/supervisor-pins.d.ts.map +1 -0
- package/dist/cli/install/supervisor-pins.js +28 -0
- package/dist/cli/install/supervisor-record.d.ts +31 -0
- package/dist/cli/install/supervisor-record.d.ts.map +1 -0
- package/dist/cli/install/supervisor-record.js +104 -0
- package/dist/cli/install/supervisor-report.d.ts +11 -0
- package/dist/cli/install/supervisor-report.d.ts.map +1 -0
- package/dist/cli/install/supervisor-report.js +33 -0
- package/dist/cli/install/supervisor-tools.d.ts +43 -0
- package/dist/cli/install/supervisor-tools.d.ts.map +1 -0
- package/dist/cli/install/supervisor-tools.js +114 -0
- package/dist/cli/install/unpack.d.ts +23 -3
- package/dist/cli/install/unpack.d.ts.map +1 -1
- package/dist/cli/install/unpack.js +124 -10
- package/dist/cli/install/zip-member.d.ts +15 -0
- package/dist/cli/install/zip-member.d.ts.map +1 -0
- package/dist/cli/install/zip-member.js +77 -0
- package/dist/cli/interrupt.d.ts +1 -1
- package/dist/cli/interrupt.d.ts.map +1 -1
- package/dist/cli/interrupt.js +15 -10
- package/dist/cli/terminal.d.ts +9 -1
- package/dist/cli/terminal.d.ts.map +1 -1
- package/dist/cli/terminal.js +14 -0
- package/dist/config/define.d.ts +2 -2
- package/dist/config/define.js +2 -2
- package/dist/diagnostics/firefox-collector.d.ts +11 -10
- package/dist/diagnostics/firefox-collector.d.ts.map +1 -1
- package/dist/diagnostics/firefox-collector.js +16 -11
- package/dist/media/client.d.ts +2 -1
- package/dist/media/client.d.ts.map +1 -1
- package/dist/media/client.js +16 -5
- package/dist/media/host.d.ts +32 -0
- package/dist/media/host.d.ts.map +1 -0
- package/dist/media/host.js +50 -0
- package/dist/media/locate.d.ts.map +1 -1
- package/dist/media/locate.js +4 -3
- package/dist/native/processes.d.ts.map +1 -1
- package/dist/native/processes.js +7 -3
- package/dist/protocol/files.d.ts +1 -1
- package/dist/protocol/files.d.ts.map +1 -1
- package/dist/protocol/files.js +10 -5
- package/dist/reporters/commands.d.ts +12 -3
- package/dist/reporters/commands.d.ts.map +1 -1
- package/dist/reporters/commands.js +29 -11
- package/dist/reporters/human-card.js +1 -1
- package/dist/reporters/human.d.ts +3 -1
- package/dist/reporters/human.d.ts.map +1 -1
- package/dist/reporters/human.js +8 -8
- package/dist/reporters/style.d.ts +21 -2
- package/dist/reporters/style.d.ts.map +1 -1
- package/dist/reporters/style.js +11 -2
- package/dist/runner/app-server.d.ts +27 -7
- package/dist/runner/app-server.d.ts.map +1 -1
- package/dist/runner/app-server.js +74 -24
- package/dist/runner/child.js +6 -2
- package/dist/runner/media-leftovers.d.ts +7 -3
- package/dist/runner/media-leftovers.d.ts.map +1 -1
- package/dist/runner/media-leftovers.js +27 -6
- package/dist/runner/resources.d.ts +8 -0
- package/dist/runner/resources.d.ts.map +1 -1
- package/dist/runner/resources.js +12 -1
- package/dist/runner/run-session.d.ts.map +1 -1
- package/dist/runner/run-session.js +9 -4
- package/dist/runner/run.d.ts.map +1 -1
- package/dist/runner/run.js +2 -1
- package/dist/runner/target-drivers.d.ts +5 -2
- package/dist/runner/target-drivers.d.ts.map +1 -1
- package/dist/runner/target-drivers.js +10 -1
- package/dist/runner/test-file-process.d.ts +8 -0
- package/dist/runner/test-file-process.d.ts.map +1 -1
- package/dist/runner/test-file-process.js +26 -3
- package/dist/runner/test-pages.d.ts.map +1 -1
- package/dist/runner/test-pages.js +5 -2
- package/dist/shared/environment.d.ts +17 -0
- package/dist/shared/environment.d.ts.map +1 -0
- package/dist/shared/environment.js +27 -0
- package/dist/shared/metadata-process-worker.js +15 -7
- package/dist/shared/metadata-process.d.ts +5 -0
- package/dist/shared/metadata-process.d.ts.map +1 -1
- package/dist/shared/metadata-process.js +36 -6
- package/dist/shared/posix-path.d.ts +12 -0
- package/dist/shared/posix-path.d.ts.map +1 -1
- package/dist/shared/posix-path.js +16 -0
- package/dist/shared/private-file.d.ts +38 -0
- package/dist/shared/private-file.d.ts.map +1 -0
- package/dist/shared/private-file.js +132 -0
- package/dist/shared/process-ownership.d.ts +52 -2
- package/dist/shared/process-ownership.d.ts.map +1 -1
- package/dist/shared/process-ownership.js +104 -11
- package/dist/shared/process-supervisor.d.ts +309 -0
- package/dist/shared/process-supervisor.d.ts.map +1 -0
- package/dist/shared/process-supervisor.js +737 -0
- package/dist/shared/process-system.d.ts +113 -0
- package/dist/shared/process-system.d.ts.map +1 -0
- package/dist/shared/process-system.js +254 -0
- package/dist/shared/regular-file.d.ts +2 -2
- package/dist/shared/regular-file.d.ts.map +1 -1
- package/dist/shared/regular-file.js +19 -2
- package/dist/store/artifacts.d.ts +5 -4
- package/dist/store/artifacts.d.ts.map +1 -1
- package/dist/store/artifacts.js +18 -5
- package/dist/store/run-store.d.ts +5 -2
- package/dist/store/run-store.d.ts.map +1 -1
- package/dist/store/run-store.js +48 -4
- package/media/Cargo.lock +16 -0
- package/media/Cargo.toml +16 -1
- package/media/src/encoder.rs +56 -12
- package/media/src/frame.rs +7 -1
- package/media/src/main.rs +20 -2
- package/media/src/place.rs +230 -11
- package/media/src/process_ownership_windows.rs +1058 -0
- package/media/src/recording.rs +15 -7
- package/media/src/replies.rs +55 -0
- package/media/src/server.rs +72 -2
- package/media/src/store.rs +71 -2
- package/package.json +9 -4
- package/src/cli/install/media-notices.txt +465 -1
- package/supervisor/Cargo.lock +25 -0
- package/supervisor/Cargo.toml +22 -0
- package/supervisor/NOTICES +464 -0
- package/supervisor/build.rs +44 -0
- package/supervisor/src/command.rs +612 -0
- package/supervisor/src/jobs.rs +1650 -0
- package/supervisor/src/main.rs +71 -0
- package/supervisor/src/reply.rs +367 -0
|
@@ -8,8 +8,14 @@ export declare const mediaSourceFiles: readonly {
|
|
|
8
8
|
readonly path: string;
|
|
9
9
|
readonly sha256: string;
|
|
10
10
|
}[];
|
|
11
|
-
export type MediaTarget = 'aarch64-apple-darwin' | 'x86_64-unknown-linux-gnu';
|
|
11
|
+
export type MediaTarget = 'aarch64-apple-darwin' | 'x86_64-unknown-linux-gnu' | 'x86_64-pc-windows-msvc';
|
|
12
12
|
export declare function mediaTarget(platform?: NodeJS.Platform, arch?: string): MediaTarget | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* The media binary's file name for a target, the name Cargo builds it under.
|
|
15
|
+
*
|
|
16
|
+
* @example mediaExecutableName('x86_64-pc-windows-msvc') // 'retest-media.exe'
|
|
17
|
+
*/
|
|
18
|
+
export declare function mediaExecutableName(target: MediaTarget): string;
|
|
13
19
|
/** No publisher artifact has been released and checked. An absent checksum is a refusal, never trust on first use. */
|
|
14
20
|
export type MediaPrebuiltPin = {
|
|
15
21
|
readonly target: MediaTarget;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-pins.d.ts","sourceRoot":"","sources":["../../../src/cli/install/media-pins.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,YAAY,EAAE,MAAgB,CAAA;AAC3C,eAAO,MAAM,WAAW,EAAE,MAAiB,CAAA;AAC3C,uFAAuF;AACvF,eAAO,MAAM,uBAAuB,EAAE,MAAyB,CAAA;AAC/D,eAAO,MAAM,uBAAuB,EAAE,MAAoB,CAAA;AAC1D,eAAO,MAAM,iBAAiB,EAAE,MAA2E,CAAA;AAC3G,eAAO,MAAM,gBAAgB,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"media-pins.d.ts","sourceRoot":"","sources":["../../../src/cli/install/media-pins.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,YAAY,EAAE,MAAgB,CAAA;AAC3C,eAAO,MAAM,WAAW,EAAE,MAAiB,CAAA;AAC3C,uFAAuF;AACvF,eAAO,MAAM,uBAAuB,EAAE,MAAyB,CAAA;AAC/D,eAAO,MAAM,uBAAuB,EAAE,MAAoB,CAAA;AAC1D,eAAO,MAAM,iBAAiB,EAAE,MAA2E,CAAA;AAC3G,eAAO,MAAM,gBAAgB,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAuBzF,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,sBAAsB,GAAG,0BAA0B,GAAG,wBAAwB,CAAA;AAExG,wBAAgB,WAAW,CAAC,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAAE,IAAI,GAAE,MAAqB,GAAG,WAAW,GAAG,SAAS,CAK9H;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE/D;AAED,sHAAsH;AACtH,MAAM,MAAM,gBAAgB,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AACzN,eAAO,MAAM,iBAAiB,EAAE,SAAS,gBAAgB,EAAO,CAAA"}
|
|
@@ -4,35 +4,46 @@ export const minimumRust = '1.88.0';
|
|
|
4
4
|
/** Fixed limits for the pinned media binary and each shipped source or notice file. */
|
|
5
5
|
export const maximumMediaBinaryBytes = 64 * 1024 * 1024;
|
|
6
6
|
export const maximumMediaSourceBytes = 1024 * 1024;
|
|
7
|
-
export const mediaSourceDigest = '
|
|
7
|
+
export const mediaSourceDigest = '1b5d0357a1fdf3e9f167131bfa25b95980016655134116dbf52b3ed67c8e9aa6';
|
|
8
8
|
export const mediaSourceFiles = [
|
|
9
|
-
{ path: 'media/Cargo.toml', sha256: '
|
|
10
|
-
{ path: 'media/Cargo.lock', sha256: '
|
|
9
|
+
{ path: 'media/Cargo.toml', sha256: 'f16379ae5212da4cb4c0ae2c06df4562342ad95455e61a6b83e4b7c8bdb2d190' },
|
|
10
|
+
{ path: 'media/Cargo.lock', sha256: '43c003fc9a5581ad5439490efd4353fadb3eddd72902d863aa8f3adafcf90f9b' },
|
|
11
11
|
{ path: 'media/build.rs', sha256: 'fd8fac59c589459c46c32508f175ac47c60118612a7561c60e39ce2787d8bee2' },
|
|
12
12
|
{ path: 'media/src/allocations.rs', sha256: 'c752e4e7342e8886348da903854a624b2dbfcedfc0da087bc74ab80e4459434a' },
|
|
13
|
-
{ path: 'media/src/encoder.rs', sha256: '
|
|
14
|
-
{ path: 'media/src/frame.rs', sha256: '
|
|
13
|
+
{ path: 'media/src/encoder.rs', sha256: '09090bdd70849d45970eabab8c9320a47a511c7d5f2d9a85cfcb54094283c7ef' },
|
|
14
|
+
{ path: 'media/src/frame.rs', sha256: 'ee2d6cfa4fd5f53a8cca08aecdd7504f5043b491c0d34de27e8caaf658a6c44a' },
|
|
15
15
|
{ path: 'media/src/jobs.rs', sha256: '3e8cef01fbda58e4ae5f306dd9301c24ec230380014cd17491c5727fe5011f0d' },
|
|
16
16
|
{ path: 'media/src/ledger.rs', sha256: 'f0e72a8a178deff760cfe6c768f2660966de5c3c25fdff4eac732bea0f96c2c9' },
|
|
17
17
|
{ path: 'media/src/live.rs', sha256: 'a195cd510988345ce66e1e1f6931cf72877007cc12763c94547cf01efa36f4b6' },
|
|
18
|
-
{ path: 'media/src/main.rs', sha256: '
|
|
19
|
-
{ path: 'media/src/place.rs', sha256: '
|
|
18
|
+
{ path: 'media/src/main.rs', sha256: '37adada6ca274c9c42463bf3d816acce31a9030c8c6ea02b1cc66d227ab18e0a' },
|
|
19
|
+
{ path: 'media/src/place.rs', sha256: 'a6e8270ec3f89b2fc277a550e5043af44406daf604e8890da3222ffe97d0d6e7' },
|
|
20
20
|
{ path: 'media/src/process_ownership.rs', sha256: '5f2913404de98e619166ed06b6c9830a2d07a3814fc263ee5b8ce20c21b55362' },
|
|
21
|
+
{ path: 'media/src/process_ownership_windows.rs', sha256: '759d1ee5ec8191a84522e791828a55a4b733e6829d0b51ddb75a1f875f5c2dad' },
|
|
21
22
|
{ path: 'media/src/protocol.rs', sha256: '3060fe05ca5d63d291dec05449a3fa9f79bcd114e4fbd26e487619fe62ab68be' },
|
|
22
23
|
{ path: 'media/src/queue.rs', sha256: '8c5993bc5d2fdbdb4e5684cad9236af6c4a03e711e658256fb4ed27e413cc7fa' },
|
|
23
|
-
{ path: 'media/src/recording.rs', sha256: '
|
|
24
|
-
{ path: 'media/src/replies.rs', sha256: '
|
|
25
|
-
{ path: 'media/src/server.rs', sha256: '
|
|
26
|
-
{ path: 'media/src/store.rs', sha256: '
|
|
24
|
+
{ path: 'media/src/recording.rs', sha256: '84b2437aa62f52239606cf2da0c7319eea358b8b870732327a85ee97a6da304f' },
|
|
25
|
+
{ path: 'media/src/replies.rs', sha256: '8fb2d312dc31a3090ae49a8e47f5697166a23dc9846c4788c7e61839f5746e17' },
|
|
26
|
+
{ path: 'media/src/server.rs', sha256: '01834e8a6ace6d7517d7c6d2f96bdb41cd1171ceadde561aca46133169031b66' },
|
|
27
|
+
{ path: 'media/src/store.rs', sha256: '48247649b875c2d508ebd860ec8492fdc9a0407cc803cbbc46a121d01378e9e9' },
|
|
27
28
|
{ path: 'media/src/timeline.rs', sha256: '48be1f8e08895a35a41c4bea2d99f59384523541fd024826f65acafdb9ac7310' },
|
|
28
29
|
{ path: 'LICENSE', sha256: 'cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30' },
|
|
29
|
-
{ path: 'src/cli/install/media-notices.txt', sha256: '
|
|
30
|
+
{ path: 'src/cli/install/media-notices.txt', sha256: '172209e5295f60ef284f33dec45bac36d00d4803a329cafba27cd6218d14834a' },
|
|
30
31
|
];
|
|
31
32
|
export function mediaTarget(platform = process.platform, arch = process.arch) {
|
|
32
33
|
if (platform === 'darwin' && arch === 'arm64')
|
|
33
34
|
return 'aarch64-apple-darwin';
|
|
34
35
|
if (platform === 'linux' && arch === 'x64')
|
|
35
36
|
return 'x86_64-unknown-linux-gnu';
|
|
37
|
+
if (platform === 'win32' && arch === 'x64')
|
|
38
|
+
return 'x86_64-pc-windows-msvc';
|
|
36
39
|
return undefined;
|
|
37
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* The media binary's file name for a target, the name Cargo builds it under.
|
|
43
|
+
*
|
|
44
|
+
* @example mediaExecutableName('x86_64-pc-windows-msvc') // 'retest-media.exe'
|
|
45
|
+
*/
|
|
46
|
+
export function mediaExecutableName(target) {
|
|
47
|
+
return target === 'x86_64-pc-windows-msvc' ? 'retest-media.exe' : 'retest-media';
|
|
48
|
+
}
|
|
38
49
|
export const mediaPrebuiltPins = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-record.d.ts","sourceRoot":"","sources":["../../../src/cli/install/media-record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAgBpE,MAAM,MAAM,WAAW,GAAG;IACxB,aAAa,EAAE,CAAC,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,WAAW,CAAA;IACnB,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAGD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC3G,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,cAAc,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IAC7F,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpG,QAAQ,CAAC,kBAAkB,CAAC,EAAE,YAAY,CAAA;CAC3C,CAAA;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAAwE;AAClH,wBAAgB,WAAW,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAG1F;AACD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"media-record.d.ts","sourceRoot":"","sources":["../../../src/cli/install/media-record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAgBpE,MAAM,MAAM,WAAW,GAAG;IACxB,aAAa,EAAE,CAAC,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,WAAW,CAAA;IACnB,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAGD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC3G,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,cAAc,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IAC7F,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpG,QAAQ,CAAC,kBAAkB,CAAC,EAAE,YAAY,CAAA;CAC3C,CAAA;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAAwE;AAClH,wBAAgB,WAAW,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAG1F;AACD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAA+E;AAEpI,8GAA8G;AAC9G,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAW/G;AAED,wBAAgB,eAAe,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,CAEpE;AAED,4HAA4H;AAC5H,wBAAsB,YAAY,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,GAAE,eAAe,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAyDzO"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { constants } from 'node:fs';
|
|
3
3
|
import { access, lstat, readdir } from 'node:fs/promises';
|
|
4
|
-
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { cacheFolders } from "../../browser/builds.js";
|
|
7
7
|
import { MEDIA_PROTOCOL_VERSION } from "../../media/protocol.js";
|
|
@@ -10,15 +10,15 @@ import { errorMessage } from "../../protocol/failures.js";
|
|
|
10
10
|
import { Deadline } from "../../protocol/deadline.js";
|
|
11
11
|
import { isMissingFile } from "../../shared/error-code.js";
|
|
12
12
|
import { checkFileIdentity, readFileSha256, readRecordText } from "../../shared/regular-file.js";
|
|
13
|
-
import { maximumMediaBinaryBytes, maximumMediaSourceBytes, mediaPrebuiltPins, mediaSourceDigest, mediaSourceFiles, mediaTarget, mediaVersion, minimumRust } from "./media-pins.js";
|
|
13
|
+
import { maximumMediaBinaryBytes, maximumMediaSourceBytes, mediaExecutableName, mediaPrebuiltPins, mediaSourceDigest, mediaSourceFiles, mediaTarget, mediaVersion, minimumRust } from "./media-pins.js";
|
|
14
14
|
import { rustAtLeast } from "./media-tools.js";
|
|
15
|
-
const recordSchema = s.object({ schemaVersion: s.literal(1), version: s.string(), protocol: s.number({ integer: true, min: 1 }), sourceDigest: s.string(), target: s.enum(['aarch64-apple-darwin', 'x86_64-unknown-linux-gnu']), method: s.enum(['source', 'prebuilt']), rustcVersion: s.union([s.string(), s.literal(null)]), binarySha256: s.string(), notices: s.array(s.object({ path: s.string(), sha256: s.string() })), installedAt: s.string(), installedBy: s.string() });
|
|
15
|
+
const recordSchema = s.object({ schemaVersion: s.literal(1), version: s.string(), protocol: s.number({ integer: true, min: 1 }), sourceDigest: s.string(), target: s.enum(['aarch64-apple-darwin', 'x86_64-unknown-linux-gnu', 'x86_64-pc-windows-msvc']), method: s.enum(['source', 'prebuilt']), rustcVersion: s.union([s.string(), s.literal(null)]), binarySha256: s.string(), notices: s.array(s.object({ path: s.string(), sha256: s.string() })), installedAt: s.string(), installedBy: s.string() });
|
|
16
16
|
export function mediaPackageRoot() { return resolve(dirname(fileURLToPath(import.meta.url)), '../../..'); }
|
|
17
17
|
export function mediaFolder(env, target) {
|
|
18
18
|
const folders = cacheFolders(env);
|
|
19
19
|
return folders === undefined ? undefined : join(dirname(folders.browsers), 'media', `media-${mediaVersion}-${target}`);
|
|
20
20
|
}
|
|
21
|
-
export function mediaLockPath(folder) { return join(dirname(dirname(folder)), 'locks', `${folder
|
|
21
|
+
export function mediaLockPath(folder) { return join(dirname(dirname(folder)), 'locks', `${basename(folder)}.lock`); }
|
|
22
22
|
/** Hashes the pinned allowlist only. A changed, absent or non-regular source is refused before cargo runs. */
|
|
23
23
|
export async function checkMediaSource(root, options = {}) {
|
|
24
24
|
const deadline = options.deadline ?? new Deadline(15_000, { signal: options.signal });
|
|
@@ -46,7 +46,8 @@ export async function inspectMedia(env, options = {}) {
|
|
|
46
46
|
const folder = mediaFolder(env, target);
|
|
47
47
|
if (folder === undefined)
|
|
48
48
|
return undefined;
|
|
49
|
-
const
|
|
49
|
+
const executable = mediaExecutableName(target);
|
|
50
|
+
const base = { engine: 'media', version: mediaVersion, protocol: MEDIA_PROTOCOL_VERSION, target, folder, executablePath: join(folder, executable) };
|
|
50
51
|
const bad = (problem, state = 'damaged') => ({ ...base, state, problems: [problem] });
|
|
51
52
|
try {
|
|
52
53
|
const stats = await lstat(folder);
|
|
@@ -87,9 +88,9 @@ export async function inspectMedia(env, options = {}) {
|
|
|
87
88
|
if (JSON.stringify(record.notices) !== JSON.stringify(notices))
|
|
88
89
|
return bad(`${path} does not name the pinned licence notices.`);
|
|
89
90
|
let executableIdentity;
|
|
90
|
-
for (const file of [{ path:
|
|
91
|
+
for (const file of [{ path: executable, sha256: record.binarySha256 }, ...notices]) {
|
|
91
92
|
const filePath = join(folder, file.path);
|
|
92
|
-
const reading = await readFileSha256(filePath, { maximumBytes: file.path ===
|
|
93
|
+
const reading = await readFileSha256(filePath, { maximumBytes: file.path === executable ? maximumMediaBinaryBytes : maximumMediaSourceBytes, deadline, ...(options.signal === undefined ? {} : { signal: options.signal }) });
|
|
93
94
|
if (reading.kind !== 'file')
|
|
94
95
|
return bad(reading.kind === 'missing' ? `${filePath} is missing.` : reading.problem);
|
|
95
96
|
if (reading.sha256 !== file.sha256)
|
|
@@ -102,7 +103,7 @@ export async function inspectMedia(env, options = {}) {
|
|
|
102
103
|
return bad(`${filePath} was replaced after Retest hashed it; its file identity no longer matches.`);
|
|
103
104
|
if ((stats.mode & 3072n) !== 0n)
|
|
104
105
|
return bad(`${filePath} has a set-user-id or set-group-id bit.`);
|
|
105
|
-
if (file.path ===
|
|
106
|
+
if (file.path === executable)
|
|
106
107
|
executableIdentity = reading.identity;
|
|
107
108
|
}
|
|
108
109
|
catch (error) {
|
|
@@ -112,7 +113,7 @@ export async function inspectMedia(env, options = {}) {
|
|
|
112
113
|
if (!(await access(base.executablePath, constants.X_OK).then(() => true, () => false)))
|
|
113
114
|
return bad(`${base.executablePath} is not executable.`);
|
|
114
115
|
if (options.verify === true) {
|
|
115
|
-
const allowed = new Set(['build.json',
|
|
116
|
+
const allowed = new Set(['build.json', executable, ...notices.map((notice) => notice.path)]);
|
|
116
117
|
let names;
|
|
117
118
|
try {
|
|
118
119
|
names = await readdir(folder);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { MediaInspection } from './media-record.ts';
|
|
2
2
|
import type { MediaInstallResult } from './media-install.ts';
|
|
3
|
+
import type { Marks } from '../../reporters/style.ts';
|
|
3
4
|
/** Adds media to the existing list document without changing browser pin or record rules. */
|
|
4
5
|
export declare function renderMediaListing(browserText: string, inspection: MediaInspection | undefined, json: boolean): string;
|
|
5
|
-
|
|
6
|
+
/** The media install as the terminal shows it, with the marks the terminal draws. */
|
|
7
|
+
export declare function renderMediaInstall(result: MediaInstallResult, marks?: Marks): string;
|
|
6
8
|
//# sourceMappingURL=media-report.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-report.d.ts","sourceRoot":"","sources":["../../../src/cli/install/media-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"media-report.d.ts","sourceRoot":"","sources":["../../../src/cli/install/media-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAWrD,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAOtH;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,EAAE,KAAK,GAAE,KAAoB,GAAG,MAAM,CAGlG"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { mediaSourceDigest } from "./media-pins.js";
|
|
2
2
|
import { parse, s } from "../../protocol/schema.js";
|
|
3
|
+
import { unicodeMarks } from "../../reporters/style.js";
|
|
3
4
|
const licenceFile = s.object({ path: s.string(), title: s.string(), licence: s.string(), sha256: s.optional(s.string()), published: s.boolean(), bundled: s.optional(s.string()) });
|
|
4
5
|
const licences = s.union([s.object({ inspected: s.literal(true), files: s.array(licenceFile) }), s.object({ inspected: s.literal(false), reason: s.string() })]);
|
|
5
6
|
const sourceCode = s.object({ repository: s.string(), revision: s.string(), patches: s.string() });
|
|
6
|
-
const listedBuild = s.object({ engine: s.string(), title: s.string(), version: s.string(), build: s.optional(s.string()), platform: s.enum(['mac-arm64', 'linux-x64']), source: s.string(), pinnedSha256: s.union([s.string(), s.literal(null)]), state: s.enum(['missing', 'installed', 'damaged', 'unverifiable']), folder: s.string(), executablePath: s.optional(s.string()), sha256: s.optional(s.string()), installedAt: s.optional(s.string()), problems: s.array(s.string()), licences, sourceCode: s.optional(sourceCode), install: s.union([s.object({ command: s.string() }), s.object({ refused: s.string(), missingNotices: s.optional(s.array(s.string())) })]) });
|
|
7
|
-
const browserDocument = s.object({ schemaVersion: s.literal(1), platform: s.enum(['mac-arm64', 'linux-x64']), cache: s.object({ browsers: s.string(), executors: s.string() }), builds: s.array(listedBuild) });
|
|
7
|
+
const listedBuild = s.object({ engine: s.string(), title: s.string(), version: s.string(), build: s.optional(s.string()), platform: s.enum(['mac-arm64', 'linux-x64', 'win-x64']), source: s.string(), pinnedSha256: s.union([s.string(), s.literal(null)]), state: s.enum(['missing', 'installed', 'damaged', 'unverifiable']), folder: s.string(), executablePath: s.optional(s.string()), sha256: s.optional(s.string()), installedAt: s.optional(s.string()), problems: s.array(s.string()), licences, sourceCode: s.optional(sourceCode), install: s.union([s.object({ command: s.string() }), s.object({ refused: s.string(), missingNotices: s.optional(s.array(s.string())) })]) });
|
|
8
|
+
const browserDocument = s.object({ schemaVersion: s.literal(1), platform: s.enum(['mac-arm64', 'linux-x64', 'win-x64']), cache: s.object({ browsers: s.string(), executors: s.string() }), builds: s.array(listedBuild) });
|
|
8
9
|
/** Adds media to the existing list document without changing browser pin or record rules. */
|
|
9
10
|
export function renderMediaListing(browserText, inspection, json) {
|
|
10
11
|
if (inspection === undefined)
|
|
@@ -17,8 +18,9 @@ export function renderMediaListing(browserText, inspection, json) {
|
|
|
17
18
|
const media = { engine: 'media', title: 'retest-media', version: inspection.version, protocol: inspection.protocol, target: inspection.target, platform: document.value.platform, source: 'shipped crate source', pinnedSha256: null, sourceDigest: mediaSourceDigest, state: inspection.state, folder: inspection.folder, executablePath: inspection.executablePath, binarySha256: inspection.record?.binarySha256, rustcVersion: inspection.record?.rustcVersion, installedAt: inspection.record?.installedAt, problems: inspection.problems, install: { command: 'npx retest install media', prebuiltRefused: 'No published prebuilt checksum is pinned.' } };
|
|
18
19
|
return `${JSON.stringify({ ...document.value, builds: [...document.value.builds, media] }, null, 2)}\n`;
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
/** The media install as the terminal shows it, with the marks the terminal draws. */
|
|
22
|
+
export function renderMediaInstall(result, marks = unicodeMarks) {
|
|
21
23
|
if (!result.ok)
|
|
22
|
-
return `
|
|
23
|
-
return `
|
|
24
|
+
return ` ${marks.failed} media ${result.message}\n`;
|
|
25
|
+
return ` ${marks.passed} media retest-media ${result.inspection.version} ${result.action === 'installed' ? 'installed' : 'was already installed'}\n ${result.inspection.executablePath}\n binary SHA-256 ${result.inspection.record?.binarySha256 ?? 'unavailable'}\n Recording finds it from this checked cache. ffmpeg is a separate host prerequisite.\n`;
|
|
24
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-tools.d.ts","sourceRoot":"","sources":["../../../src/cli/install/media-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"media-tools.d.ts","sourceRoot":"","sources":["../../../src/cli/install/media-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAWlE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;AAC3E,MAAM,MAAM,gBAAgB,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,CAAA;AACzN,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;AAE7H,sGAAsG;AACtG,eAAO,MAAM,YAAY,EAAE,eAO1B,CAAA;AAED,4FAA4F;AAC5F,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAYpG;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AACxM,eAAO,MAAM,OAAO,EAAE,MAAmM,CAAA;AAEzN,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAAoB,GAAG,OAAO,CAUnF;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,GAAG,GAAE,eAA8B,GAAG,OAAO,CAAC,SAAS,CAAC,CAcxI;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzJ,sHAAsH;AACtH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,SAAS,EAAE,KAAK,UAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBjJ"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { constants } from 'node:fs';
|
|
2
2
|
import { access, realpath, stat } from 'node:fs/promises';
|
|
3
|
-
import { isAbsolute, join, resolve } from 'node:path';
|
|
3
|
+
import { delimiter, isAbsolute, join, resolve, sep } from 'node:path';
|
|
4
4
|
import { MediaProcess, mediaArguments } from "../../media/client.js";
|
|
5
5
|
import { mediaGreetingProblem } from "../../media/build-identity.js";
|
|
6
|
+
import { hostVariable, programFileName, toolchainVariables } from "../../media/host.js";
|
|
6
7
|
import { describeCommand, runCommand } from "../../native/processes.js";
|
|
7
8
|
import { errorMessage } from "../../protocol/failures.js";
|
|
8
9
|
import { minimumRust } from "./media-pins.js";
|
|
9
10
|
/** Tools inherit only the paths needed by the host toolchain, never test or evaluator credentials. */
|
|
10
11
|
export const runMediaTool = (command, args, options) => {
|
|
11
12
|
const environment = { LC_ALL: 'C' };
|
|
12
|
-
for (const key of
|
|
13
|
-
const value = options.env
|
|
13
|
+
for (const key of toolchainVariables) {
|
|
14
|
+
const value = hostVariable(options.env, key);
|
|
14
15
|
if (value !== undefined)
|
|
15
16
|
environment[key] = value;
|
|
16
17
|
}
|
|
@@ -18,7 +19,8 @@ export const runMediaTool = (command, args, options) => {
|
|
|
18
19
|
};
|
|
19
20
|
/** An explicit name wins even when it is unusable. PATH lookup never substitutes for it. */
|
|
20
21
|
export async function findMediaTool(name, env) {
|
|
21
|
-
const
|
|
22
|
+
const named = name.includes('/') || name.includes(sep);
|
|
23
|
+
const paths = named ? [resolve(name)] : (hostVariable(env, 'PATH') ?? '').split(delimiter).filter((part) => isAbsolute(part)).map((part) => join(part, programFileName(name)));
|
|
22
24
|
for (const path of paths) {
|
|
23
25
|
try {
|
|
24
26
|
const found = await realpath(path);
|
|
@@ -2,10 +2,11 @@ import type { NativeTools } from '../../native/processes.ts';
|
|
|
2
2
|
/**
|
|
3
3
|
* When a process started. On Linux, the kernel's clock ticks from boot to its start, as /proc keeps them, which a step
|
|
4
4
|
* of the wall clock does not move; on macOS, the second `ps` prints, read in UTC so it is the same whatever time zone
|
|
5
|
-
* the reader runs in.
|
|
5
|
+
* the reader runs in. On Windows a holder is told alive by the file it holds open, not by its start, and records
|
|
6
|
+
* `held-file`, which compares to no other start.
|
|
6
7
|
*/
|
|
7
8
|
export type ProcessStart = {
|
|
8
|
-
readonly clock: 'boot-ticks' | 'utc-seconds';
|
|
9
|
+
readonly clock: 'boot-ticks' | 'utc-seconds' | 'held-file';
|
|
9
10
|
readonly value: number;
|
|
10
11
|
};
|
|
11
12
|
/** A process's start as read now: there, with its start; no process under that pid; or why it could not be read. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-start.d.ts","sourceRoot":"","sources":["../../../src/cli/install/process-start.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAW5D
|
|
1
|
+
{"version":3,"file":"process-start.d.ts","sourceRoot":"","sources":["../../../src/cli/install/process-start.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAW5D;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,GAAG,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjH,oHAAoH;AACpH,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAElL,gDAAgD;AAChD,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;AAEhE,2GAA2G;AAC3G,MAAM,MAAM,kBAAkB,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,iBAAiB,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAAE,CAAA;AAE/K;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAIpE;AA2BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAS9D;AAID;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO9D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAKxG;AAED,8GAA8G;AAC9G,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpE,oHAAoH;AACpH,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAAE,CAAA;AAExI;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAU5E;AAED,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA"}
|
|
@@ -126,6 +126,10 @@ async function readMachineOnce(options) {
|
|
|
126
126
|
return { ok: false, problem: `Retest could not read this Mac's boot session: ${describeCommand('sysctl', result)}.` };
|
|
127
127
|
return { ok: true, machine: { id: `macos boot ${session}`, host } };
|
|
128
128
|
}
|
|
129
|
+
// A Windows holder is told alive by the file it holds open, which Windows closes when it ends, at a reboot too, so no
|
|
130
|
+
// pid or boot is compared there; the name keeps another machine's lock in a shared cache from being judged.
|
|
131
|
+
if (options.platform === 'win32')
|
|
132
|
+
return { ok: true, machine: { id: `windows ${host.toLowerCase()}`, host } };
|
|
129
133
|
}
|
|
130
134
|
catch (error) {
|
|
131
135
|
return { ok: false, problem: `Retest could not read which machine this is: ${errorMessage(error)}.` };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/cli/install/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAc,MAAM,yBAAyB,CAAA;AAC/J,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAMzD,kGAAkG;AAClG,eAAO,MAAM,cAAc,2BAA2B,CAAA;AAItD,kHAAkH;AAClH,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,EAAE,CAOpD;AAWD,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/cli/install/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAc,MAAM,yBAAyB,CAAA;AAC/J,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAMzD,kGAAkG;AAClG,eAAO,MAAM,cAAc,2BAA2B,CAAA;AAItD,kHAAkH;AAClH,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,EAAE,CAOpD;AAWD,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAKhE;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAQ3F;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYhE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAY/F;AAED,uCAAuC;AACvC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;IAChC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;IAC9B,QAAQ,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,CAAA;IAChD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CACtB,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAsClE;AAWD,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAA;IACrC,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;CAC3H,CAAA"}
|
|
@@ -25,7 +25,11 @@ const licenceNames = {
|
|
|
25
25
|
};
|
|
26
26
|
/** The project's licence names; component terms remain in the full notices and JSON pin. */
|
|
27
27
|
export function licenceSummary(pin) {
|
|
28
|
-
|
|
28
|
+
if (!pin.licences.inspected || pin.licences.files.length === 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
// Chrome for Testing carries Google's Widevine module on macOS and Linux, and not on Windows.
|
|
31
|
+
const names = pin.engine === 'chromium' && !pin.licences.files.some((file) => file.licence === 'LicenseRef-Widevine') ? 'BSD' : licenceNames[pin.engine];
|
|
32
|
+
return `${names}, notices kept with the build`;
|
|
29
33
|
}
|
|
30
34
|
/** The one notice message printed for each engine named in an install. */
|
|
31
35
|
export function installLicenceText(pin, folders) {
|
|
@@ -72,11 +76,11 @@ export function mirrorProblem(mirror) {
|
|
|
72
76
|
export function describeResult(engine, result, style) {
|
|
73
77
|
const indent = ' '.repeat(4 + engineColumn);
|
|
74
78
|
if (!result.ok && result.notices !== undefined)
|
|
75
|
-
return [` ${style.red(
|
|
79
|
+
return [` ${style.red(style.marks.failed)} ${engine.padEnd(engineColumn)}${result.notices.lead}`, ...result.notices.files.map((file) => `${indent}${file.path}, ${file.title}`), ''].join('\n');
|
|
76
80
|
if (!result.ok)
|
|
77
|
-
return ` ${style.red(
|
|
81
|
+
return ` ${style.red(style.marks.failed)} ${engine.padEnd(engineColumn)}${result.message}\n`;
|
|
78
82
|
const { inspection } = result;
|
|
79
|
-
const lines = [` ${style.green(
|
|
83
|
+
const lines = [` ${style.green(style.marks.passed)} ${engine.padEnd(engineColumn)}${describePin(inspection.pin)} ${result.action === 'installed' ? 'installed' : 'was already installed'}`];
|
|
80
84
|
if (inspection.executablePath !== undefined)
|
|
81
85
|
lines.push(`${indent}${inspection.executablePath}`);
|
|
82
86
|
else
|
|
@@ -106,19 +110,19 @@ export function renderInspections(report) {
|
|
|
106
110
|
const head = ` ${pin.engine.padEnd(engineColumn)}${describePin(pin).padEnd(titleWidth)}`;
|
|
107
111
|
const refusal = pinRefusal(pin);
|
|
108
112
|
if (inspection.state === 'installed') {
|
|
109
|
-
lines.push(`${head}${style.green(
|
|
113
|
+
lines.push(`${head}${style.green(style.marks.passed)} installed${inspection.sha256 === undefined ? '' : style.dim(` · ${pin.kind === 'archive' ? 'archive' : 'products'} SHA-256 ${inspection.sha256}`)}`);
|
|
110
114
|
lines.push(`${indent}${inspection.executablePath ?? inspection.folder}`);
|
|
111
115
|
}
|
|
112
116
|
else if (inspection.state === 'damaged') {
|
|
113
117
|
// Only a pin `retest install` installs is ever damaged, so running it again is a fix it accepts.
|
|
114
|
-
lines.push(`${head}${style.red(
|
|
118
|
+
lines.push(`${head}${style.red(style.marks.failed)} not as recorded in ${inspection.folder}`);
|
|
115
119
|
for (const problem of inspection.problems)
|
|
116
120
|
lines.push(`${indent}${problem}`);
|
|
117
121
|
lines.push(`${indent}Remove the folder and run ${retestCommand} install ${pin.engine} again.`);
|
|
118
122
|
}
|
|
119
123
|
else if (inspection.state === 'unverifiable') {
|
|
120
124
|
// Nothing in such a folder was read, since no install of Retest's put it there to check against the pin.
|
|
121
|
-
lines.push(`${head}${style.red(
|
|
125
|
+
lines.push(`${head}${style.red(style.marks.failed)} not installed by Retest, and nothing in ${inspection.folder} was checked`);
|
|
122
126
|
lines.push(`${indent}Remove the folder.`);
|
|
123
127
|
if (refusal !== undefined)
|
|
124
128
|
lines.push(...refusalLines(pin.engine, refusal, indent, style));
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SupervisorInspection, SupervisorEnvironment } from './supervisor-record.ts';
|
|
2
|
+
import type { SupervisorTarget } from './supervisor-pins.ts';
|
|
3
|
+
import type { SupervisorToolRunner } from './supervisor-tools.ts';
|
|
4
|
+
export type SupervisorInstallOptions = {
|
|
5
|
+
readonly env: SupervisorEnvironment;
|
|
6
|
+
readonly signal: AbortSignal;
|
|
7
|
+
readonly version: string;
|
|
8
|
+
readonly report: (line: string) => void;
|
|
9
|
+
readonly offline?: boolean;
|
|
10
|
+
readonly packageRoot?: string;
|
|
11
|
+
readonly target?: SupervisorTarget;
|
|
12
|
+
readonly run?: SupervisorToolRunner;
|
|
13
|
+
};
|
|
14
|
+
export type SupervisorInstallResult = {
|
|
15
|
+
readonly ok: true;
|
|
16
|
+
readonly action: 'installed' | 'already_installed';
|
|
17
|
+
readonly inspection: SupervisorInspection;
|
|
18
|
+
} | {
|
|
19
|
+
readonly ok: false;
|
|
20
|
+
readonly message: string;
|
|
21
|
+
readonly stopped: boolean;
|
|
22
|
+
};
|
|
23
|
+
/** The sentence `retest install supervisor` gives where no supervisor is needed. */
|
|
24
|
+
export declare const supervisorNotNeeded: string;
|
|
25
|
+
/**
|
|
26
|
+
* Builds the shipped supervisor crate with `cargo build --release --locked` into the cache, under the install lock, and
|
|
27
|
+
* records it. Nothing is downloaded but the pinned crates cargo fetches, and none with `offline`. A build already
|
|
28
|
+
* recorded is left as it is; a damaged cache is refused, never repaired.
|
|
29
|
+
*
|
|
30
|
+
* @example await installSupervisor({ env: process.env, signal, version: '0.3.0', report: console.log })
|
|
31
|
+
*/
|
|
32
|
+
export declare function installSupervisor(options: SupervisorInstallOptions): Promise<SupervisorInstallResult>;
|
|
33
|
+
//# sourceMappingURL=supervisor-install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor-install.d.ts","sourceRoot":"","sources":["../../../src/cli/install/supervisor-install.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAoB,MAAM,wBAAwB,CAAA;AAC3G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAajE,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,CAAC,GAAG,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpI,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,CAAA;CACnI,CAAA;AACD,MAAM,MAAM,uBAAuB,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAExO,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,EAAE,MAAqH,CAAA;AAEvJ;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA6B3G"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { describeCommand } from "../../native/processes.js";
|
|
5
|
+
import { errorMessage } from "../../protocol/failures.js";
|
|
6
|
+
import { Deadline } from "../../protocol/deadline.js";
|
|
7
|
+
import { openRegularFile, readFileSha256, readRecordText } from "../../shared/regular-file.js";
|
|
8
|
+
import { takeInstallLock } from "./lock.js";
|
|
9
|
+
import { maximumSupervisorBinaryBytes, supervisorExecutable, supervisorSourceDigest, supervisorSourceFiles, supervisorTarget, supervisorVersion } from "./supervisor-pins.js";
|
|
10
|
+
import { checkSupervisorSource, inspectSupervisor, supervisorFolder, supervisorInstallCommand, supervisorLockPath, supervisorNoticePins, supervisorPackageRoot } from "./supervisor-record.js";
|
|
11
|
+
import { probeSupervisor, readSupervisorRust, runSupervisorTool } from "./supervisor-tools.js";
|
|
12
|
+
/** The sentence `retest install supervisor` gives where no supervisor is needed. */
|
|
13
|
+
export const supervisorNotNeeded = 'retest-supervisor is for Windows. On macOS and Linux Retest needs nothing installed to end what it starts.';
|
|
14
|
+
/**
|
|
15
|
+
* Builds the shipped supervisor crate with `cargo build --release --locked` into the cache, under the install lock, and
|
|
16
|
+
* records it. Nothing is downloaded but the pinned crates cargo fetches, and none with `offline`. A build already
|
|
17
|
+
* recorded is left as it is; a damaged cache is refused, never repaired.
|
|
18
|
+
*
|
|
19
|
+
* @example await installSupervisor({ env: process.env, signal, version: '0.3.0', report: console.log })
|
|
20
|
+
*/
|
|
21
|
+
export async function installSupervisor(options) {
|
|
22
|
+
const refuse = (message) => ({ ok: false, message, stopped: options.signal.aborted });
|
|
23
|
+
const target = options.target ?? supervisorTarget();
|
|
24
|
+
if (target === undefined)
|
|
25
|
+
return refuse(process.platform === 'win32' ? `Retest builds retest-supervisor for Windows x64 only, and this machine is Windows ${process.arch}.` : supervisorNotNeeded);
|
|
26
|
+
const folder = supervisorFolder(options.env, target);
|
|
27
|
+
if (folder === undefined)
|
|
28
|
+
return refuse(`Retest has no cache folder here, so it cannot install retest-supervisor. Set the folder Retest caches into, then run ${supervisorInstallCommand}.`);
|
|
29
|
+
const root = options.packageRoot ?? supervisorPackageRoot();
|
|
30
|
+
const sourceProblem = await checkSupervisorSource(root, { signal: options.signal });
|
|
31
|
+
if (sourceProblem !== undefined)
|
|
32
|
+
return refuse(sourceProblem);
|
|
33
|
+
const lock = await takeInstallLock(supervisorLockPath(folder), { signal: options.signal });
|
|
34
|
+
if (!lock.ok)
|
|
35
|
+
return { ok: false, message: lock.message, stopped: lock.stopped === true };
|
|
36
|
+
let result;
|
|
37
|
+
let cleanupProblem;
|
|
38
|
+
const staging = `${folder}.staging-${process.pid}-${randomUUID()}`;
|
|
39
|
+
try {
|
|
40
|
+
result = await installLocked(options, target, folder, root, staging);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
result = refuse(`The supervisor install failed: ${errorMessage(error)} Nothing was installed.`);
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
try {
|
|
47
|
+
await rm(staging, { recursive: true, force: true, maxRetries: 3 });
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
cleanupProblem = `Supervisor staging cleanup failed at ${staging}: ${errorMessage(error)}.`;
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
const problem = await lock.release();
|
|
54
|
+
if (problem !== undefined)
|
|
55
|
+
options.report(problem);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (cleanupProblem !== undefined)
|
|
59
|
+
return result.ok
|
|
60
|
+
? refuse(`retest-supervisor ${result.action === 'installed' ? 'was installed' : 'was already installed'}, but cleanup is not proven. ${cleanupProblem}`)
|
|
61
|
+
: { ...result, message: `${result.message} ${cleanupProblem}` };
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
async function installLocked(options, target, folder, root, staging) {
|
|
65
|
+
const refuse = (message) => ({ ok: false, message, stopped: options.signal.aborted });
|
|
66
|
+
const before = await inspectSupervisor(options.env, { target, verify: true, signal: options.signal });
|
|
67
|
+
if (before?.state === 'installed')
|
|
68
|
+
return { ok: true, action: 'already_installed', inspection: before };
|
|
69
|
+
if (before !== undefined && before.state !== 'missing')
|
|
70
|
+
return refuse(`${before.problems.join(' ')} Remove ${folder} and run ${supervisorInstallCommand} again.`);
|
|
71
|
+
if (options.signal.aborted)
|
|
72
|
+
return refuse('The supervisor install was stopped before it built anything.');
|
|
73
|
+
const run = options.run ?? runSupervisorTool;
|
|
74
|
+
const tools = await readSupervisorRust(options.env, options.signal, run);
|
|
75
|
+
if (!tools.ok)
|
|
76
|
+
return refuse(tools.message);
|
|
77
|
+
if (tools.target !== target)
|
|
78
|
+
return refuse(`rustc builds for ${tools.target}, and this machine needs ${target}. Select the matching Rust host toolchain, then run ${supervisorInstallCommand}.`);
|
|
79
|
+
await mkdir(staging, { recursive: true });
|
|
80
|
+
const source = join(staging, 'source');
|
|
81
|
+
for (const file of supervisorSourceFiles) {
|
|
82
|
+
const from = join(root, ...file.path.split('/'));
|
|
83
|
+
const to = join(source, ...file.path.split('/'));
|
|
84
|
+
const reading = await readRecordText(from);
|
|
85
|
+
if (reading.kind !== 'text')
|
|
86
|
+
return refuse(`${from} cannot be copied: ${reading.kind === 'missing' ? 'missing' : reading.problem}.`);
|
|
87
|
+
await mkdir(dirname(to), { recursive: true });
|
|
88
|
+
await writeFile(to, reading.text, { flag: 'wx' });
|
|
89
|
+
}
|
|
90
|
+
const copiedProblem = await checkSupervisorSource(source, { signal: options.signal });
|
|
91
|
+
if (copiedProblem !== undefined)
|
|
92
|
+
return refuse(copiedProblem);
|
|
93
|
+
const targetDir = join(staging, 'target');
|
|
94
|
+
const args = ['build', '--release', '--locked', ...(options.offline === true ? ['--offline'] : []), '--manifest-path', join(source, 'supervisor', 'Cargo.toml'), '--target', target, '--target-dir', targetDir];
|
|
95
|
+
options.report(`Building retest-supervisor ${supervisorVersion} with ${tools.rustcVersion}; Cargo.lock is pinned.`);
|
|
96
|
+
const built = await run(tools.cargo, args, { env: options.env, signal: options.signal, cwd: source, timeoutMs: 30 * 60_000, environment: { RUSTC: tools.rustc, CARGO_TARGET_DIR: targetDir } });
|
|
97
|
+
const afterProblem = await checkSupervisorSource(source, { signal: options.signal });
|
|
98
|
+
if (afterProblem !== undefined)
|
|
99
|
+
return refuse(`The build changed the pinned source or Cargo.lock. ${afterProblem}`);
|
|
100
|
+
if (built.code !== 0 || built.stopped || built.timedOut || built.cleanupProblems.length > 0)
|
|
101
|
+
return refuse(`${describeCommand('cargo build --release --locked', built)}. Nothing was installed.`);
|
|
102
|
+
const executable = join(staging, supervisorExecutable);
|
|
103
|
+
const placed = await copyBinary(join(targetDir, target, 'release', supervisorExecutable), executable);
|
|
104
|
+
if (placed !== undefined)
|
|
105
|
+
return refuse(placed);
|
|
106
|
+
await rm(source, { recursive: true, maxRetries: 3 });
|
|
107
|
+
await rm(targetDir, { recursive: true, maxRetries: 3 });
|
|
108
|
+
if (options.signal.aborted)
|
|
109
|
+
return refuse('The supervisor install was stopped after its build. Nothing was installed.');
|
|
110
|
+
const probe = await probeSupervisor(executable, target, { env: options.env, signal: options.signal }, run);
|
|
111
|
+
if (probe !== undefined)
|
|
112
|
+
return refuse(probe);
|
|
113
|
+
const hash = await readFileSha256(executable, { maximumBytes: maximumSupervisorBinaryBytes, deadline: new Deadline(15_000, { signal: options.signal }) });
|
|
114
|
+
if (hash.kind === 'other')
|
|
115
|
+
return refuse(hash.problem);
|
|
116
|
+
if (hash.kind !== 'file')
|
|
117
|
+
return refuse('The built supervisor cannot be checked as a regular file.');
|
|
118
|
+
for (const file of supervisorSourceFiles.filter((file) => file.path === 'LICENSE' || file.path === 'supervisor/NOTICES')) {
|
|
119
|
+
const read = await readRecordText(join(root, ...file.path.split('/')));
|
|
120
|
+
if (read.kind !== 'text')
|
|
121
|
+
return refuse(`The pinned licence ${file.path} cannot be read.`);
|
|
122
|
+
await writeFile(join(staging, file.path === 'LICENSE' ? 'LICENSE' : 'THIRD-PARTY-NOTICES.txt'), read.text, { flag: 'wx' });
|
|
123
|
+
}
|
|
124
|
+
const sourceProblem = await checkSupervisorSource(root, { signal: options.signal });
|
|
125
|
+
if (sourceProblem !== undefined)
|
|
126
|
+
return refuse(sourceProblem);
|
|
127
|
+
const record = { schemaVersion: 1, version: supervisorVersion, sourceDigest: supervisorSourceDigest, target, rustcVersion: tools.rustcVersion, binarySha256: hash.sha256, notices: supervisorNoticePins(), installedAt: new Date().toISOString(), installedBy: options.version };
|
|
128
|
+
await writeFile(join(staging, 'build.json'), `${JSON.stringify(record, null, 2)}\n`, { flag: 'wx' });
|
|
129
|
+
await mkdir(dirname(folder), { recursive: true });
|
|
130
|
+
await rename(staging, folder);
|
|
131
|
+
const after = await inspectSupervisor(options.env, { target, verify: true, signal: options.signal });
|
|
132
|
+
return after?.state === 'installed' ? { ok: true, action: 'installed', inspection: after } : refuse(`The supervisor folder did not read as installed: ${after?.problems.join(' ') ?? 'no cache'}.`);
|
|
133
|
+
}
|
|
134
|
+
// The built binary, copied from the one descriptor it was checked through, within the binary bound.
|
|
135
|
+
async function copyBinary(from, to) {
|
|
136
|
+
const opened = await openRegularFile(from);
|
|
137
|
+
if (opened.kind !== 'opened')
|
|
138
|
+
return `${from} is not a readable regular supervisor binary: ${opened.kind}.`;
|
|
139
|
+
try {
|
|
140
|
+
if (opened.size > maximumSupervisorBinaryBytes)
|
|
141
|
+
return `${from} is larger than ${maximumSupervisorBinaryBytes} bytes.`;
|
|
142
|
+
const bytes = Buffer.alloc(opened.size + 1);
|
|
143
|
+
let filled = 0;
|
|
144
|
+
while (filled < bytes.length) {
|
|
145
|
+
const read = await opened.handle.read(bytes, filled, bytes.length - filled, null);
|
|
146
|
+
if (read.bytesRead === 0)
|
|
147
|
+
break;
|
|
148
|
+
filled += read.bytesRead;
|
|
149
|
+
}
|
|
150
|
+
if (filled !== opened.size)
|
|
151
|
+
return `${from} changed size while it was copied.`;
|
|
152
|
+
await writeFile(to, bytes.subarray(0, filled), { flag: 'wx', mode: 0o755 });
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
await opened.handle.close();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { SupervisorProgram } from '../../shared/process-supervisor.ts';
|
|
2
|
+
import type { SupervisorTarget } from './supervisor-pins.ts';
|
|
3
|
+
/** The environment the install and the runner read the cache folder from. */
|
|
4
|
+
export type SupervisorEnvironment = Readonly<Record<string, string | undefined>>;
|
|
5
|
+
/** What `build.json` beside an installed supervisor says of it. */
|
|
6
|
+
export type SupervisorRecord = {
|
|
7
|
+
schemaVersion: 1;
|
|
8
|
+
version: string;
|
|
9
|
+
sourceDigest: string;
|
|
10
|
+
target: SupervisorTarget;
|
|
11
|
+
rustcVersion: string;
|
|
12
|
+
binarySha256: string;
|
|
13
|
+
notices: {
|
|
14
|
+
path: string;
|
|
15
|
+
sha256: string;
|
|
16
|
+
}[];
|
|
17
|
+
installedAt: string;
|
|
18
|
+
installedBy: string;
|
|
19
|
+
};
|
|
20
|
+
/** The command a person runs to install the supervisor. */
|
|
21
|
+
export declare const supervisorInstallCommand: string;
|
|
22
|
+
/**
|
|
23
|
+
* The cache folder one supervisor build is installed in: `%LOCALAPPDATA%\retest\supervisor\…`, beside the browsers in
|
|
24
|
+
* the root `cacheFolders` gives on Windows, the one platform it is built for. LOCALAPPDATA is read in any case, as
|
|
25
|
+
* Windows reads it.
|
|
26
|
+
*
|
|
27
|
+
* @example supervisorFolder(process.env, 'x86_64-pc-windows-msvc') // 'C:\\Users\\ada\\AppData\\Local\\retest\\supervisor\\supervisor-0.1.0-x86_64-pc-windows-msvc'
|
|
28
|
+
*/
|
|
29
|
+
export declare function supervisorFolder(env: SupervisorEnvironment, target: SupervisorTarget): string | undefined;
|
|
30
|
+
/** A `build.json` read as this version's record, or why it is not one; its notices and toolchain are the install's to check. */
|
|
31
|
+
export declare function readSupervisorRecord(path: string, text: string, target: SupervisorTarget): {
|
|
32
|
+
readonly ok: true;
|
|
33
|
+
readonly record: SupervisorRecord;
|
|
34
|
+
} | {
|
|
35
|
+
readonly ok: false;
|
|
36
|
+
readonly problem: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The supervisor the runner runs, found as it starts its first process on Windows: the executable
|
|
40
|
+
* RETEST_SUPERVISOR_BINARY names, as a developer build is given, or the installed one, read back against its record and
|
|
41
|
+
* its checksum. It reads synchronously, since it runs once, right before a launch that cannot start without it. Throws a
|
|
42
|
+
* sentence that says how to install it.
|
|
43
|
+
*
|
|
44
|
+
* @example const program = locateSupervisor(process.env) // { command: '…\\retest-supervisor.exe', args: [], environment: { SystemRoot: 'C:\\Windows' } }
|
|
45
|
+
*/
|
|
46
|
+
export declare function locateSupervisor(env: SupervisorEnvironment): SupervisorProgram;
|
|
47
|
+
//# sourceMappingURL=supervisor-locate.d.ts.map
|