diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1d085ca --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +** diff --git a/.gitignore b/.gitignore index 5d8d9ed..07cf5bb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ node_modules dist *.log + +develop.ts diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2a08a89 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,2 @@ +FROM scratch +ARG TEST diff --git a/README.md b/README.md index 7ed92d0..ba72f23 100644 --- a/README.md +++ b/README.md @@ -7,68 +7,52 @@ A [semantic-release](https://github.com/semantic-release/semantic-release) plugin to use semantic versioning for docker images. -## verifyConditions - -verifies that environment variables for authentication via username and password are set. -It uses a registry server provided via config or environment variable (preferred) or defaults to docker hub if none is given. -It also verifies that the credentials are correct by logging in to the given registry. - -## prepare - -tags the specified image with the version number determined by semantic-release and additional tags provided in the configuration. -In addition it supports specifying a complete image name (CIN) via configuration settings according to the canonical format specified by docker: - -`[registryhostname[:port]/][username/]imagename[:tag]` - -## publish +## Configuration -pushes the tagged images to the registry. +### Installation -## Configuration +`npm i --save @iteratec/semantic-release-docker` -### docker registry authentication +### Docker registry authentication The `docker registry` authentication is **required** and can be set via environment variables. ### Environment variables | Variable | Description | -|--------------------------|-------------------------------------------------------------------------------------------| +| ------------------------ | ----------------------------------------------------------------------------------------- | | DOCKER_REGISTRY_URL | The hostname and port used by the desired docker registry. Leave blank to use docker hub. | | DOCKER_REGISTRY_USER | The user name to authenticate with at the registry. | | DOCKER_REGISTRY_PASSWORD | The password used for authentication at the registry. | -### Options - -| Option | Description | -|----------------|--------------------------------------------------------------------------------------------| -| additionalTags | _Optional_. An array of strings allowing to specify additional tags to apply to the image. | -| imageName | **_Required_** The name of the image to release. | -| registryUrl | _Optional_. The hostname and port used by the the registry in format `hostname[:port]`. Omit the port if the registry uses the default port | -| repositoryName | _Optional_. The name of the repository in the registry, e.g. username on docker hub | - ### Usage -full configuration: -``` json +#### Full configuration + +```json { "verifyConfig": ["@iteratec/semantic-release-docker"], - "prepare": { - "path": "@iteratec/semantic-release-docker", - "additionalTags": ["test", "demo"], - "imageName": "my-image", - "registryUrl": "my-private-registry:5678", - "respositoryName": "my-repository" - }, + "prepare": [ + { + "path": "@iteratec/semantic-release-docker", + "additionalTags": ["test", "demo"], + "imageName": "my-image", + "registryUrl": "my-private-registry:5678", + "respositoryName": "my-repository", + "pushVersionTag": true + } + ], "publish": { "path": "@iteratec/semantic-release-docker" } } ``` -results in `my-private-registry:5678/my-repository/my-image` with tags `test`, `demo` and the `` determined by `semantic-release`. -minimum configuration: -``` json +Results in `my-private-registry:5678/my-repository/my-image` with tags `test`, `demo` and the `` determined by `semantic-release`. + +#### Minimum configuration + +```json { "verifyConfig": ["@iteratec/semantic-release-docker"], "prepare": { @@ -80,4 +64,97 @@ minimum configuration: } } ``` -results in `my-image:` \ No newline at end of file + +Results in `my-image:`. + +### Options + +| Option | Description | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| additionalTags | _Optional_. An array of strings allowing to specify additional tags to apply to the image. | +| imageName | **_Required_** The name of the image to release. | +| registryUrl | _Optional_. The hostname and port used by the the registry in format `hostname[:port]`. Omit the port if the registry uses the default port | +| repositoryName | _Optional_. The name of the repository in the registry, e.g. username on docker hub | +| pushVersionTag | _Optional_. Whether the semantic release tag, determined by the version, should be pushed. Default is `true`. | + +## Steps + +### verifyConditions + +It uses a registry server provided via config or environment variable (preferred) or defaults to docker hub if none is given. + +1. Verifies that environment variables for authentication via username and password are set. +2. It also verifies that the credentials are correct by logging in to the given registry. + +### prepare + +Tags the specified image with the version number determined by semantic-release and additional tags provided in the configuration. +In addition it supports specifying a complete image name (CIN) via configuration settings according to the canonical format specified by docker: + +`[registryhostname[:port]/][username/]imagename[:tag]` + +### publish + +Pushes the tagged images to the registry. + +## Contribute + +### Develop + +1. Create a develop.ts file in the root of this Git-Repository and copy this: + +```typescript +import { SemanticReleaseConfig, SemanticReleaseContext } from 'semantic-release'; +import { prepare, publish, verifyConditions } from './src'; +import { DockerPluginConfig } from './src/models'; + +process.env.DOCKER_REGISTRY_USER = ''; +process.env.DOCKER_REGISTRY_PASSWORD = ''; + +const config: SemanticReleaseConfig = { + branch: '', + noCi: true, + repositoryUrl: '', + tagFormat: '' +}; +const context: SemanticReleaseContext = { + logger: { + // tslint:disable-next-line:no-empty + log: (message: string) => {} + }, + options: { + branch: '', + noCi: true, + prepare: [ + { + additionalTags: ['latest'], + imageName: 'testimage', + repositoryName: '', + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig, + { + additionalTags: ['latest'], + imageName: 'testimage1', + repositoryName: '', + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + }, + nextRelease: { + version: '1.0.3', + gitHead: '45jh345g', + gitTag: 'v1.0.3', + notes: 'Nothing special' + } +}; +context.logger.log = (string: string) => { + console.log(string); +}; +verifyConditions(config, context); +prepare(config, context); +publish(config, context); +``` + +2. Simply run the "Debug" VS Code Task diff --git a/package-lock.json b/package-lock.json index 955e14d..23e3e27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,26 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, "@commitlint/cli": { "version": "7.5.2", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-7.5.2.tgz", @@ -21,37 +41,6 @@ "meow": "5.0.0", "resolve-from": "4.0.0", "resolve-global": "0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.2.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@commitlint/config-conventional": { @@ -86,37 +75,6 @@ "requires": { "babel-runtime": "^6.23.0", "chalk": "^2.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@commitlint/is-ignored": { @@ -126,14 +84,6 @@ "dev": true, "requires": { "semver": "5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - } } }, "@commitlint/lint": { @@ -260,9 +210,9 @@ "dev": true }, "@octokit/endpoint": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-4.0.0.tgz", - "integrity": "sha512-b8sptNUekjREtCTJFpOfSIL4SKh65WaakcyxWzRcSPOk5RxkZJ/S8884NGZFxZ+jCB2rDURU66pSHn14cVgWVg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-4.1.1.tgz", + "integrity": "sha512-lfphGC9hglBDiIOU84f1xDUzjWE5j3jGkO3Ng/IpDDVARw760A+/x408JOEpdV20ZUj2GRWdDBC0+HPu5qA5gQ==", "dev": true, "requires": { "deepmerge": "3.2.0", @@ -286,9 +236,9 @@ } }, "@octokit/rest": { - "version": "16.23.4", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.23.4.tgz", - "integrity": "sha512-fQuYQ0vgNLkzeN0KEsqN0aS6EPzcuaePT5M5cE5qnKayaxFwRIQOMhNR/rTmEqo/zDK/20ZAcHsgLKodSsJtww==", + "version": "16.25.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.25.0.tgz", + "integrity": "sha512-QKIzP0gNYjyIGmY3Gpm3beof0WFwxFR+HhRZ+Wi0fYYhkEUvkJiXqKF56Pf5glzzfhEwOrggfluEld5F/ZxsKw==", "dev": true, "requires": { "@octokit/request": "3.0.0", @@ -586,19 +536,18 @@ } }, "@types/dockerode": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.4.tgz", - "integrity": "sha512-Mvdxibijt8nYY8lLiQ9IAWRUiQ2LUin8IatLGz1ZlVxzpfFzKks15Kpz8COXDSrR64xjECUs9hzYsJ/Tk6jzVg==", + "version": "2.5.13", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.13.tgz", + "integrity": "sha512-TgSP2nhCZgKOYcuMyuUs1SvLWZCd20z6SczPadLL11iCEEMDiblE23cwIyc1BR7FPpntwT9Z+IcdFNAXUAKmKQ==", "dev": true, "requires": { - "@types/events": "*", "@types/node": "*" } }, "@types/events": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", - "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", "dev": true }, "@types/glob": { @@ -677,9 +626,9 @@ "dev": true }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "ansi-styles": { @@ -842,44 +791,6 @@ "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", "dev": true }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "babel-polyfill": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", @@ -1004,9 +915,9 @@ } }, "bottleneck": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.17.1.tgz", - "integrity": "sha512-ARJKJRNq6+W7BBYZnkqA1F4+HDclht7QyRJl2haAVtD7xBTG8Prpy6huO+canGLUxZaRrek8U/0NjTvoXACsaQ==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.18.0.tgz", + "integrity": "sha512-U1xiBRaokw4yEguzikOl0VrnZp6uekjpmfrh6rKtr1D+/jFjYCL6J83ZXlGtlBDwVdTmJJ+4Lg5FpB3xmLSiyA==", "dev": true }, "brace-expansion": { @@ -1120,7 +1031,7 @@ "dependencies": { "callsites": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true } @@ -1192,25 +1103,14 @@ } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", + "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", + "ansi-styles": "^3.2.0", "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "supports-color": "^5.2.0" } }, "chardet": { @@ -1323,23 +1223,6 @@ "string-width": "^2.1.1", "strip-ansi": "^4.0.0", "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } } }, "code-point-at": { @@ -1359,12 +1242,12 @@ } }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -1386,9 +1269,9 @@ "dev": true }, "commitizen": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-3.0.7.tgz", - "integrity": "sha512-2j8E8Ik1pe1Jqy1gQ1SzEZDJCdr0ItjvhbXmDqhz186Q3ukRoQHMBkmCqmsFYmfDmchBkrOXV1239mTeXEPJKQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-3.1.1.tgz", + "integrity": "sha512-n5pnG8sNM5a3dS3Kkh3rYr+hFdPWZlqV6pfz6KGLmWV/gsIiTqAwhTgFKkcF/paKUpfIMp0x4YZlD0xLBNTW9g==", "dev": true, "requires": { "cachedir": "2.1.0", @@ -1435,9 +1318,9 @@ } }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "concat-map": { @@ -1448,7 +1331,7 @@ }, "concat-stream": { "version": "1.6.2", - "resolved": "http://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "requires": { "buffer-from": "^1.0.0", @@ -1653,9 +1536,9 @@ "dev": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -1974,9 +1857,9 @@ "dev": true }, "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "esutils": { @@ -2123,15 +2006,6 @@ } } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -2463,12 +2337,12 @@ } }, "find-versions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.0.0.tgz", - "integrity": "sha512-IUvtItVFNmTtKoB0PRfbkR0zR9XMG5rWNO3qI1S8L0zdv+v2gqzM0pAunloxqbqAfT8w7bg8n/5gHzTXte8H5A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.1.0.tgz", + "integrity": "sha512-NCTfNiVzeE/xL+roNDffGuRbrWI6atI18lTJ22vKp7rs2OhYzMK3W1dIdO2TUndH/QMcacM4d1uWwgcZcHK69Q==", "dev": true, "requires": { - "array-uniq": "^2.0.0", + "array-uniq": "^2.1.0", "semver-regex": "^2.0.0" }, "dependencies": { @@ -2543,15 +2417,6 @@ } } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -2876,14 +2741,14 @@ "dev": true }, "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz", + "integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { @@ -2904,7 +2769,7 @@ "optional": true }, "are-we-there-yet": { - "version": "1.1.4", + "version": "1.1.5", "bundled": true, "dev": true, "optional": true, @@ -2928,7 +2793,7 @@ } }, "chownr": { - "version": "1.0.1", + "version": "1.1.1", "bundled": true, "dev": true, "optional": true @@ -2955,16 +2820,16 @@ "optional": true }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-extend": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "dev": true, "optional": true @@ -3013,7 +2878,7 @@ } }, "glob": { - "version": "7.1.2", + "version": "7.1.3", "bundled": true, "dev": true, "optional": true, @@ -3033,12 +2898,12 @@ "optional": true }, "iconv-lite": { - "version": "0.4.21", + "version": "0.4.24", "bundled": true, "dev": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { @@ -3099,16 +2964,16 @@ "dev": true }, "minipass": { - "version": "2.2.4", + "version": "2.3.5", "bundled": true, "dev": true, "requires": { - "safe-buffer": "^5.1.1", + "safe-buffer": "^5.1.2", "yallist": "^3.0.0" } }, "minizlib": { - "version": "1.1.0", + "version": "1.2.1", "bundled": true, "dev": true, "optional": true, @@ -3125,35 +2990,35 @@ } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.2.0", + "version": "2.3.0", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", + "debug": "^4.1.0", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.10.0", + "version": "0.12.0", "bundled": true, "dev": true, "optional": true, "requires": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.1", - "needle": "^2.2.0", + "needle": "^2.2.1", "nopt": "^4.0.1", "npm-packlist": "^1.1.6", "npmlog": "^4.0.2", - "rc": "^1.1.7", + "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", "tar": "^4" @@ -3170,13 +3035,13 @@ } }, "npm-bundled": { - "version": "1.0.3", + "version": "1.0.6", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.1.10", + "version": "1.4.1", "bundled": true, "dev": true, "optional": true, @@ -3251,12 +3116,12 @@ "optional": true }, "rc": { - "version": "1.2.7", + "version": "1.2.8", "bundled": true, "dev": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", + "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" @@ -3286,16 +3151,16 @@ } }, "rimraf": { - "version": "2.6.2", + "version": "2.6.3", "bundled": true, "dev": true, "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "^7.1.3" } }, "safe-buffer": { - "version": "5.1.1", + "version": "5.1.2", "bundled": true, "dev": true }, @@ -3312,7 +3177,7 @@ "optional": true }, "semver": { - "version": "5.5.0", + "version": "5.7.0", "bundled": true, "dev": true, "optional": true @@ -3363,17 +3228,17 @@ "optional": true }, "tar": { - "version": "4.4.1", + "version": "4.4.8", "bundled": true, "dev": true, "optional": true, "requires": { - "chownr": "^1.0.1", + "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", + "safe-buffer": "^5.1.2", "yallist": "^3.0.2" } }, @@ -3384,12 +3249,12 @@ "optional": true }, "wide-align": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true, "dev": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { @@ -3398,7 +3263,7 @@ "dev": true }, "yallist": { - "version": "3.0.2", + "version": "3.0.3", "bundled": true, "dev": true } @@ -3411,9 +3276,9 @@ "dev": true }, "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-func-name": { @@ -3672,15 +3537,23 @@ "dev": true }, "handlebars": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz", - "integrity": "sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", "dev": true, "requires": { "neo-async": "^2.6.0", "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "has": { @@ -3692,15 +3565,6 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -3808,6 +3672,17 @@ "requires": { "agent-base": "4", "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "https-proxy-agent": { @@ -3818,6 +3693,23 @@ "requires": { "agent-base": "^4.1.0", "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } } }, "iconv-lite": { @@ -3908,52 +3800,6 @@ "string-width": "^2.1.0", "strip-ansi": "^4.0.0", "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "interpret": { @@ -4243,15 +4089,15 @@ "dev": true }, "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", - "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -4449,9 +4295,9 @@ } }, "lolex": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-3.1.0.tgz", - "integrity": "sha512-zFo5MgCJ0rZ7gQg69S4pqBsLURbFw11X68C18OcJjJQbqaXm2NoTrGl1IMM3TIz0/BnN1tIs2tzmmqvCsOMMjw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.0.1.tgz", + "integrity": "sha512-UHuOBZ5jjsKuzbB/gRNNW8Vg8f00Emgskdq2kvZxgBJCS0aqquAuXai/SkWORlKeZEiNQWZjFZOqIUcH9LqKCw==", "dev": true }, "longest": { @@ -4538,15 +4384,6 @@ "supports-hyperlinks": "^1.0.1" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4557,15 +4394,6 @@ "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -4718,9 +4546,9 @@ } }, "mocha": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.2.tgz", - "integrity": "sha512-BgD2/RozoSC3uQK5R0isDcxjqaWw2n5HWdk8njYUyZf2NC79ErO5FtYVX52+rfqGoEgMfJf4fuG0IWh2TMzFoA==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", + "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", "dev": true, "requires": { "ansi-colors": "3.2.3", @@ -4732,12 +4560,12 @@ "glob": "7.1.3", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.13.0", + "js-yaml": "3.13.1", "log-symbols": "2.2.0", "minimatch": "3.0.4", "mkdirp": "0.5.1", "ms": "2.1.1", - "node-environment-flags": "1.0.4", + "node-environment-flags": "1.0.5", "object.assign": "4.1.0", "strip-json-comments": "2.0.1", "supports-color": "6.0.0", @@ -4748,12 +4576,6 @@ "yargs-unparser": "1.5.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -4778,12 +4600,6 @@ "locate-path": "^3.0.0" } }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -4798,16 +4614,6 @@ "path-is-absolute": "^1.0.0" } }, - "js-yaml": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz", - "integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -4846,51 +4652,15 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "yargs": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", - "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" + "dev": true + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } }, "yargs-parser": { @@ -4924,9 +4694,9 @@ "dev": true }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", "dev": true, "optional": true }, @@ -5018,18 +4788,27 @@ } }, "node-environment-flags": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.4.tgz", - "integrity": "sha512-M9rwCnWVLW7PX+NUWe3ejEdiLYinRpsEre9hMkU/6NS4h+EEulYaDH1gCEZ2gyXsmw+RXYDaV2JkkTNcsPDJ0Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", "dev": true, "requires": { - "object.getownpropertydescriptors": "^2.0.3" + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } } }, "node-fetch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", - "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.4.0.tgz", + "integrity": "sha512-1mt8bw5JQWWTcwUM1FGjFJLFo5lB/jz6zbm+qwdEh2iqYobKS4aHWgz1d+mvho5cqCaShFDF+hnpgraIi/5tqA==", "dev": true }, "normalize-package-data": { @@ -5051,9 +4830,9 @@ "dev": true }, "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz", + "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -8806,9 +8585,9 @@ "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "resolve": { @@ -8898,9 +8677,9 @@ } }, "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.1.tgz", + "integrity": "sha512-y0j31WJc83wPu31vS1VlAFW5JGrnGC+j+TtGAa1fRQphy48+fDYiDmX8tjGloToEsMkxnouOg/1IzXGKkJnZMg==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -8960,6 +8739,12 @@ "yargs": "^12.0.0" }, "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "cosmiconfig": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz", @@ -8990,6 +8775,12 @@ "locate-path": "^3.0.0" } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "import-fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", @@ -9008,16 +8799,6 @@ } } }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -9067,13 +8848,49 @@ "find-up": "^3.0.0", "read-pkg": "^3.0.0" } + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true }, "semver-regex": { @@ -9172,15 +8989,6 @@ "pkg-conf": "^2.1.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -9191,42 +8999,22 @@ "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, "sinon": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.3.1.tgz", - "integrity": "sha512-eQKMaeWovtOtYe2xThEvaHmmxf870Di+bim10c3ZPrL5bZhLGtu8cz+rOBTFz0CwBV4Q/7dYwZiqZbGVLZ+vjQ==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.3.2.tgz", + "integrity": "sha512-thErC1z64BeyGiPvF8aoSg0LEnptSaWE7YhdWWbWXgelOyThent7uKOnnEh9zBxDbKixtr5dEko+ws1sZMuFMA==", "dev": true, "requires": { "@sinonjs/commons": "^1.4.0", "@sinonjs/formatio": "^3.2.1", "@sinonjs/samsam": "^3.3.1", "diff": "^3.5.0", - "lolex": "^3.1.0", + "lolex": "^4.0.1", "nise": "^1.4.10", "supports-color": "^5.5.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "slash": { @@ -9251,15 +9039,6 @@ "use": "^3.1.0" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -9277,12 +9056,6 @@ "requires": { "is-extendable": "^0.1.0" } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true } } }, @@ -9359,9 +9132,9 @@ } }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, "source-map-resolve": { @@ -9385,6 +9158,14 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "source-map-url": { @@ -9514,23 +9295,6 @@ "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } } }, "string_decoder": { @@ -9542,12 +9306,12 @@ } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^3.0.0" } }, "strip-bom": { @@ -9584,9 +9348,9 @@ } }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -9607,23 +9371,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - } - } } } }, @@ -9748,9 +9495,9 @@ "dev": true }, "ts-node": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.0.3.tgz", - "integrity": "sha512-2qayBA4vdtVRuDo11DEFSsD/SFsBXQBRZZhbRGSIkmYmVkWjULn/GGMdG10KVqkaGndljfaTD8dKjWgcejO8YA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.1.0.tgz", + "integrity": "sha512-34jpuOrxDuf+O6iW1JpgTRDFynUZ1iEqtYruBqh35gICNjN8x+LpVcPAcwzLPi9VU6mdA3ym+x233nZmZp445A==", "dev": true, "requires": { "arg": "^4.1.0", @@ -9761,18 +9508,18 @@ } }, "tslib": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.1.tgz", - "integrity": "sha512-avfPS28HmGLLc2o4elcc2EIq2FcH++Yo5YxpBZi9Yw93BCTGFthI4HPE4Rpep6vSYQaK8e69PelM44tPj+RaQg==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, "tslint": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.15.0.tgz", - "integrity": "sha512-6bIEujKR21/3nyeoX2uBnE8s+tMXCQXhqMmaIPJpHmXJoBJPTLcI7/VHRtUwMhnLVdwLqqY3zmd8Dxqa5CVdJA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.16.0.tgz", + "integrity": "sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA==", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", + "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", "chalk": "^2.3.0", "commander": "^2.12.1", @@ -9792,16 +9539,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } } } }, @@ -9826,15 +9563,15 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.3.tgz", - "integrity": "sha512-FFgHdPt4T/duxx6Ndf7hwgMZZjZpB+U0nMNGVCYPq0rEzWKjEDobm4J6yb3CS7naZ0yURFqdw9Gwc7UOh/P9oQ==", + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz", + "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==", "dev": true }, "uglify-js": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.4.tgz", - "integrity": "sha512-GpKo28q/7Bm5BcX9vOu4S46FwisbPbAmkkqPnGIpKvKTM96I85N6XHQV+k4I6FA2wxgLhcsSyHoNhzucwCflvA==", + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.8.tgz", + "integrity": "sha512-GFSjB1nZIzoIq70qvDRtWRORHX3vFkAnyK/rDExc0BN7r9+/S+Voz3t/fwJuVfjppAMz+ceR2poE7tkhvnVwQQ==", "dev": true, "optional": true, "requires": { @@ -9848,6 +9585,13 @@ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "dev": true, "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true } } }, @@ -10041,6 +9785,12 @@ "strip-ansi": "^3.0.1" }, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -10060,6 +9810,15 @@ "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -10080,25 +9839,30 @@ "dev": true }, "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", "dev": true, "requires": { "cliui": "^4.0.0", - "decamelize": "^1.2.0", "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -10148,10 +9912,30 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -10178,6 +9962,99 @@ "flat": "^4.1.0", "lodash": "^4.17.11", "yargs": "^12.0.5" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } }, "yn": { diff --git a/package.json b/package.json index c802f52..3699680 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "build": "rimraf dist && tsc", - "postbuild": "cpx package.json dist/ && cpx package-lock.json dist/", + "postbuild": "cpx package.json dist/ && cpx package-lock.json dist/ && cpx README.md dist/", "commit": "git-cz", "test": "mocha -r chai -r chai-as-promised -r ts-node/register src/**/*.spec.ts", "release": "semantic-release" @@ -32,7 +32,7 @@ "@commitlint/config-conventional": "^7.5.0", "@types/chai": "^4.1.3", "@types/chai-as-promised": "^7.1.0", - "@types/dockerode": "^2.5.4", + "@types/dockerode": "^2.5.13", "@types/mocha": "^5.2.0", "@types/node": "^11.13.4", "@types/sinon": "^7.0.11", diff --git a/src/models/PluginSettings.ts b/src/models/PluginSettings.ts new file mode 100644 index 0000000..fb34060 --- /dev/null +++ b/src/models/PluginSettings.ts @@ -0,0 +1,6 @@ +import { DockerPluginConfig } from './dockerPluginConfig'; + +export interface PluginSettings { + path: '@iteratec/semantic-release-docker'; + defaultValues: DockerPluginConfig; +} diff --git a/src/models/authentication.ts b/src/models/authentication.ts new file mode 100644 index 0000000..f724072 --- /dev/null +++ b/src/models/authentication.ts @@ -0,0 +1,9 @@ +import { Credentials } from './credentials'; + +/** + * Authentication + * From: https://docs.docker.com/engine/api/v1.37/#section/Authentication + */ +export interface Authentication extends Credentials { + serveraddress: string; +} diff --git a/src/models/credentials.ts b/src/models/credentials.ts new file mode 100644 index 0000000..279aabe --- /dev/null +++ b/src/models/credentials.ts @@ -0,0 +1,4 @@ +export interface Credentials { + username: string; + password: string; +} diff --git a/src/models/dockerPluginConfig.ts b/src/models/dockerPluginConfig.ts new file mode 100644 index 0000000..a590ec6 --- /dev/null +++ b/src/models/dockerPluginConfig.ts @@ -0,0 +1,8 @@ +import { SemanticReleasePlugin } from "semantic-release"; +export interface DockerPluginConfig extends SemanticReleasePlugin { + additionalTags?: string[]; + imageName: string; + registryUrl?: string; + repositoryName?: string; + pushVersionTag?: boolean; +} diff --git a/src/models/index.ts b/src/models/index.ts new file mode 100644 index 0000000..2b9fb18 --- /dev/null +++ b/src/models/index.ts @@ -0,0 +1,3 @@ +export { Authentication } from './authentication'; +export { DockerPluginConfig } from './dockerPluginConfig'; +export { Credentials } from './credentials'; diff --git a/src/plugin-settings.ts b/src/plugin-settings.ts new file mode 100644 index 0000000..8a1080f --- /dev/null +++ b/src/plugin-settings.ts @@ -0,0 +1,13 @@ +import { PluginSettings } from "./models/PluginSettings"; + +export const pluginSettings: PluginSettings = { + path: "@iteratec/semantic-release-docker", + defaultValues: { + additionalTags: [], + imageName: "", + path: "@iteratec/semantic-release-docker", + pushVersionTag: true, + registryUrl: "", + repositoryName: "" + } +}; diff --git a/src/prepare/index.spec.ts b/src/prepare/index.spec.ts index 1f69e13..e353c93 100644 --- a/src/prepare/index.spec.ts +++ b/src/prepare/index.spec.ts @@ -1,16 +1,13 @@ import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; import Dockerode from 'dockerode'; -import { - SemanticReleaseConfig, - SemanticReleaseContext, -} from 'semantic-release'; -import { createStubInstance } from 'sinon'; -import { DockerPluginConfig } from '../dockerPluginConfig'; -import { initDocker, prepare } from './index'; - -// declare var docker: any; +import { SemanticReleaseConfig, SemanticReleaseContext } from 'semantic-release'; +import { DockerPluginConfig } from '../models'; +import { prepare } from './index'; +import { setVerified } from '../verifyConditions'; +import { buildImage } from '../test/test-helpers'; +import { afterEach } from 'mocha'; describe('@iteratec/semantic-release-docker', function() { describe('prepare', function() { @@ -18,116 +15,283 @@ describe('@iteratec/semantic-release-docker', function() { branch: '', noCi: true, repositoryUrl: '', - tagFormat: '', - }; - const context: SemanticReleaseContext = { - // tslint:disable-next-line:no-empty - logger: { log: (message: string) => {} }, - nextRelease: { - gitTag: '', - notes: '', - version: 'next', - }, - options: { - branch: '', - noCi: true, - prepare: [ - { - imageName: '', - path: '@iteratec/semantic-release-docker', - } as DockerPluginConfig, - ], - repositoryUrl: '', - tagFormat: '', - }, + tagFormat: '' }; - before(function() { + const testImage1 = 'test1'; + const testImage2 = 'test2'; + + const docker = new Dockerode(); + + before(async function() { use(chaiAsPromised); + setVerified(); + + process.env.DOCKER_REGISTRY_USER = 'username'; + process.env.DOCKER_REGISTRY_PASSWORD = 'password'; }); - before(function() { - this.timeout(10000); - // const docker = new Dockerode(); - // return await docker.pull('hello-world', {}); - const rs = {} as NodeJS.ReadableStream; - const iii = {} as Dockerode.ImageInspectInfo; - const stub = createStubInstance(Dockerode); - stub.getImage.returns({ - get( - callback?: (error?: any, result?: NodeJS.ReadableStream) => void, - ): any { - if (callback) { - return; - } - return new Promise(() => rs); - }, - history(callback?: (error?: any, result?: any) => void): any { - if (callback) { - return; - } - return new Promise(() => ''); + beforeEach(async function() { + this.timeout(20000); + await buildImage(testImage1); + await buildImage(testImage2); + }); + + it('should tag image with next version', async function() { + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' }, - inspect( - callback?: (error?: any, result?: Dockerode.ImageInspectInfo) => void, - ): any { - if (callback) { - return; - } - return new Promise(() => iii); + options: { + branch: '', + noCi: true, + prepare: [ + { + imageName: testImage1, + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + let prepareResult = await prepare(config, context); + + expect(prepareResult).to.deep.equal([[testImage1]]); + + let imagelist2 = await docker.listImages({ filters: { reference: [`${testImage1}:next`] } }); + expect(imagelist2.length).to.equal(1); + }); + + it('should tag image with next version and repositoryName', async function() { + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' }, - modem: '', - push( - options?: {}, - callback?: (error?: any, result?: NodeJS.ReadableStream) => void, - ): any { - if (callback) { - return; - } - return new Promise(() => rs); + options: { + branch: '', + noCi: true, + prepare: [ + { + imageName: testImage1, + repositoryName: 'repository', + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + let prepareResult = await prepare(config, context); + + expect(prepareResult).to.deep.equal([[testImage1]]); + + let imagelist2 = await docker.listImages({ filters: { reference: [`repository/${testImage1}:next`] } }); + expect(imagelist2.length).to.equal(1); + }); + + it('should tag image with next version and repositoryName and url', async function() { + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' }, - remove( - options?: {}, - callback?: (error?: any, result?: Dockerode.ImageRemoveInfo) => void, - ): any { - if (callback) { - return; - } - return new Promise(() => ''); + options: { + branch: '', + noCi: true, + prepare: [ + { + imageName: testImage1, + repositoryName: 'repository', + registryUrl: 'repositoryurl', + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + let prepareResult = await prepare(config, context); + + expect(prepareResult).to.deep.equal([[testImage1]]); + + let imagelist2 = await docker.listImages({ + filters: { reference: [`repositoryurl/repository/${testImage1}:next`] } + }); + expect(imagelist2.length).to.equal(1); + }); + + it('should tag image without next tag', async function() { + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' }, - tag(options?: {}, callback?: () => void): any { - if (callback) { - return; - } - return new Promise((resolve) => { - resolve({ name: stub.getImage.args[0] }); - }); + options: { + branch: '', + noCi: true, + prepare: [ + { + pushVersionTag: false, + imageName: testImage1, + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + let prepareResult = await prepare(config, context); + + expect(prepareResult).to.deep.equal([[]]); + + let imagelist2 = await docker.listImages({ filters: { reference: [`${testImage1}:next`] } }); + expect(imagelist2.length).to.equal(0); + }); + + it('should add multiple tags to an image (with next version)', async function() { + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' }, - }); - initDocker(stub); + options: { + branch: '', + noCi: true, + prepare: [ + { + imageName: testImage1, + path: '@iteratec/semantic-release-docker', + additionalTags: ['tag1', 'tag2'] + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + + let prepareResult = await prepare(config, context).then(data => data[0]); + + expect(prepareResult).to.have.length(3); + + let imagelist = await docker.listImages({ filters: { reference: [`${testImage1}:next`] } }); + expect(imagelist.length).to.equal(1); + + let imagelist1 = await docker.listImages({ filters: { reference: [`${testImage1}:tag1`] } }); + expect(imagelist1.length).to.equal(1); + + let imagelist2 = await docker.listImages({ filters: { reference: [`${testImage1}:tag2`] } }); + expect(imagelist2.length).to.equal(1); }); - it('should throw if no imagename is provided', function() { - return expect(prepare(config, context)).to.be.rejectedWith( - '\'imageName\' is not set in plugin configuration', - ); + it('should add multiple tags to an image (without next version)', async function() { + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' + }, + options: { + branch: '', + noCi: true, + prepare: [ + { + pushVersionTag: false, + imageName: testImage1, + path: '@iteratec/semantic-release-docker', + additionalTags: ['tag1', 'tag2'] + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + + let prepareResult = await prepare(config, context).then(data => data[0]); + + expect(prepareResult).to.have.length(2); + + let imagelist1 = await docker.listImages({ filters: { reference: [`${testImage1}:tag1`] } }); + expect(imagelist1.length).to.equal(1); + + let imagelist2 = await docker.listImages({ filters: { reference: [`${testImage1}:tag2`] } }); + expect(imagelist2.length).to.equal(1); }); - it('should tag an image', function() { - const expected = 'hello-world'; - (context.options.prepare![0] as DockerPluginConfig).imageName = expected; - const actual = prepare(config, context); - return expect(actual).to.eventually.not.be.rejected; + it('should add multiple images', async function() { + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' + }, + options: { + branch: '', + noCi: true, + prepare: [ + { + imageName: testImage1, + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig, + { + imageName: testImage2, + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + let prepareResult = await prepare(config, context); + + expect(prepareResult).to.have.length(2); + + let imagelist1 = await docker.listImages({ filters: { reference: [`${testImage1}:next`] } }); + expect(imagelist1.length).to.equal(1); + + let imagelist2 = await docker.listImages({ filters: { reference: [`${testImage2}:next`] } }); + expect(imagelist2.length).to.equal(1); }); - it('should add multiple tags to an image', function() { - (context.options.prepare![0] as DockerPluginConfig).imageName = - 'hello-world'; - (context.options.prepare![0] as DockerPluginConfig).additionalTags = [ - 'tag1', - 'tag2', - ]; - return expect(prepare(config, context)).to.eventually.have.length(3); + afterEach(async function() { + this.timeout(5000); + const imagelist1 = await docker.listImages({ filters: { reference: [testImage1] } }); + await Promise.all( + imagelist1.map(image => { + return docker.getImage(image.Id).remove({ + force: true + }); + }) + ); + const imagelist2 = await docker.listImages({ filters: { reference: [testImage2] } }); + await Promise.all( + imagelist2.map(image => { + return docker.getImage(image.Id).remove({ + force: true + }); + }) + ); }); }); }); diff --git a/src/prepare/index.ts b/src/prepare/index.ts index 45d340b..09747a2 100644 --- a/src/prepare/index.ts +++ b/src/prepare/index.ts @@ -1,65 +1,56 @@ import Dockerode from 'dockerode'; -import { - SemanticReleaseConfig, - SemanticReleaseContext, -} from 'semantic-release'; -import { DockerPluginConfig } from '../dockerPluginConfig'; +import { SemanticReleaseConfig, SemanticReleaseContext } from 'semantic-release'; +import { DockerPluginConfig } from '../models'; +import { pluginSettings } from '../plugin-settings'; +import { constructImageName, getImageTagsFromConfig } from '../shared-logic'; +import { verified, verifyConditions } from '../verifyConditions'; export var prepared = false; -let docker: Dockerode; - -export function initDocker(stub?: any) { - if (!docker) { - if (stub) { - docker = stub; - } else { - docker = new Dockerode(); - } +export async function prepare(pluginConfig: SemanticReleaseConfig, context: SemanticReleaseContext): Promise { + if (!verified) { + await verifyConditions(pluginConfig, context).then( + () => {}, + reject => { + return Promise.reject(reject); + } + ); } -} -export async function prepare( - pluginConfig: SemanticReleaseConfig, - context: SemanticReleaseContext, -): Promise { - const preparePlugin = context.options.prepare!.find( - (p) => p.path === '@iteratec/semantic-release-docker', - ) as DockerPluginConfig; - if (!preparePlugin.imageName) { - throw new Error('\'imageName\' is not set in plugin configuration'); - } - initDocker(); - const image = docker.getImage(preparePlugin.imageName); - let tags = [context.nextRelease!.version!]; - if (preparePlugin.additionalTags && preparePlugin.additionalTags.length > 0) { - tags = tags.concat(preparePlugin.additionalTags); - } + const preparePlugins = context.options.prepare!.filter(p => p.path === pluginSettings.path) as DockerPluginConfig[]; + return Promise.all( - tags.map((imagetag) => { - return image.tag({ - repo: - `${ - preparePlugin.registryUrl ? `${preparePlugin.registryUrl}/` : '' - }` + - `${ - preparePlugin.repositoryName - ? `${preparePlugin.repositoryName}/` - : '' - }` + - `${preparePlugin.imageName}`, - tag: imagetag, - }); - }), + preparePlugins.map(preparePlugin => { + const docker = new Dockerode(); + const image = docker.getImage(preparePlugin.imageName); + const tags = getImageTagsFromConfig(preparePlugin, context); + return Promise.all( + tags.map(imagetag => { + return image.tag({ + repo: constructImageName(preparePlugin), + tag: imagetag + }); + }) + ) + .then(data => { + if (!prepared) { + prepared = true; + } + return data.map(result => result.name); + }) + .catch(error => { + throw new Error(error); + }); + }) ) - .then((data) => { + .then(data => { if (!prepared) { prepared = true; } - return data.map((result) => result.name); + return data.map(result => result); }) - .catch((error) => { + .catch(error => { throw new Error(error); }); } diff --git a/src/publish/index.ts b/src/publish/index.ts index 40c5384..103fd69 100644 --- a/src/publish/index.ts +++ b/src/publish/index.ts @@ -1,11 +1,12 @@ import Dockerode from 'dockerode'; import { SemanticReleaseConfig, SemanticReleaseContext } from 'semantic-release'; -import { DockerPluginConfig } from '../dockerPluginConfig'; -import { Auth } from '../model/auth'; +import { Authentication, DockerPluginConfig } from '../models'; +import { pluginSettings } from '../plugin-settings'; import { prepare, prepared } from '../prepare'; +import { constructImageName, getRegistryUrlFromConfig, getImageTagsFromConfig } from '../shared-logic'; -interface PushOptions extends Auth { +interface PushOptions extends Authentication { tag: string; } @@ -15,42 +16,64 @@ export interface PublishedRelease { export async function publish(pluginConfig: SemanticReleaseConfig, context: SemanticReleaseContext) { if (!prepared) { - prepare(pluginConfig, context); + await prepare(pluginConfig, context).then( + () => {}, + reject => { + return Promise.reject(reject); + } + ); } + const docker = new Dockerode(); - let tags = [context.nextRelease!.version!]; - const preparePlugin = context.options.prepare! - .find((p) => p.path === '@iteratec/semantic-release-docker')! as DockerPluginConfig; - if (preparePlugin.additionalTags && preparePlugin.additionalTags.length > 0) { - tags = tags.concat(preparePlugin.additionalTags); - } - const imageName = `${preparePlugin.registryUrl ? `${preparePlugin.registryUrl}/` : ''}` + - `${preparePlugin.repositoryName ? `${preparePlugin.repositoryName}/` : ''}` + - `${preparePlugin.imageName}`; - const image = docker.getImage(imageName); - const options: PushOptions = { - password: process.env.DOCKER_REGISTRY_PASSWORD!, - serveraddress: process.env.DOCKER_REGISTRY_URL ? - process.env.DOCKER_REGISTRY_URL : preparePlugin.registryUrl ? preparePlugin.registryUrl : '', - tag: '', - username: process.env.DOCKER_REGISTRY_USER!, - }; - return Promise.all(tags.map((imageTag: string) => { - options.tag = imageTag; - context.logger.log(`pushing image ${imageName}:${imageTag}`); - return image.push(options); - })) - .then((streams) => Promise.all(streams.map((stream) => new Promise((resolve, reject) => { - stream.on('data', (chunk) => context.logger.log(chunk.toString())); - stream.on('end', () => resolve()); - stream.on('error', (error) => reject(error)); - })))) - .then(() => { - return { - completeImageName: tags.map((tag: string) => `${imageName}:${tag}`), - } as PublishedRelease; + + const preparePlugins = context.options.prepare!.filter(p => p.path === pluginSettings.path) as DockerPluginConfig[]; + + return Promise.all( + preparePlugins.map(preparePlugin => { + const tags = getImageTagsFromConfig(preparePlugin, context); + + const imageName = constructImageName(preparePlugin); + + const image = docker.getImage(imageName); + const options: PushOptions = { + password: process.env.DOCKER_REGISTRY_PASSWORD!, + serveraddress: getRegistryUrlFromConfig(preparePlugin), + tag: '', + username: process.env.DOCKER_REGISTRY_USER! + }; + return Promise.all( + tags.map((imageTag: string) => { + options.tag = imageTag; + context.logger.log(`pushing image ${imageName}:${imageTag}`); + return image.push(options); + }) + ) + .then(streams => + Promise.all( + streams.map( + stream => + new Promise((resolve, reject) => { + stream.on('data', chunk => context.logger.log(chunk.toString())); + stream.on('end', () => resolve()); + stream.on('error', error => { + reject(error); + }); + }) + ) + ) + ) + .then(() => { + return { + completeImageName: tags.map((tag: string) => `${imageName}:${tag}`) + } as PublishedRelease; + }) + .catch(error => { + throw new Error(error); + }); }) - .catch((error) => { - throw new Error(error); - }); + ).then(publishedImages => { + return { + publishedImages: publishedImages + }; + }); } diff --git a/src/shared-logic.spec.ts b/src/shared-logic.spec.ts new file mode 100644 index 0000000..e1bbd32 --- /dev/null +++ b/src/shared-logic.spec.ts @@ -0,0 +1,73 @@ +import { expect } from 'chai'; +import { Credentials, DockerPluginConfig } from './models'; +import { constructImageName, getCredentials, getRegistryUrlFromConfig } from './shared-logic'; + +describe('@iteratec/semantic-release-docker', function() { + describe('shared-logic', function() { + afterEach(function() { + process.env.DOCKER_REGISTRY_USER = ''; + process.env.DOCKER_REGISTRY_PASSWORD = ''; + process.env.DOCKER_REGISTRY_URL = ''; + }); + + it('should use only image name', function() { + const config: DockerPluginConfig = { + path: '@iteratec/semantic-release-docker', + imageName: 'test' + }; + expect(constructImageName(config)).to.be.equal('test'); + }); + + it('should use image name and repository', function() { + const config: DockerPluginConfig = { + path: '@iteratec/semantic-release-docker', + imageName: 'test', + repositoryName: 'repo' + }; + expect(constructImageName(config)).to.be.equal('repo/test'); + }); + + it('should use image name, repository and registry', function() { + const config: DockerPluginConfig = { + path: '@iteratec/semantic-release-docker', + imageName: 'test', + repositoryName: 'repo', + registryUrl: 'registry' + }; + expect(constructImageName(config)).to.be.equal('registry/repo/test'); + }); + + it('should use the registry from the config', function() { + const config: DockerPluginConfig = { + path: '@iteratec/semantic-release-docker', + imageName: 'test', + registryUrl: 'registry' + }; + expect(getRegistryUrlFromConfig(config)).to.be.equal('registry'); + }); + + it('should prefer the registry from the environment variable over the one from the config', function() { + process.env.DOCKER_REGISTRY_URL = 'my_other_private_registry'; + const config: DockerPluginConfig = { + path: '@iteratec/semantic-release-docker', + imageName: 'test', + registryUrl: 'registry' + }; + expect(getRegistryUrlFromConfig(config)).to.be.equal('my_other_private_registry'); + }); + + it('should default to empty string if no registry is specified', function() { + const config: DockerPluginConfig = { + path: '@iteratec/semantic-release-docker', + imageName: 'test' + }; + expect(getRegistryUrlFromConfig(config)).to.be.equal(''); + }); + + it('should get Credentials', function() { + process.env.DOCKER_REGISTRY_USER = 'username'; + process.env.DOCKER_REGISTRY_PASSWORD = 'password'; + expect(getCredentials()).to.eql({ password: 'password', username: 'username' } as Credentials); + }); + }); +}); diff --git a/src/shared-logic.ts b/src/shared-logic.ts new file mode 100644 index 0000000..617ac8e --- /dev/null +++ b/src/shared-logic.ts @@ -0,0 +1,50 @@ +import { Credentials, DockerPluginConfig } from './models'; +import { SemanticReleaseContext } from 'semantic-release'; + +export function constructImageName(config: DockerPluginConfig): string { + return ( + `${config.registryUrl ? `${config.registryUrl}/` : ''}` + + `${config.repositoryName ? `${config.repositoryName}/` : ''}` + + `${config.imageName}` + ); +} + +export function getRegistryUrlFromConfig(config: DockerPluginConfig): string { + return process.env.DOCKER_REGISTRY_URL + ? process.env.DOCKER_REGISTRY_URL + : config.registryUrl + ? config.registryUrl + : ''; +} + +export function getImageTagsFromConfig(config: DockerPluginConfig, context: SemanticReleaseContext): string[] { + let tags = []; + if (config.pushVersionTag == null || config.pushVersionTag === true) { + tags.push(context.nextRelease!.version!); + } + if (config.additionalTags && config.additionalTags.length > 0) { + tags = tags.concat(config.additionalTags); + } + return tags; +} + +/** + * Get Authentication object from Environment Variables + * Throws Error if Variables are not set. + */ +export function getCredentials(): Credentials { + // Check DOCKER_REGISTRY_USER Environment Variable + if (!process.env.DOCKER_REGISTRY_USER) { + throw new Error('Environment variable DOCKER_REGISTRY_USER must be set in order to login to the registry.'); + } + + // Check DOCKER_REGISTRY_PASSWORD Environment Variable + if (!process.env.DOCKER_REGISTRY_PASSWORD) { + throw new Error('Environment variable DOCKER_REGISTRY_PASSWORD must be set in order to login to the registry.'); + } + + return { + username: process.env.DOCKER_REGISTRY_USER, + password: process.env.DOCKER_REGISTRY_PASSWORD + }; +} diff --git a/src/test/test-helpers.ts b/src/test/test-helpers.ts new file mode 100644 index 0000000..bef986b --- /dev/null +++ b/src/test/test-helpers.ts @@ -0,0 +1,27 @@ +import Dockerode from 'dockerode'; + +export function buildImage(imageName: string): Promise { + const docker = new Dockerode(); + return new Promise((resolve, reject) => { + docker.buildImage( + { + context: './', + src: ['Dockerfile'] + }, + { + t: imageName + }, + function(error, stream) { + if (error) { + reject(error); + } + if (stream) { + stream.resume(); + stream.on('end', function() { + resolve(); + }); + } + } + ); + }); +} diff --git a/src/verifyConditions/index.spec.ts b/src/verifyConditions/index.spec.ts index dd98959..8fb17a1 100644 --- a/src/verifyConditions/index.spec.ts +++ b/src/verifyConditions/index.spec.ts @@ -1,88 +1,172 @@ import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; +import Docker from 'dockerode'; import { SemanticReleaseConfig, SemanticReleaseContext } from 'semantic-release'; -import { DockerPluginConfig } from '../dockerPluginConfig'; +import { DockerPluginConfig } from '../models'; import { verifyConditions } from './index'; +import { buildImage } from '../test/test-helpers'; describe('@iteratec/semantic-release-docker', function() { - describe('verifyConditions', function() { + const imageName = 'abcdefghijklmnopqrstuvwxyz'; const config: SemanticReleaseConfig = { branch: '', noCi: true, repositoryUrl: '', - tagFormat: '', + tagFormat: '' }; const context: SemanticReleaseContext = { logger: { // tslint:disable-next-line:no-empty - log: (message: string) => {}, + log: (message: string) => {} }, options: { branch: '', noCi: true, prepare: [ { - imageName: '', - path: '@iteratec/semantic-release-docker', - } as DockerPluginConfig, + imageName, + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig ], repositoryUrl: '', - tagFormat: '', - }, + tagFormat: '' + } }; before(function() { use(chaiAsPromised); }); - afterEach(function() { - process.env.DOCKER_REGISTRY_USER = ''; - process.env.DOCKER_REGISTRY_PASSWORD = ''; - process.env.DOCKER_REGISTRY_URL = ''; - }); - it('should throw when the username is not set', function() { - return expect(verifyConditions(config, context)).to.eventually.be - .rejectedWith('Environment variable DOCKER_REGISTRY_USER must be set in order to login to the registry.'); + delete process.env.DOCKER_REGISTRY_USER; + return expect(verifyConditions(config, context)).to.eventually.be.rejectedWith( + 'Environment variable DOCKER_REGISTRY_USER must be set in order to login to the registry.' + ); }); - it('should throw when the password is not set', function() { + it('should NOT throw when the username is set', function() { process.env.DOCKER_REGISTRY_USER = 'username'; - return expect(verifyConditions(config, context)).to.eventually.be - .rejectedWith('Environment variable DOCKER_REGISTRY_PASSWORD must be set in order to login to the registry.'); + return expect(verifyConditions(config, context)).to.not.eventually.be.rejectedWith( + 'Environment variable DOCKER_REGISTRY_USER must be set in order to login to the registry.' + ); }); - it('should use the registry from the config', function() { - this.timeout(5000); + it('should throw when the password is not set', function() { process.env.DOCKER_REGISTRY_USER = 'username'; - process.env.DOCKER_REGISTRY_PASSWORD = 'password'; - (context.options.prepare![0] as DockerPluginConfig).registryUrl = 'my_private_registry'; - return expect(verifyConditions(config, context)) - .to.eventually.be.rejectedWith(/(?:my_private_registry)/); + delete process.env.DOCKER_REGISTRY_PASSWORD; + return expect(verifyConditions(config, context)).to.eventually.be.rejectedWith( + 'Environment variable DOCKER_REGISTRY_PASSWORD must be set in order to login to the registry.' + ); }); - it('should prefer the registry from the environment variable over the one from the config', function() { - this.timeout(5000); - process.env.DOCKER_REGISTRY_USER = 'username'; + it('should NOT throw when the password is set', function() { process.env.DOCKER_REGISTRY_PASSWORD = 'password'; - process.env.DOCKER_REGISTRY_URL = 'my_other_private_registry'; - (context.options.prepare![0] as DockerPluginConfig).registryUrl = 'my_private_registry'; - return expect(verifyConditions(config, context)) - .to.eventually.be.rejectedWith(/(?:my_other_private_registry)/); + return expect(verifyConditions(config, context)).to.not.eventually.be.rejectedWith( + 'Environment variable DOCKER_REGISTRY_PASSWORD must be set in order to login to the registry.' + ); }); - it('should default to docker hub if no registry is specified', function() { + it('should default to docker hub if no registry is specified', async function() { this.timeout(10000); + await buildImage(imageName); (context.options.prepare![0] as DockerPluginConfig).registryUrl = ''; - (context.options.prepare![0] as DockerPluginConfig).imageName = ''; process.env.DOCKER_REGISTRY_USER = 'badusername'; process.env.DOCKER_REGISTRY_PASSWORD = 'pass@w0rd'; - return expect(verifyConditions(config, context)).to.eventually.be - .rejectedWith(/(?:index.docker.com|registry-1.docker.io)/); + return expect(verifyConditions(config, context)).to.eventually.be.rejectedWith( + /(?:index.docker.com|registry-1.docker.io)/ + ); }); - }); + it('should throw if no imagename is provided', function() { + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' + }, + options: { + branch: '', + noCi: true, + prepare: [ + { + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + return expect(verifyConditions(config, context)).to.eventually.be.rejectedWith( + "'imageName' is not set in plugin configuration" + ); + }); + it('should throw if image with imagename does not exist', async function() { + const docker = new Docker(); + await docker.getImage(imageName).remove(); + + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' + }, + options: { + branch: '', + noCi: true, + prepare: [ + { + imageName, + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + return expect(verifyConditions(config, context)).to.eventually.be.rejectedWith( + `Image with name '${imageName}' does not exist on this machine.` + ); + }); + + it('should NOT throw if image with imagename does exist', async function() { + await buildImage(imageName); + const context = { + // tslint:disable-next-line:no-empty + logger: { log: (message: string) => {} }, + nextRelease: { + gitTag: '', + notes: '', + version: 'next' + }, + options: { + branch: '', + noCi: true, + prepare: [ + { + imageName, + path: '@iteratec/semantic-release-docker' + } as DockerPluginConfig + ], + repositoryUrl: '', + tagFormat: '' + } + } as SemanticReleaseContext; + + return expect(verifyConditions(config, context)).to.not.eventually.be.rejectedWith( + `Image with name '${imageName}' does not exist on this machine.` + ); + }); + + after(async function() { + const docker = new Docker(); + await docker.getImage(imageName).remove(); + }); + }); }); diff --git a/src/verifyConditions/index.ts b/src/verifyConditions/index.ts index 8ec40bf..2c30fb2 100644 --- a/src/verifyConditions/index.ts +++ b/src/verifyConditions/index.ts @@ -1,34 +1,74 @@ +import Docker from 'dockerode'; import { SemanticReleaseConfig, SemanticReleaseContext } from 'semantic-release'; -import { DockerPluginConfig } from '../dockerPluginConfig'; -import { Registry } from '../model/registry'; +import { Credentials, DockerPluginConfig } from '../models'; +import { Authentication } from '../models'; +import { pluginSettings } from '../plugin-settings'; +import { getCredentials, getRegistryUrlFromConfig } from '../shared-logic'; export var verified = false; +/** + * Just for test purposes. + * @param val + */ +export function setVerified() { + verified = true; +} -export async function verifyConditions(pluginConfig: SemanticReleaseConfig, context: SemanticReleaseContext) { - if (!process.env.DOCKER_REGISTRY_USER) { - throw new Error('Environment variable DOCKER_REGISTRY_USER must be set in order to login to the registry.'); - } - if (!process.env.DOCKER_REGISTRY_PASSWORD) { - throw new Error('Environment variable DOCKER_REGISTRY_PASSWORD must be set in order to login to the registry.'); +/** + * First Step + * Verify all conditions in order to proceed with the release + */ +export async function verifyConditions( + pluginConfig: SemanticReleaseConfig, + context: SemanticReleaseContext +): Promise { + let cred: Credentials; + + // Check if Username and Password are set if not reject Promise with Error Message + try { + cred = getCredentials(); + } catch (err) { + return Promise.reject(err.message); } - let preparePlugin: DockerPluginConfig; - if (!context.options.prepare || - !context.options.prepare!.find((p) => p.path === '@iteratec/semantic-release-docker')) { - throw new Error('\'prepare\' is not configured'); + + // Check if plugin is configured in prepare step + if (!context.options.prepare || !context.options.prepare!.find(p => p.path === pluginSettings.path)) { + throw new Error("'prepare' is not configured"); } - preparePlugin = context.options.prepare - .find((p) => p.path === '@iteratec/semantic-release-docker') as DockerPluginConfig; - let registryUrl: string; - if (process.env.DOCKER_REGISTRY_URL || preparePlugin.registryUrl) { - registryUrl = process.env.DOCKER_REGISTRY_URL ? process.env.DOCKER_REGISTRY_URL : preparePlugin.registryUrl!; - } else { - registryUrl = ''; + + const preparePlugins = context.options.prepare!.filter(p => p.path === pluginSettings.path) as DockerPluginConfig[]; + + for (let i = 0; i < preparePlugins.length; i++) { + const preparePlugin = preparePlugins[i]; + + // Check if imagename is set + if (preparePlugin.imageName == null || preparePlugin.imageName.length === 0) { + throw new Error("'imageName' is not set in plugin configuration"); + } + + const docker = new Docker(); + + // Check if image exists on machine + const imagelist = await docker.listImages({ filters: { reference: [preparePlugin.imageName] } }); + if (imagelist.length === 0) { + throw new Error(`Image with name '${preparePlugin.imageName}' does not exist on this machine.`); + } + + // Check Authentication + const auth: Authentication = { + ...cred, + serveraddress: getRegistryUrlFromConfig(preparePlugin) + }; + + return docker + .checkAuth(auth) + .then(data => { + if (!verified) { + verified = true; + } + }) + .catch(error => { + throw new Error(error); + }); } - const registry = new Registry(registryUrl); - return registry.login(process.env.DOCKER_REGISTRY_USER, process.env.DOCKER_REGISTRY_PASSWORD) - .then((result) => { - if (!verified) { - verified = true; - } - }); }