@lazycatcloud/lzc-cli 2.0.0-pre.0 → 2.0.0-pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -7
- package/changelog.md +56 -19
- package/lib/app/apkshell.js +7 -44
- package/lib/app/index.js +5 -1
- package/lib/app/lpk_build.js +266 -56
- package/lib/app/lpk_build_images.js +424 -229
- package/lib/app/lpk_build_images_local.js +425 -0
- package/lib/app/lpk_build_images_pack_local.js +409 -0
- package/lib/app/lpk_create.js +158 -83
- package/lib/app/lpk_create_generator.js +35 -42
- package/lib/app/lpk_devshell.js +6 -2
- package/lib/app/lpk_installer.js +4 -3
- package/lib/app/manifest_build.js +259 -0
- package/lib/app/project_cp.js +5 -10
- package/lib/app/project_deploy.js +80 -11
- package/lib/app/project_exec.js +48 -11
- package/lib/app/project_info.js +59 -59
- package/lib/app/project_log.js +5 -10
- package/lib/app/project_runtime.js +113 -18
- package/lib/app/project_start.js +6 -11
- package/lib/app/project_sync.js +499 -0
- package/lib/appstore/apkshell.js +50 -0
- package/lib/appstore/publish.js +54 -15
- package/lib/build_remote.js +0 -1
- package/lib/config/index.js +1 -1
- package/lib/debug_bridge.js +217 -47
- package/lib/i18n/locales/en/translation.json +262 -262
- package/lib/i18n/locales/zh/translation.json +262 -262
- package/lib/lpk/core.js +2 -1
- package/lib/migrate/index.js +52 -0
- package/lib/package_info.js +135 -0
- package/lib/shellapi.js +35 -1
- package/lib/sig/core.js +2 -2
- package/lib/utils.js +92 -15
- package/package.json +89 -89
- package/scripts/cli.js +2 -0
- package/scripts/smoke/frontend-dev-entry.mjs +104 -0
- package/scripts/smoke/template-project.mjs +311 -0
- package/template/_lpk/README.md +6 -3
- package/template/_lpk/gui-vnc.manifest.yml.in +0 -9
- package/template/_lpk/hello-vue.manifest.yml.in +38 -0
- package/template/_lpk/manifest.yml.in +0 -9
- package/template/_lpk/package.yml.in +7 -0
- package/template/_lpk/todolist-golang.manifest.yml.in +23 -9
- package/template/_lpk/todolist-java.manifest.yml.in +23 -9
- package/template/_lpk/todolist-python.manifest.yml.in +31 -9
- package/template/_lpk/todolist-serverless.manifest.yml.in +38 -0
- package/template/blank/lzc-build.dev.yml +4 -0
- package/template/blank/lzc-build.yml +0 -2
- package/template/blank/lzc-manifest.yml +3 -12
- package/template/blank/package.yml +7 -0
- package/template/golang/Dockerfile +1 -1
- package/template/golang/Dockerfile.dev +20 -0
- package/template/golang/README.md +22 -11
- package/template/golang/_lzcdevignore +21 -0
- package/template/golang/lzc-build.dev.yml +12 -0
- package/template/golang/lzc-build.yml +0 -5
- package/template/golang/main.go +1 -1
- package/template/golang/manifest.dev.page.js +24 -0
- package/template/golang/run.sh +7 -0
- package/template/gui-vnc/README.md +5 -1
- package/template/gui-vnc/lzc-build.dev.yml +4 -0
- package/template/gui-vnc/lzc-build.yml +0 -5
- package/template/python/Dockerfile +2 -2
- package/template/python/Dockerfile.dev +18 -0
- package/template/python/README.md +28 -11
- package/template/python/_lzcdevignore +21 -0
- package/template/python/app.py +1 -1
- package/template/python/lzc-build.dev.yml +12 -0
- package/template/python/lzc-build.yml +0 -5
- package/template/python/manifest.dev.page.js +25 -0
- package/template/python/run.sh +12 -1
- package/template/springboot/Dockerfile +1 -1
- package/template/springboot/Dockerfile.dev +20 -0
- package/template/springboot/README.md +22 -11
- package/template/springboot/_lzcdevignore +21 -0
- package/template/springboot/lzc-build.dev.yml +12 -0
- package/template/springboot/lzc-build.yml +0 -5
- package/template/springboot/manifest.dev.page.js +24 -0
- package/template/springboot/run.sh +7 -0
- package/template/vue/README.md +14 -27
- package/template/vue/_gitignore +0 -1
- package/template/vue/lzc-build.dev.yml +7 -0
- package/template/vue/lzc-build.yml +0 -2
- package/template/vue/manifest.dev.page.js +50 -0
- package/template/vue/src/App.vue +1 -1
- package/template/vue-minidb/README.md +11 -19
- package/template/vue-minidb/_gitignore +0 -1
- package/template/vue-minidb/lzc-build.dev.yml +7 -0
- package/template/vue-minidb/lzc-build.yml +0 -2
- package/template/vue-minidb/manifest.dev.page.js +50 -0
- package/template/blank/_gitignore +0 -1
|
@@ -10,13 +10,16 @@ import shellApi from '../shellapi.js';
|
|
|
10
10
|
import { collectContextFromDockerFile } from './lpk_devshell_docker.js';
|
|
11
11
|
import { isFileExist } from '../utils.js';
|
|
12
12
|
import { resolveBuildRemoteFromOptions } from '../build_remote.js';
|
|
13
|
+
import { buildLocalImageForPack, removeLocalDockerImages, saveLocalDockerImagesArchive } from './lpk_build_images_local.js';
|
|
14
|
+
import { packLocalArchiveToStageDir } from './lpk_build_images_pack_local.js';
|
|
13
15
|
|
|
14
16
|
const EMBED_PREFIX = 'embed:';
|
|
15
17
|
const EMBED_TYPO_PREFIXES = ['emebd:'];
|
|
16
18
|
const SHA256_PREFIX = 'sha256:';
|
|
17
19
|
const DEFAULT_UPSTREAM_MATCH = 'registry.lazycat.cloud';
|
|
18
|
-
const IMAGE_BUILD_CACHE_VERSION =
|
|
20
|
+
const IMAGE_BUILD_CACHE_VERSION = 4;
|
|
19
21
|
const IMAGE_PACKAGE_CACHE_VERSION = 1;
|
|
22
|
+
const DEFAULT_LOCAL_TARGET_PLATFORM = 'linux/amd64';
|
|
20
23
|
|
|
21
24
|
function isPlainObject(value) {
|
|
22
25
|
return value && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -123,6 +126,15 @@ function normalizeImageBuildEntries(rawConfig, cwd, manifest) {
|
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
128
|
|
|
129
|
+
const builderValue = config.builder;
|
|
130
|
+
let builder = 'remote';
|
|
131
|
+
if (builderValue !== undefined && builderValue !== null) {
|
|
132
|
+
builder = String(builderValue).trim().toLowerCase();
|
|
133
|
+
if (builder !== 'remote' && builder !== 'local') {
|
|
134
|
+
throw new Error(`images.${alias}.builder must be "remote" or "local"`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
126
138
|
entries.push({
|
|
127
139
|
alias,
|
|
128
140
|
contextDir,
|
|
@@ -130,6 +142,7 @@ function normalizeImageBuildEntries(rawConfig, cwd, manifest) {
|
|
|
130
142
|
dockerfileInlineContent,
|
|
131
143
|
imageLabel: `${packageName}-image-${sanitizeTagPart(alias, 'image')}:${version}`,
|
|
132
144
|
upstreamMatch,
|
|
145
|
+
builder,
|
|
133
146
|
});
|
|
134
147
|
}
|
|
135
148
|
|
|
@@ -276,6 +289,8 @@ function normalizeMetaForPackageCache(metaList) {
|
|
|
276
289
|
return [...metaList]
|
|
277
290
|
.map((meta) => ({
|
|
278
291
|
alias: String(meta?.alias ?? ''),
|
|
292
|
+
builder: String(meta?.builder ?? ''),
|
|
293
|
+
platform: String(meta?.platform ?? ''),
|
|
279
294
|
imageID: String(meta?.imageID ?? ''),
|
|
280
295
|
upstream: String(meta?.upstream ?? ''),
|
|
281
296
|
embeddedDiffIDs: [...(meta?.embeddedDiffIDs ?? [])].map((item) => String(item ?? '')),
|
|
@@ -421,30 +436,51 @@ function rewriteImagesLock(tempDir, fallbackLockImages = {}) {
|
|
|
421
436
|
}
|
|
422
437
|
|
|
423
438
|
function ensureDockerfileForEntry(entry) {
|
|
424
|
-
|
|
439
|
+
const targetDockerfilePath = path.join(entry.contextDir, 'Dockerfile');
|
|
440
|
+
const backupPath = `${targetDockerfilePath}.lzc-inline-backup-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
441
|
+
const hasOriginalDockerfile = fs.existsSync(targetDockerfilePath);
|
|
442
|
+
const restoreOriginalDockerfile = () => {
|
|
443
|
+
if (hasOriginalDockerfile && fs.existsSync(backupPath)) {
|
|
444
|
+
fs.copyFileSync(backupPath, targetDockerfilePath);
|
|
445
|
+
fs.rmSync(backupPath, { force: true });
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
fs.rmSync(targetDockerfilePath, { force: true });
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
if (entry?.dockerfileInlineContent) {
|
|
452
|
+
if (hasOriginalDockerfile) {
|
|
453
|
+
fs.copyFileSync(targetDockerfilePath, backupPath);
|
|
454
|
+
}
|
|
455
|
+
fs.writeFileSync(targetDockerfilePath, entry.dockerfileInlineContent);
|
|
425
456
|
return {
|
|
426
|
-
dockerfilePath:
|
|
457
|
+
dockerfilePath: targetDockerfilePath,
|
|
458
|
+
cleanup: restoreOriginalDockerfile,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (!entry?.dockerfilePath) {
|
|
463
|
+
return {
|
|
464
|
+
dockerfilePath: targetDockerfilePath,
|
|
465
|
+
cleanup: () => {},
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const resolvedDockerfilePath = path.resolve(entry.dockerfilePath);
|
|
470
|
+
if (path.resolve(targetDockerfilePath) === resolvedDockerfilePath) {
|
|
471
|
+
return {
|
|
472
|
+
dockerfilePath: resolvedDockerfilePath,
|
|
427
473
|
cleanup: () => {},
|
|
428
474
|
};
|
|
429
475
|
}
|
|
430
476
|
|
|
431
|
-
const dockerfilePath = path.join(entry.contextDir, 'Dockerfile');
|
|
432
|
-
const backupPath = `${dockerfilePath}.lzc-inline-backup-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
433
|
-
const hasOriginalDockerfile = fs.existsSync(dockerfilePath);
|
|
434
477
|
if (hasOriginalDockerfile) {
|
|
435
|
-
fs.copyFileSync(
|
|
478
|
+
fs.copyFileSync(targetDockerfilePath, backupPath);
|
|
436
479
|
}
|
|
437
|
-
fs.
|
|
480
|
+
fs.copyFileSync(resolvedDockerfilePath, targetDockerfilePath);
|
|
438
481
|
return {
|
|
439
|
-
dockerfilePath,
|
|
440
|
-
cleanup:
|
|
441
|
-
if (hasOriginalDockerfile && fs.existsSync(backupPath)) {
|
|
442
|
-
fs.copyFileSync(backupPath, dockerfilePath);
|
|
443
|
-
fs.rmSync(backupPath, { force: true });
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
fs.rmSync(dockerfilePath, { force: true });
|
|
447
|
-
},
|
|
482
|
+
dockerfilePath: targetDockerfilePath,
|
|
483
|
+
cleanup: restoreOriginalDockerfile,
|
|
448
484
|
};
|
|
449
485
|
}
|
|
450
486
|
|
|
@@ -467,6 +503,109 @@ async function sha256File(filePath) {
|
|
|
467
503
|
});
|
|
468
504
|
}
|
|
469
505
|
|
|
506
|
+
async function extractPackedImagesArchive(archivePath, targetDir) {
|
|
507
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
508
|
+
await tar.x({
|
|
509
|
+
file: archivePath,
|
|
510
|
+
cwd: targetDir,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function loadPackedResult(stageDir) {
|
|
515
|
+
const packedResultPath = path.join(stageDir, 'pack-result.json');
|
|
516
|
+
if (!isFileExist(packedResultPath)) {
|
|
517
|
+
throw new Error('pack-result.json not found after pack-images');
|
|
518
|
+
}
|
|
519
|
+
const packedResult = JSON.parse(fs.readFileSync(packedResultPath, 'utf-8'));
|
|
520
|
+
fs.rmSync(packedResultPath, { force: true });
|
|
521
|
+
if (!isPlainObject(packedResult?.lockImages)) {
|
|
522
|
+
throw new Error('Invalid pack-result.json: lockImages is missing');
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
lockImages: packedResult.lockImages,
|
|
526
|
+
embeddedLayerBytes: Number(packedResult?.embeddedLayerBytes ?? 0),
|
|
527
|
+
embeddedLayerCount: Number(packedResult?.embeddedLayerCount ?? 0),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function mergeOciLayout(sourceDir, targetDir) {
|
|
532
|
+
const sourceImagesDir = path.join(sourceDir, 'images');
|
|
533
|
+
if (!fs.existsSync(sourceImagesDir)) {
|
|
534
|
+
throw new Error(`packed images dir not found: ${sourceImagesDir}`);
|
|
535
|
+
}
|
|
536
|
+
const sourceBlobsDir = path.join(sourceImagesDir, 'blobs', 'sha256');
|
|
537
|
+
const targetImagesDir = path.join(targetDir, 'images');
|
|
538
|
+
const targetBlobsDir = path.join(targetImagesDir, 'blobs', 'sha256');
|
|
539
|
+
fs.mkdirSync(targetBlobsDir, { recursive: true });
|
|
540
|
+
if (fs.existsSync(sourceBlobsDir)) {
|
|
541
|
+
for (const fileName of fs.readdirSync(sourceBlobsDir)) {
|
|
542
|
+
const sourcePath = path.join(sourceBlobsDir, fileName);
|
|
543
|
+
const targetPath = path.join(targetBlobsDir, fileName);
|
|
544
|
+
if (!isFileExist(targetPath)) {
|
|
545
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const sourceIndexPath = path.join(sourceImagesDir, 'index.json');
|
|
551
|
+
const targetIndexPath = path.join(targetImagesDir, 'index.json');
|
|
552
|
+
const sourceIndex = isFileExist(sourceIndexPath) ? JSON.parse(fs.readFileSync(sourceIndexPath, 'utf-8')) : { manifests: [] };
|
|
553
|
+
const targetIndex = isFileExist(targetIndexPath)
|
|
554
|
+
? JSON.parse(fs.readFileSync(targetIndexPath, 'utf-8'))
|
|
555
|
+
: {
|
|
556
|
+
schemaVersion: 2,
|
|
557
|
+
manifests: [],
|
|
558
|
+
};
|
|
559
|
+
const seenDigest = new Set((targetIndex.manifests ?? []).map((item) => String(item?.digest ?? '').trim()).filter((item) => item));
|
|
560
|
+
for (const manifest of sourceIndex.manifests ?? []) {
|
|
561
|
+
const digest = String(manifest?.digest ?? '').trim();
|
|
562
|
+
if (!digest || seenDigest.has(digest)) {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
seenDigest.add(digest);
|
|
566
|
+
targetIndex.manifests.push(manifest);
|
|
567
|
+
}
|
|
568
|
+
fs.mkdirSync(targetImagesDir, { recursive: true });
|
|
569
|
+
fs.writeFileSync(path.join(targetImagesDir, 'oci-layout'), JSON.stringify({ imageLayoutVersion: '1.0.0' }));
|
|
570
|
+
fs.writeFileSync(targetIndexPath, JSON.stringify(targetIndex, null, 2));
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function computeEmbeddedStats(lockImages, tempDir) {
|
|
574
|
+
const imagesDir = path.join(tempDir, 'images', 'blobs', 'sha256');
|
|
575
|
+
const seenDigest = new Set();
|
|
576
|
+
let embeddedLayerBytes = 0;
|
|
577
|
+
for (const imageInfo of Object.values(lockImages ?? {})) {
|
|
578
|
+
for (const layer of imageInfo?.layers ?? []) {
|
|
579
|
+
const source = String(layer?.source ?? '').trim().toLowerCase();
|
|
580
|
+
const digest = String(layer?.digest ?? '').trim();
|
|
581
|
+
if (source !== 'embed' || !digest.startsWith(SHA256_PREFIX) || seenDigest.has(digest)) {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
seenDigest.add(digest);
|
|
585
|
+
const blobPath = path.join(imagesDir, digest.slice(SHA256_PREFIX.length));
|
|
586
|
+
if (isFileExist(blobPath)) {
|
|
587
|
+
embeddedLayerBytes += fs.statSync(blobPath).size;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
embeddedLayerBytes,
|
|
593
|
+
embeddedLayerCount: seenDigest.size,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async function packToStageDir(bridge, targetDir, options) {
|
|
598
|
+
const packedArchivePath = path.join(targetDir, 'images.packed.tar');
|
|
599
|
+
if (options.mode === 'archive') {
|
|
600
|
+
await bridge.packImagesArchive(options.packSpecs, options.archivePath, packedArchivePath);
|
|
601
|
+
} else {
|
|
602
|
+
await bridge.packImages(options.packSpecs, packedArchivePath);
|
|
603
|
+
}
|
|
604
|
+
await extractPackedImagesArchive(packedArchivePath, targetDir);
|
|
605
|
+
fs.rmSync(packedArchivePath, { force: true });
|
|
606
|
+
return loadPackedResult(targetDir);
|
|
607
|
+
}
|
|
608
|
+
|
|
470
609
|
export async function buildConfiguredImagesToTempDir(rawConfig, manifest, cwd, tempDir, options = {}) {
|
|
471
610
|
const imageEntries = normalizeImageBuildEntries(rawConfig, cwd, manifest);
|
|
472
611
|
if (imageEntries.length === 0) {
|
|
@@ -484,245 +623,301 @@ export async function buildConfiguredImagesToTempDir(rawConfig, manifest, cwd, t
|
|
|
484
623
|
}
|
|
485
624
|
|
|
486
625
|
const buildRemote = resolveBuildRemoteFromOptions(options, 'lzc-build.yml');
|
|
487
|
-
|
|
488
|
-
|
|
626
|
+
const hasLocalBuilder = imageEntries.some((item) => item.builder === 'local');
|
|
627
|
+
const hasRemoteBuilder = imageEntries.some((item) => item.builder === 'remote');
|
|
628
|
+
let bridge = null;
|
|
629
|
+
let targetPlatform = '';
|
|
630
|
+
if (hasRemoteBuilder) {
|
|
631
|
+
if (!buildRemote) {
|
|
632
|
+
await shellApi.init();
|
|
633
|
+
}
|
|
634
|
+
bridge = new DebugBridge(cwd, buildRemote);
|
|
635
|
+
await bridge.init();
|
|
636
|
+
}
|
|
637
|
+
if (hasLocalBuilder) {
|
|
638
|
+
targetPlatform = bridge ? await bridge.platform() : DEFAULT_LOCAL_TARGET_PLATFORM;
|
|
489
639
|
}
|
|
490
|
-
const bridge = new DebugBridge(cwd, buildRemote);
|
|
491
|
-
await bridge.init();
|
|
492
|
-
|
|
493
640
|
const imageMetaByRef = new Map();
|
|
494
641
|
const imageRefs = [];
|
|
495
642
|
const buildCache = loadImageBuildCache(cwd);
|
|
496
643
|
const buildCacheRecords = { ...buildCache.records };
|
|
644
|
+
const localBuiltRefs = [];
|
|
497
645
|
|
|
498
|
-
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
dockerfileSource
|
|
514
|
-
|
|
515
|
-
contextCollectMs = Date.now() - contextStartAt;
|
|
516
|
-
const contextHash = await sha256File(contextTar);
|
|
517
|
-
const contextDigest = `${SHA256_PREFIX}${contextHash.digest}`;
|
|
518
|
-
|
|
519
|
-
const cacheRecord = buildCache.records?.[entry.alias];
|
|
520
|
-
let builtImageRef = '';
|
|
521
|
-
let reusedFromCache = false;
|
|
522
|
-
let imageID = '';
|
|
523
|
-
let builtDiffIDs = [];
|
|
524
|
-
let upstream = '';
|
|
525
|
-
let upstreamDiffIDs = [];
|
|
526
|
-
let archiveKey = '';
|
|
527
|
-
|
|
528
|
-
if (
|
|
529
|
-
cacheRecord &&
|
|
530
|
-
cacheRecord.image_label === entry.imageLabel &&
|
|
531
|
-
cacheRecord.context_digest === contextDigest &&
|
|
532
|
-
cacheRecord.upstream_match === entry.upstreamMatch &&
|
|
533
|
-
cacheRecord.build_mode === 'pack' &&
|
|
534
|
-
Array.isArray(cacheRecord.built_diff_ids) &&
|
|
535
|
-
typeof cacheRecord.image_id === 'string' &&
|
|
536
|
-
cacheRecord.image_id.trim() !== ''
|
|
537
|
-
) {
|
|
646
|
+
try {
|
|
647
|
+
for (const entry of imageEntries) {
|
|
648
|
+
const profileStartAt = Date.now();
|
|
649
|
+
let contextCollectMs = 0;
|
|
650
|
+
let buildStageMs = 0;
|
|
651
|
+
let resolveStageMs = 0;
|
|
652
|
+
let buildMode = entry.builder === 'local' ? `local:${targetPlatform}` : 'build-pack';
|
|
653
|
+
|
|
654
|
+
const dockerfileDesc = entry.dockerfilePath || '(inline dockerfile-content)';
|
|
655
|
+
if (entry.builder === 'local') {
|
|
656
|
+
logger.info(`Build image for alias "${entry.alias}" from ${dockerfileDesc} (builder=local, target=${targetPlatform})`);
|
|
657
|
+
} else {
|
|
658
|
+
logger.info(`Build image for alias "${entry.alias}" from ${dockerfileDesc} (builder=remote, target-box=${bridge?.boxname ?? 'unknown'})`);
|
|
659
|
+
}
|
|
660
|
+
const contextStartAt = Date.now();
|
|
661
|
+
const dockerfileSource = ensureDockerfileForEntry(entry);
|
|
662
|
+
let contextTar = '';
|
|
538
663
|
try {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
664
|
+
contextTar = await collectContextFromDockerFile(entry.contextDir, dockerfileSource.dockerfilePath);
|
|
665
|
+
} finally {
|
|
666
|
+
dockerfileSource.cleanup();
|
|
667
|
+
}
|
|
668
|
+
contextCollectMs = Date.now() - contextStartAt;
|
|
669
|
+
const contextHash = await sha256File(contextTar);
|
|
670
|
+
const contextDigest = `${SHA256_PREFIX}${contextHash.digest}`;
|
|
671
|
+
|
|
672
|
+
const cacheRecord = buildCache.records?.[entry.alias];
|
|
673
|
+
let builtImageRef = '';
|
|
674
|
+
let reusedFromCache = false;
|
|
675
|
+
let imageID = '';
|
|
676
|
+
let builtDiffIDs = [];
|
|
677
|
+
let upstream = '';
|
|
678
|
+
let upstreamDiffIDs = [];
|
|
679
|
+
let archiveKey = '';
|
|
680
|
+
|
|
681
|
+
if (
|
|
682
|
+
entry.builder === 'remote' &&
|
|
683
|
+
cacheRecord &&
|
|
684
|
+
String(cacheRecord.builder ?? 'remote') === 'remote' &&
|
|
685
|
+
cacheRecord.image_label === entry.imageLabel &&
|
|
686
|
+
cacheRecord.context_digest === contextDigest &&
|
|
687
|
+
cacheRecord.upstream_match === entry.upstreamMatch &&
|
|
688
|
+
cacheRecord.build_mode === 'pack' &&
|
|
689
|
+
Array.isArray(cacheRecord.built_diff_ids) &&
|
|
690
|
+
typeof cacheRecord.image_id === 'string' &&
|
|
691
|
+
cacheRecord.image_id.trim() !== ''
|
|
692
|
+
) {
|
|
693
|
+
try {
|
|
694
|
+
imageID = normalizeSha256Digest(cacheRecord.image_id, `cached image id of alias ${entry.alias}`);
|
|
695
|
+
builtDiffIDs = normalizeDigestList(cacheRecord.built_diff_ids, `cached built diff ids of alias ${entry.alias}`);
|
|
696
|
+
if (builtDiffIDs.length === 0) {
|
|
697
|
+
throw new Error('built diff ids is empty');
|
|
698
|
+
}
|
|
699
|
+
upstream = cacheRecord.upstream ? String(cacheRecord.upstream).trim() : '';
|
|
700
|
+
if (upstream && !upstream.includes('@sha256:')) {
|
|
701
|
+
throw new Error(`invalid cached upstream: ${upstream}`);
|
|
702
|
+
}
|
|
703
|
+
upstreamDiffIDs = Array.isArray(cacheRecord.upstream_diff_ids)
|
|
704
|
+
? normalizeDigestList(cacheRecord.upstream_diff_ids, `cached upstream diff ids of alias ${entry.alias}`)
|
|
705
|
+
: [];
|
|
706
|
+
archiveKey = String(cacheRecord.archive_key ?? '').trim();
|
|
707
|
+
builtImageRef = typeof cacheRecord.image_ref === 'string' && cacheRecord.image_ref.trim() !== '' ? cacheRecord.image_ref : `debug.bridge/${entry.imageLabel}`;
|
|
708
|
+
reusedFromCache = true;
|
|
709
|
+
buildMode = 'build-pack-cache';
|
|
710
|
+
logger.info(`Reuse cached build-pack metadata for alias "${entry.alias}"`);
|
|
711
|
+
} catch (error) {
|
|
712
|
+
logger.info(`Cached build-pack metadata is invalid for alias "${entry.alias}", rebuild image`);
|
|
547
713
|
}
|
|
548
|
-
upstreamDiffIDs = Array.isArray(cacheRecord.upstream_diff_ids)
|
|
549
|
-
? normalizeDigestList(cacheRecord.upstream_diff_ids, `cached upstream diff ids of alias ${entry.alias}`)
|
|
550
|
-
: [];
|
|
551
|
-
archiveKey = String(cacheRecord.archive_key ?? '').trim();
|
|
552
|
-
builtImageRef = typeof cacheRecord.image_ref === 'string' && cacheRecord.image_ref.trim() !== '' ? cacheRecord.image_ref : `debug.bridge/${entry.imageLabel}`;
|
|
553
|
-
reusedFromCache = true;
|
|
554
|
-
buildMode = 'build-pack-cache';
|
|
555
|
-
logger.info(`Reuse cached build-pack metadata for alias "${entry.alias}"`);
|
|
556
|
-
} catch (error) {
|
|
557
|
-
logger.info(`Cached build-pack metadata is invalid for alias "${entry.alias}", rebuild image`);
|
|
558
714
|
}
|
|
559
|
-
}
|
|
560
715
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
716
|
+
const buildStartAt = Date.now();
|
|
717
|
+
if (entry.builder === 'local') {
|
|
718
|
+
const dockerfileSourceForLocalBuild = ensureDockerfileForEntry(entry);
|
|
719
|
+
try {
|
|
720
|
+
const localBuildResult = await buildLocalImageForPack(entry, targetPlatform, dockerfileSourceForLocalBuild.dockerfilePath);
|
|
721
|
+
builtImageRef = localBuildResult.ref;
|
|
722
|
+
imageID = normalizeSha256Digest(localBuildResult.imageID, `local build image id of alias ${entry.alias}`);
|
|
723
|
+
builtDiffIDs = normalizeDigestList(localBuildResult.builtDiffIDs ?? [], `local build diff ids of alias ${entry.alias}`);
|
|
724
|
+
archiveKey = '';
|
|
725
|
+
upstream = localBuildResult.upstream ? String(localBuildResult.upstream).trim() : '';
|
|
726
|
+
upstreamDiffIDs = Array.isArray(localBuildResult.upstreamDiffIDs)
|
|
727
|
+
? normalizeDigestList(localBuildResult.upstreamDiffIDs, `local build upstream diff ids of alias ${entry.alias}`)
|
|
728
|
+
: [];
|
|
729
|
+
localBuiltRefs.push(builtImageRef);
|
|
730
|
+
} finally {
|
|
731
|
+
dockerfileSourceForLocalBuild.cleanup();
|
|
732
|
+
fs.rmSync(contextTar, { force: true });
|
|
576
733
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
734
|
+
delete buildCacheRecords[entry.alias];
|
|
735
|
+
} else if (!reusedFromCache) {
|
|
736
|
+
const buildPackResult = await bridge.buildImageForPack(entry.imageLabel, contextTar);
|
|
737
|
+
builtImageRef = String(buildPackResult?.tag ?? '').trim() || `debug.bridge/${entry.imageLabel}`;
|
|
738
|
+
buildMode = 'build-pack';
|
|
739
|
+
archiveKey = String(buildPackResult?.archiveKey ?? '').trim();
|
|
740
|
+
imageID = normalizeSha256Digest(buildPackResult?.imageID, `build-pack image id of alias ${entry.alias}`);
|
|
741
|
+
builtDiffIDs = normalizeDigestList(buildPackResult?.diffIDs ?? [], `build-pack diff ids of alias ${entry.alias}`);
|
|
742
|
+
if (builtDiffIDs.length === 0) {
|
|
743
|
+
throw new Error(`No rootfs layer found in build-pack output for alias "${entry.alias}"`);
|
|
744
|
+
}
|
|
745
|
+
const baseRepoDigest = String(buildPackResult?.baseRepoDigest ?? '').trim();
|
|
746
|
+
if (baseRepoDigest) {
|
|
747
|
+
if (!baseRepoDigest.includes('@sha256:')) {
|
|
748
|
+
throw new Error(`Invalid baseRepoDigest from build-pack of alias "${entry.alias}": ${baseRepoDigest}`);
|
|
582
749
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
upstream =
|
|
586
|
-
|
|
750
|
+
const baseRepo = extractRepoFromRepoDigest(baseRepoDigest);
|
|
751
|
+
if (!entry.upstreamMatch || baseRepo.startsWith(entry.upstreamMatch)) {
|
|
752
|
+
upstream = baseRepoDigest;
|
|
753
|
+
if (Array.isArray(buildPackResult?.baseDiffIDs) && buildPackResult.baseDiffIDs.length > 0) {
|
|
754
|
+
upstreamDiffIDs = normalizeDigestList(buildPackResult.baseDiffIDs, `build-pack base diff ids of alias ${entry.alias}`);
|
|
755
|
+
}
|
|
756
|
+
if (upstreamDiffIDs.length > 0 && !startsWithDigestList(builtDiffIDs, upstreamDiffIDs)) {
|
|
757
|
+
logger.warn(`Ignore invalid upstream layer prefix for alias "${entry.alias}" from build-pack metadata`);
|
|
758
|
+
upstream = '';
|
|
759
|
+
upstreamDiffIDs = [];
|
|
760
|
+
}
|
|
587
761
|
}
|
|
588
762
|
}
|
|
763
|
+
} else {
|
|
764
|
+
fs.rmSync(contextTar, { force: true });
|
|
589
765
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
buildStageMs = Date.now() - buildStartAt;
|
|
594
|
-
imageRefs.push(builtImageRef);
|
|
766
|
+
buildStageMs = Date.now() - buildStartAt;
|
|
767
|
+
imageRefs.push(builtImageRef);
|
|
595
768
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
if (upstreamDiffIDs.length > 0 && !startsWithDigestList(builtDiffIDs, upstreamDiffIDs)) {
|
|
603
|
-
throw new Error(`Failed to derive mixed layers for alias "${entry.alias}", built layers do not start with upstream layers`);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const embeddedDiffIDs = upstreamDiffIDs.length === 0 ? builtDiffIDs : builtDiffIDs.slice(upstreamDiffIDs.length);
|
|
607
|
-
if (embeddedDiffIDs.length === 0 && !upstream) {
|
|
608
|
-
throw new Error(`Alias "${entry.alias}" has no embed layers and no upstream image`);
|
|
609
|
-
}
|
|
769
|
+
const resolveStartAt = Date.now();
|
|
770
|
+
if (!imageID || builtDiffIDs.length === 0) {
|
|
771
|
+
throw new Error(`No rootfs layer metadata found in build output for alias "${entry.alias}"`);
|
|
772
|
+
}
|
|
773
|
+
resolveStageMs = Date.now() - resolveStartAt;
|
|
610
774
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
upstream: upstream || '',
|
|
615
|
-
embeddedDiffIDs,
|
|
616
|
-
archiveKey: archiveKey || '',
|
|
617
|
-
});
|
|
618
|
-
buildCacheRecords[entry.alias] = {
|
|
619
|
-
image_label: entry.imageLabel,
|
|
620
|
-
context_digest: contextDigest,
|
|
621
|
-
upstream_match: entry.upstreamMatch,
|
|
622
|
-
image_ref: builtImageRef,
|
|
623
|
-
image_id: imageID,
|
|
624
|
-
build_mode: 'pack',
|
|
625
|
-
built_diff_ids: builtDiffIDs,
|
|
626
|
-
upstream: upstream || '',
|
|
627
|
-
upstream_diff_ids: upstreamDiffIDs,
|
|
628
|
-
archive_key: archiveKey || '',
|
|
629
|
-
};
|
|
775
|
+
if (upstreamDiffIDs.length > 0 && !startsWithDigestList(builtDiffIDs, upstreamDiffIDs)) {
|
|
776
|
+
throw new Error(`Failed to derive mixed layers for alias "${entry.alias}", built layers do not start with upstream layers`);
|
|
777
|
+
}
|
|
630
778
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
)} resolve=${formatDurationMs(resolveStageMs)} total=${formatDurationMs(Date.now() - profileStartAt)}`,
|
|
636
|
-
);
|
|
637
|
-
}
|
|
638
|
-
saveImageBuildCache(buildCache.cachePath, buildCacheRecords);
|
|
639
|
-
|
|
640
|
-
const packageCacheKey = computePackageCacheKey([...imageMetaByRef.values()]);
|
|
641
|
-
const cachedPackage = tryLoadPackageCache(cwd, packageCacheKey, tempDir);
|
|
642
|
-
let convertResult = null;
|
|
643
|
-
if (cachedPackage) {
|
|
644
|
-
logger.info('Reuse cached OCI image package');
|
|
645
|
-
convertResult = {
|
|
646
|
-
lockImages: Object.fromEntries(
|
|
647
|
-
[...imageMetaByRef.values()].map((meta) => [
|
|
648
|
-
meta.alias,
|
|
649
|
-
{
|
|
650
|
-
image_id: meta.imageID,
|
|
651
|
-
upstream: meta.upstream ?? '',
|
|
652
|
-
},
|
|
653
|
-
]),
|
|
654
|
-
),
|
|
655
|
-
embeddedLayerBytes: cachedPackage.embeddedLayerBytes,
|
|
656
|
-
embeddedLayerCount: cachedPackage.embeddedLayerCount,
|
|
657
|
-
};
|
|
658
|
-
}
|
|
779
|
+
const embeddedDiffIDs = upstreamDiffIDs.length === 0 ? builtDiffIDs : builtDiffIDs.slice(upstreamDiffIDs.length);
|
|
780
|
+
if (embeddedDiffIDs.length === 0 && !upstream) {
|
|
781
|
+
throw new Error(`Alias "${entry.alias}" has no embed layers and no upstream image`);
|
|
782
|
+
}
|
|
659
783
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
784
|
+
imageMetaByRef.set(builtImageRef, {
|
|
785
|
+
alias: entry.alias,
|
|
786
|
+
builder: entry.builder,
|
|
787
|
+
platform: entry.builder === 'local' ? targetPlatform : '',
|
|
788
|
+
imageID,
|
|
789
|
+
upstream: upstream || '',
|
|
790
|
+
embeddedDiffIDs,
|
|
791
|
+
archiveKey: archiveKey || '',
|
|
792
|
+
});
|
|
793
|
+
if (entry.builder === 'remote') {
|
|
794
|
+
buildCacheRecords[entry.alias] = {
|
|
795
|
+
builder: 'remote',
|
|
796
|
+
image_label: entry.imageLabel,
|
|
797
|
+
context_digest: contextDigest,
|
|
798
|
+
upstream_match: entry.upstreamMatch,
|
|
799
|
+
image_ref: builtImageRef,
|
|
800
|
+
image_id: imageID,
|
|
801
|
+
build_mode: 'pack',
|
|
802
|
+
built_diff_ids: builtDiffIDs,
|
|
803
|
+
upstream: upstream || '',
|
|
804
|
+
upstream_diff_ids: upstreamDiffIDs,
|
|
805
|
+
archive_key: archiveKey || '',
|
|
806
|
+
};
|
|
667
807
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
alias: meta.alias,
|
|
671
|
-
imageID: meta.imageID,
|
|
672
|
-
upstream: meta.upstream ?? '',
|
|
673
|
-
embeddedDiffIDs: meta.embeddedDiffIDs,
|
|
674
|
-
archiveKey: meta.archiveKey || undefined,
|
|
675
|
-
};
|
|
676
|
-
});
|
|
677
|
-
logger.info('Pack built images to OCI layout in debug bridge');
|
|
678
|
-
await bridge.packImages(packSpecs, packedArchivePath);
|
|
679
|
-
const packBridgeMs = Date.now() - packBridgeStartAt;
|
|
680
|
-
const extractStartAt = Date.now();
|
|
681
|
-
await tar.x({
|
|
682
|
-
file: packedArchivePath,
|
|
683
|
-
cwd: tempDir,
|
|
684
|
-
});
|
|
685
|
-
const extractMs = Date.now() - extractStartAt;
|
|
686
|
-
fs.rmSync(packedArchivePath, { force: true });
|
|
687
|
-
const parseStartAt = Date.now();
|
|
688
|
-
const packedResultPath = path.join(tempDir, 'pack-result.json');
|
|
689
|
-
if (!isFileExist(packedResultPath)) {
|
|
690
|
-
throw new Error('pack-result.json not found after pack-images');
|
|
691
|
-
}
|
|
692
|
-
const packedResult = JSON.parse(fs.readFileSync(packedResultPath, 'utf-8'));
|
|
693
|
-
fs.rmSync(packedResultPath, { force: true });
|
|
694
|
-
if (!isPlainObject(packedResult?.lockImages)) {
|
|
695
|
-
throw new Error('Invalid pack-result.json: lockImages is missing');
|
|
696
|
-
}
|
|
697
|
-
convertResult = {
|
|
698
|
-
lockImages: packedResult.lockImages,
|
|
699
|
-
embeddedLayerBytes: Number(packedResult?.embeddedLayerBytes ?? 0),
|
|
700
|
-
embeddedLayerCount: Number(packedResult?.embeddedLayerCount ?? 0),
|
|
701
|
-
};
|
|
702
|
-
const parseMs = Date.now() - parseStartAt;
|
|
808
|
+
|
|
809
|
+
logger.info(`Image alias "${entry.alias}" is ready: ${builtImageRef}`);
|
|
703
810
|
logger.info(
|
|
704
|
-
`[profile]
|
|
705
|
-
packBridgeMs + extractMs + parseMs,
|
|
706
|
-
)}`,
|
|
811
|
+
`[profile] alias=${entry.alias} mode=${buildMode} context=${formatDurationMs(contextCollectMs)} build=${formatDurationMs(buildStageMs)} resolve=${formatDurationMs(resolveStageMs)} total=${formatDurationMs(Date.now() - profileStartAt)}`,
|
|
707
812
|
);
|
|
708
813
|
}
|
|
709
|
-
|
|
710
|
-
|
|
814
|
+
saveImageBuildCache(buildCache.cachePath, buildCacheRecords);
|
|
815
|
+
|
|
816
|
+
const packageCacheKey = computePackageCacheKey([...imageMetaByRef.values()]);
|
|
817
|
+
const cachedPackage = tryLoadPackageCache(cwd, packageCacheKey, tempDir);
|
|
818
|
+
let convertResult = null;
|
|
819
|
+
if (cachedPackage) {
|
|
820
|
+
logger.info('Reuse cached OCI image package');
|
|
821
|
+
convertResult = {
|
|
822
|
+
lockImages: Object.fromEntries(
|
|
823
|
+
[...imageMetaByRef.values()].map((meta) => [
|
|
824
|
+
meta.alias,
|
|
825
|
+
{
|
|
826
|
+
image_id: meta.imageID,
|
|
827
|
+
upstream: meta.upstream ?? '',
|
|
828
|
+
},
|
|
829
|
+
]),
|
|
830
|
+
),
|
|
831
|
+
embeddedLayerBytes: cachedPackage.embeddedLayerBytes,
|
|
832
|
+
embeddedLayerCount: cachedPackage.embeddedLayerCount,
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (!convertResult) {
|
|
837
|
+
const mergedLockImages = {};
|
|
838
|
+
const remotePackSpecs = [];
|
|
839
|
+
const localPackSpecs = [];
|
|
840
|
+
for (const ref of imageRefs) {
|
|
841
|
+
const meta = imageMetaByRef.get(ref);
|
|
842
|
+
if (!meta) {
|
|
843
|
+
throw new Error(`Missing image meta for ref: ${ref}`);
|
|
844
|
+
}
|
|
845
|
+
const spec = {
|
|
846
|
+
ref,
|
|
847
|
+
alias: meta.alias,
|
|
848
|
+
imageID: meta.imageID,
|
|
849
|
+
upstream: meta.upstream ?? '',
|
|
850
|
+
embeddedDiffIDs: meta.embeddedDiffIDs,
|
|
851
|
+
archiveKey: meta.archiveKey || undefined,
|
|
852
|
+
};
|
|
853
|
+
if (meta.builder === 'local') {
|
|
854
|
+
localPackSpecs.push(spec);
|
|
855
|
+
} else {
|
|
856
|
+
remotePackSpecs.push(spec);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const packStartAt = Date.now();
|
|
861
|
+
if (remotePackSpecs.length > 0) {
|
|
862
|
+
const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lzc-cli-pack-remote-'));
|
|
863
|
+
try {
|
|
864
|
+
const remoteResult = await packToStageDir(bridge, stageDir, {
|
|
865
|
+
mode: 'remote',
|
|
866
|
+
packSpecs: remotePackSpecs,
|
|
867
|
+
});
|
|
868
|
+
mergeOciLayout(stageDir, tempDir);
|
|
869
|
+
Object.assign(mergedLockImages, remoteResult.lockImages);
|
|
870
|
+
} finally {
|
|
871
|
+
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (localPackSpecs.length > 0) {
|
|
875
|
+
const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lzc-cli-pack-local-'));
|
|
876
|
+
const archivePath = path.join(stageDir, 'local-images.docker.tar');
|
|
877
|
+
try {
|
|
878
|
+
const localRefs = localPackSpecs.map((item) => item.ref);
|
|
879
|
+
logger.info(`Prepare local-built images for packaging (builder=local, target=${targetPlatform})`);
|
|
880
|
+
logger.info(`Export local docker images to archive: ${localRefs.join(', ')}`);
|
|
881
|
+
await saveLocalDockerImagesArchive(localRefs, archivePath);
|
|
882
|
+
logger.info(`Pack local image archive to OCI layout locally (target=${targetPlatform})`);
|
|
883
|
+
const localResult = await packLocalArchiveToStageDir(archivePath, localPackSpecs, stageDir);
|
|
884
|
+
mergeOciLayout(stageDir, tempDir);
|
|
885
|
+
Object.assign(mergedLockImages, localResult.lockImages);
|
|
886
|
+
} finally {
|
|
887
|
+
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
logger.info(`[profile] pack-images total=${formatDurationMs(Date.now() - packStartAt)}`);
|
|
891
|
+
const compactedLockImages = rewriteImagesLock(tempDir, mergedLockImages);
|
|
892
|
+
const embeddedStats = computeEmbeddedStats(compactedLockImages, tempDir);
|
|
893
|
+
convertResult = {
|
|
894
|
+
lockImages: compactedLockImages,
|
|
895
|
+
embeddedLayerBytes: embeddedStats.embeddedLayerBytes,
|
|
896
|
+
embeddedLayerCount: embeddedStats.embeddedLayerCount,
|
|
897
|
+
};
|
|
711
898
|
savePackageCache(cwd, packageCacheKey, tempDir, convertResult);
|
|
712
899
|
}
|
|
713
900
|
|
|
714
901
|
const upstreamByAlias = {};
|
|
715
902
|
const resolvedImageByAlias = {};
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
903
|
+
for (const meta of imageMetaByRef.values()) {
|
|
904
|
+
upstreamByAlias[meta.alias] = meta.upstream || '';
|
|
905
|
+
resolvedImageByAlias[meta.alias] = meta.imageID;
|
|
906
|
+
}
|
|
720
907
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
908
|
+
return {
|
|
909
|
+
imageCount: Object.keys(convertResult.lockImages).length,
|
|
910
|
+
upstreamByAlias,
|
|
911
|
+
resolvedImageByAlias,
|
|
912
|
+
embeddedLayerBytes: convertResult.embeddedLayerBytes,
|
|
913
|
+
embeddedLayerCount: convertResult.embeddedLayerCount,
|
|
914
|
+
};
|
|
915
|
+
} finally {
|
|
916
|
+
await removeLocalDockerImages(localBuiltRefs);
|
|
917
|
+
}
|
|
728
918
|
}
|
|
919
|
+
|
|
920
|
+
export const __test__ = {
|
|
921
|
+
normalizeImageBuildEntries,
|
|
922
|
+
computePackageCacheKey,
|
|
923
|
+
};
|