tanmi-dock 1.0.5 → 1.0.6-beta2
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/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +121 -22
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/clean.d.ts +3 -0
- package/dist/commands/clean.d.ts.map +1 -1
- package/dist/commands/clean.js +11 -17
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +28 -10
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/link.d.ts +21 -0
- package/dist/commands/link.d.ts.map +1 -1
- package/dist/commands/link.js +739 -344
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/status.js +3 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/unavailable.d.ts.map +1 -1
- package/dist/commands/unavailable.js +4 -3
- package/dist/commands/unavailable.js.map +1 -1
- package/dist/core/codepac-cache.d.ts +22 -0
- package/dist/core/codepac-cache.d.ts.map +1 -0
- package/dist/core/codepac-cache.js +122 -0
- package/dist/core/codepac-cache.js.map +1 -0
- package/dist/core/linker.d.ts +1 -1
- package/dist/core/linker.d.ts.map +1 -1
- package/dist/core/linker.js +93 -39
- package/dist/core/linker.js.map +1 -1
- package/dist/core/registry.d.ts +27 -2
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +278 -23
- package/dist/core/registry.js.map +1 -1
- package/dist/core/space/cleanup-executor.d.ts +1 -0
- package/dist/core/space/cleanup-executor.d.ts.map +1 -1
- package/dist/core/space/cleanup-executor.js +43 -3
- package/dist/core/space/cleanup-executor.js.map +1 -1
- package/dist/core/space/cleanup-planner.d.ts.map +1 -1
- package/dist/core/space/cleanup-planner.js +62 -19
- package/dist/core/space/cleanup-planner.js.map +1 -1
- package/dist/core/space/cleanup-selection.d.ts.map +1 -1
- package/dist/core/space/cleanup-selection.js +2 -2
- package/dist/core/space/cleanup-selection.js.map +1 -1
- package/dist/core/space/inventory.d.ts +11 -0
- package/dist/core/space/inventory.d.ts.map +1 -1
- package/dist/core/space/inventory.js +135 -9
- package/dist/core/space/inventory.js.map +1 -1
- package/dist/core/space/meter.d.ts.map +1 -1
- package/dist/core/space/meter.js +9 -5
- package/dist/core/space/meter.js.map +1 -1
- package/dist/core/space/service.d.ts +1 -0
- package/dist/core/space/service.d.ts.map +1 -1
- package/dist/core/space/service.js +4 -1
- package/dist/core/space/service.js.map +1 -1
- package/dist/core/space/types.d.ts +2 -1
- package/dist/core/space/types.d.ts.map +1 -1
- package/dist/core/space/types.js.map +1 -1
- package/dist/core/store-import-journal.d.ts +58 -0
- package/dist/core/store-import-journal.d.ts.map +1 -0
- package/dist/core/store-import-journal.js +304 -0
- package/dist/core/store-import-journal.js.map +1 -0
- package/dist/core/store-import.d.ts +52 -0
- package/dist/core/store-import.d.ts.map +1 -0
- package/dist/core/store-import.js +770 -0
- package/dist/core/store-import.js.map +1 -0
- package/dist/core/store-write-protection.d.ts +2 -0
- package/dist/core/store-write-protection.d.ts.map +1 -1
- package/dist/core/store-write-protection.js +6 -5
- package/dist/core/store-write-protection.js.map +1 -1
- package/dist/core/transaction.d.ts +39 -6
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/core/transaction.js +536 -73
- package/dist/core/transaction.js.map +1 -1
- package/dist/index.js +2 -19
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/fs-utils.d.ts +2 -0
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +45 -13
- package/dist/utils/fs-utils.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/link.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import fs from 'fs/promises';
|
|
6
|
-
import crypto from 'crypto';
|
|
7
6
|
import { Command } from 'commander';
|
|
8
7
|
import { ensureInitialized } from '../core/guard.js';
|
|
9
8
|
import * as config from '../core/config.js';
|
|
@@ -14,10 +13,12 @@ import * as store from '../core/store.js';
|
|
|
14
13
|
import * as linker from '../core/linker.js';
|
|
15
14
|
import * as codepac from '../core/codepac.js';
|
|
16
15
|
import { setProxyConfig } from '../core/codepac.js';
|
|
17
|
-
import { getPlatformHelpText, GENERAL_PLATFORM, SHARED_PLATFORM, pathsEqual, isSparseOnlyCommon, KNOWN_PLATFORM_VALUES, getRequestedPlatformTargets, resolveSparseConfig, getBaseKeyForCodepac, } from '../core/platform.js';
|
|
16
|
+
import { getPlatformHelpText, GENERAL_PLATFORM, SHARED_PLATFORM, pathsEqual, isSparseOnlyCommon, KNOWN_PLATFORM_VALUES, getRequestedPlatformTargets, resolveSparseConfig, getBaseKeyForCodepac, getRegistryPath, } from '../core/platform.js';
|
|
18
17
|
import { Transaction } from '../core/transaction.js';
|
|
18
|
+
import { syncCodepacCache } from '../core/codepac-cache.js';
|
|
19
|
+
import { StoreImportService } from '../core/store-import.js';
|
|
19
20
|
import { formatSize, checkDiskSpace } from '../utils/disk.js';
|
|
20
|
-
import { getDirSize } from '../utils/fs-utils.js';
|
|
21
|
+
import { getDirSize, isPathMissingError } from '../utils/fs-utils.js';
|
|
21
22
|
import { ProgressTracker, DownloadMonitor, MultiBarManager } from '../utils/progress.js';
|
|
22
23
|
import { success, warn, error, info, hint, blank, separator, debug } from '../utils/logger.js';
|
|
23
24
|
import { verifyLocalCommit, findSubmoduleConfigs } from '../utils/git.js';
|
|
@@ -65,6 +66,181 @@ export function createLinkCommand() {
|
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
68
|
}
|
|
69
|
+
/** 下载产物完成导入后仅清理临时目录;清理失败不会撤销已经验证并登记的 Store。 */
|
|
70
|
+
export async function cleanupDownloadTempDir(tempDir, context) {
|
|
71
|
+
debug(`[link-download] 临时目录清理开始: stage=${context.stage}, lib=${context.libName}, commit=${context.commit}, temp=${tempDir}`);
|
|
72
|
+
try {
|
|
73
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
74
|
+
debug(`[link-download] 临时目录清理完成: stage=${context.stage}, lib=${context.libName}, commit=${context.commit}, temp=${tempDir}, output=removed`);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
warn(`[link-download] 临时目录清理失败,Store 缓存保持有效: stage=${context.stage}, lib=${context.libName}, commit=${context.commit}, temp=${tempDir}, message=${err.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function dependencyIdentityKey(dependency, localPath) {
|
|
81
|
+
return JSON.stringify([dependency.libName, dependency.commit, path.resolve(localPath)]);
|
|
82
|
+
}
|
|
83
|
+
function implicitReferenceOwner(dependency, projectRoot, localPath, scope) {
|
|
84
|
+
return {
|
|
85
|
+
libName: dependency.libName,
|
|
86
|
+
commit: dependency.commit,
|
|
87
|
+
linkedPath: path.relative(projectRoot, localPath),
|
|
88
|
+
scope,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function implicitReferenceOwnerKey(owner) {
|
|
92
|
+
return JSON.stringify([
|
|
93
|
+
owner.libName,
|
|
94
|
+
owner.commit,
|
|
95
|
+
owner.linkedPath,
|
|
96
|
+
owner.scope ?? '',
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
99
|
+
function serializeImplicitStoreReferences(references) {
|
|
100
|
+
return [...references.values()].map(({ owner, storeKeys }) => ({
|
|
101
|
+
owner,
|
|
102
|
+
storeKeys: [...storeKeys],
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
function addImplicitStoreReference(references, owner, storeKey) {
|
|
106
|
+
const ownerKey = implicitReferenceOwnerKey(owner);
|
|
107
|
+
let entry = references.get(ownerKey);
|
|
108
|
+
if (!entry) {
|
|
109
|
+
entry = { owner, storeKeys: new Set() };
|
|
110
|
+
references.set(ownerKey, entry);
|
|
111
|
+
}
|
|
112
|
+
entry.storeKeys.add(storeKey);
|
|
113
|
+
}
|
|
114
|
+
function errorCauseHasCode(errorValue, codes) {
|
|
115
|
+
let current = errorValue;
|
|
116
|
+
while (current instanceof Error) {
|
|
117
|
+
const code = current.code;
|
|
118
|
+
if (code && codes.has(code))
|
|
119
|
+
return true;
|
|
120
|
+
current = current.cause;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
async function retainExistingLinkedDependencies(project, currentDependencies, projectRoot, storePath) {
|
|
125
|
+
if (!project)
|
|
126
|
+
return [];
|
|
127
|
+
const currentOwners = new Set(currentDependencies.map((dependency) => implicitReferenceOwnerKey({
|
|
128
|
+
libName: dependency.libName,
|
|
129
|
+
commit: dependency.commit,
|
|
130
|
+
linkedPath: dependency.linkedPath,
|
|
131
|
+
scope: dependency.scope,
|
|
132
|
+
})));
|
|
133
|
+
const groups = new Map();
|
|
134
|
+
for (const dependency of project.dependencies) {
|
|
135
|
+
const ownerKey = implicitReferenceOwnerKey({
|
|
136
|
+
libName: dependency.libName,
|
|
137
|
+
commit: dependency.commit,
|
|
138
|
+
linkedPath: dependency.linkedPath,
|
|
139
|
+
scope: dependency.scope,
|
|
140
|
+
});
|
|
141
|
+
if (currentOwners.has(ownerKey))
|
|
142
|
+
continue;
|
|
143
|
+
const group = groups.get(ownerKey) ?? [];
|
|
144
|
+
group.push(dependency);
|
|
145
|
+
groups.set(ownerKey, group);
|
|
146
|
+
}
|
|
147
|
+
const retained = [];
|
|
148
|
+
for (const dependencies of groups.values()) {
|
|
149
|
+
const template = dependencies[0];
|
|
150
|
+
const localPath = path.resolve(projectRoot, template.linkedPath);
|
|
151
|
+
const storeCommitPath = path.join(storePath, template.libName, template.commit);
|
|
152
|
+
const isGeneral = dependencies.some((dependency) => dependency.platform === GENERAL_PLATFORM) &&
|
|
153
|
+
dependencies.every((dependency) => dependency.platform === GENERAL_PLATFORM);
|
|
154
|
+
try {
|
|
155
|
+
await recordLinkedDependency(retained, template, localPath, storeCommitPath, projectRoot, isGeneral, template.scope);
|
|
156
|
+
debug(`[link-registry] 保留现有真实链接: lib=${template.libName}, commit=${template.commit}, ` +
|
|
157
|
+
`local=${localPath}, source=previous-project`);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
if (errorCauseHasCode(err, new Set(['EACCES', 'EPERM', 'EIO'])))
|
|
161
|
+
throw err;
|
|
162
|
+
debug(`[link-registry] 现有依赖不再指向当前 Store,移除登记: lib=${template.libName}, ` +
|
|
163
|
+
`commit=${template.commit}, local=${localPath}, detail=${err.message}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return retained;
|
|
167
|
+
}
|
|
168
|
+
function mergeProjectImplicitStoreReferences(project, projectHash, activeDependencies, discovered, registry) {
|
|
169
|
+
const activeOwners = new Set(activeDependencies.map((dependency) => implicitReferenceOwnerKey({
|
|
170
|
+
libName: dependency.libName,
|
|
171
|
+
commit: dependency.commit,
|
|
172
|
+
linkedPath: dependency.linkedPath,
|
|
173
|
+
scope: dependency.scope,
|
|
174
|
+
})));
|
|
175
|
+
const merged = new Map();
|
|
176
|
+
const legacyStoreKeys = new Set();
|
|
177
|
+
const knownPreviousImplicitKeys = new Set((project?.implicitStoreReferences ?? []).flatMap((reference) => reference.storeKeys));
|
|
178
|
+
const previousDependencyKeys = new Set((project?.dependencies ?? []).map((dependency) => registry.getStoreKey(dependency.libName, dependency.commit, dependency.platform)));
|
|
179
|
+
if (project) {
|
|
180
|
+
for (const storeKey of registry.getProjectStoreKeys(projectHash)) {
|
|
181
|
+
if (!previousDependencyKeys.has(storeKey) && !knownPreviousImplicitKeys.has(storeKey)) {
|
|
182
|
+
legacyStoreKeys.add(storeKey);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
for (const reference of project?.implicitStoreReferences ?? []) {
|
|
187
|
+
if (!reference.owner) {
|
|
188
|
+
for (const storeKey of reference.storeKeys)
|
|
189
|
+
legacyStoreKeys.add(storeKey);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (!activeOwners.has(implicitReferenceOwnerKey(reference.owner)))
|
|
193
|
+
continue;
|
|
194
|
+
for (const storeKey of reference.storeKeys) {
|
|
195
|
+
if (registry.getStore(storeKey))
|
|
196
|
+
addImplicitStoreReference(merged, reference.owner, storeKey);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
for (const reference of discovered) {
|
|
200
|
+
if (!reference.owner || !activeOwners.has(implicitReferenceOwnerKey(reference.owner)))
|
|
201
|
+
continue;
|
|
202
|
+
for (const storeKey of reference.storeKeys) {
|
|
203
|
+
if (registry.getStore(storeKey))
|
|
204
|
+
addImplicitStoreReference(merged, reference.owner, storeKey);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const result = serializeImplicitStoreReferences(merged);
|
|
208
|
+
const validLegacyKeys = [...legacyStoreKeys].filter((storeKey) => registry.getStore(storeKey));
|
|
209
|
+
if (validLegacyKeys.length > 0)
|
|
210
|
+
result.push({ storeKeys: validLegacyKeys });
|
|
211
|
+
debug(`[link-registry] 隐式引用合并完成: project=${projectHash}, activeOwners=${activeOwners.size}, ` +
|
|
212
|
+
`discovered=${discovered.length}, previous=${project?.implicitStoreReferences?.length ?? 0}, ` +
|
|
213
|
+
`legacy=${validLegacyKeys.length}, groups=${result.length}`);
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
function buildDependencyRefs(dependency, platforms) {
|
|
217
|
+
return [...new Set(platforms)].map((platform) => ({ ...dependency, platform }));
|
|
218
|
+
}
|
|
219
|
+
function deduplicateDependencyRefs(dependencies) {
|
|
220
|
+
const seen = new Set();
|
|
221
|
+
return dependencies.filter((dependency) => {
|
|
222
|
+
const key = JSON.stringify([
|
|
223
|
+
dependency.libName,
|
|
224
|
+
dependency.commit,
|
|
225
|
+
dependency.platform,
|
|
226
|
+
dependency.linkedPath,
|
|
227
|
+
dependency.scope ?? '',
|
|
228
|
+
]);
|
|
229
|
+
if (seen.has(key))
|
|
230
|
+
return false;
|
|
231
|
+
seen.add(key);
|
|
232
|
+
return true;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/** 并行下载批次统一管理进度组件生命周期,任务创建或汇总失败时同样恢复终端状态。 */
|
|
236
|
+
export async function runParallelDownloadBatch(createTasks, manager) {
|
|
237
|
+
try {
|
|
238
|
+
return await Promise.all(createTasks());
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
manager?.stop();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
68
244
|
function resolveRequestedPlatformsByActual(requestedPlatforms, sparse, actualPlatforms, vars) {
|
|
69
245
|
const actualExisting = [...new Set(actualPlatforms)];
|
|
70
246
|
const actualPlatformSet = new Set(actualExisting);
|
|
@@ -130,8 +306,12 @@ async function prepareSharedStoreRepair(dependency, requestedPlatforms, repairRe
|
|
|
130
306
|
try {
|
|
131
307
|
await fs.access(sharedPath);
|
|
132
308
|
}
|
|
133
|
-
catch {
|
|
134
|
-
|
|
309
|
+
catch (err) {
|
|
310
|
+
if (isPathMissingError(err))
|
|
311
|
+
return [];
|
|
312
|
+
error(`[link-store] shared 目录访问失败: lib=${dependency.libName}, commit=${dependency.commit}, ` +
|
|
313
|
+
`path=${sharedPath}, code=${err.code ?? 'unknown'}, message=${err.message}`);
|
|
314
|
+
throw err;
|
|
135
315
|
}
|
|
136
316
|
const result = await verifyExistingPlatforms(dependency.libName, dependency.commit, [SHARED_PLATFORM]);
|
|
137
317
|
if (result.valid.length > 0 && result.corrupted.length === 0 && result.repairRequired.length === 0) {
|
|
@@ -167,8 +347,12 @@ async function hasRequestedPlatformContent(dependency, requestedPlatforms, vars)
|
|
|
167
347
|
}
|
|
168
348
|
}
|
|
169
349
|
}
|
|
170
|
-
catch {
|
|
171
|
-
|
|
350
|
+
catch (err) {
|
|
351
|
+
if (!isPathMissingError(err)) {
|
|
352
|
+
error(`[link-store] 请求平台目录读取失败: lib=${dependency.libName}, commit=${dependency.commit}, ` +
|
|
353
|
+
`path=${commitPath}, code=${err.code ?? 'unknown'}, message=${err.message}`);
|
|
354
|
+
throw err;
|
|
355
|
+
}
|
|
172
356
|
}
|
|
173
357
|
return false;
|
|
174
358
|
}
|
|
@@ -489,18 +673,29 @@ function buildSubmoduleOptionalConfigState(submodules) {
|
|
|
489
673
|
}
|
|
490
674
|
return Object.keys(state).length > 0 ? state : undefined;
|
|
491
675
|
}
|
|
676
|
+
async function linkPlatformDirectory(tx, localPath, storeCommitPath, platforms) {
|
|
677
|
+
await tx.preparePathReplacement(localPath, storeCommitPath);
|
|
678
|
+
await linker.linkLib(localPath, storeCommitPath, platforms);
|
|
679
|
+
}
|
|
680
|
+
async function linkGeneralDirectory(tx, localPath, storeSharedPath) {
|
|
681
|
+
await tx.preparePathReplacement(localPath, storeSharedPath);
|
|
682
|
+
await linker.linkGeneral(localPath, storeSharedPath);
|
|
683
|
+
}
|
|
492
684
|
/**
|
|
493
685
|
* 单个 scope(主项目或 submodule)的链接处理
|
|
494
686
|
*
|
|
495
687
|
* 包含:解析依赖、分类、预扫描平台、下载确认、逐个处理、并行下载、嵌套 actions
|
|
496
688
|
*/
|
|
497
689
|
async function linkScope(params) {
|
|
498
|
-
const { scopeName, configPath, platforms, scanExtraPlatforms, registry, tx,
|
|
690
|
+
const { scopeName, configPath, platforms, scanExtraPlatforms, registry, tx, projectRoot, storePath, download, dryRun, yes, concurrency, gitLightweightDownload, optionalConfigs, scope, } = params;
|
|
499
691
|
let finalLinkPlatforms = [...params.finalLinkPlatforms];
|
|
500
692
|
const codepacPlatforms = [...new Set(platforms.map((platform) => getBaseKeyForCodepac(platform)))];
|
|
501
|
-
//
|
|
502
|
-
const
|
|
693
|
+
// General 身份包含版本和项目位置,避免同名的其他依赖被误判为 General。
|
|
694
|
+
const generalDependencyKeys = new Set();
|
|
695
|
+
// 吸收过程中发现、但项目配置没有显式 dependency 的嵌套库仍需保留项目引用。
|
|
696
|
+
const implicitStoreReferences = new Map();
|
|
503
697
|
const downloadedLibs = [];
|
|
698
|
+
const linkedMissingDependencyKeys = new Set();
|
|
504
699
|
let savedBytes = 0;
|
|
505
700
|
// 1. 解析依赖
|
|
506
701
|
info(`分析 ${scopeName}: ${configPath}`);
|
|
@@ -559,7 +754,7 @@ async function linkScope(params) {
|
|
|
559
754
|
if (dryRun) {
|
|
560
755
|
showDryRunInfo(classified, stats);
|
|
561
756
|
return {
|
|
562
|
-
linkedDeps: [], nestedLinkedDeps: [],
|
|
757
|
+
linkedDeps: [], nestedLinkedDeps: [], implicitStoreReferences: [], downloadedLibs,
|
|
563
758
|
savedBytes: 0, finalLinkPlatforms, stats,
|
|
564
759
|
};
|
|
565
760
|
}
|
|
@@ -606,8 +801,12 @@ async function linkScope(params) {
|
|
|
606
801
|
.filter(e => e.isDirectory() && KNOWN_PLATFORM_VALUES.includes(e.name))
|
|
607
802
|
.forEach(e => allLocalPlatforms.add(e.name));
|
|
608
803
|
}
|
|
609
|
-
catch {
|
|
610
|
-
|
|
804
|
+
catch (err) {
|
|
805
|
+
if (isPathMissingError(err))
|
|
806
|
+
continue;
|
|
807
|
+
error(`[link] 本地平台预扫描失败: lib=${item.dependency.libName}, commit=${item.dependency.commit}, ` +
|
|
808
|
+
`path=${item.localPath}, code=${err.code ?? 'unknown'}, message=${err.message}`);
|
|
809
|
+
throw err;
|
|
611
810
|
}
|
|
612
811
|
}
|
|
613
812
|
const extraPlatforms = [...allLocalPlatforms].filter(p => !platforms.includes(p));
|
|
@@ -625,7 +824,7 @@ async function linkScope(params) {
|
|
|
625
824
|
if (selectedPlatforms === PROMPT_CANCELLED) {
|
|
626
825
|
info('已取消');
|
|
627
826
|
return {
|
|
628
|
-
linkedDeps: [], nestedLinkedDeps: [],
|
|
827
|
+
linkedDeps: [], nestedLinkedDeps: [], implicitStoreReferences: [], downloadedLibs,
|
|
629
828
|
savedBytes: 0, finalLinkPlatforms, stats,
|
|
630
829
|
};
|
|
631
830
|
}
|
|
@@ -704,6 +903,7 @@ async function linkScope(params) {
|
|
|
704
903
|
// 7. 逐个处理依赖(LINKED/RELINK/REPLACE/ABSORB/LINK_NEW)
|
|
705
904
|
for (const item of classified) {
|
|
706
905
|
const { dependency, status, localPath, repairRequiredPlatforms } = item;
|
|
906
|
+
const implicitOwner = implicitReferenceOwner(dependency, projectRoot, localPath, scope);
|
|
707
907
|
await store.ensureCompatibleStore(storePath, dependency.libName, dependency.commit);
|
|
708
908
|
switch (status) {
|
|
709
909
|
case DependencyStatus.LINKED: {
|
|
@@ -716,22 +916,21 @@ async function linkScope(params) {
|
|
|
716
916
|
useGitLightweightDownload: gitLightweightDownload,
|
|
717
917
|
repairRequiredPlatforms,
|
|
718
918
|
});
|
|
719
|
-
await registerNestedLibraries(supplementResult.nestedLibraries,
|
|
919
|
+
await registerNestedLibraries(supplementResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
720
920
|
if (supplementResult.downloaded.length > 0) {
|
|
721
921
|
const linkedCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
722
922
|
const { actualExisting: allExisting, satisfiedRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
723
923
|
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
724
|
-
await tx
|
|
725
|
-
await
|
|
726
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, allExisting, projectHash, false);
|
|
924
|
+
await linkPlatformDirectory(tx, localPath, linkedCommitPath, allExisting);
|
|
925
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, allExisting, false);
|
|
727
926
|
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已补充平台 [${supplementResult.downloaded.join(', ')}]`);
|
|
728
927
|
}
|
|
729
928
|
}
|
|
730
929
|
const { actualExisting: linkedExisting, satisfiedRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
731
930
|
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
732
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, isLinkedGeneral ? [GENERAL_PLATFORM] : linkedExisting,
|
|
931
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, isLinkedGeneral ? [GENERAL_PLATFORM] : linkedExisting, isLinkedGeneral);
|
|
733
932
|
if (isLinkedGeneral) {
|
|
734
|
-
|
|
933
|
+
generalDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
735
934
|
}
|
|
736
935
|
break;
|
|
737
936
|
}
|
|
@@ -741,13 +940,10 @@ async function linkScope(params) {
|
|
|
741
940
|
const relinkCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
742
941
|
const isRelinkGeneral = await resolveLibraryGeneralState(dependency, requestedPlatforms, configVars);
|
|
743
942
|
if (isRelinkGeneral) {
|
|
744
|
-
await tx.recordOp('unlink', localPath);
|
|
745
|
-
await linker.unlink(localPath);
|
|
746
943
|
const sharedPath = path.join(relinkCommitPath, '_shared');
|
|
747
|
-
await tx
|
|
748
|
-
await
|
|
749
|
-
|
|
750
|
-
generalLibs.add(dependency.libName);
|
|
944
|
+
await linkGeneralDirectory(tx, localPath, sharedPath);
|
|
945
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], true);
|
|
946
|
+
generalDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
751
947
|
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,重建链接`);
|
|
752
948
|
}
|
|
753
949
|
else {
|
|
@@ -756,7 +952,7 @@ async function linkScope(params) {
|
|
|
756
952
|
useGitLightweightDownload: gitLightweightDownload,
|
|
757
953
|
repairRequiredPlatforms,
|
|
758
954
|
});
|
|
759
|
-
await registerNestedLibraries(relinkSupplementResult.nestedLibraries,
|
|
955
|
+
await registerNestedLibraries(relinkSupplementResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
760
956
|
const { actualExisting: relinkExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
761
957
|
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
762
958
|
if (relinkExisting.length === 0) {
|
|
@@ -779,11 +975,8 @@ async function linkScope(params) {
|
|
|
779
975
|
}
|
|
780
976
|
break;
|
|
781
977
|
}
|
|
782
|
-
await tx
|
|
783
|
-
await
|
|
784
|
-
await tx.recordOp('link', localPath, relinkCommitPath);
|
|
785
|
-
await linker.linkLib(localPath, relinkCommitPath, relinkExisting);
|
|
786
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, relinkExisting, projectHash, false);
|
|
978
|
+
await linkPlatformDirectory(tx, localPath, relinkCommitPath, relinkExisting);
|
|
979
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, relinkExisting, false);
|
|
787
980
|
if (relinkSupplementResult.downloaded.length > 0) {
|
|
788
981
|
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 重建链接并补充平台 [${relinkExisting.join(', ')}]`);
|
|
789
982
|
}
|
|
@@ -801,11 +994,10 @@ async function linkScope(params) {
|
|
|
801
994
|
const isReplaceGeneral = await resolveLibraryGeneralState(dependency, requestedPlatforms, configVars);
|
|
802
995
|
if (isReplaceGeneral) {
|
|
803
996
|
const sharedPath = path.join(replaceCommitPath, '_shared');
|
|
804
|
-
await tx
|
|
805
|
-
await linker.linkGeneral(localPath, sharedPath);
|
|
997
|
+
await linkGeneralDirectory(tx, localPath, sharedPath);
|
|
806
998
|
savedBytes += replaceSize;
|
|
807
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM],
|
|
808
|
-
|
|
999
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], true);
|
|
1000
|
+
generalDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
809
1001
|
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,创建链接`);
|
|
810
1002
|
}
|
|
811
1003
|
else {
|
|
@@ -814,7 +1006,7 @@ async function linkScope(params) {
|
|
|
814
1006
|
useGitLightweightDownload: gitLightweightDownload,
|
|
815
1007
|
repairRequiredPlatforms,
|
|
816
1008
|
});
|
|
817
|
-
await registerNestedLibraries(replaceSupplementResult.nestedLibraries,
|
|
1009
|
+
await registerNestedLibraries(replaceSupplementResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
818
1010
|
const { actualExisting: replaceExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
819
1011
|
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
820
1012
|
if (replaceExisting.length === 0) {
|
|
@@ -837,10 +1029,9 @@ async function linkScope(params) {
|
|
|
837
1029
|
}
|
|
838
1030
|
break;
|
|
839
1031
|
}
|
|
840
|
-
await tx
|
|
841
|
-
await linker.linkLib(localPath, replaceCommitPath, replaceExisting);
|
|
1032
|
+
await linkPlatformDirectory(tx, localPath, replaceCommitPath, replaceExisting);
|
|
842
1033
|
savedBytes += replaceSize;
|
|
843
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, replaceExisting,
|
|
1034
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, replaceExisting, false);
|
|
844
1035
|
if (replaceSupplementResult.downloaded.length > 0) {
|
|
845
1036
|
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - Store 已有,创建链接并补充平台 [${replaceExisting.join(', ')}]`);
|
|
846
1037
|
}
|
|
@@ -881,20 +1072,31 @@ async function linkScope(params) {
|
|
|
881
1072
|
const progressTracker = new ProgressTracker({
|
|
882
1073
|
name: ` 移入 Store`, total: absorbSize, showSpeed: true,
|
|
883
1074
|
});
|
|
884
|
-
await tx.recordOp('absorb', storeCommitPath, localPath);
|
|
885
1075
|
let progressStarted = false;
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1076
|
+
let absorbResult;
|
|
1077
|
+
try {
|
|
1078
|
+
absorbResult = await new StoreImportService(registry, storePath).importLibrary({
|
|
1079
|
+
libDir: localPath,
|
|
1080
|
+
platforms: finalPlatforms,
|
|
1081
|
+
libName: dependency.libName,
|
|
1082
|
+
commit: dependency.commit,
|
|
1083
|
+
branch: dependency.branch,
|
|
1084
|
+
url: dependency.url,
|
|
1085
|
+
sourceKind: 'local',
|
|
1086
|
+
totalSize: absorbSize,
|
|
1087
|
+
onProgress: (copied, _total) => {
|
|
1088
|
+
if (!progressStarted) {
|
|
1089
|
+
progressStarted = true;
|
|
1090
|
+
progressTracker.start();
|
|
1091
|
+
}
|
|
1092
|
+
progressTracker.update(copied);
|
|
1093
|
+
},
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
finally {
|
|
1097
|
+
if (progressStarted)
|
|
1098
|
+
progressTracker.stop();
|
|
1099
|
+
}
|
|
898
1100
|
let absorbLinkPlatforms = [...Object.keys(absorbResult.platformPaths), ...absorbResult.skippedPlatforms];
|
|
899
1101
|
const absorbLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
900
1102
|
if (!registry.getLibrary(absorbLibKey)) {
|
|
@@ -918,22 +1120,17 @@ async function linkScope(params) {
|
|
|
918
1120
|
}
|
|
919
1121
|
}
|
|
920
1122
|
await registerSharedStore(dependency.libName, dependency.commit, dependency.branch, dependency.url);
|
|
921
|
-
await registerNestedLibraries(absorbResult.nestedLibraries,
|
|
1123
|
+
await registerNestedLibraries(absorbResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
922
1124
|
const absorbSupplementResult = await supplementMissingPlatforms(dependency, platforms, registry, tx, {
|
|
923
1125
|
vars: configVars,
|
|
924
1126
|
useGitLightweightDownload: gitLightweightDownload,
|
|
925
1127
|
repairRequiredPlatforms,
|
|
926
1128
|
});
|
|
927
|
-
await registerNestedLibraries(absorbSupplementResult.nestedLibraries,
|
|
1129
|
+
await registerNestedLibraries(absorbSupplementResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
928
1130
|
if (absorbSupplementResult.downloaded.length > 0) {
|
|
929
1131
|
absorbLinkPlatforms = [...absorbLinkPlatforms, ...absorbSupplementResult.downloaded];
|
|
930
1132
|
}
|
|
931
|
-
await tx
|
|
932
|
-
await linker.linkLib(localPath, storeCommitPath, absorbLinkPlatforms);
|
|
933
|
-
for (const platform of absorbLinkPlatforms) {
|
|
934
|
-
const storeKey = registry.getStoreKey(dependency.libName, dependency.commit, platform);
|
|
935
|
-
registry.addStoreReference(storeKey, projectHash);
|
|
936
|
-
}
|
|
1133
|
+
await linkPlatformDirectory(tx, localPath, storeCommitPath, absorbLinkPlatforms);
|
|
937
1134
|
if (absorbSupplementResult.downloaded.length > 0) {
|
|
938
1135
|
hint(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 本地已有,移入 Store 并补充平台 [${absorbLinkPlatforms.join(', ')}]`);
|
|
939
1136
|
}
|
|
@@ -951,8 +1148,7 @@ async function linkScope(params) {
|
|
|
951
1148
|
break;
|
|
952
1149
|
}
|
|
953
1150
|
const { GENERAL_PLATFORM } = await import('../core/platform.js');
|
|
954
|
-
await tx
|
|
955
|
-
await linker.linkGeneral(localPath, sharedPath);
|
|
1151
|
+
await linkGeneralDirectory(tx, localPath, sharedPath);
|
|
956
1152
|
const storeKey = registry.getStoreKey(dependency.libName, dependency.commit, GENERAL_PLATFORM);
|
|
957
1153
|
if (!registry.getStore(storeKey)) {
|
|
958
1154
|
const integrity = await store.captureIntegrity(dependency.libName, dependency.commit, GENERAL_PLATFORM);
|
|
@@ -962,7 +1158,6 @@ async function linkScope(params) {
|
|
|
962
1158
|
usedBy: [], createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
|
|
963
1159
|
});
|
|
964
1160
|
}
|
|
965
|
-
registry.addStoreReference(storeKey, projectHash);
|
|
966
1161
|
const libKeyGen = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
967
1162
|
if (!registry.getLibrary(libKeyGen)) {
|
|
968
1163
|
const sharedSize = await getDirSize(sharedPath);
|
|
@@ -973,7 +1168,7 @@ async function linkScope(params) {
|
|
|
973
1168
|
referencedBy: [], createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
|
|
974
1169
|
});
|
|
975
1170
|
}
|
|
976
|
-
|
|
1171
|
+
generalDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
977
1172
|
hint(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,整目录链接`);
|
|
978
1173
|
}
|
|
979
1174
|
catch {
|
|
@@ -1040,9 +1235,16 @@ async function linkScope(params) {
|
|
|
1040
1235
|
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, assessment.satisfiedRequested);
|
|
1041
1236
|
const filteredDownloaded = assessment.downloadedRequested;
|
|
1042
1237
|
if (filteredDownloaded.length > 0) {
|
|
1043
|
-
await
|
|
1044
|
-
|
|
1045
|
-
|
|
1238
|
+
const linkNewAbsorbResult = await new StoreImportService(registry, storePath).importLibrary({
|
|
1239
|
+
libDir: downloadResult.libDir,
|
|
1240
|
+
platforms: filteredDownloaded,
|
|
1241
|
+
libName: dependency.libName,
|
|
1242
|
+
commit: dependency.commit,
|
|
1243
|
+
branch: dependency.branch,
|
|
1244
|
+
url: dependency.url,
|
|
1245
|
+
sourceKind: 'download',
|
|
1246
|
+
});
|
|
1247
|
+
await registerNestedLibraries(linkNewAbsorbResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
1046
1248
|
const repaired = filteredDownloaded.filter((platform) => repairRequiredSet.has(platform));
|
|
1047
1249
|
if (repaired.length > 0) {
|
|
1048
1250
|
warn(`${dependency.libName} ${repaired.join(', ')} 本地缓存不完整,已重新下载`);
|
|
@@ -1050,7 +1252,11 @@ async function linkScope(params) {
|
|
|
1050
1252
|
}
|
|
1051
1253
|
}
|
|
1052
1254
|
finally {
|
|
1053
|
-
await
|
|
1255
|
+
await cleanupDownloadTempDir(downloadResult.tempDir, {
|
|
1256
|
+
libName: dependency.libName,
|
|
1257
|
+
commit: dependency.commit,
|
|
1258
|
+
stage: 'link-new-import',
|
|
1259
|
+
});
|
|
1054
1260
|
}
|
|
1055
1261
|
}
|
|
1056
1262
|
const { actualExisting: linkNewExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
@@ -1058,10 +1264,9 @@ async function linkScope(params) {
|
|
|
1058
1264
|
const isLinkNewGeneral = await resolveLibraryGeneralState(dependency, requestedPlatforms, configVars);
|
|
1059
1265
|
if (isLinkNewGeneral) {
|
|
1060
1266
|
const sharedPath = path.join(linkNewCommitPath, '_shared');
|
|
1061
|
-
await tx
|
|
1062
|
-
await
|
|
1063
|
-
|
|
1064
|
-
generalLibs.add(dependency.libName);
|
|
1267
|
+
await linkGeneralDirectory(tx, localPath, sharedPath);
|
|
1268
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], true);
|
|
1269
|
+
generalDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
1065
1270
|
if (isRepairingGeneral) {
|
|
1066
1271
|
warn(`${dependency.libName} ${GENERAL_PLATFORM} 本地缓存不完整,已重新下载`);
|
|
1067
1272
|
}
|
|
@@ -1090,17 +1295,18 @@ async function linkScope(params) {
|
|
|
1090
1295
|
break;
|
|
1091
1296
|
}
|
|
1092
1297
|
else {
|
|
1093
|
-
await tx
|
|
1094
|
-
await
|
|
1095
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, linkNewExisting, projectHash, false);
|
|
1298
|
+
await linkPlatformDirectory(tx, localPath, linkNewCommitPath, linkNewExisting);
|
|
1299
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, linkNewExisting, false);
|
|
1096
1300
|
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 创建链接 [${linkNewExisting.join(', ')}]`);
|
|
1097
1301
|
}
|
|
1098
1302
|
break;
|
|
1099
1303
|
}
|
|
1100
1304
|
}
|
|
1101
|
-
//
|
|
1102
|
-
const desiredLocalPlatforms = await
|
|
1103
|
-
|
|
1305
|
+
// 本地链接集合由本次请求及 sparse 映射决定,不能使用 Store 中全部可用平台。
|
|
1306
|
+
const { actualExisting: desiredLocalPlatforms } = await resolveRequestedStoreState(dependency, finalLinkPlatforms, configVars);
|
|
1307
|
+
debug(`[link-registry] 本地平台清理准备: lib=${dependency.libName}, commit=${dependency.commit}, ` +
|
|
1308
|
+
`requested=${finalLinkPlatforms.join(',')}, desired=${desiredLocalPlatforms.join(',')}, local=${localPath}`);
|
|
1309
|
+
await linker.cleanupLocalExtraPlatforms(localPath, desiredLocalPlatforms, (entryPath) => tx.preparePathReplacement(entryPath, 'extra-platform-cleanup'));
|
|
1104
1310
|
await tx.save();
|
|
1105
1311
|
}
|
|
1106
1312
|
// 8. 并行处理 MISSING 依赖
|
|
@@ -1116,8 +1322,9 @@ async function linkScope(params) {
|
|
|
1116
1322
|
const isTTY = process.stdout.isTTY ?? false;
|
|
1117
1323
|
const multiBarManager = isTTY ? new MultiBarManager() : null;
|
|
1118
1324
|
const downloadLimit = pLimit(concurrency);
|
|
1119
|
-
const
|
|
1325
|
+
const results = await runParallelDownloadBatch(() => toDownload.map((item) => downloadLimit(async () => {
|
|
1120
1326
|
const { dependency, localPath, repairRequiredPlatforms = [] } = item;
|
|
1327
|
+
const implicitOwner = implicitReferenceOwner(dependency, projectRoot, localPath, scope);
|
|
1121
1328
|
const repairRequiredSet = new Set(repairRequiredPlatforms);
|
|
1122
1329
|
const pLog = {
|
|
1123
1330
|
info: (msg) => multiBarManager ? multiBarManager.log(`[info] ${msg}`) : info(msg),
|
|
@@ -1126,6 +1333,7 @@ async function linkScope(params) {
|
|
|
1126
1333
|
warn: (msg) => multiBarManager ? multiBarManager.log(`[warn] ${msg}`) : warn(msg),
|
|
1127
1334
|
error: (msg) => multiBarManager ? multiBarManager.log(`[err] ${msg}`) : error(msg),
|
|
1128
1335
|
};
|
|
1336
|
+
let downloadCompleted = false;
|
|
1129
1337
|
try {
|
|
1130
1338
|
const storeCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
1131
1339
|
const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
|
|
@@ -1159,10 +1367,10 @@ async function linkScope(params) {
|
|
|
1159
1367
|
};
|
|
1160
1368
|
}
|
|
1161
1369
|
pLog.info(`${dependency.libName} 所有平台已存在,直接链接...`);
|
|
1162
|
-
await tx
|
|
1163
|
-
await
|
|
1164
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, existing, projectHash, false);
|
|
1370
|
+
await linkPlatformDirectory(tx, localPath, storeCommitPath, existing);
|
|
1371
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, existing, false);
|
|
1165
1372
|
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 链接完成 [${existing.join(', ')}]`);
|
|
1373
|
+
linkedMissingDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
1166
1374
|
return { success: true, name: dependency.libName, downloadedPlatforms: existing, skippedPlatforms: [] };
|
|
1167
1375
|
}
|
|
1168
1376
|
const dlLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
@@ -1209,6 +1417,7 @@ async function linkScope(params) {
|
|
|
1209
1417
|
finally {
|
|
1210
1418
|
await downloadMonitor.stop();
|
|
1211
1419
|
}
|
|
1420
|
+
downloadCompleted = true;
|
|
1212
1421
|
if (downloadResult.cleanedPlatforms.length > 0) {
|
|
1213
1422
|
pLog.hint(` 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
|
|
1214
1423
|
}
|
|
@@ -1220,13 +1429,18 @@ async function linkScope(params) {
|
|
|
1220
1429
|
platforms: [GENERAL_PLATFORM],
|
|
1221
1430
|
isGeneral: true,
|
|
1222
1431
|
});
|
|
1223
|
-
await
|
|
1224
|
-
|
|
1432
|
+
await new StoreImportService(registry, storePath).importGeneral({
|
|
1433
|
+
libDir: downloadResult.libDir,
|
|
1434
|
+
libName: dependency.libName,
|
|
1435
|
+
commit: dependency.commit,
|
|
1436
|
+
branch: dependency.branch,
|
|
1437
|
+
url: dependency.url,
|
|
1438
|
+
sourceKind: 'download',
|
|
1439
|
+
});
|
|
1225
1440
|
const sharedPath = path.join(storeCommitPath, '_shared');
|
|
1226
|
-
await tx
|
|
1227
|
-
await
|
|
1228
|
-
|
|
1229
|
-
generalLibs.add(dependency.libName);
|
|
1441
|
+
await linkGeneralDirectory(tx, localPath, sharedPath);
|
|
1442
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], true);
|
|
1443
|
+
generalDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
1230
1444
|
if (isRepairingGeneral) {
|
|
1231
1445
|
pLog.warn(`${dependency.libName} ${GENERAL_PLATFORM} 本地缓存不完整,已重新下载`);
|
|
1232
1446
|
}
|
|
@@ -1236,6 +1450,7 @@ async function linkScope(params) {
|
|
|
1236
1450
|
else {
|
|
1237
1451
|
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,下载完成`);
|
|
1238
1452
|
}
|
|
1453
|
+
linkedMissingDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
1239
1454
|
return { success: true, name: dependency.libName, downloadedPlatforms: [GENERAL_PLATFORM], skippedPlatforms: [], isGeneral: true };
|
|
1240
1455
|
}
|
|
1241
1456
|
const filteredDownloaded = assessment.downloadedRequested;
|
|
@@ -1249,9 +1464,16 @@ async function linkScope(params) {
|
|
|
1249
1464
|
pLog.warn(`${dependency.libName} 平台 [${newUnavailable.join(', ')}] 远程不存在,已记录`);
|
|
1250
1465
|
}
|
|
1251
1466
|
if (filteredDownloaded.length > 0) {
|
|
1252
|
-
await
|
|
1253
|
-
|
|
1254
|
-
|
|
1467
|
+
const downloadAbsorbResult = await new StoreImportService(registry, storePath).importLibrary({
|
|
1468
|
+
libDir: downloadResult.libDir,
|
|
1469
|
+
platforms: filteredDownloaded,
|
|
1470
|
+
libName: dependency.libName,
|
|
1471
|
+
commit: dependency.commit,
|
|
1472
|
+
branch: dependency.branch,
|
|
1473
|
+
url: dependency.url,
|
|
1474
|
+
sourceKind: 'download',
|
|
1475
|
+
});
|
|
1476
|
+
await registerNestedLibraries(downloadAbsorbResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
1255
1477
|
}
|
|
1256
1478
|
const linkPlatforms = [...existing, ...filteredDownloaded];
|
|
1257
1479
|
if (linkPlatforms.length === 0) {
|
|
@@ -1266,9 +1488,8 @@ async function linkScope(params) {
|
|
|
1266
1488
|
skippedPlatforms: requestedPlatforms,
|
|
1267
1489
|
};
|
|
1268
1490
|
}
|
|
1269
|
-
await tx
|
|
1270
|
-
await
|
|
1271
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, linkPlatforms, projectHash, false);
|
|
1491
|
+
await linkPlatformDirectory(tx, localPath, storeCommitPath, linkPlatforms);
|
|
1492
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, linkPlatforms, false);
|
|
1272
1493
|
const notLinkedPlatforms = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, linkPlatforms, configVars).missingRequested;
|
|
1273
1494
|
const repaired = filteredDownloaded.filter((platform) => repairRequiredSet.has(platform));
|
|
1274
1495
|
if (repaired.length > 0) {
|
|
@@ -1277,13 +1498,27 @@ async function linkScope(params) {
|
|
|
1277
1498
|
else {
|
|
1278
1499
|
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 下载完成 [${linkPlatforms.join(', ')}]`);
|
|
1279
1500
|
}
|
|
1501
|
+
linkedMissingDependencyKeys.add(dependencyIdentityKey(dependency, localPath));
|
|
1280
1502
|
return { success: true, name: dependency.libName, downloadedPlatforms: linkPlatforms, skippedPlatforms: notLinkedPlatforms };
|
|
1281
1503
|
}
|
|
1282
1504
|
finally {
|
|
1283
|
-
await
|
|
1505
|
+
await cleanupDownloadTempDir(downloadResult.tempDir, {
|
|
1506
|
+
libName: dependency.libName,
|
|
1507
|
+
commit: dependency.commit,
|
|
1508
|
+
stage: 'parallel-download-import',
|
|
1509
|
+
});
|
|
1284
1510
|
}
|
|
1285
1511
|
}
|
|
1286
1512
|
catch (err) {
|
|
1513
|
+
if (downloadCompleted) {
|
|
1514
|
+
pLog.error(`${dependency.libName} Store 导入或项目处理失败: ${err.message}`);
|
|
1515
|
+
return {
|
|
1516
|
+
success: false,
|
|
1517
|
+
name: dependency.libName,
|
|
1518
|
+
error: err.message,
|
|
1519
|
+
fatal: true,
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1287
1522
|
const repairTargets = repairRequiredPlatforms.filter((platform) => platforms.includes(platform) || platform === GENERAL_PLATFORM);
|
|
1288
1523
|
const repairFailure = repairTargets.length > 0;
|
|
1289
1524
|
if (repairTargets.length > 0) {
|
|
@@ -1301,9 +1536,15 @@ async function linkScope(params) {
|
|
|
1301
1536
|
repairFailure,
|
|
1302
1537
|
};
|
|
1303
1538
|
}
|
|
1304
|
-
}));
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1539
|
+
})), multiBarManager);
|
|
1540
|
+
const fatalResults = results.filter((result) => 'fatal' in result && result.fatal);
|
|
1541
|
+
if (fatalResults.length > 0) {
|
|
1542
|
+
const details = fatalResults
|
|
1543
|
+
.map((result) => `${result.name}: ${'error' in result ? result.error : '未知错误'}`)
|
|
1544
|
+
.join(' | ');
|
|
1545
|
+
debug(`[link] 并行任务全部结束后中止项目事务: failures=${details}`);
|
|
1546
|
+
throw new Error(`Store 导入或项目处理失败: ${details}`);
|
|
1547
|
+
}
|
|
1307
1548
|
const succeeded = results.filter((r) => r.success);
|
|
1308
1549
|
const failed = results.filter((r) => !r.success && !('skipped' in r && r.skipped));
|
|
1309
1550
|
blank();
|
|
@@ -1355,35 +1596,40 @@ async function linkScope(params) {
|
|
|
1355
1596
|
const thirdPartyDir = path.dirname(configPath);
|
|
1356
1597
|
for (const action of actions) {
|
|
1357
1598
|
await processAction(action, nestedContext, configPath, thirdPartyDir, {
|
|
1358
|
-
tx, registry,
|
|
1599
|
+
tx, registry, projectRoot, storePath, dryRun, download, yes,
|
|
1359
1600
|
gitLightweightDownload, skipActions: new Set(params.skipActions ?? []),
|
|
1360
|
-
|
|
1601
|
+
scope, generalDependencyKeys, implicitStoreReferences, downloadedLibs, nestedLinkedDeps,
|
|
1361
1602
|
});
|
|
1362
1603
|
}
|
|
1363
1604
|
}
|
|
1364
1605
|
// 10. 同步 cache 文件
|
|
1365
1606
|
for (const cacheConfigPath of cacheConfigPaths) {
|
|
1366
|
-
await
|
|
1607
|
+
await syncCodepacCache(cacheConfigPath, {
|
|
1608
|
+
projectRoot,
|
|
1609
|
+
storePath,
|
|
1610
|
+
transaction: tx,
|
|
1611
|
+
reason: `scope-cache:${scopeName}:${path.basename(cacheConfigPath)}`,
|
|
1612
|
+
dryRun,
|
|
1613
|
+
});
|
|
1367
1614
|
}
|
|
1368
1615
|
// 11. 构建返回结果
|
|
1369
1616
|
const linkedDeps = [];
|
|
1370
1617
|
for (const item of classified) {
|
|
1371
|
-
if (item.status === DependencyStatus.MISSING &&
|
|
1618
|
+
if (item.status === DependencyStatus.MISSING &&
|
|
1619
|
+
!linkedMissingDependencyKeys.has(dependencyIdentityKey(item.dependency, item.localPath))) {
|
|
1372
1620
|
continue;
|
|
1373
1621
|
}
|
|
1374
|
-
const
|
|
1375
|
-
|
|
1376
|
-
: await resolveStoredPlatforms(item.dependency.libName, item.dependency.commit, platforms);
|
|
1377
|
-
linkedDeps.push({
|
|
1378
|
-
libName: item.dependency.libName,
|
|
1379
|
-
commit: item.dependency.commit,
|
|
1380
|
-
platform: actualPlatforms[0] ?? platforms[0],
|
|
1381
|
-
linkedPath: path.relative(projectRoot, item.localPath),
|
|
1382
|
-
scope,
|
|
1383
|
-
});
|
|
1622
|
+
const storeCommitPath = path.join(storePath, item.dependency.libName, item.dependency.commit);
|
|
1623
|
+
await recordLinkedDependency(linkedDeps, item.dependency, item.localPath, storeCommitPath, projectRoot, generalDependencyKeys.has(dependencyIdentityKey(item.dependency, item.localPath)), scope);
|
|
1384
1624
|
}
|
|
1385
1625
|
return {
|
|
1386
|
-
linkedDeps,
|
|
1626
|
+
linkedDeps,
|
|
1627
|
+
nestedLinkedDeps,
|
|
1628
|
+
implicitStoreReferences: serializeImplicitStoreReferences(implicitStoreReferences),
|
|
1629
|
+
downloadedLibs,
|
|
1630
|
+
savedBytes,
|
|
1631
|
+
finalLinkPlatforms,
|
|
1632
|
+
stats,
|
|
1387
1633
|
};
|
|
1388
1634
|
}
|
|
1389
1635
|
/**
|
|
@@ -1402,6 +1648,39 @@ export async function linkProject(projectPath, options) {
|
|
|
1402
1648
|
setProxyConfig(cfg.proxy);
|
|
1403
1649
|
const concurrency = cfg?.concurrency ?? 5;
|
|
1404
1650
|
const gitLightweightDownload = cfg?.gitLightweightDownload ?? true;
|
|
1651
|
+
// 必须先恢复 Registry 文件和项目目录,再加载内存登记并据此选择平台与配置。
|
|
1652
|
+
if (!options.dryRun) {
|
|
1653
|
+
let recoveryInProgress = false;
|
|
1654
|
+
try {
|
|
1655
|
+
await Transaction.recoverPendingTransactions({
|
|
1656
|
+
onRecoveryStart: (transaction) => {
|
|
1657
|
+
recoveryInProgress = true;
|
|
1658
|
+
warn(`发现未完成的事务 (${transaction.id.slice(0, 8)})`);
|
|
1659
|
+
info('正在尝试回滚...');
|
|
1660
|
+
},
|
|
1661
|
+
onRecoveryComplete: () => {
|
|
1662
|
+
recoveryInProgress = false;
|
|
1663
|
+
success('事务回滚完成');
|
|
1664
|
+
},
|
|
1665
|
+
onRecoveryFailure: (transaction, rollbackErrors) => {
|
|
1666
|
+
for (const rollbackError of rollbackErrors) {
|
|
1667
|
+
error(`[transaction] 历史事务恢复失败: transaction=${transaction.id}, detail=${rollbackError}`);
|
|
1668
|
+
}
|
|
1669
|
+
},
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
catch (err) {
|
|
1673
|
+
if (recoveryInProgress) {
|
|
1674
|
+
error(`回滚失败: ${err.message}`);
|
|
1675
|
+
hint('请手动检查 Store 和项目目录状态');
|
|
1676
|
+
}
|
|
1677
|
+
throw err;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
else {
|
|
1681
|
+
const pendingTransactions = await Transaction.getPendingTransactions();
|
|
1682
|
+
debug(`[link] dry-run 跳过事务恢复与事务日志创建: pending=${pendingTransactions.length}`);
|
|
1683
|
+
}
|
|
1405
1684
|
const registry = getRegistry();
|
|
1406
1685
|
await registry.load({ migrate: !options.dryRun });
|
|
1407
1686
|
const existingProject = registry.getProjectByPath(normalizedPath);
|
|
@@ -1436,8 +1715,13 @@ export async function linkProject(projectPath, options) {
|
|
|
1436
1715
|
process.exit(EXIT_CODES.NOINPUT);
|
|
1437
1716
|
}
|
|
1438
1717
|
}
|
|
1439
|
-
catch {
|
|
1440
|
-
|
|
1718
|
+
catch (err) {
|
|
1719
|
+
if (isPathMissingError(err))
|
|
1720
|
+
error(`路径不存在: ${absolutePath}`);
|
|
1721
|
+
else {
|
|
1722
|
+
error(`项目路径无法访问: path=${absolutePath}, code=${err.code ?? 'unknown'}, ` +
|
|
1723
|
+
`message=${err.message}`);
|
|
1724
|
+
}
|
|
1441
1725
|
process.exit(EXIT_CODES.NOINPUT);
|
|
1442
1726
|
}
|
|
1443
1727
|
// 发现可选配置文件,扫描位置跟随实际主配置所在目录。
|
|
@@ -1536,26 +1820,6 @@ export async function linkProject(projectPath, options) {
|
|
|
1536
1820
|
}
|
|
1537
1821
|
}
|
|
1538
1822
|
const finalPath = normalizedRoot;
|
|
1539
|
-
// 预演模式只读取现状,不恢复历史事务,也不创建新的持久事务日志。
|
|
1540
|
-
if (!options.dryRun) {
|
|
1541
|
-
const pendingTx = await Transaction.findPending();
|
|
1542
|
-
if (pendingTx) {
|
|
1543
|
-
warn(`发现未完成的事务 (${pendingTx.id.slice(0, 8)})`);
|
|
1544
|
-
info('正在尝试回滚...');
|
|
1545
|
-
try {
|
|
1546
|
-
await pendingTx.rollback();
|
|
1547
|
-
success('事务回滚完成');
|
|
1548
|
-
}
|
|
1549
|
-
catch (err) {
|
|
1550
|
-
error(`回滚失败: ${err.message}`);
|
|
1551
|
-
hint('请手动检查 Store 和项目目录状态');
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
else {
|
|
1556
|
-
const pendingTransactions = await Transaction.getPendingTransactions();
|
|
1557
|
-
debug(`[link] dry-run 跳过事务恢复与事务日志创建: pending=${pendingTransactions.length}`);
|
|
1558
|
-
}
|
|
1559
1823
|
const storePath = await store.getStorePath();
|
|
1560
1824
|
if (!options.dryRun) {
|
|
1561
1825
|
const recovered = await new SpaceService(registry, storePath)
|
|
@@ -1564,9 +1828,12 @@ export async function linkProject(projectPath, options) {
|
|
|
1564
1828
|
debug(`[link] 清理日志恢复完成: recovered=${recovered}, store=${storePath}`);
|
|
1565
1829
|
}
|
|
1566
1830
|
const projectHash = registry.hashPath(finalPath);
|
|
1567
|
-
const tx = new Transaction(
|
|
1831
|
+
const tx = new Transaction(finalPath, storePath);
|
|
1568
1832
|
if (!options.dryRun)
|
|
1569
1833
|
await tx.begin();
|
|
1834
|
+
let transactionCommitted = false;
|
|
1835
|
+
let projectRegistrySnapshot;
|
|
1836
|
+
let projectRegistryStage = 'linking';
|
|
1570
1837
|
// === 阶段 4: 依次执行 linkScope ===
|
|
1571
1838
|
try {
|
|
1572
1839
|
// 主项目
|
|
@@ -1576,7 +1843,7 @@ export async function linkProject(projectPath, options) {
|
|
|
1576
1843
|
platforms,
|
|
1577
1844
|
finalLinkPlatforms: platforms,
|
|
1578
1845
|
scanExtraPlatforms: true,
|
|
1579
|
-
registry, tx,
|
|
1846
|
+
registry, tx,
|
|
1580
1847
|
projectRoot: finalPath,
|
|
1581
1848
|
storePath,
|
|
1582
1849
|
download: options.download,
|
|
@@ -1601,7 +1868,7 @@ export async function linkProject(projectPath, options) {
|
|
|
1601
1868
|
platforms,
|
|
1602
1869
|
finalLinkPlatforms,
|
|
1603
1870
|
scanExtraPlatforms: false,
|
|
1604
|
-
registry, tx,
|
|
1871
|
+
registry, tx,
|
|
1605
1872
|
projectRoot: finalPath,
|
|
1606
1873
|
storePath,
|
|
1607
1874
|
download: options.download,
|
|
@@ -1628,7 +1895,7 @@ export async function linkProject(projectPath, options) {
|
|
|
1628
1895
|
platforms,
|
|
1629
1896
|
finalLinkPlatforms,
|
|
1630
1897
|
scanExtraPlatforms: false,
|
|
1631
|
-
registry, tx,
|
|
1898
|
+
registry, tx,
|
|
1632
1899
|
projectRoot: finalPath,
|
|
1633
1900
|
storePath,
|
|
1634
1901
|
download: options.download,
|
|
@@ -1643,14 +1910,23 @@ export async function linkProject(projectPath, options) {
|
|
|
1643
1910
|
subResults.push(subResult);
|
|
1644
1911
|
}
|
|
1645
1912
|
// === 阶段 5: 合并结果、注册项目 ===
|
|
1913
|
+
projectRegistrySnapshot = registry.snapshotState();
|
|
1914
|
+
projectRegistryStage = 'assembling-project-references';
|
|
1646
1915
|
// 合并所有依赖
|
|
1647
|
-
const
|
|
1916
|
+
const currentLinkedDeps = deduplicateDependencyRefs([
|
|
1648
1917
|
...mainResult.linkedDeps,
|
|
1649
1918
|
...mainResult.nestedLinkedDeps,
|
|
1650
1919
|
...subResults.flatMap(r => [...r.linkedDeps, ...r.nestedLinkedDeps]),
|
|
1651
|
-
];
|
|
1652
|
-
|
|
1653
|
-
const
|
|
1920
|
+
]);
|
|
1921
|
+
const retainedLinkedDeps = await retainExistingLinkedDependencies(existingProject, currentLinkedDeps, finalPath, storePath);
|
|
1922
|
+
const allLinkedDeps = deduplicateDependencyRefs([
|
|
1923
|
+
...currentLinkedDeps,
|
|
1924
|
+
...retainedLinkedDeps,
|
|
1925
|
+
]);
|
|
1926
|
+
const implicitStoreReferences = mergeProjectImplicitStoreReferences(existingProject, projectHash, allLinkedDeps, [
|
|
1927
|
+
...mainResult.implicitStoreReferences,
|
|
1928
|
+
...subResults.flatMap((result) => result.implicitStoreReferences),
|
|
1929
|
+
], registry);
|
|
1654
1930
|
// 更新项目信息
|
|
1655
1931
|
const relConfigPath = getRelativeConfigPath(finalPath, mainConfigPath);
|
|
1656
1932
|
registry.addProject({
|
|
@@ -1664,19 +1940,26 @@ export async function linkProject(projectPath, options) {
|
|
|
1664
1940
|
submodules: selectedSubmodules.length > 0
|
|
1665
1941
|
? selectedSubmodules.map(s => s.relativePath) : undefined,
|
|
1666
1942
|
submoduleOptionalConfigs: buildSubmoduleOptionalConfigState(selectedSubmodules),
|
|
1943
|
+
implicitStoreReferences: implicitStoreReferences.length > 0 ? implicitStoreReferences : undefined,
|
|
1667
1944
|
});
|
|
1668
|
-
//
|
|
1669
|
-
const newStoreKeys =
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
}
|
|
1945
|
+
// dependency 和 usedBy 使用同一份真实链接结果;隐式嵌套库由吸收结果补充。
|
|
1946
|
+
const newStoreKeys = new Set([
|
|
1947
|
+
...allLinkedDeps.map((dependency) => registry.getStoreKey(dependency.libName, dependency.commit, dependency.platform)),
|
|
1948
|
+
...implicitStoreReferences.flatMap((reference) => reference.storeKeys),
|
|
1949
|
+
]);
|
|
1950
|
+
debug(`[link-registry] 项目引用提交准备: project=${projectHash}, dependencies=${allLinkedDeps.length}, ` +
|
|
1951
|
+
`retainedDependencies=${retainedLinkedDeps.length}, implicit=${implicitStoreReferences.length}, ` +
|
|
1952
|
+
`storeKeys=${[...newStoreKeys].join(',') || 'none'}`);
|
|
1953
|
+
const referenceUpdate = registry.replaceProjectReferences(projectHash, newStoreKeys);
|
|
1954
|
+
debug(`[link-registry] 项目引用提交结果: project=${projectHash}, ` +
|
|
1955
|
+
`added=${referenceUpdate.added.length}, removed=${referenceUpdate.removed.length}, ` +
|
|
1956
|
+
`retained=${referenceUpdate.retained.length}`);
|
|
1957
|
+
await tx.prepareFileReplacement(getRegistryPath(), 'registry-save');
|
|
1958
|
+
projectRegistryStage = 'saving-registry';
|
|
1678
1959
|
await registry.save();
|
|
1960
|
+
projectRegistryStage = 'committing-project-transaction';
|
|
1679
1961
|
await tx.commit();
|
|
1962
|
+
transactionCommitted = true;
|
|
1680
1963
|
// === 阶段 6: 统计报告 ===
|
|
1681
1964
|
const allSavedBytes = mainResult.savedBytes + subResults.reduce((sum, r) => sum + r.savedBytes, 0);
|
|
1682
1965
|
blank();
|
|
@@ -1767,20 +2050,43 @@ export async function linkProject(projectPath, options) {
|
|
|
1767
2050
|
await checkForUpdates();
|
|
1768
2051
|
}
|
|
1769
2052
|
catch (err) {
|
|
2053
|
+
if (transactionCommitted) {
|
|
2054
|
+
error(`[link] 链接事务提交后的处理失败: transaction=${tx.id}, state=committed, message=${err.message}`);
|
|
2055
|
+
hint('依赖链接和项目登记已经完成,本次不会回滚;后续运行会继续清理事务产物');
|
|
2056
|
+
process.exit(1);
|
|
2057
|
+
}
|
|
1770
2058
|
// 链接过程出错,回滚事务
|
|
1771
2059
|
error(`链接失败: ${err.message}`);
|
|
2060
|
+
if (projectRegistrySnapshot) {
|
|
2061
|
+
error(`[link-registry] 项目登记失败: transaction=${tx.id}, stage=${projectRegistryStage}, ` +
|
|
2062
|
+
`project=${projectHash}, message=${err.message}`);
|
|
2063
|
+
}
|
|
1772
2064
|
hintStoreRepairCommand(err);
|
|
1773
2065
|
if (!options.dryRun) {
|
|
1774
2066
|
warn('正在回滚事务...');
|
|
1775
2067
|
try {
|
|
1776
|
-
await tx.rollback();
|
|
1777
|
-
|
|
2068
|
+
const rollbackErrors = await tx.rollback();
|
|
2069
|
+
if (rollbackErrors.length === 0) {
|
|
2070
|
+
success('事务已回滚');
|
|
2071
|
+
}
|
|
2072
|
+
else {
|
|
2073
|
+
error(`事务回滚未完成: transaction=${tx.id}, failedOperations=${rollbackErrors.length}`);
|
|
2074
|
+
for (const rollbackError of rollbackErrors) {
|
|
2075
|
+
error(`[transaction] 当前事务恢复失败: transaction=${tx.id}, detail=${rollbackError}`);
|
|
2076
|
+
}
|
|
2077
|
+
hint('事务日志已保留,请停止修改项目并重新运行 td link 触发恢复');
|
|
2078
|
+
}
|
|
1778
2079
|
}
|
|
1779
2080
|
catch (rollbackErr) {
|
|
1780
2081
|
error(`回滚失败: ${rollbackErr.message}`);
|
|
1781
2082
|
hint('请手动检查 Store 和项目目录状态');
|
|
1782
2083
|
}
|
|
1783
2084
|
}
|
|
2085
|
+
if (projectRegistrySnapshot) {
|
|
2086
|
+
registry.restoreState(projectRegistrySnapshot);
|
|
2087
|
+
debug(`[link-registry] 项目登记内存恢复完成: transaction=${tx.id}, project=${projectHash}, ` +
|
|
2088
|
+
`stage=${projectRegistryStage}`);
|
|
2089
|
+
}
|
|
1784
2090
|
process.exit(1);
|
|
1785
2091
|
}
|
|
1786
2092
|
}
|
|
@@ -1821,8 +2127,12 @@ async function registerSharedStore(libName, commit, branch = '', url = '') {
|
|
|
1821
2127
|
return false; // 空目录不注册
|
|
1822
2128
|
}
|
|
1823
2129
|
}
|
|
1824
|
-
catch {
|
|
1825
|
-
|
|
2130
|
+
catch (err) {
|
|
2131
|
+
if (isPathMissingError(err))
|
|
2132
|
+
return false;
|
|
2133
|
+
error(`[link-registry] shared 登记读取失败: lib=${libName}, commit=${commit}, path=${sharedPath}, ` +
|
|
2134
|
+
`code=${err.code ?? 'unknown'}, message=${err.message}`);
|
|
2135
|
+
throw err;
|
|
1826
2136
|
}
|
|
1827
2137
|
// 计算 _shared 完整性信息并注册
|
|
1828
2138
|
const integrity = await store.captureIntegrity(libName, commit, SHARED_PLATFORM);
|
|
@@ -1850,7 +2160,7 @@ async function registerSharedStore(libName, commit, branch = '', url = '') {
|
|
|
1850
2160
|
debug(`注册 _shared: ${libName}:${commit.slice(0, 8)} (${formatSize(integrity.size)})`);
|
|
1851
2161
|
return true;
|
|
1852
2162
|
}
|
|
1853
|
-
async function ensureStoreInfo(libName, commit, platform, branch, url
|
|
2163
|
+
async function ensureStoreInfo(libName, commit, platform, branch, url) {
|
|
1854
2164
|
const registry = getRegistry();
|
|
1855
2165
|
const storeKey = registry.getStoreKey(libName, commit, platform);
|
|
1856
2166
|
const existingEntry = registry.getStore(storeKey);
|
|
@@ -1876,9 +2186,6 @@ async function ensureStoreInfo(libName, commit, platform, branch, url, projectHa
|
|
|
1876
2186
|
lastAccess: new Date().toISOString(),
|
|
1877
2187
|
});
|
|
1878
2188
|
}
|
|
1879
|
-
if (projectHash) {
|
|
1880
|
-
registry.addStoreReference(storeKey, projectHash);
|
|
1881
|
-
}
|
|
1882
2189
|
}
|
|
1883
2190
|
async function syncLibraryInfo(libName, commit, branch, url, platforms, isGeneral) {
|
|
1884
2191
|
const registry = getRegistry();
|
|
@@ -1926,17 +2233,101 @@ async function resolveStoredPlatforms(libName, commit, fallbackPlatforms) {
|
|
|
1926
2233
|
.map(entry => entry.name);
|
|
1927
2234
|
return storedPlatforms.length > 0 ? storedPlatforms : [...new Set(fallbackPlatforms)];
|
|
1928
2235
|
}
|
|
1929
|
-
catch {
|
|
1930
|
-
|
|
2236
|
+
catch (err) {
|
|
2237
|
+
if (isPathMissingError(err))
|
|
2238
|
+
return [...new Set(fallbackPlatforms)];
|
|
2239
|
+
error(`[link-registry] Store 平台枚举失败: lib=${libName}, commit=${commit}, path=${commitPath}, ` +
|
|
2240
|
+
`code=${err.code ?? 'unknown'}, message=${err.message}`);
|
|
2241
|
+
throw err;
|
|
1931
2242
|
}
|
|
1932
2243
|
}
|
|
1933
|
-
|
|
2244
|
+
/**
|
|
2245
|
+
* 项目引用登记以当前项目中的真实链接为准。任何无法证明的目录形态都会终止项目事务,
|
|
2246
|
+
* 避免根据请求平台写入推测的 dependency 和 Store usedBy。
|
|
2247
|
+
*/
|
|
2248
|
+
export async function resolveLinkedPlatforms(input) {
|
|
2249
|
+
const { libName, commit, localPath, storeCommitPath } = input;
|
|
2250
|
+
debug(`[link-registry] 真实平台核对开始: lib=${libName}, commit=${commit}, local=${localPath}, store=${storeCommitPath}`);
|
|
2251
|
+
try {
|
|
2252
|
+
const stat = await fs.lstat(localPath);
|
|
2253
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
2254
|
+
throw new Error(`项目库应为包含平台链接的物理目录: isDirectory=${stat.isDirectory()}, isSymbolicLink=${stat.isSymbolicLink()}`);
|
|
2255
|
+
}
|
|
2256
|
+
const entries = await fs.readdir(localPath, { withFileTypes: true });
|
|
2257
|
+
const linkedPlatforms = [];
|
|
2258
|
+
for (const entry of entries) {
|
|
2259
|
+
if (!KNOWN_PLATFORM_VALUES.includes(entry.name))
|
|
2260
|
+
continue;
|
|
2261
|
+
const entryPath = path.join(localPath, entry.name);
|
|
2262
|
+
if (!entry.isSymbolicLink()) {
|
|
2263
|
+
throw new Error(`平台项不是符号链接: platform=${entry.name}, path=${entryPath}`);
|
|
2264
|
+
}
|
|
2265
|
+
const rawTarget = await fs.readlink(entryPath);
|
|
2266
|
+
const target = path.resolve(localPath, rawTarget);
|
|
2267
|
+
const expectedTarget = path.join(storeCommitPath, entry.name);
|
|
2268
|
+
if (!pathsEqual(target, expectedTarget)) {
|
|
2269
|
+
throw new Error(`平台链接目标不属于当前 Store 版本: platform=${entry.name}, path=${entryPath}, target=${target}, expected=${expectedTarget}`);
|
|
2270
|
+
}
|
|
2271
|
+
linkedPlatforms.push(entry.name);
|
|
2272
|
+
debug(`[link-registry] 真实平台链接确认: lib=${libName}, commit=${commit}, platform=${entry.name}, path=${entryPath}, target=${target}`);
|
|
2273
|
+
}
|
|
2274
|
+
if (linkedPlatforms.length === 0) {
|
|
2275
|
+
throw new Error('项目目录中没有指向当前 Store 版本的平台链接');
|
|
2276
|
+
}
|
|
2277
|
+
const resolved = [...new Set(linkedPlatforms)];
|
|
2278
|
+
debug(`[link-registry] 真实平台核对完成: lib=${libName}, commit=${commit}, platforms=${resolved.join(',')}`);
|
|
2279
|
+
return resolved;
|
|
2280
|
+
}
|
|
2281
|
+
catch (err) {
|
|
2282
|
+
const message = `无法确认项目真实平台: lib=${libName}, commit=${commit}, local=${localPath}, ` +
|
|
2283
|
+
`store=${storeCommitPath}, detail=${err.message}`;
|
|
2284
|
+
error(`[link-registry] 真实平台核对失败: ${message}`);
|
|
2285
|
+
throw new Error(message, { cause: err });
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
async function resolveLinkedDependencyPlatforms(input) {
|
|
2289
|
+
if (!input.isGeneral)
|
|
2290
|
+
return resolveLinkedPlatforms(input);
|
|
2291
|
+
const expectedTarget = path.join(input.storeCommitPath, SHARED_PLATFORM);
|
|
2292
|
+
debug(`[link-registry] General 链接核对开始: lib=${input.libName}, commit=${input.commit}, ` +
|
|
2293
|
+
`local=${input.localPath}, expected=${expectedTarget}`);
|
|
2294
|
+
const status = await linker.getPathStatus(input.localPath, expectedTarget);
|
|
2295
|
+
const materialized = status === 'directory' &&
|
|
2296
|
+
(await isMaterializedStoreDirectory(input.localPath, expectedTarget));
|
|
2297
|
+
if (status !== 'linked' && !materialized) {
|
|
2298
|
+
const message = `无法确认项目 General 引用: lib=${input.libName}, commit=${input.commit}, ` +
|
|
2299
|
+
`local=${input.localPath}, expected=${expectedTarget}, status=${status}`;
|
|
2300
|
+
error(`[link-registry] General 链接核对失败: ${message}`);
|
|
2301
|
+
throw new Error(message);
|
|
2302
|
+
}
|
|
2303
|
+
debug(`[link-registry] General 链接核对完成: lib=${input.libName}, commit=${input.commit}, ` +
|
|
2304
|
+
`local=${input.localPath}, status=${status}, materialized=${materialized}, platform=${GENERAL_PLATFORM}`);
|
|
2305
|
+
return [GENERAL_PLATFORM];
|
|
2306
|
+
}
|
|
2307
|
+
async function recordLinkedDependency(dependencies, dependency, localPath, storeCommitPath, projectRoot, isGeneral, scope) {
|
|
2308
|
+
const actualPlatforms = await resolveLinkedDependencyPlatforms({
|
|
2309
|
+
libName: dependency.libName,
|
|
2310
|
+
commit: dependency.commit,
|
|
2311
|
+
localPath,
|
|
2312
|
+
storeCommitPath,
|
|
2313
|
+
isGeneral,
|
|
2314
|
+
});
|
|
2315
|
+
dependencies.push(...buildDependencyRefs({
|
|
2316
|
+
libName: dependency.libName,
|
|
2317
|
+
commit: dependency.commit,
|
|
2318
|
+
linkedPath: path.relative(projectRoot, localPath),
|
|
2319
|
+
scope,
|
|
2320
|
+
}, actualPlatforms));
|
|
2321
|
+
debug(`[link-registry] dependency 登记完成: lib=${dependency.libName}, commit=${dependency.commit}, ` +
|
|
2322
|
+
`local=${localPath}, scope=${scope ?? 'main'}, platforms=${actualPlatforms.join(',')}`);
|
|
2323
|
+
}
|
|
2324
|
+
async function ensureLinkedRegistryState(libName, commit, branch, url, platforms, isGeneral) {
|
|
1934
2325
|
const linkedPlatforms = isGeneral ? [GENERAL_PLATFORM] : [...new Set(platforms)];
|
|
1935
2326
|
const libraryPlatforms = isGeneral
|
|
1936
2327
|
? [GENERAL_PLATFORM]
|
|
1937
2328
|
: await resolveStoredPlatforms(libName, commit, linkedPlatforms);
|
|
1938
2329
|
for (const platform of linkedPlatforms) {
|
|
1939
|
-
await ensureStoreInfo(libName, commit, platform, branch, url
|
|
2330
|
+
await ensureStoreInfo(libName, commit, platform, branch, url);
|
|
1940
2331
|
}
|
|
1941
2332
|
if (!isGeneral) {
|
|
1942
2333
|
await registerSharedStore(libName, commit, branch, url);
|
|
@@ -1947,7 +2338,7 @@ async function ensureLinkedRegistryState(libName, commit, branch, url, platforms
|
|
|
1947
2338
|
* 注册嵌套依赖到 Registry
|
|
1948
2339
|
* absorbLib 递归吸收嵌套依赖时只做文件操作,此函数负责 Registry 注册
|
|
1949
2340
|
*/
|
|
1950
|
-
async function registerNestedLibraries(nestedLibraries,
|
|
2341
|
+
async function registerNestedLibraries(nestedLibraries, references, owner) {
|
|
1951
2342
|
if (nestedLibraries.length === 0)
|
|
1952
2343
|
return;
|
|
1953
2344
|
debug(`注册 ${nestedLibraries.length} 个嵌套依赖: ${nestedLibraries.map(n => n.libName).join(', ')}`);
|
|
@@ -1972,7 +2363,7 @@ async function registerNestedLibraries(nestedLibraries, projectHash) {
|
|
|
1972
2363
|
lastAccess: new Date().toISOString(),
|
|
1973
2364
|
});
|
|
1974
2365
|
}
|
|
1975
|
-
|
|
2366
|
+
addImplicitStoreReference(references, owner, storeKey);
|
|
1976
2367
|
// 兼容旧结构:LibraryInfo
|
|
1977
2368
|
const libKey = registry.getLibraryKey(nested.libName, nested.commit);
|
|
1978
2369
|
if (!registry.getLibrary(libKey)) {
|
|
@@ -2008,7 +2399,7 @@ async function registerNestedLibraries(nestedLibraries, projectHash) {
|
|
|
2008
2399
|
lastAccess: new Date().toISOString(),
|
|
2009
2400
|
});
|
|
2010
2401
|
}
|
|
2011
|
-
|
|
2402
|
+
addImplicitStoreReference(references, owner, storeKey);
|
|
2012
2403
|
}
|
|
2013
2404
|
// 注册 _shared 目录(如果存在)
|
|
2014
2405
|
await registerSharedStore(nested.libName, nested.commit, '', '');
|
|
@@ -2030,6 +2421,11 @@ async function registerNestedLibraries(nestedLibraries, projectHash) {
|
|
|
2030
2421
|
}
|
|
2031
2422
|
}
|
|
2032
2423
|
}
|
|
2424
|
+
debug(`[link-registry] 隐式嵌套库登记完成: libraries=${nestedLibraries.length}, ` +
|
|
2425
|
+
`owner=${implicitReferenceOwnerKey(owner)}, ` +
|
|
2426
|
+
`storeKeys=${serializeImplicitStoreReferences(references)
|
|
2427
|
+
.flatMap((reference) => reference.storeKeys)
|
|
2428
|
+
.join(',') || 'none'}`);
|
|
2033
2429
|
}
|
|
2034
2430
|
/**
|
|
2035
2431
|
* 分类依赖的链接状态
|
|
@@ -2079,8 +2475,10 @@ export async function classifyLinkStatus(localPath, storeCommitPath, isGeneral,
|
|
|
2079
2475
|
await fs.lstat(localPath);
|
|
2080
2476
|
return 'replace';
|
|
2081
2477
|
}
|
|
2082
|
-
catch {
|
|
2083
|
-
|
|
2478
|
+
catch (err) {
|
|
2479
|
+
if (isPathMissingError(err))
|
|
2480
|
+
return 'link_new';
|
|
2481
|
+
throw err;
|
|
2084
2482
|
}
|
|
2085
2483
|
}
|
|
2086
2484
|
/**
|
|
@@ -2247,6 +2645,7 @@ async function supplementMissingPlatforms(dependency, platforms, registry, tx, o
|
|
|
2247
2645
|
else {
|
|
2248
2646
|
info(`${dependency.libName} 缺少平台 [${toDownload.join(', ')}],尝试下载...`);
|
|
2249
2647
|
}
|
|
2648
|
+
let downloadCompleted = false;
|
|
2250
2649
|
try {
|
|
2251
2650
|
const downloadResult = await codepac.downloadToTemp({
|
|
2252
2651
|
url: dependency.url,
|
|
@@ -2258,6 +2657,7 @@ async function supplementMissingPlatforms(dependency, platforms, registry, tx, o
|
|
|
2258
2657
|
vars: options.vars,
|
|
2259
2658
|
useGitLightweightDownload: options.useGitLightweightDownload,
|
|
2260
2659
|
});
|
|
2660
|
+
downloadCompleted = true;
|
|
2261
2661
|
// 提示清理的平台(如果有)
|
|
2262
2662
|
if (downloadResult.cleanedPlatforms.length > 0) {
|
|
2263
2663
|
hint(` 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
|
|
@@ -2286,9 +2686,15 @@ async function supplementMissingPlatforms(dependency, platforms, registry, tx, o
|
|
|
2286
2686
|
// 7. 吸收下载的内容到 Store(不做链接,由调用者负责)
|
|
2287
2687
|
if (downloaded.length > 0) {
|
|
2288
2688
|
const storePath = await store.getStorePath();
|
|
2289
|
-
const
|
|
2290
|
-
|
|
2291
|
-
|
|
2689
|
+
const supplementAbsorbResult = await new StoreImportService(registry, storePath).importLibrary({
|
|
2690
|
+
libDir: downloadResult.libDir,
|
|
2691
|
+
platforms: downloaded,
|
|
2692
|
+
libName: dependency.libName,
|
|
2693
|
+
commit: dependency.commit,
|
|
2694
|
+
branch: dependency.branch,
|
|
2695
|
+
url: dependency.url,
|
|
2696
|
+
sourceKind: 'download',
|
|
2697
|
+
});
|
|
2292
2698
|
// 收集嵌套依赖(由调用者负责注册)
|
|
2293
2699
|
result.nestedLibraries.push(...supplementAbsorbResult.nestedLibraries);
|
|
2294
2700
|
// 8. 更新 Registry StoreEntry
|
|
@@ -2317,11 +2723,17 @@ async function supplementMissingPlatforms(dependency, platforms, registry, tx, o
|
|
|
2317
2723
|
}
|
|
2318
2724
|
}
|
|
2319
2725
|
finally {
|
|
2320
|
-
|
|
2321
|
-
|
|
2726
|
+
await cleanupDownloadTempDir(downloadResult.tempDir, {
|
|
2727
|
+
libName: dependency.libName,
|
|
2728
|
+
commit: dependency.commit,
|
|
2729
|
+
stage: 'supplement-import',
|
|
2730
|
+
});
|
|
2322
2731
|
}
|
|
2323
2732
|
}
|
|
2324
2733
|
catch (err) {
|
|
2734
|
+
if (downloadCompleted) {
|
|
2735
|
+
throw err;
|
|
2736
|
+
}
|
|
2325
2737
|
const repairTargets = toDownload.filter((platform) => repairRequiredPlatforms.has(platform));
|
|
2326
2738
|
if (repairTargets.length > 0) {
|
|
2327
2739
|
error(`${dependency.libName} ${repairTargets.join(', ')} 本地缓存不完整,自动重新下载失败`);
|
|
@@ -2381,74 +2793,6 @@ function getStatusKey(status) {
|
|
|
2381
2793
|
};
|
|
2382
2794
|
return map[status];
|
|
2383
2795
|
}
|
|
2384
|
-
/**
|
|
2385
|
-
* 同步配置文件到 cache 目录
|
|
2386
|
-
* 使 cmake 的 checkValid.js 检测能够通过
|
|
2387
|
-
*/
|
|
2388
|
-
async function syncCacheFile(configPath, storePath, dryRun = false) {
|
|
2389
|
-
const configDir = path.dirname(configPath);
|
|
2390
|
-
const cacheDir = path.join(configDir, '.cache');
|
|
2391
|
-
const configFileName = path.basename(configPath);
|
|
2392
|
-
const cachePath = path.join(cacheDir, configFileName);
|
|
2393
|
-
const tempCachePath = path.join(cacheDir, `.${configFileName}.tanmi-dock-${process.pid}-${crypto.randomBytes(4).toString('hex')}.tmp`);
|
|
2394
|
-
let tempCacheCreated = false;
|
|
2395
|
-
debug(`[link-cache] 同步检查: config=${configPath}, cache=${cachePath}, store=${storePath}, dryRun=${dryRun}`);
|
|
2396
|
-
if (dryRun) {
|
|
2397
|
-
debug(`[link-cache] 预演跳过写入: config=${configPath}, cache=${cachePath}`);
|
|
2398
|
-
return;
|
|
2399
|
-
}
|
|
2400
|
-
try {
|
|
2401
|
-
await assertPathOutsideStore(cacheDir, storePath, 'sync-action-cache-directory');
|
|
2402
|
-
// 确保 .cache 目录存在
|
|
2403
|
-
await fs.mkdir(cacheDir, { recursive: true });
|
|
2404
|
-
// 文件级符号链接必须先删除链接本身,避免 copyFile 跟随链接覆盖 Store。
|
|
2405
|
-
try {
|
|
2406
|
-
const cacheStat = await fs.lstat(cachePath);
|
|
2407
|
-
if (cacheStat.isDirectory()) {
|
|
2408
|
-
throw new Error(`cache 目标是目录: ${cachePath}`);
|
|
2409
|
-
}
|
|
2410
|
-
if (cacheStat.isSymbolicLink()) {
|
|
2411
|
-
const previousTarget = await fs.readlink(cachePath).catch(() => '(unreadable)');
|
|
2412
|
-
await fs.unlink(cachePath);
|
|
2413
|
-
debug(`[link-cache] 已移除旧 cache 符号链接: cache=${cachePath}, previousTarget=${previousTarget}`);
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
catch (err) {
|
|
2417
|
-
if (err.code !== 'ENOENT')
|
|
2418
|
-
throw err;
|
|
2419
|
-
}
|
|
2420
|
-
await assertPathOutsideStore(cachePath, storePath, 'sync-action-cache-file');
|
|
2421
|
-
const tempHandle = await fs.open(tempCachePath, 'wx');
|
|
2422
|
-
await tempHandle.close();
|
|
2423
|
-
tempCacheCreated = true;
|
|
2424
|
-
await fs.copyFile(configPath, tempCachePath);
|
|
2425
|
-
try {
|
|
2426
|
-
await fs.rename(tempCachePath, cachePath);
|
|
2427
|
-
}
|
|
2428
|
-
catch (err) {
|
|
2429
|
-
const code = err.code;
|
|
2430
|
-
if (code !== 'EEXIST' && code !== 'EPERM')
|
|
2431
|
-
throw err;
|
|
2432
|
-
await fs.rm(cachePath, { force: true });
|
|
2433
|
-
await fs.rename(tempCachePath, cachePath);
|
|
2434
|
-
}
|
|
2435
|
-
if (process.env.VERBOSE) {
|
|
2436
|
-
info(`已同步 cache: ${configFileName} -> ${path.relative(configDir, cachePath)}`);
|
|
2437
|
-
}
|
|
2438
|
-
debug(`[link-cache] 同步完成: config=${configPath}, cache=${cachePath}, state=written`);
|
|
2439
|
-
}
|
|
2440
|
-
catch (err) {
|
|
2441
|
-
// cache 同步失败不应阻塞主流程,仅警告
|
|
2442
|
-
warn(`cache 同步失败: ${configFileName} -> ${path.relative(configDir, cachePath)}: ${err.message}`);
|
|
2443
|
-
}
|
|
2444
|
-
finally {
|
|
2445
|
-
if (tempCacheCreated) {
|
|
2446
|
-
await fs.rm(tempCachePath, { force: true }).catch((err) => {
|
|
2447
|
-
debug(`[link-cache] 临时文件清理失败: temp=${tempCachePath}, message=${err.message}`);
|
|
2448
|
-
});
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
2796
|
/**
|
|
2453
2797
|
* 处理单个 action(嵌套依赖)
|
|
2454
2798
|
* @param action action 配置
|
|
@@ -2491,10 +2835,16 @@ async function processAction(action, context, parentConfigPath, parentTargetDir,
|
|
|
2491
2835
|
try {
|
|
2492
2836
|
await fs.access(plan.nestedConfigPath);
|
|
2493
2837
|
}
|
|
2494
|
-
catch {
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2838
|
+
catch (err) {
|
|
2839
|
+
if (isPathMissingError(err)) {
|
|
2840
|
+
warn(`${indent} 跳过嵌套 action: name=${plan.actionName ?? '(未命名)'}, 原因=配置不存在, config=${plan.nestedConfigPath}`);
|
|
2841
|
+
hint(`${indent} 请确保 ${plan.configDir} 库已被下载`);
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
error(`${indent}嵌套 action 配置访问失败: name=${plan.actionName ?? '(未命名)'}, ` +
|
|
2845
|
+
`config=${plan.nestedConfigPath}, code=${err.code ?? 'unknown'}, ` +
|
|
2846
|
+
`message=${err.message}`);
|
|
2847
|
+
throw err;
|
|
2498
2848
|
}
|
|
2499
2849
|
// 4. 提取指定库的依赖
|
|
2500
2850
|
let nestedResult;
|
|
@@ -2539,7 +2889,13 @@ async function processAction(action, context, parentConfigPath, parentTargetDir,
|
|
|
2539
2889
|
indent,
|
|
2540
2890
|
});
|
|
2541
2891
|
// 同步嵌套配置文件的 cache(兼容 checkValid.js 检测)
|
|
2542
|
-
await
|
|
2892
|
+
const cacheSync = await syncCodepacCache(plan.nestedConfigPath, {
|
|
2893
|
+
projectRoot: options.projectRoot,
|
|
2894
|
+
storePath: options.storePath,
|
|
2895
|
+
transaction: options.tx,
|
|
2896
|
+
reason: `action-cache:${plan.actionName ?? 'unnamed'}:depth-${context.depth}`,
|
|
2897
|
+
dryRun: options.dryRun,
|
|
2898
|
+
});
|
|
2543
2899
|
// 7. 递归处理嵌套 actions(如果没有 disable_action)
|
|
2544
2900
|
if (!plan.parsed.disableAction && nestedActions.length > 0) {
|
|
2545
2901
|
const nestedContext = {
|
|
@@ -2557,7 +2913,11 @@ async function processAction(action, context, parentConfigPath, parentTargetDir,
|
|
|
2557
2913
|
info(`${indent} 跳过后续嵌套 action: name=${plan.actionName ?? '(未命名)'}, 原因=disable_action`);
|
|
2558
2914
|
}
|
|
2559
2915
|
const materializedPaths = [
|
|
2560
|
-
...new Set([
|
|
2916
|
+
...new Set([
|
|
2917
|
+
...configPreparation.materializedPaths,
|
|
2918
|
+
...targetPreparation.materializedPaths,
|
|
2919
|
+
...cacheSync.directoryPreparation.materializedPaths,
|
|
2920
|
+
]),
|
|
2561
2921
|
];
|
|
2562
2922
|
debug(`${indent}CodePac action 完成: name=${plan.actionName ?? '(未命名)'}, dependencies=${dependencies.length}, nestedActions=${nestedActions.length}, materialized=${materializedPaths.join(',') || 'none'}, output=${plan.nestedTargetDir}`);
|
|
2563
2923
|
}
|
|
@@ -2585,8 +2945,14 @@ async function resolveLocalAndAbsorb(localPath, libName, commit, options) {
|
|
|
2585
2945
|
case 'match': {
|
|
2586
2946
|
info(`${indent} ${libName} - 本地 commit 匹配,吸收到 Store`);
|
|
2587
2947
|
if (!dryRun) {
|
|
2588
|
-
await options.
|
|
2589
|
-
|
|
2948
|
+
const absorbResult = await options.storeImporter.importLocalLibrary({
|
|
2949
|
+
libDir: localPath,
|
|
2950
|
+
libName,
|
|
2951
|
+
commit,
|
|
2952
|
+
branch: options.branch,
|
|
2953
|
+
url: options.url,
|
|
2954
|
+
sourceKind: 'local',
|
|
2955
|
+
});
|
|
2590
2956
|
return { action: 'absorbed', isGeneral: absorbResult.isGeneral, absorbResult };
|
|
2591
2957
|
}
|
|
2592
2958
|
return { action: 'absorbed' };
|
|
@@ -2595,6 +2961,7 @@ async function resolveLocalAndAbsorb(localPath, libName, commit, options) {
|
|
|
2595
2961
|
warn(`${indent} ${libName}: 本地 commit (${verifyResult.actualCommit?.slice(0, 7)}) 与预期 (${commit.slice(0, 7)}) 不匹配,将重新下载`);
|
|
2596
2962
|
if (!dryRun) {
|
|
2597
2963
|
await options.onMismatch?.(verifyResult.actualCommit);
|
|
2964
|
+
await options.onDelete?.();
|
|
2598
2965
|
await fs.rm(localPath, { recursive: true, force: true });
|
|
2599
2966
|
}
|
|
2600
2967
|
return { action: 'deleted' };
|
|
@@ -2603,8 +2970,14 @@ async function resolveLocalAndAbsorb(localPath, libName, commit, options) {
|
|
|
2603
2970
|
if (unverifiedStrategy === 'absorb') {
|
|
2604
2971
|
info(`${indent} ${libName}: 本地无 .git 目录,按配置直接吸收`);
|
|
2605
2972
|
if (!dryRun) {
|
|
2606
|
-
await options.
|
|
2607
|
-
|
|
2973
|
+
const absorbResult = await options.storeImporter.importLocalLibrary({
|
|
2974
|
+
libDir: localPath,
|
|
2975
|
+
libName,
|
|
2976
|
+
commit,
|
|
2977
|
+
branch: options.branch,
|
|
2978
|
+
url: options.url,
|
|
2979
|
+
sourceKind: 'local',
|
|
2980
|
+
});
|
|
2608
2981
|
return { action: 'absorbed', isGeneral: absorbResult.isGeneral, absorbResult };
|
|
2609
2982
|
}
|
|
2610
2983
|
return { action: 'absorbed' };
|
|
@@ -2612,6 +2985,7 @@ async function resolveLocalAndAbsorb(localPath, libName, commit, options) {
|
|
|
2612
2985
|
else {
|
|
2613
2986
|
warn(`${indent} ${libName}: 本地无 .git 目录,无法验证 commit,将重新下载`);
|
|
2614
2987
|
if (!dryRun) {
|
|
2988
|
+
await options.onDelete?.();
|
|
2615
2989
|
await fs.rm(localPath, { recursive: true, force: true });
|
|
2616
2990
|
}
|
|
2617
2991
|
return { action: 'deleted' };
|
|
@@ -2624,7 +2998,7 @@ async function resolveLocalAndAbsorb(localPath, libName, commit, options) {
|
|
|
2624
2998
|
*/
|
|
2625
2999
|
async function linkNestedDependencies(dependencies, params) {
|
|
2626
3000
|
const { targetDir: nestedTargetDir, context, options, indent } = params;
|
|
2627
|
-
const { tx, registry,
|
|
3001
|
+
const { tx, registry, projectRoot, dryRun, download, generalDependencyKeys, implicitStoreReferences, downloadedLibs, nestedLinkedDeps, } = options;
|
|
2628
3002
|
const { platforms, vars } = context;
|
|
2629
3003
|
if (!dryRun) {
|
|
2630
3004
|
await assertPathOutsideStore(nestedTargetDir, options.storePath, 'link-nested-dependencies');
|
|
@@ -2633,6 +3007,7 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2633
3007
|
const localPath = path.join(nestedTargetDir, dep.libName);
|
|
2634
3008
|
const storePath = await store.getStorePath();
|
|
2635
3009
|
const storeCommitPath = path.join(storePath, dep.libName, dep.commit);
|
|
3010
|
+
const implicitOwner = implicitReferenceOwner(dep, projectRoot, localPath, options.scope);
|
|
2636
3011
|
// 检查本地状态
|
|
2637
3012
|
let localExists = false;
|
|
2638
3013
|
let localIsSymlink = false;
|
|
@@ -2641,8 +3016,12 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2641
3016
|
localExists = true;
|
|
2642
3017
|
localIsSymlink = stat.isSymbolicLink();
|
|
2643
3018
|
}
|
|
2644
|
-
catch {
|
|
2645
|
-
|
|
3019
|
+
catch (err) {
|
|
3020
|
+
if (!isPathMissingError(err)) {
|
|
3021
|
+
error(`[link-action] 本地依赖状态读取失败: lib=${dep.libName}, commit=${dep.commit}, ` +
|
|
3022
|
+
`path=${localPath}, code=${err.code ?? 'unknown'}, message=${err.message}`);
|
|
3023
|
+
throw err;
|
|
3024
|
+
}
|
|
2646
3025
|
}
|
|
2647
3026
|
// 检查 Store 状态和历史记录
|
|
2648
3027
|
const unavailablePlatforms = getBlockedRequestedPlatforms(registry, dep, platforms, vars);
|
|
@@ -2698,8 +3077,10 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2698
3077
|
unverifiedStrategy: strategy,
|
|
2699
3078
|
dryRun,
|
|
2700
3079
|
indent,
|
|
2701
|
-
|
|
2702
|
-
|
|
3080
|
+
storeImporter: new StoreImportService(registry, storePath),
|
|
3081
|
+
branch: dep.branch,
|
|
3082
|
+
url: dep.url,
|
|
3083
|
+
onDelete: () => tx.preparePathReplacement(localPath, storeCommitPath),
|
|
2703
3084
|
});
|
|
2704
3085
|
if (resolveResult.action === 'absorbed' && !dryRun) {
|
|
2705
3086
|
if (resolveResult.isGeneral) {
|
|
@@ -2717,7 +3098,7 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2717
3098
|
debug(`${indent} ${dep.libName} - 本地吸收后平台回填: requested=${availablePlatforms.join(', ') || '无'}, absorbed=${absorbedPlatforms.join(', ') || '无'}, linkable=${existingPlatforms.join(', ') || '无'}`);
|
|
2718
3099
|
}
|
|
2719
3100
|
if (resolveResult.absorbResult?.nestedLibraries.length) {
|
|
2720
|
-
await registerNestedLibraries(resolveResult.absorbResult.nestedLibraries,
|
|
3101
|
+
await registerNestedLibraries(resolveResult.absorbResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
2721
3102
|
}
|
|
2722
3103
|
storeHas = isGeneral || existingPlatforms.length > 0;
|
|
2723
3104
|
// 吸收为 General 后,检查是否实际需要平台内容
|
|
@@ -2725,8 +3106,9 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2725
3106
|
if (resolveResult.isGeneral && download) {
|
|
2726
3107
|
const hasPlatformSparse = dep.sparse && !isSparseOnlyCommon(dep.sparse);
|
|
2727
3108
|
if (hasPlatformSparse && availablePlatforms.length > 0) {
|
|
3109
|
+
let platformDownload;
|
|
2728
3110
|
try {
|
|
2729
|
-
|
|
3111
|
+
platformDownload = await codepac.downloadToTemp({
|
|
2730
3112
|
url: dep.url,
|
|
2731
3113
|
commit: dep.commit,
|
|
2732
3114
|
branch: dep.branch,
|
|
@@ -2736,25 +3118,43 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2736
3118
|
vars,
|
|
2737
3119
|
useGitLightweightDownload: options.gitLightweightDownload,
|
|
2738
3120
|
});
|
|
2739
|
-
const assessment = assessDownloadResult(availablePlatforms, dep.sparse,
|
|
3121
|
+
const assessment = assessDownloadResult(availablePlatforms, dep.sparse, platformDownload, vars);
|
|
2740
3122
|
clearSatisfiedAutoUnavailablePlatforms(registry, dep, assessment.satisfiedRequested);
|
|
2741
3123
|
if (assessment.downloadedRequested.length > 0) {
|
|
2742
3124
|
// 有平台内容 → 吸收平台目录,重新分类为平台库
|
|
2743
|
-
await
|
|
2744
|
-
|
|
2745
|
-
|
|
3125
|
+
const nestedAbsorbResult = await new StoreImportService(registry, storePath).importLibrary({
|
|
3126
|
+
libDir: platformDownload.libDir,
|
|
3127
|
+
platforms: assessment.downloadedRequested,
|
|
3128
|
+
libName: dep.libName,
|
|
3129
|
+
commit: dep.commit,
|
|
3130
|
+
branch: dep.branch,
|
|
3131
|
+
url: dep.url,
|
|
3132
|
+
sourceKind: 'download',
|
|
3133
|
+
});
|
|
3134
|
+
await registerNestedLibraries(nestedAbsorbResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
2746
3135
|
existingPlatforms.push(...assessment.downloadedRequested);
|
|
2747
3136
|
isGeneral = false;
|
|
2748
3137
|
info(`${indent} ${dep.libName} - 补充平台内容 [${assessment.downloadedRequested.join(', ')}]`);
|
|
2749
3138
|
}
|
|
2750
|
-
if (
|
|
2751
|
-
hint(`${indent} 已过滤: ${
|
|
3139
|
+
if (platformDownload.cleanedPlatforms.length > 0) {
|
|
3140
|
+
hint(`${indent} 已过滤: ${platformDownload.cleanedPlatforms.join(', ')}`);
|
|
2752
3141
|
}
|
|
2753
3142
|
}
|
|
2754
|
-
catch {
|
|
3143
|
+
catch (err) {
|
|
3144
|
+
if (platformDownload)
|
|
3145
|
+
throw err;
|
|
2755
3146
|
// 下载失败,保持 General 分类
|
|
2756
3147
|
warn(`${indent} ${dep.libName} - 平台内容下载失败,保持 General 分类`);
|
|
2757
3148
|
}
|
|
3149
|
+
finally {
|
|
3150
|
+
if (platformDownload) {
|
|
3151
|
+
await cleanupDownloadTempDir(platformDownload.tempDir, {
|
|
3152
|
+
libName: dep.libName,
|
|
3153
|
+
commit: dep.commit,
|
|
3154
|
+
stage: 'nested-general-platform-import',
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
2758
3158
|
}
|
|
2759
3159
|
}
|
|
2760
3160
|
}
|
|
@@ -2763,7 +3163,12 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2763
3163
|
await fs.lstat(localPath);
|
|
2764
3164
|
localExists = true;
|
|
2765
3165
|
}
|
|
2766
|
-
catch {
|
|
3166
|
+
catch (err) {
|
|
3167
|
+
if (!isPathMissingError(err)) {
|
|
3168
|
+
error(`[link-action] 本地依赖更新后状态读取失败: lib=${dep.libName}, commit=${dep.commit}, ` +
|
|
3169
|
+
`path=${localPath}, code=${err.code ?? 'unknown'}, message=${err.message}`);
|
|
3170
|
+
throw err;
|
|
3171
|
+
}
|
|
2767
3172
|
localExists = false;
|
|
2768
3173
|
}
|
|
2769
3174
|
}
|
|
@@ -2777,32 +3182,32 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2777
3182
|
const supplementResult = await supplementMissingPlatforms(dep, platforms, registry, tx, { vars, useGitLightweightDownload: options.gitLightweightDownload, repairRequiredPlatforms });
|
|
2778
3183
|
// 注册嵌套依赖
|
|
2779
3184
|
if (supplementResult.nestedLibraries.length > 0) {
|
|
2780
|
-
await registerNestedLibraries(supplementResult.nestedLibraries,
|
|
3185
|
+
await registerNestedLibraries(supplementResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
2781
3186
|
}
|
|
2782
3187
|
// 如果补充了新平台,需要重新链接
|
|
2783
3188
|
if (supplementResult.downloaded.length > 0) {
|
|
2784
3189
|
success(`${indent} ${dep.libName} - 补充平台 [${supplementResult.downloaded.join(', ')}]`);
|
|
2785
|
-
// 删除现有链接
|
|
2786
|
-
if (!dryRun) {
|
|
2787
|
-
await fs.rm(localPath, { recursive: true, force: true });
|
|
2788
|
-
}
|
|
2789
3190
|
// 合并所有平台并重新链接
|
|
2790
3191
|
const allPlatforms = [...existingPlatforms, ...supplementResult.downloaded];
|
|
2791
3192
|
linkedPlatforms = allPlatforms;
|
|
2792
3193
|
if (!dryRun) {
|
|
2793
|
-
await tx
|
|
2794
|
-
await linker.linkLib(localPath, storeCommitPath, allPlatforms);
|
|
3194
|
+
await linkPlatformDirectory(tx, localPath, storeCommitPath, allPlatforms);
|
|
2795
3195
|
}
|
|
2796
3196
|
success(`${indent} ${dep.libName} - 重新链接完成 [${allPlatforms.join(', ')}]`);
|
|
2797
3197
|
}
|
|
2798
3198
|
}
|
|
2799
|
-
await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, linkedPlatforms,
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
3199
|
+
await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, linkedPlatforms, isGeneral);
|
|
3200
|
+
if (!isGeneral) {
|
|
3201
|
+
const { actualExisting: desiredNestedPlatforms } = await resolveRequestedStoreState(dep, platforms, vars);
|
|
3202
|
+
debug(`[link-registry] 嵌套平台清理准备: lib=${dep.libName}, commit=${dep.commit}, ` +
|
|
3203
|
+
`requested=${platforms.join(',')}, desired=${desiredNestedPlatforms.join(',')}, local=${localPath}`);
|
|
3204
|
+
await linker.cleanupLocalExtraPlatforms(localPath, desiredNestedPlatforms, (entryPath) => tx.preparePathReplacement(entryPath, 'nested-extra-platform-cleanup'));
|
|
3205
|
+
await tx.save();
|
|
3206
|
+
}
|
|
3207
|
+
if (isGeneral) {
|
|
3208
|
+
generalDependencyKeys.add(dependencyIdentityKey(dep, localPath));
|
|
3209
|
+
}
|
|
3210
|
+
await recordLinkedDependency(nestedLinkedDeps, dep, localPath, storeCommitPath, projectRoot, isGeneral);
|
|
2806
3211
|
if (!isGeneral) {
|
|
2807
3212
|
success(`${indent} ${dep.libName} - 已链接`);
|
|
2808
3213
|
}
|
|
@@ -2810,9 +3215,6 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2810
3215
|
}
|
|
2811
3216
|
if (linkStatus === 'relink') {
|
|
2812
3217
|
warn(`${indent} ${dep.libName} - 链接异常(断链或指向错误),将重新链接`);
|
|
2813
|
-
if (!dryRun) {
|
|
2814
|
-
await fs.rm(localPath, { recursive: true, force: true });
|
|
2815
|
-
}
|
|
2816
3218
|
localExists = false;
|
|
2817
3219
|
localIsSymlink = false;
|
|
2818
3220
|
}
|
|
@@ -2824,23 +3226,12 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2824
3226
|
info(`${indent} ${dep.libName} - 将链接到 Store`);
|
|
2825
3227
|
continue;
|
|
2826
3228
|
}
|
|
2827
|
-
if (localExists && !localIsSymlink) {
|
|
2828
|
-
// 本地是目录,先删除
|
|
2829
|
-
await fs.rm(localPath, { recursive: true, force: true });
|
|
2830
|
-
}
|
|
2831
3229
|
if (isGeneral) {
|
|
2832
3230
|
const sharedPath = path.join(storeCommitPath, '_shared');
|
|
2833
|
-
await tx
|
|
2834
|
-
await
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
// 记录到 nestedLinkedDeps
|
|
2838
|
-
nestedLinkedDeps.push({
|
|
2839
|
-
libName: dep.libName,
|
|
2840
|
-
commit: dep.commit,
|
|
2841
|
-
platform: GENERAL_PLATFORM,
|
|
2842
|
-
linkedPath: path.relative(projectRoot, localPath),
|
|
2843
|
-
});
|
|
3231
|
+
await linkGeneralDirectory(tx, localPath, sharedPath);
|
|
3232
|
+
await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, [GENERAL_PLATFORM], true);
|
|
3233
|
+
generalDependencyKeys.add(dependencyIdentityKey(dep, localPath));
|
|
3234
|
+
await recordLinkedDependency(nestedLinkedDeps, dep, localPath, storeCommitPath, projectRoot, true);
|
|
2844
3235
|
success(`${indent} ${dep.libName} - 链接完成 (General)`);
|
|
2845
3236
|
}
|
|
2846
3237
|
else {
|
|
@@ -2848,7 +3239,7 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2848
3239
|
const supplementResult = await supplementMissingPlatforms(dep, platforms, registry, tx, { vars, useGitLightweightDownload: options.gitLightweightDownload, repairRequiredPlatforms });
|
|
2849
3240
|
// 注册嵌套依赖
|
|
2850
3241
|
if (supplementResult.nestedLibraries.length > 0) {
|
|
2851
|
-
await registerNestedLibraries(supplementResult.nestedLibraries,
|
|
3242
|
+
await registerNestedLibraries(supplementResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
2852
3243
|
}
|
|
2853
3244
|
// 合并所有可链接的平台(已有 + 新下载)
|
|
2854
3245
|
const allPlatforms = [...existingPlatforms, ...supplementResult.downloaded];
|
|
@@ -2856,16 +3247,9 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2856
3247
|
if (supplementResult.downloaded.length > 0) {
|
|
2857
3248
|
success(`${indent} ${dep.libName} - 补充平台 [${supplementResult.downloaded.join(', ')}]`);
|
|
2858
3249
|
}
|
|
2859
|
-
await tx
|
|
2860
|
-
await
|
|
2861
|
-
await
|
|
2862
|
-
// 记录到 nestedLinkedDeps
|
|
2863
|
-
nestedLinkedDeps.push({
|
|
2864
|
-
libName: dep.libName,
|
|
2865
|
-
commit: dep.commit,
|
|
2866
|
-
platform: allPlatforms[0],
|
|
2867
|
-
linkedPath: path.relative(projectRoot, localPath),
|
|
2868
|
-
});
|
|
3250
|
+
await linkPlatformDirectory(tx, localPath, storeCommitPath, allPlatforms);
|
|
3251
|
+
await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, allPlatforms, false);
|
|
3252
|
+
await recordLinkedDependency(nestedLinkedDeps, dep, localPath, storeCommitPath, projectRoot, false);
|
|
2869
3253
|
success(`${indent} ${dep.libName} - 链接完成 [${allPlatforms.join(', ')}]`);
|
|
2870
3254
|
}
|
|
2871
3255
|
}
|
|
@@ -2882,6 +3266,8 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2882
3266
|
info(`${indent} ${dep.libName} - 将下载`);
|
|
2883
3267
|
continue;
|
|
2884
3268
|
}
|
|
3269
|
+
let downloadResult;
|
|
3270
|
+
let downloadTempDir;
|
|
2885
3271
|
try {
|
|
2886
3272
|
const nestedLibKey = registry.getLibraryKey(dep.libName, dep.commit);
|
|
2887
3273
|
const nestedHistoryLib = registry.getLibrary(nestedLibKey);
|
|
@@ -2890,7 +3276,6 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2890
3276
|
estimatedSize: nestedHistoryLib?.size,
|
|
2891
3277
|
getDirSize,
|
|
2892
3278
|
});
|
|
2893
|
-
let downloadResult;
|
|
2894
3279
|
try {
|
|
2895
3280
|
downloadResult = await codepac.downloadToTemp({
|
|
2896
3281
|
url: dep.url,
|
|
@@ -2901,13 +3286,18 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2901
3286
|
sparse: dep.sparse,
|
|
2902
3287
|
vars,
|
|
2903
3288
|
useGitLightweightDownload: options.gitLightweightDownload,
|
|
2904
|
-
onTempDirCreated: (
|
|
3289
|
+
onTempDirCreated: (tempDir, libDir) => {
|
|
3290
|
+
downloadTempDir = tempDir;
|
|
3291
|
+
downloadMonitor.start(libDir);
|
|
3292
|
+
},
|
|
2905
3293
|
onHeartbeat: (message) => { downloadMonitor.heartbeat(message); },
|
|
2906
3294
|
});
|
|
2907
3295
|
}
|
|
2908
3296
|
finally {
|
|
2909
3297
|
await downloadMonitor.stop();
|
|
2910
3298
|
}
|
|
3299
|
+
if (!downloadResult)
|
|
3300
|
+
throw new Error(`${dep.libName} 下载未返回结果`);
|
|
2911
3301
|
// 提示清理的平台(如果有)
|
|
2912
3302
|
if (downloadResult.cleanedPlatforms.length > 0) {
|
|
2913
3303
|
hint(`${indent} 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
|
|
@@ -2920,21 +3310,20 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2920
3310
|
platforms: [GENERAL_PLATFORM],
|
|
2921
3311
|
isGeneral: true,
|
|
2922
3312
|
});
|
|
2923
|
-
await
|
|
2924
|
-
|
|
2925
|
-
const sharedPath = path.join(storeCommitPath, '_shared');
|
|
2926
|
-
await tx.recordOp('link', localPath, sharedPath);
|
|
2927
|
-
await linker.linkGeneral(localPath, sharedPath);
|
|
2928
|
-
await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, [GENERAL_PLATFORM], projectHash, true);
|
|
2929
|
-
generalLibs.add(dep.libName);
|
|
2930
|
-
downloadedLibs.push(dep.libName);
|
|
2931
|
-
// 记录到 nestedLinkedDeps
|
|
2932
|
-
nestedLinkedDeps.push({
|
|
3313
|
+
await new StoreImportService(registry, storePath).importGeneral({
|
|
3314
|
+
libDir: downloadResult.libDir,
|
|
2933
3315
|
libName: dep.libName,
|
|
2934
3316
|
commit: dep.commit,
|
|
2935
|
-
|
|
2936
|
-
|
|
3317
|
+
branch: dep.branch,
|
|
3318
|
+
url: dep.url,
|
|
3319
|
+
sourceKind: 'download',
|
|
2937
3320
|
});
|
|
3321
|
+
const sharedPath = path.join(storeCommitPath, '_shared');
|
|
3322
|
+
await linkGeneralDirectory(tx, localPath, sharedPath);
|
|
3323
|
+
await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, [GENERAL_PLATFORM], true);
|
|
3324
|
+
generalDependencyKeys.add(dependencyIdentityKey(dep, localPath));
|
|
3325
|
+
downloadedLibs.push(dep.libName);
|
|
3326
|
+
await recordLinkedDependency(nestedLinkedDeps, dep, localPath, storeCommitPath, projectRoot, true);
|
|
2938
3327
|
if (repairTargets.includes(GENERAL_PLATFORM)) {
|
|
2939
3328
|
warn(`${indent} ${dep.libName} ${GENERAL_PLATFORM} 本地缓存不完整,已重新下载`);
|
|
2940
3329
|
}
|
|
@@ -2944,20 +3333,20 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2944
3333
|
}
|
|
2945
3334
|
else if (assessment.downloadedRequested.length > 0) {
|
|
2946
3335
|
// 平台库处理
|
|
2947
|
-
await
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
await tx.recordOp('link', localPath, storeCommitPath);
|
|
2951
|
-
await linker.linkLib(localPath, storeCommitPath, assessment.downloadedRequested);
|
|
2952
|
-
await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, assessment.downloadedRequested, projectHash, false);
|
|
2953
|
-
downloadedLibs.push(dep.libName);
|
|
2954
|
-
// 记录到 nestedLinkedDeps
|
|
2955
|
-
nestedLinkedDeps.push({
|
|
3336
|
+
const nestedAbsorbResult = await new StoreImportService(registry, storePath).importLibrary({
|
|
3337
|
+
libDir: downloadResult.libDir,
|
|
3338
|
+
platforms: assessment.downloadedRequested,
|
|
2956
3339
|
libName: dep.libName,
|
|
2957
3340
|
commit: dep.commit,
|
|
2958
|
-
|
|
2959
|
-
|
|
3341
|
+
branch: dep.branch,
|
|
3342
|
+
url: dep.url,
|
|
3343
|
+
sourceKind: 'download',
|
|
2960
3344
|
});
|
|
3345
|
+
await registerNestedLibraries(nestedAbsorbResult.nestedLibraries, implicitStoreReferences, implicitOwner);
|
|
3346
|
+
await linkPlatformDirectory(tx, localPath, storeCommitPath, assessment.downloadedRequested);
|
|
3347
|
+
await ensureLinkedRegistryState(dep.libName, dep.commit, dep.branch, dep.url, assessment.downloadedRequested, false);
|
|
3348
|
+
downloadedLibs.push(dep.libName);
|
|
3349
|
+
await recordLinkedDependency(nestedLinkedDeps, dep, localPath, storeCommitPath, projectRoot, false);
|
|
2961
3350
|
// 检查并记录新发现的不可用平台
|
|
2962
3351
|
const newUnavailable = assessment.unavailableRequested;
|
|
2963
3352
|
if (newUnavailable.length > 0) {
|
|
@@ -2986,15 +3375,11 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2986
3375
|
});
|
|
2987
3376
|
warn(`${indent} ${dep.libName} - 下载成功但平台 [${availablePlatforms.join(', ')}] 不可用,已记录`);
|
|
2988
3377
|
}
|
|
2989
|
-
// 清理临时目录
|
|
2990
|
-
try {
|
|
2991
|
-
await fs.rm(downloadResult.tempDir, { recursive: true, force: true });
|
|
2992
|
-
}
|
|
2993
|
-
catch {
|
|
2994
|
-
// 忽略清理错误
|
|
2995
|
-
}
|
|
2996
3378
|
}
|
|
2997
3379
|
catch (err) {
|
|
3380
|
+
if (downloadResult) {
|
|
3381
|
+
throw err;
|
|
3382
|
+
}
|
|
2998
3383
|
if (repairTargets.length > 0) {
|
|
2999
3384
|
error(`${indent} ${dep.libName} ${repairTargets.join(', ')} 本地缓存不完整,自动重新下载失败`);
|
|
3000
3385
|
hint(`${indent} 可运行 td reset ${dep.libName} 后重试`);
|
|
@@ -3005,6 +3390,16 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
3005
3390
|
warn(`${indent} ${dep.libName} - 下载失败: ${err.message}`);
|
|
3006
3391
|
}
|
|
3007
3392
|
}
|
|
3393
|
+
finally {
|
|
3394
|
+
const tempDir = downloadResult?.tempDir ?? downloadTempDir;
|
|
3395
|
+
if (tempDir) {
|
|
3396
|
+
await cleanupDownloadTempDir(tempDir, {
|
|
3397
|
+
libName: dep.libName,
|
|
3398
|
+
commit: dep.commit,
|
|
3399
|
+
stage: 'nested-download-import',
|
|
3400
|
+
});
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3008
3403
|
}
|
|
3009
3404
|
else {
|
|
3010
3405
|
warn(`${indent} ${dep.libName} - 缺失 (跳过下载)`);
|