diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1f803fa..e09e545 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,6 +2,15 @@ name: Publish to NPM and GitHub on: workflow_dispatch: + inputs: + pre-release: + description: Is this a pre-release? + type: boolean + default: false + draft: + description: Is this a draft release? + type: boolean + default: false jobs: release: @@ -18,6 +27,14 @@ jobs: fetch-depth: 0 token: ${{ env.PUSH_TOKEN }} + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + - name: git config run: | git config user.name "${GITHUB_ACTOR}" @@ -40,4 +57,4 @@ jobs: run: pnpm install && pnpm build - name: Publish to NPM and GitHub release - run: pnpm release + run: pnpm release --github.preRelease=${{ inputs.pre-release }} --github.draft=${{ inputs.draft }} diff --git a/.release-it.json b/.release-it.json index 1470d10..7bc57d7 100644 --- a/.release-it.json +++ b/.release-it.json @@ -25,15 +25,23 @@ } }, "github": { - "release": true + "release": true, + "tokenRef": "PUSH_TOKEN", + "comments": { + "submit": true, + "issue": ":rocket: _This issue has been resolved in v${version}. See [${releaseName}](${releaseUrl}) for release notes._", + "pr": ":rocket: _This pull request is included in v${version}. See [${releaseName}](${releaseUrl}) for release notes._" + } }, "git": { + "commit": true, + "tag": true, + "push": true, "commitMessage": "chore: release v${version}", "requireCleanWorkingDir": true, "requireBranch": "main" }, "npm": { - "publish": true, - "tokenRef": "PUSH_TOKEN" + "publish": true } } diff --git a/package.json b/package.json index 4855cc1..9b21626 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ }, "devDependencies": { "@release-it/conventional-changelog": "^8.0.1", - "@stylistic/eslint-plugin-js": "^1.5.1", - "@stylistic/eslint-plugin-ts": "^1.5.1", + "@stylistic/eslint-plugin-js": "^1.5.3", + "@stylistic/eslint-plugin-ts": "^1.5.3", "@tsconfig/node18": "^18.2.2", "@types/node": "^20.10.6", "@types/yargs": "^17.0.32", @@ -57,6 +57,8 @@ }, "dependencies": { "@inquirer/prompts": "^3.3.0", + "chalk": "^4.1.2", + "nanospinner": "^1.1.0", "yargs": "^17.7.2" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5253471..24db53f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,12 @@ dependencies: '@inquirer/prompts': specifier: ^3.3.0 version: 3.3.0 + chalk: + specifier: ^4.1.2 + version: 4.1.2 + nanospinner: + specifier: ^1.1.0 + version: 1.1.0 yargs: specifier: ^17.7.2 version: 17.7.2 @@ -17,11 +23,11 @@ devDependencies: specifier: ^8.0.1 version: 8.0.1(release-it@17.0.1) '@stylistic/eslint-plugin-js': - specifier: ^1.5.1 - version: 1.5.1(eslint@8.56.0) + specifier: ^1.5.3 + version: 1.5.3(eslint@8.56.0) '@stylistic/eslint-plugin-ts': - specifier: ^1.5.1 - version: 1.5.1(eslint@8.56.0)(typescript@5.3.3) + specifier: ^1.5.3 + version: 1.5.3(eslint@8.56.0)(typescript@5.3.3) '@tsconfig/node18': specifier: ^18.2.2 version: 18.2.2 @@ -466,27 +472,27 @@ packages: engines: {node: '>=18'} dev: true - /@stylistic/eslint-plugin-js@1.5.1(eslint@8.56.0): - resolution: {integrity: sha512-iZF0rF+uOhAmOJYOJx1Yvmm3CZ1uz9n0SRd9dpBYHA3QAvfABUORh9LADWwZCigjHJkp2QbCZelGFJGwGz7Siw==} + /@stylistic/eslint-plugin-js@1.5.3(eslint@8.56.0): + resolution: {integrity: sha512-XlKnm82fD7Sw9kQ6FFigE0tobvptNBXZWsdfoKmUyK7bNxHsAHOFT8zJGY3j3MjZ0Fe7rLTu86hX/vOl0bRRdQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: '>=8.40.0' dependencies: - acorn: 8.11.2 + acorn: 8.11.3 escape-string-regexp: 4.0.0 eslint: 8.56.0 eslint-visitor-keys: 3.4.3 espree: 9.6.1 dev: true - /@stylistic/eslint-plugin-ts@1.5.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-oXM1V7Jp8G9+udxQTy+Igo79LR2e5HXiWqlA/3v+/PAqWxniR9nJqJSBjtQKJTPsGplDqn/ASpHUOETP4EI/4A==} + /@stylistic/eslint-plugin-ts@1.5.3(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-/gUEqGo0gpFeu220YmC0788VliKnmTaAz4pI82KA5cUuCp6OzEhGlrNkb1eevMwH0RRgyND20HJxOYvEGlwu+w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: '>=8.40.0' dependencies: - '@stylistic/eslint-plugin-js': 1.5.1(eslint@8.56.0) - '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3) + '@stylistic/eslint-plugin-js': 1.5.3(eslint@8.56.0) + '@typescript-eslint/utils': 6.17.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 transitivePeerDependencies: - supports-color @@ -603,14 +609,6 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@6.14.0: - resolution: {integrity: sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg==} - engines: {node: ^16.0.0 || >=18.0.0} - dependencies: - '@typescript-eslint/types': 6.14.0 - '@typescript-eslint/visitor-keys': 6.14.0 - dev: true - /@typescript-eslint/scope-manager@6.17.0: resolution: {integrity: sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==} engines: {node: ^16.0.0 || >=18.0.0} @@ -639,37 +637,11 @@ packages: - supports-color dev: true - /@typescript-eslint/types@6.14.0: - resolution: {integrity: sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA==} - engines: {node: ^16.0.0 || >=18.0.0} - dev: true - /@typescript-eslint/types@6.17.0: resolution: {integrity: sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.14.0(typescript@5.3.3): - resolution: {integrity: sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 6.14.0 - '@typescript-eslint/visitor-keys': 6.14.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/typescript-estree@6.17.0(typescript@5.3.3): resolution: {integrity: sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -692,25 +664,6 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.14.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.14.0 - '@typescript-eslint/types': 6.14.0 - '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) - eslint: 8.56.0 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - /@typescript-eslint/utils@6.17.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==} engines: {node: ^16.0.0 || >=18.0.0} @@ -730,14 +683,6 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys@6.14.0: - resolution: {integrity: sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw==} - engines: {node: ^16.0.0 || >=18.0.0} - dependencies: - '@typescript-eslint/types': 6.14.0 - eslint-visitor-keys: 3.4.3 - dev: true - /@typescript-eslint/visitor-keys@6.17.0: resolution: {integrity: sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -772,6 +717,12 @@ packages: hasBin: true dev: true + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /add-stream@1.0.0: resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} dev: true @@ -3098,6 +3049,12 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + /nanospinner@1.1.0: + resolution: {integrity: sha512-yFvNYMig4AthKYfHFl1sLj7B2nkHL4lzdig4osvl9/LdGbXwrdFRoqBS98gsEsOakr0yH+r5NZ/1Y9gdVB8trA==} + dependencies: + picocolors: 1.0.0 + dev: false + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -3447,7 +3404,6 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} diff --git a/src/handler/config/init/init.ts b/src/handler/config/init/init.ts index ee2b736..02c994d 100644 --- a/src/handler/config/init/init.ts +++ b/src/handler/config/init/init.ts @@ -4,7 +4,13 @@ import path from 'path'; import { createApiSetting } from './api'; import { createWebSetting } from './web'; import { createMobileSetting } from './mobile'; -import { configFileName } from '../../../utils/constants'; +import { + configFileExists, + configFileName, + configPathNotFolder, + configPathNotFound, + initMessage, +} from '../../../utils/constants'; import { getPlatform, getPlatformType } from '../../../questions/inputs'; import { FrameworkSetting } from '../../../types/configType'; import { createConfigFile } from '../../../utils/json'; @@ -16,16 +22,16 @@ export const handleConfigInit = async (argv: yargs.ArgumentsCamelCase) => { const checkConfigFile = (configPath: string) => { if (fs.existsSync(path.join(configPath, configFileName))) { - throw new Error(`Boyka config file is already available at [${configPath}]...`); + throw new Error(configFileExists(configPath)); } }; const checkConfigPath = (configPath: string) => { if (!fs.lstatSync(configPath).isDirectory()) { - throw new Error(`Config path [${configPath}] is not a folder...`); + throw new Error(configPathNotFolder(configPath)); } if (!fs.existsSync(configPath)) { - throw new Error(`Boyka config path [${configPath}] does not exists...`); + throw new Error(configPathNotFound(configPath)); } }; @@ -47,7 +53,7 @@ const createUiSetting = async () => { const createConfigJson = async (configPath: string) => { const path = configPath === '.' ? process.cwd() : configPath; - console.info(`Creating Boyka config file at ${path}...`); + console.info(initMessage(path)); validateConfigPath(path); let setting: FrameworkSetting; switch (await getPlatform()) { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 1878883..157e3d0 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,13 +1,21 @@ -import { TargetProviders } from '../types/configType'; +import chalk from 'chalk'; +import { TargetProviders } from '../types/configType.js'; -export const epiLogMessage = `For more information, - visit: https://boykaframework.github.io/boyka-framework`; +const danger = chalk.red.bold; +const warn = chalk.yellow.bold; +const success = chalk.green.bold; +const info = chalk.blueBright.bold; + +export const sleep = (ms = 2000) => new Promise((r) => setTimeout(r, ms)); + +export const epiLogMessage = info(`For more information, + visit: https://boykaframework.github.io/boyka-framework`); export const configFileName = 'boyka-config.json'; export const failureMessage = (command: string = ''): string => { const targetCommand = command.length === 0 ? '' : ` in ${command}`; - return `Something went wrong${targetCommand}! Run the command with '--help' option`; + return danger(`Something went wrong${targetCommand}! Run the command with '--help' option`); }; let targetProviders: TargetProviders; @@ -16,14 +24,35 @@ export const setTarget = (target: TargetProviders) => (targetProviders = target) export const getTarget = () => targetProviders; -export const helpMessage = ` +export const helpMessage = info(` Check out the Boyka config documentation 👉 [https://boykaframework.github.io/boyka-framework/docs/guides/configuration] 🗒️ You can update the generated config file to include more settings 🛠️ as per your requirement. -`; +`); -export const capabilitiesHelpMessage = ` +export const capabilitiesHelpMessage = warn(` ❗❗ Since you have selected Cloud platform to run your tests, you must also add cloud specific capabilities to the empty \`capabilities\` block -added to the config file.`; +added to the config file.`); + +export const successMessage = (filePath: string) => + success(`Boyka config file created at [${filePath}]`); + +export const errorMessage = (error: Error) => + danger(`Error occurred! ${error.message} +Caused by: ${error.cause} +`); + +export const savingMessage = warn('Creating the [boyka-config.json] file...'); + +export const initMessage = (path: string) => warn(`Creating Boyka config file at ${path}...`); + +export const configPathNotFound = (path: string) => + danger(`Boyka config path [${path}] does not exists...`); + +export const configPathNotFolder = (path: string) => + danger(`Config path [${path}] is not a folder...`); + +export const configFileExists = (path: string) => + danger(`Boyka config file is already available at [${path}]...`); diff --git a/src/utils/json.ts b/src/utils/json.ts index 5bb25f5..a32b7bd 100644 --- a/src/utils/json.ts +++ b/src/utils/json.ts @@ -1,16 +1,20 @@ import fs from 'fs'; import path from 'path'; import { FrameworkSetting } from '../types/configType'; -import { configFileName } from './constants'; +import { configFileName, errorMessage, savingMessage, sleep, successMessage } from './constants'; +import { createSpinner } from 'nanospinner'; export const createConfigFile = (filePath: string, setting: FrameworkSetting) => { const content = JSON.stringify(setting, null, 2); - fs.writeFile(path.join(filePath, configFileName), content, (err) => { + fs.writeFile(path.join(filePath, configFileName), content, async (err) => { + const spinner = createSpinner(savingMessage).start(); + await sleep(); + if (err) { - console.error(`❌ Error occurred! ${err.message}`); + spinner.error({ text: errorMessage(err) }); process.exit(1); } else { - console.info(`✅ Boyka config file created at [${filePath}]`); + spinner.success({ text: successMessage(filePath) }); } }); };