diff --git a/npmextra.json b/npmextra.json index 7b1f1d6..94f8917 100644 --- a/npmextra.json +++ b/npmextra.json @@ -1,9 +1,4 @@ { - "npmts": { - "mode": "default", - "coverageTreshold": "70", - "cli": true - }, "npmci": { "npmGlobalTools": [], "npmAccessLevel": "public", diff --git a/package-lock.json b/package-lock.json index 18a5cff..fe2c905 100644 --- a/package-lock.json +++ b/package-lock.json @@ -151,6 +151,15 @@ } } }, + "@pushrocks/qenv": { + "version": "4.0.2", + "resolved": "https://verdaccio.lossless.one/@pushrocks%2fqenv/-/qenv-4.0.2.tgz", + "integrity": "sha512-ip/yN8etdy4lOTeNjU7dyKB8oN9eEZG3P/DhFYXDAgqEo0oDUUckEjGZadTLhjLw4wb/oQZqrZ/RjXwlCrBciQ==", + "requires": { + "@pushrocks/smartfile": "^7.0.4", + "@pushrocks/smartlog": "^2.0.19" + } + }, "@pushrocks/smartanalytics": { "version": "2.0.15", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartanalytics/-/smartanalytics-2.0.15.tgz", @@ -261,9 +270,9 @@ } }, "@pushrocks/smartgit": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@pushrocks/smartgit/-/smartgit-1.0.12.tgz", - "integrity": "sha512-RTEj2OA1twzkoNeSpOpI4rKyJGpeaKYKakRf8S72YAd8RfsNBObFeVk7rNniSH6cIbtLkGjL31wuvFUiQz8Spg==", + "version": "1.0.13", + "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartgit/-/smartgit-1.0.13.tgz", + "integrity": "sha512-NHI9Umo5bzUVsrSb9RZSA+t9LFsXzeoFmTjIK/LtUd/4oyvbArPRs97tYe1ueufViW1HZh0vXZKigrHxlWvypA==", "requires": { "@pushrocks/smartfile": "^7.0.4", "@pushrocks/smartpath": "^4.0.1", diff --git a/package.json b/package.json index f443568..9acd6a8 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,12 @@ "@pushrocks/lik": "^3.0.11", "@pushrocks/npmextra": "^3.0.5", "@pushrocks/projectinfo": "^4.0.2", + "@pushrocks/qenv": "^4.0.2", "@pushrocks/smartanalytics": "^2.0.15", "@pushrocks/smartcli": "^3.0.7", "@pushrocks/smartdelay": "^2.0.3", "@pushrocks/smartfile": "^7.0.2", - "@pushrocks/smartgit": "^1.0.12", + "@pushrocks/smartgit": "^1.0.13", "@pushrocks/smartlog": "^2.0.19", "@pushrocks/smartlog-destination-local": "^8.0.2", "@pushrocks/smartparam": "^1.0.4", diff --git a/test/test.ts b/test/test.ts index 12571aa..257540d 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,7 +1,6 @@ import { tap, expect } from '@pushrocks/tapbundle'; import * as path from 'path'; -// Setup test process.env.NPMTS_TEST = 'true'; // set up environment @@ -18,66 +17,66 @@ process.cwd = () => { return path.join(__dirname, 'assets/'); }; -// require NPMCI files -import '../ts/index'; -import npmciModDocker = require('../ts/mod_docker/index'); -import npmciModNpm = require('../ts/mod_npm/index'); -import npmciModNode = require('../ts/mod_node/index'); -import npmciModSsh = require('../ts/mod_ssh/index'); -import npmciEnv = require('../ts/npmci.env'); +import * as npmci from '../ts'; // ====== // Docker // ====== -let dockerfile1: npmciModDocker.Dockerfile; -let dockerfile2: npmciModDocker.Dockerfile; -let sortableArray: npmciModDocker.Dockerfile[]; +let dockerfile1: npmci.Dockerfile; +let dockerfile2: npmci.Dockerfile; +let sortableArray: npmci.Dockerfile[]; tap.test('should return valid Dockerfiles', async () => { - dockerfile1 = new npmciModDocker.Dockerfile({ filePath: './Dockerfile', read: true }); - dockerfile2 = new npmciModDocker.Dockerfile({ filePath: './Dockerfile_sometag1', read: true }); + const npmciInstance = new npmci.Npmci(); + dockerfile1 = new npmci.Dockerfile(npmciInstance.dockerManager, { filePath: './Dockerfile', read: true }); + dockerfile2 = new npmci.Dockerfile(npmciInstance.dockerManager, { filePath: './Dockerfile_sometag1', read: true }); expect(dockerfile1.version).to.equal('latest'); return expect(dockerfile2.version).to.equal('sometag1'); }); tap.test('should read a directory of Dockerfiles', async () => { - return npmciModDocker.helpers - .readDockerfiles() - .then(async (readDockerfilesArrayArg: npmciModDocker.Dockerfile[]) => { + const npmciInstance = new npmci.Npmci(); + return npmci.Dockerfile + .readDockerfiles(npmciInstance.dockerManager) + .then(async (readDockerfilesArrayArg: npmci.Dockerfile[]) => { sortableArray = readDockerfilesArrayArg; return expect(readDockerfilesArrayArg[1].version).to.equal('sometag1'); }); }); tap.test('should sort an array of Dockerfiles', async () => { - return npmciModDocker.helpers + return npmci.Dockerfile .sortDockerfiles(sortableArray) - .then(async (sortedArrayArg: npmciModDocker.Dockerfile[]) => { + .then(async (sortedArrayArg: npmci.Dockerfile[]) => { console.log(sortedArrayArg); }); }); tap.test('should build all Dockerfiles', async () => { - return npmciModDocker.handleCli({ + const npmciInstance = new npmci.Npmci(); + return npmciInstance.dockerManager.handleCli({ _: ['docker', 'build'] }); }); tap.test('should test all Dockerfiles', async () => { - return await npmciModDocker.handleCli({ + const npmciInstance = new npmci.Npmci(); + return npmciInstance.dockerManager.handleCli({ _: ['docker', 'test'] }); }); tap.test('should test dockerfiles', async () => { - return await npmciModDocker.handleCli({ + const npmciInstance = new npmci.Npmci(); + return npmciInstance.dockerManager.handleCli({ _: ['docker', 'test'] }); }); tap.test('should login docker daemon', async () => { - return await npmciModDocker.handleCli({ + const npmciInstance = new npmci.Npmci(); + return npmciInstance.dockerManager.handleCli({ _: ['docker', 'login'] }); }); @@ -86,6 +85,7 @@ tap.test('should login docker daemon', async () => { // SSH // === tap.test('should prepare SSH keys', async () => { + const npmciModSsh = await import('../ts/mod_ssh'); return await npmciModSsh.handleCli({ _: ['ssh', 'prepare'] }); @@ -95,13 +95,14 @@ tap.test('should prepare SSH keys', async () => { // node // ==== tap.test('should install a certain version of node', async () => { - await npmciModNode.handleCli({ + const npmciInstance = new npmci.Npmci(); + await npmciInstance.nodejsManager.handleCli({ _: ['node', 'install', 'stable'] }); - await npmciModNode.handleCli({ + await npmciInstance.nodejsManager.handleCli({ _: ['node', 'install', 'lts'] }); - await npmciModNode.handleCli({ + await npmciInstance.nodejsManager.handleCli({ _: ['node', 'install', 'legacy'] }); }); diff --git a/ts/connector.cloudly/cloudlyconnector.ts b/ts/connector.cloudly/cloudlyconnector.ts new file mode 100644 index 0000000..ea4f00e --- /dev/null +++ b/ts/connector.cloudly/cloudlyconnector.ts @@ -0,0 +1,8 @@ +import * as plugins from '../npmci.plugins'; + +/** + * + */ +export class CloudlyConnector { + +} \ No newline at end of file diff --git a/ts/index.ts b/ts/index.ts index 5e3d2cb..bf14eab 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -1 +1,10 @@ -import './npmci.cli'; +import { Npmci } from './npmci.classes.npmci'; +import { Dockerfile } from './manager.docker/mod.classes.dockerfile'; + +export const npmciInstance = new Npmci(); + +export { Dockerfile, Npmci }; + +if (process.env.CLI_CALL) { + npmciInstance.start(); +} diff --git a/ts/manager.docker/index.ts b/ts/manager.docker/index.ts new file mode 100644 index 0000000..14edc7f --- /dev/null +++ b/ts/manager.docker/index.ts @@ -0,0 +1,177 @@ +import { logger } from '../npmci.logging'; +import * as plugins from './mod.plugins'; +import * as paths from '../npmci.paths'; +import { bash } from '../npmci.bash'; + +// classes +import { Npmci } from '../npmci.classes.npmci'; +import { Dockerfile } from './mod.classes.dockerfile'; +import { DockerRegistry } from './mod.classes.dockerregistry'; +import { RegistryStorage } from './mod.classes.registrystorage'; + +export class NpmciDockerManager { + public npmciRef: Npmci; + public npmciRegistryStorage = new RegistryStorage(); + + constructor(npmciArg: Npmci) { + this.npmciRef = npmciArg; + } + + /** + * handle cli input + * @param argvArg + */ + public handleCli = async argvArg => { + 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', `>>npmci docker ...<< action >>${action}<< not supported`); + } + } else { + logger.log( + 'info', + `>>npmci 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.npmciRegistryStorage.loginAll(); + } + + /** + * logs in docker + */ + public prepare = async () => { + // Always login to GitLab Registry + if (!process.env.CI_BUILD_TOKEN || process.env.CI_BUILD_TOKEN === '') { + logger.log('error', 'No registry token specified by gitlab!'); + process.exit(1); + } + this.npmciRegistryStorage.addRegistry( + new DockerRegistry({ + registryUrl: 'registry.gitlab.com', + username: 'gitlab-ci-token', + password: process.env.CI_BUILD_TOKEN + }) + ); + + // handle registries + await plugins.smartparam.forEachMinimatch( + process.env, + 'NPMCI_LOGIN_DOCKER*', + async envString => { + this.npmciRegistryStorage.addRegistry(DockerRegistry.fromEnvString(envString)); + } + ); + return; + } + + /** + * pushes an image towards a registry + * @param argvArg + */ + public push = async argvArg => { + await this.prepare(); + let dockerRegistryUrls: string[] = []; + + // lets parse the input of cli and npmextra + if (argvArg._.length >= 3 && argvArg._[2] !== 'npmextra') { + dockerRegistryUrls.push(argvArg._[2]); + } else { + if (this.npmciRef.npmciConfig.getConfig().dockerRegistries.length === 0) { + logger.log( + 'warn', + `There are no docker registries listed in npmextra.json! This is strange!` + ); + } + dockerRegistryUrls = dockerRegistryUrls.concat( + this.npmciRef.npmciConfig.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 = this.npmciRegistryStorage.getRegistryByUrl(dockerRegistryUrl); + if (!dockerRegistryToPushTo) { + logger.log( + 'error', + `Cannot push to registry ${dockerRegistryUrl}, because it was not found in the authenticated registry list.` + ); + process.exit(1); + } + for (const dockerfile of dockerfileArray) { + await dockerfile.push(dockerRegistryToPushTo, suffix); + } + } + } + + /** + * pulls an image + */ + public pull = async argvArg => { + await this.prepare(); + const registryUrlArg = argvArg._[2]; + let suffix = null; + if (argvArg._.length >= 4) { + suffix = argvArg._[3]; + } + const localDockerRegistry = this.npmciRegistryStorage.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); + } +} diff --git a/ts/manager.docker/mod.classes.dockerfile.ts b/ts/manager.docker/mod.classes.dockerfile.ts new file mode 100644 index 0000000..d9db08c --- /dev/null +++ b/ts/manager.docker/mod.classes.dockerfile.ts @@ -0,0 +1,316 @@ +import * as plugins from './mod.plugins'; +import * as paths from '../npmci.paths'; + +import { logger } from '../npmci.logging'; +import { bash } from '../npmci.bash'; + +import { DockerRegistry } from './mod.classes.dockerregistry'; +import * as helpers from './mod.helpers'; +import { NpmciDockerManager } from '.'; +import { Npmci } from '../npmci.classes.npmci'; + +/** + * class Dockerfile represents a Dockerfile on disk in npmci + */ +export class Dockerfile { + // STATIC + + /** + * creates instance of class Dockerfile for all Dockerfiles in cwd + * @returns Promise + */ + public static async readDockerfiles( + npmciDockerManagerRefArg: NpmciDockerManager + ): Promise { + const fileTree = await plugins.smartfile.fs.listFileTree(paths.cwd, '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(npmciDockerManagerRefArg, { + 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 + */ + public static async sortDockerfiles(sortableArrayArg: Dockerfile[]): Promise { + const done = plugins.smartpromise.defer(); + logger.log('info', 'sorting Dockerfiles:'); + const sortedArray: Dockerfile[] = []; + const cleanTagsOriginal = Dockerfile.cleanTagsArrayFunction(sortableArrayArg, sortedArray); + let sorterFunctionCounter: number = 0; + const sorterFunction = () => { + sortableArrayArg.forEach(dockerfileArg => { + const cleanTags = Dockerfile.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 (const dockerfile of sortedArray) { + logger.log('info', `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 + */ + public static async mapDockerfiles(sortedDockerfileArray: Dockerfile[]): Promise { + 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(dockerfileNameArg: string): string { + let versionString: string; + const versionRegex = /Dockerfile_([a-zA-Z0-9\.]*)$/; + const 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 + */ + public static dockerBaseImage(dockerfileContentArg: string): string { + const baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/; + const regexResultArray = baseImageRegex.exec(dockerfileContentArg); + return regexResultArray[1]; + } + + /** + * returns the docker tag + */ + public static getDockerTagString( + npmciDockerManagerRef: NpmciDockerManager, + registryArg: string, + repoArg: string, + versionArg: string, + suffixArg?: string + ): string { + // determine wether the repo should be mapped accordingly to the registry + const mappedRepo = npmciDockerManagerRef.npmciRef.npmciConfig.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( + npmciDockerManagerRef: NpmciDockerManager + ): Promise { + logger.log('info', 'checking for env vars to be supplied to the docker build'); + let buildArgsString: string = ''; + for (const key of Object.keys( + npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().dockerBuildargEnvMap + )) { + const targetValue = + process.env[ + npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().dockerBuildargEnvMap[key] + ]; + buildArgsString = `${buildArgsString} --build-arg ${key}="${targetValue}"`; + } + return buildArgsString; + } + + /** + * + */ + public static cleanTagsArrayFunction( + dockerfileArrayArg: Dockerfile[], + trackingArrayArg: Dockerfile[] + ): string[] { + const cleanTagsArray: string[] = []; + dockerfileArrayArg.forEach(dockerfileArg => { + if (trackingArrayArg.indexOf(dockerfileArg) === -1) { + cleanTagsArray.push(dockerfileArg.cleanTag); + } + }); + return cleanTagsArray; + } + + // INSTANCE + public npmciDockerManagerRef: NpmciDockerManager; + + public filePath: string; + public repo: string; + public version: string; + public cleanTag: string; + public buildTag: string; + public containerName: string; + public content: string; + public baseImage: string; + public localBaseImageDependent: boolean; + public localBaseDockerfile: Dockerfile; + + constructor( + dockerManagerRefArg: NpmciDockerManager, + options: { filePath?: string; fileContents?: string | Buffer; read?: boolean } + ) { + this.npmciDockerManagerRef = dockerManagerRefArg; + this.filePath = options.filePath; + this.repo = + this.npmciDockerManagerRef.npmciRef.npmciEnv.repo.user + + '/' + + this.npmciDockerManagerRef.npmciRef.npmciEnv.repo.repo; + this.version = Dockerfile.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 = 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.npmciDockerManagerRef); + const buildCommand = `docker build -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`; + await bash(buildCommand); + return; + } + + /** + * pushes the Dockerfile to a registry + */ + public async push(dockerRegistryArg: DockerRegistry, versionSuffix: string = null) { + const pushTag = Dockerfile.getDockerTagString( + this.npmciDockerManagerRef, + 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 + */ + public async pull(registryArg: DockerRegistry, versionSuffixArg: string = null) { + const pullTag = Dockerfile.getDockerTagString( + this.npmciDockerManagerRef, + 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.NpmciTestDir, 'test_' + this.version + '.sh'); + const testFileExists: boolean = plugins.smartfile.fs.fileExistsSync(testFile); + if (testFileExists) { + // run tests + await bash( + `docker run --name npmci_test_container --entrypoint="bash" ${this.buildTag} -c "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 --entrypoint="bash" npmci_test_image -x /npmci_test/test.sh`); + await bash(`docker rm npmci_test_container`); + await bash(`docker rmi --force npmci_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; + } +} diff --git a/ts/mod_docker/mod.classes.dockerregistry.ts b/ts/manager.docker/mod.classes.dockerregistry.ts similarity index 100% rename from ts/mod_docker/mod.classes.dockerregistry.ts rename to ts/manager.docker/mod.classes.dockerregistry.ts diff --git a/ts/mod_docker/mod.classes.registrystorage.ts b/ts/manager.docker/mod.classes.registrystorage.ts similarity index 100% rename from ts/mod_docker/mod.classes.registrystorage.ts rename to ts/manager.docker/mod.classes.registrystorage.ts diff --git a/ts/manager.docker/mod.helpers.ts b/ts/manager.docker/mod.helpers.ts new file mode 100644 index 0000000..823851f --- /dev/null +++ b/ts/manager.docker/mod.helpers.ts @@ -0,0 +1,6 @@ +import { logger } from '../npmci.logging'; +import * as plugins from './mod.plugins'; +import * as paths from '../npmci.paths'; + +import { Dockerfile } from './mod.classes.dockerfile'; + diff --git a/ts/mod_docker/mod.plugins.ts b/ts/manager.docker/mod.plugins.ts similarity index 100% rename from ts/mod_docker/mod.plugins.ts rename to ts/manager.docker/mod.plugins.ts diff --git a/ts/manager.git/index.ts b/ts/manager.git/index.ts new file mode 100644 index 0000000..a524a41 --- /dev/null +++ b/ts/manager.git/index.ts @@ -0,0 +1,65 @@ +import { logger } from '../npmci.logging'; +import * as plugins from './mod.plugins'; +import { bash } from '../npmci.bash'; +import { Npmci } from '../npmci.classes.npmci'; + +export class NpmciGitManager { + public npmciRef: Npmci; + + constructor(npmciRefArg: Npmci) { + this.npmciRef = npmciRefArg; + } + + /** + * handle cli input + * @param argvArg + */ + public handleCli = async argvArg => { + if (argvArg._.length >= 2) { + const action: string = argvArg._[1]; + switch (action) { + case 'mirror': + await this.mirror(); + break; + default: + logger.log('error', `npmci git -> action >>${action}<< not supported!`); + } + } else { + logger.log('info', `npmci git -> cli arguments invalid! Please read the documentation.`); + } + } + + public mirror = async () => { + const githubToken = process.env.NPMCI_GIT_GITHUBTOKEN; + const githubUser = process.env.NPMCI_GIT_GITHUBGROUP || this.npmciRef.npmciEnv.repo.user; + const githubRepo = process.env.NPMCI_GIT_GITHUB || this.npmciRef.npmciEnv.repo; + if ( + this.npmciRef.npmciConfig.getConfig().projectInfo.npm.packageJson.private === true || + this.npmciRef.npmciConfig.getConfig().npmAccessLevel === 'private' + ) { + logger.log( + 'warn', + `refusing to mirror due to private property use a private mirror location instead` + ); + return; + } + if (githubToken) { + logger.log('info', 'found github token.'); + logger.log('info', 'attempting the mirror the repository to GitHub'); + + // plugins.smartgit.GitRepo; + + // add the mirror + await bash( + `git remote add mirror https://${githubToken}@github.com/${githubUser}/${githubRepo}.git` + ); + await bash(`git push mirror --all`); + logger.log('ok', 'pushed all branches to mirror!'); + await bash(`git push mirror --tags`); + logger.log('ok', 'pushed all tags to mirror!'); + } else { + logger.log('error', `cannot find NPMCI_GIT_GITHUBTOKEN env var!`); + process.exit(1); + } + } +} diff --git a/ts/mod_git/mod.plugins.ts b/ts/manager.git/mod.plugins.ts similarity index 100% rename from ts/mod_git/mod.plugins.ts rename to ts/manager.git/mod.plugins.ts diff --git a/ts/manager.nodejs/index.ts b/ts/manager.nodejs/index.ts new file mode 100644 index 0000000..08016dd --- /dev/null +++ b/ts/manager.nodejs/index.ts @@ -0,0 +1,83 @@ +import * as plugins from '../npmci.plugins'; +import * as paths from '../npmci.paths'; + +import { logger } from '../npmci.logging'; +import { bash, bashNoError, nvmAvailable } from '../npmci.bash'; +import { Npmci } from '../npmci.classes.npmci'; + +export class NpmciNodeJsManager { + public npmciRef: Npmci; + + constructor(npmciRefArg: Npmci) { + this.npmciRef = npmciRefArg; + } + + /** + * handle cli input + * @param argvArg + */ + public async handleCli(argvArg) { + if (argvArg._.length >= 3) { + const action: string = argvArg._[1]; + switch (action) { + case 'install': + await this.install(argvArg._[2]); + break; + default: + logger.log('error', `>>npmci node ...<< action >>${action}<< not supported`); + process.exit(1); + } + } else { + logger.log( + 'error', + `>>npmci node ...<< cli arguments invalid... Please read the documentation.` + ); + process.exit(1); + } + } + + /** + * Install a specific version of node + * @param versionArg + */ + public async install(versionArg) { + logger.log('info', `now installing node version ${versionArg}`); + let version: string; + if (versionArg === 'stable') { + version = '12'; + } else if (versionArg === 'lts') { + version = '10'; + } else if (versionArg === 'legacy') { + version = '8'; + } else { + version = versionArg; + } + if (await nvmAvailable.promise) { + await bash(`nvm install ${version} && nvm alias default ${version}`); + logger.log('success', `Node version ${version} successfully installed!`); + } else { + logger.log('warn', 'Nvm not in path so staying at installed node version!'); + } + logger.log('info', 'now installing latest npm version'); + await bash('npm install -g npm'); + await bash('node -v'); + await bash('npm -v'); + await bash(`npm config set cache ${paths.NpmciCacheDir} --global `); + + // lets look for further config + const config = await this.npmciRef.npmciConfig.getConfig(); + logger.log('info', 'Now checking for needed global npm tools...'); + for (const npmTool of config.npmGlobalTools) { + logger.log('info', `Checking for global "${npmTool}"`); + const whichOutput: string = await bashNoError(`which ${npmTool}`); + const toolAvailable: boolean = !(/not\sfound/.test(whichOutput) || whichOutput === ''); + if (toolAvailable) { + logger.log('info', `Tool ${npmTool} is available`); + } else { + logger.log('info', `globally installing ${npmTool} from npm`); + await bash(`npm install ${npmTool} -q -g`); + } + } + logger.log('success', 'all global npm tools specified in npmextra.json are now available!'); + } +} diff --git a/ts/manager.npm/index.ts b/ts/manager.npm/index.ts new file mode 100644 index 0000000..d22d3b3 --- /dev/null +++ b/ts/manager.npm/index.ts @@ -0,0 +1,162 @@ +import * as plugins from './mod.plugins'; + +import { logger } from '../npmci.logging'; +import { bash, bashNoError, nvmAvailable } from '../npmci.bash'; +import { Npmci } from '../npmci.classes.npmci'; + +export class NpmciNpmManager { + public npmciRef: Npmci; + + constructor(npmciRefArg) { + this.npmciRef = npmciRefArg; + } + + /** + * handle cli input + * @param argvArg + */ + public async handleCli(argvArg) { + if (argvArg._.length >= 2) { + const action: string = argvArg._[1]; + switch (action) { + case 'install': + await this.install(); + break; + case 'prepare': + await this.prepare(); + break; + case 'test': + await this.test(); + break; + case 'publish': + await this.publish(); + break; + default: + logger.log('error', `>>npmci npm ...<< action >>${action}<< not supported`); + process.exit(1); + } + } else { + logger.log( + 'info', + `>>npmci npm ...<< cli arguments invalid... Please read the documentation.` + ); + process.exit(1); + } + } + + /** + * authenticates npm with token from env var + */ + public async prepare() { + const config = this.npmciRef.npmciConfig.getConfig(); + let npmrcFileString: string = ''; + await plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_TOKEN_NPM*', npmEnvArg => { + const npmRegistryUrl = npmEnvArg.split('|')[0]; + const npmToken = npmEnvArg.split('|')[1]; + npmrcFileString += `//${npmRegistryUrl}/:_authToken="${plugins.smartstring.base64.decode( + npmToken + )}"\n`; + }); + logger.log('info', `setting default npm registry to ${config.npmRegistryUrl}`); + npmrcFileString += `registry=https://${config.npmRegistryUrl}\n`; + + // final check + if (npmrcFileString.length > 0) { + logger.log('info', 'found one or more access tokens'); + } else { + logger.log('error', 'no access token found! Exiting!'); + process.exit(1); + } + + // lets save it to disk + plugins.smartfile.memory.toFsSync(npmrcFileString, '/root/.npmrc'); + return; + } + + /** + * publish a package to npm + */ + public async publish() { + const buildPublishCommand = async () => { + let npmAccessCliString = ``; + let npmRegistryCliString = ``; + let publishVerdaccioAsWell = false; + const config = this.npmciRef.npmciConfig.getConfig(); + const availableRegistries: string[] = []; + await plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_TOKEN_NPM*', npmEnvArg => { + availableRegistries.push(npmEnvArg.split('|')[0]); + }); + + // -> configure package access level + if (config.npmAccessLevel) { + npmAccessCliString = `--access=${config.npmAccessLevel}`; + if (config.npmAccessLevel === 'public') { + publishVerdaccioAsWell = true; + } + } else { + throw new Error('You need to set a npmAccessLevel!!!'); + } + // -> configure registry url + if (config.npmRegistryUrl) { + npmRegistryCliString = `--registry=https://${config.npmRegistryUrl}`; + } else { + logger.log('error', `no registry url specified. Can't publish!`); + process.exit(1); + } + + let publishCommand = `npm publish ${npmAccessCliString} ${npmRegistryCliString} `; + + // publishEverywhere + if (publishVerdaccioAsWell) { + const verdaccioRegistry = availableRegistries.find(registryString => + registryString.startsWith('verdaccio') + ); + if (verdaccioRegistry) { + logger.log( + 'info', + `package is public and verdaccio registry is specified. Also publishing to Verdaccio!` + ); + publishCommand = `${publishCommand} && npm publish ${npmAccessCliString} --registry=https://${verdaccioRegistry}`; + } else { + logger.log( + 'error', + `This package should also be published to Verdaccio, however there is no Verdaccio registry data available!` + ); + } + } + return publishCommand; + }; + + // -> preparing + logger.log('info', `now preparing environment:`); + this.prepare(); + await bash(`npm -v`); + + // -> build it + await bash(`npm install`); + await bash(`npm run build`); + + logger.log('success', `Nice!!! The build for the publication was successfull!`); + logger.log('info', `Lets clean up so we don't publish any packages that don't belong to us:`); + // -> clean up before we publish stuff + await bashNoError(`rm -r ./.npmci_cache`); + await bash(`rm -r ./node_modules`); + + logger.log('success', `Cleaned up!:`); + + // -> publish it + logger.log('info', `now invoking npm to publish the package!`); + await bash(await buildPublishCommand()); + logger.log('success', `Package was successfully published!`); + } + + public async install(): Promise { + logger.log('info', 'now installing dependencies:'); + await bash('npm install'); + } + + public async test(): Promise { + logger.log('info', 'now starting tests:'); + await bash('npm test'); + } +} diff --git a/ts/mod_npm/mod.plugins.ts b/ts/manager.npm/mod.plugins.ts similarity index 100% rename from ts/mod_npm/mod.plugins.ts rename to ts/manager.npm/mod.plugins.ts diff --git a/ts/mod_docker/index.ts b/ts/mod_docker/index.ts deleted file mode 100644 index 6689e64..0000000 --- a/ts/mod_docker/index.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { logger } from '../npmci.logging'; -import * as plugins from './mod.plugins'; -import * as paths from '../npmci.paths'; -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'; - -// config -import { configObject } from '../npmci.config'; - -// instances -const npmciRegistryStorage = new RegistryStorage(); - -export { Dockerfile, helpers }; - -export let modArgvArg; // will be set through the build command - -/** - * handle cli input - * @param argvArg - */ -export const handleCli = async argvArg => { - modArgvArg = argvArg; - if (argvArg._.length >= 2) { - const action: string = argvArg._[1]; - switch (action) { - case 'build': - await build(); - break; - case 'login': - case 'prepare': - await login(); - break; - case 'test': - await test(); - break; - case 'push': - await push(argvArg); - break; - case 'pull': - await pull(argvArg); - break; - default: - logger.log('error', `>>npmci docker ...<< action >>${action}<< not supported`); - } - } else { - logger.log( - 'info', - `>>npmci docker ...<< cli arguments invalid... Please read the documentation.` - ); - } -}; - -/** - * builds a cwd of Dockerfiles by triggering a promisechain - */ -export const build = async () => { - await prepare(); - logger.log('info', 'now building Dockerfiles...'); - await helpers - .readDockerfiles() - .then(helpers.sortDockerfiles) - .then(helpers.mapDockerfiles) - .then(helpers.buildDockerfiles); -}; - -/** - * login to the DockerRegistries - */ -export const login = async () => { - await prepare(); - await npmciRegistryStorage.loginAll(); -}; - -/** - * logs in docker - */ -export const prepare = async () => { - // Always login to GitLab Registry - if (!process.env.CI_BUILD_TOKEN || process.env.CI_BUILD_TOKEN === '') { - logger.log('error', 'No registry token specified by gitlab!'); - process.exit(1); - } - npmciRegistryStorage.addRegistry( - new DockerRegistry({ - registryUrl: 'registry.gitlab.com', - username: 'gitlab-ci-token', - password: process.env.CI_BUILD_TOKEN - }) - ); - - // handle registries - await plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_LOGIN_DOCKER*', async envString => { - npmciRegistryStorage.addRegistry(DockerRegistry.fromEnvString(envString)); - }); - return; -}; - -/** - * pushes an image towards a registry - * @param argvArg - */ -export const push = async argvArg => { - await prepare(); - let dockerRegistryUrls: string[] = []; - - // lets parse the input of cli and npmextra - if (argvArg._.length >= 3 && argvArg._[2] !== 'npmextra') { - dockerRegistryUrls.push(argvArg._[2]); - } else { - if (configObject.dockerRegistries.length === 0) { - logger.log( - 'warn', - `There are no docker registries listed in npmextra.json! This is strange!` - ); - } - dockerRegistryUrls = dockerRegistryUrls.concat(configObject.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 helpers - .readDockerfiles() - .then(helpers.sortDockerfiles) - .then(helpers.mapDockerfiles); - const dockerRegistryToPushTo = npmciRegistryStorage.getRegistryByUrl(dockerRegistryUrl); - if (!dockerRegistryToPushTo) { - logger.log( - 'error', - `Cannot push to registry ${dockerRegistryUrl}, because it was not found in the authenticated registry list.` - ); - process.exit(1); - } - for (const dockerfile of dockerfileArray) { - await dockerfile.push(dockerRegistryToPushTo, suffix); - } - } -}; - -export const pull = async argvArg => { - await prepare(); - const registryUrlArg = argvArg._[2]; - let suffix = null; - if (argvArg._.length >= 4) { - suffix = argvArg._[3]; - } - const localDockerRegistry = npmciRegistryStorage.getRegistryByUrl(registryUrlArg); - const dockerfileArray = await helpers - .readDockerfiles() - .then(helpers.sortDockerfiles) - .then(helpers.mapDockerfiles); - for (const dockerfile of dockerfileArray) { - await dockerfile.pull(localDockerRegistry, suffix); - } -}; - -/** - * tests docker files - */ -export const test = async () => { - await prepare(); - 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 deleted file mode 100644 index c38ab0d..0000000 --- a/ts/mod_docker/mod.classes.dockerfile.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { logger } from '../npmci.logging'; -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 { - public filePath: string; - public repo: string; - public version: string; - public cleanTag: string; - public buildTag: string; - public containerName: string; - public content: string; - public baseImage: string; - public localBaseImageDependent: boolean; - public 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 - */ - public async build() { - logger.log('info', 'now building Dockerfile for ' + this.cleanTag); - const buildArgsString = await helpers.getDockerBuildArgs(); - const buildCommand = `docker build -t ${this.buildTag} -f ${this.filePath} ${buildArgsString} .`; - await bash(buildCommand); - return; - } - - /** - * pushes the Dockerfile to a registry - */ - public async push(dockerRegistryArg: DockerRegistry, versionSuffix: string = null) { - const 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 - */ - public async pull(registryArg: DockerRegistry, versionSuffixArg: string = null) { - const pullTag = helpers.getDockerTagString( - 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.NpmciTestDir, 'test_' + this.version + '.sh'); - const testFileExists: boolean = plugins.smartfile.fs.fileExistsSync(testFile); - if (testFileExists) { - // run tests - await bash( - `docker run --name npmci_test_container --entrypoint="bash" ${this.buildTag} -c "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 --entrypoint="bash" npmci_test_image -x /npmci_test/test.sh`); - await bash(`docker rm npmci_test_container`); - await bash(`docker rmi --force npmci_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; - } -} diff --git a/ts/mod_docker/mod.helpers.ts b/ts/mod_docker/mod.helpers.ts deleted file mode 100644 index ccd569e..0000000 --- a/ts/mod_docker/mod.helpers.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { logger } from '../npmci.logging'; -import * as plugins from './mod.plugins'; -import * as paths from '../npmci.paths'; -import * as NpmciEnv from '../npmci.env'; -import * as NpmciConfig from '../npmci.config'; -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 => { - const fileTree = await plugins.smartfile.fs.listFileTree(paths.cwd, '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({ - 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 => { - const done = plugins.smartpromise.defer(); - logger.log('info', 'sorting Dockerfiles:'); - const sortedArray: Dockerfile[] = []; - const cleanTagsOriginal = cleanTagsArrayFunction(sortableArrayArg, sortedArray); - let sorterFunctionCounter: number = 0; - const sorterFunction = () => { - sortableArrayArg.forEach(dockerfileArg => { - const 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 (const dockerfile of sortedArray) { - logger.log('info', `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 (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 - */ -export let testDockerfiles = async (sortedArrayArg: Dockerfile[]) => { - for (const 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; - const versionRegex = /Dockerfile_([a-zA-Z0-9\.]*)$/; - const 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 = (dockerfileContentArg: string) => { - const baseImageRegex = /FROM\s([a-zA-z0-9\/\-\:]*)\n?/; - const regexResultArray = baseImageRegex.exec(dockerfileContentArg); - return regexResultArray[1]; -}; - -/** - * returns the docker tag - */ -export let getDockerTagString = ( - registryArg: string, - repoArg: string, - versionArg: string, - suffixArg?: string -): string => { - // determine wether the repo should be mapped accordingly to the registry - const mappedRepo = NpmciConfig.configObject.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; -}; - -export let getDockerBuildArgs = async (): Promise => { - logger.log('info', 'checking for env vars to be supplied to the docker build'); - let buildArgsString: string = ''; - for (const key in NpmciConfig.configObject.dockerBuildargEnvMap) { - const targetValue = process.env[NpmciConfig.configObject.dockerBuildargEnvMap[key]]; - buildArgsString = `${buildArgsString} --build-arg ${key}="${targetValue}"`; - } - return buildArgsString; -}; - -/** - * - */ -export let cleanTagsArrayFunction = ( - dockerfileArrayArg: Dockerfile[], - trackingArrayArg: Dockerfile[] -): string[] => { - const cleanTagsArray: string[] = []; - dockerfileArrayArg.forEach(dockerfileArg => { - if (trackingArrayArg.indexOf(dockerfileArg) === -1) { - cleanTagsArray.push(dockerfileArg.cleanTag); - } - }); - return cleanTagsArray; -}; diff --git a/ts/mod_git/index.ts b/ts/mod_git/index.ts deleted file mode 100644 index 75cb61b..0000000 --- a/ts/mod_git/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { logger } from '../npmci.logging'; -import * as plugins from './mod.plugins'; -import { bash } from '../npmci.bash'; -import { repo } from '../npmci.env'; - -import { configObject } from '../npmci.config'; - -/** - * handle cli input - * @param argvArg - */ -export let handleCli = async argvArg => { - if (argvArg._.length >= 2) { - const action: string = argvArg._[1]; - switch (action) { - case 'mirror': - await mirror(); - break; - default: - logger.log('error', `npmci git -> action >>${action}<< not supported!`); - } - } else { - logger.log('info', `npmci git -> cli arguments invalid! Please read the documentation.`); - } -}; - -export let mirror = async () => { - const githubToken = process.env.NPMCI_GIT_GITHUBTOKEN; - const githubUser = process.env.NPMCI_GIT_GITHUBGROUP || repo.user; - const githubRepo = process.env.NPMCI_GIT_GITHUB || repo.repo; - if ( - configObject.projectInfo.npm.packageJson.private === true || - configObject.npmAccessLevel === 'private' - ) { - logger.log( - 'warn', - `refusing to mirror due to private property use a private mirror location instead` - ); - return; - } - if (githubToken) { - logger.log('info', 'found github token.'); - logger.log('info', 'attempting the mirror the repository to GitHub'); - - // plugins.smartgit.GitRepo; - - // add the mirror - await bash( - `git remote add mirror https://${githubToken}@github.com/${githubUser}/${githubRepo}.git` - ); - await bash(`git push mirror --all`); - logger.log('ok', 'pushed all branches to mirror!'); - await bash(`git push mirror --tags`); - logger.log('ok', 'pushed all tags to mirror!'); - } else { - logger.log('error', `cannot find NPMCI_GIT_GITHUBTOKEN env var!`); - process.exit(1); - } -}; diff --git a/ts/mod_node/index.ts b/ts/mod_node/index.ts deleted file mode 100644 index 188f4c5..0000000 --- a/ts/mod_node/index.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { logger } from '../npmci.logging'; -import * as plugins from '../npmci.plugins'; -import * as paths from '../npmci.paths'; -import * as npmciConfig from '../npmci.config'; -import { bash, bashNoError, nvmAvailable } from '../npmci.bash'; - -/** - * handle cli input - * @param argvArg - */ -export let handleCli = async argvArg => { - if (argvArg._.length >= 3) { - const action: string = argvArg._[1]; - switch (action) { - case 'install': - await install(argvArg._[2]); - break; - default: - logger.log('error', `>>npmci node ...<< action >>${action}<< not supported`); - process.exit(1); - } - } else { - logger.log( - 'error', - `>>npmci node ...<< cli arguments invalid... Please read the documentation.` - ); - process.exit(1); - } -}; - -/** - * Install a specific version of node - * @param versionArg - */ -export let install = async versionArg => { - logger.log('info', `now installing node version ${versionArg}`); - let version: string; - if (versionArg === 'stable') { - version = '12'; - } else if (versionArg === 'lts') { - version = '10'; - } else if (versionArg === 'legacy') { - version = '8'; - } else { - version = versionArg; - } - if (await nvmAvailable.promise) { - await bash(`nvm install ${version} && nvm alias default ${version}`); - logger.log('success', `Node version ${version} successfully installed!`); - } else { - logger.log('warn', 'Nvm not in path so staying at installed node version!'); - } - logger.log('info', 'now installing latest npm version'); - await bash('npm install -g npm'); - await bash('node -v'); - await bash('npm -v'); - await bash(`npm config set cache ${paths.NpmciCacheDir} --global `); - // lets look for further config - await npmciConfig.getConfig().then(async configArg => { - logger.log('info', 'Now checking for needed global npm tools...'); - for (const npmTool of configArg.npmGlobalTools) { - logger.log('info', `Checking for global "${npmTool}"`); - const whichOutput: string = await bashNoError(`which ${npmTool}`); - const toolAvailable: boolean = !(/not\sfound/.test(whichOutput) || whichOutput === ''); - if (toolAvailable) { - logger.log('info', `Tool ${npmTool} is available`); - } else { - logger.log('info', `globally installing ${npmTool} from npm`); - await bash(`npm install ${npmTool} -q -g`); - } - } - logger.log('success', 'all global npm tools specified in npmextra.json are now available!'); - }); -}; diff --git a/ts/mod_npm/index.ts b/ts/mod_npm/index.ts deleted file mode 100644 index ef501d3..0000000 --- a/ts/mod_npm/index.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { logger } from '../npmci.logging'; -import * as plugins from './mod.plugins'; -import * as configModule from '../npmci.config'; -import { bash, bashNoError, nvmAvailable } from '../npmci.bash'; - -/** - * handle cli input - * @param argvArg - */ -export let handleCli = async argvArg => { - if (argvArg._.length >= 2) { - const action: string = argvArg._[1]; - switch (action) { - case 'install': - await install(); - break; - case 'prepare': - await prepare(); - break; - case 'test': - await test(); - break; - case 'publish': - await publish(); - break; - default: - logger.log('error', `>>npmci npm ...<< action >>${action}<< not supported`); - process.exit(1); - } - } else { - logger.log('info', `>>npmci npm ...<< cli arguments invalid... Please read the documentation.`); - process.exit(1); - } -}; - -/** - * authenticates npm with token from env var - */ -const prepare = async () => { - const config = await configModule.getConfig(); - let npmrcFileString: string = ''; - await plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_TOKEN_NPM*', npmEnvArg => { - const npmRegistryUrl = npmEnvArg.split('|')[0]; - const npmToken = npmEnvArg.split('|')[1]; - npmrcFileString += `//${npmRegistryUrl}/:_authToken="${plugins.smartstring.base64.decode( - npmToken - )}"\n`; - }); - logger.log('info', `setting default npm registry to ${config.npmRegistryUrl}`); - npmrcFileString += `registry=https://${config.npmRegistryUrl}\n`; - - // final check - if (npmrcFileString.length > 0) { - logger.log('info', 'found one or more access tokens'); - } else { - logger.log('error', 'no access token found! Exiting!'); - process.exit(1); - } - - // lets save it to disk - plugins.smartfile.memory.toFsSync(npmrcFileString, '/root/.npmrc'); - return; -}; - -/** - * publish a package to npm - */ -const publish = async () => { - const buildPublishCommand = async () => { - let npmAccessCliString = ``; - let npmRegistryCliString = ``; - let publishVerdaccioAsWell = false; - const config = await configModule.getConfig(); - const availableRegistries: string[] = []; - await plugins.smartparam.forEachMinimatch(process.env, 'NPMCI_TOKEN_NPM*', npmEnvArg => { - availableRegistries.push(npmEnvArg.split('|')[0]); - }); - - // -> configure package access level - if (config.npmAccessLevel) { - npmAccessCliString = `--access=${config.npmAccessLevel}`; - if (config.npmAccessLevel === 'public') { - publishVerdaccioAsWell = true; - } - } else { - throw new Error('You need to set a npmAccessLevel!!!'); - } - // -> configure registry url - if (config.npmRegistryUrl) { - npmRegistryCliString = `--registry=https://${config.npmRegistryUrl}`; - } else { - logger.log('error', `no registry url specified. Can't publish!`); - process.exit(1); - } - - let publishCommand = `npm publish ${npmAccessCliString} ${npmRegistryCliString} `; - - // publishEverywhere - if (publishVerdaccioAsWell) { - const verdaccioRegistry = availableRegistries.find(registryString => - registryString.startsWith('verdaccio') - ); - if (verdaccioRegistry) { - logger.log( - 'info', - `package is public and verdaccio registry is specified. Also publishing to Verdaccio!` - ); - publishCommand = `${publishCommand} && npm publish ${npmAccessCliString} --registry=https://${verdaccioRegistry}`; - } else { - logger.log( - 'error', - `This package should also be published to Verdaccio, however there is no Verdaccio registry data available!` - ); - } - } - return publishCommand; - }; - - // -> preparing - logger.log('info', `now preparing environment:`); - prepare(); - await bash(`npm -v`); - - // -> build it - await bash(`npm install`); - await bash(`npm run build`); - - logger.log('success', `Nice!!! The build for the publication was successfull!`); - logger.log('info', `Lets clean up so we don't publish any packages that don't belong to us:`); - // -> clean up before we publish stuff - await bashNoError(`rm -r ./.npmci_cache`); - await bash(`rm -r ./node_modules`); - - logger.log('success', `Cleaned up!:`); - - // -> publish it - logger.log('info', `now invoking npm to publish the package!`); - await bash(await buildPublishCommand()); - logger.log('success', `Package was successfully published!`); -}; - -const install = async (): Promise => { - logger.log('info', 'now installing dependencies:'); - await bash('npm install'); -}; - -export let test = async (): Promise => { - logger.log('info', 'now starting tests:'); - await bash('npm test'); -}; diff --git a/ts/npmci.classes.npmci.ts b/ts/npmci.classes.npmci.ts new file mode 100644 index 0000000..7e77a08 --- /dev/null +++ b/ts/npmci.classes.npmci.ts @@ -0,0 +1,55 @@ +import * as plugins from './npmci.plugins'; + +import { CloudlyConnector } from './connector.cloudly/cloudlyconnector'; + +import { NpmciInfo } from './npmci.classes.npmciinfo'; +import { NpmciCli } from './npmci.classes.npmcicli'; +import { NpmciConfig } from './npmci.classes.npmciconfig'; + +// mods +import { NpmciDockerManager } from './manager.docker'; +import { NpmciGitManager } from './manager.git'; +import { NpmciNodeJsManager } from './manager.nodejs'; +import { NpmciNpmManager } from './manager.npm'; +import { NpmciEnv } from './npmci.classes.npmcienv'; + +export class Npmci { + public analytics: plugins.smartanalytics.Analytics; + public cloudlyConnector: CloudlyConnector; + + public npmciEnv: NpmciEnv; + public npmciInfo: NpmciInfo; + public npmciConfig: NpmciConfig; + public npmciCli: NpmciCli; + + // managers + public dockerManager: NpmciDockerManager; + public gitManager: NpmciGitManager; + public nodejsManager: NpmciNodeJsManager; + public npmManager: NpmciNpmManager; + + constructor() { + this.analytics = new plugins.smartanalytics.Analytics({ + apiEndPoint: 'https://pubapi.lossless.one/analytics', + projectId: 'gitzone', + appName: 'npmci' + }); + this.cloudlyConnector = new CloudlyConnector(); + this.npmciEnv = new NpmciEnv(this); + this.npmciInfo = new NpmciInfo(this); + this.npmciCli = new NpmciCli(this); + this.npmciConfig = new NpmciConfig(this); + + // managers + this.dockerManager = new NpmciDockerManager(this); + this.gitManager = new NpmciGitManager(this); + this.nodejsManager = new NpmciNodeJsManager(this); + this.npmManager = new NpmciNpmManager(this); + } + + public async start() { + await this.npmciInfo.printToConsole(); + await this.npmciConfig.init(); + this.npmciCli.startParse(); + } +} \ No newline at end of file diff --git a/ts/npmci.classes.npmcicli.ts b/ts/npmci.classes.npmcicli.ts new file mode 100644 index 0000000..7db03dc --- /dev/null +++ b/ts/npmci.classes.npmcicli.ts @@ -0,0 +1,110 @@ +import { logger } from './npmci.logging'; +import * as plugins from './npmci.plugins'; +import * as paths from './npmci.paths'; +import { Npmci } from './npmci.classes.npmci'; + +export class NpmciCli { + public npmciRef: Npmci; + public smartcli: plugins.smartcli.Smartcli; + + constructor(npmciArg: Npmci) { + this.npmciRef = npmciArg; + this.smartcli = new plugins.smartcli.Smartcli(); + this.smartcli.addVersion(this.npmciRef.npmciInfo.projectInfo.version); + + // clean + this.smartcli.addCommand('clean').subscribe( + async argv => { + const modClean = await import('./mod_clean/index'); + await modClean.clean(); + }, + err => { + console.log(err); + process.exit(1); + } + ); + + // command + this.smartcli.addCommand('command').subscribe( + async argv => { + const modCommand = await import('./mod_command/index'); + await modCommand.command(); + }, + err => { + console.log(err); + process.exit(1); + } + ); + + // command + this.smartcli.addCommand('git').subscribe( + async argvArg => { + await this.npmciRef.gitManager.handleCli(argvArg); + }, + err => { + console.log(err); + process.exit(1); + } + ); + + // build + this.smartcli.addCommand('docker').subscribe( + async argvArg => { + await this.npmciRef.dockerManager.handleCli(argvArg); + }, + err => { + console.log(err); + process.exit(1); + } + ); + + // node + this.smartcli.addCommand('node').subscribe( + async argvArg => { + await this.npmciRef.nodejsManager.handleCli(argvArg); + }, + err => { + console.log(err); + process.exit(1); + } + ); + + // npm + this.smartcli.addCommand('npm').subscribe( + async argvArg => { + await this.npmciRef.npmManager.handleCli(argvArg); + }, + err => { + console.log(err); + } + ); + + // trigger + this.smartcli.addCommand('ssh').subscribe( + async argvArg => { + const modSsh = await import('./mod_ssh/index'); + await modSsh.handleCli(argvArg); + }, + err => { + console.log(err); + process.exit(1); + } + ); + + // trigger + this.smartcli.addCommand('trigger').subscribe( + async argv => { + const modTrigger = await import('./mod_trigger/index'); + await modTrigger.trigger(); + }, + err => { + console.log(err); + process.exit(1); + } + ); + } + + public startParse = () => { + this.smartcli.startParse(); + } +} diff --git a/ts/npmci.classes.npmciconfig.ts b/ts/npmci.classes.npmciconfig.ts new file mode 100644 index 0000000..41f3c25 --- /dev/null +++ b/ts/npmci.classes.npmciconfig.ts @@ -0,0 +1,73 @@ +import * as plugins from './npmci.plugins'; +import * as paths from './npmci.paths'; + +import { logger } from './npmci.logging'; +import { Npmci } from './npmci.classes.npmci'; + +/** + * the main config interface for npmci + */ +export interface INpmciOptions { + projectInfo: plugins.projectinfo.ProjectInfo; + + // npm + npmGlobalTools: string[]; + npmAccessLevel?: 'private' | 'public'; + npmRegistryUrl: string; + + // docker + dockerRegistries: string[]; + dockerRegistryRepoMap: { [key: string]: string }; + dockerBuildargEnvMap: { [key: string]: string }; + + // urls + urlCloudly: string; +} + +/** + * a config class for Npmci + */ +export class NpmciConfig { + public npmciRef: Npmci; + + public npmciNpmextra: plugins.npmextra.Npmextra; + public kvStorage: plugins.npmextra.KeyValueStore; + public npmciQenv: plugins.qenv.Qenv; + + private configObject: INpmciOptions; + + constructor(npmciRefArg: Npmci) { + this.npmciRef = npmciRefArg; + + this.npmciNpmextra = new plugins.npmextra.Npmextra(paths.cwd); + this.kvStorage = new plugins.npmextra.KeyValueStore( + 'custom', + `${this.npmciRef.npmciEnv.repo.user}_${this.npmciRef.npmciEnv.repo.repo}` + ); + this.npmciQenv = new plugins.qenv.Qenv( + paths.NpmciProjectDir, + paths.NpmciProjectNogitDir, + false, + logger + ); + + this.configObject = { + projectInfo: new plugins.projectinfo.ProjectInfo(paths.cwd), + npmGlobalTools: [], + dockerRegistries: [], + dockerRegistryRepoMap: {}, + npmAccessLevel: 'private', + npmRegistryUrl: 'registry.npmjs.org', + dockerBuildargEnvMap: {}, + urlCloudly: this.npmciQenv.getEnvVarOnDemand('NPMCI_URL_CLOUDLY') + }; + } + + public async init() { + this.configObject = this.npmciNpmextra.dataFor('npmci', this.configObject); + } + + public getConfig(): INpmciOptions { + return this.configObject; + } +} diff --git a/ts/npmci.classes.npmcienv.ts b/ts/npmci.classes.npmcienv.ts new file mode 100644 index 0000000..dd1b00a --- /dev/null +++ b/ts/npmci.classes.npmcienv.ts @@ -0,0 +1,18 @@ +import * as plugins from './npmci.plugins'; +import { Npmci } from './npmci.classes.npmci'; + +export class NpmciEnv { + public npmciRef: Npmci; + + public repoString: string; + public repo: plugins.smartstring.GitRepo; + + constructor(npmciRefArg: Npmci) { + this.npmciRef = npmciRefArg; + this.repoString = process.env.CI_REPOSITORY_URL; + if (!this.repoString) { + this.repoString = 'https://undefined:undefined@github.com/undefined/undefined.git'; + } + this.repo = new plugins.smartstring.GitRepo(this.repoString); + } +} diff --git a/ts/npmci.classes.npmciinfo.ts b/ts/npmci.classes.npmciinfo.ts new file mode 100644 index 0000000..f2a638d --- /dev/null +++ b/ts/npmci.classes.npmciinfo.ts @@ -0,0 +1,18 @@ +import * as plugins from './npmci.plugins'; +import * as paths from './npmci.paths'; +import { logger } from "./npmci.logging"; +import { Npmci } from './npmci.classes.npmci'; + +export class NpmciInfo { + + public npmciRef: Npmci; + public projectInfo = new plugins.projectinfo.ProjectinfoNpm(paths.NpmciPackageRoot); + + constructor(npmciArg: Npmci) { + this.npmciRef = npmciArg; + } + + public printToConsole () { + logger.log('info', `npmci version: ${this.projectInfo.version}`); + } +} \ No newline at end of file diff --git a/ts/npmci.cli.ts b/ts/npmci.cli.ts deleted file mode 100644 index beebe70..0000000 --- a/ts/npmci.cli.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { logger } from './npmci.logging'; -import * as plugins from './npmci.plugins'; -import * as paths from './npmci.paths'; -import * as npmciMonitor from './npmci.monitor'; -npmciMonitor.run(); - -// Get Info about npmci itself -const npmciInfo = new plugins.projectinfo.ProjectinfoNpm(paths.NpmciPackageRoot); -logger.log('info', 'npmci version: ' + npmciInfo.version); - -import * as NpmciEnv from './npmci.env'; - -const npmciSmartcli = new plugins.smartcli.Smartcli(); -npmciSmartcli.addVersion(npmciInfo.version); - -// clean -npmciSmartcli.addCommand('clean').subscribe( - async argv => { - const modClean = await import('./mod_clean/index'); - await modClean.clean(); - }, - err => { - console.log(err); - process.exit(1); - } -); - -// command -npmciSmartcli.addCommand('command').subscribe( - async argv => { - const modCommand = await import('./mod_command/index'); - await modCommand.command(); - }, - err => { - console.log(err); - process.exit(1); - } -); - -// command -npmciSmartcli.addCommand('git').subscribe( - async argvArg => { - const modGit = await import('./mod_git/index'); - await modGit.handleCli(argvArg); - }, - err => { - console.log(err); - process.exit(1); - } -); - -// build -npmciSmartcli.addCommand('docker').subscribe( - async argvArg => { - const modDocker = await import('./mod_docker/index'); - await modDocker.handleCli(argvArg); - }, - err => { - console.log(err); - process.exit(1); - } -); - -// node -npmciSmartcli.addCommand('node').subscribe( - async argvArg => { - const modNode = await import('./mod_node/index'); - await modNode.handleCli(argvArg); - }, - err => { - console.log(err); - process.exit(1); - } -); - -// npm -npmciSmartcli.addCommand('npm').subscribe( - async argvArg => { - const modNpm = await import('./mod_npm/index'); - await modNpm.handleCli(argvArg); - }, - err => { - console.log(err); - } -); - -// trigger -npmciSmartcli.addCommand('ssh').subscribe( - async argvArg => { - const modSsh = await import('./mod_ssh/index'); - await modSsh.handleCli(argvArg); - }, - err => { - console.log(err); - process.exit(1); - } -); - -// trigger -npmciSmartcli.addCommand('trigger').subscribe( - async argv => { - const modTrigger = await import('./mod_trigger/index'); - await modTrigger.trigger(); - }, - err => { - console.log(err); - process.exit(1); - } -); - -npmciSmartcli.startParse(); diff --git a/ts/npmci.config.ts b/ts/npmci.config.ts deleted file mode 100644 index e4a057e..0000000 --- a/ts/npmci.config.ts +++ /dev/null @@ -1,46 +0,0 @@ -import * as plugins from './npmci.plugins'; -import * as paths from './npmci.paths'; - -import { repo } from './npmci.env'; - -import { KeyValueStore } from '@pushrocks/npmextra'; - -/** - * the main config interface for npmci - */ -export interface INpmciOptions { - projectInfo: plugins.projectinfo.ProjectInfo; - - // npm - npmGlobalTools: string[]; - npmAccessLevel?: 'private' | 'public'; - npmRegistryUrl: string; - - // docker - dockerRegistries: string[]; - dockerRegistryRepoMap: { [key: string]: string }; - dockerBuildargEnvMap: { [key: string]: string }; -} - -// instantiate a kvStorage for the current directory -export let kvStorage = new KeyValueStore('custom', `${repo.user}_${repo.repo}`); - -// handle config retrival -const npmciNpmextra = new plugins.npmextra.Npmextra(paths.cwd); -const defaultConfig: INpmciOptions = { - projectInfo: new plugins.projectinfo.ProjectInfo(paths.cwd), - npmGlobalTools: [], - dockerRegistries: [], - dockerRegistryRepoMap: {}, - npmAccessLevel: 'private', - npmRegistryUrl: 'registry.npmjs.org', - dockerBuildargEnvMap: {} -}; -export let configObject = npmciNpmextra.dataFor('npmci', defaultConfig); - -/** - * gets the npmci portion of the npmextra.json file - */ -export let getConfig = async (): Promise => { - return configObject; -}; diff --git a/ts/npmci.env.ts b/ts/npmci.env.ts deleted file mode 100644 index 32a4330..0000000 --- a/ts/npmci.env.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as plugins from './npmci.plugins'; -import * as paths from './npmci.paths'; -import { GitRepo } from '@pushrocks/smartstring'; -import { Dockerfile } from './mod_docker/index'; - -/** - * a info instance about the git respoitory at cwd :) - */ -let repoString: string = process.env.CI_REPOSITORY_URL; -if (!repoString) { - repoString = 'https://undefined:undefined@github.com/undefined/undefined.git'; -} -export let repo = new GitRepo(repoString); diff --git a/ts/npmci.monitor.ts b/ts/npmci.monitor.ts deleted file mode 100644 index e1765bd..0000000 --- a/ts/npmci.monitor.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { logger } from './npmci.logging'; -import * as plugins from './npmci.plugins'; -import * as env from './npmci.env'; - -import { Analytics } from '@pushrocks/smartanalytics'; - -export let npmciAnalytics = new Analytics({ - apiEndPoint: 'https://pubapi.lossless.one/analytics', - projectId: 'gitzone', - appName: 'npmci' -}); - -export let run = async () => { - npmciAnalytics - .recordEvent('npmToolExecution', { - host: env.repo.host, - user: env.repo.user, - repo: env.repo.repo - }) - .catch(err => { - logger.log('warn', 'Lossless Analytics API not available...'); - }); -}; diff --git a/ts/npmci.paths.ts b/ts/npmci.paths.ts index 32cb2a7..05e5a41 100644 --- a/ts/npmci.paths.ts +++ b/ts/npmci.paths.ts @@ -1,9 +1,13 @@ import * as plugins from './npmci.plugins'; -export let cwd = process.cwd(); +export const cwd = process.cwd(); -export let NpmciPackageRoot = plugins.path.join(__dirname, '../'); -export let NpmciPackageConfig = plugins.path.join(NpmciPackageRoot, './config.json'); -export let NpmciProjectDir = cwd; -export let NpmciTestDir = plugins.path.join(cwd, './test'); -export let NpmciCacheDir = plugins.path.join(cwd, './.npmci_cache'); +// package paths +export const NpmciPackageRoot = plugins.path.join(__dirname, '../'); +export const NpmciPackageConfig = plugins.path.join(NpmciPackageRoot, './config.json'); + +// project paths +export const NpmciProjectDir = cwd; +export const NpmciProjectNogitDir = plugins.path.join(NpmciProjectDir, './.nogit'); +export const NpmciTestDir = plugins.path.join(cwd, './test'); +export const NpmciCacheDir = plugins.path.join(cwd, './.npmci_cache'); diff --git a/ts/npmci.plugins.ts b/ts/npmci.plugins.ts index 2ebb278..5240c5b 100644 --- a/ts/npmci.plugins.ts +++ b/ts/npmci.plugins.ts @@ -11,8 +11,10 @@ export { }; // @pushrocks -import * as projectinfo from '@pushrocks/projectinfo'; import * as npmextra from '@pushrocks/npmextra'; +import * as projectinfo from '@pushrocks/projectinfo'; +import * as qenv from '@pushrocks/qenv'; +import * as smartanalytics from '@pushrocks/smartanalytics'; import * as smartdelay from '@pushrocks/smartdelay'; import * as smartfile from '@pushrocks/smartfile'; import * as smartcli from '@pushrocks/smartcli'; @@ -27,8 +29,10 @@ import * as smartssh from '@pushrocks/smartssh'; import * as smartstring from '@pushrocks/smartstring'; export { - projectinfo, npmextra, + projectinfo, + qenv, + smartanalytics, smartdelay, smartfile, smartcli,