Files
tsdocker/ts/classes.tsdockermanager.ts

255 lines
7.5 KiB
TypeScript

import * as plugins from './tsdocker.plugins.js';
import * as paths from './tsdocker.paths.js';
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';
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
});
/**
* Main orchestrator class for Docker operations
*/
export class TsDockerManager {
public registryStorage: RegistryStorage;
public config: ITsDockerConfig;
public projectInfo: any;
private dockerfiles: Dockerfile[] = [];
constructor(config: ITsDockerConfig) {
this.config = config;
this.registryStorage = new RegistryStorage();
}
/**
* Prepares the manager by loading project info and registries
*/
public async prepare(): Promise<void> {
// Load project info
try {
const projectinfoInstance = new plugins.projectinfo.ProjectInfo(paths.cwd);
this.projectInfo = {
npm: {
name: projectinfoInstance.npm.name,
version: projectinfoInstance.npm.version,
},
};
} catch (err) {
logger.log('warn', 'Could not load project info');
this.projectInfo = null;
}
// Load registries from environment
this.registryStorage.loadFromEnv();
// Add registries from config if specified
if (this.config.registries) {
for (const registryUrl of this.config.registries) {
// Check if already loaded from env
if (!this.registryStorage.getRegistryByUrl(registryUrl)) {
// Try to load credentials for this registry from env
const envVarName = registryUrl.replace(/\./g, '_').toUpperCase();
const envString = process.env[`DOCKER_REGISTRY_${envVarName}`];
if (envString) {
try {
const registry = DockerRegistry.fromEnvString(envString);
this.registryStorage.addRegistry(registry);
} catch (err) {
logger.log('warn', `Could not load credentials for registry ${registryUrl}`);
}
}
}
}
}
logger.log('info', `Prepared TsDockerManager with ${this.registryStorage.getAllRegistries().length} registries`);
}
/**
* Logs in to all configured registries
*/
public async login(): Promise<void> {
if (this.registryStorage.getAllRegistries().length === 0) {
logger.log('warn', 'No registries configured');
return;
}
await this.registryStorage.loginAll();
}
/**
* Discovers and sorts Dockerfiles in the current directory
*/
public async discoverDockerfiles(): Promise<Dockerfile[]> {
this.dockerfiles = await Dockerfile.readDockerfiles(this);
this.dockerfiles = await Dockerfile.sortDockerfiles(this.dockerfiles);
this.dockerfiles = await Dockerfile.mapDockerfiles(this.dockerfiles);
return this.dockerfiles;
}
/**
* Builds all discovered Dockerfiles in dependency order
*/
public async build(): Promise<Dockerfile[]> {
if (this.dockerfiles.length === 0) {
await this.discoverDockerfiles();
}
if (this.dockerfiles.length === 0) {
logger.log('warn', 'No Dockerfiles found');
return [];
}
// Check if buildx is needed
if (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('success', 'All Dockerfiles built successfully');
return this.dockerfiles;
}
/**
* Ensures Docker buildx is set up for multi-architecture builds
*/
private async ensureBuildx(): Promise<void> {
logger.log('info', 'Setting up Docker buildx for multi-platform builds...');
// Check if a buildx builder exists
const inspectResult = await smartshellInstance.exec('docker buildx inspect tsdocker-builder 2>/dev/null');
if (inspectResult.exitCode !== 0) {
// Create a new buildx builder
logger.log('info', 'Creating new buildx builder...');
await smartshellInstance.exec('docker buildx create --name tsdocker-builder --use');
await smartshellInstance.exec('docker buildx inspect --bootstrap');
} else {
// Use existing builder
await smartshellInstance.exec('docker buildx use tsdocker-builder');
}
logger.log('ok', 'Docker buildx ready');
}
/**
* Pushes all built images to specified registries
*/
public async push(registryUrls?: string[]): Promise<void> {
if (this.dockerfiles.length === 0) {
await this.discoverDockerfiles();
}
if (this.dockerfiles.length === 0) {
logger.log('warn', 'No Dockerfiles found to push');
return;
}
// Determine which registries to push to
let registriesToPush: DockerRegistry[] = [];
if (registryUrls && registryUrls.length > 0) {
// Push to specified registries
for (const url of registryUrls) {
const registry = this.registryStorage.getRegistryByUrl(url);
if (registry) {
registriesToPush.push(registry);
} else {
logger.log('warn', `Registry ${url} not found in storage`);
}
}
} else {
// Push to all configured registries
registriesToPush = this.registryStorage.getAllRegistries();
}
if (registriesToPush.length === 0) {
logger.log('warn', 'No registries available to push to');
return;
}
// Push each Dockerfile to each registry
for (const dockerfile of this.dockerfiles) {
for (const registry of registriesToPush) {
await dockerfile.push(registry);
}
}
logger.log('success', 'All images pushed successfully');
}
/**
* Pulls images from a specified registry
*/
public async pull(registryUrl: string): Promise<void> {
if (this.dockerfiles.length === 0) {
await this.discoverDockerfiles();
}
const registry = this.registryStorage.getRegistryByUrl(registryUrl);
if (!registry) {
throw new Error(`Registry ${registryUrl} not found`);
}
for (const dockerfile of this.dockerfiles) {
await dockerfile.pull(registry);
}
logger.log('success', 'All images pulled successfully');
}
/**
* Runs tests for all Dockerfiles
*/
public async test(): Promise<void> {
if (this.dockerfiles.length === 0) {
await this.discoverDockerfiles();
}
if (this.dockerfiles.length === 0) {
logger.log('warn', 'No Dockerfiles found to test');
return;
}
await Dockerfile.testDockerfiles(this.dockerfiles);
logger.log('success', 'All tests completed');
}
/**
* Lists all discovered Dockerfiles and their info
*/
public async list(): Promise<Dockerfile[]> {
if (this.dockerfiles.length === 0) {
await this.discoverDockerfiles();
}
console.log('\nDiscovered Dockerfiles:');
console.log('========================\n');
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}`);
if (df.localBaseImageDependent) {
console.log(` Depends on: ${df.localBaseDockerfile?.cleanTag}`);
}
console.log('');
}
return this.dockerfiles;
}
/**
* Gets the cached Dockerfiles (after discovery)
*/
public getDockerfiles(): Dockerfile[] {
return this.dockerfiles;
}
}