@stacksjs/ts-cloud 0.5.2 → 0.5.4
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/dist/bin/cli.js +480 -480
- package/dist/index.js +58 -14
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -26359,9 +26359,12 @@ function composeServerlessAppTemplate(opts) {
|
|
|
26359
26359
|
};
|
|
26360
26360
|
}
|
|
26361
26361
|
if (hasVpc && needsDataVpc) {
|
|
26362
|
+
if (!app.vpc?.id)
|
|
26363
|
+
throw new Error("serverless app: data services (elasticache / aurora-serverless / rdsProxy / efs) need app.vpc.id (the VPC id) so the managed security group is created in the right VPC.");
|
|
26362
26364
|
resources.DataSecurityGroup = {
|
|
26363
26365
|
Type: "AWS::EC2::SecurityGroup",
|
|
26364
26366
|
Properties: {
|
|
26367
|
+
VpcId: app.vpc.id,
|
|
26365
26368
|
GroupDescription: `${slug}-${environment} serverless data access`,
|
|
26366
26369
|
SecurityGroupIngress: [{ IpProtocol: "-1", CidrIp: "10.0.0.0/8" }]
|
|
26367
26370
|
}
|
|
@@ -26442,6 +26445,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
26442
26445
|
Engine: "aurora-mysql",
|
|
26443
26446
|
EngineMode: "provisioned",
|
|
26444
26447
|
DBClusterIdentifier: `${slug}-${environment}-db`,
|
|
26448
|
+
DatabaseName: "app",
|
|
26445
26449
|
MasterUsername: Fn2.sub("{{resolve:secretsmanager:${DbSecret}:SecretString:username}}"),
|
|
26446
26450
|
MasterUserPassword: Fn2.sub("{{resolve:secretsmanager:${DbSecret}:SecretString:password}}"),
|
|
26447
26451
|
ServerlessV2ScalingConfiguration: { MinCapacity: 0.5, MaxCapacity: 4 },
|
|
@@ -26488,6 +26492,15 @@ function composeServerlessAppTemplate(opts) {
|
|
|
26488
26492
|
RequireTLS: false
|
|
26489
26493
|
}
|
|
26490
26494
|
};
|
|
26495
|
+
resources.DbProxyTargetGroup = {
|
|
26496
|
+
Type: "AWS::RDS::DBProxyTargetGroup",
|
|
26497
|
+
DependsOn: ["DbInstance"],
|
|
26498
|
+
Properties: {
|
|
26499
|
+
DBProxyName: Fn2.ref("DbProxy"),
|
|
26500
|
+
TargetGroupName: "default",
|
|
26501
|
+
DBClusterIdentifiers: [Fn2.ref("DbCluster")]
|
|
26502
|
+
}
|
|
26503
|
+
};
|
|
26491
26504
|
outputs.DbProxyEndpoint = { Description: "RDS Proxy endpoint", Value: Fn2.getAtt("DbProxy", "Endpoint") };
|
|
26492
26505
|
}
|
|
26493
26506
|
outputs.HttpFunctionName = { Description: "HTTP function name", Value: Fn2.ref("HttpFunction") };
|
|
@@ -57435,7 +57448,7 @@ var exports_serverless_image = {};
|
|
|
57435
57448
|
__export(exports_serverless_image, {
|
|
57436
57449
|
buildAndPushServerlessImage: () => buildAndPushServerlessImage
|
|
57437
57450
|
});
|
|
57438
|
-
import { execFileSync as execFileSync3
|
|
57451
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
57439
57452
|
import { mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "node:fs";
|
|
57440
57453
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
57441
57454
|
import { dirname as dirname7, join as join15 } from "node:path";
|
|
@@ -57497,12 +57510,17 @@ async function buildAndPushServerlessImage(opts) {
|
|
|
57497
57510
|
throw new Error(`could not resolve ECR repository URI for ${repository}`);
|
|
57498
57511
|
const imageUri = `${repoUri}:${tag}`;
|
|
57499
57512
|
step2("docker build");
|
|
57500
|
-
execFileSync3("docker", ["build", "--platform", platform, "-t", imageUri, stage], { stdio: "inherit" });
|
|
57501
|
-
step2("ecr
|
|
57502
|
-
const
|
|
57503
|
-
|
|
57504
|
-
|
|
57505
|
-
|
|
57513
|
+
execFileSync3("docker", ["build", "--platform", platform, "--provenance=false", "--sbom=false", "-t", imageUri, stage], { stdio: "inherit" });
|
|
57514
|
+
step2("ecr auth + push");
|
|
57515
|
+
const auth = await ecr2.getAuthorizationToken();
|
|
57516
|
+
const data = auth.authorizationData?.[0];
|
|
57517
|
+
if (!data?.authorizationToken || !data.proxyEndpoint)
|
|
57518
|
+
throw new Error("failed to get an ECR authorization token");
|
|
57519
|
+
const registry = data.proxyEndpoint.replace(/^https?:\/\//, "");
|
|
57520
|
+
const dockerConfig = join15(stage, ".docker");
|
|
57521
|
+
mkdirSync7(dockerConfig, { recursive: true });
|
|
57522
|
+
writeFileSync10(join15(dockerConfig, "config.json"), JSON.stringify({ auths: { [registry]: { auth: data.authorizationToken } } }));
|
|
57523
|
+
execFileSync3("docker", ["push", imageUri], { stdio: "inherit", env: { ...process.env, DOCKER_CONFIG: dockerConfig } });
|
|
57506
57524
|
info(`Image: ${imageUri}`);
|
|
57507
57525
|
return { imageUri, tag, handlers };
|
|
57508
57526
|
} finally {
|
|
@@ -83326,7 +83344,7 @@ async function deployServerlessApp(config6, environment, opts = {}) {
|
|
|
83326
83344
|
step(`Creating artifact bucket ${artifactBucket}`);
|
|
83327
83345
|
await s32.createBucket(artifactBucket);
|
|
83328
83346
|
}
|
|
83329
|
-
const exists = await s32.headObject(artifactBucket, key).then(() =>
|
|
83347
|
+
const exists = await s32.headObject(artifactBucket, key).then((r) => r !== null).catch(() => false);
|
|
83330
83348
|
if (exists) {
|
|
83331
83349
|
info("Artifact already uploaded — reusing");
|
|
83332
83350
|
} else {
|
|
@@ -83339,7 +83357,20 @@ async function deployServerlessApp(config6, environment, opts = {}) {
|
|
|
83339
83357
|
const composed = composeServerlessAppTemplate({ config: config6, environment, app, handlers, runtimeLayers });
|
|
83340
83358
|
const templateBody = JSON.stringify(composed.template);
|
|
83341
83359
|
const capabilities = ["CAPABILITY_NAMED_IAM"];
|
|
83342
|
-
const
|
|
83360
|
+
const status = await cfn.describeStacks({ stackName }).then((r) => r.Stacks[0]?.StackStatus).catch(() => {
|
|
83361
|
+
return;
|
|
83362
|
+
});
|
|
83363
|
+
let stackExists = status !== undefined;
|
|
83364
|
+
if (status && /DELETE_IN_PROGRESS/.test(status)) {
|
|
83365
|
+
step("Waiting for an in-progress stack delete to finish");
|
|
83366
|
+
await cfn.waitForStack(stackName, "stack-delete-complete").catch(() => {});
|
|
83367
|
+
stackExists = false;
|
|
83368
|
+
} else if (status && /(?:ROLLBACK_COMPLETE|REVIEW_IN_PROGRESS|CREATE_FAILED)/.test(status)) {
|
|
83369
|
+
step(`Deleting unusable stack (status ${status}) before recreate`);
|
|
83370
|
+
await cfn.deleteStack(stackName);
|
|
83371
|
+
await cfn.waitForStack(stackName, "stack-delete-complete").catch(() => {});
|
|
83372
|
+
stackExists = false;
|
|
83373
|
+
}
|
|
83343
83374
|
step(stackExists ? "Updating infrastructure stack" : "Creating infrastructure stack");
|
|
83344
83375
|
try {
|
|
83345
83376
|
if (stackExists) {
|
|
@@ -83372,6 +83403,19 @@ async function deployServerlessApp(config6, environment, opts = {}) {
|
|
|
83372
83403
|
}
|
|
83373
83404
|
step("Resolving secrets");
|
|
83374
83405
|
const secrets = await resolveSecrets(app, region);
|
|
83406
|
+
if (app.database?.connection === "aurora-serverless") {
|
|
83407
|
+
try {
|
|
83408
|
+
const sm = new SecretsManagerClient(region);
|
|
83409
|
+
const v = await sm.getSecretValue({ SecretId: `${slug}/${environment}/db` });
|
|
83410
|
+
const creds = JSON.parse(v.SecretString ?? "{}");
|
|
83411
|
+
if (creds.username)
|
|
83412
|
+
secrets.DB_USERNAME = creds.username;
|
|
83413
|
+
if (creds.password)
|
|
83414
|
+
secrets.DB_PASSWORD = creds.password;
|
|
83415
|
+
} catch (err) {
|
|
83416
|
+
warn(`Could not resolve Aurora DB credentials: ${err.message}`);
|
|
83417
|
+
}
|
|
83418
|
+
}
|
|
83375
83419
|
if (!await s32.bucketExists(artifactBucket)) {
|
|
83376
83420
|
await s32.createBucket(artifactBucket);
|
|
83377
83421
|
}
|
|
@@ -83571,7 +83615,7 @@ function planServices(services) {
|
|
|
83571
83615
|
const packages = [];
|
|
83572
83616
|
const names = [];
|
|
83573
83617
|
if (enabled(services.mysql)) {
|
|
83574
|
-
packages.push("mysql.com");
|
|
83618
|
+
packages.push(typeof services.mysql === "object" && services.mysql.version ? `mysql.com@${services.mysql.version}` : "mysql.com@8.0.43");
|
|
83575
83619
|
names.push("mysql");
|
|
83576
83620
|
} else if (enabled(services.mariadb)) {
|
|
83577
83621
|
packages.push(PANTRY_PACKAGES.mariadb);
|
|
@@ -84772,7 +84816,7 @@ await init_dist2();
|
|
|
84772
84816
|
import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
|
|
84773
84817
|
import { homedir as homedir7 } from "node:os";
|
|
84774
84818
|
import { join as join18 } from "node:path";
|
|
84775
|
-
import { execSync as
|
|
84819
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
84776
84820
|
|
|
84777
84821
|
// src/drivers/hetzner/client.ts
|
|
84778
84822
|
var DEFAULT_API_URL = "https://api.hetzner.cloud/v1";
|
|
@@ -85708,7 +85752,7 @@ class HetznerDriver {
|
|
|
85708
85752
|
let lastErr;
|
|
85709
85753
|
while (Date.now() - start < sshTimeoutMs) {
|
|
85710
85754
|
try {
|
|
85711
|
-
|
|
85755
|
+
execSync3(`ssh ${this.sshBaseArgs(host, ["-o", "ConnectTimeout=5"]).map((a) => `"${a.replace(/"/g, "\\\"")}"`).join(" ")} true`, {
|
|
85712
85756
|
stdio: "pipe",
|
|
85713
85757
|
maxBuffer: SSH_MAX_BUFFER
|
|
85714
85758
|
});
|
|
@@ -85768,7 +85812,7 @@ ${out}`);
|
|
|
85768
85812
|
];
|
|
85769
85813
|
}
|
|
85770
85814
|
scpToHost(host, localPath, remotePath) {
|
|
85771
|
-
|
|
85815
|
+
execSync3([
|
|
85772
85816
|
"scp",
|
|
85773
85817
|
"-i",
|
|
85774
85818
|
this.sshPrivateKeyPath,
|
|
@@ -85781,7 +85825,7 @@ ${out}`);
|
|
|
85781
85825
|
}
|
|
85782
85826
|
sshExec(host, script) {
|
|
85783
85827
|
const escaped = script.replace(/'/g, `'\\''`);
|
|
85784
|
-
return
|
|
85828
|
+
return execSync3(`ssh ${this.sshBaseArgs(host).map((a) => `"${a.replace(/"/g, "\\\"")}"`).join(" ")} '${escaped}'`, {
|
|
85785
85829
|
encoding: "utf8",
|
|
85786
85830
|
stdio: ["pipe", "pipe", "pipe"],
|
|
85787
85831
|
maxBuffer: SSH_MAX_BUFFER
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/ts-cloud",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.4",
|
|
5
5
|
"description": "A lightweight, performant infrastructure-as-code library and CLI for deploying both server-based (EC2) and serverless applications.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"test": "bun test"
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
|
-
"@ts-cloud/aws-types": "0.5.
|
|
93
|
-
"@ts-cloud/core": "0.5.
|
|
92
|
+
"@ts-cloud/aws-types": "0.5.4",
|
|
93
|
+
"@ts-cloud/core": "0.5.4",
|
|
94
94
|
"@stacksjs/ts-xml": "^0.1.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|