@govuk-pay/cli 0.0.52 → 0.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/package.json +2 -1
  2. package/readme.md +4 -3
  3. package/src/commands/secrets/config/config.types.js +50 -0
  4. package/src/commands/secrets/config/secrets/bitwarden_cli.js +4 -0
  5. package/src/commands/secrets/config/secrets/pay_low_pass/deploy-7.js +9 -0
  6. package/src/commands/secrets/config/secrets/pay_low_pass/deploy-tooling.js +18 -0
  7. package/src/commands/secrets/config/secrets/pay_low_pass/deploy.js +64 -0
  8. package/src/commands/secrets/config/secrets/pay_low_pass/dev.js +13 -0
  9. package/src/commands/secrets/config/secrets/pay_low_pass/production-2.js +104 -0
  10. package/src/commands/secrets/config/secrets/pay_low_pass/production.js +8 -0
  11. package/src/commands/secrets/config/secrets/pay_low_pass/staging-2.js +98 -0
  12. package/src/commands/secrets/config/secrets/pay_low_pass/staging.js +8 -0
  13. package/src/commands/secrets/config/secrets/pay_low_pass/test-12.js +101 -0
  14. package/src/commands/secrets/config/secrets/pay_low_pass/test-perf-1.js +98 -0
  15. package/src/commands/secrets/config/secrets/pay_low_pass/test.js +13 -0
  16. package/src/commands/secrets/config/secrets/pay_low_pass.js +27 -0
  17. package/src/commands/secrets/config/secrets/ssm.js +4 -0
  18. package/src/commands/secrets/config/secrets/value/deploy-tooling.js +10 -0
  19. package/src/commands/secrets/config/secrets/value/deploy.js +20 -0
  20. package/src/commands/secrets/config/secrets/value/production-2.js +45 -0
  21. package/src/commands/secrets/config/secrets/value/staging-2.js +47 -0
  22. package/src/commands/secrets/config/secrets/value/test-12.js +47 -0
  23. package/src/commands/secrets/config/secrets/value/test-perf-1.js +49 -0
  24. package/src/commands/secrets/config/secrets/value.js +17 -0
  25. package/src/commands/secrets/config/secrets.js +86 -0
  26. package/src/commands/secrets/config/service_secrets.js +238 -0
  27. package/src/commands/secrets/providers/bitwarden_cli.js +98 -0
  28. package/src/commands/secrets/providers/factory.js +42 -0
  29. package/src/commands/secrets/providers/pass_repo.js +65 -0
  30. package/src/commands/secrets/providers/providers.types.js +21 -0
  31. package/src/commands/secrets/providers/ssm.js +155 -0
  32. package/src/commands/secrets/providers/value.js +10 -0
  33. package/src/commands/secrets/subcommands/audit.js +41 -9
  34. package/src/commands/secrets/subcommands/fetch.js +36 -15
  35. package/src/commands/secrets/subcommands/provision.js +99 -7
  36. package/src/commands/secrets.js +1 -1
  37. package/src/core/standardContent.js +5 -1
  38. package/src/util/configs.js +7 -1
  39. package/src/commands/secrets/subcommands/copy.js +0 -35
@@ -4,19 +4,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.handler = exports.builder = exports.desc = exports.command = void 0;
7
- const node_child_process_1 = require("node:child_process");
8
- const preflight_js_1 = __importDefault(require("../utils/preflight.js"));
9
- exports.command = 'provision <service> <env>';
7
+ const cli_table3_1 = __importDefault(require("cli-table3"));
8
+ const promises_1 = __importDefault(require("node:readline/promises"));
9
+ const config_types_1 = require("../config/config.types");
10
+ const secrets_1 = require("../config/secrets");
11
+ const configs_1 = require("../../../util/configs");
12
+ const factory_1 = require("../providers/factory");
13
+ const standardContent_js_1 = require("../../../core/standardContent.js");
14
+ exports.command = 'provision <service> <env> [--redact] [--dry-run]';
10
15
  exports.desc = 'Provisions secrets from config for <service> in <env>';
