ttsc 0.31.1 → 0.31.2
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/lib/compiler/internal/build/BuildExecution.d.ts +4 -2
- package/lib/compiler/internal/build/BuildExecution.js +40 -12
- package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +5 -4
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +9 -7
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/internal/javascriptRuntimeCapabilities.d.ts +13 -4
- package/lib/internal/javascriptRuntimeCapabilities.js +56 -6
- package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
- package/lib/internal/pluginSource.d.ts +1 -0
- package/lib/internal/pluginSource.js +1 -0
- package/lib/internal/pluginSource.js.map +1 -1
- package/lib/internal/resolveNodeBinary.d.ts +6 -1
- package/lib/internal/resolveNodeBinary.js +6 -2
- package/lib/internal/resolveNodeBinary.js.map +1 -1
- package/lib/internal/runtimeExecutableIdentity.d.ts +22 -13
- package/lib/internal/runtimeExecutableIdentity.js +192 -102
- package/lib/internal/runtimeExecutableIdentity.js.map +1 -1
- package/lib/launcher/internal/runtime/installRuntimeHooks.js +58 -4
- package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
- package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.d.ts +5 -1
- package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js +7 -2
- package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js.map +1 -1
- package/lib/launcher/internal/ttscserver/runTtscserver.js +8 -1
- package/lib/launcher/internal/ttscserver/runTtscserver.js.map +1 -1
- package/lib/plugin/internal/CapabilityResolutionFormat.d.ts +21 -62
- package/lib/plugin/internal/CapabilityResolutionFormat.js +15 -106
- package/lib/plugin/internal/CapabilityResolutionFormat.js.map +1 -1
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +6 -0
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +4 -4
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
- package/lib/plugin/internal/load/loadProjectPlugins.d.ts +12 -1
- package/lib/plugin/internal/load/loadProjectPlugins.js +80 -20
- package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
- package/lib/plugin/internal/readCapabilityResolution.d.ts +6 -0
- package/lib/plugin/internal/readCapabilityResolution.js +8 -5
- package/lib/plugin/internal/readCapabilityResolution.js.map +1 -1
- package/lib/plugin/internal/source/GoEnvironmentReading.d.ts +9 -3
- package/lib/plugin/internal/source/GoEnvironmentReading.js +53 -7
- package/lib/plugin/internal/source/GoEnvironmentReading.js.map +1 -1
- package/lib/plugin/internal/source/NativeSourcePackages.d.ts +44 -6
- package/lib/plugin/internal/source/NativeSourcePackages.js +150 -8
- package/lib/plugin/internal/source/NativeSourcePackages.js.map +1 -1
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +21 -3
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +32 -7
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
- package/lib/plugin/internal/source/PluginContentIdentities.d.ts +265 -0
- package/lib/plugin/internal/source/PluginContentIdentities.js +452 -0
- package/lib/plugin/internal/source/PluginContentIdentities.js.map +1 -0
- package/lib/plugin/internal/source/PluginLoadAnswers.d.ts +63 -0
- package/lib/plugin/internal/source/PluginLoadAnswers.js +128 -0
- package/lib/plugin/internal/source/PluginLoadAnswers.js.map +1 -0
- package/lib/plugin/internal/source/SourceBuildCacheLayout.d.ts +10 -0
- package/lib/plugin/internal/source/SourceBuildCacheLayout.js +12 -0
- package/lib/plugin/internal/source/SourceBuildCacheLayout.js.map +1 -1
- package/lib/plugin/internal/source/SourcePluginWorkspace.d.ts +5 -2
- package/lib/plugin/internal/source/SourcePluginWorkspace.js +5 -3
- package/lib/plugin/internal/source/SourcePluginWorkspace.js.map +1 -1
- package/lib/plugin/internal/source/buildSourcePlugin.d.ts +15 -2
- package/lib/plugin/internal/source/buildSourcePlugin.js +27 -4
- package/lib/plugin/internal/source/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/source/computeCacheKey.d.ts +21 -5
- package/lib/plugin/internal/source/computeCacheKey.js +71 -29
- package/lib/plugin/internal/source/computeCacheKey.js.map +1 -1
- package/lib/plugin/internal/source/hashPluginBuildEnvironment.d.ts +7 -3
- package/lib/plugin/internal/source/hashPluginBuildEnvironment.js +74 -35
- package/lib/plugin/internal/source/hashPluginBuildEnvironment.js.map +1 -1
- package/lib/plugin/internal/source/pluginBuildEnvironment.d.ts +8 -3
- package/lib/plugin/internal/source/pluginBuildEnvironment.js +8 -4
- package/lib/plugin/internal/source/pluginBuildEnvironment.js.map +1 -1
- package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js +12 -2
- package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js.map +1 -1
- package/lib/plugin/internal/source/pluginBuildVersions.d.ts +2 -2
- package/lib/plugin/internal/source/pluginBuildVersions.js +4 -17
- package/lib/plugin/internal/source/pluginBuildVersions.js.map +1 -1
- package/lib/plugin/internal/source/pluginSourceState.d.ts +6 -2
- package/lib/plugin/internal/source/pluginSourceState.js +20 -8
- package/lib/plugin/internal/source/pluginSourceState.js.map +1 -1
- package/lib/plugin/internal/source/pluginSourceStateHolds.d.ts +7 -2
- package/lib/plugin/internal/source/pluginSourceStateHolds.js +12 -7
- package/lib/plugin/internal/source/pluginSourceStateHolds.js.map +1 -1
- package/lib/plugin/internal/source/processPluginBuildEnvironment.d.ts +7 -2
- package/lib/plugin/internal/source/processPluginBuildEnvironment.js +8 -3
- package/lib/plugin/internal/source/processPluginBuildEnvironment.js.map +1 -1
- package/lib/plugin/internal/source/provenPluginSourceDigest.d.ts +28 -0
- package/lib/plugin/internal/source/provenPluginSourceDigest.js +41 -0
- package/lib/plugin/internal/source/provenPluginSourceDigest.js.map +1 -0
- package/lib/plugin/internal/source/pruneCacheFileRoot.d.ts +3 -2
- package/lib/plugin/internal/source/pruneCacheFileRoot.js +3 -2
- package/lib/plugin/internal/source/pruneCacheFileRoot.js.map +1 -1
- package/lib/plugin/internal/source/ttscPackageVersion.d.ts +26 -0
- package/lib/plugin/internal/source/ttscPackageVersion.js +47 -0
- package/lib/plugin/internal/source/ttscPackageVersion.js.map +1 -0
- package/lib/plugin/internal/writeCapabilityResolution.d.ts +6 -0
- package/lib/plugin/internal/writeCapabilityResolution.js +11 -7
- package/lib/plugin/internal/writeCapabilityResolution.js.map +1 -1
- package/lib/plugin/resolveCapabilityPlugins.js +67 -15
- package/lib/plugin/resolveCapabilityPlugins.js.map +1 -1
- package/package.json +8 -8
- package/src/compiler/internal/build/BuildExecution.ts +46 -13
- package/src/compiler/internal/startResidentTransform.ts +12 -3
- package/src/compiler/internal/transformProjectInMemory.ts +10 -2
- package/src/internal/javascriptRuntimeCapabilities.ts +71 -5
- package/src/internal/pluginSource.ts +1 -0
- package/src/internal/resolveNodeBinary.ts +12 -1
- package/src/internal/runtimeExecutableIdentity.ts +227 -141
- package/src/launcher/internal/runtime/installRuntimeHooks.ts +68 -3
- package/src/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.ts +11 -2
- package/src/launcher/internal/ttscserver/runTtscserver.ts +18 -1
- package/src/plugin/internal/CapabilityResolutionFormat.ts +23 -107
- package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +14 -4
- package/src/plugin/internal/load/loadProjectPlugins.ts +212 -94
- package/src/plugin/internal/readCapabilityResolution.ts +17 -6
- package/src/plugin/internal/source/GoEnvironmentReading.ts +74 -5
- package/src/plugin/internal/source/NativeSourcePackages.ts +262 -5
- package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +41 -5
- package/src/plugin/internal/source/PluginContentIdentities.ts +563 -0
- package/src/plugin/internal/source/PluginLoadAnswers.ts +151 -0
- package/src/plugin/internal/source/SourceBuildCacheLayout.ts +14 -0
- package/src/plugin/internal/source/SourcePluginWorkspace.ts +6 -1
- package/src/plugin/internal/source/buildSourcePlugin.ts +43 -3
- package/src/plugin/internal/source/computeCacheKey.ts +103 -47
- package/src/plugin/internal/source/hashPluginBuildEnvironment.ts +144 -29
- package/src/plugin/internal/source/pluginBuildEnvironment.ts +9 -2
- package/src/plugin/internal/source/pluginBuildEnvironmentWorker.ts +18 -2
- package/src/plugin/internal/source/pluginBuildVersions.ts +5 -26
- package/src/plugin/internal/source/pluginSourceState.ts +28 -12
- package/src/plugin/internal/source/pluginSourceStateHolds.ts +15 -8
- package/src/plugin/internal/source/processPluginBuildEnvironment.ts +8 -2
- package/src/plugin/internal/source/provenPluginSourceDigest.ts +45 -0
- package/src/plugin/internal/source/pruneCacheFileRoot.ts +3 -2
- package/src/plugin/internal/source/ttscPackageVersion.ts +45 -0
- package/src/plugin/internal/writeCapabilityResolution.ts +29 -8
- package/src/plugin/resolveCapabilityPlugins.ts +87 -13
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ITtscBuildContributor } from "./ITtscBuildContributor";
|
|
2
2
|
import type { PluginBuildEnvironmentWitness } from "./PluginBuildEnvironmentWitness";
|
|
3
|
+
import { PluginContentIdentities } from "./PluginContentIdentities";
|
|
3
4
|
import type { SourceBuildFilesystemOperations } from "./SourceBuildFilesystemOperations";
|
|
4
5
|
import type { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
5
6
|
/**
|
|
@@ -21,10 +22,12 @@ import type { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
|
21
22
|
* values are trusted producer readings, not revalidated content observations.
|
|
22
23
|
* `sourceDigests` carries the digests one load already took: every build of the
|
|
23
24
|
* load keys on one reading of each directory, and the load reports those
|
|
24
|
-
* readings. The environment enters
|
|
25
|
-
* rule that reported state takes it from too,
|
|
26
|
-
* the digest of each build directory's, so the
|
|
27
|
-
* `go env` run.
|
|
25
|
+
* readings. The environment enters as the digest of
|
|
26
|
+
* `hashPluginBuildEnvironment`, the rule that reported state takes it from too,
|
|
27
|
+
* and `environmentDigests` carries the digest of each build directory's, so the
|
|
28
|
+
* load reports it without a second `go env` run. With `environmentWitnesses`, a
|
|
29
|
+
* reading the load already took for the same directory, such as package
|
|
30
|
+
* selection's, keys the build too, and its witness joins the build's.
|
|
28
31
|
*
|
|
29
32
|
* The `ttsc cache` CLI and plugin build pipeline share this key computation.
|
|
30
33
|
*
|
|
@@ -34,7 +37,7 @@ import type { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
|
34
37
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain input coverage and the provenance of reported digests; documented optional output maps have blank separation between members.
|
|
35
38
|
* @evidence contracts/portability.md#os-neutral-implementation Native roots are resolved with Node path APIs and executable resolution; platform/architecture intentionally distinguish incompatible binary artifacts.
|
|
36
39
|
* @evidence contracts/performance.md#efficient-algorithms Source digest misses enumerate/sort paths and read individual files in full, not bounded streaming chunks. Overlay/contributor ordering compares path/name text; replacement discovery can read a manifest/run Go and native containment queries. Toolchain/env hashing delegates native metadata/probes/full bytes and memo checks without an unmeasured dominant-cost ranking; JSON framing processes all version/entry/label/digest text.
|
|
37
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Caller-owned sourceDigests share supplied absolute-directory readings across roles without independently verifying their provenance/currentness. EnvironmentDigests receives this call's selected environment identity so reporting can avoid another probe; it
|
|
40
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Caller-owned sourceDigests share supplied absolute-directory readings across roles without independently verifying their provenance/currentness. A supplied record store lets a new process reuse a directory, SDK or executable digest only while its separable metadata signature matches the recorded one (#1722), so unchanged inputs are not re-read per process. EnvironmentDigests receives this call's selected environment identity so reporting can avoid another probe. A same-directory reading of the same load, with its witness, replaces a second environment probe; the witness then joins this build's, which the build compares after it ran, and a non-default byte adapter never shares. Reuse inherits the selected population and metadata/producer premises.
|
|
38
41
|
*
|
|
39
42
|
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Output maps belong to the enclosing load; this operation retains no handles or process-wide records itself.
|
|
40
43
|
*/
|
|
@@ -54,6 +57,13 @@ export declare function computeCacheKey(inputs: {
|
|
|
54
57
|
* its plugin sources' states without reading the environment again.
|
|
55
58
|
*/
|
|
56
59
|
environmentDigests?: Map<string, string>;
|
|
60
|
+
/**
|
|
61
|
+
* Witness of each reading in `environmentDigests` this load took, by
|
|
62
|
+
* directory. A digest with its witness stands for this call's own reading:
|
|
63
|
+
* the key takes it instead of running Go again and copies the witness into
|
|
64
|
+
* `environmentWitness`. Filled with this call's reading when it takes one.
|
|
65
|
+
*/
|
|
66
|
+
environmentWitnesses?: Map<string, PluginBuildEnvironmentWitness.Record>;
|
|
57
67
|
/**
|
|
58
68
|
* Receives selected native toolchain dependencies from the environment reader
|
|
59
69
|
* for later comparison under its metadata-distinguishability premise; it is
|
|
@@ -64,6 +74,12 @@ export declare function computeCacheKey(inputs: {
|
|
|
64
74
|
filesystem?: Partial<SourceBuildFilesystemOperations>;
|
|
65
75
|
goBinary?: string;
|
|
66
76
|
goModReader?: SourcePluginWorkspace.GoModReader;
|
|
77
|
+
/**
|
|
78
|
+
* Record store that proves unchanged sources, overlays, contributors, GOROOT
|
|
79
|
+
* and executables from metadata in a new process (#1722). Without it every
|
|
80
|
+
* process reads their bytes.
|
|
81
|
+
*/
|
|
82
|
+
identities?: PluginContentIdentities.Store;
|
|
67
83
|
overlayDirs?: readonly string[];
|
|
68
84
|
/**
|
|
69
85
|
* Digests of the source directories this load already read, by absolute path.
|
|
@@ -8,9 +8,9 @@ const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const GoToolResolution_1 = require("./GoToolResolution");
|
|
11
|
+
const PluginContentIdentities_1 = require("./PluginContentIdentities");
|
|
11
12
|
const hashPluginBuildEnvironment_1 = require("./hashPluginBuildEnvironment");
|
|
12
13
|
const pluginModuleReplaceDirectories_1 = require("./pluginModuleReplaceDirectories");
|
|
13
|
-
const pluginSourceDigest_1 = require("./pluginSourceDigest");
|
|
14
14
|
/**
|
|
15
15
|
* Compute a 32-character lowercase key from a plugin build's SHA-256 digest.
|
|
16
16
|
*
|
|
@@ -30,10 +30,12 @@ const pluginSourceDigest_1 = require("./pluginSourceDigest");
|
|
|
30
30
|
* values are trusted producer readings, not revalidated content observations.
|
|
31
31
|
* `sourceDigests` carries the digests one load already took: every build of the
|
|
32
32
|
* load keys on one reading of each directory, and the load reports those
|
|
33
|
-
* readings. The environment enters
|
|
34
|
-
* rule that reported state takes it from too,
|
|
35
|
-
* the digest of each build directory's, so the
|
|
36
|
-
* `go env` run.
|
|
33
|
+
* readings. The environment enters as the digest of
|
|
34
|
+
* `hashPluginBuildEnvironment`, the rule that reported state takes it from too,
|
|
35
|
+
* and `environmentDigests` carries the digest of each build directory's, so the
|
|
36
|
+
* load reports it without a second `go env` run. With `environmentWitnesses`, a
|
|
37
|
+
* reading the load already took for the same directory, such as package
|
|
38
|
+
* selection's, keys the build too, and its witness joins the build's.
|
|
37
39
|
*
|
|
38
40
|
* The `ttsc cache` CLI and plugin build pipeline share this key computation.
|
|
39
41
|
*
|
|
@@ -43,7 +45,7 @@ const pluginSourceDigest_1 = require("./pluginSourceDigest");
|
|
|
43
45
|
* @evidence contracts/common.md#meaningful-documentation Native paragraphs explain input coverage and the provenance of reported digests; documented optional output maps have blank separation between members.
|
|
44
46
|
* @evidence contracts/portability.md#os-neutral-implementation Native roots are resolved with Node path APIs and executable resolution; platform/architecture intentionally distinguish incompatible binary artifacts.
|
|
45
47
|
* @evidence contracts/performance.md#efficient-algorithms Source digest misses enumerate/sort paths and read individual files in full, not bounded streaming chunks. Overlay/contributor ordering compares path/name text; replacement discovery can read a manifest/run Go and native containment queries. Toolchain/env hashing delegates native metadata/probes/full bytes and memo checks without an unmeasured dominant-cost ranking; JSON framing processes all version/entry/label/digest text.
|
|
46
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Caller-owned sourceDigests share supplied absolute-directory readings across roles without independently verifying their provenance/currentness. EnvironmentDigests receives this call's selected environment identity so reporting can avoid another probe; it
|
|
48
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Caller-owned sourceDigests share supplied absolute-directory readings across roles without independently verifying their provenance/currentness. A supplied record store lets a new process reuse a directory, SDK or executable digest only while its separable metadata signature matches the recorded one (#1722), so unchanged inputs are not re-read per process. EnvironmentDigests receives this call's selected environment identity so reporting can avoid another probe. A same-directory reading of the same load, with its witness, replaces a second environment probe; the witness then joins this build's, which the build compares after it ran, and a non-default byte adapter never shares. Reuse inherits the selected population and metadata/producer premises.
|
|
47
49
|
*
|
|
48
50
|
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Output maps belong to the enclosing load; this operation retains no handles or process-wide records itself.
|
|
49
51
|
*/
|
|
@@ -78,43 +80,83 @@ function computeCacheKey(inputs) {
|
|
|
78
80
|
// binaries produced by the former absolute-ancestry layout out of this
|
|
79
81
|
// layout's cache admissions, including callers with no SDK overlays.
|
|
80
82
|
hash.update(JSON.stringify(["external-source-layout", 1]));
|
|
81
|
-
|
|
82
|
-
// which pluginBuildEnvironment reads for this directory.
|
|
83
|
-
const environment = node_crypto_1.default.createHash("sha256");
|
|
84
|
-
(0, hashPluginBuildEnvironment_1.hashPluginBuildEnvironment)({
|
|
85
|
-
update: (data) => {
|
|
86
|
-
hash.update(data);
|
|
87
|
-
environment.update(data);
|
|
88
|
-
},
|
|
89
|
-
}, goBinary, inputs.dir, env, filesystem, inputs.environmentWitness);
|
|
90
|
-
inputs.environmentDigests?.set(node_path_1.default.resolve(inputs.dir), environment.digest("hex"));
|
|
91
|
-
hashSourceDirectory(hash, "plugin", inputs.dir, inputs.sourceDigests);
|
|
92
|
-
// Local replacement targets outside the module supply separately keyed
|
|
93
|
-
// sources. buildSourcePlugin snapshots and proves their copies before
|
|
94
|
-
// redirecting the Go replacement directives to the scratch tree.
|
|
95
|
-
for (const replacement of (0, pluginModuleReplaceDirectories_1.pluginModuleReplaceDirectories)(inputs.dir, env, goBinary, inputs.goModReader)) {
|
|
96
|
-
hashSourceDirectory(hash, `replace:${replacement.modulePath}${replacement.version === undefined ? "" : `@${replacement.version}`}`, replacement.directory, inputs.sourceDigests);
|
|
97
|
-
}
|
|
98
|
-
for (const [index, dir] of [...(inputs.overlayDirs ?? [])].sort().entries()) {
|
|
99
|
-
hashSourceDirectory(hash, `overlay:${index}`, dir, inputs.sourceDigests);
|
|
100
|
-
}
|
|
83
|
+
const overlays = [...(inputs.overlayDirs ?? [])].sort();
|
|
101
84
|
// Hash contributors in sorted-by-name order so two consumers with the
|
|
102
85
|
// same logical set produce the same key regardless of declaration order
|
|
103
86
|
// in the host's plugin descriptor.
|
|
104
87
|
const sortedContributors = [...(inputs.contributors ?? [])].sort((a, b) => a.name === b.name ? 0 : a.name < b.name ? -1 : 1);
|
|
88
|
+
// A store whose root lies inside a directory this key hashes would write
|
|
89
|
+
// records into that source, so such a store proves nothing here. The build
|
|
90
|
+
// rejects that cache placement (`SourcePluginAdmission`) once every source
|
|
91
|
+
// is known; replacement targets narrow the store below, before their own
|
|
92
|
+
// digests, since only Go's reading of the manifest names them.
|
|
93
|
+
let identities = PluginContentIdentities_1.PluginContentIdentities.outside(inputs.identities, [
|
|
94
|
+
inputs.dir,
|
|
95
|
+
...overlays,
|
|
96
|
+
...sortedContributors.map((contributor) => contributor.source),
|
|
97
|
+
]);
|
|
98
|
+
// The key frames the environment digest pluginBuildEnvironment reports for
|
|
99
|
+
// this directory. WARNING (#1721): package selection reads that environment
|
|
100
|
+
// first in the same load; reading it again here doubled `go env` and
|
|
101
|
+
// `go version` per load. A reading taken through another byte adapter is
|
|
102
|
+
// not this one's, so only the default adapter shares it.
|
|
103
|
+
const environmentDirectory = node_path_1.default.resolve(inputs.dir);
|
|
104
|
+
const sharedDigest = inputs.filesystem === undefined
|
|
105
|
+
? inputs.environmentDigests?.get(environmentDirectory)
|
|
106
|
+
: undefined;
|
|
107
|
+
const sharedWitness = sharedDigest === undefined
|
|
108
|
+
? undefined
|
|
109
|
+
: inputs.environmentWitnesses?.get(environmentDirectory);
|
|
110
|
+
let environment;
|
|
111
|
+
if (sharedDigest !== undefined && sharedWitness !== undefined) {
|
|
112
|
+
// The build compares this witness after it ran, exactly as it would its
|
|
113
|
+
// own reading's, so a toolchain change since the reading still refuses.
|
|
114
|
+
for (const [file, state] of sharedWitness)
|
|
115
|
+
if (!inputs.environmentWitness?.has(file))
|
|
116
|
+
inputs.environmentWitness?.set(file, state);
|
|
117
|
+
environment = sharedDigest;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
const witness = inputs.environmentWitness ??
|
|
121
|
+
(inputs.environmentWitnesses === undefined ? undefined : new Map());
|
|
122
|
+
const reading = node_crypto_1.default.createHash("sha256");
|
|
123
|
+
(0, hashPluginBuildEnvironment_1.hashPluginBuildEnvironment)(reading, goBinary, inputs.dir, env, filesystem, witness, identities);
|
|
124
|
+
environment = reading.digest("hex");
|
|
125
|
+
inputs.environmentDigests?.set(environmentDirectory, environment);
|
|
126
|
+
if (inputs.filesystem === undefined && witness !== undefined)
|
|
127
|
+
inputs.environmentWitnesses?.set(environmentDirectory, new Map(witness));
|
|
128
|
+
}
|
|
129
|
+
hash.update(JSON.stringify(["environment", environment]));
|
|
130
|
+
const directory = (label, root) => hashSourceDirectory(hash, label, root, inputs.sourceDigests, identities);
|
|
131
|
+
directory("plugin", inputs.dir);
|
|
132
|
+
// Local replacement targets outside the module supply separately keyed
|
|
133
|
+
// sources. buildSourcePlugin snapshots and proves their copies before
|
|
134
|
+
// redirecting the Go replacement directives to the scratch tree. Go reads the
|
|
135
|
+
// manifest after the plugin's digest, so an edit made meanwhile lands between
|
|
136
|
+
// that digest and the build's copy, which `requireKeyedSource` refuses.
|
|
137
|
+
const replacements = (0, pluginModuleReplaceDirectories_1.pluginModuleReplaceDirectories)(inputs.dir, env, goBinary, inputs.goModReader);
|
|
138
|
+
identities = PluginContentIdentities_1.PluginContentIdentities.outside(identities, replacements.map((replacement) => replacement.directory));
|
|
139
|
+
for (const replacement of replacements) {
|
|
140
|
+
directory(`replace:${replacement.modulePath}${replacement.version === undefined ? "" : `@${replacement.version}`}`, replacement.directory);
|
|
141
|
+
}
|
|
142
|
+
for (const [index, dir] of overlays.entries()) {
|
|
143
|
+
directory(`overlay:${index}`, dir);
|
|
144
|
+
}
|
|
105
145
|
for (const contributor of sortedContributors) {
|
|
106
|
-
|
|
146
|
+
directory(`contributor:${contributor.name}`, contributor.source);
|
|
107
147
|
}
|
|
108
148
|
return hash.digest("hex").slice(0, 32);
|
|
109
149
|
}
|
|
110
150
|
const DEFAULT_SOURCE_BUILD_FILESYSTEM = Object.freeze({
|
|
111
151
|
readFile: (location) => node_fs_1.default.readFileSync(location),
|
|
112
152
|
});
|
|
113
|
-
function hashSourceDirectory(hash, label, root, digests) {
|
|
153
|
+
function hashSourceDirectory(hash, label, root, digests, identities) {
|
|
114
154
|
const directory = node_path_1.default.resolve(root);
|
|
115
155
|
let digest = digests?.get(directory);
|
|
116
156
|
if (digest === undefined) {
|
|
117
|
-
|
|
157
|
+
// WARNING (#1722): the load-scoped map above ends with the process. Only the
|
|
158
|
+
// record store spares a new process from reading every source byte again.
|
|
159
|
+
digest = PluginContentIdentities_1.PluginContentIdentities.sourceDirectory(identities, directory);
|
|
118
160
|
digests?.set(directory, digest);
|
|
119
161
|
}
|
|
120
162
|
hash.update(JSON.stringify(["dir", label, digest]));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computeCacheKey.js","sourceRoot":"","sources":["../../../../src/plugin/internal/source/computeCacheKey.ts"],"names":[],"mappings":";;;;;;AAAA,8DAAiC;AACjC,sDAAyB;AACzB,0DAA6B;AAE7B,yDAAsD;
|
|
1
|
+
{"version":3,"file":"computeCacheKey.js","sourceRoot":"","sources":["../../../../src/plugin/internal/source/computeCacheKey.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PluginBuildEnvironmentWitness } from "./PluginBuildEnvironmentWitness";
|
|
2
|
+
import { PluginContentIdentities } from "./PluginContentIdentities";
|
|
2
3
|
import type { SourceBuildFilesystemOperations } from "./SourceBuildFilesystemOperations";
|
|
3
4
|
/**
|
|
4
5
|
* Hash the environment a plugin build is keyed on into `hash`: the Go
|
|
@@ -33,15 +34,18 @@ import type { SourceBuildFilesystemOperations } from "./SourceBuildFilesystemOpe
|
|
|
33
34
|
* variable carries: the Go tool, the Go environment file `go env -w` writes,
|
|
34
35
|
* the executables the C toolchain commands name, and GOROOT. A consumer that
|
|
35
36
|
* keeps the reading compares their metadata before reusing it.
|
|
37
|
+
* @param identities Record store that lets a new process prove the GOROOT
|
|
38
|
+
* corpus and executable bytes from their metadata (`PluginContentIdentities`,
|
|
39
|
+
* #1722); without it every process reads them.
|
|
36
40
|
* @evidence contracts/common.md#principled-implementation Fixed artifact flags, observed compiler bytes/version, selected reported-or-fallback build values, named command executables and selected SDK files enter the same framed identity used by source-state reporting. Fallback values and metadata-qualified reuse are explicit premises, not complete observation of arbitrary toolchain inputs.
|
|
37
41
|
* @evidence contracts/common.md#clear-and-simple-design Private helpers separate compiler identity, Go-reported settings, external variables and SDK content while sharing one hash sink and optional pre-read witness.
|
|
38
42
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Cache signatures include real identity/change metadata and effective invocation context; failed SDK witnessing refuses reuse rather than accepting a VERSION-only proxy.
|
|
39
43
|
* @evidence contracts/common.md#meaningful-documentation Native documentation names the input classes and witness purpose; private comments explain context-sensitive compiler memoization and SDK exclusions without treating a passing check as proof.
|
|
40
44
|
* @evidence contracts/portability.md#os-neutral-implementation Node path/stat/process APIs resolve native tool identities; platform-specific executable suffixes and environment lookup are isolated in GoToolResolution, and Go supplies its own effective build settings.
|
|
41
45
|
* @evidence contracts/performance.md#efficient-algorithms SDK traversal sorts reached entry names and selected file paths, performs native stat/realpath/link queries and serializes topology/metadata/path text. Matching manifests share aggregate hashes; changes read full selected files via the caller adapter, retaining file buffers with population/topology data. Compiler misses capture at most three complete version/byte readings when native metadata moves during a reading; only a stable attempt enters the memo. Go env can run up to three times for its file witness, and command tokens can each perform executable searches/full reads. Environment sorting/framing and command substring parsing also process name/value/command bytes; E/D/F/B alone do not capture native lookup or text comparison costs.
|
|
42
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Compiler memo identity includes selected path/file metadata and invocation cwd/environment; SDK aggregate reuse requires matching complete ordered metadata/topology.
|
|
43
|
-
* @evidence contracts/performance.md#bound-retention-and-release-resources Compiler, Go-environment-path and SDK maps retain historical distinct tool/context/root keys without eviction. SDK population/topology and full individual file reads contribute transient bytes; synchronous probes delegate process/capture lifetime to their owner and do not certify arbitrary descendants are gone. No independent entry/record/file/output byte ceiling is imposed here.
|
|
46
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Compiler memo identity includes selected path/file metadata and invocation cwd/environment; SDK aggregate reuse requires matching complete ordered metadata/topology. With a record store, a new process reuses the SDK aggregate and executable digests only while their signatures match the recorded ones and every stamp is separable from a freshly minted reference (#1722); `go version` and `go env` stay live because they report toolchain selection no file metadata shows. All of this relies on native metadata distinguishability and sequential observation premises. Physical directories expand once per selection policy while aliases retain edges; optional caller witnesses receive reached selected dependencies.
|
|
47
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Compiler, Go-environment-path and SDK maps retain historical distinct tool/context/root keys without eviction; persisted records belong to the single-file collector. SDK population/topology and full individual file reads contribute transient bytes; synchronous probes delegate process/capture lifetime to their owner and do not certify arbitrary descendants are gone. No independent entry/record/file/output byte ceiling is imposed here.
|
|
44
48
|
*/
|
|
45
49
|
export declare function hashPluginBuildEnvironment(hash: {
|
|
46
50
|
update(data: string): unknown;
|
|
47
|
-
}, goBinary: string | undefined, directory: string, env: NodeJS.ProcessEnv, filesystem: SourceBuildFilesystemOperations, witness?: PluginBuildEnvironmentWitness.Record): void;
|
|
51
|
+
}, goBinary: string | undefined, directory: string, env: NodeJS.ProcessEnv, filesystem: SourceBuildFilesystemOperations, witness?: PluginBuildEnvironmentWitness.Record, identities?: PluginContentIdentities.Store): void;
|
|
@@ -11,6 +11,7 @@ const GoEnvironmentReading_1 = require("./GoEnvironmentReading");
|
|
|
11
11
|
const GoSourceInputs_1 = require("./GoSourceInputs");
|
|
12
12
|
const GoToolResolution_1 = require("./GoToolResolution");
|
|
13
13
|
const PluginBuildEnvironmentWitness_1 = require("./PluginBuildEnvironmentWitness");
|
|
14
|
+
const PluginContentIdentities_1 = require("./PluginContentIdentities");
|
|
14
15
|
const spawnGoTool_1 = require("./spawnGoTool");
|
|
15
16
|
/**
|
|
16
17
|
* Hash the environment a plugin build is keyed on into `hash`: the Go
|
|
@@ -45,26 +46,29 @@ const spawnGoTool_1 = require("./spawnGoTool");
|
|
|
45
46
|
* variable carries: the Go tool, the Go environment file `go env -w` writes,
|
|
46
47
|
* the executables the C toolchain commands name, and GOROOT. A consumer that
|
|
47
48
|
* keeps the reading compares their metadata before reusing it.
|
|
49
|
+
* @param identities Record store that lets a new process prove the GOROOT
|
|
50
|
+
* corpus and executable bytes from their metadata (`PluginContentIdentities`,
|
|
51
|
+
* #1722); without it every process reads them.
|
|
48
52
|
* @evidence contracts/common.md#principled-implementation Fixed artifact flags, observed compiler bytes/version, selected reported-or-fallback build values, named command executables and selected SDK files enter the same framed identity used by source-state reporting. Fallback values and metadata-qualified reuse are explicit premises, not complete observation of arbitrary toolchain inputs.
|
|
49
53
|
* @evidence contracts/common.md#clear-and-simple-design Private helpers separate compiler identity, Go-reported settings, external variables and SDK content while sharing one hash sink and optional pre-read witness.
|
|
50
54
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts Cache signatures include real identity/change metadata and effective invocation context; failed SDK witnessing refuses reuse rather than accepting a VERSION-only proxy.
|
|
51
55
|
* @evidence contracts/common.md#meaningful-documentation Native documentation names the input classes and witness purpose; private comments explain context-sensitive compiler memoization and SDK exclusions without treating a passing check as proof.
|
|
52
56
|
* @evidence contracts/portability.md#os-neutral-implementation Node path/stat/process APIs resolve native tool identities; platform-specific executable suffixes and environment lookup are isolated in GoToolResolution, and Go supplies its own effective build settings.
|
|
53
57
|
* @evidence contracts/performance.md#efficient-algorithms SDK traversal sorts reached entry names and selected file paths, performs native stat/realpath/link queries and serializes topology/metadata/path text. Matching manifests share aggregate hashes; changes read full selected files via the caller adapter, retaining file buffers with population/topology data. Compiler misses capture at most three complete version/byte readings when native metadata moves during a reading; only a stable attempt enters the memo. Go env can run up to three times for its file witness, and command tokens can each perform executable searches/full reads. Environment sorting/framing and command substring parsing also process name/value/command bytes; E/D/F/B alone do not capture native lookup or text comparison costs.
|
|
54
|
-
* @evidence contracts/performance.md#reuse-equivalent-work Compiler memo identity includes selected path/file metadata and invocation cwd/environment; SDK aggregate reuse requires matching complete ordered metadata/topology.
|
|
55
|
-
* @evidence contracts/performance.md#bound-retention-and-release-resources Compiler, Go-environment-path and SDK maps retain historical distinct tool/context/root keys without eviction. SDK population/topology and full individual file reads contribute transient bytes; synchronous probes delegate process/capture lifetime to their owner and do not certify arbitrary descendants are gone. No independent entry/record/file/output byte ceiling is imposed here.
|
|
58
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Compiler memo identity includes selected path/file metadata and invocation cwd/environment; SDK aggregate reuse requires matching complete ordered metadata/topology. With a record store, a new process reuses the SDK aggregate and executable digests only while their signatures match the recorded ones and every stamp is separable from a freshly minted reference (#1722); `go version` and `go env` stay live because they report toolchain selection no file metadata shows. All of this relies on native metadata distinguishability and sequential observation premises. Physical directories expand once per selection policy while aliases retain edges; optional caller witnesses receive reached selected dependencies.
|
|
59
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Compiler, Go-environment-path and SDK maps retain historical distinct tool/context/root keys without eviction; persisted records belong to the single-file collector. SDK population/topology and full individual file reads contribute transient bytes; synchronous probes delegate process/capture lifetime to their owner and do not certify arbitrary descendants are gone. No independent entry/record/file/output byte ceiling is imposed here.
|
|
56
60
|
*/
|
|
57
|
-
function hashPluginBuildEnvironment(hash, goBinary, directory, env, filesystem, witness) {
|
|
61
|
+
function hashPluginBuildEnvironment(hash, goBinary, directory, env, filesystem, witness, identities) {
|
|
58
62
|
// Fixed artifact policy must invalidate binaries and reported source states
|
|
59
63
|
// together, including binaries cached before this policy was introduced.
|
|
60
64
|
hash.update(JSON.stringify(["source-build-flags", GoSourceInputs_1.GoSourceInputs.BUILD_FLAGS]));
|
|
61
65
|
if (goBinary !== undefined) {
|
|
62
66
|
hash.update(JSON.stringify([
|
|
63
67
|
"go",
|
|
64
|
-
resolveGoCompilerIdentity(goBinary, env, directory, witness),
|
|
68
|
+
resolveGoCompilerIdentity(goBinary, env, directory, witness, identities),
|
|
65
69
|
]));
|
|
66
70
|
}
|
|
67
|
-
hashGoBuildEnvironment(hash, goBinary, directory, env, filesystem, witness);
|
|
71
|
+
hashGoBuildEnvironment(hash, goBinary, directory, env, filesystem, witness, identities);
|
|
68
72
|
hashExternalGoBuildEnvironment(hash, env);
|
|
69
73
|
}
|
|
70
74
|
// Go build environment values that can change the produced binary or decide
|
|
@@ -155,7 +159,7 @@ const EXTERNAL_GO_BUILD_ENV_KEYS = [
|
|
|
155
159
|
// memo key includes that context so an environment-sensitive wrapper cannot
|
|
156
160
|
// lend its version result to another compiler invocation.
|
|
157
161
|
const goCompilerIdentityCache = new Map();
|
|
158
|
-
function resolveGoCompilerIdentity(goBinary, env = process.env, cwd = process.cwd(), witness) {
|
|
162
|
+
function resolveGoCompilerIdentity(goBinary, env = process.env, cwd = process.cwd(), witness, identities) {
|
|
159
163
|
const selected = GoToolResolution_1.GoToolResolution.resolveGoToolForBuild(goBinary, env, cwd, witness);
|
|
160
164
|
const resolved = process.platform === "win32"
|
|
161
165
|
? resolveRealPath(selected)
|
|
@@ -183,7 +187,7 @@ function resolveGoCompilerIdentity(goBinary, env = process.env, cwd = process.cw
|
|
|
183
187
|
const observed = new Map();
|
|
184
188
|
PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(observed, resolved);
|
|
185
189
|
const memoKey = goCompilerIdentityMemoKey(goBinary, resolved, compilerEnv, cwd);
|
|
186
|
-
identity = computeGoCompilerIdentity(selected, resolved, compilerEnv, cwd);
|
|
190
|
+
identity = computeGoCompilerIdentity(selected, resolved, compilerEnv, cwd, identities);
|
|
187
191
|
if (!PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.holds(observed))
|
|
188
192
|
continue;
|
|
189
193
|
for (const [file, signature] of observed)
|
|
@@ -225,10 +229,12 @@ function goCompilerIdentityMemoKey(goBinary, resolved, env, cwd) {
|
|
|
225
229
|
return null;
|
|
226
230
|
}
|
|
227
231
|
}
|
|
228
|
-
function computeGoCompilerIdentity(goBinary, resolved, env, cwd) {
|
|
232
|
+
function computeGoCompilerIdentity(goBinary, resolved, env, cwd, identities) {
|
|
229
233
|
if (!node_fs_1.default.existsSync(resolved)) {
|
|
230
234
|
return "missing";
|
|
231
235
|
}
|
|
236
|
+
// `go version` stays a live probe: it answers which toolchain the effective
|
|
237
|
+
// environment selects (GOTOOLCHAIN, go.mod), which no file metadata shows.
|
|
232
238
|
const version = (0, spawnGoTool_1.spawnGoTool)(goBinary, ["version"], {
|
|
233
239
|
cwd,
|
|
234
240
|
encoding: "utf8",
|
|
@@ -238,9 +244,16 @@ function computeGoCompilerIdentity(goBinary, resolved, env, cwd) {
|
|
|
238
244
|
const versionText = version.error !== undefined
|
|
239
245
|
? (version.error.code ?? version.error.message)
|
|
240
246
|
: `${version.status ?? 0}:${version.stdout}${version.stderr}`;
|
|
241
|
-
const binaryHash =
|
|
247
|
+
const binaryHash = hashExecutable(resolved, identities);
|
|
242
248
|
return `sha256:${binaryHash}:${versionText}`;
|
|
243
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* The content digest of an executable, proven from its metadata in a process
|
|
252
|
+
* that has the record store (#1722) and read in full otherwise.
|
|
253
|
+
*/
|
|
254
|
+
function hashExecutable(file, identities) {
|
|
255
|
+
return PluginContentIdentities_1.PluginContentIdentities.digest(identities, "executable", file, (references) => PluginContentIdentities_1.PluginContentIdentities.fileObservation(references, file), () => hashFile(file));
|
|
256
|
+
}
|
|
244
257
|
function resolveExecutableIdentityPath(binary, env = process.env, cwd = process.cwd(), witness) {
|
|
245
258
|
const resolved = findExecutablePath(binary, env, cwd, witness);
|
|
246
259
|
return resolved === null ? binary : resolveRealPath(resolved);
|
|
@@ -282,8 +295,8 @@ function hashFile(file) {
|
|
|
282
295
|
hash.update(node_fs_1.default.readFileSync(file));
|
|
283
296
|
return hash.digest("hex");
|
|
284
297
|
}
|
|
285
|
-
function hashGoBuildEnvironment(hash, goBinary, cwd, env, filesystem, witness) {
|
|
286
|
-
const values = resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness);
|
|
298
|
+
function hashGoBuildEnvironment(hash, goBinary, cwd, env, filesystem, witness, identities) {
|
|
299
|
+
const values = resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness, identities);
|
|
287
300
|
for (const key of GO_BUILD_ENV_KEYS) {
|
|
288
301
|
const value = values.get(key);
|
|
289
302
|
if (value !== undefined && value !== "") {
|
|
@@ -291,16 +304,16 @@ function hashGoBuildEnvironment(hash, goBinary, cwd, env, filesystem, witness) {
|
|
|
291
304
|
}
|
|
292
305
|
}
|
|
293
306
|
}
|
|
294
|
-
function resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness) {
|
|
307
|
+
function resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness, identities) {
|
|
295
308
|
const values = new Map();
|
|
296
309
|
if (goBinary !== undefined) {
|
|
297
|
-
const parsed = GoEnvironmentReading_1.GoEnvironmentReading.read(goBinary, cwd, env, GO_BUILD_ENV_KEYS, witness);
|
|
310
|
+
const parsed = GoEnvironmentReading_1.GoEnvironmentReading.read(goBinary, cwd, env, GO_BUILD_ENV_KEYS, witness, identities);
|
|
298
311
|
if (parsed !== undefined) {
|
|
299
312
|
try {
|
|
300
313
|
for (const key of GO_BUILD_ENV_KEYS) {
|
|
301
314
|
const raw = parsed[key];
|
|
302
315
|
if (typeof raw === "string" && raw !== "") {
|
|
303
|
-
values.set(key, normalizeGoBuildEnvValue(key, raw, env, filesystem, witness));
|
|
316
|
+
values.set(key, normalizeGoBuildEnvValue(key, raw, env, filesystem, witness, identities));
|
|
304
317
|
}
|
|
305
318
|
}
|
|
306
319
|
}
|
|
@@ -315,22 +328,22 @@ function resolveGoBuildEnvironment(goBinary, cwd, env, filesystem, witness) {
|
|
|
315
328
|
continue;
|
|
316
329
|
const value = env[key];
|
|
317
330
|
if (value !== undefined && value !== "") {
|
|
318
|
-
values.set(key, normalizeGoBuildEnvValue(key, value, env, filesystem, witness));
|
|
331
|
+
values.set(key, normalizeGoBuildEnvValue(key, value, env, filesystem, witness, identities));
|
|
319
332
|
}
|
|
320
333
|
}
|
|
321
334
|
return values;
|
|
322
335
|
}
|
|
323
|
-
function normalizeGoBuildEnvValue(key, value, env, filesystem, witness) {
|
|
336
|
+
function normalizeGoBuildEnvValue(key, value, env, filesystem, witness, identities) {
|
|
324
337
|
if (key === "GOROOT") {
|
|
325
338
|
// Root/version observations also cover a missing SDK. The complete SDK
|
|
326
339
|
// manifest below witnesses contributing files and directories, including
|
|
327
340
|
// nested content changes that do not move the root or VERSION metadata.
|
|
328
341
|
PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(witness, value);
|
|
329
342
|
PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(witness, node_path_1.default.join(value, "VERSION"));
|
|
330
|
-
return resolveGoRootCacheIdentity(value, filesystem, witness);
|
|
343
|
+
return resolveGoRootCacheIdentity(value, filesystem, witness, identities);
|
|
331
344
|
}
|
|
332
345
|
if (GO_BUILD_COMMAND_ENV_KEYS.has(key)) {
|
|
333
|
-
return `${value}\0${resolveCommandCacheIdentity(value, env, witness)}`;
|
|
346
|
+
return `${value}\0${resolveCommandCacheIdentity(value, env, witness, identities)}`;
|
|
334
347
|
}
|
|
335
348
|
return value;
|
|
336
349
|
}
|
|
@@ -346,7 +359,7 @@ function normalizeGoBuildEnvValue(key, value, env, filesystem, witness) {
|
|
|
346
359
|
* which the key carries beside this identity. A program a launcher finds by its
|
|
347
360
|
* own means, not named in the command, is outside what the command can show.
|
|
348
361
|
*/
|
|
349
|
-
function resolveCommandCacheIdentity(command, env, witness) {
|
|
362
|
+
function resolveCommandCacheIdentity(command, env, witness, identities) {
|
|
350
363
|
const tokens = splitGoCommand(command);
|
|
351
364
|
if (tokens === null) {
|
|
352
365
|
return `command:unterminated:${command}`;
|
|
@@ -362,7 +375,7 @@ function resolveCommandCacheIdentity(command, env, witness) {
|
|
|
362
375
|
}
|
|
363
376
|
let identity;
|
|
364
377
|
try {
|
|
365
|
-
identity = `command:sha256:${
|
|
378
|
+
identity = `command:sha256:${hashExecutable(resolved, identities)}`;
|
|
366
379
|
}
|
|
367
380
|
catch {
|
|
368
381
|
return `command:unreadable:${resolved}`;
|
|
@@ -373,7 +386,7 @@ function resolveCommandCacheIdentity(command, env, witness) {
|
|
|
373
386
|
return;
|
|
374
387
|
PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.add(witness, operand);
|
|
375
388
|
try {
|
|
376
|
-
identity += `;${index + 1}:sha256:${
|
|
389
|
+
identity += `;${index + 1}:sha256:${hashExecutable(operand, identities)}`;
|
|
377
390
|
}
|
|
378
391
|
catch {
|
|
379
392
|
identity += `;${index + 1}:unreadable:${operand}`;
|
|
@@ -426,14 +439,21 @@ function hashExternalGoBuildEnvironment(hash, env) {
|
|
|
426
439
|
// GOROOT is usually stable but its selected source/tool payloads contribute to
|
|
427
440
|
// every plugin key. Retain only the final aggregate
|
|
428
441
|
// identity, guarded by a fresh metadata/topology manifest on every call. A
|
|
429
|
-
// changed or incomplete manifest falls through to
|
|
442
|
+
// changed or incomplete manifest falls through to the record store, then to a
|
|
443
|
+
// full content read.
|
|
444
|
+
//
|
|
445
|
+
// WARNING (#1186, #1722): this map lives only as long as the process. #1186
|
|
446
|
+
// stopped at it, so every CLI launch, worker and bundler restart still read the
|
|
447
|
+
// whole SDK (3,723 files, 135 MB for the bundled toolchain). The record store
|
|
448
|
+
// below is what carries the identity across processes; never drop it in favor
|
|
449
|
+
// of this memo alone.
|
|
430
450
|
const goRootIdentityCache = new Map();
|
|
431
|
-
function resolveGoRootCacheIdentity(goRoot, filesystem, witness) {
|
|
451
|
+
function resolveGoRootCacheIdentity(goRoot, filesystem, witness, identities) {
|
|
432
452
|
const resolved = resolveRealPath(goRoot);
|
|
433
453
|
if (!node_fs_1.default.existsSync(resolved)) {
|
|
434
454
|
return `missing:${goRoot}`;
|
|
435
455
|
}
|
|
436
|
-
const snapshot = collectGoRootIdentitySnapshot(resolved, witness);
|
|
456
|
+
const snapshot = collectGoRootIdentitySnapshot(resolved, witness, identities?.references);
|
|
437
457
|
if (!snapshot.complete)
|
|
438
458
|
PluginBuildEnvironmentWitness_1.PluginBuildEnvironmentWitness.refuse(witness, resolved);
|
|
439
459
|
if (snapshot.complete) {
|
|
@@ -442,15 +462,30 @@ function resolveGoRootCacheIdentity(goRoot, filesystem, witness) {
|
|
|
442
462
|
return cached.identity;
|
|
443
463
|
}
|
|
444
464
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
465
|
+
// The first observation is the snapshot the witness just recorded; a second
|
|
466
|
+
// one, after the content read, brackets that read for publication.
|
|
467
|
+
let observed = 0;
|
|
468
|
+
const identity = PluginContentIdentities_1.PluginContentIdentities.digest(snapshot.complete ? identities : undefined, "goroot", resolved, (references) => {
|
|
469
|
+
const current = observed++ === 0
|
|
470
|
+
? snapshot
|
|
471
|
+
: collectGoRootIdentitySnapshot(resolved, undefined, references);
|
|
472
|
+
return current.complete
|
|
473
|
+
? { separable: current.separable, signature: current.signature }
|
|
474
|
+
: undefined;
|
|
475
|
+
}, () => {
|
|
476
|
+
const hash = node_crypto_1.default.createHash("sha256");
|
|
477
|
+
hash.update(JSON.stringify(snapshot.topology));
|
|
478
|
+
for (const file of snapshot.files) {
|
|
479
|
+
const relative = node_path_1.default
|
|
480
|
+
.relative(resolved, file)
|
|
481
|
+
.split(node_path_1.default.sep)
|
|
482
|
+
.join("/");
|
|
483
|
+
const contents = filesystem.readFile(file);
|
|
484
|
+
hash.update(JSON.stringify([relative, contents.length]));
|
|
485
|
+
hash.update(contents);
|
|
486
|
+
}
|
|
487
|
+
return `sha256:${hash.digest("hex")}`;
|
|
488
|
+
});
|
|
454
489
|
if (snapshot.complete) {
|
|
455
490
|
goRootIdentityCache.set(resolved, {
|
|
456
491
|
identity,
|
|
@@ -459,9 +494,9 @@ function resolveGoRootCacheIdentity(goRoot, filesystem, witness) {
|
|
|
459
494
|
}
|
|
460
495
|
return identity;
|
|
461
496
|
}
|
|
462
|
-
function collectGoRootIdentitySnapshot(root, witness) {
|
|
497
|
+
function collectGoRootIdentitySnapshot(root, witness, references) {
|
|
463
498
|
const out = [];
|
|
464
|
-
const state = { complete: true };
|
|
499
|
+
const state = { complete: true, separable: references !== undefined };
|
|
465
500
|
const topology = [];
|
|
466
501
|
walkGoRootIdentity(root, out, topology, state, witness);
|
|
467
502
|
out.sort();
|
|
@@ -476,6 +511,9 @@ function collectGoRootIdentitySnapshot(root, witness) {
|
|
|
476
511
|
state.complete = false;
|
|
477
512
|
continue;
|
|
478
513
|
}
|
|
514
|
+
if (references === undefined ||
|
|
515
|
+
!PluginContentIdentities_1.PluginContentIdentities.separable(references, stats))
|
|
516
|
+
state.separable = false;
|
|
479
517
|
signature.update([
|
|
480
518
|
relative,
|
|
481
519
|
stats.dev,
|
|
@@ -496,6 +534,7 @@ function collectGoRootIdentitySnapshot(root, witness) {
|
|
|
496
534
|
files: out,
|
|
497
535
|
topology,
|
|
498
536
|
signature: signature.digest("hex"),
|
|
537
|
+
separable: state.separable,
|
|
499
538
|
};
|
|
500
539
|
}
|
|
501
540
|
function walkGoRootIdentity(root, out, topology, state, witness) {
|