tanmi-dock 0.9.3 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/dist/commands/check.d.ts +36 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +61 -7
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +10 -0
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/link.d.ts +3 -2
- package/dist/commands/link.d.ts.map +1 -1
- package/dist/commands/link.js +877 -598
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/reset.d.ts.map +1 -1
- package/dist/commands/reset.js +46 -17
- package/dist/commands/reset.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +145 -20
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/unavailable.d.ts.map +1 -1
- package/dist/commands/unavailable.js +7 -2
- package/dist/commands/unavailable.js.map +1 -1
- package/dist/core/codepac.d.ts +46 -0
- package/dist/core/codepac.d.ts.map +1 -1
- package/dist/core/codepac.js +599 -63
- package/dist/core/codepac.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +14 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/linker.d.ts +7 -0
- package/dist/core/linker.d.ts.map +1 -1
- package/dist/core/linker.js +47 -0
- package/dist/core/linker.js.map +1 -1
- package/dist/core/parser.d.ts +30 -5
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +375 -56
- package/dist/core/parser.js.map +1 -1
- package/dist/core/platform.js +2 -2
- package/dist/core/platform.js.map +1 -1
- package/dist/core/store.d.ts +2 -0
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +32 -6
- package/dist/core/store.js.map +1 -1
- package/dist/types/index.d.ts +63 -11
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -2
- package/dist/types/index.js.map +1 -1
- package/dist/utils/git.d.ts +20 -1
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +181 -4
- package/dist/utils/git.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/link.js
CHANGED
|
@@ -7,13 +7,13 @@ import { Command } from 'commander';
|
|
|
7
7
|
import { ensureInitialized } from '../core/guard.js';
|
|
8
8
|
import * as config from '../core/config.js';
|
|
9
9
|
import { setLogLevel } from '../utils/logger.js';
|
|
10
|
-
import { parseProjectDependencies, getRelativeConfigPath, parseCodepacDep, extractActions,
|
|
10
|
+
import { parseProjectDependencies, getRelativeConfigPath, parseCodepacDep, extractActions, buildActionExecutionPlan, extractNestedDependencies, findAllCodepacConfigs, extractDependencies, resolveProjectRootPath, } from '../core/parser.js';
|
|
11
11
|
import { getRegistry } from '../core/registry.js';
|
|
12
12
|
import * as store from '../core/store.js';
|
|
13
13
|
import * as linker from '../core/linker.js';
|
|
14
14
|
import * as codepac from '../core/codepac.js';
|
|
15
15
|
import { setProxyConfig } from '../core/codepac.js';
|
|
16
|
-
import { getPlatformHelpText, GENERAL_PLATFORM, SHARED_PLATFORM, pathsEqual, isSparseOnlyCommon, KNOWN_PLATFORM_VALUES, getRequestedPlatformTargets, } from '../core/platform.js';
|
|
16
|
+
import { getPlatformHelpText, GENERAL_PLATFORM, SHARED_PLATFORM, pathsEqual, isSparseOnlyCommon, KNOWN_PLATFORM_VALUES, getRequestedPlatformTargets, resolveSparseConfig, getBaseKeyForCodepac, } from '../core/platform.js';
|
|
17
17
|
import { Transaction } from '../core/transaction.js';
|
|
18
18
|
import { formatSize, checkDiskSpace } from '../utils/disk.js';
|
|
19
19
|
import { getDirSize } from '../utils/fs-utils.js';
|
|
@@ -37,6 +37,7 @@ export function createLinkCommand() {
|
|
|
37
37
|
.option('--no-download', '不自动下载缺失库')
|
|
38
38
|
.option('--dry-run', '只显示将要执行的操作')
|
|
39
39
|
.option('--config <configs...>', '指定可选配置文件 (如 codepac-dep-inner.json)')
|
|
40
|
+
.option('--skip-action, --skip_action, -sa <names...>', '按 action.name 跳过指定嵌套 action')
|
|
40
41
|
.option('--no-submodules', '不检测 git submodule 依赖')
|
|
41
42
|
.addHelpText('after', `${getPlatformHelpText()}
|
|
42
43
|
|
|
@@ -48,6 +49,7 @@ export function createLinkCommand() {
|
|
|
48
49
|
td link --dry-run 预览操作,不实际执行
|
|
49
50
|
td link -y 跳过确认,自动执行
|
|
50
51
|
td link --config codepac-dep-inner.json 使用指定的可选配置
|
|
52
|
+
td link --skip-action prepareNested 跳过指定 action.name
|
|
51
53
|
td link --no-submodules 跳过 git submodule 检测`)
|
|
52
54
|
.action(async (projectPath, options) => {
|
|
53
55
|
await ensureInitialized();
|
|
@@ -97,8 +99,20 @@ async function resolveRequestedStoreState(dependency, requestedPlatforms, vars)
|
|
|
97
99
|
if (await store.exists(dependency.libName, dependency.commit, platform)) {
|
|
98
100
|
resolvedExisting.push(platform);
|
|
99
101
|
}
|
|
102
|
+
else {
|
|
103
|
+
const registry = getRegistry();
|
|
104
|
+
const storeKey = registry.getStoreKey(dependency.libName, dependency.commit, platform);
|
|
105
|
+
if (registry.getStore(storeKey)) {
|
|
106
|
+
registry.removeStore(storeKey);
|
|
107
|
+
const storePath = await store.getStorePath();
|
|
108
|
+
const platformPath = store.getLibraryPath(storePath, dependency.libName, dependency.commit, platform);
|
|
109
|
+
await fs.rm(platformPath, { recursive: true, force: true }).catch(() => { });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
100
112
|
}
|
|
101
|
-
|
|
113
|
+
const result = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, resolvedExisting, vars);
|
|
114
|
+
debug(`[link-store-state] ${dependency.libName}@${dependency.commit.slice(0, 7)} 请求平台=${requestedPlatforms.join(', ') || '空'}, Store候选=${candidatePlatforms.join(', ') || '空'}, 实际存在=${result.actualExisting.join(', ') || '无'}, 满足请求=${result.satisfiedRequested.join(', ') || '无'}, 缺失请求=${result.missingRequested.join(', ') || '无'}`);
|
|
115
|
+
return result;
|
|
102
116
|
}
|
|
103
117
|
function getBlockedRequestedPlatforms(registry, dependency, requestedPlatforms, vars) {
|
|
104
118
|
const { manualPlatforms, autoPlatforms } = resolveUnavailablePlatforms(registry, dependency);
|
|
@@ -109,7 +123,9 @@ function getBlockedRequestedPlatforms(registry, dependency, requestedPlatforms,
|
|
|
109
123
|
const targets = getRequestedPlatformTargets(platform, dependency.sparse, vars);
|
|
110
124
|
return targets.length === 1 && targets[0] === platform;
|
|
111
125
|
});
|
|
112
|
-
|
|
126
|
+
const blocked = [...new Set([...manualBlocked, ...autoBlocked])];
|
|
127
|
+
debug(`[link-platforms] ${dependency.libName}@${dependency.commit.slice(0, 7)} 请求平台=${requestedPlatforms.join(', ') || '空'}, 手动跳过=${manualBlocked.join(', ') || '无'}, 自动跳过=${autoBlocked.join(', ') || '无'}, 最终跳过=${blocked.join(', ') || '无'}`);
|
|
128
|
+
return blocked;
|
|
113
129
|
}
|
|
114
130
|
function clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested) {
|
|
115
131
|
if (satisfiedRequested.length === 0)
|
|
@@ -154,12 +170,93 @@ function upsertLibraryAvailability(dependency, updates) {
|
|
|
154
170
|
lastAccess: new Date().toISOString(),
|
|
155
171
|
});
|
|
156
172
|
}
|
|
157
|
-
export async function resolveLibraryGeneralState(dependency) {
|
|
173
|
+
export async function resolveLibraryGeneralState(dependency, requestedPlatforms = [], vars) {
|
|
158
174
|
const registry = getRegistry();
|
|
159
175
|
const libKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
160
176
|
const library = registry.getLibrary(libKey);
|
|
177
|
+
const sparseConfig = resolveSparseConfig(dependency.sparse, vars);
|
|
178
|
+
const hasPlatformSparse = sparseConfig
|
|
179
|
+
? Object.keys(sparseConfig).some((key) => key !== 'common')
|
|
180
|
+
: Boolean(dependency.sparse && !isSparseOnlyCommon(dependency.sparse));
|
|
181
|
+
const hasPlatformLibraryHistory = requestedPlatforms.length > 0
|
|
182
|
+
&& library?.isGeneral === false
|
|
183
|
+
&& library.platforms.some((platform) => platform !== GENERAL_PLATFORM);
|
|
184
|
+
if (requestedPlatforms.length > 0) {
|
|
185
|
+
const requestedState = await resolveRequestedStoreState(dependency, requestedPlatforms, vars);
|
|
186
|
+
if (requestedState.actualExisting.length > 0) {
|
|
187
|
+
if (library?.isGeneral === true) {
|
|
188
|
+
registry.updateLibrary(libKey, {
|
|
189
|
+
platforms: requestedState.actualExisting,
|
|
190
|
+
isGeneral: false,
|
|
191
|
+
lastAccess: new Date().toISOString(),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (requestedPlatforms.length > 0 && library?.isGeneral === true) {
|
|
198
|
+
const requestedTargets = [...new Set(requestedPlatforms.flatMap((platform) => getRequestedPlatformTargets(platform, dependency.sparse, vars)))];
|
|
199
|
+
const storePath = await store.getStorePath();
|
|
200
|
+
const commitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
201
|
+
try {
|
|
202
|
+
const entries = await fs.readdir(commitPath, { withFileTypes: true });
|
|
203
|
+
let hasRequestedPlatformDir = false;
|
|
204
|
+
for (const entry of entries) {
|
|
205
|
+
if (!entry.isDirectory() || !requestedTargets.includes(entry.name))
|
|
206
|
+
continue;
|
|
207
|
+
if (await store.exists(dependency.libName, dependency.commit, entry.name)) {
|
|
208
|
+
hasRequestedPlatformDir = true;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (hasRequestedPlatformDir) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// Store 目录不存在时继续使用历史记录或后续 General 检测
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (requestedPlatforms.length > 0 && hasPlatformSparse) {
|
|
221
|
+
if (library?.isGeneral === true) {
|
|
222
|
+
registry.updateLibrary(libKey, {
|
|
223
|
+
isGeneral: false,
|
|
224
|
+
lastAccess: new Date().toISOString(),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
if (hasPlatformLibraryHistory) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
161
232
|
if (typeof library?.isGeneral === 'boolean') {
|
|
162
|
-
|
|
233
|
+
if (library.isGeneral) {
|
|
234
|
+
const hasGeneralContent = await store.exists(dependency.libName, dependency.commit, GENERAL_PLATFORM);
|
|
235
|
+
if (!hasGeneralContent) {
|
|
236
|
+
registry.updateLibrary(libKey, {
|
|
237
|
+
isGeneral: false,
|
|
238
|
+
lastAccess: new Date().toISOString(),
|
|
239
|
+
});
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
const actualGeneral = await store.isGeneralLib(dependency.libName, dependency.commit);
|
|
243
|
+
if (!actualGeneral) {
|
|
244
|
+
registry.updateLibrary(libKey, {
|
|
245
|
+
isGeneral: false,
|
|
246
|
+
lastAccess: new Date().toISOString(),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return actualGeneral;
|
|
250
|
+
}
|
|
251
|
+
const actualGeneral = await store.isGeneralLib(dependency.libName, dependency.commit);
|
|
252
|
+
if (actualGeneral) {
|
|
253
|
+
registry.updateLibrary(libKey, {
|
|
254
|
+
platforms: [GENERAL_PLATFORM],
|
|
255
|
+
isGeneral: true,
|
|
256
|
+
lastAccess: new Date().toISOString(),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return actualGeneral;
|
|
163
260
|
}
|
|
164
261
|
return store.isGeneralLib(dependency.libName, dependency.commit);
|
|
165
262
|
}
|
|
@@ -173,7 +270,7 @@ export function resolveUnavailablePlatforms(registry, dependency) {
|
|
|
173
270
|
autoPlatforms,
|
|
174
271
|
};
|
|
175
272
|
}
|
|
176
|
-
export async function collectProjectDependencyGraph(projectRoot) {
|
|
273
|
+
export async function collectProjectDependencyGraph(projectRoot, codepacPlatforms = []) {
|
|
177
274
|
const { normalizedPath, configPath } = await resolveProjectRootPath(projectRoot);
|
|
178
275
|
if (!configPath) {
|
|
179
276
|
throw new Error('只能在项目目录下使用');
|
|
@@ -182,11 +279,17 @@ export async function collectProjectDependencyGraph(projectRoot) {
|
|
|
182
279
|
const seenDeps = new Set();
|
|
183
280
|
const visitedConfigs = new Set();
|
|
184
281
|
const queue = [
|
|
185
|
-
{ configPath, source: 'direct' },
|
|
282
|
+
{ configPath, targetDir: path.dirname(configPath), codepacPlatforms, source: 'direct' },
|
|
186
283
|
];
|
|
187
|
-
const submodules = await findSubmoduleConfigs(normalizedPath);
|
|
284
|
+
const submodules = await findSubmoduleConfigs(normalizedPath, '', 0, codepacPlatforms);
|
|
188
285
|
for (const submodule of submodules) {
|
|
189
|
-
queue.push({
|
|
286
|
+
queue.push({
|
|
287
|
+
configPath: submodule.configPath,
|
|
288
|
+
targetDir: path.dirname(submodule.configPath),
|
|
289
|
+
codepacPlatforms,
|
|
290
|
+
scope: submodule.relativePath,
|
|
291
|
+
source: 'submodule',
|
|
292
|
+
});
|
|
190
293
|
}
|
|
191
294
|
while (queue.length > 0) {
|
|
192
295
|
const current = queue.shift();
|
|
@@ -200,25 +303,32 @@ export async function collectProjectDependencyGraph(projectRoot) {
|
|
|
200
303
|
catch {
|
|
201
304
|
continue;
|
|
202
305
|
}
|
|
203
|
-
|
|
306
|
+
const extractOptions = { platforms: current.codepacPlatforms, configPath: current.configPath };
|
|
307
|
+
for (const dependency of extractDependencies(parsedConfig, extractOptions)) {
|
|
204
308
|
const key = `${dependency.libName}:${dependency.commit}:${current.scope ?? ''}`;
|
|
205
309
|
if (!seenDeps.has(key)) {
|
|
206
310
|
seenDeps.add(key);
|
|
207
311
|
results.push({ ...dependency, scope: current.scope, source: current.source });
|
|
208
312
|
}
|
|
209
313
|
}
|
|
210
|
-
for (const action of extractActions(parsedConfig)) {
|
|
211
|
-
let
|
|
314
|
+
for (const action of extractActions(parsedConfig, extractOptions)) {
|
|
315
|
+
let plan;
|
|
212
316
|
try {
|
|
213
|
-
|
|
317
|
+
plan = buildActionExecutionPlan(action, {
|
|
318
|
+
parentConfigPath: current.configPath,
|
|
319
|
+
parentTargetDir: current.targetDir,
|
|
320
|
+
inheritedCodepacPlatforms: current.codepacPlatforms,
|
|
321
|
+
});
|
|
214
322
|
}
|
|
215
323
|
catch {
|
|
216
324
|
continue;
|
|
217
325
|
}
|
|
218
|
-
const nestedConfigPath = path.resolve(path.dirname(current.configPath), parsedAction.configDir, 'codepac-dep.json');
|
|
219
326
|
let nested;
|
|
220
327
|
try {
|
|
221
|
-
nested = await extractNestedDependencies(nestedConfigPath,
|
|
328
|
+
nested = await extractNestedDependencies(plan.nestedConfigPath, plan.libraries, {
|
|
329
|
+
platforms: plan.effectiveCodepacPlatforms,
|
|
330
|
+
configPath: plan.nestedConfigPath,
|
|
331
|
+
});
|
|
222
332
|
}
|
|
223
333
|
catch {
|
|
224
334
|
continue;
|
|
@@ -230,7 +340,15 @@ export async function collectProjectDependencyGraph(projectRoot) {
|
|
|
230
340
|
results.push({ ...dependency, scope: current.scope, source: 'nested' });
|
|
231
341
|
}
|
|
232
342
|
}
|
|
233
|
-
|
|
343
|
+
if (!plan.parsed.disableAction) {
|
|
344
|
+
queue.push({
|
|
345
|
+
configPath: plan.nestedConfigPath,
|
|
346
|
+
targetDir: plan.nestedTargetDir,
|
|
347
|
+
codepacPlatforms: plan.effectiveCodepacPlatforms,
|
|
348
|
+
scope: current.scope,
|
|
349
|
+
source: 'nested',
|
|
350
|
+
});
|
|
351
|
+
}
|
|
234
352
|
}
|
|
235
353
|
}
|
|
236
354
|
return results;
|
|
@@ -276,14 +394,25 @@ async function selectSubmodules(configs, options, remembered) {
|
|
|
276
394
|
}
|
|
277
395
|
return selected.map(s => ({ ...s }));
|
|
278
396
|
}
|
|
397
|
+
function buildSubmoduleOptionalConfigState(submodules) {
|
|
398
|
+
const state = {};
|
|
399
|
+
for (const submodule of submodules) {
|
|
400
|
+
const names = submodule.selectedOptionalConfigs?.map((config) => config.name) ?? [];
|
|
401
|
+
if (names.length > 0) {
|
|
402
|
+
state[submodule.relativePath] = names;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return Object.keys(state).length > 0 ? state : undefined;
|
|
406
|
+
}
|
|
279
407
|
/**
|
|
280
408
|
* 单个 scope(主项目或 submodule)的链接处理
|
|
281
409
|
*
|
|
282
410
|
* 包含:解析依赖、分类、预扫描平台、下载确认、逐个处理、并行下载、嵌套 actions
|
|
283
411
|
*/
|
|
284
412
|
async function linkScope(params) {
|
|
285
|
-
const { scopeName, configPath, platforms, scanExtraPlatforms, registry, tx, projectHash, projectRoot, storePath, download, dryRun, yes, concurrency, optionalConfigs, scope, } = params;
|
|
413
|
+
const { scopeName, configPath, platforms, scanExtraPlatforms, registry, tx, projectHash, projectRoot, storePath, download, dryRun, yes, concurrency, gitLightweightDownload, optionalConfigs, scope, } = params;
|
|
286
414
|
let finalLinkPlatforms = [...params.finalLinkPlatforms];
|
|
415
|
+
const codepacPlatforms = [...new Set(platforms.map((platform) => getBaseKeyForCodepac(platform)))];
|
|
287
416
|
// 记录 General 类型库
|
|
288
417
|
const generalLibs = new Set();
|
|
289
418
|
const downloadedLibs = [];
|
|
@@ -292,17 +421,32 @@ async function linkScope(params) {
|
|
|
292
421
|
info(`分析 ${scopeName}: ${configPath}`);
|
|
293
422
|
let dependencies;
|
|
294
423
|
let configVars;
|
|
424
|
+
const selectedActions = [];
|
|
425
|
+
const cacheConfigPaths = [configPath];
|
|
295
426
|
try {
|
|
296
|
-
const result = await parseProjectDependencies(path.dirname(path.dirname(configPath))
|
|
427
|
+
const result = await parseProjectDependencies(path.dirname(path.dirname(configPath)), {
|
|
428
|
+
platforms: codepacPlatforms,
|
|
429
|
+
});
|
|
297
430
|
dependencies = result.dependencies;
|
|
298
431
|
configVars = result.vars;
|
|
432
|
+
const topLevelConfig = await parseCodepacDep(configPath);
|
|
433
|
+
selectedActions.push(...extractActions(topLevelConfig, { platforms: codepacPlatforms, configPath }));
|
|
299
434
|
// 合并可选配置依赖
|
|
300
435
|
if (optionalConfigs && optionalConfigs.length > 0) {
|
|
301
436
|
for (const optionalConfig of optionalConfigs) {
|
|
302
437
|
try {
|
|
303
438
|
const optionalResult = await parseCodepacDep(optionalConfig.path);
|
|
304
|
-
const optionalDeps = extractDependencies(optionalResult
|
|
439
|
+
const optionalDeps = extractDependencies(optionalResult, {
|
|
440
|
+
platforms: codepacPlatforms,
|
|
441
|
+
configPath: optionalConfig.path,
|
|
442
|
+
});
|
|
305
443
|
dependencies = mergeDepLists(dependencies, optionalDeps);
|
|
444
|
+
configVars = { ...configVars, ...optionalResult.vars };
|
|
445
|
+
selectedActions.push(...extractActions(optionalResult, {
|
|
446
|
+
platforms: codepacPlatforms,
|
|
447
|
+
configPath: optionalConfig.path,
|
|
448
|
+
}));
|
|
449
|
+
cacheConfigPaths.push(optionalConfig.path);
|
|
306
450
|
info(` + ${optionalConfig.name}: ${optionalDeps.length} 个依赖`);
|
|
307
451
|
}
|
|
308
452
|
catch (optErr) {
|
|
@@ -456,561 +600,606 @@ async function linkScope(params) {
|
|
|
456
600
|
}
|
|
457
601
|
}
|
|
458
602
|
// 7. 逐个处理依赖(LINKED/RELINK/REPLACE/ABSORB/LINK_NEW)
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
}
|
|
603
|
+
for (const item of classified) {
|
|
604
|
+
const { dependency, status, localPath } = item;
|
|
605
|
+
await store.ensureCompatibleStore(storePath, dependency.libName, dependency.commit);
|
|
606
|
+
switch (status) {
|
|
607
|
+
case DependencyStatus.LINKED: {
|
|
608
|
+
const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
|
|
609
|
+
const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
|
|
610
|
+
const isLinkedGeneral = await resolveLibraryGeneralState(dependency, requestedPlatforms, configVars);
|
|
611
|
+
if (!isLinkedGeneral) {
|
|
612
|
+
const supplementResult = await supplementMissingPlatforms(dependency, platforms, registry, tx, {
|
|
613
|
+
vars: configVars,
|
|
614
|
+
useGitLightweightDownload: gitLightweightDownload,
|
|
615
|
+
});
|
|
616
|
+
await registerNestedLibraries(supplementResult.nestedLibraries, projectHash);
|
|
617
|
+
if (supplementResult.downloaded.length > 0) {
|
|
618
|
+
const linkedCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
619
|
+
const { actualExisting: allExisting, satisfiedRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
620
|
+
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
621
|
+
tx.recordOp('link', localPath, linkedCommitPath);
|
|
622
|
+
await linker.linkLib(localPath, linkedCommitPath, allExisting);
|
|
623
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, allExisting, projectHash, false);
|
|
624
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已补充平台 [${supplementResult.downloaded.join(', ')}]`);
|
|
481
625
|
}
|
|
482
|
-
const { actualExisting: linkedExisting, satisfiedRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
483
|
-
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
484
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, isLinkedGeneral ? [GENERAL_PLATFORM] : linkedExisting, projectHash, isLinkedGeneral);
|
|
485
|
-
break;
|
|
486
626
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
627
|
+
const { actualExisting: linkedExisting, satisfiedRequested } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
628
|
+
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
629
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, isLinkedGeneral ? [GENERAL_PLATFORM] : linkedExisting, projectHash, isLinkedGeneral);
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
case DependencyStatus.RELINK: {
|
|
633
|
+
const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
|
|
634
|
+
const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
|
|
635
|
+
const relinkCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
636
|
+
const isRelinkGeneral = await resolveLibraryGeneralState(dependency, requestedPlatforms, configVars);
|
|
637
|
+
if (isRelinkGeneral) {
|
|
638
|
+
tx.recordOp('unlink', localPath);
|
|
639
|
+
await linker.unlink(localPath);
|
|
640
|
+
const sharedPath = path.join(relinkCommitPath, '_shared');
|
|
641
|
+
tx.recordOp('link', localPath, sharedPath);
|
|
642
|
+
await linker.linkGeneral(localPath, sharedPath);
|
|
643
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], projectHash, true);
|
|
644
|
+
generalLibs.add(dependency.libName);
|
|
645
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,重建链接`);
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
const relinkSupplementResult = await supplementMissingPlatforms(dependency, platforms, registry, tx, {
|
|
649
|
+
vars: configVars,
|
|
650
|
+
useGitLightweightDownload: gitLightweightDownload,
|
|
651
|
+
});
|
|
652
|
+
await registerNestedLibraries(relinkSupplementResult.nestedLibraries, projectHash);
|
|
653
|
+
const { actualExisting: relinkExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
654
|
+
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
655
|
+
if (relinkExisting.length === 0) {
|
|
656
|
+
const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
|
|
657
|
+
const relinkCommitEntries = await fs.readdir(relinkCommitPath, { withFileTypes: true });
|
|
658
|
+
const relinkAvailablePlatforms = relinkCommitEntries
|
|
659
|
+
.filter(e => e.isDirectory() && e.name !== '_shared' && KNOWN_PLATFORM_VALUES.includes(e.name))
|
|
660
|
+
.map(e => e.name);
|
|
661
|
+
warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已按规则跳过平台 [${platforms.join('/')}], 可用平台 [${relinkAvailablePlatforms.join(', ')}]`);
|
|
662
|
+
const relinkLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
663
|
+
const relinkLib = registry.getLibrary(relinkLibKey);
|
|
664
|
+
if (relinkLib) {
|
|
665
|
+
const unavailable = relinkLib.unavailablePlatforms || [];
|
|
666
|
+
const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, relinkAvailablePlatforms, configVars).missingRequested;
|
|
667
|
+
for (const requestedPlatform of missingRequested) {
|
|
668
|
+
if (!unavailable.includes(requestedPlatform))
|
|
669
|
+
unavailable.push(requestedPlatform);
|
|
524
670
|
}
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
tx.recordOp('unlink', localPath);
|
|
528
|
-
await linker.unlink(localPath);
|
|
529
|
-
tx.recordOp('link', localPath, relinkCommitPath);
|
|
530
|
-
await linker.linkLib(localPath, relinkCommitPath, relinkExisting);
|
|
531
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, relinkExisting, projectHash, false);
|
|
532
|
-
if (relinkSupplementResult.downloaded.length > 0) {
|
|
533
|
-
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 重建链接并补充平台 [${relinkExisting.join(', ')}]`);
|
|
534
|
-
}
|
|
535
|
-
else {
|
|
536
|
-
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 重建链接 [${relinkExisting.join(', ')}]`);
|
|
671
|
+
registry.updateLibrary(relinkLibKey, { unavailablePlatforms: unavailable });
|
|
537
672
|
}
|
|
673
|
+
break;
|
|
538
674
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const isReplaceGeneral = await resolveLibraryGeneralState(dependency);
|
|
547
|
-
if (isReplaceGeneral) {
|
|
548
|
-
const sharedPath = path.join(replaceCommitPath, '_shared');
|
|
549
|
-
tx.recordOp('replace', localPath, sharedPath);
|
|
550
|
-
await linker.linkGeneral(localPath, sharedPath);
|
|
551
|
-
savedBytes += replaceSize;
|
|
552
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], projectHash, true);
|
|
553
|
-
generalLibs.add(dependency.libName);
|
|
554
|
-
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,创建链接`);
|
|
675
|
+
tx.recordOp('unlink', localPath);
|
|
676
|
+
await linker.unlink(localPath);
|
|
677
|
+
tx.recordOp('link', localPath, relinkCommitPath);
|
|
678
|
+
await linker.linkLib(localPath, relinkCommitPath, relinkExisting);
|
|
679
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, relinkExisting, projectHash, false);
|
|
680
|
+
if (relinkSupplementResult.downloaded.length > 0) {
|
|
681
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 重建链接并补充平台 [${relinkExisting.join(', ')}]`);
|
|
555
682
|
}
|
|
556
683
|
else {
|
|
557
|
-
|
|
558
|
-
await registerNestedLibraries(replaceSupplementResult.nestedLibraries, projectHash);
|
|
559
|
-
const { actualExisting: replaceExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
560
|
-
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
561
|
-
if (replaceExisting.length === 0) {
|
|
562
|
-
const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
|
|
563
|
-
const replaceCommitEntries = await fs.readdir(replaceCommitPath, { withFileTypes: true });
|
|
564
|
-
const replaceAvailablePlatforms = replaceCommitEntries
|
|
565
|
-
.filter(e => e.isDirectory() && e.name !== '_shared' && KNOWN_PLATFORM_VALUES.includes(e.name))
|
|
566
|
-
.map(e => e.name);
|
|
567
|
-
warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已按规则跳过平台 [${platforms.join('/')}], 可用平台 [${replaceAvailablePlatforms.join(', ')}]`);
|
|
568
|
-
const replaceLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
569
|
-
const replaceLib = registry.getLibrary(replaceLibKey);
|
|
570
|
-
if (replaceLib) {
|
|
571
|
-
const unavailable = replaceLib.unavailablePlatforms || [];
|
|
572
|
-
const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, replaceAvailablePlatforms, configVars).missingRequested;
|
|
573
|
-
for (const requestedPlatform of missingRequested) {
|
|
574
|
-
if (!unavailable.includes(requestedPlatform))
|
|
575
|
-
unavailable.push(requestedPlatform);
|
|
576
|
-
}
|
|
577
|
-
registry.updateLibrary(replaceLibKey, { unavailablePlatforms: unavailable });
|
|
578
|
-
}
|
|
579
|
-
break;
|
|
580
|
-
}
|
|
581
|
-
tx.recordOp('replace', localPath, replaceCommitPath);
|
|
582
|
-
await linker.linkLib(localPath, replaceCommitPath, replaceExisting);
|
|
583
|
-
savedBytes += replaceSize;
|
|
584
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, replaceExisting, projectHash, false);
|
|
585
|
-
if (replaceSupplementResult.downloaded.length > 0) {
|
|
586
|
-
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - Store 已有,创建链接并补充平台 [${replaceExisting.join(', ')}]`);
|
|
587
|
-
}
|
|
588
|
-
else {
|
|
589
|
-
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - Store 已有,创建链接 [${replaceExisting.join(', ')}]`);
|
|
590
|
-
}
|
|
684
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 重建链接 [${relinkExisting.join(', ')}]`);
|
|
591
685
|
}
|
|
592
|
-
break;
|
|
593
686
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
case DependencyStatus.REPLACE: {
|
|
690
|
+
const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
|
|
691
|
+
const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
|
|
692
|
+
const replaceSize = await getDirSize(localPath);
|
|
693
|
+
const replaceCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
694
|
+
const isReplaceGeneral = await resolveLibraryGeneralState(dependency, requestedPlatforms, configVars);
|
|
695
|
+
if (isReplaceGeneral) {
|
|
696
|
+
const sharedPath = path.join(replaceCommitPath, '_shared');
|
|
697
|
+
tx.recordOp('replace', localPath, sharedPath);
|
|
698
|
+
await linker.linkGeneral(localPath, sharedPath);
|
|
699
|
+
savedBytes += replaceSize;
|
|
700
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], projectHash, true);
|
|
701
|
+
generalLibs.add(dependency.libName);
|
|
702
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,创建链接`);
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
const replaceSupplementResult = await supplementMissingPlatforms(dependency, platforms, registry, tx, {
|
|
706
|
+
vars: configVars,
|
|
707
|
+
useGitLightweightDownload: gitLightweightDownload,
|
|
708
|
+
});
|
|
709
|
+
await registerNestedLibraries(replaceSupplementResult.nestedLibraries, projectHash);
|
|
710
|
+
const { actualExisting: replaceExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
711
|
+
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
712
|
+
if (replaceExisting.length === 0) {
|
|
713
|
+
const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
|
|
714
|
+
const replaceCommitEntries = await fs.readdir(replaceCommitPath, { withFileTypes: true });
|
|
715
|
+
const replaceAvailablePlatforms = replaceCommitEntries
|
|
716
|
+
.filter(e => e.isDirectory() && e.name !== '_shared' && KNOWN_PLATFORM_VALUES.includes(e.name))
|
|
717
|
+
.map(e => e.name);
|
|
718
|
+
warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已按规则跳过平台 [${platforms.join('/')}], 可用平台 [${replaceAvailablePlatforms.join(', ')}]`);
|
|
719
|
+
const replaceLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
720
|
+
const replaceLib = registry.getLibrary(replaceLibKey);
|
|
721
|
+
if (replaceLib) {
|
|
722
|
+
const unavailable = replaceLib.unavailablePlatforms || [];
|
|
723
|
+
const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, replaceAvailablePlatforms, configVars).missingRequested;
|
|
612
724
|
for (const requestedPlatform of missingRequested) {
|
|
613
725
|
if (!unavailable.includes(requestedPlatform))
|
|
614
726
|
unavailable.push(requestedPlatform);
|
|
615
727
|
}
|
|
616
|
-
registry.updateLibrary(
|
|
728
|
+
registry.updateLibrary(replaceLibKey, { unavailablePlatforms: unavailable });
|
|
617
729
|
}
|
|
618
730
|
break;
|
|
619
731
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
732
|
+
tx.recordOp('replace', localPath, replaceCommitPath);
|
|
733
|
+
await linker.linkLib(localPath, replaceCommitPath, replaceExisting);
|
|
734
|
+
savedBytes += replaceSize;
|
|
735
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, replaceExisting, projectHash, false);
|
|
736
|
+
if (replaceSupplementResult.downloaded.length > 0) {
|
|
737
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - Store 已有,创建链接并补充平台 [${replaceExisting.join(', ')}]`);
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - Store 已有,创建链接 [${replaceExisting.join(', ')}]`);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
case DependencyStatus.ABSORB: {
|
|
746
|
+
const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
|
|
747
|
+
const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
|
|
748
|
+
const storeCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
749
|
+
const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
|
|
750
|
+
const localDirEntries = await fs.readdir(localPath, { withFileTypes: true });
|
|
751
|
+
const localPlatforms = localDirEntries
|
|
752
|
+
.filter(entry => entry.isDirectory() && KNOWN_PLATFORM_VALUES.includes(entry.name))
|
|
753
|
+
.map(entry => entry.name);
|
|
754
|
+
const requestedTargets = [...new Set(requestedPlatforms.flatMap((platform) => getRequestedPlatformTargets(platform, dependency.sparse, configVars)))];
|
|
755
|
+
const finalPlatforms = localPlatforms.filter((platform) => requestedTargets.includes(platform));
|
|
756
|
+
if (finalPlatforms.length === 0 && localPlatforms.length > 0) {
|
|
757
|
+
warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 已按规则跳过平台 [${platforms.join('/')}], 本地有 [${localPlatforms.join(', ')}]`);
|
|
640
758
|
const absorbLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
759
|
+
const absorbLib = registry.getLibrary(absorbLibKey);
|
|
760
|
+
if (absorbLib) {
|
|
761
|
+
const unavailable = absorbLib.unavailablePlatforms || [];
|
|
762
|
+
const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, localPlatforms, configVars).missingRequested;
|
|
763
|
+
for (const requestedPlatform of missingRequested) {
|
|
764
|
+
if (!unavailable.includes(requestedPlatform))
|
|
765
|
+
unavailable.push(requestedPlatform);
|
|
766
|
+
}
|
|
767
|
+
registry.updateLibrary(absorbLibKey, { unavailablePlatforms: unavailable });
|
|
648
768
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
info(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 正在分析...`);
|
|
772
|
+
const absorbSize = await getDirSize(localPath);
|
|
773
|
+
const progressTracker = new ProgressTracker({
|
|
774
|
+
name: ` 移入 Store`, total: absorbSize, showSpeed: true,
|
|
775
|
+
});
|
|
776
|
+
tx.recordOp('absorb', storeCommitPath, localPath);
|
|
777
|
+
let progressStarted = false;
|
|
778
|
+
const absorbResult = await store.absorbLib(localPath, finalPlatforms, dependency.libName, dependency.commit, {
|
|
779
|
+
totalSize: absorbSize,
|
|
780
|
+
onProgress: (copied, _total) => {
|
|
781
|
+
if (!progressStarted) {
|
|
782
|
+
progressStarted = true;
|
|
783
|
+
progressTracker.start();
|
|
660
784
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
785
|
+
progressTracker.update(copied);
|
|
786
|
+
},
|
|
787
|
+
});
|
|
788
|
+
if (progressStarted)
|
|
789
|
+
progressTracker.stop();
|
|
790
|
+
let absorbLinkPlatforms = [...Object.keys(absorbResult.platformPaths), ...absorbResult.skippedPlatforms];
|
|
791
|
+
const absorbLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
792
|
+
if (!registry.getLibrary(absorbLibKey)) {
|
|
793
|
+
registry.addLibrary({
|
|
794
|
+
libName: dependency.libName, commit: dependency.commit, branch: dependency.branch,
|
|
795
|
+
url: dependency.url, platforms: absorbLinkPlatforms, size: absorbSize,
|
|
796
|
+
isGeneral: false,
|
|
797
|
+
referencedBy: [], createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
if (absorbLinkPlatforms.length > 0) {
|
|
801
|
+
for (const platform of absorbLinkPlatforms) {
|
|
802
|
+
const storeKey = registry.getStoreKey(dependency.libName, dependency.commit, platform);
|
|
803
|
+
if (!registry.getStore(storeKey)) {
|
|
804
|
+
const integrity = await store.captureIntegrity(dependency.libName, dependency.commit, platform);
|
|
805
|
+
registry.addStore({
|
|
806
|
+
libName: dependency.libName, commit: dependency.commit, platform,
|
|
807
|
+
branch: dependency.branch, url: dependency.url, ...integrity,
|
|
808
|
+
usedBy: [], createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
|
|
809
|
+
});
|
|
667
810
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
811
|
+
}
|
|
812
|
+
await registerSharedStore(dependency.libName, dependency.commit, dependency.branch, dependency.url);
|
|
813
|
+
await registerNestedLibraries(absorbResult.nestedLibraries, projectHash);
|
|
814
|
+
const absorbSupplementResult = await supplementMissingPlatforms(dependency, platforms, registry, tx, {
|
|
815
|
+
vars: configVars,
|
|
816
|
+
useGitLightweightDownload: gitLightweightDownload,
|
|
817
|
+
});
|
|
818
|
+
await registerNestedLibraries(absorbSupplementResult.nestedLibraries, projectHash);
|
|
819
|
+
if (absorbSupplementResult.downloaded.length > 0) {
|
|
820
|
+
absorbLinkPlatforms = [...absorbLinkPlatforms, ...absorbSupplementResult.downloaded];
|
|
821
|
+
}
|
|
822
|
+
tx.recordOp('link', localPath, storeCommitPath);
|
|
823
|
+
await linker.linkLib(localPath, storeCommitPath, absorbLinkPlatforms);
|
|
824
|
+
for (const platform of absorbLinkPlatforms) {
|
|
825
|
+
const storeKey = registry.getStoreKey(dependency.libName, dependency.commit, platform);
|
|
826
|
+
registry.addStoreReference(storeKey, projectHash);
|
|
827
|
+
}
|
|
828
|
+
if (absorbSupplementResult.downloaded.length > 0) {
|
|
829
|
+
hint(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 本地已有,移入 Store 并补充平台 [${absorbLinkPlatforms.join(', ')}]`);
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
hint(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 本地已有,移入 Store [${absorbLinkPlatforms.join(', ')}]`);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
const sharedPath = path.join(storeCommitPath, '_shared');
|
|
837
|
+
try {
|
|
838
|
+
await fs.access(sharedPath);
|
|
839
|
+
const sharedEntries = await fs.readdir(sharedPath);
|
|
840
|
+
if (sharedEntries.length === 0) {
|
|
841
|
+
warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - _shared 目录为空,请重新下载源文件后再 link`);
|
|
842
|
+
break;
|
|
673
843
|
}
|
|
674
|
-
|
|
675
|
-
|
|
844
|
+
const { GENERAL_PLATFORM } = await import('../core/platform.js');
|
|
845
|
+
tx.recordOp('link', localPath, sharedPath);
|
|
846
|
+
await linker.linkGeneral(localPath, sharedPath);
|
|
847
|
+
const storeKey = registry.getStoreKey(dependency.libName, dependency.commit, GENERAL_PLATFORM);
|
|
848
|
+
if (!registry.getStore(storeKey)) {
|
|
849
|
+
const integrity = await store.captureIntegrity(dependency.libName, dependency.commit, GENERAL_PLATFORM);
|
|
850
|
+
registry.addStore({
|
|
851
|
+
libName: dependency.libName, commit: dependency.commit, platform: GENERAL_PLATFORM,
|
|
852
|
+
branch: dependency.branch, url: dependency.url, ...integrity,
|
|
853
|
+
usedBy: [], createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
|
|
854
|
+
});
|
|
676
855
|
}
|
|
677
|
-
|
|
678
|
-
|
|
856
|
+
registry.addStoreReference(storeKey, projectHash);
|
|
857
|
+
const libKeyGen = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
858
|
+
if (!registry.getLibrary(libKeyGen)) {
|
|
859
|
+
const sharedSize = await getDirSize(sharedPath);
|
|
860
|
+
registry.addLibrary({
|
|
861
|
+
libName: dependency.libName, commit: dependency.commit, branch: dependency.branch,
|
|
862
|
+
url: dependency.url, platforms: [GENERAL_PLATFORM], size: sharedSize,
|
|
863
|
+
isGeneral: true,
|
|
864
|
+
referencedBy: [], createdAt: new Date().toISOString(), lastAccess: new Date().toISOString(),
|
|
865
|
+
});
|
|
679
866
|
}
|
|
867
|
+
generalLibs.add(dependency.libName);
|
|
868
|
+
hint(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,整目录链接`);
|
|
680
869
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
870
|
+
catch {
|
|
871
|
+
warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 本地目录不含任何内容,跳过`);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
case DependencyStatus.MISSING:
|
|
877
|
+
break;
|
|
878
|
+
case DependencyStatus.LINK_NEW: {
|
|
879
|
+
const linkNewCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
880
|
+
const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
|
|
881
|
+
const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
|
|
882
|
+
const { missingRequested: missing } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
883
|
+
const linkNewLibId = `${dependency.libName}@${dependency.commit}`;
|
|
884
|
+
if (missing.length > 0 && !skipAllDownloads && downloadConfirmedLibs.has(linkNewLibId)) {
|
|
885
|
+
info(`${dependency.libName} 缺少平台 [${missing.join(', ')}],开始下载...`);
|
|
886
|
+
const linkNewLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
887
|
+
const linkNewHistoryLib = registry.getLibrary(linkNewLibKey);
|
|
888
|
+
const linkNewMonitor = new DownloadMonitor({
|
|
889
|
+
name: ` ${dependency.libName}`, estimatedSize: linkNewHistoryLib?.size, getDirSize,
|
|
890
|
+
});
|
|
891
|
+
let downloadResult;
|
|
892
|
+
try {
|
|
893
|
+
downloadResult = await codepac.downloadToTemp({
|
|
894
|
+
url: dependency.url, commit: dependency.commit, branch: dependency.branch,
|
|
895
|
+
libName: dependency.libName, platforms: missing, sparse: dependency.sparse, vars: configVars,
|
|
896
|
+
useGitLightweightDownload: gitLightweightDownload,
|
|
897
|
+
onTempDirCreated: (_tempDir, libDir) => { linkNewMonitor.start(libDir); },
|
|
898
|
+
onHeartbeat: (message) => { linkNewMonitor.heartbeat(message); },
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
finally {
|
|
902
|
+
await linkNewMonitor.stop();
|
|
903
|
+
}
|
|
904
|
+
if (downloadResult.cleanedPlatforms.length > 0) {
|
|
905
|
+
hint(` 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
|
|
906
|
+
}
|
|
907
|
+
try {
|
|
908
|
+
const assessment = assessDownloadResult(missing, dependency.sparse, downloadResult, configVars);
|
|
909
|
+
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, assessment.satisfiedRequested);
|
|
910
|
+
const filteredDownloaded = assessment.downloadedRequested;
|
|
911
|
+
if (filteredDownloaded.length > 0) {
|
|
912
|
+
tx.recordOp('absorb', linkNewCommitPath, downloadResult.libDir);
|
|
913
|
+
const linkNewAbsorbResult = await store.absorbLib(downloadResult.libDir, filteredDownloaded, dependency.libName, dependency.commit);
|
|
914
|
+
await registerNestedLibraries(linkNewAbsorbResult.nestedLibraries, projectHash);
|
|
715
915
|
}
|
|
716
|
-
|
|
717
|
-
|
|
916
|
+
}
|
|
917
|
+
finally {
|
|
918
|
+
await fs.rm(downloadResult.tempDir, { recursive: true, force: true }).catch(() => { });
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const { actualExisting: linkNewExisting, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
922
|
+
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
923
|
+
const isLinkNewGeneral = await resolveLibraryGeneralState(dependency, requestedPlatforms, configVars);
|
|
924
|
+
if (isLinkNewGeneral) {
|
|
925
|
+
const sharedPath = path.join(linkNewCommitPath, '_shared');
|
|
926
|
+
tx.recordOp('link', localPath, sharedPath);
|
|
927
|
+
await linker.linkGeneral(localPath, sharedPath);
|
|
928
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], projectHash, true);
|
|
929
|
+
generalLibs.add(dependency.libName);
|
|
930
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,创建链接`);
|
|
931
|
+
}
|
|
932
|
+
else if (linkNewExisting.length === 0) {
|
|
933
|
+
const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
|
|
934
|
+
const commitEntries = await fs.readdir(linkNewCommitPath, { withFileTypes: true });
|
|
935
|
+
const availablePlatforms = commitEntries
|
|
936
|
+
.filter(e => e.isDirectory() && e.name !== '_shared' && KNOWN_PLATFORM_VALUES.includes(e.name))
|
|
937
|
+
.map(e => e.name);
|
|
938
|
+
warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 不支持 ${platforms.join('/')} 平台 [可用: ${availablePlatforms.join(', ')}]`);
|
|
939
|
+
const lnLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
940
|
+
const lnLib = registry.getLibrary(lnLibKey);
|
|
941
|
+
if (lnLib) {
|
|
942
|
+
const unavailable = lnLib.unavailablePlatforms || [];
|
|
943
|
+
const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, availablePlatforms, configVars).missingRequested;
|
|
944
|
+
for (const requestedPlatform of missingRequested) {
|
|
945
|
+
if (!unavailable.includes(requestedPlatform))
|
|
946
|
+
unavailable.push(requestedPlatform);
|
|
718
947
|
}
|
|
948
|
+
registry.updateLibrary(lnLibKey, { unavailablePlatforms: unavailable });
|
|
719
949
|
}
|
|
720
950
|
break;
|
|
721
951
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
952
|
+
else {
|
|
953
|
+
tx.recordOp('link', localPath, linkNewCommitPath);
|
|
954
|
+
await linker.linkLib(localPath, linkNewCommitPath, linkNewExisting);
|
|
955
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, linkNewExisting, projectHash, false);
|
|
956
|
+
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 创建链接 [${linkNewExisting.join(', ')}]`);
|
|
957
|
+
}
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
// 无论当前是跳过、补平台还是重建链接,都要把本地残留的平台目录清理到最终平台集合
|
|
962
|
+
const desiredLocalPlatforms = await resolveStoredPlatforms(dependency.libName, dependency.commit, finalLinkPlatforms);
|
|
963
|
+
await linker.cleanupLocalExtraPlatforms(localPath, desiredLocalPlatforms);
|
|
964
|
+
await tx.save();
|
|
965
|
+
}
|
|
966
|
+
// 8. 并行处理 MISSING 依赖
|
|
967
|
+
const missingItems = classified.filter((c) => c.status === DependencyStatus.MISSING);
|
|
968
|
+
if (missingItems.length > 0 && download && !skipAllDownloads) {
|
|
969
|
+
const toDownload = missingItems.filter((item) => {
|
|
970
|
+
const libId = `${item.dependency.libName}@${item.dependency.commit}`;
|
|
971
|
+
return downloadConfirmedLibs.has(libId);
|
|
972
|
+
});
|
|
973
|
+
if (toDownload.length > 0) {
|
|
974
|
+
info(`开始并行下载 ${toDownload.length} 个库 (最多 ${concurrency} 个并发)...`);
|
|
975
|
+
blank();
|
|
976
|
+
const isTTY = process.stdout.isTTY ?? false;
|
|
977
|
+
const multiBarManager = isTTY ? new MultiBarManager() : null;
|
|
978
|
+
const downloadLimit = pLimit(concurrency);
|
|
979
|
+
const downloadTasks = toDownload.map((item) => downloadLimit(async () => {
|
|
980
|
+
const { dependency, localPath } = item;
|
|
981
|
+
const pLog = {
|
|
982
|
+
info: (msg) => multiBarManager ? multiBarManager.log(`[info] ${msg}`) : info(msg),
|
|
983
|
+
success: (msg) => multiBarManager ? multiBarManager.log(`[ok] ${msg}`) : success(msg),
|
|
984
|
+
hint: (msg) => multiBarManager ? multiBarManager.log(`[hint] ${msg}`) : hint(msg),
|
|
985
|
+
warn: (msg) => multiBarManager ? multiBarManager.log(`[warn] ${msg}`) : warn(msg),
|
|
986
|
+
error: (msg) => multiBarManager ? multiBarManager.log(`[error] ${msg}`) : error(msg),
|
|
987
|
+
};
|
|
988
|
+
try {
|
|
989
|
+
const storeCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
726
990
|
const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
|
|
727
991
|
const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const linkNewLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
733
|
-
const linkNewHistoryLib = registry.getLibrary(linkNewLibKey);
|
|
734
|
-
const linkNewMonitor = new DownloadMonitor({
|
|
735
|
-
name: ` ${dependency.libName}`, estimatedSize: linkNewHistoryLib?.size, getDirSize,
|
|
736
|
-
});
|
|
737
|
-
let downloadResult;
|
|
738
|
-
try {
|
|
739
|
-
downloadResult = await codepac.downloadToTemp({
|
|
740
|
-
url: dependency.url, commit: dependency.commit, branch: dependency.branch,
|
|
741
|
-
libName: dependency.libName, platforms: missing, sparse: dependency.sparse, vars: configVars,
|
|
742
|
-
onTempDirCreated: (_tempDir, libDir) => { linkNewMonitor.start(libDir); },
|
|
743
|
-
onHeartbeat: (message) => { linkNewMonitor.heartbeat(message); },
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
finally {
|
|
747
|
-
await linkNewMonitor.stop();
|
|
748
|
-
}
|
|
749
|
-
if (downloadResult.cleanedPlatforms.length > 0) {
|
|
750
|
-
hint(` 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
|
|
751
|
-
}
|
|
752
|
-
try {
|
|
753
|
-
const assessment = assessDownloadResult(missing, dependency.sparse, downloadResult, configVars);
|
|
754
|
-
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, assessment.satisfiedRequested);
|
|
755
|
-
const filteredDownloaded = assessment.downloadedRequested;
|
|
756
|
-
if (filteredDownloaded.length > 0) {
|
|
757
|
-
tx.recordOp('absorb', linkNewCommitPath, downloadResult.libDir);
|
|
758
|
-
const linkNewAbsorbResult = await store.absorbLib(downloadResult.libDir, filteredDownloaded, dependency.libName, dependency.commit);
|
|
759
|
-
await registerNestedLibraries(linkNewAbsorbResult.nestedLibraries, projectHash);
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
finally {
|
|
763
|
-
await fs.rm(downloadResult.tempDir, { recursive: true, force: true }).catch(() => { });
|
|
992
|
+
if (requestedPlatforms.length === 0) {
|
|
993
|
+
pLog.warn(`${dependency.libName} 请求平台 [${platforms.join(', ')}] 均已按规则跳过,没有可链接产物`);
|
|
994
|
+
if (await linker.cleanupEmptyLocalDirectory(localPath)) {
|
|
995
|
+
pLog.warn(`${dependency.libName} 已清理历史遗留的空本地目录`);
|
|
764
996
|
}
|
|
997
|
+
return {
|
|
998
|
+
success: false,
|
|
999
|
+
name: dependency.libName,
|
|
1000
|
+
skipped: true,
|
|
1001
|
+
skippedPlatforms: blockedPlatforms,
|
|
1002
|
+
unsupported: true,
|
|
1003
|
+
};
|
|
765
1004
|
}
|
|
766
|
-
const { actualExisting:
|
|
1005
|
+
const { actualExisting: existing, missingRequested: missing, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
767
1006
|
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], projectHash, true);
|
|
774
|
-
generalLibs.add(dependency.libName);
|
|
775
|
-
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,创建链接`);
|
|
776
|
-
}
|
|
777
|
-
else if (linkNewExisting.length === 0) {
|
|
778
|
-
const { KNOWN_PLATFORM_VALUES } = await import('../core/platform.js');
|
|
779
|
-
const commitEntries = await fs.readdir(linkNewCommitPath, { withFileTypes: true });
|
|
780
|
-
const availablePlatforms = commitEntries
|
|
781
|
-
.filter(e => e.isDirectory() && e.name !== '_shared' && KNOWN_PLATFORM_VALUES.includes(e.name))
|
|
782
|
-
.map(e => e.name);
|
|
783
|
-
warn(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 不支持 ${platforms.join('/')} 平台 [可用: ${availablePlatforms.join(', ')}]`);
|
|
784
|
-
const lnLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
785
|
-
const lnLib = registry.getLibrary(lnLibKey);
|
|
786
|
-
if (lnLib) {
|
|
787
|
-
const unavailable = lnLib.unavailablePlatforms || [];
|
|
788
|
-
const missingRequested = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, availablePlatforms, configVars).missingRequested;
|
|
789
|
-
for (const requestedPlatform of missingRequested) {
|
|
790
|
-
if (!unavailable.includes(requestedPlatform))
|
|
791
|
-
unavailable.push(requestedPlatform);
|
|
1007
|
+
if (missing.length === 0) {
|
|
1008
|
+
if (existing.length === 0) {
|
|
1009
|
+
pLog.warn(`${dependency.libName} 请求平台 [${requestedPlatforms.join(', ')}] 在 Store 中没有可链接产物,已拒绝创建空目录`);
|
|
1010
|
+
if (await linker.cleanupEmptyLocalDirectory(localPath)) {
|
|
1011
|
+
pLog.warn(`${dependency.libName} 已清理历史遗留的空本地目录`);
|
|
792
1012
|
}
|
|
793
|
-
|
|
1013
|
+
return {
|
|
1014
|
+
success: false,
|
|
1015
|
+
name: dependency.libName,
|
|
1016
|
+
skipped: true,
|
|
1017
|
+
skippedPlatforms: requestedPlatforms,
|
|
1018
|
+
};
|
|
794
1019
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 创建链接 [${linkNewExisting.join(', ')}]`);
|
|
1020
|
+
pLog.info(`${dependency.libName} 所有平台已存在,直接链接...`);
|
|
1021
|
+
tx.recordOp('link', localPath, storeCommitPath);
|
|
1022
|
+
await linker.linkLib(localPath, storeCommitPath, existing);
|
|
1023
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, existing, projectHash, false);
|
|
1024
|
+
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 链接完成 [${existing.join(', ')}]`);
|
|
1025
|
+
return { success: true, name: dependency.libName, downloadedPlatforms: existing, skippedPlatforms: [] };
|
|
802
1026
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
// 8. 并行处理 MISSING 依赖
|
|
812
|
-
const missingItems = classified.filter((c) => c.status === DependencyStatus.MISSING);
|
|
813
|
-
if (missingItems.length > 0 && download && !skipAllDownloads) {
|
|
814
|
-
const toDownload = missingItems.filter((item) => {
|
|
815
|
-
const libId = `${item.dependency.libName}@${item.dependency.commit}`;
|
|
816
|
-
return downloadConfirmedLibs.has(libId);
|
|
817
|
-
});
|
|
818
|
-
if (toDownload.length > 0) {
|
|
819
|
-
info(`开始并行下载 ${toDownload.length} 个库 (最多 ${concurrency} 个并发)...`);
|
|
820
|
-
blank();
|
|
821
|
-
const isTTY = process.stdout.isTTY ?? false;
|
|
822
|
-
const multiBarManager = isTTY ? new MultiBarManager() : null;
|
|
823
|
-
const downloadLimit = pLimit(concurrency);
|
|
824
|
-
const downloadTasks = toDownload.map((item) => downloadLimit(async () => {
|
|
825
|
-
const { dependency, localPath } = item;
|
|
826
|
-
const pLog = {
|
|
827
|
-
info: (msg) => multiBarManager ? multiBarManager.log(`[info] ${msg}`) : info(msg),
|
|
828
|
-
success: (msg) => multiBarManager ? multiBarManager.log(`[ok] ${msg}`) : success(msg),
|
|
829
|
-
hint: (msg) => multiBarManager ? multiBarManager.log(`[hint] ${msg}`) : hint(msg),
|
|
830
|
-
warn: (msg) => multiBarManager ? multiBarManager.log(`[warn] ${msg}`) : warn(msg),
|
|
831
|
-
error: (msg) => multiBarManager ? multiBarManager.log(`[error] ${msg}`) : error(msg),
|
|
832
|
-
};
|
|
833
|
-
try {
|
|
834
|
-
const storeCommitPath = path.join(storePath, dependency.libName, dependency.commit);
|
|
835
|
-
const blockedPlatforms = getBlockedRequestedPlatforms(registry, dependency, platforms, configVars);
|
|
836
|
-
const requestedPlatforms = platforms.filter((platform) => !blockedPlatforms.includes(platform));
|
|
837
|
-
const { actualExisting: existing, missingRequested: missing, satisfiedRequested, } = await resolveRequestedStoreState(dependency, requestedPlatforms, configVars);
|
|
838
|
-
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, satisfiedRequested);
|
|
839
|
-
if (missing.length === 0) {
|
|
840
|
-
pLog.info(`${dependency.libName} 所有平台已存在,直接链接...`);
|
|
841
|
-
tx.recordOp('link', localPath, storeCommitPath);
|
|
842
|
-
await linker.linkLib(localPath, storeCommitPath, existing);
|
|
843
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, existing, projectHash, false);
|
|
844
|
-
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 链接完成 [${existing.join(', ')}]`);
|
|
845
|
-
return { success: true, name: dependency.libName, downloadedPlatforms: existing, skippedPlatforms: [] };
|
|
1027
|
+
const dlLibKey = registry.getLibraryKey(dependency.libName, dependency.commit);
|
|
1028
|
+
const historyLib = registry.getLibrary(dlLibKey);
|
|
1029
|
+
const unavailablePlatforms = blockedPlatforms;
|
|
1030
|
+
const dlToDownload = missing.filter(p => !unavailablePlatforms.includes(p));
|
|
1031
|
+
const knownUnavailable = missing.filter(p => unavailablePlatforms.includes(p));
|
|
1032
|
+
if (dlToDownload.length === 0) {
|
|
1033
|
+
if (knownUnavailable.length > 0) {
|
|
1034
|
+
pLog.warn(`${dependency.libName} 平台 [${knownUnavailable.join(', ')}] 已按规则跳过`);
|
|
846
1035
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
const unavailablePlatforms = blockedPlatforms;
|
|
850
|
-
const dlToDownload = missing.filter(p => !unavailablePlatforms.includes(p));
|
|
851
|
-
const knownUnavailable = missing.filter(p => unavailablePlatforms.includes(p));
|
|
852
|
-
if (dlToDownload.length === 0) {
|
|
853
|
-
if (knownUnavailable.length > 0) {
|
|
854
|
-
pLog.warn(`${dependency.libName} 平台 [${knownUnavailable.join(', ')}] 已按规则跳过`);
|
|
855
|
-
}
|
|
856
|
-
return { success: false, name: dependency.libName, skipped: true, skippedPlatforms: missing, unsupported: true };
|
|
1036
|
+
if (await linker.cleanupEmptyLocalDirectory(localPath)) {
|
|
1037
|
+
pLog.warn(`${dependency.libName} 已清理历史遗留的空本地目录`);
|
|
857
1038
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1039
|
+
return { success: false, name: dependency.libName, skipped: true, skippedPlatforms: missing, unsupported: true };
|
|
1040
|
+
}
|
|
1041
|
+
pLog.info(`下载 ${dependency.libName} [${dlToDownload.join(', ')}]...`);
|
|
1042
|
+
const estimatedSize = historyLib?.size;
|
|
1043
|
+
const downloadMonitor = new DownloadMonitor({
|
|
1044
|
+
name: ` ${dependency.libName}`, estimatedSize, getDirSize,
|
|
1045
|
+
manager: multiBarManager ?? undefined,
|
|
1046
|
+
});
|
|
1047
|
+
let downloadResult;
|
|
1048
|
+
try {
|
|
1049
|
+
downloadResult = await codepac.downloadToTemp({
|
|
1050
|
+
url: dependency.url, commit: dependency.commit, branch: dependency.branch,
|
|
1051
|
+
libName: dependency.libName, platforms: dlToDownload, sparse: dependency.sparse,
|
|
1052
|
+
vars: configVars,
|
|
1053
|
+
useGitLightweightDownload: gitLightweightDownload,
|
|
1054
|
+
onTempDirCreated: (_tempDir, libDir) => { downloadMonitor.start(libDir); },
|
|
1055
|
+
onHeartbeat: (message) => { downloadMonitor.heartbeat(message); },
|
|
863
1056
|
});
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1057
|
+
}
|
|
1058
|
+
finally {
|
|
1059
|
+
await downloadMonitor.stop();
|
|
1060
|
+
}
|
|
1061
|
+
if (downloadResult.cleanedPlatforms.length > 0) {
|
|
1062
|
+
pLog.hint(` 已过滤: ${downloadResult.cleanedPlatforms.join(', ')}`);
|
|
1063
|
+
}
|
|
1064
|
+
try {
|
|
1065
|
+
const assessment = assessDownloadResult(dlToDownload, dependency.sparse, downloadResult, configVars);
|
|
1066
|
+
clearSatisfiedAutoUnavailablePlatforms(registry, dependency, assessment.satisfiedRequested);
|
|
1067
|
+
if (assessment.isPureGeneral) {
|
|
1068
|
+
upsertLibraryAvailability(dependency, {
|
|
1069
|
+
platforms: [GENERAL_PLATFORM],
|
|
1070
|
+
isGeneral: true,
|
|
872
1071
|
});
|
|
1072
|
+
tx.recordOp('absorb', storeCommitPath, downloadResult.libDir);
|
|
1073
|
+
await store.absorbGeneral(downloadResult.libDir, dependency.libName, dependency.commit);
|
|
1074
|
+
const sharedPath = path.join(storeCommitPath, '_shared');
|
|
1075
|
+
tx.recordOp('link', localPath, sharedPath);
|
|
1076
|
+
await linker.linkGeneral(localPath, sharedPath);
|
|
1077
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], projectHash, true);
|
|
1078
|
+
generalLibs.add(dependency.libName);
|
|
1079
|
+
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,下载完成`);
|
|
1080
|
+
return { success: true, name: dependency.libName, downloadedPlatforms: [GENERAL_PLATFORM], skippedPlatforms: [], isGeneral: true };
|
|
873
1081
|
}
|
|
874
|
-
|
|
875
|
-
|
|
1082
|
+
const filteredDownloaded = assessment.downloadedRequested;
|
|
1083
|
+
const newUnavailable = assessment.unavailableRequested;
|
|
1084
|
+
if (newUnavailable.length > 0) {
|
|
1085
|
+
const updatedUnavailable = [...new Set([...unavailablePlatforms, ...newUnavailable])];
|
|
1086
|
+
upsertLibraryAvailability(dependency, {
|
|
1087
|
+
unavailablePlatforms: updatedUnavailable,
|
|
1088
|
+
isGeneral: false,
|
|
1089
|
+
});
|
|
1090
|
+
pLog.warn(`${dependency.libName} 平台 [${newUnavailable.join(', ')}] 远程不存在,已记录`);
|
|
876
1091
|
}
|
|
877
|
-
if (
|
|
878
|
-
|
|
1092
|
+
if (filteredDownloaded.length > 0) {
|
|
1093
|
+
tx.recordOp('absorb', storeCommitPath, downloadResult.libDir);
|
|
1094
|
+
const downloadAbsorbResult = await store.absorbLib(downloadResult.libDir, filteredDownloaded, dependency.libName, dependency.commit);
|
|
1095
|
+
await registerNestedLibraries(downloadAbsorbResult.nestedLibraries, projectHash);
|
|
879
1096
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
if (
|
|
884
|
-
|
|
885
|
-
platforms: [GENERAL_PLATFORM],
|
|
886
|
-
isGeneral: true,
|
|
887
|
-
});
|
|
888
|
-
tx.recordOp('absorb', storeCommitPath, downloadResult.libDir);
|
|
889
|
-
await store.absorbGeneral(downloadResult.libDir, dependency.libName, dependency.commit);
|
|
890
|
-
const sharedPath = path.join(storeCommitPath, '_shared');
|
|
891
|
-
tx.recordOp('link', localPath, sharedPath);
|
|
892
|
-
await linker.linkGeneral(localPath, sharedPath);
|
|
893
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, [GENERAL_PLATFORM], projectHash, true);
|
|
894
|
-
generalLibs.add(dependency.libName);
|
|
895
|
-
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - General 库,下载完成`);
|
|
896
|
-
return { success: true, name: dependency.libName, downloadedPlatforms: [GENERAL_PLATFORM], skippedPlatforms: [], isGeneral: true };
|
|
897
|
-
}
|
|
898
|
-
const filteredDownloaded = assessment.downloadedRequested;
|
|
899
|
-
const newUnavailable = assessment.unavailableRequested;
|
|
900
|
-
if (newUnavailable.length > 0) {
|
|
901
|
-
const updatedUnavailable = [...new Set([...unavailablePlatforms, ...newUnavailable])];
|
|
902
|
-
upsertLibraryAvailability(dependency, {
|
|
903
|
-
unavailablePlatforms: updatedUnavailable,
|
|
904
|
-
isGeneral: false,
|
|
905
|
-
});
|
|
906
|
-
pLog.warn(`${dependency.libName} 平台 [${newUnavailable.join(', ')}] 远程不存在,已记录`);
|
|
1097
|
+
const linkPlatforms = [...existing, ...filteredDownloaded];
|
|
1098
|
+
if (linkPlatforms.length === 0) {
|
|
1099
|
+
pLog.warn(`${dependency.libName} 下载后没有生成请求平台 [${requestedPlatforms.join(', ')}] 的可链接产物,已拒绝创建空目录`);
|
|
1100
|
+
if (await linker.cleanupEmptyLocalDirectory(localPath)) {
|
|
1101
|
+
pLog.warn(`${dependency.libName} 已清理历史遗留的空本地目录`);
|
|
907
1102
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
if (linkPlatforms.length === 0) {
|
|
915
|
-
return {
|
|
916
|
-
success: false,
|
|
917
|
-
name: dependency.libName,
|
|
918
|
-
skipped: true,
|
|
919
|
-
skippedPlatforms: requestedPlatforms,
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
tx.recordOp('link', localPath, storeCommitPath);
|
|
923
|
-
await linker.linkLib(localPath, storeCommitPath, linkPlatforms);
|
|
924
|
-
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, linkPlatforms, projectHash, false);
|
|
925
|
-
const notLinkedPlatforms = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, linkPlatforms, configVars).missingRequested;
|
|
926
|
-
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 下载完成 [${linkPlatforms.join(', ')}]`);
|
|
927
|
-
return { success: true, name: dependency.libName, downloadedPlatforms: linkPlatforms, skippedPlatforms: notLinkedPlatforms };
|
|
928
|
-
}
|
|
929
|
-
finally {
|
|
930
|
-
await fs.rm(downloadResult.tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1103
|
+
return {
|
|
1104
|
+
success: false,
|
|
1105
|
+
name: dependency.libName,
|
|
1106
|
+
skipped: true,
|
|
1107
|
+
skippedPlatforms: requestedPlatforms,
|
|
1108
|
+
};
|
|
931
1109
|
}
|
|
1110
|
+
tx.recordOp('link', localPath, storeCommitPath);
|
|
1111
|
+
await linker.linkLib(localPath, storeCommitPath, linkPlatforms);
|
|
1112
|
+
await ensureLinkedRegistryState(dependency.libName, dependency.commit, dependency.branch, dependency.url, linkPlatforms, projectHash, false);
|
|
1113
|
+
const notLinkedPlatforms = resolveRequestedPlatformsByActual(requestedPlatforms, dependency.sparse, linkPlatforms, configVars).missingRequested;
|
|
1114
|
+
pLog.success(`${dependency.libName} (${dependency.commit.slice(0, 7)}) - 下载完成 [${linkPlatforms.join(', ')}]`);
|
|
1115
|
+
return { success: true, name: dependency.libName, downloadedPlatforms: linkPlatforms, skippedPlatforms: notLinkedPlatforms };
|
|
932
1116
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
return { success: false, name: dependency.libName, error: err.message };
|
|
936
|
-
}
|
|
937
|
-
}));
|
|
938
|
-
const results = await Promise.all(downloadTasks);
|
|
939
|
-
multiBarManager?.stop();
|
|
940
|
-
const succeeded = results.filter((r) => r.success);
|
|
941
|
-
const failed = results.filter((r) => !r.success && !('skipped' in r && r.skipped));
|
|
942
|
-
blank();
|
|
943
|
-
info(`下载完成: ${succeeded.length}/${toDownload.length} 个库`);
|
|
944
|
-
if (failed.length > 0)
|
|
945
|
-
warn(`${failed.length} 个库下载失败`);
|
|
946
|
-
const allSkipped = [];
|
|
947
|
-
for (const r of results) {
|
|
948
|
-
if ('skippedPlatforms' in r && r.skippedPlatforms && r.skippedPlatforms.length > 0) {
|
|
949
|
-
allSkipped.push({ name: r.name, platforms: r.skippedPlatforms });
|
|
1117
|
+
finally {
|
|
1118
|
+
await fs.rm(downloadResult.tempDir, { recursive: true, force: true }).catch(() => { });
|
|
950
1119
|
}
|
|
951
1120
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1121
|
+
catch (err) {
|
|
1122
|
+
pLog.error(`${dependency.libName} 下载失败: ${err.message}`);
|
|
1123
|
+
return { success: false, name: dependency.libName, error: err.message };
|
|
1124
|
+
}
|
|
1125
|
+
}));
|
|
1126
|
+
const results = await Promise.all(downloadTasks);
|
|
1127
|
+
multiBarManager?.stop();
|
|
1128
|
+
const succeeded = results.filter((r) => r.success);
|
|
1129
|
+
const failed = results.filter((r) => !r.success && !('skipped' in r && r.skipped));
|
|
1130
|
+
blank();
|
|
1131
|
+
info(`下载完成: ${succeeded.length}/${toDownload.length} 个库`);
|
|
1132
|
+
if (failed.length > 0)
|
|
1133
|
+
warn(`${failed.length} 个库下载失败`);
|
|
1134
|
+
const allSkipped = [];
|
|
1135
|
+
for (const r of results) {
|
|
1136
|
+
if ('skippedPlatforms' in r && r.skippedPlatforms && r.skippedPlatforms.length > 0) {
|
|
1137
|
+
allSkipped.push({ name: r.name, platforms: r.skippedPlatforms });
|
|
958
1138
|
}
|
|
959
|
-
for (const r of succeeded)
|
|
960
|
-
downloadedLibs.push(r.name);
|
|
961
1139
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1140
|
+
if (allSkipped.length > 0) {
|
|
1141
|
+
blank();
|
|
1142
|
+
warn('以下库/平台组合不可用(已跳过):');
|
|
1143
|
+
for (const item of allSkipped) {
|
|
1144
|
+
warn(` - ${item.name} / ${item.platforms.join(', ')}`);
|
|
1145
|
+
}
|
|
966
1146
|
}
|
|
1147
|
+
for (const r of succeeded)
|
|
1148
|
+
downloadedLibs.push(r.name);
|
|
967
1149
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
if (actions.length > 0) {
|
|
973
|
-
blank();
|
|
974
|
-
separator();
|
|
975
|
-
info(`发现 ${actions.length} 个嵌套依赖配置`);
|
|
976
|
-
const nestedContext = {
|
|
977
|
-
depth: 0, processedConfigs: new Set([configPath]), platforms, vars: configVars,
|
|
978
|
-
};
|
|
979
|
-
const thirdPartyDir = path.dirname(configPath);
|
|
980
|
-
for (const action of actions) {
|
|
981
|
-
await processAction(action, nestedContext, thirdPartyDir, {
|
|
982
|
-
tx, registry, projectHash, projectRoot, dryRun, download, yes,
|
|
983
|
-
generalLibs, downloadedLibs, nestedLinkedDeps,
|
|
984
|
-
});
|
|
985
|
-
}
|
|
1150
|
+
}
|
|
1151
|
+
else if (missingItems.length > 0 && !download) {
|
|
1152
|
+
for (const item of missingItems) {
|
|
1153
|
+
warn(`${item.dependency.libName} (${item.dependency.commit.slice(0, 7)}) - 缺失 (跳过下载)`);
|
|
986
1154
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1155
|
+
}
|
|
1156
|
+
// 9. 处理嵌套依赖 (actions)
|
|
1157
|
+
const actions = selectedActions;
|
|
1158
|
+
const nestedLinkedDeps = [];
|
|
1159
|
+
if (actions.length > 0) {
|
|
1160
|
+
blank();
|
|
1161
|
+
separator();
|
|
1162
|
+
info(`发现 ${actions.length} 个嵌套依赖配置`);
|
|
1163
|
+
const nestedContext = {
|
|
1164
|
+
depth: 0,
|
|
1165
|
+
processedConfigs: new Set([buildActionVisitKey(configPath, path.dirname(configPath))]),
|
|
1166
|
+
platforms,
|
|
1167
|
+
codepacPlatforms,
|
|
1168
|
+
vars: configVars,
|
|
1169
|
+
};
|
|
1170
|
+
const thirdPartyDir = path.dirname(configPath);
|
|
1171
|
+
for (const action of actions) {
|
|
1172
|
+
await processAction(action, nestedContext, configPath, thirdPartyDir, {
|
|
1173
|
+
tx, registry, projectHash, projectRoot, dryRun, download, yes,
|
|
1174
|
+
gitLightweightDownload, skipActions: new Set(params.skipActions ?? []),
|
|
1175
|
+
generalLibs, downloadedLibs, nestedLinkedDeps,
|
|
1004
1176
|
});
|
|
1005
1177
|
}
|
|
1006
|
-
return {
|
|
1007
|
-
linkedDeps, nestedLinkedDeps, generalLibs, downloadedLibs, savedBytes, finalLinkPlatforms, stats,
|
|
1008
|
-
};
|
|
1009
1178
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1179
|
+
// 10. 同步 cache 文件
|
|
1180
|
+
for (const cacheConfigPath of cacheConfigPaths) {
|
|
1181
|
+
await syncCacheFile(cacheConfigPath);
|
|
1182
|
+
}
|
|
1183
|
+
// 11. 构建返回结果
|
|
1184
|
+
const linkedDeps = [];
|
|
1185
|
+
for (const item of classified) {
|
|
1186
|
+
if (item.status === DependencyStatus.MISSING && !downloadedLibs.includes(item.dependency.libName)) {
|
|
1187
|
+
continue;
|
|
1188
|
+
}
|
|
1189
|
+
const actualPlatforms = generalLibs.has(item.dependency.libName)
|
|
1190
|
+
? [GENERAL_PLATFORM]
|
|
1191
|
+
: await resolveStoredPlatforms(item.dependency.libName, item.dependency.commit, platforms);
|
|
1192
|
+
linkedDeps.push({
|
|
1193
|
+
libName: item.dependency.libName,
|
|
1194
|
+
commit: item.dependency.commit,
|
|
1195
|
+
platform: actualPlatforms[0] ?? platforms[0],
|
|
1196
|
+
linkedPath: path.relative(projectRoot, item.localPath),
|
|
1197
|
+
scope,
|
|
1198
|
+
});
|
|
1013
1199
|
}
|
|
1200
|
+
return {
|
|
1201
|
+
linkedDeps, nestedLinkedDeps, generalLibs, downloadedLibs, savedBytes, finalLinkPlatforms, stats,
|
|
1202
|
+
};
|
|
1014
1203
|
}
|
|
1015
1204
|
/**
|
|
1016
1205
|
* 执行链接操作
|
|
@@ -1024,6 +1213,7 @@ export async function linkProject(projectPath, options) {
|
|
|
1024
1213
|
if (cfg?.proxy)
|
|
1025
1214
|
setProxyConfig(cfg.proxy);
|
|
1026
1215
|
const concurrency = cfg?.concurrency ?? 5;
|
|
1216
|
+
const gitLightweightDownload = cfg?.gitLightweightDownload ?? true;
|
|
1027
1217
|
const registry = getRegistry();
|
|
1028
1218
|
await registry.load();
|
|
1029
1219
|
const existingProject = registry.getProjectByPath(normalizedPath);
|
|
@@ -1062,9 +1252,10 @@ export async function linkProject(projectPath, options) {
|
|
|
1062
1252
|
error(`路径不存在: ${absolutePath}`);
|
|
1063
1253
|
process.exit(EXIT_CODES.NOINPUT);
|
|
1064
1254
|
}
|
|
1065
|
-
//
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1255
|
+
// 发现可选配置文件,扫描位置跟随实际主配置所在目录。
|
|
1256
|
+
const configDiscovery = discoveredConfigPath
|
|
1257
|
+
? await findAllCodepacConfigs(path.dirname(discoveredConfigPath))
|
|
1258
|
+
: null;
|
|
1068
1259
|
let selectedOptionalConfigs = [];
|
|
1069
1260
|
if (configDiscovery && configDiscovery.optionalConfigs.length > 0) {
|
|
1070
1261
|
if (options.config && options.config.length > 0) {
|
|
@@ -1111,14 +1302,21 @@ export async function linkProject(projectPath, options) {
|
|
|
1111
1302
|
// === 阶段 2: Submodule 检测 ===
|
|
1112
1303
|
let selectedSubmodules = [];
|
|
1113
1304
|
if (options.submodules !== false) {
|
|
1114
|
-
const
|
|
1305
|
+
const codepacPlatforms = [...new Set(platforms.map((platform) => getBaseKeyForCodepac(platform)))];
|
|
1306
|
+
const submoduleConfigs = await findSubmoduleConfigs(normalizedPath, '', 0, codepacPlatforms);
|
|
1115
1307
|
if (submoduleConfigs.length > 0) {
|
|
1116
1308
|
selectedSubmodules = await selectSubmodules(submoduleConfigs, options, existingProject?.submodules);
|
|
1117
1309
|
// 为选中的 submodule 处理可选配置
|
|
1118
1310
|
for (const sub of selectedSubmodules) {
|
|
1119
|
-
if (sub.optionalConfigs.length
|
|
1311
|
+
if (sub.optionalConfigs.length === 0)
|
|
1312
|
+
continue;
|
|
1313
|
+
const rememberedConfigs = existingProject?.submoduleOptionalConfigs?.[sub.relativePath] ?? [];
|
|
1314
|
+
if (options.yes) {
|
|
1315
|
+
sub.selectedOptionalConfigs = sub.optionalConfigs.filter((configInfo) => rememberedConfigs.includes(configInfo.name));
|
|
1316
|
+
}
|
|
1317
|
+
else if (process.stdout.isTTY) {
|
|
1120
1318
|
info(`${sub.name} 发现可选配置:`);
|
|
1121
|
-
const selected = await selectOptionalConfigs(sub.optionalConfigs, { isTTY: true, specifiedConfigs:
|
|
1319
|
+
const selected = await selectOptionalConfigs(sub.optionalConfigs, { isTTY: true, specifiedConfigs: rememberedConfigs });
|
|
1122
1320
|
if (selected !== PROMPT_CANCELLED) {
|
|
1123
1321
|
sub.selectedOptionalConfigs = selected;
|
|
1124
1322
|
}
|
|
@@ -1184,7 +1382,9 @@ export async function linkProject(projectPath, options) {
|
|
|
1184
1382
|
dryRun: options.dryRun,
|
|
1185
1383
|
yes: options.yes,
|
|
1186
1384
|
concurrency,
|
|
1385
|
+
gitLightweightDownload,
|
|
1187
1386
|
optionalConfigs: selectedOptionalConfigs,
|
|
1387
|
+
skipActions: options.skipAction ?? [],
|
|
1188
1388
|
});
|
|
1189
1389
|
const finalLinkPlatforms = mainResult.finalLinkPlatforms;
|
|
1190
1390
|
// 如果 dry-run 模式,linkScope 内已显示信息
|
|
@@ -1207,8 +1407,10 @@ export async function linkProject(projectPath, options) {
|
|
|
1207
1407
|
dryRun: true,
|
|
1208
1408
|
yes: options.yes,
|
|
1209
1409
|
concurrency,
|
|
1410
|
+
gitLightweightDownload,
|
|
1210
1411
|
optionalConfigs: sub.selectedOptionalConfigs,
|
|
1211
1412
|
scope: sub.relativePath,
|
|
1413
|
+
skipActions: options.skipAction ?? [],
|
|
1212
1414
|
});
|
|
1213
1415
|
}
|
|
1214
1416
|
return;
|
|
@@ -1232,20 +1434,14 @@ export async function linkProject(projectPath, options) {
|
|
|
1232
1434
|
dryRun: options.dryRun,
|
|
1233
1435
|
yes: options.yes,
|
|
1234
1436
|
concurrency,
|
|
1437
|
+
gitLightweightDownload,
|
|
1235
1438
|
optionalConfigs: sub.selectedOptionalConfigs,
|
|
1236
1439
|
scope: sub.relativePath,
|
|
1440
|
+
skipActions: options.skipAction ?? [],
|
|
1237
1441
|
});
|
|
1238
1442
|
subResults.push(subResult);
|
|
1239
1443
|
}
|
|
1240
1444
|
// === 阶段 5: 合并结果、注册项目 ===
|
|
1241
|
-
const allGeneralLibs = new Set([
|
|
1242
|
-
...mainResult.generalLibs,
|
|
1243
|
-
...subResults.flatMap(r => [...r.generalLibs]),
|
|
1244
|
-
]);
|
|
1245
|
-
const allDownloadedLibs = [
|
|
1246
|
-
...mainResult.downloadedLibs,
|
|
1247
|
-
...subResults.flatMap(r => r.downloadedLibs),
|
|
1248
|
-
];
|
|
1249
1445
|
// 合并所有依赖
|
|
1250
1446
|
const allLinkedDeps = [
|
|
1251
1447
|
...mainResult.linkedDeps,
|
|
@@ -1266,6 +1462,7 @@ export async function linkProject(projectPath, options) {
|
|
|
1266
1462
|
? selectedOptionalConfigs.map(c => c.name) : undefined,
|
|
1267
1463
|
submodules: selectedSubmodules.length > 0
|
|
1268
1464
|
? selectedSubmodules.map(s => s.relativePath) : undefined,
|
|
1465
|
+
submoduleOptionalConfigs: buildSubmoduleOptionalConfigState(selectedSubmodules),
|
|
1269
1466
|
});
|
|
1270
1467
|
// 更新 Store 引用关系
|
|
1271
1468
|
const newStoreKeys = allLinkedDeps.map((d) => registry.getStoreKey(d.libName, d.commit, d.platform));
|
|
@@ -1416,10 +1613,7 @@ function hintStoreRepairCommand(err) {
|
|
|
1416
1613
|
async function registerSharedStore(libName, commit, branch = '', url = '') {
|
|
1417
1614
|
const registry = getRegistry();
|
|
1418
1615
|
const storeKey = registry.getStoreKey(libName, commit, SHARED_PLATFORM);
|
|
1419
|
-
|
|
1420
|
-
if (registry.getStore(storeKey)) {
|
|
1421
|
-
return true;
|
|
1422
|
-
}
|
|
1616
|
+
const existingEntry = registry.getStore(storeKey);
|
|
1423
1617
|
// 检查 _shared 目录是否存在
|
|
1424
1618
|
const storePath = await config.getStorePath();
|
|
1425
1619
|
if (!storePath)
|
|
@@ -1437,17 +1631,27 @@ async function registerSharedStore(libName, commit, branch = '', url = '') {
|
|
|
1437
1631
|
}
|
|
1438
1632
|
// 计算 _shared 完整性信息并注册
|
|
1439
1633
|
const integrity = await store.captureIntegrity(libName, commit, SHARED_PLATFORM);
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1634
|
+
if (existingEntry) {
|
|
1635
|
+
registry.updateStore(storeKey, {
|
|
1636
|
+
branch: existingEntry.branch || branch,
|
|
1637
|
+
url: existingEntry.url || url,
|
|
1638
|
+
...integrity,
|
|
1639
|
+
lastAccess: new Date().toISOString(),
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
else {
|
|
1643
|
+
registry.addStore({
|
|
1644
|
+
libName,
|
|
1645
|
+
commit,
|
|
1646
|
+
platform: SHARED_PLATFORM,
|
|
1647
|
+
branch,
|
|
1648
|
+
url,
|
|
1649
|
+
...integrity,
|
|
1650
|
+
usedBy: [], // _shared 不追踪引用,生命周期跟随平台
|
|
1651
|
+
createdAt: new Date().toISOString(),
|
|
1652
|
+
lastAccess: new Date().toISOString(),
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1451
1655
|
debug(`注册 _shared: ${libName}:${commit.slice(0, 8)} (${formatSize(integrity.size)})`);
|
|
1452
1656
|
return true;
|
|
1453
1657
|
}
|
|
@@ -1473,6 +1677,13 @@ async function ensureStoreInfo(libName, commit, platform, branch, url, projectHa
|
|
|
1473
1677
|
const integrity = await store.captureIntegrity(libName, commit, platform);
|
|
1474
1678
|
registry.updateStore(storeKey, integrity);
|
|
1475
1679
|
}
|
|
1680
|
+
if (existingEntry && ((!existingEntry.branch && branch) || (!existingEntry.url && url))) {
|
|
1681
|
+
registry.updateStore(storeKey, {
|
|
1682
|
+
branch: existingEntry.branch || branch,
|
|
1683
|
+
url: existingEntry.url || url,
|
|
1684
|
+
lastAccess: new Date().toISOString(),
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1476
1687
|
if (projectHash) {
|
|
1477
1688
|
registry.addStoreReference(storeKey, projectHash);
|
|
1478
1689
|
}
|
|
@@ -1713,7 +1924,7 @@ async function classifyDependencies(dependencies, projectPath, configPath, platf
|
|
|
1713
1924
|
}
|
|
1714
1925
|
const existing = verifiedExisting;
|
|
1715
1926
|
// 也检查是否为 General 库(有 _shared 且有内容)
|
|
1716
|
-
const isGeneral = await resolveLibraryGeneralState(dep);
|
|
1927
|
+
const isGeneral = await resolveLibraryGeneralState(dep, requestedPlatforms, vars);
|
|
1717
1928
|
const inStore = existing.length > 0 || isGeneral;
|
|
1718
1929
|
if (satisfiedRequested.length > 0) {
|
|
1719
1930
|
clearSatisfiedAutoUnavailablePlatforms(getRegistry(), dep, satisfiedRequested);
|
|
@@ -1833,6 +2044,7 @@ async function supplementMissingPlatforms(dependency, platforms, registry, tx, o
|
|
|
1833
2044
|
platforms: toDownload,
|
|
1834
2045
|
sparse: dependency.sparse,
|
|
1835
2046
|
vars: options.vars,
|
|
2047
|
+
useGitLightweightDownload: options.useGitLightweightDownload,
|
|
1836
2048
|
});
|
|
1837
2049
|
// 提示清理的平台(如果有)
|
|
1838
2050
|
if (downloadResult.cleanedPlatforms.length > 0) {
|
|
@@ -1949,19 +2161,20 @@ function getStatusKey(status) {
|
|
|
1949
2161
|
async function syncCacheFile(configPath) {
|
|
1950
2162
|
const configDir = path.dirname(configPath);
|
|
1951
2163
|
const cacheDir = path.join(configDir, '.cache');
|
|
1952
|
-
const
|
|
2164
|
+
const configFileName = path.basename(configPath);
|
|
2165
|
+
const cachePath = path.join(cacheDir, configFileName);
|
|
1953
2166
|
try {
|
|
1954
2167
|
// 确保 .cache 目录存在
|
|
1955
2168
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
1956
2169
|
// 复制配置文件到 cache
|
|
1957
2170
|
await fs.copyFile(configPath, cachePath);
|
|
1958
2171
|
if (process.env.VERBOSE) {
|
|
1959
|
-
info(`已同步 cache: ${path.
|
|
2172
|
+
info(`已同步 cache: ${configFileName} -> ${path.relative(configDir, cachePath)}`);
|
|
1960
2173
|
}
|
|
1961
2174
|
}
|
|
1962
2175
|
catch (err) {
|
|
1963
2176
|
// cache 同步失败不应阻塞主流程,仅警告
|
|
1964
|
-
warn(`cache 同步失败: ${err.message}`);
|
|
2177
|
+
warn(`cache 同步失败: ${configFileName} -> ${path.relative(configDir, cachePath)}: ${err.message}`);
|
|
1965
2178
|
}
|
|
1966
2179
|
}
|
|
1967
2180
|
/**
|
|
@@ -1971,78 +2184,98 @@ async function syncCacheFile(configPath) {
|
|
|
1971
2184
|
* @param thirdPartyDir 3rdparty 目录路径
|
|
1972
2185
|
* @param options 处理选项
|
|
1973
2186
|
*/
|
|
1974
|
-
async function processAction(action, context,
|
|
2187
|
+
async function processAction(action, context, parentConfigPath, parentTargetDir, options) {
|
|
1975
2188
|
const indent = ' '.repeat(context.depth);
|
|
1976
|
-
|
|
1977
|
-
|
|
2189
|
+
if (action.name && options.skipActions.has(action.name)) {
|
|
2190
|
+
info(`${indent}跳过嵌套 action: name=${action.name}, 原因=skip-action`);
|
|
2191
|
+
debug(`${indent}CodePac action 跳过: name=${action.name}, command=${action.command}, skipActions=${Array.from(options.skipActions).join(',')}`);
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2194
|
+
// 1. 解析 action 命令和执行计划
|
|
2195
|
+
let plan;
|
|
1978
2196
|
try {
|
|
1979
|
-
|
|
2197
|
+
plan = buildActionExecutionPlan(action, {
|
|
2198
|
+
parentConfigPath,
|
|
2199
|
+
parentTargetDir,
|
|
2200
|
+
inheritedCodepacPlatforms: context.codepacPlatforms ?? context.platforms,
|
|
2201
|
+
});
|
|
1980
2202
|
}
|
|
1981
2203
|
catch (err) {
|
|
1982
|
-
warn(`${indent}
|
|
2204
|
+
warn(`${indent}跳过嵌套 action: name=${action.name ?? '(未命名)'}, 原因=无法解析命令, message=${err.message}`);
|
|
2205
|
+
debug(`${indent}CodePac action 解析失败: command=${action.command}`);
|
|
1983
2206
|
return;
|
|
1984
2207
|
}
|
|
1985
|
-
const libsDisplay =
|
|
1986
|
-
info(`${indent}
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
if (context.processedConfigs.has(
|
|
1991
|
-
warn(`${indent}
|
|
2208
|
+
const libsDisplay = plan.libraries.length > 0 ? plan.libraries.join(', ') : '全部依赖';
|
|
2209
|
+
info(`${indent}处理嵌套 action: name=${plan.actionName ?? '(未命名)'}, config=${plan.nestedConfigPath}, target=${plan.nestedTargetDir}, libs=[${libsDisplay}]`);
|
|
2210
|
+
debug(`${indent}CodePac action 执行计划: command=${plan.command}, parentConfig=${parentConfigPath}, parentTarget=${parentTargetDir}, parsedConfigDir=${plan.configDir}, parsedTargetDir=${plan.targetDir}, resolvedConfig=${plan.nestedConfigPath}, resolvedTarget=${plan.nestedTargetDir}, inheritedPlatforms=${formatActionPlatforms(plan.inheritedPlatforms)}, effectivePlatforms=${formatActionPlatforms(plan.effectiveCodepacPlatforms)}, explicitPlatform=${plan.parsed.hasExplicitPlatform}, fullgit=${plan.parsed.hasExplicitFullGit}, unshallow=${plan.parsed.hasExplicitUnshallow}, disable_sparse=${plan.parsed.hasExplicitDisableSparse}`);
|
|
2211
|
+
// 2. 循环检测
|
|
2212
|
+
const visitKey = buildActionVisitKey(plan.nestedConfigPath, plan.nestedTargetDir);
|
|
2213
|
+
if (context.processedConfigs.has(visitKey)) {
|
|
2214
|
+
warn(`${indent} 跳过嵌套 action: name=${plan.actionName ?? '(未命名)'}, 原因=循环配置, config=${plan.nestedConfigPath}, target=${plan.nestedTargetDir}`);
|
|
1992
2215
|
return;
|
|
1993
2216
|
}
|
|
1994
|
-
context.processedConfigs.add(
|
|
1995
|
-
//
|
|
2217
|
+
context.processedConfigs.add(visitKey);
|
|
2218
|
+
// 3. 检查配置文件是否存在
|
|
1996
2219
|
try {
|
|
1997
|
-
await fs.access(nestedConfigPath);
|
|
2220
|
+
await fs.access(plan.nestedConfigPath);
|
|
1998
2221
|
}
|
|
1999
2222
|
catch {
|
|
2000
|
-
warn(`${indent}
|
|
2001
|
-
hint(`${indent} 请确保 ${
|
|
2223
|
+
warn(`${indent} 跳过嵌套 action: name=${plan.actionName ?? '(未命名)'}, 原因=配置不存在, config=${plan.nestedConfigPath}`);
|
|
2224
|
+
hint(`${indent} 请确保 ${plan.configDir} 库已被下载`);
|
|
2002
2225
|
return;
|
|
2003
2226
|
}
|
|
2004
|
-
//
|
|
2227
|
+
// 4. 提取指定库的依赖
|
|
2005
2228
|
let nestedResult;
|
|
2006
2229
|
try {
|
|
2007
|
-
nestedResult = await extractNestedDependencies(nestedConfigPath,
|
|
2230
|
+
nestedResult = await extractNestedDependencies(plan.nestedConfigPath, plan.libraries, {
|
|
2231
|
+
platforms: plan.effectiveCodepacPlatforms,
|
|
2232
|
+
configPath: plan.nestedConfigPath,
|
|
2233
|
+
});
|
|
2008
2234
|
}
|
|
2009
2235
|
catch (err) {
|
|
2010
|
-
warn(`${indent}
|
|
2236
|
+
warn(`${indent} 跳过嵌套 action: name=${plan.actionName ?? '(未命名)'}, 原因=解析配置失败, message=${err.message}`);
|
|
2011
2237
|
return;
|
|
2012
2238
|
}
|
|
2013
2239
|
const { dependencies, vars, nestedActions } = nestedResult;
|
|
2014
2240
|
if (dependencies.length === 0) {
|
|
2015
|
-
warn(`${indent}
|
|
2241
|
+
warn(`${indent} 跳过嵌套 action: name=${plan.actionName ?? '(未命名)'}, 原因=未找到依赖, libs=[${libsDisplay}], config=${plan.nestedConfigPath}`);
|
|
2016
2242
|
return;
|
|
2017
2243
|
}
|
|
2018
|
-
info(`${indent}
|
|
2019
|
-
//
|
|
2244
|
+
info(`${indent} 嵌套 action 命中 ${dependencies.length} 个依赖,继承平台: ${formatActionPlatforms(plan.inheritedPlatforms)}, 生效平台: ${formatActionPlatforms(plan.effectiveCodepacPlatforms)}`);
|
|
2245
|
+
// 5. 合并变量
|
|
2020
2246
|
const mergedVars = { ...context.vars, ...vars };
|
|
2021
|
-
//
|
|
2247
|
+
// 6. 处理这些依赖(targetDir 指定嵌套依赖的目标目录)
|
|
2022
2248
|
await linkNestedDependencies(dependencies, {
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
context: { ...context, vars: mergedVars },
|
|
2249
|
+
targetDir: plan.nestedTargetDir,
|
|
2250
|
+
nestedConfigPath: plan.nestedConfigPath,
|
|
2251
|
+
context: { ...context, codepacPlatforms: plan.effectiveCodepacPlatforms, vars: mergedVars },
|
|
2027
2252
|
options,
|
|
2028
2253
|
indent,
|
|
2029
2254
|
});
|
|
2030
2255
|
// 同步嵌套配置文件的 cache(兼容 checkValid.js 检测)
|
|
2031
|
-
await syncCacheFile(nestedConfigPath);
|
|
2032
|
-
//
|
|
2033
|
-
|
|
2034
|
-
if (!parsed.disableAction && nestedActions.length > 0) {
|
|
2256
|
+
await syncCacheFile(plan.nestedConfigPath);
|
|
2257
|
+
// 7. 递归处理嵌套 actions(如果没有 disable_action)
|
|
2258
|
+
if (!plan.parsed.disableAction && nestedActions.length > 0) {
|
|
2035
2259
|
const nestedContext = {
|
|
2036
2260
|
depth: context.depth + 1,
|
|
2037
2261
|
processedConfigs: context.processedConfigs,
|
|
2038
2262
|
platforms: context.platforms,
|
|
2263
|
+
codepacPlatforms: plan.effectiveCodepacPlatforms,
|
|
2039
2264
|
vars: mergedVars,
|
|
2040
2265
|
};
|
|
2041
|
-
const nestedThirdPartyDir = path.join(thirdPartyDir, parsed.targetDir);
|
|
2042
2266
|
for (const nestedAction of nestedActions) {
|
|
2043
|
-
await processAction(nestedAction, nestedContext,
|
|
2267
|
+
await processAction(nestedAction, nestedContext, plan.nestedConfigPath, plan.nestedTargetDir, options);
|
|
2044
2268
|
}
|
|
2045
2269
|
}
|
|
2270
|
+
else if (plan.parsed.disableAction && nestedActions.length > 0) {
|
|
2271
|
+
info(`${indent} 跳过后续嵌套 action: name=${plan.actionName ?? '(未命名)'}, 原因=disable_action`);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
function formatActionPlatforms(platforms) {
|
|
2275
|
+
return platforms && platforms.length > 0 ? platforms.join(',') : 'all';
|
|
2276
|
+
}
|
|
2277
|
+
function buildActionVisitKey(configPath, targetDir) {
|
|
2278
|
+
return `${configPath}::${targetDir}`;
|
|
2046
2279
|
}
|
|
2047
2280
|
/**
|
|
2048
2281
|
* 统一的本地目录验证+吸收流程
|
|
@@ -2100,9 +2333,7 @@ async function resolveLocalAndAbsorb(localPath, libName, commit, options) {
|
|
|
2100
2333
|
* 链接嵌套依赖
|
|
2101
2334
|
*/
|
|
2102
2335
|
async function linkNestedDependencies(dependencies, params) {
|
|
2103
|
-
const {
|
|
2104
|
-
// 计算嵌套依赖的实际目标目录
|
|
2105
|
-
const nestedTargetDir = path.join(thirdPartyDir, targetDir);
|
|
2336
|
+
const { targetDir: nestedTargetDir, context, options, indent } = params;
|
|
2106
2337
|
const { tx, registry, projectHash, projectRoot, dryRun, download, generalLibs, downloadedLibs, nestedLinkedDeps } = options;
|
|
2107
2338
|
const { platforms, vars } = context;
|
|
2108
2339
|
for (const dep of dependencies) {
|
|
@@ -2121,14 +2352,12 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2121
2352
|
// 不存在
|
|
2122
2353
|
}
|
|
2123
2354
|
// 检查 Store 状态和历史记录
|
|
2124
|
-
const libKey = registry.getLibraryKey(dep.libName, dep.commit);
|
|
2125
|
-
const historyLib = registry.getLibrary(libKey);
|
|
2126
2355
|
const unavailablePlatforms = getBlockedRequestedPlatforms(registry, dep, platforms, vars);
|
|
2127
2356
|
// 过滤掉已知不可用的平台
|
|
2128
2357
|
const availablePlatforms = platforms.filter(p => !unavailablePlatforms.includes(p));
|
|
2129
2358
|
const knownUnavailable = platforms.filter(p => unavailablePlatforms.includes(p));
|
|
2130
2359
|
let storeHas = false;
|
|
2131
|
-
|
|
2360
|
+
let existingPlatforms = [];
|
|
2132
2361
|
const initialStoreState = await resolveRequestedStoreState(dep, availablePlatforms, vars);
|
|
2133
2362
|
existingPlatforms.push(...initialStoreState.actualExisting);
|
|
2134
2363
|
if (initialStoreState.satisfiedRequested.length > 0) {
|
|
@@ -2138,6 +2367,7 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2138
2367
|
// 完整性校验:验证 existing 平台的文件完整性(与 classifyDependencies 同逻辑)
|
|
2139
2368
|
if (existingPlatforms.length > 0) {
|
|
2140
2369
|
const { valid: verifiedPlatforms, corrupted } = await verifyExistingPlatforms(dep.libName, dep.commit, existingPlatforms);
|
|
2370
|
+
existingPlatforms = verifiedPlatforms;
|
|
2141
2371
|
if (corrupted.length > 0) {
|
|
2142
2372
|
for (const p of corrupted) {
|
|
2143
2373
|
const storeKey = registry.getStoreKey(dep.libName, dep.commit, p);
|
|
@@ -2145,10 +2375,10 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2145
2375
|
const platformPath = store.getLibraryPath(await store.getStorePath(), dep.libName, dep.commit, p);
|
|
2146
2376
|
await fs.rm(platformPath, { recursive: true, force: true }).catch(() => { });
|
|
2147
2377
|
}
|
|
2148
|
-
storeHas = verifiedPlatforms.length > 0;
|
|
2149
2378
|
}
|
|
2379
|
+
storeHas = existingPlatforms.length > 0;
|
|
2150
2380
|
}
|
|
2151
|
-
let isGeneral = await resolveLibraryGeneralState(dep);
|
|
2381
|
+
let isGeneral = await resolveLibraryGeneralState(dep, availablePlatforms, vars);
|
|
2152
2382
|
// 如果所有平台都已知不可用,跳过
|
|
2153
2383
|
if (availablePlatforms.length === 0 && knownUnavailable.length > 0 && !isGeneral) {
|
|
2154
2384
|
warn(`${indent} ${dep.libName} - 平台 [${knownUnavailable.join(', ')}] 已按规则跳过`);
|
|
@@ -2169,10 +2399,21 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2169
2399
|
if (resolveResult.isGeneral) {
|
|
2170
2400
|
isGeneral = true;
|
|
2171
2401
|
}
|
|
2402
|
+
else if (resolveResult.absorbResult) {
|
|
2403
|
+
const absorbedPlatforms = resolveResult.absorbResult.platforms;
|
|
2404
|
+
const absorbedPlatformSet = new Set(absorbedPlatforms);
|
|
2405
|
+
const linkableAbsorbedPlatforms = [...new Set(availablePlatforms.flatMap((platform) => getRequestedPlatformTargets(platform, dep.sparse, vars)
|
|
2406
|
+
.filter((target) => absorbedPlatformSet.has(target))))];
|
|
2407
|
+
existingPlatforms = [...new Set([
|
|
2408
|
+
...existingPlatforms,
|
|
2409
|
+
...linkableAbsorbedPlatforms,
|
|
2410
|
+
])];
|
|
2411
|
+
debug(`${indent} ${dep.libName} - 本地吸收后平台回填: requested=${availablePlatforms.join(', ') || '无'}, absorbed=${absorbedPlatforms.join(', ') || '无'}, linkable=${existingPlatforms.join(', ') || '无'}`);
|
|
2412
|
+
}
|
|
2172
2413
|
if (resolveResult.absorbResult?.nestedLibraries.length) {
|
|
2173
2414
|
await registerNestedLibraries(resolveResult.absorbResult.nestedLibraries, projectHash);
|
|
2174
2415
|
}
|
|
2175
|
-
storeHas =
|
|
2416
|
+
storeHas = isGeneral || existingPlatforms.length > 0;
|
|
2176
2417
|
// 吸收为 General 后,检查是否实际需要平台内容
|
|
2177
2418
|
// 本地可能只有部分文件(如只有 _shared 内容),被误分类为 General
|
|
2178
2419
|
if (resolveResult.isGeneral && download) {
|
|
@@ -2187,6 +2428,7 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2187
2428
|
platforms: availablePlatforms,
|
|
2188
2429
|
sparse: dep.sparse,
|
|
2189
2430
|
vars,
|
|
2431
|
+
useGitLightweightDownload: options.gitLightweightDownload,
|
|
2190
2432
|
});
|
|
2191
2433
|
const assessment = assessDownloadResult(availablePlatforms, dep.sparse, downloadResult, vars);
|
|
2192
2434
|
clearSatisfiedAutoUnavailablePlatforms(registry, dep, assessment.satisfiedRequested);
|
|
@@ -2226,7 +2468,7 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2226
2468
|
let linkedPlatforms = isGeneral ? [GENERAL_PLATFORM] : [...existingPlatforms];
|
|
2227
2469
|
// 已链接,但需要检查是否需要补充缺失平台(与顶层依赖逻辑一致)
|
|
2228
2470
|
if (!isGeneral) {
|
|
2229
|
-
const supplementResult = await supplementMissingPlatforms(dep, platforms, registry, tx, { vars });
|
|
2471
|
+
const supplementResult = await supplementMissingPlatforms(dep, platforms, registry, tx, { vars, useGitLightweightDownload: options.gitLightweightDownload });
|
|
2230
2472
|
// 注册嵌套依赖
|
|
2231
2473
|
if (supplementResult.nestedLibraries.length > 0) {
|
|
2232
2474
|
await registerNestedLibraries(supplementResult.nestedLibraries, projectHash);
|
|
@@ -2297,7 +2539,7 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2297
2539
|
}
|
|
2298
2540
|
else {
|
|
2299
2541
|
// 平台库:先补充缺失平台(与顶层依赖逻辑一致)
|
|
2300
|
-
const supplementResult = await supplementMissingPlatforms(dep, platforms, registry, tx, { vars });
|
|
2542
|
+
const supplementResult = await supplementMissingPlatforms(dep, platforms, registry, tx, { vars, useGitLightweightDownload: options.gitLightweightDownload });
|
|
2301
2543
|
// 注册嵌套依赖
|
|
2302
2544
|
if (supplementResult.nestedLibraries.length > 0) {
|
|
2303
2545
|
await registerNestedLibraries(supplementResult.nestedLibraries, projectHash);
|
|
@@ -2346,6 +2588,7 @@ async function linkNestedDependencies(dependencies, params) {
|
|
|
2346
2588
|
platforms: availablePlatforms,
|
|
2347
2589
|
sparse: dep.sparse,
|
|
2348
2590
|
vars,
|
|
2591
|
+
useGitLightweightDownload: options.gitLightweightDownload,
|
|
2349
2592
|
onTempDirCreated: (_tempDir, libDir) => { downloadMonitor.start(libDir); },
|
|
2350
2593
|
onHeartbeat: (message) => { downloadMonitor.heartbeat(message); },
|
|
2351
2594
|
});
|
|
@@ -2477,26 +2720,62 @@ export async function verifyExistingPlatforms(libName, commit, existingPlatforms
|
|
|
2477
2720
|
for (const platform of existingPlatforms) {
|
|
2478
2721
|
const storeKey = registry.getStoreKey(libName, commit, platform);
|
|
2479
2722
|
const entry = registry.getStore(storeKey);
|
|
2480
|
-
// 没有 StoreEntry,跳过校验视为合法
|
|
2481
2723
|
if (!entry) {
|
|
2724
|
+
try {
|
|
2725
|
+
const integrity = await store.captureIntegrity(libName, commit, platform);
|
|
2726
|
+
if (integrity.fileCount === 0) {
|
|
2727
|
+
corrupted.push(platform);
|
|
2728
|
+
warn(`${libName} (${commit.slice(0, 7)}) ${platform}: Store 目录为空`);
|
|
2729
|
+
continue;
|
|
2730
|
+
}
|
|
2731
|
+
registry.addStore({
|
|
2732
|
+
libName,
|
|
2733
|
+
commit,
|
|
2734
|
+
platform,
|
|
2735
|
+
branch: '',
|
|
2736
|
+
url: '',
|
|
2737
|
+
...integrity,
|
|
2738
|
+
usedBy: [],
|
|
2739
|
+
createdAt: new Date().toISOString(),
|
|
2740
|
+
lastAccess: new Date().toISOString(),
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
catch {
|
|
2744
|
+
corrupted.push(platform);
|
|
2745
|
+
warn(`${libName} (${commit.slice(0, 7)}) ${platform}: Store 完整性回填失败`);
|
|
2746
|
+
continue;
|
|
2747
|
+
}
|
|
2482
2748
|
valid.push(platform);
|
|
2483
2749
|
continue;
|
|
2484
2750
|
}
|
|
2751
|
+
if (entry.fileCount === 0) {
|
|
2752
|
+
corrupted.push(platform);
|
|
2753
|
+
warn(`${libName} (${commit.slice(0, 7)}) ${platform}: Store 记录为空`);
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2485
2756
|
// 旧数据回填:升级后首次遇到无 fileCount 的条目,顺便记录完整性数据
|
|
2486
2757
|
if (entry.fileCount == null) {
|
|
2487
2758
|
try {
|
|
2488
2759
|
const integrity = await store.captureIntegrity(libName, commit, platform);
|
|
2489
2760
|
registry.updateStore(storeKey, integrity);
|
|
2761
|
+
if (integrity.fileCount === 0) {
|
|
2762
|
+
corrupted.push(platform);
|
|
2763
|
+
warn(`${libName} (${commit.slice(0, 7)}) ${platform}: Store 目录为空`);
|
|
2764
|
+
continue;
|
|
2765
|
+
}
|
|
2490
2766
|
}
|
|
2491
2767
|
catch {
|
|
2492
|
-
|
|
2768
|
+
corrupted.push(platform);
|
|
2769
|
+
warn(`${libName} (${commit.slice(0, 7)}) ${platform}: Store 完整性回填失败`);
|
|
2770
|
+
continue;
|
|
2493
2771
|
}
|
|
2494
2772
|
valid.push(platform);
|
|
2495
2773
|
continue;
|
|
2496
2774
|
}
|
|
2497
|
-
const result = await store.
|
|
2775
|
+
const result = await store.fullVerify(libName, commit, platform, {
|
|
2498
2776
|
size: entry.size,
|
|
2499
2777
|
fileCount: entry.fileCount,
|
|
2778
|
+
contentHash: entry.contentHash,
|
|
2500
2779
|
});
|
|
2501
2780
|
if (result.valid) {
|
|
2502
2781
|
valid.push(platform);
|