feat(services): Add comprehensive development services management (v1.17.0)
- Implemented gitzone services command for managing MongoDB and MinIO containers - Added smart port assignment (20000-30000 range) to avoid conflicts - Project-specific container names for complete isolation - Data persistence in .nogit/ directories - MongoDB Compass connection string generation with network IP detection - Auto-configuration via .nogit/env.json with secure defaults - Commands: start, stop, restart, status, config, compass, logs, remove, clean - Interactive confirmations for destructive operations - Comprehensive documentation and Task Venture Capital GmbH legal update
This commit is contained in:
226
ts/mod_services/classes.dockercontainer.ts
Normal file
226
ts/mod_services/classes.dockercontainer.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import * as plugins from './mod.plugins.js';
|
||||
import * as helpers from './helpers.js';
|
||||
|
||||
export type ContainerStatus = 'running' | 'stopped' | 'not_exists';
|
||||
|
||||
export interface IDockerRunOptions {
|
||||
name: string;
|
||||
image: string;
|
||||
ports?: { [key: string]: string };
|
||||
volumes?: { [key: string]: string };
|
||||
environment?: { [key: string]: string };
|
||||
restart?: string;
|
||||
command?: string;
|
||||
}
|
||||
|
||||
export class DockerContainer {
|
||||
private smartshell: plugins.smartshell.Smartshell;
|
||||
|
||||
constructor() {
|
||||
this.smartshell = new plugins.smartshell.Smartshell({
|
||||
executor: 'bash',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Docker is installed and available
|
||||
*/
|
||||
public async checkDocker(): Promise<boolean> {
|
||||
try {
|
||||
const result = await this.smartshell.exec('docker --version');
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get container status
|
||||
*/
|
||||
public async getStatus(containerName: string): Promise<ContainerStatus> {
|
||||
try {
|
||||
// Check if running
|
||||
const runningResult = await this.smartshell.exec(
|
||||
`docker ps --format '{{.Names}}' | grep -q "^${containerName}$"`
|
||||
);
|
||||
|
||||
if (runningResult.exitCode === 0) {
|
||||
return 'running';
|
||||
}
|
||||
|
||||
// Check if exists but stopped
|
||||
const existsResult = await this.smartshell.exec(
|
||||
`docker ps -a --format '{{.Names}}' | grep -q "^${containerName}$"`
|
||||
);
|
||||
|
||||
if (existsResult.exitCode === 0) {
|
||||
return 'stopped';
|
||||
}
|
||||
|
||||
return 'not_exists';
|
||||
} catch (error) {
|
||||
return 'not_exists';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a container
|
||||
*/
|
||||
public async start(containerName: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await this.smartshell.exec(`docker start ${containerName}`);
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a container
|
||||
*/
|
||||
public async stop(containerName: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await this.smartshell.exec(`docker stop ${containerName}`);
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a container
|
||||
*/
|
||||
public async remove(containerName: string, force: boolean = false): Promise<boolean> {
|
||||
try {
|
||||
const forceFlag = force ? '-f' : '';
|
||||
const result = await this.smartshell.exec(`docker rm ${forceFlag} ${containerName}`);
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a new container
|
||||
*/
|
||||
public async run(options: IDockerRunOptions): Promise<boolean> {
|
||||
let command = 'docker run -d';
|
||||
|
||||
// Add name
|
||||
command += ` --name ${options.name}`;
|
||||
|
||||
// Add ports
|
||||
if (options.ports) {
|
||||
for (const [hostPort, containerPort] of Object.entries(options.ports)) {
|
||||
command += ` -p ${hostPort}:${containerPort}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add volumes
|
||||
if (options.volumes) {
|
||||
for (const [hostPath, containerPath] of Object.entries(options.volumes)) {
|
||||
command += ` -v "${hostPath}:${containerPath}"`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add environment variables
|
||||
if (options.environment) {
|
||||
for (const [key, value] of Object.entries(options.environment)) {
|
||||
command += ` -e ${key}="${value}"`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add restart policy
|
||||
if (options.restart) {
|
||||
command += ` --restart ${options.restart}`;
|
||||
}
|
||||
|
||||
// Add image
|
||||
command += ` ${options.image}`;
|
||||
|
||||
// Add command if provided
|
||||
if (options.command) {
|
||||
command += ` ${options.command}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.smartshell.exec(command);
|
||||
return result.exitCode === 0;
|
||||
} catch (error) {
|
||||
helpers.printMessage(`Failed to run container: ${error.message}`, 'red');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command in a running container
|
||||
*/
|
||||
public async exec(containerName: string, command: string): Promise<string> {
|
||||
try {
|
||||
const result = await this.smartshell.exec(`docker exec ${containerName} ${command}`);
|
||||
if (result.exitCode === 0) {
|
||||
return result.stdout;
|
||||
}
|
||||
return '';
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get container logs
|
||||
*/
|
||||
public async logs(containerName: string, lines?: number): Promise<string> {
|
||||
try {
|
||||
const tailFlag = lines ? `--tail ${lines}` : '';
|
||||
const result = await this.smartshell.exec(`docker logs ${tailFlag} ${containerName}`);
|
||||
return result.stdout;
|
||||
} catch (error) {
|
||||
return `Error getting logs: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a container exists
|
||||
*/
|
||||
public async exists(containerName: string): Promise<boolean> {
|
||||
const status = await this.getStatus(containerName);
|
||||
return status !== 'not_exists';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a container is running
|
||||
*/
|
||||
public async isRunning(containerName: string): Promise<boolean> {
|
||||
const status = await this.getStatus(containerName);
|
||||
return status === 'running';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a container to be ready
|
||||
*/
|
||||
public async waitForReady(containerName: string, maxAttempts: number = 30): Promise<boolean> {
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
if (await this.isRunning(containerName)) {
|
||||
return true;
|
||||
}
|
||||
await plugins.smartdelay.delayFor(1000);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get container information
|
||||
*/
|
||||
public async inspect(containerName: string): Promise<any> {
|
||||
try {
|
||||
const result = await this.smartshell.exec(`docker inspect ${containerName}`);
|
||||
if (result.exitCode === 0) {
|
||||
return JSON.parse(result.stdout);
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user