@awsless/cli 0.0.5 → 0.0.7
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.js
CHANGED
|
@@ -3894,9 +3894,9 @@ var createLambdaFunction = (parentGroup, ctx, ns, id, local) => {
|
|
|
3894
3894
|
const policy = new aws4.iam.RolePolicy(group, "policy", {
|
|
3895
3895
|
role: role.name,
|
|
3896
3896
|
name: "lambda-policy",
|
|
3897
|
-
policy: new Output3(statementDeps, async (
|
|
3897
|
+
policy: new Output3(statementDeps, async (resolve2) => {
|
|
3898
3898
|
const list3 = await resolveInputs(statements);
|
|
3899
|
-
|
|
3899
|
+
resolve2(
|
|
3900
3900
|
JSON.stringify({
|
|
3901
3901
|
Version: "2012-10-17",
|
|
3902
3902
|
Statement: list3.map((statement) => ({
|
|
@@ -4645,9 +4645,9 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, props) => {
|
|
|
4645
4645
|
const policy = new aws8.iam.RolePolicy(group, "policy", {
|
|
4646
4646
|
role: role.name,
|
|
4647
4647
|
name: "lambda-policy",
|
|
4648
|
-
policy: new Output4(statementDeps, async (
|
|
4648
|
+
policy: new Output4(statementDeps, async (resolve2) => {
|
|
4649
4649
|
const list3 = await resolveInputs2(statements);
|
|
4650
|
-
|
|
4650
|
+
resolve2(
|
|
4651
4651
|
JSON.stringify({
|
|
4652
4652
|
Version: "2012-10-17",
|
|
4653
4653
|
Statement: list3.map((statement) => ({
|
|
@@ -7233,11 +7233,14 @@ var iconFeature = defineFeature({
|
|
|
7233
7233
|
}
|
|
7234
7234
|
});
|
|
7235
7235
|
|
|
7236
|
-
// src/feature/
|
|
7237
|
-
import { Group as Group26 } from "@terraforge/core";
|
|
7236
|
+
// src/feature/job/index.ts
|
|
7237
|
+
import { Group as Group26, Output as Output7, resolveInputs as resolveInputs4, findInputDeps as findInputDeps4 } from "@terraforge/core";
|
|
7238
7238
|
import { aws as aws27 } from "@terraforge/aws";
|
|
7239
|
+
import { camelCase as camelCase8 } from "change-case";
|
|
7240
|
+
import { relative as relative8 } from "path";
|
|
7239
7241
|
|
|
7240
|
-
// src/feature/
|
|
7242
|
+
// src/feature/job/util.ts
|
|
7243
|
+
import { createHash as createHash4 } from "crypto";
|
|
7241
7244
|
import { toDays as toDays9, toSeconds as toSeconds9 } from "@awsless/duration";
|
|
7242
7245
|
import { stringify } from "@awsless/json";
|
|
7243
7246
|
import { toMebibytes as toMebibytes5 } from "@awsless/size";
|
|
@@ -7245,46 +7248,79 @@ import { generateFileHash as generateFileHash2 } from "@awsless/ts-file-cache";
|
|
|
7245
7248
|
import { aws as aws26 } from "@terraforge/aws";
|
|
7246
7249
|
import { Group as Group25, Output as Output6, findInputDeps as findInputDeps3, resolveInputs as resolveInputs3 } from "@terraforge/core";
|
|
7247
7250
|
import { constantCase as constantCase12, pascalCase as pascalCase3 } from "change-case";
|
|
7248
|
-
import { createHash as createHash4 } from "crypto";
|
|
7249
7251
|
import deepmerge4 from "deepmerge";
|
|
7250
|
-
import { join as join18 } from "path";
|
|
7251
7252
|
|
|
7252
|
-
// src/feature/
|
|
7253
|
+
// src/feature/job/build/executable.ts
|
|
7253
7254
|
import { createHash as createHash3 } from "crypto";
|
|
7254
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
7255
|
-
import { join as join17 } from "path";
|
|
7256
|
-
var
|
|
7255
|
+
import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
7256
|
+
import { join as join17, resolve } from "path";
|
|
7257
|
+
var buildJobExecutable = async (input, outputPath, architecture) => {
|
|
7257
7258
|
const filePath = join17(outputPath, "program");
|
|
7259
|
+
const wrapperPath = join17(outputPath, "wrapper.ts");
|
|
7260
|
+
const handlerPath = resolve(input);
|
|
7261
|
+
await writeFile3(
|
|
7262
|
+
wrapperPath,
|
|
7263
|
+
`import { parse } from '@awsless/json'
|
|
7264
|
+
import { getObject } from '@awsless/s3'
|
|
7265
|
+
import handler from '${handlerPath}'
|
|
7266
|
+
|
|
7267
|
+
let payload = process.env.PAYLOAD ? parse(process.env.PAYLOAD) : undefined
|
|
7268
|
+
if (typeof payload === 'string' && payload.startsWith('s3://')) {
|
|
7269
|
+
const url = new URL(payload)
|
|
7270
|
+
const response = await getObject({ bucket: url.hostname, key: url.pathname.slice(1) })
|
|
7271
|
+
if (!response) throw new Error('Failed to fetch payload from S3: ' + payload)
|
|
7272
|
+
payload = parse(await response.body.transformToString())
|
|
7273
|
+
}
|
|
7274
|
+
await handler(payload)
|
|
7275
|
+
`
|
|
7276
|
+
);
|
|
7258
7277
|
const target = architecture === "x86_64" ? "bun-linux-x64" : "bun-linux-arm64";
|
|
7259
7278
|
let result;
|
|
7260
7279
|
try {
|
|
7261
7280
|
result = await Bun.build({
|
|
7262
|
-
entrypoints: [
|
|
7281
|
+
entrypoints: [wrapperPath],
|
|
7263
7282
|
compile: {
|
|
7264
7283
|
target,
|
|
7265
7284
|
outfile: filePath
|
|
7266
7285
|
},
|
|
7267
|
-
target: "bun"
|
|
7286
|
+
target: "bun",
|
|
7287
|
+
loader: {
|
|
7288
|
+
".md": "text",
|
|
7289
|
+
".txt": "text",
|
|
7290
|
+
".html": "text",
|
|
7291
|
+
".css": "text",
|
|
7292
|
+
".yaml": "text",
|
|
7293
|
+
".yml": "text",
|
|
7294
|
+
".xml": "text",
|
|
7295
|
+
".csv": "text",
|
|
7296
|
+
".svg": "text",
|
|
7297
|
+
".png": "file",
|
|
7298
|
+
".jpg": "file",
|
|
7299
|
+
".jpeg": "file",
|
|
7300
|
+
".gif": "file",
|
|
7301
|
+
".webp": "file",
|
|
7302
|
+
".wasm": "file"
|
|
7303
|
+
}
|
|
7268
7304
|
});
|
|
7269
7305
|
} catch (error) {
|
|
7270
7306
|
throw new ExpectedError(
|
|
7271
|
-
`
|
|
7307
|
+
`Job executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
|
|
7272
7308
|
);
|
|
7273
7309
|
}
|
|
7274
7310
|
if (!result.success) {
|
|
7275
|
-
throw new ExpectedError(`
|
|
7311
|
+
throw new ExpectedError(`Job executable build failed:
|
|
7276
7312
|
${result.logs?.map((log35) => log35.message).join("\n")}`);
|
|
7277
7313
|
}
|
|
7278
7314
|
const file = await readFile4(filePath);
|
|
7279
7315
|
return {
|
|
7280
|
-
hash: createHash3("sha1").update(file).update(
|
|
7316
|
+
hash: createHash3("sha1").update(file).update(target).digest("hex"),
|
|
7281
7317
|
file
|
|
7282
7318
|
};
|
|
7283
7319
|
};
|
|
7284
7320
|
|
|
7285
|
-
// src/feature/
|
|
7286
|
-
var
|
|
7287
|
-
const group = new Group25(parentGroup, "
|
|
7321
|
+
// src/feature/job/util.ts
|
|
7322
|
+
var createFargateJob = (parentGroup, ctx, ns, id, local) => {
|
|
7323
|
+
const group = new Group25(parentGroup, "job", ns);
|
|
7288
7324
|
const name = formatLocalResourceName({
|
|
7289
7325
|
appName: ctx.app.name,
|
|
7290
7326
|
stackName: ctx.stack.name,
|
|
@@ -7292,13 +7328,13 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7292
7328
|
resourceName: id
|
|
7293
7329
|
});
|
|
7294
7330
|
const shortName = shortId(`${ctx.app.name}:${ctx.stack.name}:${ns}:${id}:${ctx.appId}`);
|
|
7295
|
-
const props = deepmerge4(ctx.appConfig.defaults.
|
|
7296
|
-
const image2 = props.image ||
|
|
7297
|
-
ctx.registerBuild("
|
|
7298
|
-
const fingerprint = await generateFileHash2(workspace, local.code.file);
|
|
7331
|
+
const props = deepmerge4(ctx.appConfig.defaults.job, local);
|
|
7332
|
+
const image2 = props.image || "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal";
|
|
7333
|
+
ctx.registerBuild("job", name, async (build3, { workspace }) => {
|
|
7334
|
+
const fingerprint = [await generateFileHash2(workspace, local.code.file), props.architecture].join(":");
|
|
7299
7335
|
return build3(fingerprint, async (write) => {
|
|
7300
|
-
const temp = await createTempFolder(`
|
|
7301
|
-
const executable = await
|
|
7336
|
+
const temp = await createTempFolder(`job--${name}`);
|
|
7337
|
+
const executable = await buildJobExecutable(local.code.file, temp.path, props.architecture);
|
|
7302
7338
|
await Promise.all([
|
|
7303
7339
|
//
|
|
7304
7340
|
write("HASH", executable.hash),
|
|
@@ -7311,10 +7347,10 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7311
7347
|
});
|
|
7312
7348
|
});
|
|
7313
7349
|
const code = new aws26.s3.BucketObject(group, "code", {
|
|
7314
|
-
bucket: ctx.shared.get("
|
|
7350
|
+
bucket: ctx.shared.get("job", "bucket-name"),
|
|
7315
7351
|
key: name,
|
|
7316
|
-
source: relativePath(getBuildPath("
|
|
7317
|
-
sourceHash: $file(getBuildPath("
|
|
7352
|
+
source: relativePath(getBuildPath("job", name, "program")),
|
|
7353
|
+
sourceHash: $file(getBuildPath("job", name, "HASH"))
|
|
7318
7354
|
});
|
|
7319
7355
|
const executionRole = new aws26.iam.Role(group, "execution-role", {
|
|
7320
7356
|
name: shortId(`${shortName}:execution-role`),
|
|
@@ -7360,8 +7396,8 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7360
7396
|
Statement: [
|
|
7361
7397
|
{
|
|
7362
7398
|
Effect: pascalCase3("allow"),
|
|
7363
|
-
Action: ["s3:
|
|
7364
|
-
Resource: `arn:aws:s3:::${bucket}/${key}`
|
|
7399
|
+
Action: ["s3:GetObject", "s3:HeadObject"],
|
|
7400
|
+
Resource: [`arn:aws:s3:::${bucket}/${key}`, `arn:aws:s3:::${bucket}/payloads/*`]
|
|
7365
7401
|
}
|
|
7366
7402
|
]
|
|
7367
7403
|
});
|
|
@@ -7378,9 +7414,9 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7378
7414
|
const policy = new aws26.iam.RolePolicy(group, "policy", {
|
|
7379
7415
|
role: role.name,
|
|
7380
7416
|
name: "task-policy",
|
|
7381
|
-
policy: new Output6(statementDeps, async (
|
|
7417
|
+
policy: new Output6(statementDeps, async (resolve2) => {
|
|
7382
7418
|
const list3 = await resolveInputs3(statements);
|
|
7383
|
-
|
|
7419
|
+
resolve2(
|
|
7384
7420
|
JSON.stringify({
|
|
7385
7421
|
Version: "2012-10-17",
|
|
7386
7422
|
Statement: list3.map((statement) => ({
|
|
@@ -7405,7 +7441,6 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7405
7441
|
if (props.log.retention && props.log.retention.value > 0n) {
|
|
7406
7442
|
logGroup = new aws26.cloudwatch.LogGroup(group, "log", {
|
|
7407
7443
|
name: `/aws/ecs/${name}`,
|
|
7408
|
-
// name: `/aws/lambda/${name}`,
|
|
7409
7444
|
retentionInDays: toDays9(props.log.retention)
|
|
7410
7445
|
});
|
|
7411
7446
|
if (ctx.shared.has("on-error-log", "subscriber-arn")) {
|
|
@@ -7424,6 +7459,63 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7424
7459
|
};
|
|
7425
7460
|
const variables = {};
|
|
7426
7461
|
const variableDeps = /* @__PURE__ */ new Set();
|
|
7462
|
+
const taskDependsOn = [code];
|
|
7463
|
+
const accessPoint = props.persistentStorage ? (() => {
|
|
7464
|
+
const persistentStorageSecurityGroup = new aws26.security.Group(
|
|
7465
|
+
group,
|
|
7466
|
+
"persistent-storage-security-group",
|
|
7467
|
+
{
|
|
7468
|
+
name: shortId(`${shortName}:storage-sg`),
|
|
7469
|
+
description: name,
|
|
7470
|
+
vpcId: ctx.shared.get("vpc", "id"),
|
|
7471
|
+
revokeRulesOnDelete: true,
|
|
7472
|
+
tags
|
|
7473
|
+
}
|
|
7474
|
+
);
|
|
7475
|
+
new aws26.vpc.SecurityGroupIngressRule(group, "persistent-storage-ingress-rule", {
|
|
7476
|
+
securityGroupId: persistentStorageSecurityGroup.id,
|
|
7477
|
+
referencedSecurityGroupId: ctx.shared.get("job", "security-group-id"),
|
|
7478
|
+
description: "Allow NFS traffic from this job",
|
|
7479
|
+
ipProtocol: "tcp",
|
|
7480
|
+
fromPort: 2049,
|
|
7481
|
+
toPort: 2049,
|
|
7482
|
+
tags
|
|
7483
|
+
});
|
|
7484
|
+
const fileSystem = new aws26.efs.FileSystem(group, "persistent-storage-file-system", {
|
|
7485
|
+
encrypted: true,
|
|
7486
|
+
performanceMode: "generalPurpose",
|
|
7487
|
+
throughputMode: "elastic",
|
|
7488
|
+
tags: {
|
|
7489
|
+
...tags,
|
|
7490
|
+
Name: `${name}-storage`
|
|
7491
|
+
}
|
|
7492
|
+
});
|
|
7493
|
+
for (const [index, subnetId] of ctx.shared.get("vpc", "public-subnets").entries()) {
|
|
7494
|
+
const mountTarget = new aws26.efs.MountTarget(group, `mount-target-${index + 1}`, {
|
|
7495
|
+
fileSystemId: fileSystem.id,
|
|
7496
|
+
subnetId,
|
|
7497
|
+
securityGroups: [persistentStorageSecurityGroup.id]
|
|
7498
|
+
});
|
|
7499
|
+
taskDependsOn.push(mountTarget);
|
|
7500
|
+
}
|
|
7501
|
+
const accessPoint2 = new aws26.efs.AccessPoint(group, "access-point", {
|
|
7502
|
+
fileSystemId: fileSystem.id,
|
|
7503
|
+
rootDirectory: {
|
|
7504
|
+
path: `/jobs/${name}`,
|
|
7505
|
+
creationInfo: {
|
|
7506
|
+
ownerUid: 0,
|
|
7507
|
+
ownerGid: 0,
|
|
7508
|
+
permissions: "755"
|
|
7509
|
+
}
|
|
7510
|
+
},
|
|
7511
|
+
tags
|
|
7512
|
+
});
|
|
7513
|
+
taskDependsOn.push(fileSystem, accessPoint2);
|
|
7514
|
+
return {
|
|
7515
|
+
accessPoint: accessPoint2,
|
|
7516
|
+
fileSystem
|
|
7517
|
+
};
|
|
7518
|
+
})() : void 0;
|
|
7427
7519
|
const task2 = new aws26.ecs.TaskDefinition(
|
|
7428
7520
|
group,
|
|
7429
7521
|
"task",
|
|
@@ -7440,13 +7532,512 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7440
7532
|
operatingSystemFamily: "LINUX"
|
|
7441
7533
|
},
|
|
7442
7534
|
trackLatest: true,
|
|
7443
|
-
|
|
7535
|
+
...accessPoint && {
|
|
7536
|
+
volume: [
|
|
7537
|
+
{
|
|
7538
|
+
name: "persistent-storage",
|
|
7539
|
+
efsVolumeConfiguration: {
|
|
7540
|
+
fileSystemId: accessPoint.fileSystem.id,
|
|
7541
|
+
transitEncryption: "ENABLED",
|
|
7542
|
+
authorizationConfig: {
|
|
7543
|
+
accessPointId: accessPoint.accessPoint.id
|
|
7544
|
+
}
|
|
7545
|
+
}
|
|
7546
|
+
}
|
|
7547
|
+
]
|
|
7548
|
+
},
|
|
7549
|
+
containerDefinitions: new Output6(variableDeps, async (resolve2) => {
|
|
7444
7550
|
const data = await resolveInputs3(variables);
|
|
7445
7551
|
const { s3Bucket, s3Key } = await resolveInputs3({
|
|
7446
7552
|
s3Bucket: code.bucket,
|
|
7447
7553
|
s3Key: code.key
|
|
7448
7554
|
});
|
|
7449
|
-
|
|
7555
|
+
resolve2(
|
|
7556
|
+
JSON.stringify([
|
|
7557
|
+
{
|
|
7558
|
+
name: `container-${id}`,
|
|
7559
|
+
essential: true,
|
|
7560
|
+
image: image2,
|
|
7561
|
+
workingDirectory: "/usr/app",
|
|
7562
|
+
entryPoint: ["sh", "-c"],
|
|
7563
|
+
command: [
|
|
7564
|
+
[
|
|
7565
|
+
...props.startupCommand?.length ? (() => {
|
|
7566
|
+
const setupHash = createHash4("sha1").update(props.startupCommand.join(" && ")).digest("hex").slice(0, 8);
|
|
7567
|
+
return [`if [ ! -f /root/.setup-done-${setupHash} ]; then ${props.startupCommand.join(" && ")} && touch /root/.setup-done-${setupHash}; fi`];
|
|
7568
|
+
})() : [],
|
|
7569
|
+
`if [ "$(cat /root/.code-hash 2>/dev/null)" != "$CODE_HASH" ]; then command -v aws >/dev/null 2>&1 || dnf install -y awscli && aws s3 cp s3://${s3Bucket}/${s3Key} /root/program.tmp && mv /root/program.tmp /root/program && chmod +x /root/program && echo "$CODE_HASH" > /root/.code-hash; fi`,
|
|
7570
|
+
`exec timeout --kill-after=10 ${toSeconds9(props.timeout)} /root/program`
|
|
7571
|
+
].join(" && ")
|
|
7572
|
+
],
|
|
7573
|
+
environment: Object.entries(data).map(([name2, value]) => ({
|
|
7574
|
+
name: name2,
|
|
7575
|
+
value
|
|
7576
|
+
})),
|
|
7577
|
+
...accessPoint && {
|
|
7578
|
+
mountPoints: [
|
|
7579
|
+
{
|
|
7580
|
+
sourceVolume: "persistent-storage",
|
|
7581
|
+
containerPath: "/root"
|
|
7582
|
+
}
|
|
7583
|
+
]
|
|
7584
|
+
},
|
|
7585
|
+
...logGroup && {
|
|
7586
|
+
logConfiguration: {
|
|
7587
|
+
logDriver: "awslogs",
|
|
7588
|
+
options: {
|
|
7589
|
+
"awslogs-group": `/aws/ecs/${name}`,
|
|
7590
|
+
"awslogs-region": ctx.appConfig.region,
|
|
7591
|
+
"awslogs-stream-prefix": "ecs",
|
|
7592
|
+
mode: "non-blocking"
|
|
7593
|
+
}
|
|
7594
|
+
}
|
|
7595
|
+
}
|
|
7596
|
+
}
|
|
7597
|
+
])
|
|
7598
|
+
);
|
|
7599
|
+
}),
|
|
7600
|
+
tags
|
|
7601
|
+
},
|
|
7602
|
+
{
|
|
7603
|
+
replaceOnChanges: [
|
|
7604
|
+
"containerDefinitions",
|
|
7605
|
+
"cpu",
|
|
7606
|
+
"memory",
|
|
7607
|
+
"runtimePlatform",
|
|
7608
|
+
"executionRoleArn",
|
|
7609
|
+
"taskRoleArn"
|
|
7610
|
+
],
|
|
7611
|
+
dependsOn: taskDependsOn
|
|
7612
|
+
}
|
|
7613
|
+
);
|
|
7614
|
+
ctx.onEnv((name2, value) => {
|
|
7615
|
+
variables[name2] = value;
|
|
7616
|
+
for (const dep of findInputDeps3([value])) {
|
|
7617
|
+
variableDeps.add(dep);
|
|
7618
|
+
}
|
|
7619
|
+
});
|
|
7620
|
+
variables.APP = ctx.appConfig.name;
|
|
7621
|
+
variables.APP_ID = ctx.appId;
|
|
7622
|
+
variables.AWS_ACCOUNT_ID = ctx.accountId;
|
|
7623
|
+
variables.STACK = ctx.stackConfig.name;
|
|
7624
|
+
variables.CODE_HASH = code.sourceHash;
|
|
7625
|
+
variables.JOB_CONFIG_HASH = createHash4("sha1").update(stringify(props)).digest("hex");
|
|
7626
|
+
if (props.environment) {
|
|
7627
|
+
for (const [key, value] of Object.entries(props.environment)) {
|
|
7628
|
+
variables[key] = value;
|
|
7629
|
+
}
|
|
7630
|
+
}
|
|
7631
|
+
if (ctx.appConfig.defaults.job.permissions) {
|
|
7632
|
+
statements.push(...ctx.appConfig.defaults.job.permissions);
|
|
7633
|
+
}
|
|
7634
|
+
if ("permissions" in local && local.permissions) {
|
|
7635
|
+
statements.push(...local.permissions);
|
|
7636
|
+
}
|
|
7637
|
+
return { name, task: task2, policy, code, group };
|
|
7638
|
+
};
|
|
7639
|
+
|
|
7640
|
+
// src/feature/job/index.ts
|
|
7641
|
+
var typeGenCode10 = `
|
|
7642
|
+
import type { Mock } from 'vitest'
|
|
7643
|
+
|
|
7644
|
+
type Func = (...args: any[]) => any
|
|
7645
|
+
|
|
7646
|
+
type Invoke<N extends string, F extends Func> = Parameters<F> extends []
|
|
7647
|
+
? InvokeWithoutPayload<N, F>
|
|
7648
|
+
: unknown extends Parameters<F>[0]
|
|
7649
|
+
? InvokeWithoutPayload<N, F>
|
|
7650
|
+
: InvokeWithPayload<N, F>
|
|
7651
|
+
|
|
7652
|
+
type InvokeWithPayload<Name extends string, F extends Func> = {
|
|
7653
|
+
readonly name: Name
|
|
7654
|
+
(payload: Parameters<F>[0]): Promise<{ taskArn: string | undefined }>
|
|
7655
|
+
}
|
|
7656
|
+
|
|
7657
|
+
type InvokeWithoutPayload<Name extends string, F extends Func> = {
|
|
7658
|
+
readonly name: Name
|
|
7659
|
+
(payload?: Parameters<F>[0]): Promise<{ taskArn: string | undefined }>
|
|
7660
|
+
}
|
|
7661
|
+
|
|
7662
|
+
type MockHandle<F extends Func> = (payload: Parameters<F>[0]) => void | Promise<void>
|
|
7663
|
+
type MockBuilder<F extends Func> = (handle?: MockHandle<F>) => void
|
|
7664
|
+
type MockObject<F extends Func> = Mock<Parameters<F>, ReturnType<F>>
|
|
7665
|
+
`;
|
|
7666
|
+
var jobFeature = defineFeature({
|
|
7667
|
+
name: "job",
|
|
7668
|
+
async onTypeGen(ctx) {
|
|
7669
|
+
const types2 = new TypeFile("awsless");
|
|
7670
|
+
const resources2 = new TypeObject(1);
|
|
7671
|
+
const mocks = new TypeObject(1);
|
|
7672
|
+
const mockResponses = new TypeObject(1);
|
|
7673
|
+
for (const stack of ctx.stackConfigs) {
|
|
7674
|
+
const resource = new TypeObject(2);
|
|
7675
|
+
const mock = new TypeObject(2);
|
|
7676
|
+
const mockResponse = new TypeObject(2);
|
|
7677
|
+
for (const [name, props] of Object.entries(stack.jobs || {})) {
|
|
7678
|
+
const varName = camelCase8(`${stack.name}-${name}`);
|
|
7679
|
+
const funcName = formatLocalResourceName({
|
|
7680
|
+
appName: ctx.appConfig.name,
|
|
7681
|
+
stackName: stack.name,
|
|
7682
|
+
resourceType: "job",
|
|
7683
|
+
resourceName: name
|
|
7684
|
+
});
|
|
7685
|
+
if ("file" in props.code) {
|
|
7686
|
+
const relFile = relative8(directories.types, props.code.file);
|
|
7687
|
+
types2.addImport(varName, relFile);
|
|
7688
|
+
resource.addType(name, `Invoke<'${funcName}', typeof ${varName}>`);
|
|
7689
|
+
mock.addType(name, `MockBuilder<typeof ${varName}>`);
|
|
7690
|
+
mockResponse.addType(name, `MockObject<typeof ${varName}>`);
|
|
7691
|
+
}
|
|
7692
|
+
}
|
|
7693
|
+
mocks.addType(stack.name, mock);
|
|
7694
|
+
resources2.addType(stack.name, resource);
|
|
7695
|
+
mockResponses.addType(stack.name, mockResponse);
|
|
7696
|
+
}
|
|
7697
|
+
types2.addCode(typeGenCode10);
|
|
7698
|
+
types2.addInterface("JobResources", resources2);
|
|
7699
|
+
types2.addInterface("JobMock", mocks);
|
|
7700
|
+
types2.addInterface("JobMockResponse", mockResponses);
|
|
7701
|
+
await ctx.write("job.d.ts", types2, true);
|
|
7702
|
+
},
|
|
7703
|
+
onBefore(ctx) {
|
|
7704
|
+
const group = new Group26(ctx.base, "job", "asset");
|
|
7705
|
+
const bucket = new aws27.s3.Bucket(group, "bucket", {
|
|
7706
|
+
bucket: formatGlobalResourceName({
|
|
7707
|
+
appName: ctx.app.name,
|
|
7708
|
+
resourceType: "job",
|
|
7709
|
+
resourceName: "assets",
|
|
7710
|
+
postfix: ctx.appId
|
|
7711
|
+
}),
|
|
7712
|
+
forceDestroy: true,
|
|
7713
|
+
lifecycleRule: [
|
|
7714
|
+
{
|
|
7715
|
+
id: "expire-payloads",
|
|
7716
|
+
enabled: true,
|
|
7717
|
+
prefix: "payloads/",
|
|
7718
|
+
expiration: { days: 1 }
|
|
7719
|
+
}
|
|
7720
|
+
]
|
|
7721
|
+
});
|
|
7722
|
+
ctx.shared.set("job", "bucket-name", bucket.bucket);
|
|
7723
|
+
},
|
|
7724
|
+
onApp(ctx) {
|
|
7725
|
+
const found = ctx.stackConfigs.filter((stack) => {
|
|
7726
|
+
return Object.keys(stack.jobs ?? {}).length > 0;
|
|
7727
|
+
});
|
|
7728
|
+
if (found.length === 0) {
|
|
7729
|
+
return;
|
|
7730
|
+
}
|
|
7731
|
+
const group = new Group26(ctx.base, "job", "cluster");
|
|
7732
|
+
const cluster = new aws27.ecs.Cluster(
|
|
7733
|
+
group,
|
|
7734
|
+
"cluster",
|
|
7735
|
+
{
|
|
7736
|
+
name: `${ctx.app.name}-job`
|
|
7737
|
+
},
|
|
7738
|
+
{
|
|
7739
|
+
replaceOnChanges: ["name"]
|
|
7740
|
+
}
|
|
7741
|
+
);
|
|
7742
|
+
ctx.shared.set("job", "cluster-name", cluster.name);
|
|
7743
|
+
ctx.shared.set("job", "cluster-arn", cluster.arn);
|
|
7744
|
+
const securityGroup = new aws27.security.Group(group, "security-group", {
|
|
7745
|
+
name: `${ctx.app.name}-job`,
|
|
7746
|
+
description: "Shared security group for jobs",
|
|
7747
|
+
vpcId: ctx.shared.get("vpc", "id"),
|
|
7748
|
+
revokeRulesOnDelete: true,
|
|
7749
|
+
tags: {
|
|
7750
|
+
APP: ctx.appConfig.name
|
|
7751
|
+
}
|
|
7752
|
+
});
|
|
7753
|
+
new aws27.vpc.SecurityGroupEgressRule(group, "egress-rule", {
|
|
7754
|
+
securityGroupId: securityGroup.id,
|
|
7755
|
+
description: "Allow all outbound traffic from jobs",
|
|
7756
|
+
ipProtocol: "-1",
|
|
7757
|
+
cidrIpv4: "0.0.0.0/0",
|
|
7758
|
+
tags: {
|
|
7759
|
+
APP: ctx.appConfig.name
|
|
7760
|
+
}
|
|
7761
|
+
});
|
|
7762
|
+
ctx.shared.set("job", "security-group-id", securityGroup.id);
|
|
7763
|
+
},
|
|
7764
|
+
onStack(ctx) {
|
|
7765
|
+
const subnets = ctx.shared.get("vpc", "public-subnets");
|
|
7766
|
+
ctx.addEnv(
|
|
7767
|
+
"JOB_SUBNETS",
|
|
7768
|
+
new Output7(new Set(findInputDeps4(subnets)), async (resolve2) => {
|
|
7769
|
+
const resolved = await resolveInputs4(subnets);
|
|
7770
|
+
resolve2(JSON.stringify(resolved));
|
|
7771
|
+
})
|
|
7772
|
+
);
|
|
7773
|
+
ctx.addEnv("JOB_SECURITY_GROUP", ctx.shared.get("job", "security-group-id"));
|
|
7774
|
+
ctx.addEnv("JOB_PAYLOAD_BUCKET", ctx.shared.get("job", "bucket-name"));
|
|
7775
|
+
const jobs = Object.entries(ctx.stackConfig.jobs ?? {});
|
|
7776
|
+
if (jobs.length === 0) return;
|
|
7777
|
+
for (const [id, props] of jobs) {
|
|
7778
|
+
const group = new Group26(ctx.stack, "job", id);
|
|
7779
|
+
createFargateJob(group, ctx, "job", id, props);
|
|
7780
|
+
}
|
|
7781
|
+
ctx.addStackPermission({
|
|
7782
|
+
actions: ["ecs:RunTask"],
|
|
7783
|
+
resources: [
|
|
7784
|
+
`arn:aws:ecs:${ctx.appConfig.region}:*:task-definition/${ctx.app.name}--${ctx.stackConfig.name}--*`
|
|
7785
|
+
]
|
|
7786
|
+
});
|
|
7787
|
+
ctx.addStackPermission({
|
|
7788
|
+
actions: ["iam:PassRole"],
|
|
7789
|
+
resources: ["*"],
|
|
7790
|
+
conditions: {
|
|
7791
|
+
StringEquals: {
|
|
7792
|
+
"iam:PassedToService": "ecs-tasks.amazonaws.com"
|
|
7793
|
+
}
|
|
7794
|
+
}
|
|
7795
|
+
});
|
|
7796
|
+
ctx.addStackPermission({
|
|
7797
|
+
actions: ["s3:PutObject"],
|
|
7798
|
+
resources: [
|
|
7799
|
+
`arn:aws:s3:::${formatGlobalResourceName({
|
|
7800
|
+
appName: ctx.app.name,
|
|
7801
|
+
resourceType: "job",
|
|
7802
|
+
resourceName: "assets",
|
|
7803
|
+
postfix: ctx.appId
|
|
7804
|
+
})}/payloads/*`
|
|
7805
|
+
]
|
|
7806
|
+
});
|
|
7807
|
+
}
|
|
7808
|
+
});
|
|
7809
|
+
|
|
7810
|
+
// src/feature/instance/index.ts
|
|
7811
|
+
import { Group as Group28 } from "@terraforge/core";
|
|
7812
|
+
import { aws as aws29 } from "@terraforge/aws";
|
|
7813
|
+
|
|
7814
|
+
// src/feature/instance/util.ts
|
|
7815
|
+
import { toDays as toDays10, toSeconds as toSeconds10 } from "@awsless/duration";
|
|
7816
|
+
import { stringify as stringify2 } from "@awsless/json";
|
|
7817
|
+
import { toMebibytes as toMebibytes6 } from "@awsless/size";
|
|
7818
|
+
import { generateFileHash as generateFileHash3 } from "@awsless/ts-file-cache";
|
|
7819
|
+
import { aws as aws28 } from "@terraforge/aws";
|
|
7820
|
+
import { Group as Group27, Output as Output8, findInputDeps as findInputDeps5, resolveInputs as resolveInputs5 } from "@terraforge/core";
|
|
7821
|
+
import { constantCase as constantCase13, pascalCase as pascalCase4 } from "change-case";
|
|
7822
|
+
import { createHash as createHash6 } from "crypto";
|
|
7823
|
+
import deepmerge5 from "deepmerge";
|
|
7824
|
+
import { join as join19 } from "path";
|
|
7825
|
+
|
|
7826
|
+
// src/feature/instance/build/executable.ts
|
|
7827
|
+
import { createHash as createHash5 } from "crypto";
|
|
7828
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
7829
|
+
import { join as join18 } from "path";
|
|
7830
|
+
var buildExecutable = async (input, outputPath, architecture) => {
|
|
7831
|
+
const filePath = join18(outputPath, "program");
|
|
7832
|
+
const target = architecture === "x86_64" ? "bun-linux-x64" : "bun-linux-arm64";
|
|
7833
|
+
let result;
|
|
7834
|
+
try {
|
|
7835
|
+
result = await Bun.build({
|
|
7836
|
+
entrypoints: [input],
|
|
7837
|
+
compile: {
|
|
7838
|
+
target,
|
|
7839
|
+
outfile: filePath
|
|
7840
|
+
},
|
|
7841
|
+
target: "bun",
|
|
7842
|
+
loader: {
|
|
7843
|
+
".md": "text",
|
|
7844
|
+
".txt": "text",
|
|
7845
|
+
".html": "text",
|
|
7846
|
+
".css": "text",
|
|
7847
|
+
".yaml": "text",
|
|
7848
|
+
".yml": "text",
|
|
7849
|
+
".xml": "text",
|
|
7850
|
+
".csv": "text",
|
|
7851
|
+
".svg": "text",
|
|
7852
|
+
".png": "file",
|
|
7853
|
+
".jpg": "file",
|
|
7854
|
+
".jpeg": "file",
|
|
7855
|
+
".gif": "file",
|
|
7856
|
+
".webp": "file",
|
|
7857
|
+
".wasm": "file"
|
|
7858
|
+
}
|
|
7859
|
+
});
|
|
7860
|
+
} catch (error) {
|
|
7861
|
+
throw new ExpectedError(
|
|
7862
|
+
`Executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
|
|
7863
|
+
);
|
|
7864
|
+
}
|
|
7865
|
+
if (!result.success) {
|
|
7866
|
+
throw new ExpectedError(`Executable build failed:
|
|
7867
|
+
${result.logs?.map((log35) => log35.message).join("\n")}`);
|
|
7868
|
+
}
|
|
7869
|
+
const file = await readFile5(filePath);
|
|
7870
|
+
return {
|
|
7871
|
+
hash: createHash5("sha1").update(file).update("x86_64").digest("hex"),
|
|
7872
|
+
file
|
|
7873
|
+
};
|
|
7874
|
+
};
|
|
7875
|
+
|
|
7876
|
+
// src/feature/instance/util.ts
|
|
7877
|
+
var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
7878
|
+
const group = new Group27(parentGroup, "instance", ns);
|
|
7879
|
+
const name = formatLocalResourceName({
|
|
7880
|
+
appName: ctx.app.name,
|
|
7881
|
+
stackName: ctx.stack.name,
|
|
7882
|
+
resourceType: ns,
|
|
7883
|
+
resourceName: id
|
|
7884
|
+
});
|
|
7885
|
+
const shortName = shortId(`${ctx.app.name}:${ctx.stack.name}:${ns}:${id}:${ctx.appId}`);
|
|
7886
|
+
const props = deepmerge5(ctx.appConfig.defaults.instance, local);
|
|
7887
|
+
const image2 = props.image || (props.architecture === "arm64" ? "public.ecr.aws/aws-cli/aws-cli:arm64" : "public.ecr.aws/aws-cli/aws-cli:amd64");
|
|
7888
|
+
ctx.registerBuild("instance", name, async (build3, { workspace }) => {
|
|
7889
|
+
const fingerprint = await generateFileHash3(workspace, local.code.file);
|
|
7890
|
+
return build3(fingerprint, async (write) => {
|
|
7891
|
+
const temp = await createTempFolder(`instance--${name}`);
|
|
7892
|
+
const executable = await buildExecutable(local.code.file, temp.path, props.architecture);
|
|
7893
|
+
await Promise.all([
|
|
7894
|
+
//
|
|
7895
|
+
write("HASH", executable.hash),
|
|
7896
|
+
write("program", executable.file),
|
|
7897
|
+
temp.delete()
|
|
7898
|
+
]);
|
|
7899
|
+
return {
|
|
7900
|
+
size: formatByteSize(executable.file.byteLength)
|
|
7901
|
+
};
|
|
7902
|
+
});
|
|
7903
|
+
});
|
|
7904
|
+
const code = new aws28.s3.BucketObject(group, "code", {
|
|
7905
|
+
bucket: ctx.shared.get("instance", "bucket-name"),
|
|
7906
|
+
key: name,
|
|
7907
|
+
source: relativePath(getBuildPath("instance", name, "program")),
|
|
7908
|
+
sourceHash: $file(getBuildPath("instance", name, "HASH"))
|
|
7909
|
+
});
|
|
7910
|
+
const executionRole = new aws28.iam.Role(group, "execution-role", {
|
|
7911
|
+
name: shortId(`${shortName}:execution-role`),
|
|
7912
|
+
description: name,
|
|
7913
|
+
assumeRolePolicy: JSON.stringify({
|
|
7914
|
+
Version: "2012-10-17",
|
|
7915
|
+
Statement: [
|
|
7916
|
+
{
|
|
7917
|
+
Effect: "Allow",
|
|
7918
|
+
Action: "sts:AssumeRole",
|
|
7919
|
+
Principal: {
|
|
7920
|
+
Service: ["ecs-tasks.amazonaws.com"]
|
|
7921
|
+
}
|
|
7922
|
+
}
|
|
7923
|
+
]
|
|
7924
|
+
}),
|
|
7925
|
+
managedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
|
|
7926
|
+
});
|
|
7927
|
+
const role = new aws28.iam.Role(
|
|
7928
|
+
group,
|
|
7929
|
+
"task-role",
|
|
7930
|
+
{
|
|
7931
|
+
name: shortId(`${shortName}:task-role`),
|
|
7932
|
+
description: name,
|
|
7933
|
+
assumeRolePolicy: JSON.stringify({
|
|
7934
|
+
Version: "2012-10-17",
|
|
7935
|
+
Statement: [
|
|
7936
|
+
{
|
|
7937
|
+
Effect: "Allow",
|
|
7938
|
+
Action: "sts:AssumeRole",
|
|
7939
|
+
Principal: {
|
|
7940
|
+
Service: ["ecs-tasks.amazonaws.com"]
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
]
|
|
7944
|
+
}),
|
|
7945
|
+
inlinePolicy: [
|
|
7946
|
+
{
|
|
7947
|
+
name: "s3-code-access",
|
|
7948
|
+
policy: $resolve([code.bucket, code.key], (bucket, key) => {
|
|
7949
|
+
return JSON.stringify({
|
|
7950
|
+
Version: "2012-10-17",
|
|
7951
|
+
Statement: [
|
|
7952
|
+
{
|
|
7953
|
+
Effect: pascalCase4("allow"),
|
|
7954
|
+
Action: ["s3:getObject", "s3:HeadObject"],
|
|
7955
|
+
Resource: `arn:aws:s3:::${bucket}/${key}`
|
|
7956
|
+
}
|
|
7957
|
+
]
|
|
7958
|
+
});
|
|
7959
|
+
})
|
|
7960
|
+
}
|
|
7961
|
+
]
|
|
7962
|
+
},
|
|
7963
|
+
{
|
|
7964
|
+
dependsOn: [code]
|
|
7965
|
+
}
|
|
7966
|
+
);
|
|
7967
|
+
const statements = [];
|
|
7968
|
+
const statementDeps = /* @__PURE__ */ new Set();
|
|
7969
|
+
const policy = new aws28.iam.RolePolicy(group, "policy", {
|
|
7970
|
+
role: role.name,
|
|
7971
|
+
name: "task-policy",
|
|
7972
|
+
policy: new Output8(statementDeps, async (resolve2) => {
|
|
7973
|
+
const list3 = await resolveInputs5(statements);
|
|
7974
|
+
resolve2(
|
|
7975
|
+
JSON.stringify({
|
|
7976
|
+
Version: "2012-10-17",
|
|
7977
|
+
Statement: list3.map((statement) => ({
|
|
7978
|
+
Effect: pascalCase4(statement.effect ?? "allow"),
|
|
7979
|
+
Action: statement.actions,
|
|
7980
|
+
Resource: statement.resources
|
|
7981
|
+
}))
|
|
7982
|
+
})
|
|
7983
|
+
);
|
|
7984
|
+
})
|
|
7985
|
+
});
|
|
7986
|
+
const addPermission = (...permissions) => {
|
|
7987
|
+
statements.push(...permissions);
|
|
7988
|
+
for (const dep of findInputDeps5(permissions)) {
|
|
7989
|
+
statementDeps.add(dep);
|
|
7990
|
+
}
|
|
7991
|
+
};
|
|
7992
|
+
ctx.onPermission((statement) => {
|
|
7993
|
+
addPermission(statement);
|
|
7994
|
+
});
|
|
7995
|
+
let logGroup;
|
|
7996
|
+
if (props.log.retention && props.log.retention.value > 0n) {
|
|
7997
|
+
logGroup = new aws28.cloudwatch.LogGroup(group, "log", {
|
|
7998
|
+
name: `/aws/ecs/${name}`,
|
|
7999
|
+
// name: `/aws/lambda/${name}`,
|
|
8000
|
+
retentionInDays: toDays10(props.log.retention)
|
|
8001
|
+
});
|
|
8002
|
+
if (ctx.shared.has("on-error-log", "subscriber-arn")) {
|
|
8003
|
+
new aws28.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
|
|
8004
|
+
name: "error-log-subscription",
|
|
8005
|
+
destinationArn: ctx.shared.get("on-error-log", "subscriber-arn"),
|
|
8006
|
+
logGroupName: logGroup.name,
|
|
8007
|
+
filterPattern
|
|
8008
|
+
});
|
|
8009
|
+
}
|
|
8010
|
+
}
|
|
8011
|
+
const tags = {
|
|
8012
|
+
APP: ctx.appConfig.name,
|
|
8013
|
+
APP_ID: ctx.appId,
|
|
8014
|
+
STACK: ctx.stackConfig.name
|
|
8015
|
+
};
|
|
8016
|
+
const variables = {};
|
|
8017
|
+
const variableDeps = /* @__PURE__ */ new Set();
|
|
8018
|
+
const task2 = new aws28.ecs.TaskDefinition(
|
|
8019
|
+
group,
|
|
8020
|
+
"task",
|
|
8021
|
+
{
|
|
8022
|
+
family: name,
|
|
8023
|
+
networkMode: "awsvpc",
|
|
8024
|
+
cpu: props.cpu,
|
|
8025
|
+
memory: toMebibytes6(props.memorySize).toString(),
|
|
8026
|
+
requiresCompatibilities: ["FARGATE"],
|
|
8027
|
+
executionRoleArn: executionRole.arn,
|
|
8028
|
+
taskRoleArn: role.arn,
|
|
8029
|
+
runtimePlatform: {
|
|
8030
|
+
cpuArchitecture: constantCase13(props.architecture),
|
|
8031
|
+
operatingSystemFamily: "LINUX"
|
|
8032
|
+
},
|
|
8033
|
+
trackLatest: true,
|
|
8034
|
+
containerDefinitions: new Output8(variableDeps, async (resolve2) => {
|
|
8035
|
+
const data = await resolveInputs5(variables);
|
|
8036
|
+
const { s3Bucket, s3Key } = await resolveInputs5({
|
|
8037
|
+
s3Bucket: code.bucket,
|
|
8038
|
+
s3Key: code.key
|
|
8039
|
+
});
|
|
8040
|
+
resolve2(
|
|
7450
8041
|
JSON.stringify([
|
|
7451
8042
|
{
|
|
7452
8043
|
name: `container-${id}`,
|
|
@@ -7497,12 +8088,12 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7497
8088
|
healthCheck: props.healthCheck ? {
|
|
7498
8089
|
command: [
|
|
7499
8090
|
"CMD-SHELL",
|
|
7500
|
-
`curl -f http://${
|
|
8091
|
+
`curl -f http://${join19("localhost", props.healthCheck.path)} || exit 1`
|
|
7501
8092
|
],
|
|
7502
|
-
interval:
|
|
8093
|
+
interval: toSeconds10(props.healthCheck.interval),
|
|
7503
8094
|
retries: props.healthCheck.retries,
|
|
7504
|
-
startPeriod:
|
|
7505
|
-
timeout:
|
|
8095
|
+
startPeriod: toSeconds10(props.healthCheck.startPeriod),
|
|
8096
|
+
timeout: toSeconds10(props.healthCheck.timeout)
|
|
7506
8097
|
} : void 0
|
|
7507
8098
|
}
|
|
7508
8099
|
])
|
|
@@ -7522,14 +8113,14 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7522
8113
|
dependsOn: [code]
|
|
7523
8114
|
}
|
|
7524
8115
|
);
|
|
7525
|
-
const securityGroup = new
|
|
8116
|
+
const securityGroup = new aws28.security.Group(group, "security-group", {
|
|
7526
8117
|
name,
|
|
7527
8118
|
description: "Security group for the instance",
|
|
7528
8119
|
vpcId: ctx.shared.get("vpc", "id"),
|
|
7529
8120
|
revokeRulesOnDelete: true,
|
|
7530
8121
|
tags
|
|
7531
8122
|
});
|
|
7532
|
-
new
|
|
8123
|
+
new aws28.vpc.SecurityGroupEgressRule(group, "egress-rule", {
|
|
7533
8124
|
securityGroupId: securityGroup.id,
|
|
7534
8125
|
description: `Allow all outbound traffic from the ${name} instance`,
|
|
7535
8126
|
ipProtocol: "-1",
|
|
@@ -7538,7 +8129,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7538
8129
|
});
|
|
7539
8130
|
const clusterName = ctx.shared.get("instance", "cluster-name");
|
|
7540
8131
|
const clusterArn = ctx.shared.get("instance", "cluster-arn");
|
|
7541
|
-
const service = new
|
|
8132
|
+
const service = new aws28.ecs.Service(
|
|
7542
8133
|
group,
|
|
7543
8134
|
"service",
|
|
7544
8135
|
{
|
|
@@ -7574,7 +8165,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7574
8165
|
replaceOnChanges: ["cluster"]
|
|
7575
8166
|
}
|
|
7576
8167
|
);
|
|
7577
|
-
new
|
|
8168
|
+
new aws28.appautoscaling.Target(
|
|
7578
8169
|
group,
|
|
7579
8170
|
"autoscaling-target",
|
|
7580
8171
|
{
|
|
@@ -7593,7 +8184,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7593
8184
|
);
|
|
7594
8185
|
ctx.onEnv((name2, value) => {
|
|
7595
8186
|
variables[name2] = value;
|
|
7596
|
-
for (const dep of
|
|
8187
|
+
for (const dep of findInputDeps5([value])) {
|
|
7597
8188
|
variableDeps.add(dep);
|
|
7598
8189
|
}
|
|
7599
8190
|
});
|
|
@@ -7602,7 +8193,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7602
8193
|
variables.AWS_ACCOUNT_ID = ctx.accountId;
|
|
7603
8194
|
variables.STACK = ctx.stackConfig.name;
|
|
7604
8195
|
variables.CODE_HASH = code.sourceHash;
|
|
7605
|
-
variables.INSTANCE_CONFIG_HASH =
|
|
8196
|
+
variables.INSTANCE_CONFIG_HASH = createHash6("sha1").update(stringify2(props)).digest("hex");
|
|
7606
8197
|
if (props.environment) {
|
|
7607
8198
|
for (const [key, value] of Object.entries(props.environment)) {
|
|
7608
8199
|
variables[key] = value;
|
|
@@ -7621,8 +8212,8 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7621
8212
|
var instanceFeature = defineFeature({
|
|
7622
8213
|
name: "instance",
|
|
7623
8214
|
onBefore(ctx) {
|
|
7624
|
-
const group = new
|
|
7625
|
-
const bucket = new
|
|
8215
|
+
const group = new Group28(ctx.base, "instance", "asset");
|
|
8216
|
+
const bucket = new aws29.s3.Bucket(group, "bucket", {
|
|
7626
8217
|
bucket: formatGlobalResourceName({
|
|
7627
8218
|
appName: ctx.app.name,
|
|
7628
8219
|
resourceType: "instance",
|
|
@@ -7640,8 +8231,8 @@ var instanceFeature = defineFeature({
|
|
|
7640
8231
|
if (found.length === 0) {
|
|
7641
8232
|
return;
|
|
7642
8233
|
}
|
|
7643
|
-
const group = new
|
|
7644
|
-
const cluster = new
|
|
8234
|
+
const group = new Group28(ctx.base, "instance", "cluster");
|
|
8235
|
+
const cluster = new aws29.ecs.Cluster(
|
|
7645
8236
|
group,
|
|
7646
8237
|
"cluster",
|
|
7647
8238
|
{
|
|
@@ -7656,18 +8247,18 @@ var instanceFeature = defineFeature({
|
|
|
7656
8247
|
},
|
|
7657
8248
|
onStack(ctx) {
|
|
7658
8249
|
for (const [id, props] of Object.entries(ctx.stackConfig.instances ?? {})) {
|
|
7659
|
-
const group = new
|
|
8250
|
+
const group = new Group28(ctx.stack, "instance", id);
|
|
7660
8251
|
createFargateTask(group, ctx, "instance", id, props);
|
|
7661
8252
|
}
|
|
7662
8253
|
}
|
|
7663
8254
|
});
|
|
7664
8255
|
|
|
7665
8256
|
// src/feature/metric/index.ts
|
|
7666
|
-
import { Group as
|
|
7667
|
-
import { aws as
|
|
7668
|
-
import { kebabCase as kebabCase8, constantCase as
|
|
7669
|
-
import { toSeconds as
|
|
7670
|
-
var
|
|
8257
|
+
import { Group as Group29 } from "@terraforge/core";
|
|
8258
|
+
import { aws as aws30 } from "@terraforge/aws";
|
|
8259
|
+
import { kebabCase as kebabCase8, constantCase as constantCase14 } from "change-case";
|
|
8260
|
+
import { toSeconds as toSeconds11 } from "@awsless/duration";
|
|
8261
|
+
var typeGenCode11 = `
|
|
7671
8262
|
import { type PutDataProps, putData, batchPutData } from '@awsless/cloudwatch'
|
|
7672
8263
|
import { type Duration } from '@awsless/duration'
|
|
7673
8264
|
import { type Size } from '@awsless/size'
|
|
@@ -7715,7 +8306,7 @@ var metricFeature = defineFeature({
|
|
|
7715
8306
|
resources2.addType(stack.name, stackResources);
|
|
7716
8307
|
}
|
|
7717
8308
|
resources2.addType("batch", "Batch");
|
|
7718
|
-
gen.addCode(
|
|
8309
|
+
gen.addCode(typeGenCode11);
|
|
7719
8310
|
gen.addInterface("MetricResources", resources2);
|
|
7720
8311
|
await ctx.write("metric.d.ts", gen, true);
|
|
7721
8312
|
},
|
|
@@ -7732,16 +8323,16 @@ var metricFeature = defineFeature({
|
|
|
7732
8323
|
}
|
|
7733
8324
|
});
|
|
7734
8325
|
for (const [id, props] of Object.entries(ctx.stackConfig.metrics ?? {})) {
|
|
7735
|
-
const group = new
|
|
7736
|
-
ctx.addEnv(`METRIC_${
|
|
8326
|
+
const group = new Group29(ctx.stack, "metric", id);
|
|
8327
|
+
ctx.addEnv(`METRIC_${constantCase14(id)}`, props.type);
|
|
7737
8328
|
for (const alarmId in props.alarms ?? []) {
|
|
7738
|
-
const alarmGroup = new
|
|
8329
|
+
const alarmGroup = new Group29(group, "alarm", alarmId);
|
|
7739
8330
|
const alarmName = kebabCase8(`${id}-${alarmId}`);
|
|
7740
8331
|
const alarmProps = props.alarms[alarmId];
|
|
7741
8332
|
let alarmAction;
|
|
7742
8333
|
let alarmLambda;
|
|
7743
8334
|
if (Array.isArray(alarmProps.trigger)) {
|
|
7744
|
-
const topic = new
|
|
8335
|
+
const topic = new aws30.sns.Topic(alarmGroup, "alarm-trigger", {
|
|
7745
8336
|
name: formatLocalResourceName({
|
|
7746
8337
|
appName: ctx.app.name,
|
|
7747
8338
|
stackName: ctx.stack.name,
|
|
@@ -7751,7 +8342,7 @@ var metricFeature = defineFeature({
|
|
|
7751
8342
|
});
|
|
7752
8343
|
alarmAction = topic.arn;
|
|
7753
8344
|
for (const email of alarmProps.trigger) {
|
|
7754
|
-
new
|
|
8345
|
+
new aws30.sns.TopicSubscription(alarmGroup, email, {
|
|
7755
8346
|
topicArn: topic.arn,
|
|
7756
8347
|
protocol: "email",
|
|
7757
8348
|
endpoint: email
|
|
@@ -7762,7 +8353,7 @@ var metricFeature = defineFeature({
|
|
|
7762
8353
|
alarmLambda = lambda;
|
|
7763
8354
|
alarmAction = lambda.arn;
|
|
7764
8355
|
}
|
|
7765
|
-
const alarm = new
|
|
8356
|
+
const alarm = new aws30.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
|
|
7766
8357
|
namespace,
|
|
7767
8358
|
metricName: kebabCase8(id),
|
|
7768
8359
|
alarmName: formatLocalResourceName({
|
|
@@ -7774,13 +8365,13 @@ var metricFeature = defineFeature({
|
|
|
7774
8365
|
alarmDescription: alarmProps.description,
|
|
7775
8366
|
statistic: alarmProps.where.stat,
|
|
7776
8367
|
threshold: alarmProps.where.value,
|
|
7777
|
-
period:
|
|
8368
|
+
period: toSeconds11(alarmProps.period),
|
|
7778
8369
|
evaluationPeriods: alarmProps.minDataPoints,
|
|
7779
8370
|
comparisonOperator: alarmProps.where.op,
|
|
7780
8371
|
alarmActions: [alarmAction]
|
|
7781
8372
|
});
|
|
7782
8373
|
if (alarmLambda) {
|
|
7783
|
-
new
|
|
8374
|
+
new aws30.lambda.Permission(alarmGroup, "permission", {
|
|
7784
8375
|
action: "lambda:InvokeFunction",
|
|
7785
8376
|
principal: "lambda.alarms.cloudwatch.amazonaws.com",
|
|
7786
8377
|
functionName: alarmLambda.functionName,
|
|
@@ -7793,10 +8384,10 @@ var metricFeature = defineFeature({
|
|
|
7793
8384
|
});
|
|
7794
8385
|
|
|
7795
8386
|
// src/feature/router/index.ts
|
|
7796
|
-
import { days as days9, seconds as seconds10, toSeconds as
|
|
7797
|
-
import { Future, Group as
|
|
7798
|
-
import { aws as
|
|
7799
|
-
import { camelCase as
|
|
8387
|
+
import { days as days9, seconds as seconds10, toSeconds as toSeconds12, years } from "@awsless/duration";
|
|
8388
|
+
import { Future, Group as Group30 } from "@terraforge/core";
|
|
8389
|
+
import { aws as aws31 } from "@terraforge/aws";
|
|
8390
|
+
import { camelCase as camelCase9, constantCase as constantCase15 } from "change-case";
|
|
7800
8391
|
|
|
7801
8392
|
// src/feature/router/router-code.ts
|
|
7802
8393
|
var getViewerRequestFunctionCode = (props) => {
|
|
@@ -8044,18 +8635,18 @@ async function handler(event) {
|
|
|
8044
8635
|
`;
|
|
8045
8636
|
|
|
8046
8637
|
// src/feature/router/index.ts
|
|
8047
|
-
import { createHash as
|
|
8638
|
+
import { createHash as createHash7 } from "crypto";
|
|
8048
8639
|
var routerFeature = defineFeature({
|
|
8049
8640
|
name: "router",
|
|
8050
8641
|
onApp(ctx) {
|
|
8051
8642
|
for (const [id, props] of Object.entries(ctx.appConfig.defaults.router ?? {})) {
|
|
8052
|
-
const group = new
|
|
8643
|
+
const group = new Group30(ctx.base, "router", id);
|
|
8053
8644
|
const name = formatGlobalResourceName({
|
|
8054
8645
|
appName: ctx.app.name,
|
|
8055
8646
|
resourceType: "router",
|
|
8056
8647
|
resourceName: id
|
|
8057
8648
|
});
|
|
8058
|
-
const routeStore = new
|
|
8649
|
+
const routeStore = new aws31.cloudfront.KeyValueStore(group, "routes", {
|
|
8059
8650
|
name,
|
|
8060
8651
|
comment: "Store for routes"
|
|
8061
8652
|
});
|
|
@@ -8085,11 +8676,11 @@ var routerFeature = defineFeature({
|
|
|
8085
8676
|
);
|
|
8086
8677
|
importedRoutes.push(importKeys);
|
|
8087
8678
|
});
|
|
8088
|
-
const cache = new
|
|
8679
|
+
const cache = new aws31.cloudfront.CachePolicy(group, "cache", {
|
|
8089
8680
|
name,
|
|
8090
|
-
minTtl:
|
|
8091
|
-
maxTtl:
|
|
8092
|
-
defaultTtl:
|
|
8681
|
+
minTtl: toSeconds12(seconds10(0)),
|
|
8682
|
+
maxTtl: toSeconds12(days9(365)),
|
|
8683
|
+
defaultTtl: toSeconds12(days9(0)),
|
|
8093
8684
|
parametersInCacheKeyAndForwardedToOrigin: {
|
|
8094
8685
|
enableAcceptEncodingBrotli: true,
|
|
8095
8686
|
enableAcceptEncodingGzip: true,
|
|
@@ -8117,10 +8708,10 @@ var routerFeature = defineFeature({
|
|
|
8117
8708
|
}
|
|
8118
8709
|
}
|
|
8119
8710
|
});
|
|
8120
|
-
const originRequest = new
|
|
8711
|
+
const originRequest = new aws31.cloudfront.OriginRequestPolicy(group, "request", {
|
|
8121
8712
|
name,
|
|
8122
8713
|
headersConfig: {
|
|
8123
|
-
headerBehavior:
|
|
8714
|
+
headerBehavior: camelCase9("all-except"),
|
|
8124
8715
|
headers: {
|
|
8125
8716
|
items: [
|
|
8126
8717
|
"host"
|
|
@@ -8135,11 +8726,11 @@ var routerFeature = defineFeature({
|
|
|
8135
8726
|
queryStringBehavior: "all"
|
|
8136
8727
|
}
|
|
8137
8728
|
});
|
|
8138
|
-
const responseHeaders = new
|
|
8729
|
+
const responseHeaders = new aws31.cloudfront.ResponseHeadersPolicy(group, "response", {
|
|
8139
8730
|
name,
|
|
8140
8731
|
corsConfig: {
|
|
8141
8732
|
originOverride: props.cors?.override ?? true,
|
|
8142
|
-
accessControlMaxAgeSec:
|
|
8733
|
+
accessControlMaxAgeSec: toSeconds12(props.cors?.maxAge ?? years(1)),
|
|
8143
8734
|
accessControlAllowHeaders: { items: props.cors?.headers ?? ["*"] },
|
|
8144
8735
|
accessControlAllowMethods: { items: props.cors?.methods ?? ["ALL"] },
|
|
8145
8736
|
accessControlAllowOrigins: { items: props.cors?.origins ?? ["*"] },
|
|
@@ -8164,7 +8755,7 @@ var routerFeature = defineFeature({
|
|
|
8164
8755
|
strictTransportSecurity: {
|
|
8165
8756
|
override: true,
|
|
8166
8757
|
preload: true,
|
|
8167
|
-
accessControlMaxAgeSec:
|
|
8758
|
+
accessControlMaxAgeSec: toSeconds12(years(1)),
|
|
8168
8759
|
includeSubdomains: true
|
|
8169
8760
|
},
|
|
8170
8761
|
xssProtection: {
|
|
@@ -8174,7 +8765,7 @@ var routerFeature = defineFeature({
|
|
|
8174
8765
|
}
|
|
8175
8766
|
}
|
|
8176
8767
|
});
|
|
8177
|
-
const viewerRequest = new
|
|
8768
|
+
const viewerRequest = new aws31.cloudfront.Function(group, "viewer-request", {
|
|
8178
8769
|
name,
|
|
8179
8770
|
runtime: `cloudfront-js-2.0`,
|
|
8180
8771
|
comment: `Viewer Request - ${name}`,
|
|
@@ -8196,7 +8787,7 @@ var routerFeature = defineFeature({
|
|
|
8196
8787
|
rateBasedStatement: {
|
|
8197
8788
|
limit: wafSettingsConfig.rateLimiter.limit,
|
|
8198
8789
|
aggregateKeyType: "IP",
|
|
8199
|
-
evaluationWindowSec:
|
|
8790
|
+
evaluationWindowSec: toSeconds12(wafSettingsConfig.rateLimiter.window)
|
|
8200
8791
|
}
|
|
8201
8792
|
},
|
|
8202
8793
|
action: {
|
|
@@ -8276,7 +8867,7 @@ var routerFeature = defineFeature({
|
|
|
8276
8867
|
}
|
|
8277
8868
|
let waf;
|
|
8278
8869
|
if (wafRules.length && wafSettingsConfig) {
|
|
8279
|
-
waf = new
|
|
8870
|
+
waf = new aws31.wafv2.WebAcl(group, "waf", {
|
|
8280
8871
|
name: `${name}-wafv2`,
|
|
8281
8872
|
scope: "CLOUDFRONT",
|
|
8282
8873
|
defaultAction: {
|
|
@@ -8286,12 +8877,12 @@ var routerFeature = defineFeature({
|
|
|
8286
8877
|
rule: wafRules,
|
|
8287
8878
|
captchaConfig: {
|
|
8288
8879
|
immunityTimeProperty: {
|
|
8289
|
-
immunityTime:
|
|
8880
|
+
immunityTime: toSeconds12(wafSettingsConfig.captchaImmunityTime)
|
|
8290
8881
|
}
|
|
8291
8882
|
},
|
|
8292
8883
|
challengeConfig: {
|
|
8293
8884
|
immunityTimeProperty: {
|
|
8294
|
-
immunityTime:
|
|
8885
|
+
immunityTime: toSeconds12(wafSettingsConfig.challengeImmunityTime)
|
|
8295
8886
|
}
|
|
8296
8887
|
},
|
|
8297
8888
|
visibilityConfig: {
|
|
@@ -8301,7 +8892,7 @@ var routerFeature = defineFeature({
|
|
|
8301
8892
|
}
|
|
8302
8893
|
});
|
|
8303
8894
|
}
|
|
8304
|
-
const distribution = new
|
|
8895
|
+
const distribution = new aws31.cloudfront.MultitenantDistribution(group, "distribution", {
|
|
8305
8896
|
tags: {
|
|
8306
8897
|
name
|
|
8307
8898
|
},
|
|
@@ -8347,7 +8938,7 @@ var routerFeature = defineFeature({
|
|
|
8347
8938
|
}
|
|
8348
8939
|
return {
|
|
8349
8940
|
errorCode: Number(errorCode),
|
|
8350
|
-
errorCachingMinTtl: item.minTTL ?
|
|
8941
|
+
errorCachingMinTtl: item.minTTL ? toSeconds12(item.minTTL) : void 0,
|
|
8351
8942
|
responseCode: item.statusCode?.toString() ?? errorCode,
|
|
8352
8943
|
responsePagePath: item.path
|
|
8353
8944
|
};
|
|
@@ -8395,11 +8986,11 @@ var routerFeature = defineFeature({
|
|
|
8395
8986
|
{
|
|
8396
8987
|
distributionId: distribution.id,
|
|
8397
8988
|
paths,
|
|
8398
|
-
version: new Future((
|
|
8989
|
+
version: new Future((resolve2) => {
|
|
8399
8990
|
$combine(...versions).then((versions2) => {
|
|
8400
8991
|
const combined = versions2.filter((v) => !!v).sort().join(",");
|
|
8401
|
-
const version =
|
|
8402
|
-
|
|
8992
|
+
const version = createHash7("sha1").update(combined).digest("hex");
|
|
8993
|
+
resolve2(version);
|
|
8403
8994
|
});
|
|
8404
8995
|
})
|
|
8405
8996
|
},
|
|
@@ -8413,10 +9004,10 @@ var routerFeature = defineFeature({
|
|
|
8413
9004
|
const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
|
|
8414
9005
|
const certificateArn = ctx.shared.entry("domain", `global-certificate-arn`, props.domain);
|
|
8415
9006
|
const zoneId = ctx.shared.entry("domain", "zone-id", props.domain);
|
|
8416
|
-
const connectionGroup = new
|
|
9007
|
+
const connectionGroup = new aws31.cloudfront.ConnectionGroup(group, "connection-group", {
|
|
8417
9008
|
name
|
|
8418
9009
|
});
|
|
8419
|
-
new
|
|
9010
|
+
new aws31.cloudfront.DistributionTenant(group, `tenant`, {
|
|
8420
9011
|
name,
|
|
8421
9012
|
enabled: true,
|
|
8422
9013
|
distributionId: distribution.id,
|
|
@@ -8424,7 +9015,7 @@ var routerFeature = defineFeature({
|
|
|
8424
9015
|
domain: [{ domain: domainName }],
|
|
8425
9016
|
customizations: [{ certificate: [{ arn: certificateArn }] }]
|
|
8426
9017
|
});
|
|
8427
|
-
new
|
|
9018
|
+
new aws31.route53.Record(group, `record`, {
|
|
8428
9019
|
zoneId,
|
|
8429
9020
|
type: "A",
|
|
8430
9021
|
name: domainName,
|
|
@@ -8434,7 +9025,7 @@ var routerFeature = defineFeature({
|
|
|
8434
9025
|
evaluateTargetHealth: false
|
|
8435
9026
|
}
|
|
8436
9027
|
});
|
|
8437
|
-
ctx.bind(`ROUTER_${
|
|
9028
|
+
ctx.bind(`ROUTER_${constantCase15(id)}_ENDPOINT`, domainName);
|
|
8438
9029
|
}
|
|
8439
9030
|
}
|
|
8440
9031
|
}
|
|
@@ -8457,7 +9048,7 @@ var features = [
|
|
|
8457
9048
|
// 5
|
|
8458
9049
|
functionFeature,
|
|
8459
9050
|
instanceFeature,
|
|
8460
|
-
|
|
9051
|
+
jobFeature,
|
|
8461
9052
|
// graphqlFeature,
|
|
8462
9053
|
configFeature,
|
|
8463
9054
|
searchFeature,
|
|
@@ -9546,14 +10137,14 @@ import wildstring4 from "wildstring";
|
|
|
9546
10137
|
|
|
9547
10138
|
// src/cli/ui/complex/run-tests.ts
|
|
9548
10139
|
import { log as log18 } from "@awsless/clui";
|
|
9549
|
-
import { mkdir as mkdir4, readFile as
|
|
9550
|
-
import { join as
|
|
10140
|
+
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
10141
|
+
import { join as join21 } from "path";
|
|
9551
10142
|
import wildstring3 from "wildstring";
|
|
9552
|
-
import { parse as parse4, stringify as
|
|
10143
|
+
import { parse as parse4, stringify as stringify3 } from "@awsless/json";
|
|
9553
10144
|
import { generateFolderHash, loadWorkspace as loadWorkspace2 } from "@awsless/ts-file-cache";
|
|
9554
10145
|
|
|
9555
10146
|
// src/test/start.ts
|
|
9556
|
-
import { dirname as dirname9, join as
|
|
10147
|
+
import { dirname as dirname9, join as join20 } from "path";
|
|
9557
10148
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
9558
10149
|
import { configDefaults } from "vitest/config";
|
|
9559
10150
|
import { startVitest } from "vitest/node";
|
|
@@ -9591,7 +10182,7 @@ var startTest = async (props) => {
|
|
|
9591
10182
|
// },
|
|
9592
10183
|
setupFiles: [
|
|
9593
10184
|
//
|
|
9594
|
-
|
|
10185
|
+
join20(__dirname5, "test-global-setup.js")
|
|
9595
10186
|
]
|
|
9596
10187
|
// globalSetup: [
|
|
9597
10188
|
// //
|
|
@@ -9785,12 +10376,12 @@ var logTestErrors = (event) => {
|
|
|
9785
10376
|
};
|
|
9786
10377
|
var runTest = async (stack, dir, filters, workspace, opts) => {
|
|
9787
10378
|
await mkdir4(directories.test, { recursive: true });
|
|
9788
|
-
const file =
|
|
10379
|
+
const file = join21(directories.test, `${stack}.json`);
|
|
9789
10380
|
const fingerprint = await generateFolderHash(workspace, dir);
|
|
9790
10381
|
if (!process.env.NO_CACHE) {
|
|
9791
10382
|
const exists = await fileExist(file);
|
|
9792
10383
|
if (exists) {
|
|
9793
|
-
const raw = await
|
|
10384
|
+
const raw = await readFile6(file, { encoding: "utf8" });
|
|
9794
10385
|
const data = parse4(raw);
|
|
9795
10386
|
if (data.fingerprint === fingerprint) {
|
|
9796
10387
|
log18.step(
|
|
@@ -9833,9 +10424,9 @@ var runTest = async (stack, dir, filters, workspace, opts) => {
|
|
|
9833
10424
|
logTestLogs(result);
|
|
9834
10425
|
}
|
|
9835
10426
|
logTestErrors(result);
|
|
9836
|
-
await
|
|
10427
|
+
await writeFile4(
|
|
9837
10428
|
file,
|
|
9838
|
-
|
|
10429
|
+
stringify3({
|
|
9839
10430
|
...result,
|
|
9840
10431
|
fingerprint
|
|
9841
10432
|
})
|
|
@@ -10373,7 +10964,7 @@ var auth = (program2) => {
|
|
|
10373
10964
|
// src/cli/command/bind.ts
|
|
10374
10965
|
import { log as log24 } from "@awsless/clui";
|
|
10375
10966
|
import chalk4 from "chalk";
|
|
10376
|
-
import { constantCase as
|
|
10967
|
+
import { constantCase as constantCase16 } from "change-case";
|
|
10377
10968
|
var bind = (program2) => {
|
|
10378
10969
|
program2.command("bind").argument("[command...]", "The command to execute").option("--config <string...>", "List of config values that will be accessable", (v) => v.split(",")).description(`Bind your site environment variables to a command`).action(async (commands11 = [], opts) => {
|
|
10379
10970
|
await layout("bind", async ({ appConfig, stackConfigs }) => {
|
|
@@ -10400,10 +10991,10 @@ var bind = (program2) => {
|
|
|
10400
10991
|
const configList = opts.config ?? [];
|
|
10401
10992
|
const configs = {};
|
|
10402
10993
|
for (const name of configList) {
|
|
10403
|
-
configs[`CONFIG_${
|
|
10994
|
+
configs[`CONFIG_${constantCase16(name)}`] = name;
|
|
10404
10995
|
}
|
|
10405
10996
|
if (configList.length ?? 0 > 0) {
|
|
10406
|
-
log24.note("Bind Config", configList.map((v) => color.label(
|
|
10997
|
+
log24.note("Bind Config", configList.map((v) => color.label(constantCase16(v))).join("\n"));
|
|
10407
10998
|
}
|
|
10408
10999
|
if (commands11.length === 0) {
|
|
10409
11000
|
return "No command to execute.";
|
|
@@ -10442,7 +11033,7 @@ var bind = (program2) => {
|
|
|
10442
11033
|
|
|
10443
11034
|
// src/config/load/watch.ts
|
|
10444
11035
|
import { watch } from "chokidar";
|
|
10445
|
-
var watchConfig = async (options,
|
|
11036
|
+
var watchConfig = async (options, resolve2, reject) => {
|
|
10446
11037
|
await loadAppConfig(options);
|
|
10447
11038
|
debug("Start watching...");
|
|
10448
11039
|
const ext = "{json,jsonc,json5}";
|
|
@@ -10457,7 +11048,7 @@ var watchConfig = async (options, resolve, reject) => {
|
|
|
10457
11048
|
const appConfig = await loadAppConfig(options);
|
|
10458
11049
|
const stackConfigs = await loadStackConfigs(options);
|
|
10459
11050
|
validateFeatures({ appConfig, stackConfigs });
|
|
10460
|
-
|
|
11051
|
+
resolve2({ appConfig, stackConfigs });
|
|
10461
11052
|
} catch (error) {
|
|
10462
11053
|
reject(error);
|
|
10463
11054
|
}
|
|
@@ -10469,8 +11060,8 @@ var watchConfig = async (options, resolve, reject) => {
|
|
|
10469
11060
|
import { log as log25 } from "@awsless/clui";
|
|
10470
11061
|
|
|
10471
11062
|
// src/type-gen/generate.ts
|
|
10472
|
-
import { mkdir as mkdir5, writeFile as
|
|
10473
|
-
import { dirname as dirname10, join as
|
|
11063
|
+
import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
|
|
11064
|
+
import { dirname as dirname10, join as join22, relative as relative9 } from "path";
|
|
10474
11065
|
var generateTypes = async (props) => {
|
|
10475
11066
|
const files = [];
|
|
10476
11067
|
await Promise.all(
|
|
@@ -10479,13 +11070,13 @@ var generateTypes = async (props) => {
|
|
|
10479
11070
|
...props,
|
|
10480
11071
|
async write(file, data, include = false) {
|
|
10481
11072
|
const code = data?.toString("utf8");
|
|
10482
|
-
const path =
|
|
11073
|
+
const path = join22(directories.types, file);
|
|
10483
11074
|
if (code) {
|
|
10484
11075
|
if (include) {
|
|
10485
|
-
files.push(
|
|
11076
|
+
files.push(relative9(directories.root, path));
|
|
10486
11077
|
}
|
|
10487
11078
|
await mkdir5(dirname10(path), { recursive: true });
|
|
10488
|
-
await
|
|
11079
|
+
await writeFile5(path, code);
|
|
10489
11080
|
}
|
|
10490
11081
|
}
|
|
10491
11082
|
});
|
|
@@ -10493,7 +11084,7 @@ var generateTypes = async (props) => {
|
|
|
10493
11084
|
);
|
|
10494
11085
|
if (files.length) {
|
|
10495
11086
|
const code = files.map((file) => `/// <reference path='${file}' />`).join("\n");
|
|
10496
|
-
await
|
|
11087
|
+
await writeFile5(join22(directories.root, `awsless.d.ts`), code);
|
|
10497
11088
|
}
|
|
10498
11089
|
};
|
|
10499
11090
|
|
|
@@ -10517,9 +11108,9 @@ var dev = (program2) => {
|
|
|
10517
11108
|
logError(error);
|
|
10518
11109
|
}
|
|
10519
11110
|
);
|
|
10520
|
-
await new Promise((
|
|
10521
|
-
process.once("exit",
|
|
10522
|
-
process.once("SIGINT",
|
|
11111
|
+
await new Promise((resolve2) => {
|
|
11112
|
+
process.once("exit", resolve2);
|
|
11113
|
+
process.once("SIGINT", resolve2);
|
|
10523
11114
|
});
|
|
10524
11115
|
});
|
|
10525
11116
|
});
|
|
@@ -10919,7 +11510,7 @@ var domain = (program2) => {
|
|
|
10919
11510
|
// src/cli/command/logs.ts
|
|
10920
11511
|
import { CloudWatchLogsClient, StartLiveTailCommand } from "@aws-sdk/client-cloudwatch-logs";
|
|
10921
11512
|
import { log as log30 } from "@awsless/clui";
|
|
10922
|
-
import { aws as
|
|
11513
|
+
import { aws as aws32 } from "@terraforge/aws";
|
|
10923
11514
|
import chalk6 from "chalk";
|
|
10924
11515
|
import chunk2 from "chunk";
|
|
10925
11516
|
import { formatDate } from "date-fns";
|
|
@@ -10942,7 +11533,7 @@ var logs = (program2) => {
|
|
|
10942
11533
|
for (const stack of app.stacks) {
|
|
10943
11534
|
if (filters.find((f) => wildstring7.match(f, stack.name))) {
|
|
10944
11535
|
for (const resource of stack.resources) {
|
|
10945
|
-
if (resource instanceof
|
|
11536
|
+
if (resource instanceof aws32.cloudwatch.LogGroup) {
|
|
10946
11537
|
logGroupArns.push(await resource.arn);
|
|
10947
11538
|
}
|
|
10948
11539
|
}
|