ttsc 0.32.1 → 0.33.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/cmd/ttsc-cache/account.go +94 -0
- package/cmd/ttsc-cache/interruption_test.go +176 -0
- package/cmd/ttsc-cache/main.go +252 -0
- package/cmd/ttsc-cache/merge.go +324 -0
- package/cmd/ttsc-cache/merge_test.go +161 -0
- package/cmd/ttsc-cache/prepare.go +330 -0
- package/cmd/ttsc-cache/prepare_test.go +456 -0
- package/cmd/ttsc-cache/protocol_test.go +119 -0
- package/cmd/ttsc-cache/publisher_seal_test.go +135 -0
- package/cmd/ttsc-cache/seal.go +427 -0
- package/cmd/ttsc-cache/stock_go_failure_test.go +94 -0
- package/cmd/ttsc-cache/stock_go_test.go +163 -0
- package/cmd/ttsc-cache/store.go +249 -0
- package/cmd/ttsc-cache/store_test.go +104 -0
- package/cmd/ttsc-cache/tuple.go +107 -0
- package/cmd/ttsc-cache/tuple_test.go +62 -0
- package/cmd/ttsc-cache/usage.go +73 -0
- package/driver/configloaderscratch/canonical_directory_other.go +34 -0
- package/driver/configloaderscratch/canonical_directory_windows.go +60 -0
- package/driver/configloaderscratch/identity_temp_base.go +81 -0
- package/driver/configloaderscratch/identity_temp_base_posix_test.go +124 -0
- package/driver/configloaderscratch/identity_temp_base_windows_test.go +156 -0
- package/driver/emit_provenance.go +7 -5
- package/driver/resolutioninputs/recorder.cjs +16 -4
- package/driver/rewrite.go +25 -16
- package/driver/write_file_if_changed.go +69 -0
- package/driver/write_file_if_changed_test.go +148 -0
- package/internal/packageartifacts/manifest.go +288 -0
- package/internal/packageartifacts/manifest_test.go +127 -0
- package/lib/compiler/internal/build/BuildExecution.d.ts +11 -7
- package/lib/compiler/internal/build/BuildExecution.js +22 -8
- package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
- package/lib/compiler/internal/build/ResidentCheckWatchSession.d.ts +10 -10
- package/lib/compiler/internal/build/ResidentCheckWatchSession.js +19 -14
- package/lib/compiler/internal/build/ResidentCheckWatchSession.js.map +1 -1
- package/lib/compiler/internal/build/RunBuildOptions.d.ts +8 -1
- package/lib/compiler/internal/build/TsgoArguments.d.ts +16 -0
- package/lib/compiler/internal/build/TsgoArguments.js +19 -0
- package/lib/compiler/internal/build/TsgoArguments.js.map +1 -1
- package/lib/compiler/internal/captureProcessOutput.d.ts +16 -13
- package/lib/compiler/internal/captureProcessOutput.js +22 -14
- package/lib/compiler/internal/captureProcessOutput.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +3 -1
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +5 -1
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/launcher/internal/runtime/PluginDescriptorInputReport.d.ts +43 -0
- package/lib/launcher/internal/runtime/PluginDescriptorInputReport.js +106 -0
- package/lib/launcher/internal/runtime/PluginDescriptorInputReport.js.map +1 -0
- package/lib/launcher/internal/runtime/installRuntimeHooks.js +15 -29
- package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
- package/lib/launcher/internal/ttscserver/runTtscserver.js +1 -0
- package/lib/launcher/internal/ttscserver/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/watch/WatchTopology.js +5 -4
- package/lib/launcher/internal/watch/WatchTopology.js.map +1 -1
- package/lib/plugin/internal/CapabilityResolutionFormat.js +4 -3
- package/lib/plugin/internal/CapabilityResolutionFormat.js.map +1 -1
- package/lib/plugin/internal/load/COMMONJS_PLUGIN_DESCRIPTOR_SHIM_SOURCE.js +6 -0
- package/lib/plugin/internal/load/COMMONJS_PLUGIN_DESCRIPTOR_SHIM_SOURCE.js.map +1 -1
- package/lib/plugin/internal/load/PLUGIN_DESCRIPTOR_SHIM_SOURCE.js +6 -0
- package/lib/plugin/internal/load/PLUGIN_DESCRIPTOR_SHIM_SOURCE.js.map +1 -1
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +3 -3
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
- package/lib/plugin/internal/load/descriptorRecordEnvironment.d.ts +15 -0
- package/lib/plugin/internal/load/descriptorRecordEnvironment.js +24 -0
- package/lib/plugin/internal/load/descriptorRecordEnvironment.js.map +1 -0
- package/lib/plugin/internal/load/loadProjectPlugins.js +28 -26
- package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
- package/lib/plugin/internal/source/SourceBuildCacheLayout.d.ts +2 -0
- package/lib/plugin/internal/source/SourceBuildCacheLayout.js +2 -0
- package/lib/plugin/internal/source/SourceBuildCacheLayout.js.map +1 -1
- package/lib/plugin/internal/source/SourcePackageArtifacts.d.ts +145 -0
- package/lib/plugin/internal/source/SourcePackageArtifacts.js +612 -0
- package/lib/plugin/internal/source/SourcePackageArtifacts.js.map +1 -0
- package/lib/plugin/internal/source/buildSourcePlugin.js +54 -4
- package/lib/plugin/internal/source/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/source/prunePackageArtifactRoot.d.ts +19 -0
- package/lib/plugin/internal/source/prunePackageArtifactRoot.js +95 -0
- package/lib/plugin/internal/source/prunePackageArtifactRoot.js.map +1 -0
- package/lib/plugin/internal/source/resolveCleanTargets.js +1 -1
- package/lib/plugin/internal/source/resolveCleanTargets.js.map +1 -1
- package/lib/plugin/internal/source/resolveSourceBuildCachePaths.js +9 -4
- package/lib/plugin/internal/source/resolveSourceBuildCachePaths.js.map +1 -1
- package/lib/plugin/internal/source/selectedRecordIdentityTempParent.d.ts +18 -0
- package/lib/plugin/internal/source/selectedRecordIdentityTempParent.js +46 -0
- package/lib/plugin/internal/source/selectedRecordIdentityTempParent.js.map +1 -0
- package/package.json +8 -8
- package/src/compiler/internal/build/BuildExecution.ts +30 -10
- package/src/compiler/internal/build/ResidentCheckWatchSession.ts +23 -22
- package/src/compiler/internal/build/RunBuildOptions.ts +9 -1
- package/src/compiler/internal/build/TsgoArguments.ts +22 -0
- package/src/compiler/internal/captureProcessOutput.ts +28 -14
- package/src/compiler/internal/startResidentTransform.ts +7 -1
- package/src/compiler/internal/transformProjectInMemory.ts +9 -1
- package/src/launcher/internal/runtime/PluginDescriptorInputReport.ts +100 -0
- package/src/launcher/internal/runtime/installRuntimeHooks.ts +57 -55
- package/src/launcher/internal/ttscserver/runTtscserver.ts +5 -0
- package/src/launcher/internal/watch/WatchTopology.ts +5 -4
- package/src/plugin/internal/CapabilityResolutionFormat.ts +4 -3
- package/src/plugin/internal/load/COMMONJS_PLUGIN_DESCRIPTOR_SHIM_SOURCE.ts +4 -0
- package/src/plugin/internal/load/PLUGIN_DESCRIPTOR_SHIM_SOURCE.ts +4 -0
- package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +3 -3
- package/src/plugin/internal/load/descriptorRecordEnvironment.ts +24 -0
- package/src/plugin/internal/load/loadProjectPlugins.ts +45 -29
- package/src/plugin/internal/source/SourceBuildCacheLayout.ts +3 -0
- package/src/plugin/internal/source/SourcePackageArtifacts.ts +787 -0
- package/src/plugin/internal/source/buildSourcePlugin.ts +82 -12
- package/src/plugin/internal/source/prunePackageArtifactRoot.ts +100 -0
- package/src/plugin/internal/source/resolveCleanTargets.ts +1 -0
- package/src/plugin/internal/source/resolveSourceBuildCachePaths.ts +11 -4
- package/src/plugin/internal/source/selectedRecordIdentityTempParent.ts +40 -0
|
@@ -21,6 +21,7 @@ import { PluginBuildLockProtocol } from "./PluginBuildLockProtocol";
|
|
|
21
21
|
import { PluginContentIdentities } from "./PluginContentIdentities";
|
|
22
22
|
import { SourceBuildCacheLayout } from "./SourceBuildCacheLayout";
|
|
23
23
|
import type { SourceBuildFilesystemOperations } from "./SourceBuildFilesystemOperations";
|
|
24
|
+
import { SourcePackageArtifacts } from "./SourcePackageArtifacts";
|
|
24
25
|
import { SourcePluginAdmission } from "./SourcePluginAdmission";
|
|
25
26
|
import { SourcePluginWorkspace } from "./SourcePluginWorkspace";
|
|
26
27
|
import { acquirePluginBuildLock } from "./acquirePluginBuildLock";
|
|
@@ -31,6 +32,7 @@ import { ensureExecutableGoToolchain } from "./ensureExecutableGoToolchain";
|
|
|
31
32
|
import { pluginModuleReplaceDirectories } from "./pluginModuleReplaceDirectories";
|
|
32
33
|
import { pluginSourceDigest } from "./pluginSourceDigest";
|
|
33
34
|
import { pruneGoBuildCacheRoot } from "./pruneGoBuildCacheRoot";
|
|
35
|
+
import { prunePackageArtifactRoot } from "./prunePackageArtifactRoot";
|
|
34
36
|
import { prunePluginCacheRoot } from "./prunePluginCacheRoot";
|
|
35
37
|
import { reclaimPluginBuildLock } from "./reclaimPluginBuildLock";
|
|
36
38
|
import { releasePluginBuildLock } from "./releasePluginBuildLock";
|
|
@@ -383,6 +385,11 @@ function buildSourcePluginAttempt(
|
|
|
383
385
|
pluginName: opts.pluginName,
|
|
384
386
|
quiet,
|
|
385
387
|
source,
|
|
388
|
+
artifactRoot: path.join(
|
|
389
|
+
paths.root,
|
|
390
|
+
SourceBuildCacheLayout.PACKAGE_ARTIFACTS_DIRNAME,
|
|
391
|
+
),
|
|
392
|
+
ttscVersion: opts.ttscVersion,
|
|
386
393
|
});
|
|
387
394
|
},
|
|
388
395
|
);
|
|
@@ -447,6 +454,7 @@ const CONTRIB_DIRNAME = "contrib";
|
|
|
447
454
|
|
|
448
455
|
/** Run the actual `go build` and publish the binary; assumes the lock is held. */
|
|
449
456
|
function compileSourcePlugin(opts: {
|
|
457
|
+
artifactRoot: string;
|
|
450
458
|
binaryPath: string;
|
|
451
459
|
cacheDir: string;
|
|
452
460
|
contributors: readonly ITtscBuildContributor[];
|
|
@@ -476,6 +484,7 @@ function compileSourcePlugin(opts: {
|
|
|
476
484
|
pluginName: string;
|
|
477
485
|
quiet: boolean;
|
|
478
486
|
source: string;
|
|
487
|
+
ttscVersion: string;
|
|
479
488
|
}): string {
|
|
480
489
|
OwnedSynchronousProcess.checkpoint();
|
|
481
490
|
if (!opts.quiet) {
|
|
@@ -574,6 +583,8 @@ function compileSourcePlugin(opts: {
|
|
|
574
583
|
const scratchBinaryName =
|
|
575
584
|
process.platform === "win32" ? ".ttsc-plugin.exe" : ".ttsc-plugin";
|
|
576
585
|
let attemptedGoBuildCacheRoot: string | undefined;
|
|
586
|
+
let publisherRecording: ReturnType<typeof SourcePackageArtifacts.record> =
|
|
587
|
+
null;
|
|
577
588
|
try {
|
|
578
589
|
withGoBuildCacheLease(
|
|
579
590
|
opts.goBuildCacheRoot,
|
|
@@ -616,16 +627,57 @@ function compileSourcePlugin(opts: {
|
|
|
616
627
|
);
|
|
617
628
|
});
|
|
618
629
|
}
|
|
619
|
-
|
|
620
|
-
scratchDir,
|
|
621
|
-
opts.entry,
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
opts.
|
|
628
|
-
|
|
630
|
+
const recording = SourcePackageArtifacts.record({
|
|
631
|
+
cwd: scratchDir,
|
|
632
|
+
entry: opts.entry,
|
|
633
|
+
env: GoSourceInputs.goBuildEnv(
|
|
634
|
+
opts.goBinary,
|
|
635
|
+
goBuildCacheRoot,
|
|
636
|
+
opts.env,
|
|
637
|
+
),
|
|
638
|
+
goBinary: opts.goBinary,
|
|
639
|
+
key: opts.key,
|
|
640
|
+
pluginName: opts.pluginName,
|
|
641
|
+
ttscVersion: opts.ttscVersion,
|
|
642
|
+
});
|
|
643
|
+
publisherRecording = recording;
|
|
644
|
+
const artifacts =
|
|
645
|
+
recording === null
|
|
646
|
+
? SourcePackageArtifacts.prepare({
|
|
647
|
+
artifactRoot: opts.artifactRoot,
|
|
648
|
+
cwd: scratchDir,
|
|
649
|
+
entry: opts.entry,
|
|
650
|
+
env: GoSourceInputs.goBuildEnv(
|
|
651
|
+
opts.goBinary,
|
|
652
|
+
goBuildCacheRoot,
|
|
653
|
+
opts.env,
|
|
654
|
+
),
|
|
655
|
+
goBinary: opts.goBinary,
|
|
656
|
+
pluginName: opts.pluginName,
|
|
657
|
+
quiet: opts.quiet,
|
|
658
|
+
ttscVersion: opts.ttscVersion,
|
|
659
|
+
})
|
|
660
|
+
: null;
|
|
661
|
+
try {
|
|
662
|
+
runGoBuild(
|
|
663
|
+
scratchDir,
|
|
664
|
+
opts.entry,
|
|
665
|
+
scratchBinaryName,
|
|
666
|
+
opts.pluginName,
|
|
667
|
+
opts.goBinary,
|
|
668
|
+
goBuildCacheRoot,
|
|
669
|
+
recording?.env ?? artifacts!.env,
|
|
670
|
+
opts.normalizeGoToolPermissions,
|
|
671
|
+
recording?.actionGraph,
|
|
672
|
+
);
|
|
673
|
+
} finally {
|
|
674
|
+
if (artifacts !== null)
|
|
675
|
+
SourcePackageArtifacts.reportUsage(
|
|
676
|
+
artifacts,
|
|
677
|
+
opts.pluginName,
|
|
678
|
+
opts.quiet,
|
|
679
|
+
);
|
|
680
|
+
}
|
|
629
681
|
OwnedSynchronousProcess.checkpoint();
|
|
630
682
|
if (process.env.TTSC_E2E_TRACE) {
|
|
631
683
|
E2ETrace.capabilityResolution(
|
|
@@ -655,6 +707,7 @@ function compileSourcePlugin(opts: {
|
|
|
655
707
|
// grow the cache unchecked behind a fresh daily marker. Cancellation
|
|
656
708
|
// still releases the lease and scratch, but admits no new maintenance.
|
|
657
709
|
pruneGoBuildCacheRoot(attemptedGoBuildCacheRoot, { force: true });
|
|
710
|
+
prunePackageArtifactRoot(attemptedGoBuildCacheRoot, opts.artifactRoot);
|
|
658
711
|
}
|
|
659
712
|
}
|
|
660
713
|
// The toolchain was read for the key before the lock wait and the build,
|
|
@@ -685,6 +738,8 @@ function compileSourcePlugin(opts: {
|
|
|
685
738
|
);
|
|
686
739
|
throw pluginBuildEnvironmentChanged(opts.pluginName);
|
|
687
740
|
}
|
|
741
|
+
if (publisherRecording !== null)
|
|
742
|
+
SourcePackageArtifacts.seal(publisherRecording);
|
|
688
743
|
const builtBinary = path.join(scratchDir, scratchBinaryName);
|
|
689
744
|
OwnedSynchronousProcess.checkpoint();
|
|
690
745
|
publishBuiltBinary(builtBinary, opts.binaryPath);
|
|
@@ -697,7 +752,14 @@ function compileSourcePlugin(opts: {
|
|
|
697
752
|
SourceNativeRetirement.releaseResource(
|
|
698
753
|
scratchDir,
|
|
699
754
|
() => {
|
|
700
|
-
|
|
755
|
+
// Retry transient Windows removal refusals only after native closure
|
|
756
|
+
// has authorized this resource callback.
|
|
757
|
+
fs.rmSync(scratchDir, {
|
|
758
|
+
recursive: true,
|
|
759
|
+
force: true,
|
|
760
|
+
maxRetries: 10,
|
|
761
|
+
retryDelay: 100,
|
|
762
|
+
});
|
|
701
763
|
SourceNativeRetirement.forget(opts.cacheDir);
|
|
702
764
|
},
|
|
703
765
|
taskFailure,
|
|
@@ -1194,12 +1256,20 @@ function runGoBuild(
|
|
|
1194
1256
|
goBuildCacheRoot: string,
|
|
1195
1257
|
env: NodeJS.ProcessEnv,
|
|
1196
1258
|
normalizeGoToolPermissions: boolean,
|
|
1259
|
+
actionGraph?: string,
|
|
1197
1260
|
): void {
|
|
1198
1261
|
OwnedSynchronousProcess.checkpoint();
|
|
1199
1262
|
ensureExecutableGoToolchain(goBinary, normalizeGoToolPermissions);
|
|
1200
1263
|
const result = spawnGoTool(
|
|
1201
1264
|
goBinary,
|
|
1202
|
-
[
|
|
1265
|
+
[
|
|
1266
|
+
"build",
|
|
1267
|
+
...GoSourceInputs.BUILD_FLAGS,
|
|
1268
|
+
...(actionGraph ? [`-debug-actiongraph=${actionGraph}`] : []),
|
|
1269
|
+
"-o",
|
|
1270
|
+
binaryName,
|
|
1271
|
+
entry,
|
|
1272
|
+
],
|
|
1203
1273
|
{
|
|
1204
1274
|
cwd,
|
|
1205
1275
|
encoding: "utf8",
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { SourceNativeRetirement } from "../../../internal/SourceNativeRetirement";
|
|
5
|
+
import { GoBuildCacheCoordination } from "./GoBuildCacheCoordination";
|
|
6
|
+
import { SourceBuildCacheLayout } from "./SourceBuildCacheLayout";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reclaim a default-owned package artifact population after size or idle age.
|
|
10
|
+
*
|
|
11
|
+
* The entire base and overlay share an eviction generation. A Go-cache
|
|
12
|
+
* maintenance intent excludes cooperating builders while the native guard
|
|
13
|
+
* reader retains unknown process-tree outcomes. Explicit cache roots are never
|
|
14
|
+
* passed here. Metadata accounting is bounded and cannot establish a hard disk
|
|
15
|
+
* ceiling while another process writes or a protected generation remains.
|
|
16
|
+
*
|
|
17
|
+
* @evidence contracts/common.md#principled-implementation The same maintenance and native-guard gates used by the owning Go cache precede deletion of the sibling artifact population.
|
|
18
|
+
* @evidence contracts/common.md#clear-and-simple-design One idle/size decision reclaims a complete base-and-overlay generation, leaving future builds to prepare a fresh root.
|
|
19
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Unknown files, aliases and unresolved native owners refuse reclamation rather than being silently ignored.
|
|
20
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states whole-generation eviction and its concurrency and accounting limits.
|
|
21
|
+
* @evidence contracts/portability.md#os-neutral-implementation Native path, directory and metadata APIs inspect ordinary entries on Windows and POSIX.
|
|
22
|
+
* @evidence contracts/performance.md#efficient-algorithms A bounded metadata walk visits at most 100000 entries and six directory levels; it reads no body bytes.
|
|
23
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work This routine decides deletion from mutable ownership and filesystem state, not reusable compilation identity.
|
|
24
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Sixteen GiB or 30 idle days triggers best-effort generation removal; active/unknown owners, locked files and errors can defer reclamation without a hard ceiling.
|
|
25
|
+
*/
|
|
26
|
+
export function prunePackageArtifactRoot(
|
|
27
|
+
goBuildRoot: string,
|
|
28
|
+
artifactRoot: string,
|
|
29
|
+
now: number = Date.now(),
|
|
30
|
+
): void {
|
|
31
|
+
let intent:
|
|
32
|
+
| GoBuildCacheCoordination.GoBuildCacheCoordinationRecord
|
|
33
|
+
| undefined;
|
|
34
|
+
try {
|
|
35
|
+
const cacheRoot =
|
|
36
|
+
GoBuildCacheCoordination.canonicalGoBuildCacheRoot(goBuildRoot);
|
|
37
|
+
const parent = fs.realpathSync.native(path.dirname(artifactRoot));
|
|
38
|
+
if (
|
|
39
|
+
parent !== path.dirname(cacheRoot) ||
|
|
40
|
+
path.basename(artifactRoot) !==
|
|
41
|
+
SourceBuildCacheLayout.PACKAGE_ARTIFACTS_DIRNAME
|
|
42
|
+
)
|
|
43
|
+
return;
|
|
44
|
+
const info = fs.lstatSync(artifactRoot);
|
|
45
|
+
if (!info.isDirectory() || info.isSymbolicLink()) return;
|
|
46
|
+
const physical = fs.realpathSync.native(artifactRoot);
|
|
47
|
+
if (path.dirname(physical) !== parent) return;
|
|
48
|
+
if (SourceNativeRetirement.isProtected(physical)) return;
|
|
49
|
+
|
|
50
|
+
intent = GoBuildCacheCoordination.createGoBuildCacheCoordinationRecord(
|
|
51
|
+
cacheRoot,
|
|
52
|
+
GoBuildCacheCoordination.GO_BUILD_CACHE_MAINTENANCE_DIR,
|
|
53
|
+
);
|
|
54
|
+
if (!intent.startHeartbeat()) return;
|
|
55
|
+
if (
|
|
56
|
+
GoBuildCacheCoordination.collectLiveGoBuildCacheCoordinationRecords(
|
|
57
|
+
cacheRoot,
|
|
58
|
+
GoBuildCacheCoordination.GO_BUILD_CACHE_LEASE_DIR,
|
|
59
|
+
now,
|
|
60
|
+
).length !== 0 ||
|
|
61
|
+
SourceNativeRetirement.isProtected(physical)
|
|
62
|
+
)
|
|
63
|
+
return;
|
|
64
|
+
|
|
65
|
+
const marker = path.join(physical, ".last-used");
|
|
66
|
+
const lastUsed = SourceBuildCacheLayout.readTimestamp(marker);
|
|
67
|
+
const expired = lastUsed !== null && now - lastUsed > 30 * DAY_MS;
|
|
68
|
+
const bytes = countArtifactBytes(physical);
|
|
69
|
+
if (expired || bytes > 16 * GIB) fs.rmSync(physical, { recursive: true });
|
|
70
|
+
} catch {
|
|
71
|
+
// A failed ownership observation or locked Windows body defers collection.
|
|
72
|
+
} finally {
|
|
73
|
+
intent?.finish();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
78
|
+
const GIB = 1024 * 1024 * 1024;
|
|
79
|
+
|
|
80
|
+
function countArtifactBytes(root: string): number {
|
|
81
|
+
let entries = 0;
|
|
82
|
+
let total = 0;
|
|
83
|
+
const walk = (directory: string, depth: number): void => {
|
|
84
|
+
if (depth > 6) throw new Error("artifact directory depth exceeds policy");
|
|
85
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
86
|
+
if (++entries > 100000)
|
|
87
|
+
throw new Error("artifact entry count exceeds policy");
|
|
88
|
+
const child = path.join(directory, entry.name);
|
|
89
|
+
const info = fs.lstatSync(child);
|
|
90
|
+
if (info.isSymbolicLink()) throw new Error("aliased artifact entry");
|
|
91
|
+
if (info.isDirectory()) walk(child, depth + 1);
|
|
92
|
+
else if (info.isFile()) total += info.size;
|
|
93
|
+
else throw new Error("unknown artifact entry");
|
|
94
|
+
if (!Number.isSafeInteger(total))
|
|
95
|
+
throw new Error("artifact size exceeds policy");
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
walk(root, 0);
|
|
99
|
+
return total;
|
|
100
|
+
}
|
|
@@ -55,6 +55,7 @@ export function resolveCleanTargets(
|
|
|
55
55
|
if (isTtscOwnedRoot) {
|
|
56
56
|
targets.push(
|
|
57
57
|
path.join(paths.root, SourceBuildCacheLayout.GO_BUILD_CACHE_DIRNAME),
|
|
58
|
+
path.join(paths.root, SourceBuildCacheLayout.PACKAGE_ARTIFACTS_DIRNAME),
|
|
58
59
|
);
|
|
59
60
|
}
|
|
60
61
|
// An explicit TTSC_GO_CACHE_DIR is a ttsc-dedicated external cache; a
|
|
@@ -94,8 +94,10 @@ function resolveSourceBuildCacheRoot(
|
|
|
94
94
|
* Walks up from `projectRoot` and returns, in order of preference: the NEAREST
|
|
95
95
|
* ancestor that is a workspace root (holds `pnpm-workspace.yaml`, or a
|
|
96
96
|
* `package.json` with a `workspaces` field); else the nearest ancestor that
|
|
97
|
-
* contains an installation
|
|
98
|
-
* below the nearest ordinary package manifest, or
|
|
97
|
+
* contains an installation within the package boundary; else the outermost
|
|
98
|
+
* ttsc-only cache owner at or below the nearest ordinary package manifest, or
|
|
99
|
+
* `projectRoot` itself. An unrelated ancestor's installation without its own
|
|
100
|
+
* package manifest cannot take ownership after a package boundary is crossed.
|
|
99
101
|
*
|
|
100
102
|
* A nonempty, unmarked ttsc-only `.cache/ttsc` payload is fallback cache
|
|
101
103
|
* evidence rather than an installation. Empty or marked ttsc roots remain
|
|
@@ -116,13 +118,18 @@ function resolveWorkspaceRoot(projectRoot: string): string {
|
|
|
116
118
|
if (isWorkspaceRootDir(dir)) {
|
|
117
119
|
return dir;
|
|
118
120
|
}
|
|
121
|
+
const packageHere = hasPackageManifest(dir);
|
|
119
122
|
const nodeModules = classifyNodeModulesBoundary(dir);
|
|
120
|
-
if (
|
|
123
|
+
if (
|
|
124
|
+
nearestNodeModulesOwner === null &&
|
|
125
|
+
nodeModules === "installation" &&
|
|
126
|
+
(!packageBoundarySeen || packageHere)
|
|
127
|
+
) {
|
|
121
128
|
nearestNodeModulesOwner = dir;
|
|
122
129
|
} else if (nodeModules === "ttsc-cache" && !packageBoundarySeen) {
|
|
123
130
|
outermostTtscCacheOwner = dir;
|
|
124
131
|
}
|
|
125
|
-
packageBoundarySeen ||=
|
|
132
|
+
packageBoundarySeen ||= packageHere;
|
|
126
133
|
const parent = path.dirname(dir);
|
|
127
134
|
if (parent === dir) {
|
|
128
135
|
break;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return an existing ordinary identities directory below a selected record
|
|
6
|
+
* root, or leave temporary placement to the caller's system default.
|
|
7
|
+
*
|
|
8
|
+
* The returned physical path is a direct child of the selected root. The caller
|
|
9
|
+
* still uses createCanonicalTempDirectory to verify its own allocation; these
|
|
10
|
+
* observations do not hold the namespace against later mutation.
|
|
11
|
+
*
|
|
12
|
+
* @evidence contracts/common.md#principled-implementation Native realpath and link-preserving stat admit only a present ordinary identities directory whose physical parent is the selected root; an unproved path supplies no alternate temporary parent.
|
|
13
|
+
* @evidence contracts/common.md#clear-and-simple-design One small selector shares the same record-root placement rule between descriptor evaluation and child output capture; allocation and cleanup remain with their respective owners.
|
|
14
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The function neither creates a cache root nor accepts a linked identities entry as evidence of an owned child; failed observations retain the ordinary caller path.
|
|
15
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states the selected-root premise, returned physical path and remaining namespace race without claiming held-directory protection.
|
|
16
|
+
* @evidence contracts/portability.md#os-neutral-implementation Node native path and filesystem APIs observe the actual root and child without assuming case policy or a fixed temporary volume; a junctioned selected root can resolve to its physical directory, while the identities entry itself must be ordinary.
|
|
17
|
+
* @evidence contracts/performance.md#efficient-algorithms A fixed number of native path observations and path-text operations selects the parent without enumeration; lookup depth and path length determine their underlying cost.
|
|
18
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work A record root or identities entry can be replaced between calls, so each allocation needs current observations instead of a remembered parent.
|
|
19
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The selector acquires no handle or directory and retains no state; its returned path is caller-owned text.
|
|
20
|
+
*/
|
|
21
|
+
export function selectedRecordIdentityTempParent(
|
|
22
|
+
recordRoot: string | undefined,
|
|
23
|
+
): string | undefined {
|
|
24
|
+
if (recordRoot === undefined || !path.isAbsolute(recordRoot))
|
|
25
|
+
return undefined;
|
|
26
|
+
try {
|
|
27
|
+
const physicalRoot = fs.realpathSync.native(recordRoot);
|
|
28
|
+
if (!fs.lstatSync(physicalRoot).isDirectory()) return undefined;
|
|
29
|
+
const identities = path.join(recordRoot, "identities");
|
|
30
|
+
const identityStats = fs.lstatSync(identities);
|
|
31
|
+
if (!identityStats.isDirectory() || identityStats.isSymbolicLink())
|
|
32
|
+
return undefined;
|
|
33
|
+
const physicalIdentities = fs.realpathSync.native(identities);
|
|
34
|
+
return path.dirname(physicalIdentities) === physicalRoot
|
|
35
|
+
? physicalIdentities
|
|
36
|
+
: undefined;
|
|
37
|
+
} catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|