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

View File

@@ -2,7 +2,7 @@ import * as plugins from './tsdocker.plugins.js';
import * as paths from './tsdocker.paths.js';
import { logger } from './tsdocker.logging.js';
import { DockerRegistry } from './classes.dockerregistry.js';
import type { IDockerfileOptions, ITsDockerConfig } from './interfaces/index.js';
import type { IDockerfileOptions, ITsDockerConfig, IBuildCommandOptions } from './interfaces/index.js';
import type { TsDockerManager } from './classes.tsdockermanager.js';
import * as fs from 'fs';
@@ -136,9 +136,12 @@ export class Dockerfile {
/**
* Builds the corresponding real docker image for each Dockerfile class instance
*/
public static async buildDockerfiles(sortedArrayArg: Dockerfile[]): Promise<Dockerfile[]> {
public static async buildDockerfiles(
sortedArrayArg: Dockerfile[],
options?: { platform?: string; timeout?: number },
): Promise<Dockerfile[]> {
for (const dockerfileArg of sortedArrayArg) {
await dockerfileArg.build();
await dockerfileArg.build(options);
}
return sortedArrayArg;
}
@@ -362,15 +365,19 @@ export class Dockerfile {
/**
* Builds the Dockerfile
*/
public async build(): Promise<void> {
public async build(options?: { platform?: string; timeout?: number }): Promise<void> {
logger.log('info', 'now building Dockerfile for ' + this.cleanTag);
const buildArgsString = await Dockerfile.getDockerBuildArgs(this.managerRef);
const config = this.managerRef.config;
const platformOverride = options?.platform;
const timeout = options?.timeout;
let buildCommand: string;
// Check if multi-platform build is needed
if (config.platforms && config.platforms.length > 1) {
if (platformOverride) {
// Single platform override via buildx
buildCommand = `docker buildx build --platform ${platformOverride} --load -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
} else if (config.platforms && config.platforms.length > 1) {
// Multi-platform build using buildx
const platformString = config.platforms.join(',');
buildCommand = `docker buildx build --platform ${platformString} -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
@@ -386,11 +393,27 @@ export class Dockerfile {
buildCommand = `docker build --label="version=${versionLabel}" -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
}
const result = await smartshellInstance.exec(buildCommand);
if (result.exitCode !== 0) {
logger.log('error', `Build failed for ${this.cleanTag}`);
console.log(result.stdout);
throw new Error(`Build failed for ${this.cleanTag}`);
if (timeout) {
// Use streaming execution with timeout
const streaming = await smartshellInstance.execStreaming(buildCommand);
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
streaming.childProcess.kill();
reject(new Error(`Build timed out after ${timeout}s for ${this.cleanTag}`));
}, timeout * 1000);
});
const result = await Promise.race([streaming.finalPromise, timeoutPromise]);
if (result.exitCode !== 0) {
logger.log('error', `Build failed for ${this.cleanTag}`);
throw new Error(`Build failed for ${this.cleanTag}`);
}
} else {
const result = await smartshellInstance.exec(buildCommand);
if (result.exitCode !== 0) {
logger.log('error', `Build failed for ${this.cleanTag}`);
console.log(result.stdout);
throw new Error(`Build failed for ${this.cleanTag}`);
}
}
logger.log('ok', `Built ${this.cleanTag}`);