feat(build): add optional content-hash based build cache to skip rebuilding unchanged Dockerfiles
This commit is contained in:
@@ -4,6 +4,7 @@ import { logger } from './tsdocker.logging.js';
|
||||
import { Dockerfile } from './classes.dockerfile.js';
|
||||
import { DockerRegistry } from './classes.dockerregistry.js';
|
||||
import { RegistryStorage } from './classes.registrystorage.js';
|
||||
import { TsDockerCache } from './classes.tsdockercache.js';
|
||||
import type { ITsDockerConfig, IBuildCommandOptions } from './interfaces/index.js';
|
||||
|
||||
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||
@@ -136,11 +137,54 @@ export class TsDockerManager {
|
||||
}
|
||||
|
||||
logger.log('info', `Building ${toBuild.length} Dockerfiles...`);
|
||||
await Dockerfile.buildDockerfiles(toBuild, {
|
||||
platform: options?.platform,
|
||||
timeout: options?.timeout,
|
||||
noCache: options?.noCache,
|
||||
});
|
||||
|
||||
if (options?.cached) {
|
||||
// === CACHED MODE: skip builds for unchanged Dockerfiles ===
|
||||
logger.log('info', '=== CACHED MODE ACTIVE ===');
|
||||
const cache = new TsDockerCache();
|
||||
cache.load();
|
||||
|
||||
for (const dockerfileArg of toBuild) {
|
||||
const skip = await cache.shouldSkipBuild(dockerfileArg.cleanTag, dockerfileArg.content);
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cache miss — build this Dockerfile
|
||||
await dockerfileArg.build({
|
||||
platform: options?.platform,
|
||||
timeout: options?.timeout,
|
||||
noCache: options?.noCache,
|
||||
});
|
||||
|
||||
const imageId = await dockerfileArg.getId();
|
||||
cache.recordBuild(dockerfileArg.cleanTag, dockerfileArg.content, imageId, dockerfileArg.buildTag);
|
||||
}
|
||||
|
||||
// Perform dependency tagging for all Dockerfiles (even cache hits, since tags may be stale)
|
||||
for (const dockerfileArg of toBuild) {
|
||||
const dependentBaseImages = new Set<string>();
|
||||
for (const other of toBuild) {
|
||||
if (other.localBaseDockerfile === dockerfileArg && other.baseImage !== dockerfileArg.buildTag) {
|
||||
dependentBaseImages.add(other.baseImage);
|
||||
}
|
||||
}
|
||||
for (const fullTag of dependentBaseImages) {
|
||||
logger.log('info', `Tagging ${dockerfileArg.buildTag} as ${fullTag} for local dependency resolution`);
|
||||
await smartshellInstance.exec(`docker tag ${dockerfileArg.buildTag} ${fullTag}`);
|
||||
}
|
||||
}
|
||||
|
||||
cache.save();
|
||||
} else {
|
||||
// === STANDARD MODE: build all via static helper ===
|
||||
await Dockerfile.buildDockerfiles(toBuild, {
|
||||
platform: options?.platform,
|
||||
timeout: options?.timeout,
|
||||
noCache: options?.noCache,
|
||||
});
|
||||
}
|
||||
|
||||
logger.log('success', 'All Dockerfiles built successfully');
|
||||
|
||||
return toBuild;
|
||||
|
||||
Reference in New Issue
Block a user