diff --git a/dist/mod_docker/index.d.ts b/dist/mod_docker/index.d.ts index d7417f4..a5bf0f2 100644 --- a/dist/mod_docker/index.d.ts +++ b/dist/mod_docker/index.d.ts @@ -1,111 +1,20 @@ -/// +import * as helpers from './mod.helpers'; +import { Dockerfile } from './mod.classes.dockerfile'; +export { Dockerfile, helpers }; export declare let modArgvArg: any; /** * handle cli input * @param argvArg */ export declare let handleCli: (argvArg: any) => Promise; -/** - * logs in docker - */ -export declare let prepare: () => Promise; /** * builds a cwd of Dockerfiles by triggering a promisechain */ export declare let build: () => Promise; -export declare let push: () => Promise; -export declare let pull: () => Promise; +/** + * logs in docker + */ +export declare let prepare: () => Promise; +export declare let push: (argvArg: any) => Promise; +export declare let pull: (argvArg: any) => Promise; export declare let test: () => Promise; -/** - * creates instance of class Dockerfile for all Dockerfiles in cwd - * @returns Promise - */ -export declare let readDockerfiles: () => Promise; -/** - * sorts Dockerfiles into a dependency chain - * @param sortableArrayArg an array of instances of class Dockerfile - * @returns Promise - */ -export declare let sortDockerfiles: (sortableArrayArg: Dockerfile[]) => Promise; -/** - * maps local Dockerfiles dependencies to the correspoding Dockerfile class instances - */ -export declare let mapDockerfiles: (sortedArray: Dockerfile[]) => Promise; -/** - * builds the correspoding real docker image for each Dockerfile class instance - */ -export declare let buildDockerfiles: (sortedArrayArg: Dockerfile[]) => Promise; -/** - * pushes the real Dockerfile images to a Docker registry - */ -export declare let pushDockerfiles: (sortedArrayArg: Dockerfile[]) => Promise; -/** - * pulls corresponding real Docker images for instances of Dockerfile from a registry. - * This is needed if building, testing, and publishing of Docker images is carried out in seperate CI stages. - */ -export declare let pullDockerfileImages: (sortableArrayArg: Dockerfile[], registryArg?: string) => Promise; -/** - * tests all Dockerfiles in by calling class Dockerfile.test(); - * @param sortedArrayArg Dockerfile[] that contains all Dockerfiles in cwd - */ -export declare let testDockerfiles: (sortedArrayArg: Dockerfile[]) => Promise; -/** - * class Dockerfile represents a Dockerfile on disk in npmci - */ -export declare class Dockerfile { - filePath: string; - repo: string; - version: string; - cleanTag: string; - buildTag: string; - gitlabTestTag: string; - gitlabReleaseTag: string; - releaseTag: string; - containerName: string; - content: string; - baseImage: string; - localBaseImageDependent: boolean; - localBaseDockerfile: Dockerfile; - constructor(options: { - filePath?: string; - fileContents?: string | Buffer; - read?: boolean; - }); - /** - * builds the Dockerfile - */ - build(): Promise; - /** - * pushes the Dockerfile to a registry - */ - push(stageArg: any): Promise; - /** - * pulls the Dockerfile from a registry - */ - pull(registryArg: string): Promise; - /** - * tests the Dockerfile; - */ - test(): Promise; - /** - * gets the id of a Dockerfile - */ - getId(): Promise; -} -/** - * returns a version for a docker file - * @execution SYNC - */ -export declare let dockerFileVersion: (dockerfileNameArg: string) => string; -/** - * returns the docker base image for a Dockerfile - */ -export declare let dockerBaseImage: (dockerfileContentArg: string) => string; -/** - * returns the docker tag - */ -export declare let getDockerTagString: (registryArg: string, repoArg: string, versionArg: string, suffixArg?: string) => string; -/** - * - */ -export declare let cleanTagsArrayFunction: (dockerfileArrayArg: Dockerfile[], trackingArrayArg: Dockerfile[]) => string[]; diff --git a/dist/mod_docker/index.js b/dist/mod_docker/index.js index 9dde4e1..9894c80 100644 --- a/dist/mod_docker/index.js +++ b/dist/mod_docker/index.js @@ -9,9 +9,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", { value: true }); const plugins = require("./mod.plugins"); -const paths = require("../npmci.paths"); const NpmciEnv = require("../npmci.env"); -const npmci_bash_1 = require("../npmci.bash"); +const helpers = require("./mod.helpers"); +exports.helpers = helpers; +// classes +const mod_classes_dockerfile_1 = require("./mod.classes.dockerfile"); +exports.Dockerfile = mod_classes_dockerfile_1.Dockerfile; +const mod_classes_dockerregistry_1 = require("./mod.classes.dockerregistry"); +const mod_classes_registrystorage_1 = require("./mod.classes.registrystorage"); +// instances +let npmciRegistryStorage = new mod_classes_registrystorage_1.RegistryStorage(); /** * handle cli input * @param argvArg @@ -30,6 +37,12 @@ exports.handleCli = (argvArg) => __awaiter(this, void 0, void 0, function* () { case 'test': yield exports.test(); break; + case 'push': + yield exports.push(argvArg); + break; + case 'pull': + yield exports.pull(argvArg); + break; default: plugins.beautylog.error(`>>npmci node ...<< action >>${action}<< not supported`); } @@ -38,321 +51,68 @@ exports.handleCli = (argvArg) => __awaiter(this, void 0, void 0, function* () { plugins.beautylog.log(`>>npmci node ...<< cli arguments invalid... Please read the documentation.`); } }); +/** + * builds a cwd of Dockerfiles by triggering a promisechain + */ +exports.build = () => __awaiter(this, void 0, void 0, function* () { + plugins.beautylog.log('now building Dockerfiles...'); + yield helpers.readDockerfiles() + .then(helpers.sortDockerfiles) + .then(helpers.mapDockerfiles) + .then(helpers.buildDockerfiles); +}); /** * logs in docker */ exports.prepare = () => __awaiter(this, void 0, void 0, function* () { NpmciEnv.setDockerRegistry('docker.io'); // TODO: checkup why we set this here - // handle registries - plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_LOGIN_DOCKER*', (envString) => __awaiter(this, void 0, void 0, function* () { - let dockerRegexResultArray = envString.split('|'); - if (dockerRegexResultArray.length !== 3) { - plugins.beautylog.error('malformed docker env var...'); - process.exit(1); - return; - } - let registry = dockerRegexResultArray[0]; - let username = dockerRegexResultArray[1]; - let password = dockerRegexResultArray[2]; - if (registry === 'docker.io') { - yield npmci_bash_1.bash(`docker login -u ${username} -p ${password}`); - plugins.beautylog.info('Logged in to standard docker hub'); - } - else { - yield npmci_bash_1.bash(`docker login -u ${username} -p ${password} ${registry}`); - } - plugins.beautylog.success(`docker authenticated for ${registry}!`); - })); // Always login to GitLab Registry if (!process.env.CI_BUILD_TOKEN || process.env.CI_BUILD_TOKEN === '') { plugins.beautylog.error('No registry token specified by gitlab!'); return; } - yield npmci_bash_1.bash(`docker login -u gitlab-ci-token -p ${process.env.CI_BUILD_TOKEN} registry.gitlab.com`); - plugins.beautylog.success(`docker authenticated for registry.gitlab.com!`); + npmciRegistryStorage.addRegistry(new mod_classes_dockerregistry_1.DockerRegistry({ + registryUrl: 'registry.gitlab.com', + username: 'gitlab-ci-token', + password: process.env.CI_BUILD_TOKEN + })); + // handle registries + plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_LOGIN_DOCKER*', (envString) => __awaiter(this, void 0, void 0, function* () { + npmciRegistryStorage.addRegistry(mod_classes_dockerregistry_1.DockerRegistry.fromEnvString(envString)); + yield npmciRegistryStorage.loginAll(); + })); return; }); -/** - * builds a cwd of Dockerfiles by triggering a promisechain - */ -exports.build = () => __awaiter(this, void 0, void 0, function* () { - plugins.beautylog.log('now building Dockerfiles...'); - yield exports.readDockerfiles() - .then(exports.sortDockerfiles) - .then(exports.mapDockerfiles) - .then(exports.buildDockerfiles); +exports.push = (argvArg) => __awaiter(this, void 0, void 0, function* () { + let registryUrlArg = argvArg._[2]; + let suffix = null; + if (argvArg._.length >= 4) { + suffix = argvArg._[3]; + } + let dockerfileArray = yield helpers.readDockerfiles() + .then(helpers.sortDockerfiles) + .then(helpers.mapDockerfiles); + let localDockerRegistry = npmciRegistryStorage.getRegistryByUrl(registryUrlArg); + for (let dockerfile of dockerfileArray) { + dockerfile.push(localDockerRegistry, suffix); + } }); -exports.push = () => __awaiter(this, void 0, void 0, function* () { - yield exports.readDockerfiles() - .then(exports.sortDockerfiles) - .then(exports.mapDockerfiles) - .then(exports.pushDockerfiles); -}); -exports.pull = () => __awaiter(this, void 0, void 0, function* () { - return yield exports.readDockerfiles() - .then(exports.pullDockerfileImages); +exports.pull = (argvArg) => __awaiter(this, void 0, void 0, function* () { + let registryUrlArg = argvArg._[2]; + let suffix = null; + if (argvArg._.length >= 4) { + suffix = argvArg._[3]; + } + let localDockerRegistry = npmciRegistryStorage.getRegistryByUrl(registryUrlArg); + let dockerfileArray = yield helpers.readDockerfiles() + .then(helpers.sortDockerfiles) + .then(helpers.mapDockerfiles); + for (let dockerfile of dockerfileArray) { + dockerfile.pull(localDockerRegistry, suffix); + } }); exports.test = () => __awaiter(this, void 0, void 0, function* () { - return yield exports.readDockerfiles() - .then(exports.testDockerfiles); + return yield helpers.readDockerfiles() + .then(helpers.testDockerfiles); }); -/** - * creates instance of class Dockerfile for all Dockerfiles in cwd - * @returns Promise - */ -exports.readDockerfiles = () => __awaiter(this, void 0, void 0, function* () { - let fileTree = yield plugins.smartfile.fs.listFileTree(paths.cwd, 'Dockerfile*'); - // create the Dockerfile array - let readDockerfilesArray = []; - plugins.beautylog.info(`found ${fileTree.length} Dockerfiles:`); - console.log(fileTree); - for (let dockerfilePath of fileTree) { - let myDockerfile = new Dockerfile({ - filePath: dockerfilePath, - read: true - }); - readDockerfilesArray.push(myDockerfile); - } - return readDockerfilesArray; -}); -/** - * sorts Dockerfiles into a dependency chain - * @param sortableArrayArg an array of instances of class Dockerfile - * @returns Promise - */ -exports.sortDockerfiles = (sortableArrayArg) => { - let done = plugins.q.defer(); - plugins.beautylog.info('sorting Dockerfiles:'); - let sortedArray = []; - let cleanTagsOriginal = exports.cleanTagsArrayFunction(sortableArrayArg, sortedArray); - let sorterFunctionCounter = 0; - let sorterFunction = function () { - sortableArrayArg.forEach((dockerfileArg) => { - let cleanTags = exports.cleanTagsArrayFunction(sortableArrayArg, sortedArray); - if (cleanTags.indexOf(dockerfileArg.baseImage) === -1 && sortedArray.indexOf(dockerfileArg) === -1) { - sortedArray.push(dockerfileArg); - } - if (cleanTagsOriginal.indexOf(dockerfileArg.baseImage) !== -1) { - dockerfileArg.localBaseImageDependent = true; - } - }); - if (sortableArrayArg.length === sortedArray.length) { - let counter = 1; - for (let dockerfile of sortedArray) { - plugins.beautylog.log(`tag ${counter}: -> ${dockerfile.cleanTag}`); - counter++; - } - done.resolve(sortedArray); - } - else if (sorterFunctionCounter < 10) { - sorterFunctionCounter++; - sorterFunction(); - } - }; - sorterFunction(); - return done.promise; -}; -/** - * maps local Dockerfiles dependencies to the correspoding Dockerfile class instances - */ -exports.mapDockerfiles = (sortedArray) => __awaiter(this, void 0, void 0, function* () { - sortedArray.forEach((dockerfileArg) => { - if (dockerfileArg.localBaseImageDependent) { - sortedArray.forEach((dockfile2) => { - if (dockfile2.cleanTag === dockerfileArg.baseImage) { - dockerfileArg.localBaseDockerfile = dockfile2; - } - }); - } - }); - return sortedArray; -}); -/** - * builds the correspoding real docker image for each Dockerfile class instance - */ -exports.buildDockerfiles = (sortedArrayArg) => __awaiter(this, void 0, void 0, function* () { - for (let dockerfileArg of sortedArrayArg) { - yield dockerfileArg.build(); - } - return sortedArrayArg; -}); -/** - * pushes the real Dockerfile images to a Docker registry - */ -exports.pushDockerfiles = (sortedArrayArg) => __awaiter(this, void 0, void 0, function* () { - let stageArg = (function () { - if (exports.modArgvArg._ && exports.modArgvArg._.length >= 3) { - return exports.modArgvArg._[2]; - } - else { - return NpmciEnv.buildStage; - } - })(); - for (let dockerfileArg of sortedArrayArg) { - yield dockerfileArg.push(stageArg); - } - return sortedArrayArg; -}); -/** - * pulls corresponding real Docker images for instances of Dockerfile from a registry. - * This is needed if building, testing, and publishing of Docker images is carried out in seperate CI stages. - */ -exports.pullDockerfileImages = (sortableArrayArg, registryArg = 'registry.gitlab.com') => __awaiter(this, void 0, void 0, function* () { - for (let dockerfileArg of sortableArrayArg) { - yield dockerfileArg.pull(registryArg); - } - return sortableArrayArg; -}); -/** - * tests all Dockerfiles in by calling class Dockerfile.test(); - * @param sortedArrayArg Dockerfile[] that contains all Dockerfiles in cwd - */ -exports.testDockerfiles = (sortedArrayArg) => __awaiter(this, void 0, void 0, function* () { - for (let dockerfileArg of sortedArrayArg) { - yield dockerfileArg.test(); - } - return sortedArrayArg; -}); -/** - * class Dockerfile represents a Dockerfile on disk in npmci - */ -class Dockerfile { - constructor(options) { - this.filePath = options.filePath; - this.repo = NpmciEnv.repo.user + '/' + NpmciEnv.repo.repo; - this.version = exports.dockerFileVersion(plugins.path.parse(options.filePath).base); - this.cleanTag = this.repo + ':' + this.version; - this.buildTag = this.cleanTag; - this.gitlabTestTag = exports.getDockerTagString('docker.io', this.repo, this.version, 'test'); // TODO: using docker.io until gitlab is fixed - this.gitlabReleaseTag = exports.getDockerTagString('docker.io', this.repo, this.version); // TODO: using docker.io until gitlab is fixed - // the releaseTag determines where the image gets released - this.releaseTag = exports.getDockerTagString('docker.io', this.repo, this.version); - this.containerName = 'dockerfile-' + this.version; - if (options.filePath && options.read) { - this.content = plugins.smartfile.fs.toStringSync(plugins.path.resolve(options.filePath)); - } - this.baseImage = exports.dockerBaseImage(this.content); - this.localBaseImageDependent = false; - } - /** - * builds the Dockerfile - */ - build() { - return __awaiter(this, void 0, void 0, function* () { - plugins.beautylog.info('now building Dockerfile for ' + this.cleanTag); - let buildCommand = `docker build -t ${this.buildTag} -f ${this.filePath} .`; - yield npmci_bash_1.bash(buildCommand); - NpmciEnv.dockerFilesBuilt.push(this); - return; - }); - } - /** - * pushes the Dockerfile to a registry - */ - push(stageArg) { - return __awaiter(this, void 0, void 0, function* () { - yield npmci_bash_1.bash(`docker tag ${this.buildTag} ${this.releaseTag}`); - yield npmci_bash_1.bash(`docker tag ${this.buildTag} ${this.gitlabReleaseTag}`); - yield npmci_bash_1.bash(`docker tag ${this.buildTag} ${this.gitlabTestTag}`); - switch (stageArg) { - case 'release': - yield npmci_bash_1.bash(`docker push ${this.gitlabReleaseTag}`); - yield npmci_bash_1.bash(`docker push ${this.releaseTag}`); - break; - case 'test': - default: - yield npmci_bash_1.bash(`docker push ${this.gitlabTestTag}`); - break; - } - }); - } - /** - * pulls the Dockerfile from a registry - */ - pull(registryArg) { - return __awaiter(this, void 0, void 0, function* () { - yield npmci_bash_1.bash(`docker pull ${this.gitlabTestTag}`); - yield npmci_bash_1.bash(`docker tag ${this.gitlabTestTag} ${this.buildTag}`); - }); - } - /** - * tests the Dockerfile; - */ - test() { - return __awaiter(this, void 0, void 0, function* () { - let testFile = plugins.path.join(paths.NpmciTestDir, 'test_' + this.version + '.sh'); - let testFileExists = plugins.smartfile.fs.fileExistsSync(testFile); - if (testFileExists) { - // run tests - yield npmci_bash_1.bash('docker run --name npmci_test_container ' + this.buildTag + ' mkdir /npmci_test'); - yield npmci_bash_1.bash('docker cp ' + testFile + ' npmci_test_container:/npmci_test/test.sh'); - yield npmci_bash_1.bash('docker commit npmci_test_container npmci_test_image'); - yield npmci_bash_1.bash('docker run npmci_test_image sh /npmci_test/test.sh'); - yield npmci_bash_1.bash('docker rm npmci_test_container'); - yield npmci_bash_1.bash('docker rmi --force npmci_test_image'); - } - else { - plugins.beautylog.warn('skipping tests for ' + this.cleanTag + ' because no testfile was found!'); - } - }); - } - /** - * gets the id of a Dockerfile - */ - getId() { - return __awaiter(this, void 0, void 0, function* () { - let containerId = yield npmci_bash_1.bash('docker inspect --type=image --format=\"{{.Id}}\" ' + this.buildTag); - return containerId; - }); - } -} -exports.Dockerfile = Dockerfile; -/** - * returns a version for a docker file - * @execution SYNC - */ -exports.dockerFileVersion = (dockerfileNameArg) => { - let versionString; - let versionRegex = /Dockerfile_([a-zA-Z0-9\.]*)$/; - let regexResultArray = versionRegex.exec(dockerfileNameArg); - if (regexResultArray && regexResultArray.length === 2) { - versionString = regexResultArray[1]; - } - else { - versionString = 'latest'; - } - return versionString; -}; -/** - * returns the docker base image for a Dockerfile - */ -exports.dockerBaseImage = function (dockerfileContentArg) { - let baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/; - let regexResultArray = baseImageRegex.exec(dockerfileContentArg); - return regexResultArray[1]; -}; -/** - * returns the docker tag - */ -exports.getDockerTagString = function (registryArg, repoArg, versionArg, suffixArg) { - // determine wether the suffix is needed - let version = versionArg; - if (suffixArg) { - version = versionArg + '_' + suffixArg; - } - let tagString = `${registryArg}/${repoArg}:${version}`; - return tagString; -}; -/** - * - */ -exports.cleanTagsArrayFunction = function (dockerfileArrayArg, trackingArrayArg) { - let cleanTagsArray = []; - dockerfileArrayArg.forEach(function (dockerfileArg) { - if (trackingArrayArg.indexOf(dockerfileArg) === -1) { - cleanTagsArray.push(dockerfileArg.cleanTag); - } - }); - return cleanTagsArray; -}; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfZG9ja2VyL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQSx5Q0FBd0M7QUFDeEMsd0NBQXVDO0FBQ3ZDLHlDQUF3QztBQUN4Qyw4Q0FBb0M7QUFJcEM7OztHQUdHO0FBQ1EsUUFBQSxTQUFTLEdBQUcsQ0FBTyxPQUFPO0lBQ25DLGtCQUFVLEdBQUcsT0FBTyxDQUFBO0lBQ3BCLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDMUIsSUFBSSxNQUFNLEdBQVcsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUNqQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBQ2YsS0FBSyxPQUFPO2dCQUNWLE1BQU0sYUFBSyxFQUFFLENBQUE7Z0JBQ2IsS0FBSyxDQUFBO1lBQ1AsS0FBSyxTQUFTO2dCQUNaLE1BQU0sZUFBTyxFQUFFLENBQUE7Z0JBQ2YsS0FBSyxDQUFBO1lBQ1AsS0FBSyxNQUFNO2dCQUNULE1BQU0sWUFBSSxFQUFFLENBQUE7Z0JBQ1osS0FBSyxDQUFBO1lBQ1A7Z0JBQ0UsT0FBTyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsK0JBQStCLE1BQU0sa0JBQWtCLENBQUMsQ0FBQTtRQUNwRixDQUFDO0lBQ0gsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ04sT0FBTyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsNEVBQTRFLENBQUMsQ0FBQTtJQUNyRyxDQUFDO0FBQ0gsQ0FBQyxDQUFBLENBQUE7QUFHRDs7R0FFRztBQUNRLFFBQUEsT0FBTyxHQUFHO0lBQ25CLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLENBQUMsQ0FBQSxDQUFDLHFDQUFxQztJQUU3RSxvQkFBb0I7SUFDcEIsT0FBTyxDQUFDLFVBQVUsQ0FB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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfZG9ja2VyL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQSx5Q0FBd0M7QUFFeEMseUNBQXdDO0FBR3hDLHlDQUF3QztBQVl0QywwQkFBTztBQVZULFVBQVU7QUFDVixxRUFBcUQ7QUFRbkQscUJBUk8sbUNBQVUsQ0FRUDtBQVBaLDZFQUE2RDtBQUM3RCwrRUFBK0Q7QUFFL0QsWUFBWTtBQUNaLElBQUksb0JBQW9CLEdBQUcsSUFBSSw2Q0FBZSxFQUFFLENBQUE7QUFTaEQ7OztHQUdHO0FBQ1EsUUFBQSxTQUFTLEdBQUcsQ0FBTyxPQUFPO0lBQ25DLGtCQUFVLEdBQUcsT0FBTyxDQUFBO0lBQ3BCLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDMUIsSUFBSSxNQUFNLEdBQVcsT0FBTyxDQUFDLENBQUMsQ0FBRSxDQUFDLENBQUUsQ0FBQTtRQUNuQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBQ2YsS0FBSyxPQUFPO2dCQUNWLE1BQU0sYUFBSyxFQUFFLENBQUE7Z0JBQ2IsS0FBSyxDQUFBO1lBQ1AsS0FBSyxTQUFTO2dCQUNaLE1BQU0sZUFBTyxFQUFFLENBQUE7Z0JBQ2YsS0FBSyxDQUFBO1lBQ1AsS0FBSyxNQUFNO2dCQUNULE1BQU0sWUFBSSxFQUFFLENBQUE7Z0JBQ1osS0FBSyxDQUFBO1lBQ1AsS0FBSyxNQUFNO2dCQUNULE1BQU0sWUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFBO2dCQUNuQixLQUFLLENBQUE7WUFDUCxLQUFLLE1BQU07Z0JBQ1QsTUFBTSxZQUFJLENBQUMsT0FBTyxDQUFDLENBQUE7Z0JBQ25CLEtBQUssQ0FBQTtZQUNQO2dCQUNFLE9BQU8sQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLCtCQUErQixNQUFNLGtCQUFr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 \ No newline at end of file diff --git a/dist/mod_docker/mod.classes.dockerfile.d.ts b/dist/mod_docker/mod.classes.dockerfile.d.ts new file mode 100644 index 0000000..c936c22 --- /dev/null +++ b/dist/mod_docker/mod.classes.dockerfile.d.ts @@ -0,0 +1,42 @@ +/// +import { DockerRegistry } from './mod.classes.dockerregistry'; +/** + * class Dockerfile represents a Dockerfile on disk in npmci + */ +export declare class Dockerfile { + filePath: string; + repo: string; + version: string; + cleanTag: string; + buildTag: string; + containerName: string; + content: string; + baseImage: string; + localBaseImageDependent: boolean; + localBaseDockerfile: Dockerfile; + constructor(options: { + filePath?: string; + fileContents?: string | Buffer; + read?: boolean; + }); + /** + * builds the Dockerfile + */ + build(): Promise; + /** + * pushes the Dockerfile to a registry + */ + push(dockerRegistryArg: DockerRegistry, versionSuffix?: string): Promise; + /** + * pulls the Dockerfile from a registry + */ + pull(registryArg: DockerRegistry, versionSuffixArg?: string): Promise; + /** + * tests the Dockerfile; + */ + test(): Promise; + /** + * gets the id of a Dockerfile + */ + getId(): Promise; +} diff --git a/dist/mod_docker/mod.classes.dockerfile.js b/dist/mod_docker/mod.classes.dockerfile.js new file mode 100644 index 0000000..193ab44 --- /dev/null +++ b/dist/mod_docker/mod.classes.dockerfile.js @@ -0,0 +1,96 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const plugins = require("./mod.plugins"); +const NpmciEnv = require("../npmci.env"); +const npmci_bash_1 = require("../npmci.bash"); +const paths = require("../npmci.paths"); +const helpers = require("./mod.helpers"); +/** + * class Dockerfile represents a Dockerfile on disk in npmci + */ +class Dockerfile { + constructor(options) { + this.filePath = options.filePath; + this.repo = NpmciEnv.repo.user + '/' + NpmciEnv.repo.repo; + this.version = helpers.dockerFileVersion(plugins.path.parse(options.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 = helpers.dockerBaseImage(this.content); + this.localBaseImageDependent = false; + } + /** + * builds the Dockerfile + */ + build() { + return __awaiter(this, void 0, void 0, function* () { + plugins.beautylog.info('now building Dockerfile for ' + this.cleanTag); + let buildCommand = `docker build -t ${this.buildTag} -f ${this.filePath} .`; + yield npmci_bash_1.bash(buildCommand); + NpmciEnv.dockerFilesBuilt.push(this); + return; + }); + } + /** + * pushes the Dockerfile to a registry + */ + push(dockerRegistryArg, versionSuffix = null) { + return __awaiter(this, void 0, void 0, function* () { + let pushTag = helpers.getDockerTagString(dockerRegistryArg.registryUrl, this.repo, this.version, versionSuffix); + yield npmci_bash_1.bash(`docker tag ${this.buildTag} ${pushTag}`); + yield npmci_bash_1.bash(`docker push ${pushTag}`); + }); + } + /** + * pulls the Dockerfile from a registry + */ + pull(registryArg, versionSuffixArg = null) { + return __awaiter(this, void 0, void 0, function* () { + let pullTag = helpers.getDockerTagString(registryArg.registryUrl, this.repo, this.version, versionSuffixArg); + yield npmci_bash_1.bash(`docker tag ${pullTag} ${this.buildTag}`); + }); + } + /** + * tests the Dockerfile; + */ + test() { + return __awaiter(this, void 0, void 0, function* () { + let testFile = plugins.path.join(paths.NpmciTestDir, 'test_' + this.version + '.sh'); + let testFileExists = plugins.smartfile.fs.fileExistsSync(testFile); + if (testFileExists) { + // run tests + yield npmci_bash_1.bash('docker run --name npmci_test_container ' + this.buildTag + ' mkdir /npmci_test'); + yield npmci_bash_1.bash('docker cp ' + testFile + ' npmci_test_container:/npmci_test/test.sh'); + yield npmci_bash_1.bash('docker commit npmci_test_container npmci_test_image'); + yield npmci_bash_1.bash('docker run npmci_test_image sh /npmci_test/test.sh'); + yield npmci_bash_1.bash('docker rm npmci_test_container'); + yield npmci_bash_1.bash('docker rmi --force npmci_test_image'); + } + else { + plugins.beautylog.warn('skipping tests for ' + this.cleanTag + ' because no testfile was found!'); + } + }); + } + /** + * gets the id of a Dockerfile + */ + getId() { + return __awaiter(this, void 0, void 0, function* () { + let containerId = yield npmci_bash_1.bash('docker inspect --type=image --format=\"{{.Id}}\" ' + this.buildTag); + return containerId; + }); + } +} +exports.Dockerfile = Dockerfile; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kLmNsYXNzZXMuZG9ja2VyZmlsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL21vZF9kb2NrZXIvbW9kLmNsYXNzZXMuZG9ja2VyZmlsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQUEseUNBQXdDO0FBQ3hDLHlDQUF3QztBQUN4Qyw4Q0FBb0M7QUFDcEMsd0NBQXVDO0FBR3ZDLHlDQUF3QztBQUV4Qzs7R0FFRztBQUNIO0lBV0UsWUFBYSxPQUE4RTtRQUN6RixJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUE7UUFDaEMsSUFBSSxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUE7UUFDekQsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFBO1FBQ25GLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQTtRQUM5QyxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUE7UUFFN0IsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQTtRQUNqRCxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3JDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFBO1FBQzFGLENBQUM7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 \ No newline at end of file diff --git a/dist/mod_docker/mod.classes.dockerregistry.d.ts b/dist/mod_docker/mod.classes.dockerregistry.d.ts new file mode 100644 index 0000000..31218f4 --- /dev/null +++ b/dist/mod_docker/mod.classes.dockerregistry.d.ts @@ -0,0 +1,13 @@ +export interface IDockerRegistryConstructorOptions { + registryUrl: string; + username: string; + password: string; +} +export declare class DockerRegistry { + registryUrl: string; + username: string; + password: string; + constructor(optionsArg: IDockerRegistryConstructorOptions); + static fromEnvString(envString: string): DockerRegistry; + login(): Promise; +} diff --git a/dist/mod_docker/mod.classes.dockerregistry.js b/dist/mod_docker/mod.classes.dockerregistry.js new file mode 100644 index 0000000..24cfc21 --- /dev/null +++ b/dist/mod_docker/mod.classes.dockerregistry.js @@ -0,0 +1,49 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const plugins = require("./mod.plugins"); +const npmci_bash_1 = require("../npmci.bash"); +class DockerRegistry { + constructor(optionsArg) { + this.registryUrl = optionsArg.registryUrl; + this.username = optionsArg.username; + this.password = optionsArg.password; + } + static fromEnvString(envString) { + let dockerRegexResultArray = envString.split('|'); + if (dockerRegexResultArray.length !== 3) { + plugins.beautylog.error('malformed docker env var...'); + process.exit(1); + return; + } + let registryUrl = dockerRegexResultArray[0]; + let username = dockerRegexResultArray[1]; + let password = dockerRegexResultArray[2]; + return new DockerRegistry({ + registryUrl: registryUrl, + username: username, + password: password + }); + } + login() { + return __awaiter(this, void 0, void 0, function* () { + if (this.registryUrl === 'docker.io') { + yield npmci_bash_1.bash(`docker login -u ${this.username} -p ${this.password}`); + plugins.beautylog.info('Logged in to standard docker hub'); + } + else { + yield npmci_bash_1.bash(`docker login -u ${this.username} -p ${this.password} ${this.registryUrl}`); + } + plugins.beautylog.success(`docker authenticated for ${this.registryUrl}!`); + }); + } +} +exports.DockerRegistry = DockerRegistry; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kLmNsYXNzZXMuZG9ja2VycmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfZG9ja2VyL21vZC5jbGFzc2VzLmRvY2tlcnJlZ2lzdHJ5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQSx5Q0FBd0M7QUFDeEMsOENBQW9DO0FBUXBDO0lBSUUsWUFBYSxVQUE2QztRQUN4RCxJQUFJLENBQUMsV0FBVyxHQUFHLFVBQVUsQ0FBQyxXQUFXLENBQUE7UUFDekMsSUFBSSxDQUFDLFFBQVEsR0FBRyxVQUFVLENBQUMsUUFBUSxDQUFBO1FBQ25DLElBQUksQ0FBQyxRQUFRLEdBQUcsVUFBVSxDQUFDLFFBQVEsQ0FBQTtJQUNyQyxDQUFDO0lBRUQsTUFBTSxDQUFDLGFBQWEsQ0FBRSxTQUFpQjtRQUNyQyxJQUFJLHNCQUFzQixHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDakQsRUFBRSxDQUFDLENBQUMsc0JBQXNCLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDeEMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQTtZQUN0RCxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFBO1lBQ2YsTUFBTSxDQUFBO1FBQ1IsQ0FBQztRQUNELElBQUksV0FBVyxHQUFHLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQzNDLElBQUksUUFBUSxHQUFHLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQ3hDLElBQUksUUFBUSxHQUFHLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQ3hDLE1BQU0sQ0FBQyxJQUFJLGNBQWMsQ0FBQztZQUN4QixXQUFXLEVBQUUsV0FBVztZQUN4QixRQUFRLEVBQUUsUUFBUTtZQUNsQixRQUFRLEVBQUUsUUFBUTtTQUNuQixDQUFDLENBQUE7SUFDSixDQUFDO0lBRUssS0FBSzs7WUFDVCxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsV0FBVyxLQUFLLFdBQVcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3JDLE1BQU0saUJBQUksQ0FBQyxtQkFBbUIsSUFBSSxDQUFDLFFBQVEsT0FBTyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQTtnQkFDbEUsT0FBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsa0NBQWtDLENBQUMsQ0FBQTtZQUM1RCxDQUFDO1lBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ04sTUFBTSxpQkFBSSxDQUFDLG1CQUFtQixJQUFJLENBQUMsUUFBUSxPQUFPLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUE7WUFDeEYsQ0FBQztZQUNELE9BQU8sQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLDRCQUE0QixJQUFJLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQTtRQUM1RSxDQUFDO0tBQUE7Q0FDRjtBQXBDRCx3Q0FvQ0MifQ== \ No newline at end of file diff --git a/dist/mod_docker/mod.classes.registrystorage.d.ts b/dist/mod_docker/mod.classes.registrystorage.d.ts new file mode 100644 index 0000000..1e8e655 --- /dev/null +++ b/dist/mod_docker/mod.classes.registrystorage.d.ts @@ -0,0 +1,9 @@ +import { Objectmap } from 'lik'; +import { DockerRegistry } from './mod.classes.dockerregistry'; +export declare class RegistryStorage { + objectMap: Objectmap; + constructor(); + addRegistry(registryArg: DockerRegistry): void; + getRegistryByUrl(registryUrlArg: string): DockerRegistry; + loginAll(): Promise; +} diff --git a/dist/mod_docker/mod.classes.registrystorage.js b/dist/mod_docker/mod.classes.registrystorage.js new file mode 100644 index 0000000..0647df8 --- /dev/null +++ b/dist/mod_docker/mod.classes.registrystorage.js @@ -0,0 +1,34 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const lik_1 = require("lik"); +class RegistryStorage { + constructor() { + this.objectMap = new lik_1.Objectmap(); + // Nothing here + } + addRegistry(registryArg) { + this.objectMap.add(registryArg); + } + getRegistryByUrl(registryUrlArg) { + return this.objectMap.find(registryArg => { + return registryArg.registryUrl === registryUrlArg; + }); + } + loginAll() { + return __awaiter(this, void 0, void 0, function* () { + yield this.objectMap.forEach((registryArg) => __awaiter(this, void 0, void 0, function* () { + yield registryArg.login(); + })); + }); + } +} +exports.RegistryStorage = RegistryStorage; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kLmNsYXNzZXMucmVnaXN0cnlzdG9yYWdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvbW9kX2RvY2tlci9tb2QuY2xhc3Nlcy5yZWdpc3RyeXN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUNBLDZCQUErQjtBQUkvQjtJQUVFO1FBREEsY0FBUyxHQUFHLElBQUksZUFBUyxFQUFrQixDQUFBO1FBRXpDLGVBQWU7SUFDakIsQ0FBQztJQUVELFdBQVcsQ0FBRSxXQUEyQjtRQUN0QyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQTtJQUNqQyxDQUFDO0lBRUQsZ0JBQWdCLENBQUUsY0FBc0I7UUFDdEMsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFdBQVc7WUFDcEMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxXQUFXLEtBQUssY0FBYyxDQUFBO1FBQ25ELENBQUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUVLLFFBQVE7O1lBQ1osTUFBTSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFNLFdBQVc7Z0JBQzVDLE1BQU0sV0FBVyxDQUFDLEtBQUssRUFBRSxDQUFBO1lBQzNCLENBQUMsQ0FBQSxDQUFDLENBQUE7UUFDSixDQUFDO0tBQUE7Q0FDRjtBQXJCRCwwQ0FxQkMifQ== \ No newline at end of file diff --git a/dist/mod_docker/mod.helpers.d.ts b/dist/mod_docker/mod.helpers.d.ts new file mode 100644 index 0000000..4ba728b --- /dev/null +++ b/dist/mod_docker/mod.helpers.d.ts @@ -0,0 +1,42 @@ +import { Dockerfile } from './mod.classes.dockerfile'; +/** + * creates instance of class Dockerfile for all Dockerfiles in cwd + * @returns Promise + */ +export declare let readDockerfiles: () => Promise; +/** + * sorts Dockerfiles into a dependency chain + * @param sortableArrayArg an array of instances of class Dockerfile + * @returns Promise + */ +export declare let sortDockerfiles: (sortableArrayArg: Dockerfile[]) => Promise; +/** + * maps local Dockerfiles dependencies to the correspoding Dockerfile class instances + */ +export declare let mapDockerfiles: (sortedArray: Dockerfile[]) => Promise; +/** + * builds the correspoding real docker image for each Dockerfile class instance + */ +export declare let buildDockerfiles: (sortedArrayArg: Dockerfile[]) => Promise; +/** + * tests all Dockerfiles in by calling class Dockerfile.test(); + * @param sortedArrayArg Dockerfile[] that contains all Dockerfiles in cwd + */ +export declare let testDockerfiles: (sortedArrayArg: Dockerfile[]) => Promise; +/** + * returns a version for a docker file + * @execution SYNC + */ +export declare let dockerFileVersion: (dockerfileNameArg: string) => string; +/** + * returns the docker base image for a Dockerfile + */ +export declare let dockerBaseImage: (dockerfileContentArg: string) => string; +/** + * returns the docker tag + */ +export declare let getDockerTagString: (registryArg: string, repoArg: string, versionArg: string, suffixArg?: string) => string; +/** + * + */ +export declare let cleanTagsArrayFunction: (dockerfileArrayArg: Dockerfile[], trackingArrayArg: Dockerfile[]) => string[]; diff --git a/dist/mod_docker/mod.helpers.js b/dist/mod_docker/mod.helpers.js new file mode 100644 index 0000000..87d0fe9 --- /dev/null +++ b/dist/mod_docker/mod.helpers.js @@ -0,0 +1,152 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const plugins = require("./mod.plugins"); +const paths = require("../npmci.paths"); +const mod_classes_dockerfile_1 = require("./mod.classes.dockerfile"); +/** + * creates instance of class Dockerfile for all Dockerfiles in cwd + * @returns Promise + */ +exports.readDockerfiles = () => __awaiter(this, void 0, void 0, function* () { + let fileTree = yield plugins.smartfile.fs.listFileTree(paths.cwd, 'Dockerfile*'); + // create the Dockerfile array + let readDockerfilesArray = []; + plugins.beautylog.info(`found ${fileTree.length} Dockerfiles:`); + console.log(fileTree); + for (let dockerfilePath of fileTree) { + let myDockerfile = new mod_classes_dockerfile_1.Dockerfile({ + filePath: dockerfilePath, + read: true + }); + readDockerfilesArray.push(myDockerfile); + } + return readDockerfilesArray; +}); +/** + * sorts Dockerfiles into a dependency chain + * @param sortableArrayArg an array of instances of class Dockerfile + * @returns Promise + */ +exports.sortDockerfiles = (sortableArrayArg) => { + let done = plugins.q.defer(); + plugins.beautylog.info('sorting Dockerfiles:'); + let sortedArray = []; + let cleanTagsOriginal = exports.cleanTagsArrayFunction(sortableArrayArg, sortedArray); + let sorterFunctionCounter = 0; + let sorterFunction = function () { + sortableArrayArg.forEach((dockerfileArg) => { + let cleanTags = exports.cleanTagsArrayFunction(sortableArrayArg, sortedArray); + if (cleanTags.indexOf(dockerfileArg.baseImage) === -1 && sortedArray.indexOf(dockerfileArg) === -1) { + sortedArray.push(dockerfileArg); + } + if (cleanTagsOriginal.indexOf(dockerfileArg.baseImage) !== -1) { + dockerfileArg.localBaseImageDependent = true; + } + }); + if (sortableArrayArg.length === sortedArray.length) { + let counter = 1; + for (let dockerfile of sortedArray) { + plugins.beautylog.log(`tag ${counter}: -> ${dockerfile.cleanTag}`); + counter++; + } + done.resolve(sortedArray); + } + else if (sorterFunctionCounter < 10) { + sorterFunctionCounter++; + sorterFunction(); + } + }; + sorterFunction(); + return done.promise; +}; +/** + * maps local Dockerfiles dependencies to the correspoding Dockerfile class instances + */ +exports.mapDockerfiles = (sortedArray) => __awaiter(this, void 0, void 0, function* () { + sortedArray.forEach((dockerfileArg) => { + if (dockerfileArg.localBaseImageDependent) { + sortedArray.forEach((dockfile2) => { + if (dockfile2.cleanTag === dockerfileArg.baseImage) { + dockerfileArg.localBaseDockerfile = dockfile2; + } + }); + } + }); + return sortedArray; +}); +/** + * builds the correspoding real docker image for each Dockerfile class instance + */ +exports.buildDockerfiles = (sortedArrayArg) => __awaiter(this, void 0, void 0, function* () { + for (let dockerfileArg of sortedArrayArg) { + yield dockerfileArg.build(); + } + return sortedArrayArg; +}); +/** + * tests all Dockerfiles in by calling class Dockerfile.test(); + * @param sortedArrayArg Dockerfile[] that contains all Dockerfiles in cwd + */ +exports.testDockerfiles = (sortedArrayArg) => __awaiter(this, void 0, void 0, function* () { + for (let dockerfileArg of sortedArrayArg) { + yield dockerfileArg.test(); + } + return sortedArrayArg; +}); +/** + * returns a version for a docker file + * @execution SYNC + */ +exports.dockerFileVersion = (dockerfileNameArg) => { + let versionString; + let versionRegex = /Dockerfile_([a-zA-Z0-9\.]*)$/; + let regexResultArray = versionRegex.exec(dockerfileNameArg); + if (regexResultArray && regexResultArray.length === 2) { + versionString = regexResultArray[1]; + } + else { + versionString = 'latest'; + } + return versionString; +}; +/** + * returns the docker base image for a Dockerfile + */ +exports.dockerBaseImage = function (dockerfileContentArg) { + let baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/; + let regexResultArray = baseImageRegex.exec(dockerfileContentArg); + return regexResultArray[1]; +}; +/** + * returns the docker tag + */ +exports.getDockerTagString = function (registryArg, repoArg, versionArg, suffixArg) { + // determine wether the suffix is needed + let version = versionArg; + if (suffixArg) { + version = versionArg + '_' + suffixArg; + } + let tagString = `${registryArg}/${repoArg}:${version}`; + return tagString; +}; +/** + * + */ +exports.cleanTagsArrayFunction = function (dockerfileArrayArg, trackingArrayArg) { + let cleanTagsArray = []; + dockerfileArrayArg.forEach(function (dockerfileArg) { + if (trackingArrayArg.indexOf(dockerfileArg) === -1) { + cleanTagsArray.push(dockerfileArg.cleanTag); + } + }); + return cleanTagsArray; +}; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kLmhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tb2RfZG9ja2VyL21vZC5oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQSx5Q0FBd0M7QUFDeEMsd0NBQXVDO0FBSXZDLHFFQUFxRDtBQUVyRDs7O0dBR0c7QUFDUSxRQUFBLGVBQWUsR0FBRztJQUMzQixJQUFJLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLGFBQWEsQ0FBQyxDQUFBO0lBRWhGLDhCQUE4QjtJQUM5QixJQUFJLG9CQUFvQixHQUFpQixFQUFFLENBQUE7SUFDM0MsT0FBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxRQUFRLENBQUMsTUFBTSxlQUFlLENBQUMsQ0FBQTtJQUMvRCxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBQ3JCLEdBQUcsQ0FBQyxDQUFDLElBQUksY0FBYyxJQUFJLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDcEMsSUFBSSxZQUFZLEdBQUcsSUFBSSxtQ0FBVSxDQUFDO1lBQ2hDLFFBQVEsRUFBRSxjQUFjO1lBQ3hCLElBQUksRUFBRSxJQUFJO1NBQ1gsQ0FBQyxDQUFBO1FBQ0Ysb0JBQW9CLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFBO0lBQ3pDLENBQUM7SUFFRCxNQUFNLENBQUMsb0JBQW9CLENBQUE7QUFFN0IsQ0FBQyxDQUFBLENBQUE7QUFFRDs7OztHQUlHO0FBQ1EsUUFBQSxlQUFlLEdBQUcsQ0FBQyxnQkFBOEI7SUFDMUQsSUFBSSxJQUFJLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQWdCLENBQUE7SUFDMUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsc0JBQXNCLENBQUMsQ0FBQTtJQUM5QyxJQUFJLFdBQVcsR0FBaUIsRUFBRSxD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 \ No newline at end of file diff --git a/package.json b/package.json index 2267eeb..e4597ab 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "cflare": "^1.0.5", "gulp": "^3.9.1", "gulp-function": "^2.2.9", + "lik": "^1.0.39", "lodash": "^4.17.4", "npmextra": "^2.0.9", "projectinfo": "^3.0.2", diff --git a/test/test.ts b/test/test.ts index 602173c..2d318cd 100644 --- a/test/test.ts +++ b/test/test.ts @@ -33,7 +33,7 @@ tap.test('should return valid Dockerfiles', async () => { }) tap.test('should read a directory of Dockerfiles', async () => { - return npmciModDocker.readDockerfiles() + return npmciModDocker.helpers.readDockerfiles() .then(async (readDockerfilesArrayArg: npmciModDocker.Dockerfile[]) => { sortableArray = readDockerfilesArrayArg return expect(readDockerfilesArrayArg[1].version).to.equal('sometag1') @@ -41,7 +41,7 @@ tap.test('should read a directory of Dockerfiles', async () => { }) tap.test('should sort an array of Dockerfiles', async () => { - return npmciModDocker.sortDockerfiles(sortableArray) + return npmciModDocker.helpers.sortDockerfiles(sortableArray) .then(async (sortedArrayArg: npmciModDocker.Dockerfile[]) => { console.log(sortedArrayArg) }) diff --git a/ts/mod_docker/index.ts b/ts/mod_docker/index.ts index e824714..1dbfedd 100644 --- a/ts/mod_docker/index.ts +++ b/ts/mod_docker/index.ts @@ -3,6 +3,21 @@ import * as paths from '../npmci.paths' import * as NpmciEnv from '../npmci.env' import { bash } from '../npmci.bash' +import * as helpers from './mod.helpers' + +// classes +import { Dockerfile } from './mod.classes.dockerfile' +import { DockerRegistry } from './mod.classes.dockerregistry' +import { RegistryStorage } from './mod.classes.registrystorage' + +// instances +let npmciRegistryStorage = new RegistryStorage() + +export { + Dockerfile, + helpers +} + export let modArgvArg // will be set through the build command /** @@ -12,7 +27,7 @@ export let modArgvArg // will be set through the build command export let handleCli = async (argvArg) => { modArgvArg = argvArg if (argvArg._.length >= 2) { - let action: string = argvArg._[1] + let action: string = argvArg._[ 1 ] switch (action) { case 'build': await build() @@ -23,6 +38,12 @@ export let handleCli = async (argvArg) => { case 'test': await test() break + case 'push': + await push(argvArg) + break + case 'pull': + await pull(argvArg) + break default: plugins.beautylog.error(`>>npmci node ...<< action >>${action}<< not supported`) } @@ -31,6 +52,16 @@ export let handleCli = async (argvArg) => { } } +/** + * builds a cwd of Dockerfiles by triggering a promisechain + */ +export let build = async () => { + plugins.beautylog.log('now building Dockerfiles...') + await helpers.readDockerfiles() + .then(helpers.sortDockerfiles) + .then(helpers.mapDockerfiles) + .then(helpers.buildDockerfiles) +} /** * logs in docker @@ -38,339 +69,58 @@ export let handleCli = async (argvArg) => { export let prepare = async () => { NpmciEnv.setDockerRegistry('docker.io') // TODO: checkup why we set this here - // handle registries - plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_LOGIN_DOCKER*', async (envString) => { - let dockerRegexResultArray = envString.split('|') - if (dockerRegexResultArray.length !== 3) { - plugins.beautylog.error('malformed docker env var...') - process.exit(1) - return - } - let registry = dockerRegexResultArray[0] - let username = dockerRegexResultArray[1] - let password = dockerRegexResultArray[2] - if (registry === 'docker.io') { - await bash(`docker login -u ${username} -p ${password}`) - plugins.beautylog.info('Logged in to standard docker hub') - } else { - await bash(`docker login -u ${username} -p ${password} ${registry}`) - } - plugins.beautylog.success(`docker authenticated for ${registry}!`) - }) - // Always login to GitLab Registry if (!process.env.CI_BUILD_TOKEN || process.env.CI_BUILD_TOKEN === '') { plugins.beautylog.error('No registry token specified by gitlab!') return } - await bash(`docker login -u gitlab-ci-token -p ${process.env.CI_BUILD_TOKEN} registry.gitlab.com`) - plugins.beautylog.success(`docker authenticated for registry.gitlab.com!`) + npmciRegistryStorage.addRegistry(new DockerRegistry({ + registryUrl: 'registry.gitlab.com', + username: 'gitlab-ci-token', + password: process.env.CI_BUILD_TOKEN + })) + + // handle registries + plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_LOGIN_DOCKER*', async (envString) => { + npmciRegistryStorage.addRegistry( + DockerRegistry.fromEnvString(envString) + ) + await npmciRegistryStorage.loginAll() + }) return } -/** - * builds a cwd of Dockerfiles by triggering a promisechain - */ -export let build = async () => { - plugins.beautylog.log('now building Dockerfiles...') - await readDockerfiles() - .then(sortDockerfiles) - .then(mapDockerfiles) - .then(buildDockerfiles) +export let push = async (argvArg) => { + let registryUrlArg = argvArg._[ 2 ] + let suffix = null + if (argvArg._.length >= 4) { + suffix = argvArg._[ 3 ] + } + let dockerfileArray = await helpers.readDockerfiles() + .then(helpers.sortDockerfiles) + .then(helpers.mapDockerfiles) + let localDockerRegistry = npmciRegistryStorage.getRegistryByUrl(registryUrlArg) + for (let dockerfile of dockerfileArray) { + dockerfile.push(localDockerRegistry, suffix) + } } -export let push = async () => { - await readDockerfiles() - .then(sortDockerfiles) - .then(mapDockerfiles) - .then(pushDockerfiles) -} - -export let pull = async () => { - return await readDockerfiles() - .then(pullDockerfileImages) +export let pull = async (argvArg) => { + let registryUrlArg = argvArg._[ 2 ] + let suffix = null + if (argvArg._.length >= 4) { + suffix = argvArg._[ 3 ] + } + let localDockerRegistry = npmciRegistryStorage.getRegistryByUrl(registryUrlArg) + let dockerfileArray = await helpers.readDockerfiles() + .then(helpers.sortDockerfiles) + .then(helpers.mapDockerfiles) + for (let dockerfile of dockerfileArray) { + dockerfile.pull(localDockerRegistry, suffix) + } } export let test = async () => { - return await readDockerfiles() - .then(testDockerfiles) -} - -/** - * creates instance of class Dockerfile for all Dockerfiles in cwd - * @returns Promise - */ -export let readDockerfiles = async (): Promise => { - let fileTree = await plugins.smartfile.fs.listFileTree(paths.cwd, 'Dockerfile*') - - // create the Dockerfile array - let readDockerfilesArray: Dockerfile[] = [] - plugins.beautylog.info(`found ${fileTree.length} Dockerfiles:`) - console.log(fileTree) - for (let dockerfilePath of fileTree) { - let myDockerfile = new Dockerfile({ - filePath: dockerfilePath, - read: true - }) - readDockerfilesArray.push(myDockerfile) - } - - return readDockerfilesArray - -} - -/** - * sorts Dockerfiles into a dependency chain - * @param sortableArrayArg an array of instances of class Dockerfile - * @returns Promise - */ -export let sortDockerfiles = (sortableArrayArg: Dockerfile[]): Promise => { - let done = plugins.q.defer() - plugins.beautylog.info('sorting Dockerfiles:') - let sortedArray: Dockerfile[] = [] - let cleanTagsOriginal = cleanTagsArrayFunction(sortableArrayArg, sortedArray) - let sorterFunctionCounter: number = 0 - let sorterFunction = function () { - sortableArrayArg.forEach((dockerfileArg) => { - let cleanTags = cleanTagsArrayFunction(sortableArrayArg, sortedArray) - if (cleanTags.indexOf(dockerfileArg.baseImage) === -1 && sortedArray.indexOf(dockerfileArg) === -1) { - sortedArray.push(dockerfileArg) - } - if (cleanTagsOriginal.indexOf(dockerfileArg.baseImage) !== -1) { - dockerfileArg.localBaseImageDependent = true - } - }) - if (sortableArrayArg.length === sortedArray.length) { - let counter = 1 - for (let dockerfile of sortedArray) { - plugins.beautylog.log(`tag ${counter}: -> ${dockerfile.cleanTag}`) - counter++ - } - done.resolve(sortedArray) - } else if (sorterFunctionCounter < 10) { - sorterFunctionCounter++ - sorterFunction() - } - } - sorterFunction() - return done.promise -} - -/** - * maps local Dockerfiles dependencies to the correspoding Dockerfile class instances - */ -export let mapDockerfiles = async (sortedArray: Dockerfile[]): Promise => { - sortedArray.forEach((dockerfileArg) => { - if (dockerfileArg.localBaseImageDependent) { - sortedArray.forEach((dockfile2: Dockerfile) => { - if (dockfile2.cleanTag === dockerfileArg.baseImage) { - dockerfileArg.localBaseDockerfile = dockfile2 - } - }) - } - }) - return sortedArray -} - -/** - * builds the correspoding real docker image for each Dockerfile class instance - */ -export let buildDockerfiles = async (sortedArrayArg: Dockerfile[]) => { - for (let dockerfileArg of sortedArrayArg) { - await dockerfileArg.build() - } - return sortedArrayArg -} - -/** - * pushes the real Dockerfile images to a Docker registry - */ -export let pushDockerfiles = async (sortedArrayArg: Dockerfile[]) => { - let stageArg = (function () { - if (modArgvArg._ && modArgvArg._.length >= 3) { - return modArgvArg._[ 2 ] - } else { - return NpmciEnv.buildStage - } - })() - for (let dockerfileArg of sortedArrayArg) { - await dockerfileArg.push(stageArg) - } - return sortedArrayArg -} - -/** - * pulls corresponding real Docker images for instances of Dockerfile from a registry. - * This is needed if building, testing, and publishing of Docker images is carried out in seperate CI stages. - */ -export let pullDockerfileImages = async (sortableArrayArg: Dockerfile[], registryArg = 'registry.gitlab.com') => { - for (let dockerfileArg of sortableArrayArg) { - await dockerfileArg.pull(registryArg) - } - return sortableArrayArg -} - -/** - * tests all Dockerfiles in by calling class Dockerfile.test(); - * @param sortedArrayArg Dockerfile[] that contains all Dockerfiles in cwd - */ -export let testDockerfiles = async (sortedArrayArg: Dockerfile[]) => { - for (let dockerfileArg of sortedArrayArg) { - await dockerfileArg.test() - } - return sortedArrayArg -} - -/** - * class Dockerfile represents a Dockerfile on disk in npmci - */ -export class Dockerfile { - filePath: string - repo: string - version: string - cleanTag: string - buildTag: string - gitlabTestTag: string - gitlabReleaseTag: string - releaseTag: string - containerName: string - content: string - baseImage: string - localBaseImageDependent: boolean - localBaseDockerfile: Dockerfile - constructor(options: { filePath?: string, fileContents?: string | Buffer, read?: boolean }) { - this.filePath = options.filePath - this.repo = NpmciEnv.repo.user + '/' + NpmciEnv.repo.repo - this.version = dockerFileVersion(plugins.path.parse(options.filePath).base) - this.cleanTag = this.repo + ':' + this.version - this.buildTag = this.cleanTag - this.gitlabTestTag = getDockerTagString('docker.io', this.repo, this.version, 'test') // TODO: using docker.io until gitlab is fixed - this.gitlabReleaseTag = getDockerTagString('docker.io', this.repo, this.version) // TODO: using docker.io until gitlab is fixed - - // the releaseTag determines where the image gets released - this.releaseTag = getDockerTagString('docker.io', this.repo, this.version) - - this.containerName = 'dockerfile-' + this.version - if (options.filePath && options.read) { - this.content = plugins.smartfile.fs.toStringSync(plugins.path.resolve(options.filePath)) - } - this.baseImage = dockerBaseImage(this.content) - this.localBaseImageDependent = false - } - - /** - * builds the Dockerfile - */ - async build () { - plugins.beautylog.info('now building Dockerfile for ' + this.cleanTag) - let buildCommand = `docker build -t ${this.buildTag} -f ${this.filePath} .` - await bash(buildCommand) - NpmciEnv.dockerFilesBuilt.push(this) - return - } - - /** - * pushes the Dockerfile to a registry - */ - async push (stageArg) { - await bash(`docker tag ${this.buildTag} ${this.releaseTag}`) - await bash(`docker tag ${this.buildTag} ${this.gitlabReleaseTag}`) - await bash(`docker tag ${this.buildTag} ${this.gitlabTestTag}`) - switch (stageArg) { - case 'release': - await bash(`docker push ${this.gitlabReleaseTag}`) - await bash(`docker push ${this.releaseTag}`) - break - case 'test': - default: - await bash(`docker push ${this.gitlabTestTag}`) - break - } - } - - /** - * pulls the Dockerfile from a registry - */ - async pull (registryArg: string) { - await bash(`docker pull ${this.gitlabTestTag}`) - await bash(`docker tag ${this.gitlabTestTag} ${this.buildTag}`) - } - - /** - * tests the Dockerfile; - */ - async test () { - let testFile: string = plugins.path.join(paths.NpmciTestDir, 'test_' + this.version + '.sh') - let testFileExists: boolean = plugins.smartfile.fs.fileExistsSync(testFile) - if (testFileExists) { - // run tests - await bash('docker run --name npmci_test_container ' + this.buildTag + ' mkdir /npmci_test') - await bash('docker cp ' + testFile + ' npmci_test_container:/npmci_test/test.sh') - await bash('docker commit npmci_test_container npmci_test_image') - await bash('docker run npmci_test_image sh /npmci_test/test.sh') - await bash('docker rm npmci_test_container') - await bash('docker rmi --force npmci_test_image') - } else { - plugins.beautylog.warn('skipping tests for ' + this.cleanTag + ' because no testfile was found!') - } - } - - /** - * gets the id of a Dockerfile - */ - async getId () { - let containerId = await bash('docker inspect --type=image --format=\"{{.Id}}\" ' + this.buildTag) - return containerId - } -} - -/** - * returns a version for a docker file - * @execution SYNC - */ -export let dockerFileVersion = (dockerfileNameArg: string): string => { - let versionString: string - let versionRegex = /Dockerfile_([a-zA-Z0-9\.]*)$/ - let regexResultArray = versionRegex.exec(dockerfileNameArg) - if (regexResultArray && regexResultArray.length === 2) { - versionString = regexResultArray[ 1 ] - } else { - versionString = 'latest' - } - return versionString -} - -/** - * returns the docker base image for a Dockerfile - */ -export let dockerBaseImage = function (dockerfileContentArg: string) { - let baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/ - let regexResultArray = baseImageRegex.exec(dockerfileContentArg) - return regexResultArray[ 1 ] -} - -/** - * returns the docker tag - */ -export let getDockerTagString = function (registryArg: string, repoArg: string, versionArg: string, suffixArg?: string): string { - // determine wether the suffix is needed - let version = versionArg - if (suffixArg) { - version = versionArg + '_' + suffixArg - } - let tagString = `${registryArg}/${repoArg}:${version}` - return tagString -} - -/** - * - */ -export let cleanTagsArrayFunction = function (dockerfileArrayArg: Dockerfile[], trackingArrayArg: Dockerfile[]): string[] { - let cleanTagsArray: string[] = [] - dockerfileArrayArg.forEach(function (dockerfileArg) { - if (trackingArrayArg.indexOf(dockerfileArg) === -1) { - cleanTagsArray.push(dockerfileArg.cleanTag) - } - }) - return cleanTagsArray + return await helpers.readDockerfiles() + .then(helpers.testDockerfiles) } diff --git a/ts/mod_docker/mod.classes.dockerfile.ts b/ts/mod_docker/mod.classes.dockerfile.ts new file mode 100644 index 0000000..dcf0d5b --- /dev/null +++ b/ts/mod_docker/mod.classes.dockerfile.ts @@ -0,0 +1,92 @@ +import * as plugins from './mod.plugins' +import * as NpmciEnv from '../npmci.env' +import { bash } from '../npmci.bash' +import * as paths from '../npmci.paths' + +import { DockerRegistry } from './mod.classes.dockerregistry' +import * as helpers from './mod.helpers' + +/** + * class Dockerfile represents a Dockerfile on disk in npmci + */ +export class Dockerfile { + filePath: string + repo: string + version: string + cleanTag: string + buildTag: string + containerName: string + content: string + baseImage: string + localBaseImageDependent: boolean + localBaseDockerfile: Dockerfile + constructor (options: { filePath?: string, fileContents?: string | Buffer, read?: boolean }) { + this.filePath = options.filePath + this.repo = NpmciEnv.repo.user + '/' + NpmciEnv.repo.repo + this.version = helpers.dockerFileVersion(plugins.path.parse(options.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 = helpers.dockerBaseImage(this.content) + this.localBaseImageDependent = false + } + + /** + * builds the Dockerfile + */ + async build () { + plugins.beautylog.info('now building Dockerfile for ' + this.cleanTag) + let buildCommand = `docker build -t ${this.buildTag} -f ${this.filePath} .` + await bash(buildCommand) + NpmciEnv.dockerFilesBuilt.push(this) + return + } + + /** + * pushes the Dockerfile to a registry + */ + async push (dockerRegistryArg: DockerRegistry, versionSuffix: string = null) { + let pushTag = helpers.getDockerTagString(dockerRegistryArg.registryUrl, this.repo, this.version, versionSuffix) + await bash(`docker tag ${this.buildTag} ${pushTag}`) + await bash(`docker push ${pushTag}`) + } + + /** + * pulls the Dockerfile from a registry + */ + async pull (registryArg: DockerRegistry, versionSuffixArg: string = null) { + let pullTag = helpers.getDockerTagString(registryArg.registryUrl,this.repo, this.version, versionSuffixArg) + await bash(`docker tag ${pullTag} ${this.buildTag}`) + } + + /** + * tests the Dockerfile; + */ + async test () { + let testFile: string = plugins.path.join(paths.NpmciTestDir, 'test_' + this.version + '.sh') + let testFileExists: boolean = plugins.smartfile.fs.fileExistsSync(testFile) + if (testFileExists) { + // run tests + await bash('docker run --name npmci_test_container ' + this.buildTag + ' mkdir /npmci_test') + await bash('docker cp ' + testFile + ' npmci_test_container:/npmci_test/test.sh') + await bash('docker commit npmci_test_container npmci_test_image') + await bash('docker run npmci_test_image sh /npmci_test/test.sh') + await bash('docker rm npmci_test_container') + await bash('docker rmi --force npmci_test_image') + } else { + plugins.beautylog.warn('skipping tests for ' + this.cleanTag + ' because no testfile was found!') + } + } + + /** + * gets the id of a Dockerfile + */ + async getId () { + let containerId = await bash('docker inspect --type=image --format=\"{{.Id}}\" ' + this.buildTag) + return containerId + } +} diff --git a/ts/mod_docker/mod.classes.dockerregistry.ts b/ts/mod_docker/mod.classes.dockerregistry.ts new file mode 100644 index 0000000..e55be99 --- /dev/null +++ b/ts/mod_docker/mod.classes.dockerregistry.ts @@ -0,0 +1,46 @@ +import * as plugins from './mod.plugins' +import { bash } from '../npmci.bash' + +export interface IDockerRegistryConstructorOptions { + registryUrl: string, + username: string, + password: string +} + +export class DockerRegistry { + registryUrl: string + username: string + password: string + constructor (optionsArg: IDockerRegistryConstructorOptions) { + this.registryUrl = optionsArg.registryUrl + this.username = optionsArg.username + this.password = optionsArg.password + } + + static fromEnvString (envString: string): DockerRegistry { + let dockerRegexResultArray = envString.split('|') + if (dockerRegexResultArray.length !== 3) { + plugins.beautylog.error('malformed docker env var...') + process.exit(1) + return + } + let registryUrl = dockerRegexResultArray[0] + let username = dockerRegexResultArray[1] + let password = dockerRegexResultArray[2] + return new DockerRegistry({ + registryUrl: registryUrl, + username: username, + password: password + }) + } + + async login () { + if (this.registryUrl === 'docker.io') { + await bash(`docker login -u ${this.username} -p ${this.password}`) + plugins.beautylog.info('Logged in to standard docker hub') + } else { + await bash(`docker login -u ${this.username} -p ${this.password} ${this.registryUrl}`) + } + plugins.beautylog.success(`docker authenticated for ${this.registryUrl}!`) + } +} diff --git a/ts/mod_docker/mod.classes.registrystorage.ts b/ts/mod_docker/mod.classes.registrystorage.ts new file mode 100644 index 0000000..4badb01 --- /dev/null +++ b/ts/mod_docker/mod.classes.registrystorage.ts @@ -0,0 +1,27 @@ +import * as plugins from './mod.plugins' +import { Objectmap } from 'lik' + +import { DockerRegistry } from './mod.classes.dockerregistry' + +export class RegistryStorage { + objectMap = new Objectmap() + constructor () { + // Nothing here + } + + addRegistry (registryArg: DockerRegistry) { + this.objectMap.add(registryArg) + } + + getRegistryByUrl (registryUrlArg: string) { + return this.objectMap.find(registryArg => { + return registryArg.registryUrl === registryUrlArg + }) + } + + async loginAll () { + await this.objectMap.forEach(async registryArg => { + await registryArg.login() + }) + } +} diff --git a/ts/mod_docker/mod.helpers.ts b/ts/mod_docker/mod.helpers.ts new file mode 100644 index 0000000..5c3fd65 --- /dev/null +++ b/ts/mod_docker/mod.helpers.ts @@ -0,0 +1,154 @@ +import * as plugins from './mod.plugins' +import * as paths from '../npmci.paths' +import * as NpmciEnv from '../npmci.env' +import { bash } from '../npmci.bash' + +import { Dockerfile } from './mod.classes.dockerfile' + +/** + * creates instance of class Dockerfile for all Dockerfiles in cwd + * @returns Promise + */ +export let readDockerfiles = async (): Promise => { + let fileTree = await plugins.smartfile.fs.listFileTree(paths.cwd, 'Dockerfile*') + + // create the Dockerfile array + let readDockerfilesArray: Dockerfile[] = [] + plugins.beautylog.info(`found ${fileTree.length} Dockerfiles:`) + console.log(fileTree) + for (let dockerfilePath of fileTree) { + let myDockerfile = new Dockerfile({ + filePath: dockerfilePath, + read: true + }) + readDockerfilesArray.push(myDockerfile) + } + + return readDockerfilesArray + +} + +/** + * sorts Dockerfiles into a dependency chain + * @param sortableArrayArg an array of instances of class Dockerfile + * @returns Promise + */ +export let sortDockerfiles = (sortableArrayArg: Dockerfile[]): Promise => { + let done = plugins.q.defer() + plugins.beautylog.info('sorting Dockerfiles:') + let sortedArray: Dockerfile[] = [] + let cleanTagsOriginal = cleanTagsArrayFunction(sortableArrayArg, sortedArray) + let sorterFunctionCounter: number = 0 + let sorterFunction = function () { + sortableArrayArg.forEach((dockerfileArg) => { + let cleanTags = cleanTagsArrayFunction(sortableArrayArg, sortedArray) + if (cleanTags.indexOf(dockerfileArg.baseImage) === -1 && sortedArray.indexOf(dockerfileArg) === -1) { + sortedArray.push(dockerfileArg) + } + if (cleanTagsOriginal.indexOf(dockerfileArg.baseImage) !== -1) { + dockerfileArg.localBaseImageDependent = true + } + }) + if (sortableArrayArg.length === sortedArray.length) { + let counter = 1 + for (let dockerfile of sortedArray) { + plugins.beautylog.log(`tag ${counter}: -> ${dockerfile.cleanTag}`) + counter++ + } + done.resolve(sortedArray) + } else if (sorterFunctionCounter < 10) { + sorterFunctionCounter++ + sorterFunction() + } + } + sorterFunction() + return done.promise +} + +/** + * maps local Dockerfiles dependencies to the correspoding Dockerfile class instances + */ +export let mapDockerfiles = async (sortedArray: Dockerfile[]): Promise => { + sortedArray.forEach((dockerfileArg) => { + if (dockerfileArg.localBaseImageDependent) { + sortedArray.forEach((dockfile2: Dockerfile) => { + if (dockfile2.cleanTag === dockerfileArg.baseImage) { + dockerfileArg.localBaseDockerfile = dockfile2 + } + }) + } + }) + return sortedArray +} + +/** + * builds the correspoding real docker image for each Dockerfile class instance + */ +export let buildDockerfiles = async (sortedArrayArg: Dockerfile[]) => { + for (let 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 + */ +export let testDockerfiles = async (sortedArrayArg: Dockerfile[]) => { + for (let dockerfileArg of sortedArrayArg) { + await dockerfileArg.test() + } + return sortedArrayArg +} + +/** + * returns a version for a docker file + * @execution SYNC + */ +export let dockerFileVersion = (dockerfileNameArg: string): string => { + let versionString: string + let versionRegex = /Dockerfile_([a-zA-Z0-9\.]*)$/ + let regexResultArray = versionRegex.exec(dockerfileNameArg) + if (regexResultArray && regexResultArray.length === 2) { + versionString = regexResultArray[ 1 ] + } else { + versionString = 'latest' + } + return versionString +} + +/** + * returns the docker base image for a Dockerfile + */ +export let dockerBaseImage = function (dockerfileContentArg: string) { + let baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/ + let regexResultArray = baseImageRegex.exec(dockerfileContentArg) + return regexResultArray[ 1 ] +} + +/** + * returns the docker tag + */ +export let getDockerTagString = function (registryArg: string, repoArg: string, versionArg: string, suffixArg?: string): string { + // determine wether the suffix is needed + let version = versionArg + if (suffixArg) { + version = versionArg + '_' + suffixArg + } + let tagString = `${registryArg}/${repoArg}:${version}` + return tagString +} + +/** + * + */ +export let cleanTagsArrayFunction = function (dockerfileArrayArg: Dockerfile[], trackingArrayArg: Dockerfile[]): string[] { + let cleanTagsArray: string[] = [] + dockerfileArrayArg.forEach(function (dockerfileArg) { + if (trackingArrayArg.indexOf(dockerfileArg) === -1) { + cleanTagsArray.push(dockerfileArg.cleanTag) + } + }) + return cleanTagsArray +} diff --git a/yarn.lock b/yarn.lock index d1b2b50..9d84a9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1501,9 +1501,9 @@ liftoff@^2.1.0: rechoir "^0.6.2" resolve "^1.1.7" -lik@^1.0.30, lik@^1.0.38: - version "1.0.38" - resolved "https://registry.yarnpkg.com/lik/-/lik-1.0.38.tgz#ccff0abd3d9236a5e4b7d80d514c5c210f18469b" +lik@^1.0.30, lik@^1.0.38, lik@^1.0.39: + version "1.0.39" + resolved "https://registry.yarnpkg.com/lik/-/lik-1.0.39.tgz#20eeaa93167a19f03ba2c9650939145326051ee6" dependencies: "@types/lodash" "^4.14.67" "@types/minimatch" "2.x.x"