Skip to content

Commit

Permalink
feat: add environment secrets support [sc-21028] (#964)
Browse files Browse the repository at this point in the history
* feat: add environment secrets support [sc-21028]

* feat: update copy

* feat: use error message from the API [sc-21028]

* feat: tweak confirm messages for better DX
  • Loading branch information
maxigimenez authored Sep 3, 2024
1 parent c9bd0b2 commit 2630e9e
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 13 deletions.
15 changes: 12 additions & 3 deletions packages/cli/src/commands/env/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -29,7 +35,7 @@ export default class EnvAdd extends AuthCommand {

async run (): Promise<void> {
const { flags, args } = await this.parse(EnvAdd)
const { locked } = flags
const { locked, secret } = flags

const envVariableName = args.key
let envValue = ''
Expand All @@ -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.`)
Expand Down
17 changes: 14 additions & 3 deletions packages/cli/src/commands/env/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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}.`)
Expand Down
20 changes: 17 additions & 3 deletions packages/cli/src/commands/env/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -29,7 +35,7 @@ export default class EnvUpdate extends AuthCommand {

async run (): Promise<void> {
const { flags, args } = await this.parse(EnvUpdate)
const { locked } = flags
const { locked, secret } = flags

const envVariableName = args.key
let envValue = ''
Expand All @@ -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
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/constructs/key-value-pair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export default interface KeyValuePair {
key: string
value: string
locked?: boolean
secret?: boolean
}
9 changes: 5 additions & 4 deletions packages/cli/src/rest/environment-variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface EnvironmentVariable {
key: string
value: string
locked: boolean
secret?: boolean
}

class EnvironmentVariables {
Expand All @@ -20,17 +21,17 @@ 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) {
return this.api.get<EnvironmentVariable>(`/v1/variables/${environmentVariableKey}`)
}

// 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 })
}
}

Expand Down

0 comments on commit 2630e9e

Please sign in to comment.