@awsless/cli 0.0.4 → 0.0.6
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,21 +7248,37 @@ 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
|
|
@@ -7268,23 +7287,23 @@ var buildExecutable = async (input, outputPath, architecture) => {
|
|
|
7268
7287
|
});
|
|
7269
7288
|
} catch (error) {
|
|
7270
7289
|
throw new ExpectedError(
|
|
7271
|
-
`
|
|
7290
|
+
`Job executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
|
|
7272
7291
|
);
|
|
7273
7292
|
}
|
|
7274
7293
|
if (!result.success) {
|
|
7275
|
-
throw new ExpectedError(`
|
|
7294
|
+
throw new ExpectedError(`Job executable build failed:
|
|
7276
7295
|
${result.logs?.map((log35) => log35.message).join("\n")}`);
|
|
7277
7296
|
}
|
|
7278
7297
|
const file = await readFile4(filePath);
|
|
7279
7298
|
return {
|
|
7280
|
-
hash: createHash3("sha1").update(file).update(
|
|
7299
|
+
hash: createHash3("sha1").update(file).update(target).digest("hex"),
|
|
7281
7300
|
file
|
|
7282
7301
|
};
|
|
7283
7302
|
};
|
|
7284
7303
|
|
|
7285
|
-
// src/feature/
|
|
7286
|
-
var
|
|
7287
|
-
const group = new Group25(parentGroup, "
|
|
7304
|
+
// src/feature/job/util.ts
|
|
7305
|
+
var createFargateJob = (parentGroup, ctx, ns, id, local) => {
|
|
7306
|
+
const group = new Group25(parentGroup, "job", ns);
|
|
7288
7307
|
const name = formatLocalResourceName({
|
|
7289
7308
|
appName: ctx.app.name,
|
|
7290
7309
|
stackName: ctx.stack.name,
|
|
@@ -7292,13 +7311,13 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7292
7311
|
resourceName: id
|
|
7293
7312
|
});
|
|
7294
7313
|
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);
|
|
7314
|
+
const props = deepmerge4(ctx.appConfig.defaults.job, local);
|
|
7315
|
+
const image2 = props.image || "public.ecr.aws/amazonlinux/amazonlinux:2023-minimal";
|
|
7316
|
+
ctx.registerBuild("job", name, async (build3, { workspace }) => {
|
|
7317
|
+
const fingerprint = [await generateFileHash2(workspace, local.code.file), props.architecture].join(":");
|
|
7299
7318
|
return build3(fingerprint, async (write) => {
|
|
7300
|
-
const temp = await createTempFolder(`
|
|
7301
|
-
const executable = await
|
|
7319
|
+
const temp = await createTempFolder(`job--${name}`);
|
|
7320
|
+
const executable = await buildJobExecutable(local.code.file, temp.path, props.architecture);
|
|
7302
7321
|
await Promise.all([
|
|
7303
7322
|
//
|
|
7304
7323
|
write("HASH", executable.hash),
|
|
@@ -7311,10 +7330,10 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7311
7330
|
});
|
|
7312
7331
|
});
|
|
7313
7332
|
const code = new aws26.s3.BucketObject(group, "code", {
|
|
7314
|
-
bucket: ctx.shared.get("
|
|
7333
|
+
bucket: ctx.shared.get("job", "bucket-name"),
|
|
7315
7334
|
key: name,
|
|
7316
|
-
source: relativePath(getBuildPath("
|
|
7317
|
-
sourceHash: $file(getBuildPath("
|
|
7335
|
+
source: relativePath(getBuildPath("job", name, "program")),
|
|
7336
|
+
sourceHash: $file(getBuildPath("job", name, "HASH"))
|
|
7318
7337
|
});
|
|
7319
7338
|
const executionRole = new aws26.iam.Role(group, "execution-role", {
|
|
7320
7339
|
name: shortId(`${shortName}:execution-role`),
|
|
@@ -7360,8 +7379,8 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7360
7379
|
Statement: [
|
|
7361
7380
|
{
|
|
7362
7381
|
Effect: pascalCase3("allow"),
|
|
7363
|
-
Action: ["s3:
|
|
7364
|
-
Resource: `arn:aws:s3:::${bucket}/${key}`
|
|
7382
|
+
Action: ["s3:GetObject", "s3:HeadObject"],
|
|
7383
|
+
Resource: [`arn:aws:s3:::${bucket}/${key}`, `arn:aws:s3:::${bucket}/payloads/*`]
|
|
7365
7384
|
}
|
|
7366
7385
|
]
|
|
7367
7386
|
});
|
|
@@ -7378,9 +7397,9 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7378
7397
|
const policy = new aws26.iam.RolePolicy(group, "policy", {
|
|
7379
7398
|
role: role.name,
|
|
7380
7399
|
name: "task-policy",
|
|
7381
|
-
policy: new Output6(statementDeps, async (
|
|
7400
|
+
policy: new Output6(statementDeps, async (resolve2) => {
|
|
7382
7401
|
const list3 = await resolveInputs3(statements);
|
|
7383
|
-
|
|
7402
|
+
resolve2(
|
|
7384
7403
|
JSON.stringify({
|
|
7385
7404
|
Version: "2012-10-17",
|
|
7386
7405
|
Statement: list3.map((statement) => ({
|
|
@@ -7405,7 +7424,6 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7405
7424
|
if (props.log.retention && props.log.retention.value > 0n) {
|
|
7406
7425
|
logGroup = new aws26.cloudwatch.LogGroup(group, "log", {
|
|
7407
7426
|
name: `/aws/ecs/${name}`,
|
|
7408
|
-
// name: `/aws/lambda/${name}`,
|
|
7409
7427
|
retentionInDays: toDays9(props.log.retention)
|
|
7410
7428
|
});
|
|
7411
7429
|
if (ctx.shared.has("on-error-log", "subscriber-arn")) {
|
|
@@ -7424,6 +7442,63 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7424
7442
|
};
|
|
7425
7443
|
const variables = {};
|
|
7426
7444
|
const variableDeps = /* @__PURE__ */ new Set();
|
|
7445
|
+
const taskDependsOn = [code];
|
|
7446
|
+
const accessPoint = props.persistentStorage ? (() => {
|
|
7447
|
+
const persistentStorageSecurityGroup = new aws26.security.Group(
|
|
7448
|
+
group,
|
|
7449
|
+
"persistent-storage-security-group",
|
|
7450
|
+
{
|
|
7451
|
+
name: shortId(`${shortName}:storage-sg`),
|
|
7452
|
+
description: name,
|
|
7453
|
+
vpcId: ctx.shared.get("vpc", "id"),
|
|
7454
|
+
revokeRulesOnDelete: true,
|
|
7455
|
+
tags
|
|
7456
|
+
}
|
|
7457
|
+
);
|
|
7458
|
+
new aws26.vpc.SecurityGroupIngressRule(group, "persistent-storage-ingress-rule", {
|
|
7459
|
+
securityGroupId: persistentStorageSecurityGroup.id,
|
|
7460
|
+
referencedSecurityGroupId: ctx.shared.get("job", "security-group-id"),
|
|
7461
|
+
description: "Allow NFS traffic from this job",
|
|
7462
|
+
ipProtocol: "tcp",
|
|
7463
|
+
fromPort: 2049,
|
|
7464
|
+
toPort: 2049,
|
|
7465
|
+
tags
|
|
7466
|
+
});
|
|
7467
|
+
const fileSystem = new aws26.efs.FileSystem(group, "persistent-storage-file-system", {
|
|
7468
|
+
encrypted: true,
|
|
7469
|
+
performanceMode: "generalPurpose",
|
|
7470
|
+
throughputMode: "elastic",
|
|
7471
|
+
tags: {
|
|
7472
|
+
...tags,
|
|
7473
|
+
Name: `${name}-storage`
|
|
7474
|
+
}
|
|
7475
|
+
});
|
|
7476
|
+
for (const [index, subnetId] of ctx.shared.get("vpc", "public-subnets").entries()) {
|
|
7477
|
+
const mountTarget = new aws26.efs.MountTarget(group, `mount-target-${index + 1}`, {
|
|
7478
|
+
fileSystemId: fileSystem.id,
|
|
7479
|
+
subnetId,
|
|
7480
|
+
securityGroups: [persistentStorageSecurityGroup.id]
|
|
7481
|
+
});
|
|
7482
|
+
taskDependsOn.push(mountTarget);
|
|
7483
|
+
}
|
|
7484
|
+
const accessPoint2 = new aws26.efs.AccessPoint(group, "access-point", {
|
|
7485
|
+
fileSystemId: fileSystem.id,
|
|
7486
|
+
rootDirectory: {
|
|
7487
|
+
path: `/jobs/${name}`,
|
|
7488
|
+
creationInfo: {
|
|
7489
|
+
ownerUid: 0,
|
|
7490
|
+
ownerGid: 0,
|
|
7491
|
+
permissions: "755"
|
|
7492
|
+
}
|
|
7493
|
+
},
|
|
7494
|
+
tags
|
|
7495
|
+
});
|
|
7496
|
+
taskDependsOn.push(fileSystem, accessPoint2);
|
|
7497
|
+
return {
|
|
7498
|
+
accessPoint: accessPoint2,
|
|
7499
|
+
fileSystem
|
|
7500
|
+
};
|
|
7501
|
+
})() : void 0;
|
|
7427
7502
|
const task2 = new aws26.ecs.TaskDefinition(
|
|
7428
7503
|
group,
|
|
7429
7504
|
"task",
|
|
@@ -7440,13 +7515,495 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7440
7515
|
operatingSystemFamily: "LINUX"
|
|
7441
7516
|
},
|
|
7442
7517
|
trackLatest: true,
|
|
7443
|
-
|
|
7518
|
+
...accessPoint && {
|
|
7519
|
+
volume: [
|
|
7520
|
+
{
|
|
7521
|
+
name: "persistent-storage",
|
|
7522
|
+
efsVolumeConfiguration: {
|
|
7523
|
+
fileSystemId: accessPoint.fileSystem.id,
|
|
7524
|
+
transitEncryption: "ENABLED",
|
|
7525
|
+
authorizationConfig: {
|
|
7526
|
+
accessPointId: accessPoint.accessPoint.id
|
|
7527
|
+
}
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
]
|
|
7531
|
+
},
|
|
7532
|
+
containerDefinitions: new Output6(variableDeps, async (resolve2) => {
|
|
7444
7533
|
const data = await resolveInputs3(variables);
|
|
7445
7534
|
const { s3Bucket, s3Key } = await resolveInputs3({
|
|
7446
7535
|
s3Bucket: code.bucket,
|
|
7447
7536
|
s3Key: code.key
|
|
7448
7537
|
});
|
|
7449
|
-
|
|
7538
|
+
resolve2(
|
|
7539
|
+
JSON.stringify([
|
|
7540
|
+
{
|
|
7541
|
+
name: `container-${id}`,
|
|
7542
|
+
essential: true,
|
|
7543
|
+
image: image2,
|
|
7544
|
+
workingDirectory: "/usr/app",
|
|
7545
|
+
entryPoint: ["sh", "-c"],
|
|
7546
|
+
command: [
|
|
7547
|
+
[
|
|
7548
|
+
...props.startupCommand?.length ? (() => {
|
|
7549
|
+
const setupHash = createHash4("sha1").update(props.startupCommand.join(" && ")).digest("hex").slice(0, 8);
|
|
7550
|
+
return [`if [ ! -f /root/.setup-done-${setupHash} ]; then ${props.startupCommand.join(" && ")} && touch /root/.setup-done-${setupHash}; fi`];
|
|
7551
|
+
})() : [],
|
|
7552
|
+
`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`,
|
|
7553
|
+
`exec timeout --kill-after=10 ${toSeconds9(props.timeout)} /root/program`
|
|
7554
|
+
].join(" && ")
|
|
7555
|
+
],
|
|
7556
|
+
environment: Object.entries(data).map(([name2, value]) => ({
|
|
7557
|
+
name: name2,
|
|
7558
|
+
value
|
|
7559
|
+
})),
|
|
7560
|
+
...accessPoint && {
|
|
7561
|
+
mountPoints: [
|
|
7562
|
+
{
|
|
7563
|
+
sourceVolume: "persistent-storage",
|
|
7564
|
+
containerPath: "/root"
|
|
7565
|
+
}
|
|
7566
|
+
]
|
|
7567
|
+
},
|
|
7568
|
+
...logGroup && {
|
|
7569
|
+
logConfiguration: {
|
|
7570
|
+
logDriver: "awslogs",
|
|
7571
|
+
options: {
|
|
7572
|
+
"awslogs-group": `/aws/ecs/${name}`,
|
|
7573
|
+
"awslogs-region": ctx.appConfig.region,
|
|
7574
|
+
"awslogs-stream-prefix": "ecs",
|
|
7575
|
+
mode: "non-blocking"
|
|
7576
|
+
}
|
|
7577
|
+
}
|
|
7578
|
+
}
|
|
7579
|
+
}
|
|
7580
|
+
])
|
|
7581
|
+
);
|
|
7582
|
+
}),
|
|
7583
|
+
tags
|
|
7584
|
+
},
|
|
7585
|
+
{
|
|
7586
|
+
replaceOnChanges: [
|
|
7587
|
+
"containerDefinitions",
|
|
7588
|
+
"cpu",
|
|
7589
|
+
"memory",
|
|
7590
|
+
"runtimePlatform",
|
|
7591
|
+
"executionRoleArn",
|
|
7592
|
+
"taskRoleArn"
|
|
7593
|
+
],
|
|
7594
|
+
dependsOn: taskDependsOn
|
|
7595
|
+
}
|
|
7596
|
+
);
|
|
7597
|
+
ctx.onEnv((name2, value) => {
|
|
7598
|
+
variables[name2] = value;
|
|
7599
|
+
for (const dep of findInputDeps3([value])) {
|
|
7600
|
+
variableDeps.add(dep);
|
|
7601
|
+
}
|
|
7602
|
+
});
|
|
7603
|
+
variables.APP = ctx.appConfig.name;
|
|
7604
|
+
variables.APP_ID = ctx.appId;
|
|
7605
|
+
variables.AWS_ACCOUNT_ID = ctx.accountId;
|
|
7606
|
+
variables.STACK = ctx.stackConfig.name;
|
|
7607
|
+
variables.CODE_HASH = code.sourceHash;
|
|
7608
|
+
variables.JOB_CONFIG_HASH = createHash4("sha1").update(stringify(props)).digest("hex");
|
|
7609
|
+
if (props.environment) {
|
|
7610
|
+
for (const [key, value] of Object.entries(props.environment)) {
|
|
7611
|
+
variables[key] = value;
|
|
7612
|
+
}
|
|
7613
|
+
}
|
|
7614
|
+
if (ctx.appConfig.defaults.job.permissions) {
|
|
7615
|
+
statements.push(...ctx.appConfig.defaults.job.permissions);
|
|
7616
|
+
}
|
|
7617
|
+
if ("permissions" in local && local.permissions) {
|
|
7618
|
+
statements.push(...local.permissions);
|
|
7619
|
+
}
|
|
7620
|
+
return { name, task: task2, policy, code, group };
|
|
7621
|
+
};
|
|
7622
|
+
|
|
7623
|
+
// src/feature/job/index.ts
|
|
7624
|
+
var typeGenCode10 = `
|
|
7625
|
+
import type { Mock } from 'vitest'
|
|
7626
|
+
|
|
7627
|
+
type Func = (...args: any[]) => any
|
|
7628
|
+
|
|
7629
|
+
type Invoke<N extends string, F extends Func> = Parameters<F> extends []
|
|
7630
|
+
? InvokeWithoutPayload<N, F>
|
|
7631
|
+
: unknown extends Parameters<F>[0]
|
|
7632
|
+
? InvokeWithoutPayload<N, F>
|
|
7633
|
+
: InvokeWithPayload<N, F>
|
|
7634
|
+
|
|
7635
|
+
type InvokeWithPayload<Name extends string, F extends Func> = {
|
|
7636
|
+
readonly name: Name
|
|
7637
|
+
(payload: Parameters<F>[0]): Promise<{ taskArn: string | undefined }>
|
|
7638
|
+
}
|
|
7639
|
+
|
|
7640
|
+
type InvokeWithoutPayload<Name extends string, F extends Func> = {
|
|
7641
|
+
readonly name: Name
|
|
7642
|
+
(payload?: Parameters<F>[0]): Promise<{ taskArn: string | undefined }>
|
|
7643
|
+
}
|
|
7644
|
+
|
|
7645
|
+
type MockHandle<F extends Func> = (payload: Parameters<F>[0]) => void | Promise<void>
|
|
7646
|
+
type MockBuilder<F extends Func> = (handle?: MockHandle<F>) => void
|
|
7647
|
+
type MockObject<F extends Func> = Mock<Parameters<F>, ReturnType<F>>
|
|
7648
|
+
`;
|
|
7649
|
+
var jobFeature = defineFeature({
|
|
7650
|
+
name: "job",
|
|
7651
|
+
async onTypeGen(ctx) {
|
|
7652
|
+
const types2 = new TypeFile("awsless");
|
|
7653
|
+
const resources2 = new TypeObject(1);
|
|
7654
|
+
const mocks = new TypeObject(1);
|
|
7655
|
+
const mockResponses = new TypeObject(1);
|
|
7656
|
+
for (const stack of ctx.stackConfigs) {
|
|
7657
|
+
const resource = new TypeObject(2);
|
|
7658
|
+
const mock = new TypeObject(2);
|
|
7659
|
+
const mockResponse = new TypeObject(2);
|
|
7660
|
+
for (const [name, props] of Object.entries(stack.jobs || {})) {
|
|
7661
|
+
const varName = camelCase8(`${stack.name}-${name}`);
|
|
7662
|
+
const funcName = formatLocalResourceName({
|
|
7663
|
+
appName: ctx.appConfig.name,
|
|
7664
|
+
stackName: stack.name,
|
|
7665
|
+
resourceType: "job",
|
|
7666
|
+
resourceName: name
|
|
7667
|
+
});
|
|
7668
|
+
if ("file" in props.code) {
|
|
7669
|
+
const relFile = relative8(directories.types, props.code.file);
|
|
7670
|
+
types2.addImport(varName, relFile);
|
|
7671
|
+
resource.addType(name, `Invoke<'${funcName}', typeof ${varName}>`);
|
|
7672
|
+
mock.addType(name, `MockBuilder<typeof ${varName}>`);
|
|
7673
|
+
mockResponse.addType(name, `MockObject<typeof ${varName}>`);
|
|
7674
|
+
}
|
|
7675
|
+
}
|
|
7676
|
+
mocks.addType(stack.name, mock);
|
|
7677
|
+
resources2.addType(stack.name, resource);
|
|
7678
|
+
mockResponses.addType(stack.name, mockResponse);
|
|
7679
|
+
}
|
|
7680
|
+
types2.addCode(typeGenCode10);
|
|
7681
|
+
types2.addInterface("JobResources", resources2);
|
|
7682
|
+
types2.addInterface("JobMock", mocks);
|
|
7683
|
+
types2.addInterface("JobMockResponse", mockResponses);
|
|
7684
|
+
await ctx.write("job.d.ts", types2, true);
|
|
7685
|
+
},
|
|
7686
|
+
onBefore(ctx) {
|
|
7687
|
+
const group = new Group26(ctx.base, "job", "asset");
|
|
7688
|
+
const bucket = new aws27.s3.Bucket(group, "bucket", {
|
|
7689
|
+
bucket: formatGlobalResourceName({
|
|
7690
|
+
appName: ctx.app.name,
|
|
7691
|
+
resourceType: "job",
|
|
7692
|
+
resourceName: "assets",
|
|
7693
|
+
postfix: ctx.appId
|
|
7694
|
+
}),
|
|
7695
|
+
forceDestroy: true,
|
|
7696
|
+
lifecycleRule: [
|
|
7697
|
+
{
|
|
7698
|
+
id: "expire-payloads",
|
|
7699
|
+
enabled: true,
|
|
7700
|
+
prefix: "payloads/",
|
|
7701
|
+
expiration: { days: 1 }
|
|
7702
|
+
}
|
|
7703
|
+
]
|
|
7704
|
+
});
|
|
7705
|
+
ctx.shared.set("job", "bucket-name", bucket.bucket);
|
|
7706
|
+
},
|
|
7707
|
+
onApp(ctx) {
|
|
7708
|
+
const found = ctx.stackConfigs.filter((stack) => {
|
|
7709
|
+
return Object.keys(stack.jobs ?? {}).length > 0;
|
|
7710
|
+
});
|
|
7711
|
+
if (found.length === 0) {
|
|
7712
|
+
return;
|
|
7713
|
+
}
|
|
7714
|
+
const group = new Group26(ctx.base, "job", "cluster");
|
|
7715
|
+
const cluster = new aws27.ecs.Cluster(
|
|
7716
|
+
group,
|
|
7717
|
+
"cluster",
|
|
7718
|
+
{
|
|
7719
|
+
name: `${ctx.app.name}-job`
|
|
7720
|
+
},
|
|
7721
|
+
{
|
|
7722
|
+
replaceOnChanges: ["name"]
|
|
7723
|
+
}
|
|
7724
|
+
);
|
|
7725
|
+
ctx.shared.set("job", "cluster-name", cluster.name);
|
|
7726
|
+
ctx.shared.set("job", "cluster-arn", cluster.arn);
|
|
7727
|
+
const securityGroup = new aws27.security.Group(group, "security-group", {
|
|
7728
|
+
name: `${ctx.app.name}-job`,
|
|
7729
|
+
description: "Shared security group for jobs",
|
|
7730
|
+
vpcId: ctx.shared.get("vpc", "id"),
|
|
7731
|
+
revokeRulesOnDelete: true,
|
|
7732
|
+
tags: {
|
|
7733
|
+
APP: ctx.appConfig.name
|
|
7734
|
+
}
|
|
7735
|
+
});
|
|
7736
|
+
new aws27.vpc.SecurityGroupEgressRule(group, "egress-rule", {
|
|
7737
|
+
securityGroupId: securityGroup.id,
|
|
7738
|
+
description: "Allow all outbound traffic from jobs",
|
|
7739
|
+
ipProtocol: "-1",
|
|
7740
|
+
cidrIpv4: "0.0.0.0/0",
|
|
7741
|
+
tags: {
|
|
7742
|
+
APP: ctx.appConfig.name
|
|
7743
|
+
}
|
|
7744
|
+
});
|
|
7745
|
+
ctx.shared.set("job", "security-group-id", securityGroup.id);
|
|
7746
|
+
},
|
|
7747
|
+
onStack(ctx) {
|
|
7748
|
+
const subnets = ctx.shared.get("vpc", "public-subnets");
|
|
7749
|
+
ctx.addEnv(
|
|
7750
|
+
"JOB_SUBNETS",
|
|
7751
|
+
new Output7(new Set(findInputDeps4(subnets)), async (resolve2) => {
|
|
7752
|
+
const resolved = await resolveInputs4(subnets);
|
|
7753
|
+
resolve2(JSON.stringify(resolved));
|
|
7754
|
+
})
|
|
7755
|
+
);
|
|
7756
|
+
ctx.addEnv("JOB_SECURITY_GROUP", ctx.shared.get("job", "security-group-id"));
|
|
7757
|
+
ctx.addEnv("JOB_PAYLOAD_BUCKET", ctx.shared.get("job", "bucket-name"));
|
|
7758
|
+
const jobs = Object.entries(ctx.stackConfig.jobs ?? {});
|
|
7759
|
+
if (jobs.length === 0) return;
|
|
7760
|
+
for (const [id, props] of jobs) {
|
|
7761
|
+
const group = new Group26(ctx.stack, "job", id);
|
|
7762
|
+
createFargateJob(group, ctx, "job", id, props);
|
|
7763
|
+
}
|
|
7764
|
+
ctx.addStackPermission({
|
|
7765
|
+
actions: ["ecs:RunTask"],
|
|
7766
|
+
resources: [
|
|
7767
|
+
`arn:aws:ecs:${ctx.appConfig.region}:*:task-definition/${ctx.app.name}--${ctx.stackConfig.name}--*`
|
|
7768
|
+
]
|
|
7769
|
+
});
|
|
7770
|
+
ctx.addStackPermission({
|
|
7771
|
+
actions: ["iam:PassRole"],
|
|
7772
|
+
resources: ["*"],
|
|
7773
|
+
conditions: {
|
|
7774
|
+
StringEquals: {
|
|
7775
|
+
"iam:PassedToService": "ecs-tasks.amazonaws.com"
|
|
7776
|
+
}
|
|
7777
|
+
}
|
|
7778
|
+
});
|
|
7779
|
+
ctx.addStackPermission({
|
|
7780
|
+
actions: ["s3:PutObject"],
|
|
7781
|
+
resources: [
|
|
7782
|
+
`arn:aws:s3:::${formatGlobalResourceName({
|
|
7783
|
+
appName: ctx.app.name,
|
|
7784
|
+
resourceType: "job",
|
|
7785
|
+
resourceName: "assets",
|
|
7786
|
+
postfix: ctx.appId
|
|
7787
|
+
})}/payloads/*`
|
|
7788
|
+
]
|
|
7789
|
+
});
|
|
7790
|
+
}
|
|
7791
|
+
});
|
|
7792
|
+
|
|
7793
|
+
// src/feature/instance/index.ts
|
|
7794
|
+
import { Group as Group28 } from "@terraforge/core";
|
|
7795
|
+
import { aws as aws29 } from "@terraforge/aws";
|
|
7796
|
+
|
|
7797
|
+
// src/feature/instance/util.ts
|
|
7798
|
+
import { toDays as toDays10, toSeconds as toSeconds10 } from "@awsless/duration";
|
|
7799
|
+
import { stringify as stringify2 } from "@awsless/json";
|
|
7800
|
+
import { toMebibytes as toMebibytes6 } from "@awsless/size";
|
|
7801
|
+
import { generateFileHash as generateFileHash3 } from "@awsless/ts-file-cache";
|
|
7802
|
+
import { aws as aws28 } from "@terraforge/aws";
|
|
7803
|
+
import { Group as Group27, Output as Output8, findInputDeps as findInputDeps5, resolveInputs as resolveInputs5 } from "@terraforge/core";
|
|
7804
|
+
import { constantCase as constantCase13, pascalCase as pascalCase4 } from "change-case";
|
|
7805
|
+
import { createHash as createHash6 } from "crypto";
|
|
7806
|
+
import deepmerge5 from "deepmerge";
|
|
7807
|
+
import { join as join19 } from "path";
|
|
7808
|
+
|
|
7809
|
+
// src/feature/instance/build/executable.ts
|
|
7810
|
+
import { createHash as createHash5 } from "crypto";
|
|
7811
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
7812
|
+
import { join as join18 } from "path";
|
|
7813
|
+
var buildExecutable = async (input, outputPath, architecture) => {
|
|
7814
|
+
const filePath = join18(outputPath, "program");
|
|
7815
|
+
const target = architecture === "x86_64" ? "bun-linux-x64" : "bun-linux-arm64";
|
|
7816
|
+
let result;
|
|
7817
|
+
try {
|
|
7818
|
+
result = await Bun.build({
|
|
7819
|
+
entrypoints: [input],
|
|
7820
|
+
compile: {
|
|
7821
|
+
target,
|
|
7822
|
+
outfile: filePath
|
|
7823
|
+
},
|
|
7824
|
+
target: "bun"
|
|
7825
|
+
});
|
|
7826
|
+
} catch (error) {
|
|
7827
|
+
throw new ExpectedError(
|
|
7828
|
+
`Executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
|
|
7829
|
+
);
|
|
7830
|
+
}
|
|
7831
|
+
if (!result.success) {
|
|
7832
|
+
throw new ExpectedError(`Executable build failed:
|
|
7833
|
+
${result.logs?.map((log35) => log35.message).join("\n")}`);
|
|
7834
|
+
}
|
|
7835
|
+
const file = await readFile5(filePath);
|
|
7836
|
+
return {
|
|
7837
|
+
hash: createHash5("sha1").update(file).update("x86_64").digest("hex"),
|
|
7838
|
+
file
|
|
7839
|
+
};
|
|
7840
|
+
};
|
|
7841
|
+
|
|
7842
|
+
// src/feature/instance/util.ts
|
|
7843
|
+
var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
7844
|
+
const group = new Group27(parentGroup, "instance", ns);
|
|
7845
|
+
const name = formatLocalResourceName({
|
|
7846
|
+
appName: ctx.app.name,
|
|
7847
|
+
stackName: ctx.stack.name,
|
|
7848
|
+
resourceType: ns,
|
|
7849
|
+
resourceName: id
|
|
7850
|
+
});
|
|
7851
|
+
const shortName = shortId(`${ctx.app.name}:${ctx.stack.name}:${ns}:${id}:${ctx.appId}`);
|
|
7852
|
+
const props = deepmerge5(ctx.appConfig.defaults.instance, local);
|
|
7853
|
+
const image2 = props.image || (props.architecture === "arm64" ? "public.ecr.aws/aws-cli/aws-cli:arm64" : "public.ecr.aws/aws-cli/aws-cli:amd64");
|
|
7854
|
+
ctx.registerBuild("instance", name, async (build3, { workspace }) => {
|
|
7855
|
+
const fingerprint = await generateFileHash3(workspace, local.code.file);
|
|
7856
|
+
return build3(fingerprint, async (write) => {
|
|
7857
|
+
const temp = await createTempFolder(`instance--${name}`);
|
|
7858
|
+
const executable = await buildExecutable(local.code.file, temp.path, props.architecture);
|
|
7859
|
+
await Promise.all([
|
|
7860
|
+
//
|
|
7861
|
+
write("HASH", executable.hash),
|
|
7862
|
+
write("program", executable.file),
|
|
7863
|
+
temp.delete()
|
|
7864
|
+
]);
|
|
7865
|
+
return {
|
|
7866
|
+
size: formatByteSize(executable.file.byteLength)
|
|
7867
|
+
};
|
|
7868
|
+
});
|
|
7869
|
+
});
|
|
7870
|
+
const code = new aws28.s3.BucketObject(group, "code", {
|
|
7871
|
+
bucket: ctx.shared.get("instance", "bucket-name"),
|
|
7872
|
+
key: name,
|
|
7873
|
+
source: relativePath(getBuildPath("instance", name, "program")),
|
|
7874
|
+
sourceHash: $file(getBuildPath("instance", name, "HASH"))
|
|
7875
|
+
});
|
|
7876
|
+
const executionRole = new aws28.iam.Role(group, "execution-role", {
|
|
7877
|
+
name: shortId(`${shortName}:execution-role`),
|
|
7878
|
+
description: name,
|
|
7879
|
+
assumeRolePolicy: JSON.stringify({
|
|
7880
|
+
Version: "2012-10-17",
|
|
7881
|
+
Statement: [
|
|
7882
|
+
{
|
|
7883
|
+
Effect: "Allow",
|
|
7884
|
+
Action: "sts:AssumeRole",
|
|
7885
|
+
Principal: {
|
|
7886
|
+
Service: ["ecs-tasks.amazonaws.com"]
|
|
7887
|
+
}
|
|
7888
|
+
}
|
|
7889
|
+
]
|
|
7890
|
+
}),
|
|
7891
|
+
managedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
|
|
7892
|
+
});
|
|
7893
|
+
const role = new aws28.iam.Role(
|
|
7894
|
+
group,
|
|
7895
|
+
"task-role",
|
|
7896
|
+
{
|
|
7897
|
+
name: shortId(`${shortName}:task-role`),
|
|
7898
|
+
description: name,
|
|
7899
|
+
assumeRolePolicy: JSON.stringify({
|
|
7900
|
+
Version: "2012-10-17",
|
|
7901
|
+
Statement: [
|
|
7902
|
+
{
|
|
7903
|
+
Effect: "Allow",
|
|
7904
|
+
Action: "sts:AssumeRole",
|
|
7905
|
+
Principal: {
|
|
7906
|
+
Service: ["ecs-tasks.amazonaws.com"]
|
|
7907
|
+
}
|
|
7908
|
+
}
|
|
7909
|
+
]
|
|
7910
|
+
}),
|
|
7911
|
+
inlinePolicy: [
|
|
7912
|
+
{
|
|
7913
|
+
name: "s3-code-access",
|
|
7914
|
+
policy: $resolve([code.bucket, code.key], (bucket, key) => {
|
|
7915
|
+
return JSON.stringify({
|
|
7916
|
+
Version: "2012-10-17",
|
|
7917
|
+
Statement: [
|
|
7918
|
+
{
|
|
7919
|
+
Effect: pascalCase4("allow"),
|
|
7920
|
+
Action: ["s3:getObject", "s3:HeadObject"],
|
|
7921
|
+
Resource: `arn:aws:s3:::${bucket}/${key}`
|
|
7922
|
+
}
|
|
7923
|
+
]
|
|
7924
|
+
});
|
|
7925
|
+
})
|
|
7926
|
+
}
|
|
7927
|
+
]
|
|
7928
|
+
},
|
|
7929
|
+
{
|
|
7930
|
+
dependsOn: [code]
|
|
7931
|
+
}
|
|
7932
|
+
);
|
|
7933
|
+
const statements = [];
|
|
7934
|
+
const statementDeps = /* @__PURE__ */ new Set();
|
|
7935
|
+
const policy = new aws28.iam.RolePolicy(group, "policy", {
|
|
7936
|
+
role: role.name,
|
|
7937
|
+
name: "task-policy",
|
|
7938
|
+
policy: new Output8(statementDeps, async (resolve2) => {
|
|
7939
|
+
const list3 = await resolveInputs5(statements);
|
|
7940
|
+
resolve2(
|
|
7941
|
+
JSON.stringify({
|
|
7942
|
+
Version: "2012-10-17",
|
|
7943
|
+
Statement: list3.map((statement) => ({
|
|
7944
|
+
Effect: pascalCase4(statement.effect ?? "allow"),
|
|
7945
|
+
Action: statement.actions,
|
|
7946
|
+
Resource: statement.resources
|
|
7947
|
+
}))
|
|
7948
|
+
})
|
|
7949
|
+
);
|
|
7950
|
+
})
|
|
7951
|
+
});
|
|
7952
|
+
const addPermission = (...permissions) => {
|
|
7953
|
+
statements.push(...permissions);
|
|
7954
|
+
for (const dep of findInputDeps5(permissions)) {
|
|
7955
|
+
statementDeps.add(dep);
|
|
7956
|
+
}
|
|
7957
|
+
};
|
|
7958
|
+
ctx.onPermission((statement) => {
|
|
7959
|
+
addPermission(statement);
|
|
7960
|
+
});
|
|
7961
|
+
let logGroup;
|
|
7962
|
+
if (props.log.retention && props.log.retention.value > 0n) {
|
|
7963
|
+
logGroup = new aws28.cloudwatch.LogGroup(group, "log", {
|
|
7964
|
+
name: `/aws/ecs/${name}`,
|
|
7965
|
+
// name: `/aws/lambda/${name}`,
|
|
7966
|
+
retentionInDays: toDays10(props.log.retention)
|
|
7967
|
+
});
|
|
7968
|
+
if (ctx.shared.has("on-error-log", "subscriber-arn")) {
|
|
7969
|
+
new aws28.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
|
|
7970
|
+
name: "error-log-subscription",
|
|
7971
|
+
destinationArn: ctx.shared.get("on-error-log", "subscriber-arn"),
|
|
7972
|
+
logGroupName: logGroup.name,
|
|
7973
|
+
filterPattern
|
|
7974
|
+
});
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
const tags = {
|
|
7978
|
+
APP: ctx.appConfig.name,
|
|
7979
|
+
APP_ID: ctx.appId,
|
|
7980
|
+
STACK: ctx.stackConfig.name
|
|
7981
|
+
};
|
|
7982
|
+
const variables = {};
|
|
7983
|
+
const variableDeps = /* @__PURE__ */ new Set();
|
|
7984
|
+
const task2 = new aws28.ecs.TaskDefinition(
|
|
7985
|
+
group,
|
|
7986
|
+
"task",
|
|
7987
|
+
{
|
|
7988
|
+
family: name,
|
|
7989
|
+
networkMode: "awsvpc",
|
|
7990
|
+
cpu: props.cpu,
|
|
7991
|
+
memory: toMebibytes6(props.memorySize).toString(),
|
|
7992
|
+
requiresCompatibilities: ["FARGATE"],
|
|
7993
|
+
executionRoleArn: executionRole.arn,
|
|
7994
|
+
taskRoleArn: role.arn,
|
|
7995
|
+
runtimePlatform: {
|
|
7996
|
+
cpuArchitecture: constantCase13(props.architecture),
|
|
7997
|
+
operatingSystemFamily: "LINUX"
|
|
7998
|
+
},
|
|
7999
|
+
trackLatest: true,
|
|
8000
|
+
containerDefinitions: new Output8(variableDeps, async (resolve2) => {
|
|
8001
|
+
const data = await resolveInputs5(variables);
|
|
8002
|
+
const { s3Bucket, s3Key } = await resolveInputs5({
|
|
8003
|
+
s3Bucket: code.bucket,
|
|
8004
|
+
s3Key: code.key
|
|
8005
|
+
});
|
|
8006
|
+
resolve2(
|
|
7450
8007
|
JSON.stringify([
|
|
7451
8008
|
{
|
|
7452
8009
|
name: `container-${id}`,
|
|
@@ -7497,12 +8054,12 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7497
8054
|
healthCheck: props.healthCheck ? {
|
|
7498
8055
|
command: [
|
|
7499
8056
|
"CMD-SHELL",
|
|
7500
|
-
`curl -f http://${
|
|
8057
|
+
`curl -f http://${join19("localhost", props.healthCheck.path)} || exit 1`
|
|
7501
8058
|
],
|
|
7502
|
-
interval:
|
|
8059
|
+
interval: toSeconds10(props.healthCheck.interval),
|
|
7503
8060
|
retries: props.healthCheck.retries,
|
|
7504
|
-
startPeriod:
|
|
7505
|
-
timeout:
|
|
8061
|
+
startPeriod: toSeconds10(props.healthCheck.startPeriod),
|
|
8062
|
+
timeout: toSeconds10(props.healthCheck.timeout)
|
|
7506
8063
|
} : void 0
|
|
7507
8064
|
}
|
|
7508
8065
|
])
|
|
@@ -7522,14 +8079,14 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7522
8079
|
dependsOn: [code]
|
|
7523
8080
|
}
|
|
7524
8081
|
);
|
|
7525
|
-
const securityGroup = new
|
|
8082
|
+
const securityGroup = new aws28.security.Group(group, "security-group", {
|
|
7526
8083
|
name,
|
|
7527
8084
|
description: "Security group for the instance",
|
|
7528
8085
|
vpcId: ctx.shared.get("vpc", "id"),
|
|
7529
8086
|
revokeRulesOnDelete: true,
|
|
7530
8087
|
tags
|
|
7531
8088
|
});
|
|
7532
|
-
new
|
|
8089
|
+
new aws28.vpc.SecurityGroupEgressRule(group, "egress-rule", {
|
|
7533
8090
|
securityGroupId: securityGroup.id,
|
|
7534
8091
|
description: `Allow all outbound traffic from the ${name} instance`,
|
|
7535
8092
|
ipProtocol: "-1",
|
|
@@ -7538,7 +8095,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7538
8095
|
});
|
|
7539
8096
|
const clusterName = ctx.shared.get("instance", "cluster-name");
|
|
7540
8097
|
const clusterArn = ctx.shared.get("instance", "cluster-arn");
|
|
7541
|
-
const service = new
|
|
8098
|
+
const service = new aws28.ecs.Service(
|
|
7542
8099
|
group,
|
|
7543
8100
|
"service",
|
|
7544
8101
|
{
|
|
@@ -7574,7 +8131,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7574
8131
|
replaceOnChanges: ["cluster"]
|
|
7575
8132
|
}
|
|
7576
8133
|
);
|
|
7577
|
-
new
|
|
8134
|
+
new aws28.appautoscaling.Target(
|
|
7578
8135
|
group,
|
|
7579
8136
|
"autoscaling-target",
|
|
7580
8137
|
{
|
|
@@ -7593,7 +8150,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7593
8150
|
);
|
|
7594
8151
|
ctx.onEnv((name2, value) => {
|
|
7595
8152
|
variables[name2] = value;
|
|
7596
|
-
for (const dep of
|
|
8153
|
+
for (const dep of findInputDeps5([value])) {
|
|
7597
8154
|
variableDeps.add(dep);
|
|
7598
8155
|
}
|
|
7599
8156
|
});
|
|
@@ -7602,7 +8159,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7602
8159
|
variables.AWS_ACCOUNT_ID = ctx.accountId;
|
|
7603
8160
|
variables.STACK = ctx.stackConfig.name;
|
|
7604
8161
|
variables.CODE_HASH = code.sourceHash;
|
|
7605
|
-
variables.INSTANCE_CONFIG_HASH =
|
|
8162
|
+
variables.INSTANCE_CONFIG_HASH = createHash6("sha1").update(stringify2(props)).digest("hex");
|
|
7606
8163
|
if (props.environment) {
|
|
7607
8164
|
for (const [key, value] of Object.entries(props.environment)) {
|
|
7608
8165
|
variables[key] = value;
|
|
@@ -7621,8 +8178,8 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
|
|
|
7621
8178
|
var instanceFeature = defineFeature({
|
|
7622
8179
|
name: "instance",
|
|
7623
8180
|
onBefore(ctx) {
|
|
7624
|
-
const group = new
|
|
7625
|
-
const bucket = new
|
|
8181
|
+
const group = new Group28(ctx.base, "instance", "asset");
|
|
8182
|
+
const bucket = new aws29.s3.Bucket(group, "bucket", {
|
|
7626
8183
|
bucket: formatGlobalResourceName({
|
|
7627
8184
|
appName: ctx.app.name,
|
|
7628
8185
|
resourceType: "instance",
|
|
@@ -7640,8 +8197,8 @@ var instanceFeature = defineFeature({
|
|
|
7640
8197
|
if (found.length === 0) {
|
|
7641
8198
|
return;
|
|
7642
8199
|
}
|
|
7643
|
-
const group = new
|
|
7644
|
-
const cluster = new
|
|
8200
|
+
const group = new Group28(ctx.base, "instance", "cluster");
|
|
8201
|
+
const cluster = new aws29.ecs.Cluster(
|
|
7645
8202
|
group,
|
|
7646
8203
|
"cluster",
|
|
7647
8204
|
{
|
|
@@ -7656,18 +8213,18 @@ var instanceFeature = defineFeature({
|
|
|
7656
8213
|
},
|
|
7657
8214
|
onStack(ctx) {
|
|
7658
8215
|
for (const [id, props] of Object.entries(ctx.stackConfig.instances ?? {})) {
|
|
7659
|
-
const group = new
|
|
8216
|
+
const group = new Group28(ctx.stack, "instance", id);
|
|
7660
8217
|
createFargateTask(group, ctx, "instance", id, props);
|
|
7661
8218
|
}
|
|
7662
8219
|
}
|
|
7663
8220
|
});
|
|
7664
8221
|
|
|
7665
8222
|
// 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
|
|
8223
|
+
import { Group as Group29 } from "@terraforge/core";
|
|
8224
|
+
import { aws as aws30 } from "@terraforge/aws";
|
|
8225
|
+
import { kebabCase as kebabCase8, constantCase as constantCase14 } from "change-case";
|
|
8226
|
+
import { toSeconds as toSeconds11 } from "@awsless/duration";
|
|
8227
|
+
var typeGenCode11 = `
|
|
7671
8228
|
import { type PutDataProps, putData, batchPutData } from '@awsless/cloudwatch'
|
|
7672
8229
|
import { type Duration } from '@awsless/duration'
|
|
7673
8230
|
import { type Size } from '@awsless/size'
|
|
@@ -7715,7 +8272,7 @@ var metricFeature = defineFeature({
|
|
|
7715
8272
|
resources2.addType(stack.name, stackResources);
|
|
7716
8273
|
}
|
|
7717
8274
|
resources2.addType("batch", "Batch");
|
|
7718
|
-
gen.addCode(
|
|
8275
|
+
gen.addCode(typeGenCode11);
|
|
7719
8276
|
gen.addInterface("MetricResources", resources2);
|
|
7720
8277
|
await ctx.write("metric.d.ts", gen, true);
|
|
7721
8278
|
},
|
|
@@ -7732,16 +8289,16 @@ var metricFeature = defineFeature({
|
|
|
7732
8289
|
}
|
|
7733
8290
|
});
|
|
7734
8291
|
for (const [id, props] of Object.entries(ctx.stackConfig.metrics ?? {})) {
|
|
7735
|
-
const group = new
|
|
7736
|
-
ctx.addEnv(`METRIC_${
|
|
8292
|
+
const group = new Group29(ctx.stack, "metric", id);
|
|
8293
|
+
ctx.addEnv(`METRIC_${constantCase14(id)}`, props.type);
|
|
7737
8294
|
for (const alarmId in props.alarms ?? []) {
|
|
7738
|
-
const alarmGroup = new
|
|
8295
|
+
const alarmGroup = new Group29(group, "alarm", alarmId);
|
|
7739
8296
|
const alarmName = kebabCase8(`${id}-${alarmId}`);
|
|
7740
8297
|
const alarmProps = props.alarms[alarmId];
|
|
7741
8298
|
let alarmAction;
|
|
7742
8299
|
let alarmLambda;
|
|
7743
8300
|
if (Array.isArray(alarmProps.trigger)) {
|
|
7744
|
-
const topic = new
|
|
8301
|
+
const topic = new aws30.sns.Topic(alarmGroup, "alarm-trigger", {
|
|
7745
8302
|
name: formatLocalResourceName({
|
|
7746
8303
|
appName: ctx.app.name,
|
|
7747
8304
|
stackName: ctx.stack.name,
|
|
@@ -7751,7 +8308,7 @@ var metricFeature = defineFeature({
|
|
|
7751
8308
|
});
|
|
7752
8309
|
alarmAction = topic.arn;
|
|
7753
8310
|
for (const email of alarmProps.trigger) {
|
|
7754
|
-
new
|
|
8311
|
+
new aws30.sns.TopicSubscription(alarmGroup, email, {
|
|
7755
8312
|
topicArn: topic.arn,
|
|
7756
8313
|
protocol: "email",
|
|
7757
8314
|
endpoint: email
|
|
@@ -7762,7 +8319,7 @@ var metricFeature = defineFeature({
|
|
|
7762
8319
|
alarmLambda = lambda;
|
|
7763
8320
|
alarmAction = lambda.arn;
|
|
7764
8321
|
}
|
|
7765
|
-
const alarm = new
|
|
8322
|
+
const alarm = new aws30.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
|
|
7766
8323
|
namespace,
|
|
7767
8324
|
metricName: kebabCase8(id),
|
|
7768
8325
|
alarmName: formatLocalResourceName({
|
|
@@ -7774,13 +8331,13 @@ var metricFeature = defineFeature({
|
|
|
7774
8331
|
alarmDescription: alarmProps.description,
|
|
7775
8332
|
statistic: alarmProps.where.stat,
|
|
7776
8333
|
threshold: alarmProps.where.value,
|
|
7777
|
-
period:
|
|
8334
|
+
period: toSeconds11(alarmProps.period),
|
|
7778
8335
|
evaluationPeriods: alarmProps.minDataPoints,
|
|
7779
8336
|
comparisonOperator: alarmProps.where.op,
|
|
7780
8337
|
alarmActions: [alarmAction]
|
|
7781
8338
|
});
|
|
7782
8339
|
if (alarmLambda) {
|
|
7783
|
-
new
|
|
8340
|
+
new aws30.lambda.Permission(alarmGroup, "permission", {
|
|
7784
8341
|
action: "lambda:InvokeFunction",
|
|
7785
8342
|
principal: "lambda.alarms.cloudwatch.amazonaws.com",
|
|
7786
8343
|
functionName: alarmLambda.functionName,
|
|
@@ -7793,10 +8350,10 @@ var metricFeature = defineFeature({
|
|
|
7793
8350
|
});
|
|
7794
8351
|
|
|
7795
8352
|
// 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
|
|
8353
|
+
import { days as days9, seconds as seconds10, toSeconds as toSeconds12, years } from "@awsless/duration";
|
|
8354
|
+
import { Future, Group as Group30 } from "@terraforge/core";
|
|
8355
|
+
import { aws as aws31 } from "@terraforge/aws";
|
|
8356
|
+
import { camelCase as camelCase9, constantCase as constantCase15 } from "change-case";
|
|
7800
8357
|
|
|
7801
8358
|
// src/feature/router/router-code.ts
|
|
7802
8359
|
var getViewerRequestFunctionCode = (props) => {
|
|
@@ -7990,6 +8547,18 @@ async function handler(event) {
|
|
|
7990
8547
|
const headers = request.headers;
|
|
7991
8548
|
const path = decodeURIComponent(request.uri);
|
|
7992
8549
|
|
|
8550
|
+
if (request.method === 'OPTIONS') {
|
|
8551
|
+
return {
|
|
8552
|
+
statusCode: 204,
|
|
8553
|
+
headers: {
|
|
8554
|
+
'access-control-allow-origin': { value: '*' },
|
|
8555
|
+
'access-control-allow-methods': { value: 'GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS' },
|
|
8556
|
+
'access-control-allow-headers': { value: '*' },
|
|
8557
|
+
'access-control-max-age': { value: '86400' }
|
|
8558
|
+
}
|
|
8559
|
+
};
|
|
8560
|
+
}
|
|
8561
|
+
|
|
7993
8562
|
${injection.join("\n")}
|
|
7994
8563
|
|
|
7995
8564
|
const route = await findRoute(path, request.method);
|
|
@@ -8032,18 +8601,18 @@ async function handler(event) {
|
|
|
8032
8601
|
`;
|
|
8033
8602
|
|
|
8034
8603
|
// src/feature/router/index.ts
|
|
8035
|
-
import { createHash as
|
|
8604
|
+
import { createHash as createHash7 } from "crypto";
|
|
8036
8605
|
var routerFeature = defineFeature({
|
|
8037
8606
|
name: "router",
|
|
8038
8607
|
onApp(ctx) {
|
|
8039
8608
|
for (const [id, props] of Object.entries(ctx.appConfig.defaults.router ?? {})) {
|
|
8040
|
-
const group = new
|
|
8609
|
+
const group = new Group30(ctx.base, "router", id);
|
|
8041
8610
|
const name = formatGlobalResourceName({
|
|
8042
8611
|
appName: ctx.app.name,
|
|
8043
8612
|
resourceType: "router",
|
|
8044
8613
|
resourceName: id
|
|
8045
8614
|
});
|
|
8046
|
-
const routeStore = new
|
|
8615
|
+
const routeStore = new aws31.cloudfront.KeyValueStore(group, "routes", {
|
|
8047
8616
|
name,
|
|
8048
8617
|
comment: "Store for routes"
|
|
8049
8618
|
});
|
|
@@ -8073,11 +8642,11 @@ var routerFeature = defineFeature({
|
|
|
8073
8642
|
);
|
|
8074
8643
|
importedRoutes.push(importKeys);
|
|
8075
8644
|
});
|
|
8076
|
-
const cache = new
|
|
8645
|
+
const cache = new aws31.cloudfront.CachePolicy(group, "cache", {
|
|
8077
8646
|
name,
|
|
8078
|
-
minTtl:
|
|
8079
|
-
maxTtl:
|
|
8080
|
-
defaultTtl:
|
|
8647
|
+
minTtl: toSeconds12(seconds10(0)),
|
|
8648
|
+
maxTtl: toSeconds12(days9(365)),
|
|
8649
|
+
defaultTtl: toSeconds12(days9(0)),
|
|
8081
8650
|
parametersInCacheKeyAndForwardedToOrigin: {
|
|
8082
8651
|
enableAcceptEncodingBrotli: true,
|
|
8083
8652
|
enableAcceptEncodingGzip: true,
|
|
@@ -8105,10 +8674,10 @@ var routerFeature = defineFeature({
|
|
|
8105
8674
|
}
|
|
8106
8675
|
}
|
|
8107
8676
|
});
|
|
8108
|
-
const originRequest = new
|
|
8677
|
+
const originRequest = new aws31.cloudfront.OriginRequestPolicy(group, "request", {
|
|
8109
8678
|
name,
|
|
8110
8679
|
headersConfig: {
|
|
8111
|
-
headerBehavior:
|
|
8680
|
+
headerBehavior: camelCase9("all-except"),
|
|
8112
8681
|
headers: {
|
|
8113
8682
|
items: [
|
|
8114
8683
|
"host"
|
|
@@ -8123,11 +8692,11 @@ var routerFeature = defineFeature({
|
|
|
8123
8692
|
queryStringBehavior: "all"
|
|
8124
8693
|
}
|
|
8125
8694
|
});
|
|
8126
|
-
const responseHeaders = new
|
|
8695
|
+
const responseHeaders = new aws31.cloudfront.ResponseHeadersPolicy(group, "response", {
|
|
8127
8696
|
name,
|
|
8128
8697
|
corsConfig: {
|
|
8129
8698
|
originOverride: props.cors?.override ?? true,
|
|
8130
|
-
accessControlMaxAgeSec:
|
|
8699
|
+
accessControlMaxAgeSec: toSeconds12(props.cors?.maxAge ?? years(1)),
|
|
8131
8700
|
accessControlAllowHeaders: { items: props.cors?.headers ?? ["*"] },
|
|
8132
8701
|
accessControlAllowMethods: { items: props.cors?.methods ?? ["ALL"] },
|
|
8133
8702
|
accessControlAllowOrigins: { items: props.cors?.origins ?? ["*"] },
|
|
@@ -8152,7 +8721,7 @@ var routerFeature = defineFeature({
|
|
|
8152
8721
|
strictTransportSecurity: {
|
|
8153
8722
|
override: true,
|
|
8154
8723
|
preload: true,
|
|
8155
|
-
accessControlMaxAgeSec:
|
|
8724
|
+
accessControlMaxAgeSec: toSeconds12(years(1)),
|
|
8156
8725
|
includeSubdomains: true
|
|
8157
8726
|
},
|
|
8158
8727
|
xssProtection: {
|
|
@@ -8162,7 +8731,7 @@ var routerFeature = defineFeature({
|
|
|
8162
8731
|
}
|
|
8163
8732
|
}
|
|
8164
8733
|
});
|
|
8165
|
-
const viewerRequest = new
|
|
8734
|
+
const viewerRequest = new aws31.cloudfront.Function(group, "viewer-request", {
|
|
8166
8735
|
name,
|
|
8167
8736
|
runtime: `cloudfront-js-2.0`,
|
|
8168
8737
|
comment: `Viewer Request - ${name}`,
|
|
@@ -8184,7 +8753,7 @@ var routerFeature = defineFeature({
|
|
|
8184
8753
|
rateBasedStatement: {
|
|
8185
8754
|
limit: wafSettingsConfig.rateLimiter.limit,
|
|
8186
8755
|
aggregateKeyType: "IP",
|
|
8187
|
-
evaluationWindowSec:
|
|
8756
|
+
evaluationWindowSec: toSeconds12(wafSettingsConfig.rateLimiter.window)
|
|
8188
8757
|
}
|
|
8189
8758
|
},
|
|
8190
8759
|
action: {
|
|
@@ -8264,7 +8833,7 @@ var routerFeature = defineFeature({
|
|
|
8264
8833
|
}
|
|
8265
8834
|
let waf;
|
|
8266
8835
|
if (wafRules.length && wafSettingsConfig) {
|
|
8267
|
-
waf = new
|
|
8836
|
+
waf = new aws31.wafv2.WebAcl(group, "waf", {
|
|
8268
8837
|
name: `${name}-wafv2`,
|
|
8269
8838
|
scope: "CLOUDFRONT",
|
|
8270
8839
|
defaultAction: {
|
|
@@ -8274,12 +8843,12 @@ var routerFeature = defineFeature({
|
|
|
8274
8843
|
rule: wafRules,
|
|
8275
8844
|
captchaConfig: {
|
|
8276
8845
|
immunityTimeProperty: {
|
|
8277
|
-
immunityTime:
|
|
8846
|
+
immunityTime: toSeconds12(wafSettingsConfig.captchaImmunityTime)
|
|
8278
8847
|
}
|
|
8279
8848
|
},
|
|
8280
8849
|
challengeConfig: {
|
|
8281
8850
|
immunityTimeProperty: {
|
|
8282
|
-
immunityTime:
|
|
8851
|
+
immunityTime: toSeconds12(wafSettingsConfig.challengeImmunityTime)
|
|
8283
8852
|
}
|
|
8284
8853
|
},
|
|
8285
8854
|
visibilityConfig: {
|
|
@@ -8289,7 +8858,7 @@ var routerFeature = defineFeature({
|
|
|
8289
8858
|
}
|
|
8290
8859
|
});
|
|
8291
8860
|
}
|
|
8292
|
-
const distribution = new
|
|
8861
|
+
const distribution = new aws31.cloudfront.MultitenantDistribution(group, "distribution", {
|
|
8293
8862
|
tags: {
|
|
8294
8863
|
name
|
|
8295
8864
|
},
|
|
@@ -8335,7 +8904,7 @@ var routerFeature = defineFeature({
|
|
|
8335
8904
|
}
|
|
8336
8905
|
return {
|
|
8337
8906
|
errorCode: Number(errorCode),
|
|
8338
|
-
errorCachingMinTtl: item.minTTL ?
|
|
8907
|
+
errorCachingMinTtl: item.minTTL ? toSeconds12(item.minTTL) : void 0,
|
|
8339
8908
|
responseCode: item.statusCode?.toString() ?? errorCode,
|
|
8340
8909
|
responsePagePath: item.path
|
|
8341
8910
|
};
|
|
@@ -8383,11 +8952,11 @@ var routerFeature = defineFeature({
|
|
|
8383
8952
|
{
|
|
8384
8953
|
distributionId: distribution.id,
|
|
8385
8954
|
paths,
|
|
8386
|
-
version: new Future((
|
|
8955
|
+
version: new Future((resolve2) => {
|
|
8387
8956
|
$combine(...versions).then((versions2) => {
|
|
8388
8957
|
const combined = versions2.filter((v) => !!v).sort().join(",");
|
|
8389
|
-
const version =
|
|
8390
|
-
|
|
8958
|
+
const version = createHash7("sha1").update(combined).digest("hex");
|
|
8959
|
+
resolve2(version);
|
|
8391
8960
|
});
|
|
8392
8961
|
})
|
|
8393
8962
|
},
|
|
@@ -8401,10 +8970,10 @@ var routerFeature = defineFeature({
|
|
|
8401
8970
|
const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
|
|
8402
8971
|
const certificateArn = ctx.shared.entry("domain", `global-certificate-arn`, props.domain);
|
|
8403
8972
|
const zoneId = ctx.shared.entry("domain", "zone-id", props.domain);
|
|
8404
|
-
const connectionGroup = new
|
|
8973
|
+
const connectionGroup = new aws31.cloudfront.ConnectionGroup(group, "connection-group", {
|
|
8405
8974
|
name
|
|
8406
8975
|
});
|
|
8407
|
-
new
|
|
8976
|
+
new aws31.cloudfront.DistributionTenant(group, `tenant`, {
|
|
8408
8977
|
name,
|
|
8409
8978
|
enabled: true,
|
|
8410
8979
|
distributionId: distribution.id,
|
|
@@ -8412,7 +8981,7 @@ var routerFeature = defineFeature({
|
|
|
8412
8981
|
domain: [{ domain: domainName }],
|
|
8413
8982
|
customizations: [{ certificate: [{ arn: certificateArn }] }]
|
|
8414
8983
|
});
|
|
8415
|
-
new
|
|
8984
|
+
new aws31.route53.Record(group, `record`, {
|
|
8416
8985
|
zoneId,
|
|
8417
8986
|
type: "A",
|
|
8418
8987
|
name: domainName,
|
|
@@ -8422,7 +8991,7 @@ var routerFeature = defineFeature({
|
|
|
8422
8991
|
evaluateTargetHealth: false
|
|
8423
8992
|
}
|
|
8424
8993
|
});
|
|
8425
|
-
ctx.bind(`ROUTER_${
|
|
8994
|
+
ctx.bind(`ROUTER_${constantCase15(id)}_ENDPOINT`, domainName);
|
|
8426
8995
|
}
|
|
8427
8996
|
}
|
|
8428
8997
|
}
|
|
@@ -8445,7 +9014,7 @@ var features = [
|
|
|
8445
9014
|
// 5
|
|
8446
9015
|
functionFeature,
|
|
8447
9016
|
instanceFeature,
|
|
8448
|
-
|
|
9017
|
+
jobFeature,
|
|
8449
9018
|
// graphqlFeature,
|
|
8450
9019
|
configFeature,
|
|
8451
9020
|
searchFeature,
|
|
@@ -9534,14 +10103,14 @@ import wildstring4 from "wildstring";
|
|
|
9534
10103
|
|
|
9535
10104
|
// src/cli/ui/complex/run-tests.ts
|
|
9536
10105
|
import { log as log18 } from "@awsless/clui";
|
|
9537
|
-
import { mkdir as mkdir4, readFile as
|
|
9538
|
-
import { join as
|
|
10106
|
+
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
10107
|
+
import { join as join21 } from "path";
|
|
9539
10108
|
import wildstring3 from "wildstring";
|
|
9540
|
-
import { parse as parse4, stringify as
|
|
10109
|
+
import { parse as parse4, stringify as stringify3 } from "@awsless/json";
|
|
9541
10110
|
import { generateFolderHash, loadWorkspace as loadWorkspace2 } from "@awsless/ts-file-cache";
|
|
9542
10111
|
|
|
9543
10112
|
// src/test/start.ts
|
|
9544
|
-
import { dirname as dirname9, join as
|
|
10113
|
+
import { dirname as dirname9, join as join20 } from "path";
|
|
9545
10114
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
9546
10115
|
import { configDefaults } from "vitest/config";
|
|
9547
10116
|
import { startVitest } from "vitest/node";
|
|
@@ -9579,7 +10148,7 @@ var startTest = async (props) => {
|
|
|
9579
10148
|
// },
|
|
9580
10149
|
setupFiles: [
|
|
9581
10150
|
//
|
|
9582
|
-
|
|
10151
|
+
join20(__dirname5, "test-global-setup.js")
|
|
9583
10152
|
]
|
|
9584
10153
|
// globalSetup: [
|
|
9585
10154
|
// //
|
|
@@ -9773,12 +10342,12 @@ var logTestErrors = (event) => {
|
|
|
9773
10342
|
};
|
|
9774
10343
|
var runTest = async (stack, dir, filters, workspace, opts) => {
|
|
9775
10344
|
await mkdir4(directories.test, { recursive: true });
|
|
9776
|
-
const file =
|
|
10345
|
+
const file = join21(directories.test, `${stack}.json`);
|
|
9777
10346
|
const fingerprint = await generateFolderHash(workspace, dir);
|
|
9778
10347
|
if (!process.env.NO_CACHE) {
|
|
9779
10348
|
const exists = await fileExist(file);
|
|
9780
10349
|
if (exists) {
|
|
9781
|
-
const raw = await
|
|
10350
|
+
const raw = await readFile6(file, { encoding: "utf8" });
|
|
9782
10351
|
const data = parse4(raw);
|
|
9783
10352
|
if (data.fingerprint === fingerprint) {
|
|
9784
10353
|
log18.step(
|
|
@@ -9821,9 +10390,9 @@ var runTest = async (stack, dir, filters, workspace, opts) => {
|
|
|
9821
10390
|
logTestLogs(result);
|
|
9822
10391
|
}
|
|
9823
10392
|
logTestErrors(result);
|
|
9824
|
-
await
|
|
10393
|
+
await writeFile4(
|
|
9825
10394
|
file,
|
|
9826
|
-
|
|
10395
|
+
stringify3({
|
|
9827
10396
|
...result,
|
|
9828
10397
|
fingerprint
|
|
9829
10398
|
})
|
|
@@ -10361,7 +10930,7 @@ var auth = (program2) => {
|
|
|
10361
10930
|
// src/cli/command/bind.ts
|
|
10362
10931
|
import { log as log24 } from "@awsless/clui";
|
|
10363
10932
|
import chalk4 from "chalk";
|
|
10364
|
-
import { constantCase as
|
|
10933
|
+
import { constantCase as constantCase16 } from "change-case";
|
|
10365
10934
|
var bind = (program2) => {
|
|
10366
10935
|
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) => {
|
|
10367
10936
|
await layout("bind", async ({ appConfig, stackConfigs }) => {
|
|
@@ -10388,10 +10957,10 @@ var bind = (program2) => {
|
|
|
10388
10957
|
const configList = opts.config ?? [];
|
|
10389
10958
|
const configs = {};
|
|
10390
10959
|
for (const name of configList) {
|
|
10391
|
-
configs[`CONFIG_${
|
|
10960
|
+
configs[`CONFIG_${constantCase16(name)}`] = name;
|
|
10392
10961
|
}
|
|
10393
10962
|
if (configList.length ?? 0 > 0) {
|
|
10394
|
-
log24.note("Bind Config", configList.map((v) => color.label(
|
|
10963
|
+
log24.note("Bind Config", configList.map((v) => color.label(constantCase16(v))).join("\n"));
|
|
10395
10964
|
}
|
|
10396
10965
|
if (commands11.length === 0) {
|
|
10397
10966
|
return "No command to execute.";
|
|
@@ -10430,7 +10999,7 @@ var bind = (program2) => {
|
|
|
10430
10999
|
|
|
10431
11000
|
// src/config/load/watch.ts
|
|
10432
11001
|
import { watch } from "chokidar";
|
|
10433
|
-
var watchConfig = async (options,
|
|
11002
|
+
var watchConfig = async (options, resolve2, reject) => {
|
|
10434
11003
|
await loadAppConfig(options);
|
|
10435
11004
|
debug("Start watching...");
|
|
10436
11005
|
const ext = "{json,jsonc,json5}";
|
|
@@ -10445,7 +11014,7 @@ var watchConfig = async (options, resolve, reject) => {
|
|
|
10445
11014
|
const appConfig = await loadAppConfig(options);
|
|
10446
11015
|
const stackConfigs = await loadStackConfigs(options);
|
|
10447
11016
|
validateFeatures({ appConfig, stackConfigs });
|
|
10448
|
-
|
|
11017
|
+
resolve2({ appConfig, stackConfigs });
|
|
10449
11018
|
} catch (error) {
|
|
10450
11019
|
reject(error);
|
|
10451
11020
|
}
|
|
@@ -10457,8 +11026,8 @@ var watchConfig = async (options, resolve, reject) => {
|
|
|
10457
11026
|
import { log as log25 } from "@awsless/clui";
|
|
10458
11027
|
|
|
10459
11028
|
// src/type-gen/generate.ts
|
|
10460
|
-
import { mkdir as mkdir5, writeFile as
|
|
10461
|
-
import { dirname as dirname10, join as
|
|
11029
|
+
import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
|
|
11030
|
+
import { dirname as dirname10, join as join22, relative as relative9 } from "path";
|
|
10462
11031
|
var generateTypes = async (props) => {
|
|
10463
11032
|
const files = [];
|
|
10464
11033
|
await Promise.all(
|
|
@@ -10467,13 +11036,13 @@ var generateTypes = async (props) => {
|
|
|
10467
11036
|
...props,
|
|
10468
11037
|
async write(file, data, include = false) {
|
|
10469
11038
|
const code = data?.toString("utf8");
|
|
10470
|
-
const path =
|
|
11039
|
+
const path = join22(directories.types, file);
|
|
10471
11040
|
if (code) {
|
|
10472
11041
|
if (include) {
|
|
10473
|
-
files.push(
|
|
11042
|
+
files.push(relative9(directories.root, path));
|
|
10474
11043
|
}
|
|
10475
11044
|
await mkdir5(dirname10(path), { recursive: true });
|
|
10476
|
-
await
|
|
11045
|
+
await writeFile5(path, code);
|
|
10477
11046
|
}
|
|
10478
11047
|
}
|
|
10479
11048
|
});
|
|
@@ -10481,7 +11050,7 @@ var generateTypes = async (props) => {
|
|
|
10481
11050
|
);
|
|
10482
11051
|
if (files.length) {
|
|
10483
11052
|
const code = files.map((file) => `/// <reference path='${file}' />`).join("\n");
|
|
10484
|
-
await
|
|
11053
|
+
await writeFile5(join22(directories.root, `awsless.d.ts`), code);
|
|
10485
11054
|
}
|
|
10486
11055
|
};
|
|
10487
11056
|
|
|
@@ -10505,9 +11074,9 @@ var dev = (program2) => {
|
|
|
10505
11074
|
logError(error);
|
|
10506
11075
|
}
|
|
10507
11076
|
);
|
|
10508
|
-
await new Promise((
|
|
10509
|
-
process.once("exit",
|
|
10510
|
-
process.once("SIGINT",
|
|
11077
|
+
await new Promise((resolve2) => {
|
|
11078
|
+
process.once("exit", resolve2);
|
|
11079
|
+
process.once("SIGINT", resolve2);
|
|
10511
11080
|
});
|
|
10512
11081
|
});
|
|
10513
11082
|
});
|
|
@@ -10907,7 +11476,7 @@ var domain = (program2) => {
|
|
|
10907
11476
|
// src/cli/command/logs.ts
|
|
10908
11477
|
import { CloudWatchLogsClient, StartLiveTailCommand } from "@aws-sdk/client-cloudwatch-logs";
|
|
10909
11478
|
import { log as log30 } from "@awsless/clui";
|
|
10910
|
-
import { aws as
|
|
11479
|
+
import { aws as aws32 } from "@terraforge/aws";
|
|
10911
11480
|
import chalk6 from "chalk";
|
|
10912
11481
|
import chunk2 from "chunk";
|
|
10913
11482
|
import { formatDate } from "date-fns";
|
|
@@ -10930,7 +11499,7 @@ var logs = (program2) => {
|
|
|
10930
11499
|
for (const stack of app.stacks) {
|
|
10931
11500
|
if (filters.find((f) => wildstring7.match(f, stack.name))) {
|
|
10932
11501
|
for (const resource of stack.resources) {
|
|
10933
|
-
if (resource instanceof
|
|
11502
|
+
if (resource instanceof aws32.cloudwatch.LogGroup) {
|
|
10934
11503
|
logGroupArns.push(await resource.arn);
|
|
10935
11504
|
}
|
|
10936
11505
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awsless/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -88,21 +88,21 @@
|
|
|
88
88
|
"zod-to-json-schema": "^3.24.3",
|
|
89
89
|
"@awsless/big-float": "^0.1.6",
|
|
90
90
|
"@awsless/cloudwatch": "^0.0.1",
|
|
91
|
-
"@awsless/duration": "^0.0.4",
|
|
92
91
|
"@awsless/clui": "^0.0.8",
|
|
92
|
+
"@awsless/duration": "^0.0.4",
|
|
93
|
+
"@awsless/dynamodb": "^0.3.20",
|
|
93
94
|
"@awsless/iot": "^0.0.5",
|
|
94
95
|
"@awsless/json": "^0.0.11",
|
|
95
|
-
"@awsless/lambda": "^0.0.42",
|
|
96
96
|
"@awsless/s3": "^0.0.21",
|
|
97
|
+
"@awsless/scheduler": "^0.0.4",
|
|
97
98
|
"@awsless/size": "^0.0.2",
|
|
98
|
-
"@awsless/
|
|
99
|
-
"@awsless/redis": "^0.0.16",
|
|
99
|
+
"@awsless/lambda": "^0.0.42",
|
|
100
100
|
"@awsless/sns": "^0.0.10",
|
|
101
|
-
"@awsless/
|
|
102
|
-
"@awsless/ts-file-cache": "^0.0.12",
|
|
103
|
-
"@awsless/scheduler": "^0.0.4",
|
|
101
|
+
"@awsless/redis": "^0.0.16",
|
|
104
102
|
"@awsless/weak-cache": "^0.0.1",
|
|
105
103
|
"awsless": "^0.0.3",
|
|
104
|
+
"@awsless/ts-file-cache": "^0.0.12",
|
|
105
|
+
"@awsless/sqs": "^0.0.21",
|
|
106
106
|
"@awsless/validate": "^0.1.7"
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|