Files
tsdocker/ts/classes.dockercontext.ts

80 lines
3.1 KiB
TypeScript

import * as plugins from './tsdocker.plugins.js';
import * as fs from 'fs';
import { logger } from './tsdocker.logging.js';
import type { IDockerContextInfo } from './interfaces/index.js';
const smartshellInstance = new plugins.smartshell.Smartshell({ executor: 'bash' });
export class DockerContext {
public contextInfo: IDockerContextInfo | null = null;
/** Sets DOCKER_CONTEXT env var for explicit context selection. */
public setContext(contextName: string): void {
process.env.DOCKER_CONTEXT = contextName;
logger.log('info', `Docker context explicitly set to: ${contextName}`);
}
/** Detects current Docker context via `docker context inspect` and rootless via `docker info`. */
public async detect(): Promise<IDockerContextInfo> {
let name = 'default';
let endpoint = 'unknown';
const contextResult = await smartshellInstance.execSilent(
`docker context inspect --format '{{json .}}'`
);
if (contextResult.exitCode === 0 && contextResult.stdout) {
try {
const parsed = JSON.parse(contextResult.stdout.trim());
const data = Array.isArray(parsed) ? parsed[0] : parsed;
name = data.Name || 'default';
endpoint = data.Endpoints?.docker?.Host || 'unknown';
} catch { /* fallback to defaults */ }
}
let isRootless = false;
const infoResult = await smartshellInstance.execSilent(
`docker info --format '{{json .SecurityOptions}}'`
);
if (infoResult.exitCode === 0 && infoResult.stdout) {
isRootless = infoResult.stdout.includes('name=rootless');
}
// Detect topology
let topology: 'socket-mount' | 'dind' | 'local' = 'local';
if (process.env.DOCKER_HOST && process.env.DOCKER_HOST.startsWith('tcp://')) {
topology = 'dind';
} else if (fs.existsSync('/.dockerenv')) {
topology = 'socket-mount';
}
this.contextInfo = { name, endpoint, isRootless, dockerHost: process.env.DOCKER_HOST, topology };
return this.contextInfo;
}
/** Logs context info prominently. */
public logContextInfo(): void {
if (!this.contextInfo) return;
const { name, endpoint, isRootless, dockerHost, topology } = this.contextInfo;
logger.log('info', '=== DOCKER CONTEXT ===');
logger.log('info', `Context: ${name}`);
logger.log('info', `Endpoint: ${endpoint}`);
if (dockerHost) logger.log('info', `DOCKER_HOST: ${dockerHost}`);
logger.log('info', `Rootless: ${isRootless ? 'yes' : 'no'}`);
logger.log('info', `Topology: ${topology || 'local'}`);
}
/** Emits rootless-specific warnings. */
public logRootlessWarnings(): void {
if (!this.contextInfo?.isRootless) return;
logger.log('warn', '[rootless] network=host in buildx is namespaced by rootlesskit');
logger.log('warn', '[rootless] Local registry may have localhost vs 127.0.0.1 resolution quirks');
}
/** Returns context-aware builder name: tsdocker-builder-<context> */
public getBuilderName(): string {
const contextName = this.contextInfo?.name || 'default';
const sanitized = contextName.replace(/[^a-zA-Z0-9_-]/g, '-');
return `tsdocker-builder-${sanitized}`;
}
}