feat(build): add verbose build output, progress logging, and timing for builds/tests

This commit is contained in:
2026-02-06 14:52:16 +00:00
parent 16cd0bbd87
commit 02b267ee10
8 changed files with 115 additions and 49 deletions
+31 -13
View File
@@ -1,6 +1,6 @@
import * as plugins from './tsdocker.plugins.js';
import * as paths from './tsdocker.paths.js';
import { logger } from './tsdocker.logging.js';
import { logger, formatDuration } from './tsdocker.logging.js';
import { Dockerfile } from './classes.dockerfile.js';
import { DockerRegistry } from './classes.dockerregistry.js';
import { RegistryStorage } from './classes.registrystorage.js';
@@ -136,31 +136,44 @@ export class TsDockerManager {
await this.ensureBuildx();
}
logger.log('info', `Building ${toBuild.length} Dockerfiles...`);
logger.log('info', '');
logger.log('info', '=== BUILD PHASE ===');
logger.log('info', `Building ${toBuild.length} Dockerfile(s)...`);
if (options?.cached) {
// === CACHED MODE: skip builds for unchanged Dockerfiles ===
logger.log('info', '=== CACHED MODE ACTIVE ===');
logger.log('info', '(cached mode active)');
const cache = new TsDockerCache();
cache.load();
for (const dockerfileArg of toBuild) {
const total = toBuild.length;
const overallStart = Date.now();
for (let i = 0; i < total; i++) {
const dockerfileArg = toBuild[i];
const progress = `(${i + 1}/${total})`;
const skip = await cache.shouldSkipBuild(dockerfileArg.cleanTag, dockerfileArg.content);
if (skip) {
logger.log('ok', `${progress} Skipped ${dockerfileArg.cleanTag} (cached)`);
continue;
}
// Cache miss — build this Dockerfile
await dockerfileArg.build({
logger.log('info', `${progress} Building ${dockerfileArg.cleanTag}...`);
const elapsed = await dockerfileArg.build({
platform: options?.platform,
timeout: options?.timeout,
noCache: options?.noCache,
verbose: options?.verbose,
});
logger.log('ok', `${progress} Built ${dockerfileArg.cleanTag} in ${formatDuration(elapsed)}`);
const imageId = await dockerfileArg.getId();
cache.recordBuild(dockerfileArg.cleanTag, dockerfileArg.content, imageId, dockerfileArg.buildTag);
}
logger.log('info', `Total build time: ${formatDuration(Date.now() - overallStart)}`);
// Perform dependency tagging for all Dockerfiles (even cache hits, since tags may be stale)
for (const dockerfileArg of toBuild) {
const dependentBaseImages = new Set<string>();
@@ -182,6 +195,7 @@ export class TsDockerManager {
platform: options?.platform,
timeout: options?.timeout,
noCache: options?.noCache,
verbose: options?.verbose,
});
}
@@ -308,6 +322,8 @@ export class TsDockerManager {
return;
}
logger.log('info', '');
logger.log('info', '=== TEST PHASE ===');
await Dockerfile.testDockerfiles(this.dockerfiles);
logger.log('success', 'All tests completed');
}
@@ -320,19 +336,21 @@ export class TsDockerManager {
await this.discoverDockerfiles();
}
console.log('\nDiscovered Dockerfiles:');
console.log('========================\n');
logger.log('info', '');
logger.log('info', 'Discovered Dockerfiles:');
logger.log('info', '========================');
logger.log('info', '');
for (let i = 0; i < this.dockerfiles.length; i++) {
const df = this.dockerfiles[i];
console.log(`${i + 1}. ${df.filePath}`);
console.log(` Tag: ${df.cleanTag}`);
console.log(` Base Image: ${df.baseImage}`);
console.log(` Version: ${df.version}`);
logger.log('info', `${i + 1}. ${df.filePath}`);
logger.log('info', ` Tag: ${df.cleanTag}`);
logger.log('info', ` Base Image: ${df.baseImage}`);
logger.log('info', ` Version: ${df.version}`);
if (df.localBaseImageDependent) {
console.log(` Depends on: ${df.localBaseDockerfile?.cleanTag}`);
logger.log('info', ` Depends on: ${df.localBaseDockerfile?.cleanTag}`);
}
console.log('');
logger.log('info', '');
}
return this.dockerfiles;