11
16
  const builder = (yargs) => {
12
17
  return yargs
13
18
  .positional('service', {
14
19
  type: 'string',
15
- description: 'The service (e.g. connector) to provision secrets for'
20
+ description: 'The service (e.g. connector) to provision secrets for',
21
+ choices: config_types_1.SERVICE_NAMES
16
22
  })
17
23
  .positional('env', {
18
24
  type: 'string',
19
- description: 'The environment (e.g. test-12) to provision the secrets in'
25
+ description: 'The environment (e.g. test-12) to provision the secrets in',
26
+ choices: config_types_1.ENVIRONMENT_NAMES
27
+ })
28
+ .option('redact', {
29
+ type: 'boolean',
30
+ default: false,
31
+ description: 'During a dry-run redact all printed secret values except for an empty string (which makes it suitable to paste into a PR)'
32
+ })
33
+ .option('dry-run', {
34
+ type: 'boolean',
35
+ default: false,
36
+ description: 'Perform a dry run, will not set any secrets, but will show all changes'
20
37
  });
21
38
  };
22
39
  exports.builder = builder;
@@ -24,7 +41,82 @@ exports.handler = provisionHandler;
24
41
  async function provisionHandler(argv) {
25
42
  const service = argv.service;
26
43
  const env = argv.env;
27
- const preflightInfo = (0, preflight_js_1.default)();
28
- (0, node_child_process_1.spawnSync)(preflightInfo.rbenvCommand, ['exec', 'bundle', 'exec', 'bin/pay', 'secrets', 'provision', service, env], { shell: true, stdio: 'inherit', cwd: preflightInfo.pathToLegacyRubyCli });
44
+ const redact = argv.redact;
45
+ const dryRun = argv.dryRun;
46
+ await (0, standardContent_js_1.showHeader)();
47
+ if (redact && !dryRun) {
48
+ console.error('The --redact option is only valid with the --dry-run option also specified');
49
+ process.exit(1);
50
+ }
51
+ await (0, configs_1.checkAwsCredentials)((0, configs_1.payEnvironmentToAWSAccountName)(env));
52
+ const rl = promises_1.default.createInterface({ input: process.stdin, output: process.stdout });
53
+ const secretsToProvision = [];
54
+ for (const sourceSecret of (0, secrets_1.configuredSecretsForServiceInEnv)(env, service)) {
55
+ secretsToProvision.push({
56
+ source: sourceSecret,
57
+ destination: {
58
+ ...sourceSecret,
59
+ source: 'ssm' // Currently all secrets are provisioned to ssm, but that could change in the future
60
+ }
61
+ });
62
+ }
63
+ for (const secretToProvision of secretsToProvision) {
64
+ const sourceProvider = (0, factory_1.providerFor)(secretToProvision.source);
65
+ const destinationProvider = (0, factory_1.providerFor)(secretToProvision.destination);
66
+ const sourceValue = await sourceProvider.get(secretToProvision.source);
67
+ if (sourceValue === undefined) {
68
+ console.error(`Secret ${secretToProvision.source.name} does not exist in ${secretToProvision.source.source}`);
69
+ process.exit(1);
70
+ }
71
+ const destinationValue = await destinationProvider.get(secretToProvision.destination);
72
+ if (sourceValue === destinationValue) {
73
+ console.log(`✅ Provisioning ${secretToProvision.source.name} in ${secretToProvision.destination.source} in environment ${secretToProvision.source.environment} for ${secretToProvision.source.service} would apply no change`);
74
+ continue;
75
+ }
76
+ if (destinationValue === undefined) {
77
+ console.log(`❓ Secret ${secretToProvision.source.name} does not exist in ${secretToProvision.destination.source} in environment ${secretToProvision.source.environment} for ${secretToProvision.source.service}, provision?`);
78
+ }
79
+ else {
80
+ console.log(`❓ Update ${secretToProvision.source.name} in ${secretToProvision.destination.source} in environment ${secretToProvision.source.environment} for ${secretToProvision.source.service}?`);
81
+ }
82
+ const table = new cli_table3_1.default({
83
+ head: ['Old value', 'New Value']
84
+ });
85
+ table.push([
86
+ secretForDisplay(destinationValue, redact),
87
+ secretForDisplay(sourceValue, redact)
88
+ ]);
89
+ console.log(table.toString());
90
+ if (dryRun) {
91
+ console.log('Dry run only....not provisioning');
92
+ continue;
93
+ }
94
+ const confirmResponse = await rl.question(' Type "yes" exactly > ');
95
+ if (confirmResponse.trim() !== 'yes') {
96
+ console.log('User rejected confirmation, secret NOT provisioned');
97
+ continue;
98
+ }
99
+ if (await destinationProvider.set(secretToProvision.destination, sourceValue)) {
100
+ console.log('✅ Secret successfully provisioned');
101
+ }
102
+ else {
103
+ console.error('❌ Saving the secret failed for an unknown reason');
104
+ }
105
+ }
106
+ console.log('\nProvision finished');
107
+ rl.close();
29
108
  }
