slskit-cli 1.0.0
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/CHANGELOG.md +61 -0
- package/COMMAND.md +636 -0
- package/LICENSE +21 -0
- package/NOTICE.md +22 -0
- package/README.md +168 -0
- package/bin/slskit.js +3 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +25 -0
- package/dist/commands/base-command.d.ts +5 -0
- package/dist/commands/base-command.js +29 -0
- package/dist/commands/configure/action.d.ts +2 -0
- package/dist/commands/configure/action.js +120 -0
- package/dist/commands/configure/aws-cli.d.ts +16 -0
- package/dist/commands/configure/aws-cli.js +135 -0
- package/dist/commands/configure/index.d.ts +2 -0
- package/dist/commands/configure/index.js +20 -0
- package/dist/commands/configure/prompts.d.ts +8 -0
- package/dist/commands/configure/prompts.js +117 -0
- package/dist/commands/configure/types.d.ts +24 -0
- package/dist/commands/configure/types.js +6 -0
- package/dist/commands/deploy/action.d.ts +2 -0
- package/dist/commands/deploy/action.js +115 -0
- package/dist/commands/deploy/index.d.ts +2 -0
- package/dist/commands/deploy/index.js +23 -0
- package/dist/commands/deploy/sam-deploy.d.ts +10 -0
- package/dist/commands/deploy/sam-deploy.js +127 -0
- package/dist/commands/deploy/scope.d.ts +9 -0
- package/dist/commands/deploy/scope.js +34 -0
- package/dist/commands/deploy/types.d.ts +20 -0
- package/dist/commands/deploy/types.js +2 -0
- package/dist/commands/env/action.d.ts +11 -0
- package/dist/commands/env/action.js +274 -0
- package/dist/commands/env/dotenv.d.ts +6 -0
- package/dist/commands/env/dotenv.js +99 -0
- package/dist/commands/env/index.d.ts +2 -0
- package/dist/commands/env/index.js +76 -0
- package/dist/commands/env/keys.d.ts +8 -0
- package/dist/commands/env/keys.js +41 -0
- package/dist/commands/env/parameters.d.ts +7 -0
- package/dist/commands/env/parameters.js +63 -0
- package/dist/commands/env/templates.d.ts +3 -0
- package/dist/commands/env/templates.js +65 -0
- package/dist/commands/env/types.d.ts +21 -0
- package/dist/commands/env/types.js +2 -0
- package/dist/commands/function/action.d.ts +2 -0
- package/dist/commands/function/action.js +71 -0
- package/dist/commands/function/generator.d.ts +8 -0
- package/dist/commands/function/generator.js +58 -0
- package/dist/commands/function/index.d.ts +2 -0
- package/dist/commands/function/index.js +26 -0
- package/dist/commands/function/manifest.d.ts +5 -0
- package/dist/commands/function/manifest.js +57 -0
- package/dist/commands/function/prompts.d.ts +18 -0
- package/dist/commands/function/prompts.js +196 -0
- package/dist/commands/function/tooling.d.ts +2 -0
- package/dist/commands/function/tooling.js +43 -0
- package/dist/commands/function/types.d.ts +52 -0
- package/dist/commands/function/types.js +2 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +19 -0
- package/dist/commands/init/action.d.ts +2 -0
- package/dist/commands/init/action.js +36 -0
- package/dist/commands/init/content.d.ts +2 -0
- package/dist/commands/init/content.js +97 -0
- package/dist/commands/init/index.d.ts +2 -0
- package/dist/commands/init/index.js +30 -0
- package/dist/commands/init/manifest.d.ts +6 -0
- package/dist/commands/init/manifest.js +147 -0
- package/dist/commands/init/prompts.d.ts +6 -0
- package/dist/commands/init/prompts.js +155 -0
- package/dist/commands/init/scaffold.d.ts +7 -0
- package/dist/commands/init/scaffold.js +85 -0
- package/dist/commands/init/templates/helpers.d.ts +9 -0
- package/dist/commands/init/templates/helpers.js +42 -0
- package/dist/commands/init/templates/node.d.ts +4 -0
- package/dist/commands/init/templates/node.js +99 -0
- package/dist/commands/init/templates/project.d.ts +8 -0
- package/dist/commands/init/templates/project.js +239 -0
- package/dist/commands/init/templates/python.d.ts +4 -0
- package/dist/commands/init/templates/python.js +65 -0
- package/dist/commands/init/templates/sam.d.ts +4 -0
- package/dist/commands/init/templates/sam.js +369 -0
- package/dist/commands/init/templates/shared-code.d.ts +6 -0
- package/dist/commands/init/templates/shared-code.js +159 -0
- package/dist/commands/init/types.d.ts +47 -0
- package/dist/commands/init/types.js +75 -0
- package/dist/commands/rm/action.d.ts +2 -0
- package/dist/commands/rm/action.js +89 -0
- package/dist/commands/rm/index.d.ts +2 -0
- package/dist/commands/rm/index.js +25 -0
- package/dist/commands/rm/plan.d.ts +19 -0
- package/dist/commands/rm/plan.js +78 -0
- package/dist/commands/rm/prompts.d.ts +4 -0
- package/dist/commands/rm/prompts.js +107 -0
- package/dist/commands/rm/types.d.ts +7 -0
- package/dist/commands/rm/types.js +2 -0
- package/dist/commands/run/action.d.ts +2 -0
- package/dist/commands/run/action.js +171 -0
- package/dist/commands/run/index.d.ts +2 -0
- package/dist/commands/run/index.js +21 -0
- package/dist/commands/run/local-template.d.ts +7 -0
- package/dist/commands/run/local-template.js +51 -0
- package/dist/commands/run/manifest.d.ts +2 -0
- package/dist/commands/run/manifest.js +20 -0
- package/dist/commands/run/resources.d.ts +20 -0
- package/dist/commands/run/resources.js +93 -0
- package/dist/commands/run/sam-cli.d.ts +10 -0
- package/dist/commands/run/sam-cli.js +183 -0
- package/dist/commands/run/types.d.ts +18 -0
- package/dist/commands/run/types.js +2 -0
- package/dist/commands/run/watch.d.ts +11 -0
- package/dist/commands/run/watch.js +131 -0
- package/dist/core/context.d.ts +15 -0
- package/dist/core/context.js +41 -0
- package/dist/core/credentials.d.ts +10 -0
- package/dist/core/credentials.js +22 -0
- package/dist/core/environments.d.ts +46 -0
- package/dist/core/environments.js +161 -0
- package/dist/core/errors.d.ts +4 -0
- package/dist/core/errors.js +11 -0
- package/dist/core/flags-docs.d.ts +2 -0
- package/dist/core/flags-docs.js +63 -0
- package/dist/core/is-ci.d.ts +4 -0
- package/dist/core/is-ci.js +16 -0
- package/dist/core/logger.d.ts +4 -0
- package/dist/core/logger.js +19 -0
- package/dist/core/run.d.ts +1 -0
- package/dist/core/run.js +27 -0
- package/dist/core/scope.d.ts +33 -0
- package/dist/core/scope.js +108 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +9 -0
- package/dist/program.d.ts +2 -0
- package/dist/program.js +26 -0
- package/package.json +110 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseEnvironmentName = exports.ENVIRONMENT_PROFILE = void 0;
|
|
4
|
+
exports.parseRegion = parseRegion;
|
|
5
|
+
exports.parseStackName = parseStackName;
|
|
6
|
+
exports.defaultStackName = defaultStackName;
|
|
7
|
+
exports.collectConfigureAnswers = collectConfigureAnswers;
|
|
8
|
+
const errors_js_1 = require("../../core/errors.js");
|
|
9
|
+
const aws_cli_js_1 = require("./aws-cli.js");
|
|
10
|
+
const environments_js_1 = require("../../core/environments.js");
|
|
11
|
+
Object.defineProperty(exports, "parseEnvironmentName", { enumerable: true, get: function () { return environments_js_1.parseEnvironmentName; } });
|
|
12
|
+
// CloudFormation stack names: letters, digits and hyphens, starting with a letter.
|
|
13
|
+
const STACK_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9-]*$/;
|
|
14
|
+
const STACK_NAME_MAX = 128;
|
|
15
|
+
// us-east-1, eu-west-2, ap-southeast-3, us-gov-west-1, cn-north-1 ...
|
|
16
|
+
const REGION_PATTERN = /^[a-z]{2}(-[a-z]+)?-[a-z]+-\d+$/;
|
|
17
|
+
// Sentinel for "don't pin a profile, read credentials from the environment".
|
|
18
|
+
exports.ENVIRONMENT_PROFILE = "";
|
|
19
|
+
function parseRegion(value) {
|
|
20
|
+
if (value === undefined || value.trim() === "") {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const region = value.trim().toLowerCase();
|
|
24
|
+
if (!REGION_PATTERN.test(region)) {
|
|
25
|
+
throw new errors_js_1.CliError(`Invalid AWS region "${value}". Expected something like us-east-1 or eu-west-2.`);
|
|
26
|
+
}
|
|
27
|
+
return region;
|
|
28
|
+
}
|
|
29
|
+
function parseStackName(value) {
|
|
30
|
+
if (value === undefined || value.trim() === "") {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const name = value.trim();
|
|
34
|
+
if (!STACK_NAME_PATTERN.test(name) || name.length > STACK_NAME_MAX) {
|
|
35
|
+
throw new errors_js_1.CliError(`Invalid stack name "${value}". Use letters, digits and hyphens, starting with a letter (max ${STACK_NAME_MAX} characters).`);
|
|
36
|
+
}
|
|
37
|
+
return name;
|
|
38
|
+
}
|
|
39
|
+
function defaultStackName(projectName, environment) {
|
|
40
|
+
return (0, environments_js_1.stackNameFor)(projectName, environment).slice(0, STACK_NAME_MAX).replace(/-+$/, "");
|
|
41
|
+
}
|
|
42
|
+
async function collectConfigureAnswers(manifest, options) {
|
|
43
|
+
const environment = (0, environments_js_1.resolveEnvironmentName)(manifest, options.env);
|
|
44
|
+
const existing = manifest.environments?.list?.[environment];
|
|
45
|
+
let profile = options.profile?.trim() || undefined;
|
|
46
|
+
let region = parseRegion(options.region);
|
|
47
|
+
let stackName = parseStackName(options.stackName);
|
|
48
|
+
const envCredentials = (0, aws_cli_js_1.hasEnvCredentials)();
|
|
49
|
+
const profiles = (0, aws_cli_js_1.listAwsProfiles)();
|
|
50
|
+
const awsCli = (0, aws_cli_js_1.isAwsCliAvailable)();
|
|
51
|
+
// Resolution order for each field: explicit flag, then what this project already
|
|
52
|
+
// recorded for the environment, then the shell, then the AWS config file.
|
|
53
|
+
const profileDefault = profile ?? existing?.profile ?? (0, aws_cli_js_1.envProfile)() ?? (profiles.includes("default") ? "default" : undefined);
|
|
54
|
+
const regionDefault = region ??
|
|
55
|
+
existing?.region ??
|
|
56
|
+
(0, aws_cli_js_1.envRegion)() ??
|
|
57
|
+
(awsCli ? (0, aws_cli_js_1.configuredRegion)(profileDefault) : undefined);
|
|
58
|
+
const stackNameDefault = stackName ?? existing?.stackName ?? defaultStackName(manifest.name, environment);
|
|
59
|
+
if (!process.stdin.isTTY) {
|
|
60
|
+
if (!profileDefault && !envCredentials) {
|
|
61
|
+
throw new errors_js_1.CliError('Non-interactive configure needs "--profile <name>", or AWS credentials in the environment.');
|
|
62
|
+
}
|
|
63
|
+
if (!regionDefault) {
|
|
64
|
+
throw new errors_js_1.CliError('Non-interactive configure needs "--region <region>", AWS_REGION, or a region in your AWS config.');
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
environment,
|
|
68
|
+
region: regionDefault,
|
|
69
|
+
profile: profileDefault,
|
|
70
|
+
stackName: stackNameDefault,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const { input, select } = await import("@inquirer/prompts");
|
|
74
|
+
if (profile === undefined) {
|
|
75
|
+
const choices = [
|
|
76
|
+
...profiles.map((name) => ({
|
|
77
|
+
name: name === profileDefault ? `${name} (current)` : name,
|
|
78
|
+
value: name,
|
|
79
|
+
})),
|
|
80
|
+
{
|
|
81
|
+
name: envCredentials
|
|
82
|
+
? "Environment variables (detected)"
|
|
83
|
+
: "Environment variables / instance role",
|
|
84
|
+
value: exports.ENVIRONMENT_PROFILE,
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
if (profiles.length === 0) {
|
|
88
|
+
profile = exports.ENVIRONMENT_PROFILE;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
profile = await select({
|
|
92
|
+
message: "AWS credentials:",
|
|
93
|
+
choices,
|
|
94
|
+
default: profileDefault ?? exports.ENVIRONMENT_PROFILE,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const selectedProfile = profile === exports.ENVIRONMENT_PROFILE ? undefined : profile;
|
|
99
|
+
region ?? (region = parseRegion(await input({
|
|
100
|
+
message: "AWS region:",
|
|
101
|
+
default: regionDefault ??
|
|
102
|
+
(awsCli ? (0, aws_cli_js_1.configuredRegion)(selectedProfile) : undefined) ??
|
|
103
|
+
"us-east-1",
|
|
104
|
+
required: true,
|
|
105
|
+
})));
|
|
106
|
+
stackName ?? (stackName = parseStackName(await input({
|
|
107
|
+
message: "CloudFormation stack name:",
|
|
108
|
+
default: stackNameDefault,
|
|
109
|
+
required: true,
|
|
110
|
+
})));
|
|
111
|
+
return {
|
|
112
|
+
environment,
|
|
113
|
+
region: region,
|
|
114
|
+
profile: selectedProfile,
|
|
115
|
+
stackName: stackName,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type { EnvVarDef, EnvVarSource, EnvironmentConfig, ProjectManifest as ConfigurableManifest, } from "../../core/environments.js";
|
|
2
|
+
export { DEFAULT_ENVIRONMENT, MANIFEST_FILE, } from "../../core/environments.js";
|
|
3
|
+
export interface ConfigureOptions {
|
|
4
|
+
env?: string;
|
|
5
|
+
profile?: string;
|
|
6
|
+
region?: string;
|
|
7
|
+
stackName?: string;
|
|
8
|
+
skipVerify?: boolean;
|
|
9
|
+
setCredentials?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ConfigureAnswers {
|
|
12
|
+
environment: string;
|
|
13
|
+
region: string;
|
|
14
|
+
profile?: string;
|
|
15
|
+
stackName: string;
|
|
16
|
+
}
|
|
17
|
+
export interface AwsIdentity {
|
|
18
|
+
account: string;
|
|
19
|
+
arn: string;
|
|
20
|
+
}
|
|
21
|
+
export interface IdentityResult {
|
|
22
|
+
identity?: AwsIdentity;
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MANIFEST_FILE = exports.DEFAULT_ENVIRONMENT = void 0;
|
|
4
|
+
var environments_js_1 = require("../../core/environments.js");
|
|
5
|
+
Object.defineProperty(exports, "DEFAULT_ENVIRONMENT", { enumerable: true, get: function () { return environments_js_1.DEFAULT_ENVIRONMENT; } });
|
|
6
|
+
Object.defineProperty(exports, "MANIFEST_FILE", { enumerable: true, get: function () { return environments_js_1.MANIFEST_FILE; } });
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deployAction = deployAction;
|
|
4
|
+
const credentials_js_1 = require("../../core/credentials.js");
|
|
5
|
+
const errors_js_1 = require("../../core/errors.js");
|
|
6
|
+
const is_ci_js_1 = require("../../core/is-ci.js");
|
|
7
|
+
const logger_js_1 = require("../../core/logger.js");
|
|
8
|
+
const environments_js_1 = require("../../core/environments.js");
|
|
9
|
+
const aws_cli_js_1 = require("../configure/aws-cli.js");
|
|
10
|
+
const parameters_js_1 = require("../env/parameters.js");
|
|
11
|
+
const templates_js_1 = require("../env/templates.js");
|
|
12
|
+
const sam_cli_js_1 = require("../run/sam-cli.js");
|
|
13
|
+
const sam_deploy_js_1 = require("./sam-deploy.js");
|
|
14
|
+
const scope_js_1 = require("./scope.js");
|
|
15
|
+
// The physical Lambda names the deploy will touch, so the plan names real AWS
|
|
16
|
+
// resources rather than template logical ids.
|
|
17
|
+
function plannedFunctionNames(manifest, environment, scope) {
|
|
18
|
+
return (0, scope_js_1.applicationsOf)(manifest)
|
|
19
|
+
.filter((app) => scope.kind === "all" || app.name === scope.service)
|
|
20
|
+
.flatMap((app) => app.functions ?? [])
|
|
21
|
+
.filter((fn) => scope.kind !== "function" || fn.name === scope.functionName)
|
|
22
|
+
.map((fn) => (0, environments_js_1.functionNameFor)(manifest.name, environment, fn.name));
|
|
23
|
+
}
|
|
24
|
+
async function confirmDeploy(manifest, environment, stackName, region, functionNames, scope) {
|
|
25
|
+
logger_js_1.logger.info(`\nAbout to deploy ${scope.label} of "${manifest.name}" to AWS.`);
|
|
26
|
+
logger_js_1.logger.info(` ${environments_js_1.APP_ENVIRONMENT_KEY}: ${environment}`);
|
|
27
|
+
logger_js_1.logger.info(` stack: ${stackName}`);
|
|
28
|
+
logger_js_1.logger.info(` region: ${region}`);
|
|
29
|
+
logger_js_1.logger.info(` functions: ${functionNames.length}`);
|
|
30
|
+
for (const name of functionNames) {
|
|
31
|
+
logger_js_1.logger.info(` ${name}`);
|
|
32
|
+
}
|
|
33
|
+
logger_js_1.logger.info(scope.kind === "all"
|
|
34
|
+
? "\nThis creates real AWS resources in your account and they cost money."
|
|
35
|
+
: "\nThis updates the code of those functions in the stack that is already deployed.");
|
|
36
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
37
|
+
return confirm({ message: `Deploy to "${environment}"?`, default: false });
|
|
38
|
+
}
|
|
39
|
+
async function deployAction(options) {
|
|
40
|
+
const cwd = process.cwd();
|
|
41
|
+
const manifest = (0, environments_js_1.readManifest)(cwd, "slskit deploy");
|
|
42
|
+
const environment = (0, environments_js_1.resolveEnvironmentName)(manifest, options.env);
|
|
43
|
+
const config = (0, environments_js_1.requireEnvironment)(manifest, environment);
|
|
44
|
+
if (!config.region) {
|
|
45
|
+
throw new errors_js_1.CliError(`Environment "${environment}" has no AWS region yet.\nRun: slskit configure --env ${environment}`);
|
|
46
|
+
}
|
|
47
|
+
const { profile } = (0, credentials_js_1.resolveAwsProfile)({
|
|
48
|
+
flag: options.profile,
|
|
49
|
+
stored: config.profile,
|
|
50
|
+
});
|
|
51
|
+
const target = {
|
|
52
|
+
stackName: config.stackName,
|
|
53
|
+
region: config.region,
|
|
54
|
+
profile,
|
|
55
|
+
};
|
|
56
|
+
// A variable can appear just by being typed into a .env file, so the templates are
|
|
57
|
+
// brought back in step before anything is uploaded.
|
|
58
|
+
const rewritten = (0, templates_js_1.syncTemplates)(cwd, manifest);
|
|
59
|
+
if (rewritten.length > 0) {
|
|
60
|
+
logger_js_1.logger.info(`Updated ${rewritten.length} template(s) for the current variables.`);
|
|
61
|
+
}
|
|
62
|
+
// Resolved before anything runs so a missing secret fails here, not mid-deploy.
|
|
63
|
+
const overrides = (0, parameters_js_1.toCliArguments)((0, parameters_js_1.parameterOverrides)(cwd, manifest, environment));
|
|
64
|
+
// Checked before anything slower: without --yes a non-interactive deploy can
|
|
65
|
+
// never proceed, so that is the most useful thing to say first.
|
|
66
|
+
if (!options.yes && !(0, is_ci_js_1.isInteractive)()) {
|
|
67
|
+
throw new errors_js_1.CliError(`Deploying creates real AWS resources. Re-run with --yes to deploy "${environment}" non-interactively.`);
|
|
68
|
+
}
|
|
69
|
+
const scope = await (0, scope_js_1.resolveDeployScope)(manifest, options);
|
|
70
|
+
const functionNames = plannedFunctionNames(manifest, environment, scope);
|
|
71
|
+
if (!options.skipVerify) {
|
|
72
|
+
if (!(0, aws_cli_js_1.isAwsCliAvailable)()) {
|
|
73
|
+
throw new errors_js_1.CliError('The AWS CLI is required to verify credentials before deploying.\nInstall it, or pass --skip-verify to deploy without checking.');
|
|
74
|
+
}
|
|
75
|
+
const { identity, error } = (0, aws_cli_js_1.getCallerIdentity)(target.profile, target.region);
|
|
76
|
+
if (!identity) {
|
|
77
|
+
throw new errors_js_1.CliError(`Could not verify AWS credentials for ${target.profile ? `profile "${target.profile}"` : "the environment"} in ${target.region}.\n ${error ?? "sts get-caller-identity failed."}\n\nFix them with: slskit configure --env ${environment} --set-credentials`);
|
|
78
|
+
}
|
|
79
|
+
logger_js_1.logger.info(`\nDeploying as ${identity.arn}`);
|
|
80
|
+
logger_js_1.logger.info(` account: ${identity.account}`);
|
|
81
|
+
}
|
|
82
|
+
// A code sync updates functions inside an existing stack; it cannot create one,
|
|
83
|
+
// and the error sam gives for a missing stack does not say so.
|
|
84
|
+
if (scope.kind !== "all" && !options.skipVerify && !(0, sam_deploy_js_1.stackExists)(target)) {
|
|
85
|
+
throw new errors_js_1.CliError(`Stack "${target.stackName}" does not exist yet, so there is nothing to update.\nDeploy the whole project first: slskit deploy ${environment} --all`);
|
|
86
|
+
}
|
|
87
|
+
if (!options.yes) {
|
|
88
|
+
const proceed = await confirmDeploy(manifest, environment, target.stackName, target.region, functionNames, scope);
|
|
89
|
+
if (!proceed) {
|
|
90
|
+
logger_js_1.logger.info("\nNothing was deployed.");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
await (0, sam_cli_js_1.ensureSamCliInstalled)();
|
|
95
|
+
// sam sync builds what it needs itself, so a separate build would only be repeated work.
|
|
96
|
+
if (scope.kind === "all") {
|
|
97
|
+
if (options.build !== false) {
|
|
98
|
+
(0, sam_cli_js_1.samBuild)(cwd);
|
|
99
|
+
}
|
|
100
|
+
(0, sam_deploy_js_1.samDeploy)(cwd, target, overrides, Boolean(options.guided));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
(0, sam_deploy_js_1.samSyncCode)(cwd, target, (0, scope_js_1.resourceIdsFor)(manifest, scope), overrides);
|
|
104
|
+
}
|
|
105
|
+
logger_js_1.logger.info(`\nDeployed ${scope.label} of "${manifest.name}" to "${environment}".`);
|
|
106
|
+
logger_js_1.logger.info(` stack: ${target.stackName}`);
|
|
107
|
+
logger_js_1.logger.info(` region: ${target.region}`);
|
|
108
|
+
const outputs = (0, sam_deploy_js_1.stackOutputs)(target);
|
|
109
|
+
if (outputs.length > 0) {
|
|
110
|
+
logger_js_1.logger.info("\nEndpoints:");
|
|
111
|
+
for (const output of outputs) {
|
|
112
|
+
logger_js_1.logger.info(` ${output.key}: ${output.value}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerDeployCommand = registerDeployCommand;
|
|
4
|
+
const run_js_1 = require("../../core/run.js");
|
|
5
|
+
function registerDeployCommand(program) {
|
|
6
|
+
program
|
|
7
|
+
.command("deploy")
|
|
8
|
+
.description("Deploy to AWS — the whole project, one service, or a single function")
|
|
9
|
+
.argument("[environment]", "Environment to deploy (example: dev, production) — defaults to the default environment")
|
|
10
|
+
.option("-e, --env <name>", "Environment to deploy")
|
|
11
|
+
.option("-s, --service <name>", "Deploy every function in one service")
|
|
12
|
+
.option("--function <name>", "Deploy one function on its own")
|
|
13
|
+
.option("--all", "Deploy the whole project without asking what to deploy", false)
|
|
14
|
+
.option("--profile <name>", "AWS named profile (overrides slskit.json for this run)")
|
|
15
|
+
.option("--no-build", 'Skip "sam build" before deploying')
|
|
16
|
+
.option("-y, --yes", "Skip the confirmation prompt", false)
|
|
17
|
+
.option("--skip-verify", "Deploy without checking the credentials first", false)
|
|
18
|
+
.option("--guided", 'Run "sam deploy --guided" instead of the managed defaults', false)
|
|
19
|
+
.action((environment, options) => (0, run_js_1.runCommand)(async () => {
|
|
20
|
+
const { deployAction } = await import("./action.js");
|
|
21
|
+
await deployAction({ ...options, env: environment ?? options.env });
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StackOutput } from "./types.js";
|
|
2
|
+
export interface DeployTarget {
|
|
3
|
+
stackName: string;
|
|
4
|
+
region: string;
|
|
5
|
+
profile?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function samDeploy(cwd: string, target: DeployTarget, parameterOverrides: string[], guided: boolean): void;
|
|
8
|
+
export declare function samSyncCode(cwd: string, target: DeployTarget, resourceIds: string[], parameterOverrides: string[]): void;
|
|
9
|
+
export declare function stackExists(target: DeployTarget): boolean;
|
|
10
|
+
export declare function stackOutputs(target: DeployTarget): StackOutput[];
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.samDeploy = samDeploy;
|
|
4
|
+
exports.samSyncCode = samSyncCode;
|
|
5
|
+
exports.stackExists = stackExists;
|
|
6
|
+
exports.stackOutputs = stackOutputs;
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const errors_js_1 = require("../../core/errors.js");
|
|
9
|
+
const logger_js_1 = require("../../core/logger.js");
|
|
10
|
+
const USE_SHELL = process.platform === "win32";
|
|
11
|
+
// Nested stacks are AWS::Serverless::Application, which CloudFormation will only
|
|
12
|
+
// expand when this capability is granted; without it the deploy fails outright.
|
|
13
|
+
const CAPABILITIES = ["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"];
|
|
14
|
+
function targetArgs(target) {
|
|
15
|
+
const args = ["--stack-name", target.stackName, "--region", target.region];
|
|
16
|
+
if (target.profile) {
|
|
17
|
+
args.push("--profile", target.profile);
|
|
18
|
+
}
|
|
19
|
+
return args;
|
|
20
|
+
}
|
|
21
|
+
function samDeploy(cwd, target, parameterOverrides, guided) {
|
|
22
|
+
const args = ["deploy", ...targetArgs(target), "--capabilities", ...CAPABILITIES];
|
|
23
|
+
if (guided) {
|
|
24
|
+
args.push("--guided");
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Creates and reuses a managed bucket so a first deploy needs no manual setup,
|
|
28
|
+
// and an unchanged stack is a success rather than an error.
|
|
29
|
+
args.push("--resolve-s3", "--no-confirm-changeset", "--no-fail-on-empty-changeset");
|
|
30
|
+
}
|
|
31
|
+
if (parameterOverrides.length > 0) {
|
|
32
|
+
args.push("--parameter-overrides", ...parameterOverrides);
|
|
33
|
+
}
|
|
34
|
+
// Values are deliberately not logged: overrides carry secrets.
|
|
35
|
+
const overrideNote = parameterOverrides.length > 0
|
|
36
|
+
? ` --parameter-overrides (${parameterOverrides.length} value${parameterOverrides.length === 1 ? "" : "s"})`
|
|
37
|
+
: "";
|
|
38
|
+
logger_js_1.logger.info(`\n> sam deploy --stack-name ${target.stackName} --region ${target.region}${target.profile ? ` --profile ${target.profile}` : ""}${overrideNote}\n`);
|
|
39
|
+
const result = (0, node_child_process_1.spawnSync)("sam", args, { cwd, stdio: "inherit", shell: USE_SHELL });
|
|
40
|
+
if (result.error) {
|
|
41
|
+
throw new errors_js_1.CliError(`Failed to run sam deploy: ${result.error.message}`);
|
|
42
|
+
}
|
|
43
|
+
if (result.status !== 0) {
|
|
44
|
+
throw new errors_js_1.CliError(`sam deploy exited with code ${result.status ?? 1}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// A code-only update of the functions already in a deployed stack. Much faster than
|
|
48
|
+
// a full deploy, but it can only change code: routes, memory and variables live in
|
|
49
|
+
// the template, so those still need a whole-project deploy.
|
|
50
|
+
function samSyncCode(cwd, target, resourceIds, parameterOverrides) {
|
|
51
|
+
const args = ["sync", "--code", ...targetArgs(target)];
|
|
52
|
+
for (const resourceId of resourceIds) {
|
|
53
|
+
args.push("--resource-id", resourceId);
|
|
54
|
+
}
|
|
55
|
+
if (parameterOverrides.length > 0) {
|
|
56
|
+
args.push("--parameter-overrides", ...parameterOverrides);
|
|
57
|
+
}
|
|
58
|
+
logger_js_1.logger.info(`\n> sam sync --code --stack-name ${target.stackName} ${resourceIds
|
|
59
|
+
.map((id) => `--resource-id ${id}`)
|
|
60
|
+
.join(" ")}\n`);
|
|
61
|
+
const result = (0, node_child_process_1.spawnSync)("sam", args, { cwd, stdio: "inherit", shell: USE_SHELL });
|
|
62
|
+
if (result.error) {
|
|
63
|
+
throw new errors_js_1.CliError(`Failed to run sam sync: ${result.error.message}`);
|
|
64
|
+
}
|
|
65
|
+
if (result.status !== 0) {
|
|
66
|
+
throw new errors_js_1.CliError(`sam sync exited with code ${result.status ?? 1}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// A code sync updates functions in place, so there has to be a stack holding them.
|
|
70
|
+
function stackExists(target) {
|
|
71
|
+
const args = [
|
|
72
|
+
"cloudformation",
|
|
73
|
+
"describe-stacks",
|
|
74
|
+
"--stack-name",
|
|
75
|
+
target.stackName,
|
|
76
|
+
"--region",
|
|
77
|
+
target.region,
|
|
78
|
+
"--query",
|
|
79
|
+
"Stacks[0].StackStatus",
|
|
80
|
+
"--output",
|
|
81
|
+
"text",
|
|
82
|
+
];
|
|
83
|
+
if (target.profile) {
|
|
84
|
+
args.push("--profile", target.profile);
|
|
85
|
+
}
|
|
86
|
+
const result = (0, node_child_process_1.spawnSync)("aws", args, { encoding: "utf8", shell: USE_SHELL });
|
|
87
|
+
return !result.error && result.status === 0;
|
|
88
|
+
}
|
|
89
|
+
// Read back through the AWS CLI rather than parsing sam's output, which changes
|
|
90
|
+
// shape between versions.
|
|
91
|
+
function stackOutputs(target) {
|
|
92
|
+
const args = [
|
|
93
|
+
"cloudformation",
|
|
94
|
+
"describe-stacks",
|
|
95
|
+
"--stack-name",
|
|
96
|
+
target.stackName,
|
|
97
|
+
"--region",
|
|
98
|
+
target.region,
|
|
99
|
+
"--query",
|
|
100
|
+
"Stacks[0].Outputs",
|
|
101
|
+
"--output",
|
|
102
|
+
"json",
|
|
103
|
+
];
|
|
104
|
+
if (target.profile) {
|
|
105
|
+
args.push("--profile", target.profile);
|
|
106
|
+
}
|
|
107
|
+
const result = (0, node_child_process_1.spawnSync)("aws", args, { encoding: "utf8", shell: USE_SHELL });
|
|
108
|
+
if (result.error || result.status !== 0) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(result.stdout);
|
|
113
|
+
if (!Array.isArray(parsed)) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
return parsed
|
|
117
|
+
.filter((entry) => entry.OutputKey && entry.OutputValue)
|
|
118
|
+
.map((entry) => ({
|
|
119
|
+
key: entry.OutputKey,
|
|
120
|
+
value: entry.OutputValue,
|
|
121
|
+
description: entry.Description,
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { allScope, applicationsOf, functionScope, serviceScope } from "../../core/scope.js";
|
|
2
|
+
import type { Scope, ScopeOptions } from "../../core/scope.js";
|
|
3
|
+
import type { ProjectManifest } from "../../core/environments.js";
|
|
4
|
+
export { allScope, applicationsOf, functionScope, serviceScope };
|
|
5
|
+
export type { Scope as DeployScope };
|
|
6
|
+
export declare function resourceIdsFor(manifest: ProjectManifest, scope: Scope): string[];
|
|
7
|
+
export declare function resolveDeployScope(manifest: ProjectManifest, options: ScopeOptions & {
|
|
8
|
+
yes?: boolean;
|
|
9
|
+
}): Promise<Scope>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serviceScope = exports.functionScope = exports.applicationsOf = exports.allScope = void 0;
|
|
4
|
+
exports.resourceIdsFor = resourceIdsFor;
|
|
5
|
+
exports.resolveDeployScope = resolveDeployScope;
|
|
6
|
+
const resources_js_1 = require("../run/resources.js");
|
|
7
|
+
const scope_js_1 = require("../../core/scope.js");
|
|
8
|
+
Object.defineProperty(exports, "allScope", { enumerable: true, get: function () { return scope_js_1.allScope; } });
|
|
9
|
+
Object.defineProperty(exports, "applicationsOf", { enumerable: true, get: function () { return scope_js_1.applicationsOf; } });
|
|
10
|
+
Object.defineProperty(exports, "functionScope", { enumerable: true, get: function () { return scope_js_1.functionScope; } });
|
|
11
|
+
Object.defineProperty(exports, "serviceScope", { enumerable: true, get: function () { return scope_js_1.serviceScope; } });
|
|
12
|
+
// Which template resources a scoped deploy syncs. Deploys always go through the
|
|
13
|
+
// nested service stacks, so every id is qualified by its stack.
|
|
14
|
+
function resourceIdsFor(manifest, scope) {
|
|
15
|
+
if (scope.kind === "all") {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
return (0, scope_js_1.applicationsOf)(manifest)
|
|
19
|
+
.filter((app) => app.name === scope.service)
|
|
20
|
+
.flatMap((app) => app.functions ?? [])
|
|
21
|
+
.filter((fn) => scope.kind !== "function" || fn.name === scope.functionName)
|
|
22
|
+
.map((fn) => (0, resources_js_1.resourceIdFor)(scope.service, fn.name));
|
|
23
|
+
}
|
|
24
|
+
async function resolveDeployScope(manifest, options) {
|
|
25
|
+
const fromFlags = (0, scope_js_1.scopeFromFlags)(manifest, options);
|
|
26
|
+
if (fromFlags) {
|
|
27
|
+
return fromFlags;
|
|
28
|
+
}
|
|
29
|
+
// Without a TTY there is nobody to ask, and --yes already means "no questions".
|
|
30
|
+
if (options.yes || !process.stdin.isTTY) {
|
|
31
|
+
return scope_js_1.allScope;
|
|
32
|
+
}
|
|
33
|
+
return (0, scope_js_1.promptForScope)(manifest, "deploy", "Everything — the whole project");
|
|
34
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DeployOptions {
|
|
2
|
+
/** Deploy every function in one service. */
|
|
3
|
+
service?: string;
|
|
4
|
+
/** Deploy one function on its own. */
|
|
5
|
+
function?: string;
|
|
6
|
+
/** Skip the scope prompt and deploy the whole project. */
|
|
7
|
+
all?: boolean;
|
|
8
|
+
env?: string;
|
|
9
|
+
/** Overrides the profile stored in slskit.json for this run only. */
|
|
10
|
+
profile?: string;
|
|
11
|
+
build?: boolean;
|
|
12
|
+
yes?: boolean;
|
|
13
|
+
skipVerify?: boolean;
|
|
14
|
+
guided?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface StackOutput {
|
|
17
|
+
key: string;
|
|
18
|
+
value: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { stackNameFor } from "../../core/environments.js";
|
|
2
|
+
import type { EnvAddOptions, EnvRemoveOptions, EnvSetOptions, EnvUnsetOptions, EnvVarsOptions } from "./types.js";
|
|
3
|
+
export declare function envListAction(): Promise<void>;
|
|
4
|
+
export declare function envAddAction(name: string, options: EnvAddOptions): Promise<void>;
|
|
5
|
+
export declare function envUseAction(name: string): Promise<void>;
|
|
6
|
+
export declare function envRemoveAction(name: string, options: EnvRemoveOptions): Promise<void>;
|
|
7
|
+
export declare function envSetAction(assignment: string, options: EnvSetOptions): Promise<void>;
|
|
8
|
+
export declare function envUnsetAction(key: string, options: EnvUnsetOptions): Promise<void>;
|
|
9
|
+
export declare function envVarsAction(options: EnvVarsOptions): Promise<void>;
|
|
10
|
+
export declare function seedDotenv(cwd: string, environment: string): string;
|
|
11
|
+
export { stackNameFor };
|