diff --git a/README.md b/README.md index 518d3b1d..9c03d72b 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Options: * `-r`, `--repo` - The GitHub full repository name (e.g. `cdklabs/aws-secrets-github-sync`). If this is not specified, we will try to resolve the repo from the current git settings. +* `-e`, `--environment` - The GitHub environment to store the secret in (Optional). * `-R`, `--region` - The AWS region to read the secret from. If this is not specified, `AWS_REGION` will be used. If the secret is an ARN, we will resolve the region from the ARN. diff --git a/src/cli.ts b/src/cli.ts index 43a79ded..0780918b 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,6 +7,7 @@ async function main() { .usage('$0 -C secrets.json') .option('secret', { alias: 's', describe: 'Secrets Manager secret ID or ARN', type: 'string', required: true }) .option('repo', { alias: 'r', describe: 'GitHub repository owner/name (default is derived from current git repository)', type: 'string' }) + .option('environment', { alias: 'e', describe: 'GitHub environment', type: 'string' }) .option('region', { alias: 'R', describe: 'AWS region (if --secret is an ARN, region is not required)', type: 'string' }) .option('keys', { alias: 'k', describe: 'Which keys to update (to update all keys use --all)', type: 'array' }) .option('all', { alias: 'A', describe: 'Update all keys', type: 'boolean' }) @@ -32,6 +33,7 @@ async function main() { secret: argv.secret, region: argv.region, repository: argv.repo, + environment: argv.environment, allKeys: argv.all, keys: argv.keys, confirm: !argv.yes, diff --git a/src/clients.ts b/src/clients.ts index 0fe1da01..ee5ed9e5 100644 --- a/src/clients.ts +++ b/src/clients.ts @@ -17,15 +17,19 @@ export interface SecretOptions { * Credential profile to use. */ readonly profile?: string; + /** + * Environment to use. + */ + readonly env?: string; } export interface Clients { getSecret(secretId: string, options?: SecretOptions): Promise; confirmPrompt(): Promise; getRepositoryName(): string; - storeSecret(repository: string, key: string, value: string): void; - listSecrets(repository: string): string[]; - removeSecret(repository: string, key: string): void; + storeSecret(repository: string, key: string, value: string, environment?: string): void; + listSecrets(repository: string, environment?: string): string[]; + removeSecret(repository: string, key: string, environment?: string): void; log(text?: string): void; } @@ -54,19 +58,28 @@ function getRepositoryName(): string { } } -function storeSecret(repository: string, name: string, value: string): void { +function storeSecret(repository: string, name: string, value: string, environment?: string): void { const args = ['secret', 'set', '--repo', repository, name]; + if (environment) { + args.push('-e', environment); + } spawnSync('gh', args, { input: value, stdio: ['pipe', 'inherit', 'inherit'] }); } -function listSecrets(repository: string): string[] { +function listSecrets(repository: string, environment?: string): string[] { const args = ['secret', 'list', '--repo', repository]; + if (environment) { + args.push('-e', environment); + } const stdout = spawnSync('gh', args, { stdio: ['ignore', 'pipe', 'inherit'] }).stdout.toString('utf-8').trim(); return stdout.split('\n').map(line => line.split('\t')[0]); } -function removeSecret(repository: string, key: string): void { +function removeSecret(repository: string, key: string, environment?: string): void { const args = ['secret', 'remove', '--repo', repository, key]; + if (environment) { + args.push('-e', environment); + } spawnSync('gh', args, { stdio: ['ignore', 'inherit', 'inherit'] }); } diff --git a/src/update-secrets.ts b/src/update-secrets.ts index fc153d83..4ab1979d 100644 --- a/src/update-secrets.ts +++ b/src/update-secrets.ts @@ -32,6 +32,12 @@ export interface UpdateSecretsOptions { */ readonly repository?: string; + /** + * The environment to use. + * @default - no environment + */ + readonly environment?: string; + /** * The secret keys to update. To update all keys, set `all` to `true` and leave `keys` empty. * @@ -83,6 +89,7 @@ export async function updateSecrets(options: UpdateSecretsOptions) { } const repository: string = options.repository ?? c.getRepositoryName(); + const environment = options.environment; const secret = await c.getSecret(options.secret, { region, profile: options.profile }); const keys = options.keys ?? []; const prune = options.prune ?? false; @@ -117,7 +124,7 @@ export async function updateSecrets(options: UpdateSecretsOptions) { } // find all the secrets in the repo that don't correspond to keys in the secret - const existingKeys = c.listSecrets(repository); + const existingKeys = c.listSecrets(repository, environment); const pruneCandidates = existingKeys.filter(key => !keys.includes(key)); const keysToPrune = pruneCandidates.filter(key => !keep.includes(key)); const keysToKeep = pruneCandidates.filter(key => keep.includes(key)); @@ -153,13 +160,13 @@ export async function updateSecrets(options: UpdateSecretsOptions) { continue; // skip if key is not in "keys" } - c.storeSecret(repository, key, value); + c.storeSecret(repository, key, value, environment); } // prune keys that are not in the secret if (prune) { for (const key of keysToPrune) { - c.removeSecret(repository, key); + c.removeSecret(repository, key, environment); } } }