diff --git a/package.json b/package.json index 9ce2728d..27a2aec6 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@types/uuid": "^3.4.5", "@typescript-eslint/eslint-plugin": "^2.24.0", "@typescript-eslint/parser": "^2.24.0", + "clui": "^0.3.6", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.1", "husky": ">=1", @@ -77,6 +78,7 @@ "@azure/identity": "^1.0.0", "@azure/keyvault-secrets": "^4.0.0", "@azure/ms-rest-nodeauth": "^3.0.0", + "@types/clui": "^0.3.0", "@types/fs-extra": "^8.0.0", "@types/git-url-parse": "^9.0.0", "@types/mkdirp": "^0.5.2", diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 3d6be1f8..ea3f44d7 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -3,6 +3,8 @@ import { ICoreApi } from "azure-devops-node-api/CoreApi"; import { IGitApi } from "azure-devops-node-api/GitApi"; import commander from "commander"; import fs from "fs"; +import cli from "clui"; +import chalk from "chalk"; import yaml from "js-yaml"; import { defaultConfigFile } from "../config"; import { getBuildApi, getWebApi } from "../lib/azdoClient"; @@ -46,7 +48,8 @@ import { build as buildError, log as logError } from "../lib/errorBuilder"; import { errorStatusCode } from "../lib/errorStatusCode"; import { exec } from "../lib/shell"; import { ConfigYaml } from "../types"; - +import { turnOnConsoleLogging, turnOffConsoleLogging } from "../lib/util"; +const Spinner = cli.Spinner; interface CommandOptions { file: string | undefined; } @@ -62,6 +65,36 @@ interface APIClients { buildAPI: IBuildApi; } +const installPromiseHelper = ( + delegate: Promise, + onSuccess: { (): void; (): void }, + spinner: cli.Spinner +): Promise => { + return new Promise((resolve) => { + spinner.start(); + delegate.then(() => { + spinner.stop(); + onSuccess(); + resolve(); + }); + }); +}; + +const logStatusSpinner = async ( + pendingMessage: string, + completionMessage: string, + delegate: Promise +): Promise => { + const spinner = new Spinner(pendingMessage); + return Promise.resolve( + installPromiseHelper( + delegate, + () => console.log(chalk.green(`✅ ${completionMessage}`)), + spinner + ) + ); +}; + export const isAzCLIInstall = async (): Promise => { try { const result = await exec("az", ["--version"]); @@ -199,13 +232,41 @@ export const createAppRepoTasks = async ( rc.acrName, RESOURCE_GROUP_LOCATION ); - await setupVariableGroup(rc); - await helmRepo(gitAPI, rc); - await appRepo(gitAPI, rc); - await createLifecyclePipeline(buildAPI, rc); - await completePullRequest(gitAPI, rc, HLD_REPO); - await createBuildPipeline(buildAPI, rc); - await completePullRequest(gitAPI, rc, HLD_REPO); + await logStatusSpinner( + "Updating variable group", + "Variable group updated", + setupVariableGroup(rc) + ); + await logStatusSpinner( + "Creating Helm repo", + "Helm repo created", + helmRepo(gitAPI, rc) + ); + await logStatusSpinner( + "Creating App repo", + "App repo created", + appRepo(gitAPI, rc) + ); + await logStatusSpinner( + "Creating Lifecycle pipeline", + "Lifecycle pipeline build success", + createLifecyclePipeline(buildAPI, rc) + ); + await logStatusSpinner( + "Approving HLD pull request", + "HLD pull request completed", + completePullRequest(gitAPI, rc, HLD_REPO) + ); + await logStatusSpinner( + "Creating Build-Update pipeline", + "Build-Update pipeline build success", + createBuildPipeline(buildAPI, rc) + ); + await logStatusSpinner( + "Approving HLD pull request", + "HLD pull request completed", + completePullRequest(gitAPI, rc, HLD_REPO) + ); return true; } else { return false; @@ -277,17 +338,37 @@ export const execute = async ( const { coreAPI, gitAPI, buildAPI } = await getAPIClients(); + if (logger.level == "info") { + turnOffConsoleLogging(logger); + } await createProjectIfNotExist(coreAPI, rc); - await setupVariableGroup(rc); - await hldRepo(gitAPI, rc); - await manifestRepo(gitAPI, rc); - await createHLDtoManifestPipeline(buildAPI, rc); + await logStatusSpinner( + "Creating vaiable group", + "Variable group created", + setupVariableGroup(rc) + ); + await logStatusSpinner( + "Creating HLD repo", + "HLD repo created", + hldRepo(gitAPI, rc) + ); + await logStatusSpinner( + "Creating Manifest repo", + "Manifest repo created", + manifestRepo(gitAPI, rc) + ); + await logStatusSpinner( + "Creating HLD to Manifest pipeline", + "HLD to Manifest pipeline build success", + createHLDtoManifestPipeline(buildAPI, rc) + ); await createAppRepoTasks(gitAPI, buildAPI, rc); createCLIConfig(rc); // to write storage account information. createSetupLog(rc); await exitFn(0); } catch (err) { + turnOnConsoleLogging(logger); logError(buildError(errorStatusCode.CMD_EXE_ERR, "setup-cmd-failed", err)); const msg = getErrorMessage(requestContext, err); diff --git a/src/lib/i18n.json b/src/lib/i18n.json index 8352c0c6..039b596b 100644 --- a/src/lib/i18n.json +++ b/src/lib/i18n.json @@ -13,7 +13,7 @@ "storageTableName": "Enter storage table name", "storagePartitionKey": "Enter storage partition key", "storageAccessKey": "Enter storage access key", - "storageKeVaultName": "Enter key vault name (have the value as empty and hit enter key to skip)" + "storageKeyVaultName": "Enter key vault name (have the value as empty and hit enter key to skip)" }, "errors": { "init_cmd_failed": "Init command was not successfully executed.", diff --git a/src/lib/promptBuilder.ts b/src/lib/promptBuilder.ts index f262b22c..e5fe9860 100644 --- a/src/lib/promptBuilder.ts +++ b/src/lib/promptBuilder.ts @@ -160,7 +160,7 @@ export const azureKeyVaultName = ( ): QuestionCollection => { return { default: defaultValue, - message: `${i18n.prompt.storageKeVaultName}\n`, + message: `${i18n.prompt.storageKeyVaultName}\n`, name: "azdo_storage_key_vault_name", type: "input", validate: validator.validateStorageKeyVaultName, diff --git a/src/lib/util.ts b/src/lib/util.ts index e2e62c9b..1e477c44 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -1,3 +1,4 @@ +import { transports, Logger } from "winston"; /** * Deep clone an object. * @@ -19,3 +20,22 @@ export const sleep = (timeInMs: number): Promise => { }, timeInMs); }); }; + +export const turnOffConsoleLogging = (logger: Logger): void => { + //If logging level is not verbose + if (logger.level !== "silly") { + logger.transports.forEach((t) => { + if (t instanceof transports.Console) { + t.silent = true; + } + }); + } +}; + +export const turnOnConsoleLogging = (logger: Logger): void => { + logger.transports.forEach((t) => { + if (t instanceof transports.Console) { + t.silent = false; + } + }); +}; diff --git a/yarn.lock b/yarn.lock index 102934aa..786f98b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -777,11 +777,23 @@ dependencies: "@babel/types" "^7.3.0" +"@types/cli-color@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/cli-color/-/cli-color-2.0.0.tgz#dc64e32da0fb9ea1814300fb468a58e833ce71a6" + integrity sha512-E2Oisr73FjwxMHkYU6RcN9P9mmrbG4TNQMIebWhazYxOgWRzA7s4hM+DtAs6ZwiwKFbPst42v1XUAC1APIhRJA== + "@types/cli-table@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@types/cli-table/-/cli-table-0.3.0.tgz#f1857156bf5fd115c6a2db260ba0be1f8fc5671c" integrity sha512-QnZUISJJXyhyD6L1e5QwXDV/A5i2W1/gl6D6YMc8u0ncPepbv/B4w3S+izVvtAg60m6h+JP09+Y/0zF2mojlFQ== +"@types/clui@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@types/clui/-/clui-0.3.0.tgz#a7512770d50c06b403e018c46f850a0064e53c74" + integrity sha512-4GM6iuKwOs4Lq2qUWbxw8LMiamh6YXEuPq4uKeYd7SfFWNK1sNsw41M9GjIhwbIRBOaVgxkutZLLdfZwSNDwtg== + dependencies: + "@types/cli-color" "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -1942,6 +1954,16 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +cli-color@0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-0.3.2.tgz#75fa5f728c308cc4ac594b05e06cc5d80daccd86" + integrity sha1-dfpfcowwjMSsWUsF4GzF2A2szYY= + dependencies: + d "~0.1.1" + es5-ext "~0.10.2" + memoizee "0.3.x" + timers-ext "0.1.x" + cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -1994,6 +2016,13 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +clui@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/clui/-/clui-0.3.6.tgz#8e1e5cea7332a6e54083f59da0ccbe1d6f2fa787" + integrity sha512-Z4UbgZILlIAjkEkZiDOa2aoYjohKx7fa6DxIh6cE9A6WNWZ61iXfQc6CmdC9SKdS5nO0P0UyQ+WfoXfB65e3HQ== + dependencies: + cli-color "0.3.2" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2298,6 +2327,21 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +d@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" + integrity sha1-2hhMU10Y2O57oqoim5FACfrhEwk= + dependencies: + es5-ext "~0.10.2" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -2642,16 +2686,69 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.11, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46, es5-ext@~0.10.5, es5-ext@~0.10.6: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + es6-error@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== +es6-iterator@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-0.1.3.tgz#d6f58b8c4fc413c249b4baa19768f8e4d7c8944e" + integrity sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4= + dependencies: + d "~0.1.1" + es5-ext "~0.10.5" + es6-symbol "~2.0.1" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + es6-object-assign@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-symbol@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-2.0.1.tgz#761b5c67cfd4f1d18afb234f691d678682cb3bf3" + integrity sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M= + dependencies: + d "~0.1.1" + es5-ext "~0.10.5" + +es6-weak-map@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-0.1.4.tgz#706cef9e99aa236ba7766c239c8b9e286ea7d228" + integrity sha1-cGzvnpmqI2undmwjnIueKG6n0ig= + dependencies: + d "~0.1.1" + es5-ext "~0.10.6" + es6-iterator "~0.1.3" + es6-symbol "~2.0.1" + escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -2785,6 +2882,14 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +event-emitter@~0.3.4: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -2891,6 +2996,13 @@ expect@^25.1.0: jest-message-util "^25.1.0" jest-regex-util "^25.1.0" +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -4920,6 +5032,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-queue@0.1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -5023,6 +5142,19 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" +memoizee@0.3.x: + version "0.3.10" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.3.10.tgz#4eca0d8aed39ec9d017f4c5c2f2f6432f42e5c8f" + integrity sha1-TsoNiu057J0Bf0xcLy9kMvQuXI8= + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-weak-map "~0.1.4" + event-emitter "~0.3.4" + lru-queue "0.1" + next-tick "~0.2.2" + timers-ext "0.1" + memory-fs@^0.4.0, memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -5279,6 +5411,21 @@ nested-error-stacks@^2.0.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== +next-tick@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-tick@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-0.2.2.tgz#75da4a927ee5887e39065880065b7336413b310d" + integrity sha1-ddpKkn7liH45BliABltzNkE7MQ0= + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -7134,6 +7281,14 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +timers-ext@0.1, timers-ext@0.1.x: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -7321,6 +7476,16 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" + integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== + typed-rest-client@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.2.0.tgz#723085d203f38d7d147271e5ed3a75488eb44a02"