@govuk-pay/cli 0.0.50 → 0.0.52
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.
- package/package.json +6 -5
- package/readme.md +11 -0
- package/src/commands/tunnel.js +2 -0
- package/src/core/commandRouter.js +1 -0
- package/src/core/constants.js +14 -27
- package/src/util/configs.js +62 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@govuk-pay/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.52",
|
|
4
4
|
"description": "GOV.UK Pay Command Line Interface",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pay": "bin/cli.js",
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
"node": ">= 18.x"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@aws-sdk/client-ec2": "^3.
|
|
18
|
-
"@aws-sdk/client-ecs": "^3.
|
|
19
|
-
"@aws-sdk/client-rds": "^3.
|
|
20
|
-
"@aws-sdk/client-ssm": "^3.
|
|
17
|
+
"@aws-sdk/client-ec2": "^3.709.0",
|
|
18
|
+
"@aws-sdk/client-ecs": "^3.709.0",
|
|
19
|
+
"@aws-sdk/client-rds": "^3.709.0",
|
|
20
|
+
"@aws-sdk/client-ssm": "^3.709.0",
|
|
21
|
+
"@aws-sdk/client-sts": "^3.709.0",
|
|
21
22
|
"handlebars": "^4.7.8",
|
|
22
23
|
"openurl": "^1.1.1",
|
|
23
24
|
"semver": "^7.6.3",
|
package/readme.md
CHANGED
|
@@ -40,6 +40,17 @@ use the command `legacy`, for example:
|
|
|
40
40
|
|
|
41
41
|
Please add an issue any time you need to fall back on legacy behaviour so we can improve the typescript implementation.
|
|
42
42
|
|
|
43
|
+
### Shell auto completion
|
|
44
|
+
|
|
45
|
+
If you want to enable tab completion of commands and parameters then you can run
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
pay completion
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This will generate a script (for bash or zsh only) which you can put at the end of your .bashrc or .zshrc that will cause your
|
|
52
|
+
shell to provide auto completions.
|
|
53
|
+
|
|
43
54
|
### Config files
|
|
44
55
|
|
|
45
56
|
Config files needed by the pay cli will go in `$HOME/.pay-cli"
|
package/src/commands/tunnel.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.handler = exports.builder = exports.desc = exports.command = void 0;
|
|
7
7
|
const standardContent_js_1 = require("../core/standardContent.js");
|
|
8
|
+
const configs_js_1 = require("../util/configs.js");
|
|
8
9
|
const client_ec2_1 = require("@aws-sdk/client-ec2");
|
|
9
10
|
const client_ecs_1 = require("@aws-sdk/client-ecs");
|
|
10
11
|
const client_rds_1 = require("@aws-sdk/client-rds");
|
|
@@ -75,6 +76,7 @@ async function tunnelHandler(argv) {
|
|
|
75
76
|
await (0, standardContent_js_1.showHeader)();
|
|
76
77
|
const environment = argv.environment;
|
|
77
78
|
const application = argv.application;
|
|
79
|
+
await (0, configs_js_1.checkAwsCredentials)(environment.split('-')[0]);
|
|
78
80
|
console.log(`Opening a database tunnel to ${environment} ${application}`);
|
|
79
81
|
let maximumDuration = 90;
|
|
80
82
|
if (process.env.MAXIMUM_DURATION !== undefined) {
|
|
@@ -18,6 +18,7 @@ async function runCommand() {
|
|
|
18
18
|
.strict()
|
|
19
19
|
.help()
|
|
20
20
|
.wrap(yargsInstance.terminalWidth())
|
|
21
|
+
.completion('completion', 'Generate shell (bash/zsh only) auto-completion script. Put the script at the end of your .bashrc or .zshrc')
|
|
21
22
|
.parse();
|
|
22
23
|
}
|
|
23
24
|
exports.default = runCommand;
|
package/src/core/constants.js
CHANGED
|
@@ -1,33 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
4
|
};
|
|
25
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.APPLICATIONS = exports.ENVIRONMENTS = exports.distDirForBuildTasks = exports.rootDirForBuildTasks = exports.rootDir = void 0;
|
|
27
|
-
const
|
|
28
|
-
exports.rootDir =
|
|
29
|
-
exports.rootDirForBuildTasks = exports.rootDir.endsWith('dist') ?
|
|
30
|
-
exports.distDirForBuildTasks =
|
|
6
|
+
exports.AWS_ACCOUNT_IDS = exports.APPLICATIONS = exports.ENVIRONMENTS = exports.distDirForBuildTasks = exports.rootDirForBuildTasks = exports.rootDir = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
exports.rootDir = path_1.default.resolve(__dirname, '..', '..');
|
|
9
|
+
exports.rootDirForBuildTasks = exports.rootDir.endsWith('dist') ? path_1.default.resolve(exports.rootDir, '..') : exports.rootDir;
|
|
10
|
+
exports.distDirForBuildTasks = path_1.default.join(exports.rootDirForBuildTasks, 'dist');
|
|
31
11
|
exports.ENVIRONMENTS = ['test-12', 'test-perf-1', 'staging-2', 'deploy-tooling', 'production-2'];
|
|
32
12
|
exports.APPLICATIONS = [
|
|
33
13
|
'adminusers',
|
|
@@ -46,6 +26,13 @@ exports.APPLICATIONS = [
|
|
|
46
26
|
'toolbox',
|
|
47
27
|
'webhooks'
|
|
48
28
|
];
|
|
29
|
+
exports.AWS_ACCOUNT_IDS = {
|
|
30
|
+
dev: '673337093959',
|
|
31
|
+
test: '223851549868',
|
|
32
|
+
staging: '888564216586',
|
|
33
|
+
production: '092359438320',
|
|
34
|
+
deploy: '424875624006'
|
|
35
|
+
};
|
|
49
36
|
exports.default = {
|
|
50
37
|
rootDir: exports.rootDir,
|
|
51
38
|
distDirForBuildTasks: exports.distDirForBuildTasks,
|
package/src/util/configs.js
CHANGED
|
@@ -3,10 +3,12 @@ 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.workspaceEnvVar = exports.ensureConfigDirectory = exports.PAY_CLI_CONFIG_PATH = void 0;
|
|
6
|
+
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"));
|
|
10
|
+
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
11
|
+
const constants_1 = require("../core/constants");
|
|
10
12
|
exports.PAY_CLI_CONFIG_PATH = node_path_1.default.join((0, node_os_1.homedir)(), '.pay-cli');
|
|
11
13
|
/**
|
|
12
14
|
* This function validates a config path within the pay cli config dir exists, if it does then it creates it.
|
|
@@ -45,3 +47,62 @@ function workspaceEnvVar() {
|
|
|
45
47
|
return workspacePath;
|
|
46
48
|
}
|
|
47
49
|
exports.workspaceEnvVar = workspaceEnvVar;
|
|
50
|
+
const AWS_ENV_VARS_TO_CHECK = [
|
|
51
|
+
'AWS_ACCESS_KEY_ID',
|
|
52
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
53
|
+
'AWS_SESSION_TOKEN',
|
|
54
|
+
'AWS_CREDENTIAL_EXPIRATION'
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* This function checks if there are AWS credentials in the environment, that they are for a named account, and their expiry status.
|
|
58
|
+
* If there are none, or are some but they are expired, it will exit the program with an Error
|
|
59
|
+
* If there is less than 15 mins before expiry it will print a warning
|
|
60
|
+
*
|
|
61
|
+
* The accountName is intentionally a string and not a named type in order to mean the callers of this function don't need to implement
|
|
62
|
+
* validation checking of something passed in at runtime
|
|
63
|
+
*/
|
|
64
|
+
async function checkAwsCredentials(accountName) {
|
|
65
|
+
for (const envVar of AWS_ENV_VARS_TO_CHECK) {
|
|
66
|
+
if (process.env[envVar] === undefined || process.env[envVar] === null || process.env[envVar]?.trim() === '') {
|
|
67
|
+
console.error(`It looks like you do not have any AWS credentials in the environment (missing ${envVar}).`);
|
|
68
|
+
console.error('Perhaps you need to run this with aws-vault. E.g. `aws-vault exec test -- pay ...`');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const expirationTime = process.env.AWS_CREDENTIAL_EXPIRATION;
|
|
73
|
+
const expirationTimestamp = Date.parse(expirationTime);
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
if (now >= expirationTimestamp) {
|
|
76
|
+
console.error(`There are AWS credentials in the env, but they have expired. They expired at ${expirationTime}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
// 15 minutes * 60 seconds in a minute * 1000 ms in a second
|
|
80
|
+
if (now >= (expirationTimestamp - (15 * 60 * 1000))) {
|
|
81
|
+
console.warn(`WARNING: The AWS credentials will expire at ${expirationTime} which is within 15 minutes\n`);
|
|
82
|
+
}
|
|
83
|
+
await checkAwsCredentialsAreForAccount(accountName);
|
|
84
|
+
}
|
|
85
|
+
exports.checkAwsCredentials = checkAwsCredentials;
|
|
86
|
+
/* This function will validate that the AWS credentials in the environment belong to the named account.
|
|
87
|
+
*
|
|
88
|
+
* If they are not then it will exit with an appropriate error
|
|
89
|
+
*/
|
|
90
|
+
async function checkAwsCredentialsAreForAccount(accountName) {
|
|
91
|
+
if (!Object.hasOwn(constants_1.AWS_ACCOUNT_IDS, accountName)) {
|
|
92
|
+
console.error(`AWS Account ${accountName} is unknown, perhaps you meant one of ${Object.keys(constants_1.AWS_ACCOUNT_IDS).join(',')}?`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
const typedAccountName = accountName;
|
|
96
|
+
const stsClient = new client_sts_1.STSClient();
|
|
97
|
+
const getCallerIdentityCommand = new client_sts_1.GetCallerIdentityCommand({});
|
|
98
|
+
const response = await stsClient.send(getCallerIdentityCommand);
|
|
99
|
+
if (response.Account === undefined) {
|
|
100
|
+
console.error('Error checking if AWS credentials are for the correct account, the response from AWS did not include an Account ID');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
if (constants_1.AWS_ACCOUNT_IDS[typedAccountName] !== response.Account) {
|
|
104
|
+
console.error(`Error: You're trying to perform an action against AWS account '${accountName}'`);
|
|
105
|
+
console.error(`Your credentials are not for that account. Perhaps you need to run 'aws-vault exec ${accountName} -- ...`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|