feat(build): add support for selective builds, platform override and build timeout

This commit is contained in:
2026-02-06 11:58:32 +00:00
parent c279dbd55e
commit 7cac628975
6 changed files with 143 additions and 25 deletions
+55 -7
View File
@@ -4,7 +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 type { ITsDockerConfig } from './interfaces/index.js';
import type { ITsDockerConfig, IBuildCommandOptions } from './interfaces/index.js';
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
@@ -90,9 +90,10 @@ export class TsDockerManager {
}
/**
* Builds all discovered Dockerfiles in dependency order
* Builds discovered Dockerfiles in dependency order.
* When options.patterns is provided, only matching Dockerfiles (and their dependencies) are built.
*/
public async build(): Promise<Dockerfile[]> {
public async build(options?: IBuildCommandOptions): Promise<Dockerfile[]> {
if (this.dockerfiles.length === 0) {
await this.discoverDockerfiles();
}
@@ -102,16 +103,63 @@ export class TsDockerManager {
return [];
}
// Determine which Dockerfiles to build
let toBuild = this.dockerfiles;
if (options?.patterns && options.patterns.length > 0) {
// Filter to matching Dockerfiles
const matched = this.dockerfiles.filter((df) => {
const basename = plugins.path.basename(df.filePath);
return options.patterns!.some((pattern) => {
if (pattern.includes('*') || pattern.includes('?')) {
// Convert glob pattern to regex
const regexStr = '^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$';
return new RegExp(regexStr).test(basename);
}
return basename === pattern;
});
});
if (matched.length === 0) {
logger.log('warn', `No Dockerfiles matched patterns: ${options.patterns.join(', ')}`);
return [];
}
// Resolve dependency chain and preserve topological order
toBuild = this.resolveWithDependencies(matched, this.dockerfiles);
logger.log('info', `Matched ${matched.length} Dockerfile(s), building ${toBuild.length} (including dependencies)`);
}
// Check if buildx is needed
if (this.config.platforms && this.config.platforms.length > 1) {
if (options?.platform || (this.config.platforms && this.config.platforms.length > 1)) {
await this.ensureBuildx();
}
logger.log('info', `Building ${this.dockerfiles.length} Dockerfiles...`);
await Dockerfile.buildDockerfiles(this.dockerfiles);
logger.log('info', `Building ${toBuild.length} Dockerfiles...`);
await Dockerfile.buildDockerfiles(toBuild, {
platform: options?.platform,
timeout: options?.timeout,
});
logger.log('success', 'All Dockerfiles built successfully');
return this.dockerfiles;
return toBuild;
}
/**
* Resolves a set of target Dockerfiles to include all their local base image dependencies,
* preserving the original topological build order.
*/
private resolveWithDependencies(targets: Dockerfile[], allSorted: Dockerfile[]): Dockerfile[] {
const needed = new Set<Dockerfile>();
const addWithDeps = (df: Dockerfile) => {
if (needed.has(df)) return;
needed.add(df);
if (df.localBaseImageDependent && df.localBaseDockerfile) {
addWithDeps(df.localBaseDockerfile);
}
};
for (const df of targets) addWithDeps(df);
return allSorted.filter((df) => needed.has(df));
}
/**