BREAKING CHANGE(szci): delegate Docker operations to @git.zone/tsdocker, remove internal Docker managers and deprecated modules, simplify CLI and env var handling
This commit is contained in:
@@ -1,190 +1,78 @@
|
||||
import { logger } from '../szci.logging.ts';
|
||||
import * as plugins from './mod.plugins.ts';
|
||||
import * as paths from '../szci.paths.ts';
|
||||
import { bash } from '../szci.bash.ts';
|
||||
|
||||
// classes
|
||||
import { Szci } from '../szci.classes.szci.ts';
|
||||
import { Dockerfile } from './mod.classes.dockerfile.ts';
|
||||
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
|
||||
import { RegistryStorage } from './mod.classes.registrystorage.ts';
|
||||
|
||||
export class SzciDockerManager {
|
||||
public szciRef: Szci;
|
||||
public szciRegistryStorage = new RegistryStorage();
|
||||
|
||||
constructor(szciArg: Szci) {
|
||||
this.szciRef = szciArg;
|
||||
}
|
||||
|
||||
/**
|
||||
* handle cli input
|
||||
* @param argvArg
|
||||
* Bridges SZCI_LOGIN_DOCKER* env vars to DOCKER_REGISTRY_N format for tsdocker,
|
||||
* and handles GitLab CI registry auto-login.
|
||||
*/
|
||||
private bridgeEnvVars() {
|
||||
const env = Deno.env.toObject();
|
||||
|
||||
// Bridge GitLab CI registry as DOCKER_REGISTRY_0
|
||||
if (env['GITLAB_CI']) {
|
||||
const ciJobToken = env['CI_JOB_TOKEN'];
|
||||
if (!ciJobToken) {
|
||||
logger.log('error', 'Running in GitLab CI, but no CI_JOB_TOKEN found!');
|
||||
Deno.exit(1);
|
||||
}
|
||||
Deno.env.set('DOCKER_REGISTRY_0', `registry.gitlab.com|gitlab-ci-token|${ciJobToken}`);
|
||||
}
|
||||
|
||||
// Bridge SZCI_LOGIN_DOCKER* → DOCKER_REGISTRY_N
|
||||
let registryIndex = 1;
|
||||
const sortedKeys = Object.keys(env)
|
||||
.filter((key) => key.startsWith('SZCI_LOGIN_DOCKER'))
|
||||
.sort();
|
||||
for (const key of sortedKeys) {
|
||||
Deno.env.set(`DOCKER_REGISTRY_${registryIndex}`, env[key]);
|
||||
registryIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cli input by bridging env vars and delegating to tsdocker.
|
||||
*/
|
||||
public handleCli = async (argvArg: any) => {
|
||||
if (argvArg._.length >= 2) {
|
||||
const action: string = argvArg._[1];
|
||||
switch (action) {
|
||||
case 'build':
|
||||
await this.build();
|
||||
break;
|
||||
case 'login':
|
||||
case 'prepare':
|
||||
await this.login();
|
||||
break;
|
||||
case 'test':
|
||||
await this.test();
|
||||
break;
|
||||
case 'push':
|
||||
await this.push(argvArg);
|
||||
break;
|
||||
case 'pull':
|
||||
await this.pull(argvArg);
|
||||
break;
|
||||
default:
|
||||
logger.log('error', `>>szci docker ...<< action >>${action}<< not supported`);
|
||||
}
|
||||
} else {
|
||||
if (argvArg._.length < 2) {
|
||||
logger.log(
|
||||
'info',
|
||||
`>>szci docker ...<< cli arguments invalid... Please read the documentation.`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* builds a cwd of Dockerfiles by triggering a promisechain
|
||||
*/
|
||||
public build = async () => {
|
||||
await this.prepare();
|
||||
logger.log('info', 'now building Dockerfiles...');
|
||||
await Dockerfile.readDockerfiles(this)
|
||||
.then(Dockerfile.sortDockerfiles)
|
||||
.then(Dockerfile.mapDockerfiles)
|
||||
.then(Dockerfile.buildDockerfiles);
|
||||
};
|
||||
|
||||
/**
|
||||
* login to the DockerRegistries
|
||||
*/
|
||||
public login = async () => {
|
||||
await this.prepare();
|
||||
await this.szciRegistryStorage.loginAll();
|
||||
};
|
||||
|
||||
/**
|
||||
* logs in docker
|
||||
*/
|
||||
public prepare = async () => {
|
||||
// Always login to GitLab Registry
|
||||
if (Deno.env.get("GITLAB_CI")) {
|
||||
console.log('gitlab ci detected');
|
||||
if (!Deno.env.get("CI_JOB_TOKEN") || Deno.env.get("CI_JOB_TOKEN") === '') {
|
||||
logger.log('error', 'Running in Gitlab CI, but no registry token specified by gitlab!');
|
||||
Deno.exit(1);
|
||||
}
|
||||
this.szciRegistryStorage.addRegistry(
|
||||
new DockerRegistry({
|
||||
registryUrl: 'registry.gitlab.com',
|
||||
username: 'gitlab-ci-token',
|
||||
password: Deno.env.get("CI_JOB_TOKEN")!,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle registries
|
||||
await plugins.smartobject.forEachMinimatch(
|
||||
Deno.env.toObject(),
|
||||
'SZCI_LOGIN_DOCKER*',
|
||||
async (envString: string) => {
|
||||
this.szciRegistryStorage.addRegistry(DockerRegistry.fromEnvString(envString));
|
||||
}
|
||||
);
|
||||
return;
|
||||
};
|
||||
this.bridgeEnvVars();
|
||||
|
||||
/**
|
||||
* pushes an image towards a registry
|
||||
* @param argvArg
|
||||
*/
|
||||
public push = async (argvArg: any) => {
|
||||
await this.prepare();
|
||||
let dockerRegistryUrls: string[] = [];
|
||||
const action: string = argvArg._[1];
|
||||
const extraArgs = argvArg._.slice(2).join(' ');
|
||||
|
||||
// lets parse the input of cli and npmextra
|
||||
if (argvArg._.length >= 3 && argvArg._[2] !== 'npmextra') {
|
||||
dockerRegistryUrls.push(argvArg._[2]);
|
||||
} else {
|
||||
if (this.szciRef.szciConfig.getConfig().dockerRegistries.length === 0) {
|
||||
logger.log(
|
||||
'warn',
|
||||
`There are no docker registries listed in npmextra.json! This is strange!`
|
||||
);
|
||||
}
|
||||
dockerRegistryUrls = dockerRegistryUrls.concat(
|
||||
this.szciRef.szciConfig.getConfig().dockerRegistries
|
||||
);
|
||||
}
|
||||
|
||||
// lets determine the suffix
|
||||
let suffix = null;
|
||||
if (argvArg._.length >= 4) {
|
||||
suffix = argvArg._[3];
|
||||
}
|
||||
|
||||
// lets push to the registries
|
||||
for (const dockerRegistryUrl of dockerRegistryUrls) {
|
||||
const dockerfileArray = await Dockerfile.readDockerfiles(this)
|
||||
.then(Dockerfile.sortDockerfiles)
|
||||
.then(Dockerfile.mapDockerfiles);
|
||||
const dockerRegistryToPushTo = await this.szciRegistryStorage.getRegistryByUrl(
|
||||
dockerRegistryUrl
|
||||
);
|
||||
if (!dockerRegistryToPushTo) {
|
||||
logger.log(
|
||||
'error',
|
||||
`Cannot push to registry ${dockerRegistryUrl}, because it was not found in the authenticated registry list.`
|
||||
);
|
||||
Deno.exit(1);
|
||||
}
|
||||
for (const dockerfile of dockerfileArray) {
|
||||
await dockerfile.push(dockerRegistryToPushTo, suffix);
|
||||
}
|
||||
switch (action) {
|
||||
case 'build':
|
||||
await bash(`npx @git.zone/tsdocker build ${extraArgs}`.trim());
|
||||
break;
|
||||
case 'login':
|
||||
case 'prepare':
|
||||
await bash(`npx @git.zone/tsdocker login ${extraArgs}`.trim());
|
||||
break;
|
||||
case 'test':
|
||||
await bash(`npx @git.zone/tsdocker test ${extraArgs}`.trim());
|
||||
break;
|
||||
case 'push':
|
||||
await bash(`npx @git.zone/tsdocker push ${extraArgs}`.trim());
|
||||
break;
|
||||
case 'pull':
|
||||
await bash(`npx @git.zone/tsdocker pull ${extraArgs}`.trim());
|
||||
break;
|
||||
default:
|
||||
logger.log('error', `>>szci docker ...<< action >>${action}<< not supported`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* pulls an image
|
||||
*/
|
||||
public pull = async (argvArg: any) => {
|
||||
await this.prepare();
|
||||
const registryUrlArg = argvArg._[2];
|
||||
let suffix = null;
|
||||
if (argvArg._.length >= 4) {
|
||||
suffix = argvArg._[3];
|
||||
}
|
||||
const localDockerRegistry = await this.szciRegistryStorage.getRegistryByUrl(registryUrlArg);
|
||||
const dockerfileArray = await Dockerfile.readDockerfiles(this)
|
||||
.then(Dockerfile.sortDockerfiles)
|
||||
.then(Dockerfile.mapDockerfiles);
|
||||
for (const dockerfile of dockerfileArray) {
|
||||
await dockerfile.pull(localDockerRegistry, suffix);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* tests docker files
|
||||
*/
|
||||
public test = async () => {
|
||||
await this.prepare();
|
||||
return await Dockerfile.readDockerfiles(this).then(Dockerfile.testDockerfiles);
|
||||
};
|
||||
|
||||
/**
|
||||
* can be used to get the Dockerfiles in the directory
|
||||
*/
|
||||
getDockerfiles = async () => {
|
||||
const dockerfiles = await Dockerfile.readDockerfiles(this);
|
||||
return dockerfiles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,410 +0,0 @@
|
||||
import * as plugins from './mod.plugins.ts';
|
||||
import * as paths from '../szci.paths.ts';
|
||||
|
||||
import { logger } from '../szci.logging.ts';
|
||||
import { bash } from '../szci.bash.ts';
|
||||
|
||||
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
|
||||
import * as helpers from './mod.helpers.ts';
|
||||
import { SzciDockerManager } from './index.ts';
|
||||
import { Szci } from '../szci.classes.szci.ts';
|
||||
|
||||
/**
|
||||
* class Dockerfile represents a Dockerfile on disk in szci
|
||||
*/
|
||||
export class Dockerfile {
|
||||
// STATIC
|
||||
|
||||
/**
|
||||
* creates instance of class Dockerfile for all Dockerfiles in cwd
|
||||
* @returns Promise<Dockerfile[]>
|
||||
*/
|
||||
public static async readDockerfiles(
|
||||
szciDockerManagerRefArg: SzciDockerManager
|
||||
): Promise<Dockerfile[]> {
|
||||
const fileTree = await plugins.smartfile.fs.listFileTree(paths.getCwd(), 'Dockerfile*');
|
||||
|
||||
// create the Dockerfile array
|
||||
const readDockerfilesArray: Dockerfile[] = [];
|
||||
logger.log('info', `found ${fileTree.length} Dockerfiles:`);
|
||||
console.log(fileTree);
|
||||
for (const dockerfilePath of fileTree) {
|
||||
const myDockerfile = new Dockerfile(szciDockerManagerRefArg, {
|
||||
filePath: dockerfilePath,
|
||||
read: true,
|
||||
});
|
||||
readDockerfilesArray.push(myDockerfile);
|
||||
}
|
||||
|
||||
return readDockerfilesArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts Dockerfiles into a build order based on dependencies.
|
||||
* @param dockerfiles An array of Dockerfile instances.
|
||||
* @returns A Promise that resolves to a sorted array of Dockerfiles.
|
||||
*/
|
||||
public static async sortDockerfiles(dockerfiles: Dockerfile[]): Promise<Dockerfile[]> {
|
||||
logger.log('info', 'Sorting Dockerfiles based on dependencies...');
|
||||
|
||||
// Map from cleanTag to Dockerfile instance for quick lookup
|
||||
const tagToDockerfile = new Map<string, Dockerfile>();
|
||||
dockerfiles.forEach((dockerfile) => {
|
||||
tagToDockerfile.set(dockerfile.cleanTag, dockerfile);
|
||||
});
|
||||
|
||||
// Build the dependency graph
|
||||
const graph = new Map<Dockerfile, Dockerfile[]>();
|
||||
dockerfiles.forEach((dockerfile) => {
|
||||
const dependencies: Dockerfile[] = [];
|
||||
const baseImage = dockerfile.baseImage;
|
||||
|
||||
// Check if the baseImage is among the local Dockerfiles
|
||||
if (tagToDockerfile.has(baseImage)) {
|
||||
const baseDockerfile = tagToDockerfile.get(baseImage)!;
|
||||
dependencies.push(baseDockerfile);
|
||||
dockerfile.localBaseImageDependent = true;
|
||||
dockerfile.localBaseDockerfile = baseDockerfile;
|
||||
}
|
||||
|
||||
graph.set(dockerfile, dependencies);
|
||||
});
|
||||
|
||||
// Perform topological sort
|
||||
const sortedDockerfiles: Dockerfile[] = [];
|
||||
const visited = new Set<Dockerfile>();
|
||||
const tempMarked = new Set<Dockerfile>();
|
||||
|
||||
const visit = (dockerfile: Dockerfile) => {
|
||||
if (tempMarked.has(dockerfile)) {
|
||||
throw new Error(`Circular dependency detected involving ${dockerfile.cleanTag}`);
|
||||
}
|
||||
if (!visited.has(dockerfile)) {
|
||||
tempMarked.add(dockerfile);
|
||||
const dependencies = graph.get(dockerfile) || [];
|
||||
dependencies.forEach((dep) => visit(dep));
|
||||
tempMarked.delete(dockerfile);
|
||||
visited.add(dockerfile);
|
||||
sortedDockerfiles.push(dockerfile);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
dockerfiles.forEach((dockerfile) => {
|
||||
if (!visited.has(dockerfile)) {
|
||||
visit(dockerfile);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.log('error', (error as Error).message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Log the sorted order
|
||||
sortedDockerfiles.forEach((dockerfile, index) => {
|
||||
logger.log(
|
||||
'info',
|
||||
`Build order ${index + 1}: ${dockerfile.cleanTag}
|
||||
with base image ${dockerfile.baseImage}`
|
||||
);
|
||||
});
|
||||
|
||||
return sortedDockerfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* maps local Dockerfiles dependencies to the correspoding Dockerfile class instances
|
||||
*/
|
||||
public static async mapDockerfiles(sortedDockerfileArray: Dockerfile[]): Promise<Dockerfile[]> {
|
||||
sortedDockerfileArray.forEach((dockerfileArg) => {
|
||||
if (dockerfileArg.localBaseImageDependent) {
|
||||
sortedDockerfileArray.forEach((dockfile2: Dockerfile) => {
|
||||
if (dockfile2.cleanTag === dockerfileArg.baseImage) {
|
||||
dockerfileArg.localBaseDockerfile = dockfile2;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return sortedDockerfileArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* builds the correspoding real docker image for each Dockerfile class instance
|
||||
*/
|
||||
public static async buildDockerfiles(sortedArrayArg: Dockerfile[]) {
|
||||
for (const dockerfileArg of sortedArrayArg) {
|
||||
await dockerfileArg.build();
|
||||
}
|
||||
return sortedArrayArg;
|
||||
}
|
||||
|
||||
/**
|
||||
* tests all Dockerfiles in by calling class Dockerfile.test();
|
||||
* @param sortedArrayArg Dockerfile[] that contains all Dockerfiles in cwd
|
||||
*/
|
||||
public static async testDockerfiles(sortedArrayArg: Dockerfile[]) {
|
||||
for (const dockerfileArg of sortedArrayArg) {
|
||||
await dockerfileArg.test();
|
||||
}
|
||||
return sortedArrayArg;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a version for a docker file
|
||||
* @execution SYNC
|
||||
*/
|
||||
public static dockerFileVersion(
|
||||
dockerfileInstanceArg: Dockerfile,
|
||||
dockerfileNameArg: string
|
||||
): string {
|
||||
let versionString: string;
|
||||
const versionRegex = /Dockerfile_(.+)$/;
|
||||
const regexResultArray = versionRegex.exec(dockerfileNameArg);
|
||||
if (regexResultArray && regexResultArray.length === 2) {
|
||||
versionString = regexResultArray[1];
|
||||
} else {
|
||||
versionString = 'latest';
|
||||
}
|
||||
versionString = versionString.replace(
|
||||
'##version##',
|
||||
dockerfileInstanceArg.szciDockerManagerRef.szciRef.szciConfig.getConfig().projectInfo.npm
|
||||
.version
|
||||
);
|
||||
return versionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the base image from a Dockerfile content without using external libraries.
|
||||
* @param dockerfileContentArg The content of the Dockerfile as a string.
|
||||
* @returns The base image specified in the first FROM instruction.
|
||||
*/
|
||||
public static dockerBaseImage(dockerfileContentArg: string): string {
|
||||
const lines = dockerfileContentArg.split(/\r?\n/);
|
||||
const args: { [key: string]: string } = {};
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim();
|
||||
|
||||
// Skip empty lines and comments
|
||||
if (trimmedLine === '' || trimmedLine.startsWith('#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Match ARG instructions
|
||||
const argMatch = trimmedLine.match(/^ARG\s+([^\s=]+)(?:=(.*))?$/i);
|
||||
if (argMatch) {
|
||||
const argName = argMatch[1];
|
||||
const argValue = argMatch[2] !== undefined ? argMatch[2] : Deno.env.get(argName) || '';
|
||||
args[argName] = argValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Match FROM instructions
|
||||
const fromMatch = trimmedLine.match(/^FROM\s+(.+?)(?:\s+AS\s+[^\s]+)?$/i);
|
||||
if (fromMatch) {
|
||||
let baseImage = fromMatch[1].trim();
|
||||
|
||||
// Substitute variables in the base image name
|
||||
baseImage = Dockerfile.substituteVariables(baseImage, args);
|
||||
|
||||
return baseImage;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('No FROM instruction found in Dockerfile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes variables in a string, supporting default values like ${VAR:-default}.
|
||||
* @param str The string containing variables.
|
||||
* @param vars The object containing variable values.
|
||||
* @returns The string with variables substituted.
|
||||
*/
|
||||
private static substituteVariables(str: string, vars: { [key: string]: string }): string {
|
||||
return str.replace(/\${([^}:]+)(:-([^}]+))?}/g, (_, varName, __, defaultValue) => {
|
||||
if (vars[varName] !== undefined) {
|
||||
return vars[varName];
|
||||
} else if (defaultValue !== undefined) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the docker tag
|
||||
*/
|
||||
public static getDockerTagString(
|
||||
szciDockerManagerRef: SzciDockerManager,
|
||||
registryArg: string,
|
||||
repoArg: string,
|
||||
versionArg: string,
|
||||
suffixArg?: string
|
||||
): string {
|
||||
// determine wether the repo should be mapped accordingly to the registry
|
||||
const mappedRepo =
|
||||
szciDockerManagerRef.szciRef.szciConfig.getConfig().dockerRegistryRepoMap[registryArg];
|
||||
const repo = (() => {
|
||||
if (mappedRepo) {
|
||||
return mappedRepo;
|
||||
} else {
|
||||
return repoArg;
|
||||
}
|
||||
})();
|
||||
|
||||
// determine wether the version contais a suffix
|
||||
let version = versionArg;
|
||||
if (suffixArg) {
|
||||
version = versionArg + '_' + suffixArg;
|
||||
}
|
||||
|
||||
const tagString = `${registryArg}/${repo}:${version}`;
|
||||
return tagString;
|
||||
}
|
||||
|
||||
public static async getDockerBuildArgs(
|
||||
szciDockerManagerRef: SzciDockerManager
|
||||
): Promise<string> {
|
||||
logger.log('info', 'checking for env vars to be supplied to the docker build');
|
||||
let buildArgsString: string = '';
|
||||
for (const dockerArgKey of Object.keys(
|
||||
szciDockerManagerRef.szciRef.szciConfig.getConfig().dockerBuildargEnvMap
|
||||
)) {
|
||||
const dockerArgOuterEnvVar =
|
||||
szciDockerManagerRef.szciRef.szciConfig.getConfig().dockerBuildargEnvMap[dockerArgKey];
|
||||
logger.log(
|
||||
'note',
|
||||
`docker ARG "${dockerArgKey}" maps to outer env var "${dockerArgOuterEnvVar}"`
|
||||
);
|
||||
const targetValue = Deno.env.get(dockerArgOuterEnvVar);
|
||||
buildArgsString = `${buildArgsString} --build-arg ${dockerArgKey}="${targetValue}"`;
|
||||
}
|
||||
return buildArgsString;
|
||||
}
|
||||
|
||||
// INSTANCE
|
||||
public szciDockerManagerRef: SzciDockerManager;
|
||||
|
||||
public filePath!: string;
|
||||
public repo: string;
|
||||
public version: string;
|
||||
public cleanTag: string;
|
||||
public buildTag: string;
|
||||
public pushTag!: string;
|
||||
public containerName: string;
|
||||
public content!: string;
|
||||
public baseImage: string;
|
||||
public localBaseImageDependent: boolean;
|
||||
public localBaseDockerfile!: Dockerfile;
|
||||
|
||||
constructor(
|
||||
dockerManagerRefArg: SzciDockerManager,
|
||||
options: { filePath?: string; fileContents?: string | Uint8Array; read?: boolean }
|
||||
) {
|
||||
this.szciDockerManagerRef = dockerManagerRefArg;
|
||||
this.filePath = options.filePath!;
|
||||
this.repo =
|
||||
this.szciDockerManagerRef.szciRef.szciEnv.repo.user +
|
||||
'/' +
|
||||
this.szciDockerManagerRef.szciRef.szciEnv.repo.repo;
|
||||
this.version = Dockerfile.dockerFileVersion(this, plugins.path.parse(this.filePath).base);
|
||||
this.cleanTag = this.repo + ':' + this.version;
|
||||
this.buildTag = this.cleanTag;
|
||||
|
||||
this.containerName = 'dockerfile-' + this.version;
|
||||
if (options.filePath && options.read) {
|
||||
this.content = plugins.smartfile.fs.toStringSync(plugins.path.resolve(options.filePath));
|
||||
}
|
||||
this.baseImage = Dockerfile.dockerBaseImage(this.content);
|
||||
this.localBaseImageDependent = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* builds the Dockerfile
|
||||
*/
|
||||
public async build() {
|
||||
logger.log('info', 'now building Dockerfile for ' + this.cleanTag);
|
||||
const buildArgsString = await Dockerfile.getDockerBuildArgs(this.szciDockerManagerRef);
|
||||
const buildCommand = `docker build --label="version=${
|
||||
this.szciDockerManagerRef.szciRef.szciConfig.getConfig().projectInfo.npm.version
|
||||
}" -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`;
|
||||
await bash(buildCommand);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* pushes the Dockerfile to a registry
|
||||
*/
|
||||
public async push(dockerRegistryArg: DockerRegistry, versionSuffix?: string) {
|
||||
this.pushTag = Dockerfile.getDockerTagString(
|
||||
this.szciDockerManagerRef,
|
||||
dockerRegistryArg.registryUrl,
|
||||
this.repo,
|
||||
this.version,
|
||||
versionSuffix
|
||||
);
|
||||
await bash(`docker tag ${this.buildTag} ${this.pushTag}`);
|
||||
await bash(`docker push ${this.pushTag}`);
|
||||
const imageDigest = (
|
||||
await bash(`docker inspect --format="{{index .RepoDigests 0}}" ${this.pushTag}`)
|
||||
).split('@')[1];
|
||||
console.log(`The image ${this.pushTag} has digest ${imageDigest}`);
|
||||
await this.szciDockerManagerRef.szciRef.cloudlyConnector.announceDockerContainer({
|
||||
registryUrl: this.pushTag,
|
||||
tag: this.buildTag,
|
||||
labels: [],
|
||||
version: this.szciDockerManagerRef.szciRef.szciConfig.getConfig().projectInfo.npm.version,
|
||||
});
|
||||
await this.szciDockerManagerRef.szciRef.szciConfig.kvStorage.writeKey(
|
||||
'latestPushedDockerTag',
|
||||
this.pushTag
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* pulls the Dockerfile from a registry
|
||||
*/
|
||||
public async pull(registryArg: DockerRegistry, versionSuffixArg?: string) {
|
||||
const pullTag = Dockerfile.getDockerTagString(
|
||||
this.szciDockerManagerRef,
|
||||
registryArg.registryUrl,
|
||||
this.repo,
|
||||
this.version,
|
||||
versionSuffixArg
|
||||
);
|
||||
await bash(`docker pull ${pullTag}`);
|
||||
await bash(`docker tag ${pullTag} ${this.buildTag}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* tests the Dockerfile;
|
||||
*/
|
||||
public async test() {
|
||||
const testFile: string = plugins.path.join(paths.SzciTestDir, 'test_' + this.version + '.sh');
|
||||
const testFileExists: boolean = plugins.smartfile.fs.fileExistsSync(testFile);
|
||||
if (testFileExists) {
|
||||
// run tests
|
||||
await bash(
|
||||
`docker run --name szci_test_container --entrypoint="bash" ${this.buildTag} -c "mkdir /szci_test"`
|
||||
);
|
||||
await bash(`docker cp ${testFile} szci_test_container:/szci_test/test.sh`);
|
||||
await bash(`docker commit szci_test_container szci_test_image`);
|
||||
await bash(`docker run --entrypoint="bash" szci_test_image -x /szci_test/test.sh`);
|
||||
await bash(`docker rm szci_test_container`);
|
||||
await bash(`docker rmi --force szci_test_image`);
|
||||
} else {
|
||||
logger.log('warn', 'skipping tests for ' + this.cleanTag + ' because no testfile was found!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the id of a Dockerfile
|
||||
*/
|
||||
public async getId() {
|
||||
const containerId = await bash(
|
||||
'docker inspect --type=image --format="{{.Id}}" ' + this.buildTag
|
||||
);
|
||||
return containerId;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import { logger } from '../szci.logging.ts';
|
||||
import * as plugins from './mod.plugins.ts';
|
||||
import { bash } from '../szci.bash.ts';
|
||||
|
||||
export interface IDockerRegistryConstructorOptions {
|
||||
registryUrl: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export class DockerRegistry {
|
||||
public registryUrl: string;
|
||||
public username: string;
|
||||
public password: string;
|
||||
constructor(optionsArg: IDockerRegistryConstructorOptions) {
|
||||
this.registryUrl = optionsArg.registryUrl;
|
||||
this.username = optionsArg.username;
|
||||
this.password = optionsArg.password;
|
||||
logger.log('info', `created DockerRegistry for ${this.registryUrl}`);
|
||||
}
|
||||
|
||||
public static fromEnvString(envString: string): DockerRegistry {
|
||||
const dockerRegexResultArray = envString.split('|');
|
||||
if (dockerRegexResultArray.length !== 3) {
|
||||
logger.log('error', 'malformed docker env var...');
|
||||
Deno.exit(1);
|
||||
}
|
||||
const registryUrl = dockerRegexResultArray[0].replace('https://', '').replace('http://', '');
|
||||
const username = dockerRegexResultArray[1];
|
||||
const password = dockerRegexResultArray[2];
|
||||
return new DockerRegistry({
|
||||
registryUrl: registryUrl,
|
||||
username: username,
|
||||
password: password,
|
||||
});
|
||||
}
|
||||
|
||||
public async login() {
|
||||
if (this.registryUrl === 'docker.io') {
|
||||
await bash(`docker login -u ${this.username} -p ${this.password}`);
|
||||
logger.log('info', 'Logged in to standard docker hub');
|
||||
} else {
|
||||
await bash(`docker login -u ${this.username} -p ${this.password} ${this.registryUrl}`);
|
||||
}
|
||||
logger.log('ok', `docker authenticated for ${this.registryUrl}!`);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { logger } from '../szci.logging.ts';
|
||||
import * as plugins from './mod.plugins.ts';
|
||||
|
||||
import { DockerRegistry } from './mod.classes.dockerregistry.ts';
|
||||
|
||||
export class RegistryStorage {
|
||||
objectMap = new plugins.lik.ObjectMap<DockerRegistry>();
|
||||
constructor() {
|
||||
// Nothing here
|
||||
}
|
||||
|
||||
addRegistry(registryArg: DockerRegistry) {
|
||||
this.objectMap.add(registryArg);
|
||||
}
|
||||
|
||||
getRegistryByUrl(registryUrlArg: string) {
|
||||
return this.objectMap.findSync((registryArg) => {
|
||||
return registryArg.registryUrl === registryUrlArg;
|
||||
});
|
||||
}
|
||||
|
||||
async loginAll() {
|
||||
await this.objectMap.forEach(async (registryArg) => {
|
||||
await registryArg.login();
|
||||
});
|
||||
logger.log('success', 'logged in successfully into all available DockerRegistries!');
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { logger } from '../szci.logging.ts';
|
||||
import * as plugins from './mod.plugins.ts';
|
||||
import * as paths from '../szci.paths.ts';
|
||||
|
||||
import { Dockerfile } from './mod.classes.dockerfile.ts';
|
||||
@@ -1 +0,0 @@
|
||||
export * from '../szci.plugins.ts';
|
||||
Reference in New Issue
Block a user