diff --git a/packages/cli/src/commands/env/add.ts b/packages/cli/src/commands/env/add.ts index 6c3c5a19..c5a7447e 100644 --- a/packages/cli/src/commands/env/add.ts +++ b/packages/cli/src/commands/env/add.ts @@ -12,6 +12,12 @@ export default class EnvAdd extends AuthCommand { description: 'Indicate that the environment variable will be locked.', default: false, }), + secret: Flags.boolean({ + char: 's', + description: 'Indicate that the environment variable will be secret.', + default: false, + exclusive: ['locked'], + }), } static args = { @@ -29,7 +35,7 @@ export default class EnvAdd extends AuthCommand { async run (): Promise { const { flags, args } = await this.parse(EnvAdd) - const { locked } = flags + const { locked, secret } = flags const envVariableName = args.key let envValue = '' @@ -40,8 +46,11 @@ export default class EnvAdd extends AuthCommand { envValue = await ux.prompt(`What is the value of ${envVariableName}?`, { type: 'mask' }) } try { - await api.environmentVariables.add(envVariableName, envValue, locked) - this.log(`Environment variable ${envVariableName} added.`) + await api.environmentVariables.add(envVariableName, envValue, locked, secret) + this.log(secret + ? `Secret environment variable ${envVariableName} added.` + : `Environment variable ${envVariableName} added.`, + ) } catch (err: any) { if (err?.response?.status === 409) { throw new Error(`Environment variable ${envVariableName} already exists.`) diff --git a/packages/cli/src/commands/env/pull.ts b/packages/cli/src/commands/env/pull.ts index 9249dffd..1a276be6 100644 --- a/packages/cli/src/commands/env/pull.ts +++ b/packages/cli/src/commands/env/pull.ts @@ -7,6 +7,8 @@ import { escapeValue } from '../../services/util' import * as fs from 'fs/promises' const CONTENTS_PREFIX = '# Created by Checkly CLI\n' +const CONTENTS_ENV_VARS = '# Environment variables\n' +const CONTENTS_SECRET_VARS = '# Secret variables\n' export default class EnvPull extends AuthCommand { static hidden = false @@ -36,14 +38,23 @@ export default class EnvPull extends AuthCommand { const filepath = path.resolve(args.filename) const filename = path.basename(filepath) const { data: environmentVariables } = await api.environmentVariables.getAll() + // create an file in current directory and save the env vars there - const env = CONTENTS_PREFIX + environmentVariables.map(({ key, value }) => `${key}=${escapeValue(value)}`).join('\n') + '\n' + let fileContent = CONTENTS_PREFIX + fileContent += CONTENTS_ENV_VARS + fileContent += environmentVariables + .filter(({ secret }) => !secret) + .map(({ key, value }) => `${key}=${escapeValue(value)}`).join('\n') + '\n' + fileContent += CONTENTS_SECRET_VARS + fileContent += environmentVariables + .filter(({ secret }) => secret) + .map(({ key, value }) => `${key}=${escapeValue(value)}`).join('\n') + '\n' // wx will cause the write to fail if the file already exists // https://nodejs.org/api/fs.html#file-system-flags const flag = force ? 'w' : 'wx' try { - await fs.writeFile(filepath, env, { flag }) + await fs.writeFile(filepath, fileContent, { flag }) } catch (err: any) { // By catching EEXIST rather than checking fs.existsSync, // we avoid a race condition when a file is created between writing and checking @@ -57,7 +68,7 @@ export default class EnvPull extends AuthCommand { this.log('Cancelled. No changes made.') return } - await fs.writeFile(filepath, env) + await fs.writeFile(filepath, fileContent) } } this.log(`Success! Environment variables written to ${filename}.`) diff --git a/packages/cli/src/commands/env/update.ts b/packages/cli/src/commands/env/update.ts index dbf4814e..94a46b44 100644 --- a/packages/cli/src/commands/env/update.ts +++ b/packages/cli/src/commands/env/update.ts @@ -12,6 +12,12 @@ export default class EnvUpdate extends AuthCommand { description: 'Indicate if environment variable is locked.', default: false, }), + secret: Flags.boolean({ + char: 's', + description: 'Indicate if environment variable is secret.', + default: false, + exclusive: ['locked'], + }), } static args = { @@ -29,7 +35,7 @@ export default class EnvUpdate extends AuthCommand { async run (): Promise { const { flags, args } = await this.parse(EnvUpdate) - const { locked } = flags + const { locked, secret } = flags const envVariableName = args.key let envValue = '' @@ -40,12 +46,20 @@ export default class EnvUpdate extends AuthCommand { envValue = await ux.prompt(`What is the value of ${envVariableName}?`, { type: 'mask' }) } try { - await api.environmentVariables.update(envVariableName, envValue, locked) - this.log(`Environment variable ${envVariableName} updated.`) + await api.environmentVariables.update(envVariableName, envValue, locked, secret) + this.log(secret + ? `Secret environment variable ${envVariableName} updated.` + : `Environment variable ${envVariableName} updated.`, + ) } catch (err: any) { + if (err?.response?.status === 400 && err?.response?.data?.message) { + throw new Error(err.response.data.message) + } + if (err?.response?.status === 404) { throw new Error(`Environment variable ${envVariableName} not found.`) } + throw err } } diff --git a/packages/cli/src/constructs/key-value-pair.ts b/packages/cli/src/constructs/key-value-pair.ts index 0438f671..0ee36238 100644 --- a/packages/cli/src/constructs/key-value-pair.ts +++ b/packages/cli/src/constructs/key-value-pair.ts @@ -2,4 +2,5 @@ export default interface KeyValuePair { key: string value: string locked?: boolean + secret?: boolean } diff --git a/packages/cli/src/rest/environment-variables.ts b/packages/cli/src/rest/environment-variables.ts index cdbc4176..b4c13ed5 100644 --- a/packages/cli/src/rest/environment-variables.ts +++ b/packages/cli/src/rest/environment-variables.ts @@ -4,6 +4,7 @@ export interface EnvironmentVariable { key: string value: string locked: boolean + secret?: boolean } class EnvironmentVariables { @@ -20,8 +21,8 @@ class EnvironmentVariables { return this.api.delete(`/v1/variables/${environmentVariableKey}`) } - add (environmentVariableKey: string, environmentVariableValue: string, locked: boolean) { - return this.api.post('/v1/variables', { key: environmentVariableKey, value: environmentVariableValue, locked }) + add (environmentVariableKey: string, environmentVariableValue: string, locked = false, secret = false) { + return this.api.post('/v1/variables', { key: environmentVariableKey, value: environmentVariableValue, locked, secret }) } get (environmentVariableKey: string) { @@ -29,8 +30,8 @@ class EnvironmentVariables { } // update environment variable with default locked value false - update (environmentVariableKey: string, environmentVariableValue: string, locked = false) { - return this.api.put(`/v1/variables/${environmentVariableKey}`, { value: environmentVariableValue, locked }) + update (environmentVariableKey: string, environmentVariableValue: string, locked = false, secret = false) { + return this.api.put(`/v1/variables/${environmentVariableKey}`, { value: environmentVariableValue, locked, secret }) } }