30
109
  exports.default = provisionHandler;
110
+ function secretForDisplay(secretValue, redact) {
111
+ if (secretValue === undefined) {
112
+ return '👻 NOT CURRENTLY SET 👻';
113
+ }
114
+ // Purposefully not redacting if the secret is an empty string
115
+ if (secretValue.trim() === '') {
116
+ return '"" (❗EMPTY STRING❗)';
117
+ }
118
+ if (redact) {
119
+ return '🤫 REDACTED 🤫';
120
+ }
121
+ return secretValue;
122
+ }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.builder = exports.desc = exports.command = void 0;
4
4
  exports.command = 'secrets';
5
- exports.desc = 'Provision secrets (uses the legacy ruby CLI)';
5
+ exports.desc = 'Provision secrets';
6
6
  const builder = (yargs) => {
7
7
  return yargs
8
8
  .commandDir('secrets/subcommands')
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.showHeader = void 0;
26
+ exports.showHeaderStdErr = exports.showHeader = void 0;
27
27
  const fsp = __importStar(require("fs/promises"));
28
28
  const path = __importStar(require("path"));
29
29
  const constants_js_1 = require("./constants.js");
@@ -31,3 +31,7 @@ async function showHeader() {
31
31
  console.log(await fsp.readFile(path.join(constants_js_1.rootDir, 'resources/header.txt'), 'utf8'));
32
32
  }
33
33
  exports.showHeader = showHeader;
34
+ async function showHeaderStdErr() {
35
+ console.warn(await fsp.readFile(path.join(constants_js_1.rootDir, 'resources/header.txt'), 'utf8'));
36
+ }
37
+ exports.showHeaderStdErr = showHeaderStdErr;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.checkAwsCredentials = exports.workspaceEnvVar = exports.ensureConfigDirectory = exports.PAY_CLI_CONFIG_PATH = void 0;
6
+ exports.payEnvironmentToAWSAccountName = exports.checkAwsCredentials = exports.workspaceEnvVar = exports.ensureConfigDirectory = exports.PAY_CLI_CONFIG_PATH = void 0;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_os_1 = require("node:os");
9
9
  const node_path_1 = __importDefault(require("node:path"));
@@ -106,3 +106,9 @@ async function checkAwsCredentialsAreForAccount(accountName) {
106
106
  process.exit(1);
107
107
  }
108
108
  }
109
+ /* This function will turn a Pay environment name into an AWS account name
110
+ */
111
+ function payEnvironmentToAWSAccountName(environmentName) {
112
+ return environmentName.split('-')[0];
113
+ }
114
+ exports.payEnvironmentToAWSAccountName = payEnvironmentToAWSAccountName;
@@ -1,35 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.handler = exports.builder = exports.desc = exports.command = void 0;
7
- const node_child_process_1 = require("node:child_process");
8
- const preflight_js_1 = __importDefault(require("../utils/preflight.js"));
9
- exports.command = 'copy <service> <src_env> <dest_env>';
10
- exports.desc = 'Copies secrets for <service> from <src_env> to <dest_env>';
11
- const builder = (yargs) => {
12
- return yargs
13
- .positional('service', {
14
- type: 'string',
15
- description: 'The service (e.g. connector) to copy the secrets for'
16
- })
17
- .positional('src_env', {
18
- type: 'string',
19
- description: 'The environment (e.g. test-12) to copy the secrets from'
20
- })
21
- .positional('dest_env', {
22
- type: 'string',
23
- description: 'The environment (e.g. test-perf-1) to copy the secrets to'
24
- });
25
- };
26
- exports.builder = builder;
27
- exports.handler = copyHandler;
28
- async function copyHandler(argv) {
29
- const service = argv.service;
30
- const srcEnv = argv.src_env;
31
- const destEnv = argv.dest_env;
32
- const preflightInfo = (0, preflight_js_1.default)();
33
- (0, node_child_process_1.spawnSync)(preflightInfo.rbenvCommand, ['exec', 'bundle', 'exec', 'bin/pay', 'secrets', 'copy', service, srcEnv, destEnv], { shell: true, stdio: 'inherit', cwd: preflightInfo.pathToLegacyRubyCli });
34
- }
35
- exports.default = copyHandler;