carlin 1.40.2 → 1.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -330
- package/dist/index.js +219 -56
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,340 +1,12 @@
|
|
|
1
1
|
# carlin
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
3
|
+
CLI tool for deploying AWS cloud resources using CloudFormation templates.
|
|
6
4
|
|
|
7
5
|
```bash
|
|
8
6
|
pnpm add -D carlin
|
|
9
7
|
```
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
pnpm add -g carlin
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Quick Start
|
|
18
|
-
|
|
19
|
-
Deploy your CloudFormation template with a single command:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
carlin deploy
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
carlin automatically:
|
|
26
|
-
|
|
27
|
-
- Searches for your CloudFormation template (`cloudformation.ts`, `cloudformation.yml`, or `template.yml`)
|
|
28
|
-
- Builds and uploads Lambda function code to S3
|
|
29
|
-
- Creates or updates your CloudFormation stack
|
|
30
|
-
- Displays stack outputs after deployment
|
|
31
|
-
|
|
32
|
-
### First Deployment Example
|
|
33
|
-
|
|
34
|
-
Create a simple CloudFormation template:
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
// cloudformation.ts
|
|
38
|
-
export const template = {
|
|
39
|
-
Resources: {
|
|
40
|
-
MyBucket: {
|
|
41
|
-
Type: 'AWS::S3::Bucket',
|
|
42
|
-
Properties: {
|
|
43
|
-
BucketName: 'my-app-bucket',
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
Outputs: {
|
|
48
|
-
BucketName: {
|
|
49
|
-
Value: { Ref: 'MyBucket' },
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Deploy it:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
carlin deploy
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
That's it! carlin creates the stack and outputs the bucket name.
|
|
62
|
-
|
|
63
|
-
## Core Concepts
|
|
64
|
-
|
|
65
|
-
### Stack Naming
|
|
66
|
-
|
|
67
|
-
carlin automatically generates CloudFormation stack names based on:
|
|
68
|
-
|
|
69
|
-
1. Package name from `package.json`
|
|
70
|
-
2. Environment (staging, production) or branch name
|
|
71
|
-
3. Custom `--stack-name` option (overrides automatic naming)
|
|
72
|
-
|
|
73
|
-
Example stack names:
|
|
74
|
-
|
|
75
|
-
- `my-app-production` (package: `my-app`, environment: `production`)
|
|
76
|
-
- `my-app-feature-auth` (package: `my-app`, branch: `feature/auth`)
|
|
77
|
-
- `CustomStack` (using `--stack-name CustomStack`)
|
|
78
|
-
|
|
79
|
-
### Environments
|
|
80
|
-
|
|
81
|
-
Define environments using:
|
|
82
|
-
|
|
83
|
-
- `--environment` flag: `carlin deploy --environment production`
|
|
84
|
-
- `CARLIN_ENVIRONMENT` variable: `CARLIN_ENVIRONMENT=staging carlin deploy`
|
|
85
|
-
- Config file: `carlin.yml` with `environment: production`
|
|
86
|
-
|
|
87
|
-
Environments enable:
|
|
88
|
-
|
|
89
|
-
- Automatic termination protection for production stacks
|
|
90
|
-
- Environment-specific configurations
|
|
91
|
-
- Multi-stage deployment workflows
|
|
92
|
-
|
|
93
|
-
### Lambda Functions
|
|
94
|
-
|
|
95
|
-
carlin automatically handles Lambda deployment:
|
|
96
|
-
|
|
97
|
-
1. Detects Lambda functions in your CloudFormation template
|
|
98
|
-
2. Builds code using esbuild
|
|
99
|
-
3. Uploads to S3 with versioning
|
|
100
|
-
4. Injects S3 parameters into your template
|
|
101
|
-
|
|
102
|
-
Example Lambda function:
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
// src/handler.ts
|
|
106
|
-
export const handler = async (event) => {
|
|
107
|
-
return { statusCode: 200, body: 'Hello World' };
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// cloudformation.ts
|
|
111
|
-
export const template = {
|
|
112
|
-
Resources: {
|
|
113
|
-
MyFunction: {
|
|
114
|
-
Type: 'AWS::Lambda::Function',
|
|
115
|
-
Properties: {
|
|
116
|
-
Runtime: 'nodejs20.x',
|
|
117
|
-
Handler: 'handler.handler', // carlin finds and builds src/handler.ts
|
|
118
|
-
Code: {
|
|
119
|
-
S3Bucket: { Ref: 'LambdaS3Bucket' },
|
|
120
|
-
S3Key: { Ref: 'LambdaS3Key' },
|
|
121
|
-
S3ObjectVersion: { Ref: 'LambdaS3ObjectVersion' },
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Base Stack
|
|
130
|
-
|
|
131
|
-
The base stack provides shared infrastructure:
|
|
132
|
-
|
|
133
|
-
- S3 bucket for Lambda code storage
|
|
134
|
-
- CloudFront distribution for static apps
|
|
135
|
-
- Lambda image builder for container deployments
|
|
136
|
-
|
|
137
|
-
Deploy base stack once per environment:
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
carlin deploy base-stack --environment production
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
## Commands
|
|
144
|
-
|
|
145
|
-
### deploy
|
|
146
|
-
|
|
147
|
-
Deploy CloudFormation stacks and resources.
|
|
148
|
-
|
|
149
|
-
```bash
|
|
150
|
-
carlin deploy [options]
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
#### Main Options
|
|
154
|
-
|
|
155
|
-
- `--template-path <path>` - CloudFormation template file path
|
|
156
|
-
- `--stack-name <name>` - Custom stack name
|
|
157
|
-
- `--parameters <json>` - CloudFormation parameters as JSON object
|
|
158
|
-
- `--environment <env>` - Environment name (enables termination protection)
|
|
159
|
-
- `--region <region>` - AWS region (default: `us-east-1`)
|
|
160
|
-
- `--destroy` - Delete the stack instead of deploying
|
|
161
|
-
|
|
162
|
-
#### Subcommands
|
|
163
|
-
|
|
164
|
-
- `deploy static-app` - Deploy static websites to S3 + CloudFront
|
|
165
|
-
- `deploy lambda-layer` - Deploy Lambda layers
|
|
166
|
-
- `deploy base-stack` - Deploy base infrastructure stack
|
|
167
|
-
- `deploy cicd` - Deploy CI/CD pipeline infrastructure
|
|
168
|
-
- `deploy vercel` - Deploy to Vercel platform
|
|
169
|
-
- `deploy describe` - Show stack outputs without deploying
|
|
170
|
-
|
|
171
|
-
See [full command documentation](https://ttoss.dev/docs/carlin/commands/deploy) for all options and examples.
|
|
172
|
-
|
|
173
|
-
### generate-env
|
|
174
|
-
|
|
175
|
-
Generate `.env` files from CloudFormation stack outputs.
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
carlin generate-env --stack-name MyStack --output .env
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Fetches stack outputs and creates environment variables file:
|
|
182
|
-
|
|
183
|
-
```bash
|
|
184
|
-
# Generated by carlin
|
|
185
|
-
API_URL=https://api.example.com
|
|
186
|
-
BUCKET_NAME=my-app-bucket-xyz123
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### cicd-ecs-task-report
|
|
190
|
-
|
|
191
|
-
Report ECS task status to GitHub/Slack during CI/CD deployments.
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
carlin cicd-ecs-task-report --cluster my-cluster --task-arn <arn>
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
## Configuration
|
|
198
|
-
|
|
199
|
-
Configure carlin using CLI options, environment variables, or config files.
|
|
200
|
-
|
|
201
|
-
### Config File
|
|
202
|
-
|
|
203
|
-
Create `carlin.yml` in your project root:
|
|
204
|
-
|
|
205
|
-
```yaml
|
|
206
|
-
environment: staging
|
|
207
|
-
region: us-east-1
|
|
208
|
-
stackName: my-custom-stack
|
|
209
|
-
parameters:
|
|
210
|
-
DomainName: example.com
|
|
211
|
-
CertificateArn: arn:aws:acm:...
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Environment Variables
|
|
215
|
-
|
|
216
|
-
Prefix any option with `CARLIN_`:
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
CARLIN_ENVIRONMENT=production carlin deploy
|
|
220
|
-
CARLIN_REGION=eu-west-1 carlin deploy
|
|
221
|
-
CARLIN_STACK_NAME=CustomStack carlin deploy
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### Priority
|
|
225
|
-
|
|
226
|
-
Configuration priority (highest to lowest):
|
|
227
|
-
|
|
228
|
-
1. CLI options (`--environment production`)
|
|
229
|
-
2. Environment variables (`CARLIN_ENVIRONMENT=production`)
|
|
230
|
-
3. Config file (`carlin.yml`)
|
|
231
|
-
4. Defaults
|
|
232
|
-
|
|
233
|
-
## Advanced Usage
|
|
234
|
-
|
|
235
|
-
### Multi-Environment Deployment
|
|
236
|
-
|
|
237
|
-
Deploy to multiple environments with different configurations:
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
# Deploy to staging
|
|
241
|
-
carlin deploy --environment staging --parameters '{"InstanceType":"t3.micro"}'
|
|
242
|
-
|
|
243
|
-
# Deploy to production
|
|
244
|
-
carlin deploy --environment production --parameters '{"InstanceType":"t3.large"}'
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Custom Template Paths
|
|
248
|
-
|
|
249
|
-
Search for templates in custom locations:
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
carlin deploy --template-path infrastructure/cloudformation.ts
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Docker-Based Lambda Functions
|
|
256
|
-
|
|
257
|
-
Build Lambda functions from Dockerfiles:
|
|
258
|
-
|
|
259
|
-
```bash
|
|
260
|
-
carlin deploy --lambda-dockerfile Dockerfile
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
carlin automatically:
|
|
264
|
-
|
|
265
|
-
- Builds the Docker image
|
|
266
|
-
- Pushes to ECR
|
|
267
|
-
- Updates Lambda function with new image URI
|
|
268
|
-
|
|
269
|
-
### Termination Protection
|
|
270
|
-
|
|
271
|
-
Production stacks are protected from accidental deletion:
|
|
272
|
-
|
|
273
|
-
```bash
|
|
274
|
-
# This fails if environment is defined
|
|
275
|
-
carlin deploy --destroy --environment production
|
|
276
|
-
|
|
277
|
-
# Force deletion by removing environment
|
|
278
|
-
carlin deploy --destroy --stack-name my-stack-production
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### Branch-Based Deployments
|
|
282
|
-
|
|
283
|
-
Deploy feature branches for testing:
|
|
284
|
-
|
|
285
|
-
```bash
|
|
286
|
-
# On feature/auth branch
|
|
287
|
-
carlin deploy
|
|
288
|
-
# Creates stack: my-app-feature-auth
|
|
289
|
-
|
|
290
|
-
# On main branch
|
|
291
|
-
carlin deploy --environment production
|
|
292
|
-
# Creates stack: my-app-production
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
## Troubleshooting
|
|
296
|
-
|
|
297
|
-
### Stack Already Exists
|
|
298
|
-
|
|
299
|
-
If deployment fails with "Stack already exists", check:
|
|
300
|
-
|
|
301
|
-
- Stack name conflicts (use `--stack-name` to customize)
|
|
302
|
-
- Existing stacks in AWS Console (CloudFormation → Stacks)
|
|
303
|
-
- Branch name conflicts (different branches may create same stack name)
|
|
304
|
-
|
|
305
|
-
### Lambda Build Failures
|
|
306
|
-
|
|
307
|
-
If Lambda code building fails:
|
|
308
|
-
|
|
309
|
-
- Verify `Handler` path points to existing file (`src/handler.ts`)
|
|
310
|
-
- Check `--lambda-entry-points-base-dir` matches your source directory
|
|
311
|
-
- Review build logs for missing dependencies
|
|
312
|
-
|
|
313
|
-
### Template Too Large
|
|
314
|
-
|
|
315
|
-
CloudFormation templates over 51,200 bytes must be uploaded to S3:
|
|
316
|
-
|
|
317
|
-
- carlin automatically uploads large templates to base stack bucket
|
|
318
|
-
- Ensure base stack exists: `carlin deploy base-stack`
|
|
319
|
-
- Or reduce template size by removing comments/whitespace
|
|
320
|
-
|
|
321
|
-
### Permission Errors
|
|
322
|
-
|
|
323
|
-
Ensure AWS credentials have necessary permissions:
|
|
324
|
-
|
|
325
|
-
- `cloudformation:*` for stack operations
|
|
326
|
-
- `s3:*` for Lambda code uploads
|
|
327
|
-
- `iam:*` for creating roles
|
|
328
|
-
- `lambda:*` for function deployments
|
|
329
|
-
|
|
330
|
-
## Examples
|
|
331
|
-
|
|
332
|
-
- [Terezinha Farm API](https://github.com/ttoss/ttoss/tree/main/terezinha-farm/api)
|
|
333
|
-
- [POC - AWS Serverless REST API](https://github.com/ttoss/poc-aws-serverless-rest-api)
|
|
334
|
-
|
|
335
|
-
## Documentation
|
|
336
|
-
|
|
337
|
-
Full documentation: https://ttoss.dev/docs/carlin/
|
|
9
|
+
**[Documentation →](https://ttoss.dev/docs/carlin/)**
|
|
338
10
|
|
|
339
11
|
## License
|
|
340
12
|
|
package/dist/index.js
CHANGED
|
@@ -997,24 +997,24 @@ var getStackName = /* @__PURE__ */ __name(async () => {
|
|
|
997
997
|
}).join("-");
|
|
998
998
|
return limitStackName(name);
|
|
999
999
|
}, "getStackName");
|
|
1000
|
-
var deployErrorLogs = /* @__PURE__ */ __name(({ error, logPrefix:
|
|
1001
|
-
log5.error(
|
|
1002
|
-
log5.error(
|
|
1000
|
+
var deployErrorLogs = /* @__PURE__ */ __name(({ error, logPrefix: logPrefix24 }) => {
|
|
1001
|
+
log5.error(logPrefix24, `An error occurred. Cannot deploy ${logPrefix24}.`);
|
|
1002
|
+
log5.error(logPrefix24, "Error message: %j", error?.message);
|
|
1003
1003
|
}, "deployErrorLogs");
|
|
1004
|
-
var handleDeployError = /* @__PURE__ */ __name(({ error, logPrefix:
|
|
1004
|
+
var handleDeployError = /* @__PURE__ */ __name(({ error, logPrefix: logPrefix24 }) => {
|
|
1005
1005
|
deployErrorLogs({
|
|
1006
1006
|
error,
|
|
1007
|
-
logPrefix:
|
|
1007
|
+
logPrefix: logPrefix24
|
|
1008
1008
|
});
|
|
1009
1009
|
process.exit(1);
|
|
1010
1010
|
}, "handleDeployError");
|
|
1011
|
-
var handleDeployInitialization = /* @__PURE__ */ __name(async ({ logPrefix:
|
|
1012
|
-
log5.info(
|
|
1011
|
+
var handleDeployInitialization = /* @__PURE__ */ __name(async ({ logPrefix: logPrefix24, stackName: preDefinedStackName }) => {
|
|
1012
|
+
log5.info(logPrefix24, `Starting deploy ${logPrefix24}...`);
|
|
1013
1013
|
if (preDefinedStackName) {
|
|
1014
1014
|
setPreDefinedStackName(preDefinedStackName);
|
|
1015
1015
|
}
|
|
1016
1016
|
const stackName = await getStackName();
|
|
1017
|
-
log5.info(
|
|
1017
|
+
log5.info(logPrefix24, `stackName: ${stackName}`);
|
|
1018
1018
|
return {
|
|
1019
1019
|
stackName
|
|
1020
1020
|
};
|
|
@@ -3726,6 +3726,154 @@ var readDockerfile = /* @__PURE__ */ __name((dockerfilePath) => {
|
|
|
3726
3726
|
return "";
|
|
3727
3727
|
}
|
|
3728
3728
|
}, "readDockerfile");
|
|
3729
|
+
var logPrefix14 = "report";
|
|
3730
|
+
var getGitHubErrorMessage = /* @__PURE__ */ __name(async (response) => {
|
|
3731
|
+
try {
|
|
3732
|
+
const body = await response.json();
|
|
3733
|
+
if (body.message) {
|
|
3734
|
+
return `${response.status} ${response.statusText} - ${body.message}`;
|
|
3735
|
+
}
|
|
3736
|
+
} catch {
|
|
3737
|
+
}
|
|
3738
|
+
return `${response.status} ${response.statusText}`;
|
|
3739
|
+
}, "getGitHubErrorMessage");
|
|
3740
|
+
var GITHUB_PR_COMMENT_MARKER = "<!-- carlin-deploy-outputs -->";
|
|
3741
|
+
var readAllDeployFiles = /* @__PURE__ */ __name(async () => {
|
|
3742
|
+
const files = await glob("**/.carlin/*.json", {
|
|
3743
|
+
absolute: true,
|
|
3744
|
+
ignore: [
|
|
3745
|
+
"**/node_modules/**",
|
|
3746
|
+
`**/.carlin/${LATEST_DEPLOY_OUTPUTS_FILENAME}`
|
|
3747
|
+
]
|
|
3748
|
+
});
|
|
3749
|
+
const results = [];
|
|
3750
|
+
for (const file of files) {
|
|
3751
|
+
try {
|
|
3752
|
+
const raw = await fs3.promises.readFile(file, "utf-8");
|
|
3753
|
+
const content = JSON.parse(raw);
|
|
3754
|
+
if (content.stackName && content.outputs) {
|
|
3755
|
+
results.push(content);
|
|
3756
|
+
}
|
|
3757
|
+
} catch {
|
|
3758
|
+
log5.warn(logPrefix14, `Could not read deploy file: ${path.basename(file)}`);
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
return results.sort((a, b) => {
|
|
3762
|
+
return a.packageName.localeCompare(b.packageName);
|
|
3763
|
+
});
|
|
3764
|
+
}, "readAllDeployFiles");
|
|
3765
|
+
var buildMarkdownComment = /* @__PURE__ */ __name((deploys) => {
|
|
3766
|
+
const header = `${GITHUB_PR_COMMENT_MARKER}
|
|
3767
|
+
|
|
3768
|
+
## Deploy Outputs
|
|
3769
|
+
`;
|
|
3770
|
+
if (deploys.length === 0) {
|
|
3771
|
+
return `${header}
|
|
3772
|
+
No deploy outputs found.`;
|
|
3773
|
+
}
|
|
3774
|
+
const rows = deploys.flatMap(({ packageName, stackName, outputs }) => {
|
|
3775
|
+
return Object.values(outputs).map(({ OutputKey, OutputValue }) => {
|
|
3776
|
+
return `| \`${packageName}\` | \`${stackName}\` | \`${OutputKey}\` | ${OutputValue} |`;
|
|
3777
|
+
});
|
|
3778
|
+
});
|
|
3779
|
+
const table = [
|
|
3780
|
+
"| Package | Stack | Output Key | Output Value |",
|
|
3781
|
+
"|---------|-------|------------|--------------|",
|
|
3782
|
+
...rows
|
|
3783
|
+
].join("\n");
|
|
3784
|
+
return `${header}
|
|
3785
|
+
${table}`;
|
|
3786
|
+
}, "buildMarkdownComment");
|
|
3787
|
+
var getPrNumber = /* @__PURE__ */ __name(async ({ branch, repo, token }) => {
|
|
3788
|
+
const [owner] = repo.split("/");
|
|
3789
|
+
const response = await fetch(`https://api.github.com/repos/${repo}/pulls?head=${owner}:${branch}&state=open&per_page=1`, {
|
|
3790
|
+
headers: {
|
|
3791
|
+
Accept: "application/vnd.github+json",
|
|
3792
|
+
Authorization: `Bearer ${token}`,
|
|
3793
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
3794
|
+
}
|
|
3795
|
+
});
|
|
3796
|
+
if (!response.ok) {
|
|
3797
|
+
throw new Error(`GitHub API error fetching PR: ${await getGitHubErrorMessage(response)}`);
|
|
3798
|
+
}
|
|
3799
|
+
const prs = await response.json();
|
|
3800
|
+
if (prs.length === 0) {
|
|
3801
|
+
throw new Error(`No open PR found for branch: ${branch}`);
|
|
3802
|
+
}
|
|
3803
|
+
return prs[0].number;
|
|
3804
|
+
}, "getPrNumber");
|
|
3805
|
+
var findExistingComment = /* @__PURE__ */ __name(async ({ prNumber, repo, token }) => {
|
|
3806
|
+
const response = await fetch(`https://api.github.com/repos/${repo}/issues/${prNumber}/comments?per_page=100`, {
|
|
3807
|
+
headers: {
|
|
3808
|
+
Accept: "application/vnd.github+json",
|
|
3809
|
+
Authorization: `Bearer ${token}`,
|
|
3810
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
3811
|
+
}
|
|
3812
|
+
});
|
|
3813
|
+
if (!response.ok) {
|
|
3814
|
+
throw new Error(`GitHub API error fetching comments: ${await getGitHubErrorMessage(response)}`);
|
|
3815
|
+
}
|
|
3816
|
+
const comments = await response.json();
|
|
3817
|
+
return comments.find(({ body }) => {
|
|
3818
|
+
return body.includes(GITHUB_PR_COMMENT_MARKER);
|
|
3819
|
+
});
|
|
3820
|
+
}, "findExistingComment");
|
|
3821
|
+
var createOrUpdateComment = /* @__PURE__ */ __name(async ({ body, existingCommentId, prNumber, repo, token }) => {
|
|
3822
|
+
const url = existingCommentId ? `https://api.github.com/repos/${repo}/issues/comments/${existingCommentId}` : `https://api.github.com/repos/${repo}/issues/${prNumber}/comments`;
|
|
3823
|
+
const method = existingCommentId ? "PATCH" : "POST";
|
|
3824
|
+
const response = await fetch(url, {
|
|
3825
|
+
body: JSON.stringify({
|
|
3826
|
+
body
|
|
3827
|
+
}),
|
|
3828
|
+
headers: {
|
|
3829
|
+
Accept: "application/vnd.github+json",
|
|
3830
|
+
Authorization: `Bearer ${token}`,
|
|
3831
|
+
"Content-Type": "application/json",
|
|
3832
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
3833
|
+
},
|
|
3834
|
+
method
|
|
3835
|
+
});
|
|
3836
|
+
if (!response.ok) {
|
|
3837
|
+
throw new Error(`GitHub API error ${method} comment: ${await getGitHubErrorMessage(response)}`);
|
|
3838
|
+
}
|
|
3839
|
+
}, "createOrUpdateComment");
|
|
3840
|
+
var reportToGitHubPR = /* @__PURE__ */ __name(async () => {
|
|
3841
|
+
const token = process.env.GH_TOKEN;
|
|
3842
|
+
const repo = process.env.GITHUB_REPOSITORY;
|
|
3843
|
+
const branch = process.env.CARLIN_BRANCH;
|
|
3844
|
+
if (!token) {
|
|
3845
|
+
throw new Error("GH_TOKEN environment variable is required for --channel=github-pr");
|
|
3846
|
+
}
|
|
3847
|
+
if (!repo) {
|
|
3848
|
+
throw new Error("GITHUB_REPOSITORY environment variable is required for --channel=github-pr");
|
|
3849
|
+
}
|
|
3850
|
+
if (!branch) {
|
|
3851
|
+
throw new Error("CARLIN_BRANCH environment variable is required for --channel=github-pr");
|
|
3852
|
+
}
|
|
3853
|
+
log5.info(logPrefix14, "Reading deploy outputs from workspace...");
|
|
3854
|
+
const deploys = await readAllDeployFiles();
|
|
3855
|
+
log5.info(logPrefix14, `Found ${deploys.length} deploy file(s).`);
|
|
3856
|
+
const prNumber = await getPrNumber({
|
|
3857
|
+
branch,
|
|
3858
|
+
repo,
|
|
3859
|
+
token
|
|
3860
|
+
});
|
|
3861
|
+
log5.info(logPrefix14, `Reporting to PR #${prNumber}...`);
|
|
3862
|
+
const body = buildMarkdownComment(deploys);
|
|
3863
|
+
const existingComment = await findExistingComment({
|
|
3864
|
+
prNumber,
|
|
3865
|
+
repo,
|
|
3866
|
+
token
|
|
3867
|
+
});
|
|
3868
|
+
await createOrUpdateComment({
|
|
3869
|
+
body,
|
|
3870
|
+
existingCommentId: existingComment?.id,
|
|
3871
|
+
prNumber,
|
|
3872
|
+
repo,
|
|
3873
|
+
token
|
|
3874
|
+
});
|
|
3875
|
+
log5.info(logPrefix14, existingComment ? "PR comment updated." : "PR comment created.");
|
|
3876
|
+
}, "reportToGitHubPR");
|
|
3729
3877
|
|
|
3730
3878
|
// src/deploy/staticApp/findDefaultBuildFolder.ts
|
|
3731
3879
|
var defaultBuildFolders = [
|
|
@@ -3780,11 +3928,11 @@ var getStaticAppBucket = /* @__PURE__ */ __name(async ({ stackName }) => {
|
|
|
3780
3928
|
}
|
|
3781
3929
|
}, "getStaticAppBucket");
|
|
3782
3930
|
var CLOUDFRONT_DISTRIBUTION_ID = "CloudFrontDistributionId";
|
|
3783
|
-
var
|
|
3931
|
+
var logPrefix15 = "static-app";
|
|
3784
3932
|
var invalidateCloudFront = /* @__PURE__ */ __name(async ({ outputs }) => {
|
|
3785
|
-
log5.info(
|
|
3933
|
+
log5.info(logPrefix15, "Invalidating CloudFront...");
|
|
3786
3934
|
if (!outputs) {
|
|
3787
|
-
log5.info(
|
|
3935
|
+
log5.info(logPrefix15, "Invalidation: outputs do not exist.");
|
|
3788
3936
|
return;
|
|
3789
3937
|
}
|
|
3790
3938
|
const cloudFrontDistributionIDOutput = outputs.find((output) => {
|
|
@@ -3807,13 +3955,13 @@ var invalidateCloudFront = /* @__PURE__ */ __name(async ({ outputs }) => {
|
|
|
3807
3955
|
const cloudFront = new AWS.CloudFront();
|
|
3808
3956
|
try {
|
|
3809
3957
|
await cloudFront.createInvalidation(params).promise();
|
|
3810
|
-
log5.info(
|
|
3958
|
+
log5.info(logPrefix15, `CloudFront Distribution ID ${distributionId} invalidated with success.`);
|
|
3811
3959
|
} catch (err) {
|
|
3812
|
-
log5.error(
|
|
3813
|
-
log5.error(
|
|
3960
|
+
log5.error(logPrefix15, `Error while trying to invalidate CloudFront distribution ${distributionId}.`);
|
|
3961
|
+
log5.error(logPrefix15, err);
|
|
3814
3962
|
}
|
|
3815
3963
|
} else {
|
|
3816
|
-
log5.info(
|
|
3964
|
+
log5.info(logPrefix15, `Cannot invalidate because distribution does not exist.`);
|
|
3817
3965
|
}
|
|
3818
3966
|
}, "invalidateCloudFront");
|
|
3819
3967
|
|
|
@@ -4271,11 +4419,11 @@ var uploadBuiltAppToS3 = /* @__PURE__ */ __name(async ({ buildFolder: directory,
|
|
|
4271
4419
|
}, "uploadBuiltAppToS3");
|
|
4272
4420
|
|
|
4273
4421
|
// src/deploy/staticApp/deployStaticApp.ts
|
|
4274
|
-
var
|
|
4422
|
+
var logPrefix16 = "static-app";
|
|
4275
4423
|
var deployStaticApp = /* @__PURE__ */ __name(async ({ acm, aliases, appendIndexHtml, buildFolder, cloudfront, spa, hostedZoneName, region, skipUpload }) => {
|
|
4276
4424
|
try {
|
|
4277
4425
|
const { stackName } = await handleDeployInitialization({
|
|
4278
|
-
logPrefix:
|
|
4426
|
+
logPrefix: logPrefix16
|
|
4279
4427
|
});
|
|
4280
4428
|
const params = {
|
|
4281
4429
|
StackName: stackName
|
|
@@ -4327,7 +4475,7 @@ var deployStaticApp = /* @__PURE__ */ __name(async ({ acm, aliases, appendIndexH
|
|
|
4327
4475
|
} catch (error) {
|
|
4328
4476
|
handleDeployError({
|
|
4329
4477
|
error,
|
|
4330
|
-
logPrefix:
|
|
4478
|
+
logPrefix: logPrefix16
|
|
4331
4479
|
});
|
|
4332
4480
|
}
|
|
4333
4481
|
}, "deployStaticApp");
|
|
@@ -4403,7 +4551,7 @@ var deployStaticAppCommand = {
|
|
|
4403
4551
|
}
|
|
4404
4552
|
}, "handler")
|
|
4405
4553
|
};
|
|
4406
|
-
var
|
|
4554
|
+
var logPrefix17 = "deploy vercel";
|
|
4407
4555
|
var makeCommand = /* @__PURE__ */ __name((cmds) => {
|
|
4408
4556
|
return cmds.filter((cmd) => {
|
|
4409
4557
|
return cmd !== void 0 && cmd !== null && cmd !== "";
|
|
@@ -4411,7 +4559,7 @@ var makeCommand = /* @__PURE__ */ __name((cmds) => {
|
|
|
4411
4559
|
}, "makeCommand");
|
|
4412
4560
|
var deployVercel = /* @__PURE__ */ __name(async ({ token }) => {
|
|
4413
4561
|
try {
|
|
4414
|
-
log5.info(
|
|
4562
|
+
log5.info(logPrefix17, "Deploying on Vercel...");
|
|
4415
4563
|
const environment = getEnvironment();
|
|
4416
4564
|
const finalToken = token || process.env.VERCEL_TOKEN;
|
|
4417
4565
|
if (!finalToken) {
|
|
@@ -4446,11 +4594,11 @@ var deployVercel = /* @__PURE__ */ __name(async ({ token }) => {
|
|
|
4446
4594
|
} catch (error) {
|
|
4447
4595
|
handleDeployError({
|
|
4448
4596
|
error,
|
|
4449
|
-
logPrefix:
|
|
4597
|
+
logPrefix: logPrefix17
|
|
4450
4598
|
});
|
|
4451
4599
|
}
|
|
4452
4600
|
}, "deployVercel");
|
|
4453
|
-
var
|
|
4601
|
+
var logPrefix18 = "deploy vercel";
|
|
4454
4602
|
var options4 = {
|
|
4455
4603
|
token: {
|
|
4456
4604
|
describe: "Vercel authorization token.",
|
|
@@ -4465,7 +4613,7 @@ var deployVercelCommand = {
|
|
|
4465
4613
|
}, "builder"),
|
|
4466
4614
|
handler: /* @__PURE__ */ __name(({ destroy: destroy2, ...rest }) => {
|
|
4467
4615
|
if (destroy2) {
|
|
4468
|
-
log5.info(
|
|
4616
|
+
log5.info(logPrefix18, "Destroy Vercel deployment not implemented yet.");
|
|
4469
4617
|
} else {
|
|
4470
4618
|
deployVercel(rest);
|
|
4471
4619
|
}
|
|
@@ -4543,7 +4691,7 @@ var generateSSHCommandWithPwd = /* @__PURE__ */ __name(({ userName, host, passwo
|
|
|
4543
4691
|
}, "generateSSHCommandWithPwd");
|
|
4544
4692
|
|
|
4545
4693
|
// src/deploy/vm/deployVM.ts
|
|
4546
|
-
var
|
|
4694
|
+
var logPrefix19 = "deploy-vm";
|
|
4547
4695
|
var deployVM = /* @__PURE__ */ __name(async ({ userName, host, scriptPath, keyPath, password, port, fixPermissions = false }) => {
|
|
4548
4696
|
if (!userName || !host || !scriptPath) {
|
|
4549
4697
|
throw new Error("Missing required parameters: userName, host, scriptPath");
|
|
@@ -4564,27 +4712,27 @@ var deployVM = /* @__PURE__ */ __name(async ({ userName, host, scriptPath, keyPa
|
|
|
4564
4712
|
const permissionStr = permissions.toString(8);
|
|
4565
4713
|
const fixCommand = `chmod 400 ${keyPath}`;
|
|
4566
4714
|
if (fixPermissions) {
|
|
4567
|
-
log5.info(
|
|
4715
|
+
log5.info(logPrefix19, `Fixing SSH key permissions: ${keyPath} (${permissionStr} \u2192 400)`);
|
|
4568
4716
|
chmodSync(keyPath, 256);
|
|
4569
|
-
log5.info(
|
|
4717
|
+
log5.info(logPrefix19, `Permissions set to 400 (read-only by owner)`);
|
|
4570
4718
|
} else {
|
|
4571
|
-
log5.error(
|
|
4572
|
-
log5.error(
|
|
4573
|
-
log5.error(
|
|
4574
|
-
log5.error(
|
|
4719
|
+
log5.error(logPrefix19, `SSH key permissions too open: ${permissionStr} (octal)`);
|
|
4720
|
+
log5.error(logPrefix19, `SSH requires permissions 400 or 600`);
|
|
4721
|
+
log5.error(logPrefix19, `Fix manually: ${fixCommand}`);
|
|
4722
|
+
log5.error(logPrefix19, `Or run with: --fix-permissions`);
|
|
4575
4723
|
throw new Error(`Invalid SSH key permissions: ${permissionStr}. Expected 400 or 600.`);
|
|
4576
4724
|
}
|
|
4577
4725
|
} else {
|
|
4578
|
-
log5.info(
|
|
4726
|
+
log5.info(logPrefix19, `SSH key permissions OK: ${permissions.toString(8)}`);
|
|
4579
4727
|
}
|
|
4580
4728
|
} catch (error) {
|
|
4581
4729
|
if (error instanceof Error) {
|
|
4582
4730
|
if (error.message.includes("Invalid SSH key permissions")) {
|
|
4583
4731
|
throw error;
|
|
4584
4732
|
}
|
|
4585
|
-
log5.warn(
|
|
4733
|
+
log5.warn(logPrefix19, `Warning: Could not check key permissions: ${error.message}`);
|
|
4586
4734
|
} else {
|
|
4587
|
-
log5.warn(
|
|
4735
|
+
log5.warn(logPrefix19, "Warning: Could not check key permissions: Unknown error");
|
|
4588
4736
|
}
|
|
4589
4737
|
}
|
|
4590
4738
|
}
|
|
@@ -4619,15 +4767,15 @@ var deployVM = /* @__PURE__ */ __name(async ({ userName, host, scriptPath, keyPa
|
|
|
4619
4767
|
});
|
|
4620
4768
|
const validateStdin = /* @__PURE__ */ __name((stdin) => {
|
|
4621
4769
|
if (!stdin) {
|
|
4622
|
-
log5.error(
|
|
4770
|
+
log5.error(logPrefix19, "SSH process stdin is null or undefined");
|
|
4623
4771
|
return false;
|
|
4624
4772
|
}
|
|
4625
4773
|
if (stdin.destroyed) {
|
|
4626
|
-
log5.error(
|
|
4774
|
+
log5.error(logPrefix19, "SSH process stdin has been destroyed");
|
|
4627
4775
|
return false;
|
|
4628
4776
|
}
|
|
4629
4777
|
if (!stdin.writable) {
|
|
4630
|
-
log5.error(
|
|
4778
|
+
log5.error(logPrefix19, "SSH process stdin is not writable");
|
|
4631
4779
|
return false;
|
|
4632
4780
|
}
|
|
4633
4781
|
return true;
|
|
@@ -4638,7 +4786,7 @@ var deployVM = /* @__PURE__ */ __name(async ({ userName, host, scriptPath, keyPa
|
|
|
4638
4786
|
}
|
|
4639
4787
|
if (!existsSync(scriptPath)) {
|
|
4640
4788
|
const message = `Deployment script not found at path: ${scriptPath}`;
|
|
4641
|
-
log5.error(
|
|
4789
|
+
log5.error(logPrefix19, message);
|
|
4642
4790
|
reject(new Error(message));
|
|
4643
4791
|
return;
|
|
4644
4792
|
}
|
|
@@ -4648,7 +4796,7 @@ var deployVM = /* @__PURE__ */ __name(async ({ userName, host, scriptPath, keyPa
|
|
|
4648
4796
|
}
|
|
4649
4797
|
deployScript.pipe(sshProcess.stdin);
|
|
4650
4798
|
const sigintHandler = /* @__PURE__ */ __name(() => {
|
|
4651
|
-
log5.info(
|
|
4799
|
+
log5.info(logPrefix19, "Interrupting deployment...");
|
|
4652
4800
|
sshProcess.kill("SIGINT");
|
|
4653
4801
|
process.exit(130);
|
|
4654
4802
|
}, "sigintHandler");
|
|
@@ -4672,7 +4820,7 @@ var deployVM = /* @__PURE__ */ __name(async ({ userName, host, scriptPath, keyPa
|
|
|
4672
4820
|
}, "deployVM");
|
|
4673
4821
|
|
|
4674
4822
|
// src/deploy/vm/command.ts
|
|
4675
|
-
var
|
|
4823
|
+
var logPrefix20 = "deploy-vm";
|
|
4676
4824
|
var deployVMCommand = {
|
|
4677
4825
|
command: "vm",
|
|
4678
4826
|
describe: "Deploy to a VM via SSH by executing a deployment script",
|
|
@@ -4690,16 +4838,16 @@ var deployVMCommand = {
|
|
|
4690
4838
|
port,
|
|
4691
4839
|
fixPermissions
|
|
4692
4840
|
});
|
|
4693
|
-
log5.info(
|
|
4841
|
+
log5.info(logPrefix20, "Deployment completed successfully!");
|
|
4694
4842
|
} catch (error) {
|
|
4695
|
-
log5.error(
|
|
4843
|
+
log5.error(logPrefix20, "Deployment failed: %s", error.message);
|
|
4696
4844
|
process.exit(1);
|
|
4697
4845
|
}
|
|
4698
4846
|
}, "handler")
|
|
4699
4847
|
};
|
|
4700
4848
|
|
|
4701
4849
|
// src/deploy/command.ts
|
|
4702
|
-
var
|
|
4850
|
+
var logPrefix21 = "deploy";
|
|
4703
4851
|
var checkAwsAccountId = /* @__PURE__ */ __name(async (awsAccountId) => {
|
|
4704
4852
|
try {
|
|
4705
4853
|
const currentAwsAccountId = await getAwsAccountId();
|
|
@@ -4710,21 +4858,36 @@ var checkAwsAccountId = /* @__PURE__ */ __name(async (awsAccountId) => {
|
|
|
4710
4858
|
if (error.code === "CredentialsError") {
|
|
4711
4859
|
return;
|
|
4712
4860
|
}
|
|
4713
|
-
log5.error(
|
|
4861
|
+
log5.error(logPrefix21, error.message);
|
|
4714
4862
|
process.exit();
|
|
4715
4863
|
}
|
|
4716
4864
|
}, "checkAwsAccountId");
|
|
4717
|
-
var
|
|
4718
|
-
command: "
|
|
4719
|
-
describe: "
|
|
4720
|
-
|
|
4865
|
+
var reportDeployCommand = {
|
|
4866
|
+
command: "report",
|
|
4867
|
+
describe: "Report the outputs of the deployment.",
|
|
4868
|
+
builder: /* @__PURE__ */ __name((yargs3) => {
|
|
4869
|
+
return yargs3.options({
|
|
4870
|
+
channel: {
|
|
4871
|
+
choices: [
|
|
4872
|
+
"github-pr"
|
|
4873
|
+
],
|
|
4874
|
+
describe: 'Report deploy outputs to the specified channel. Use "github-pr" to post or update a PR comment with all workspace deploy outputs.',
|
|
4875
|
+
type: "string"
|
|
4876
|
+
}
|
|
4877
|
+
});
|
|
4878
|
+
}, "builder"),
|
|
4879
|
+
handler: /* @__PURE__ */ __name(async ({ stackName, channel }) => {
|
|
4721
4880
|
try {
|
|
4881
|
+
if (channel === "github-pr") {
|
|
4882
|
+
await reportToGitHubPR();
|
|
4883
|
+
return;
|
|
4884
|
+
}
|
|
4722
4885
|
const newStackName = stackName || await getStackName();
|
|
4723
4886
|
await printStackOutputsAfterDeploy({
|
|
4724
4887
|
stackName: newStackName
|
|
4725
4888
|
});
|
|
4726
4889
|
} catch (error) {
|
|
4727
|
-
log5.info(
|
|
4890
|
+
log5.info(logPrefix21, "Cannot report stack. Message: %s", error.message);
|
|
4728
4891
|
}
|
|
4729
4892
|
}, "handler")
|
|
4730
4893
|
};
|
|
@@ -4909,7 +5072,7 @@ var deployCommand = {
|
|
|
4909
5072
|
}
|
|
4910
5073
|
}).middleware(({ skipDeploy }) => {
|
|
4911
5074
|
if (skipDeploy) {
|
|
4912
|
-
log5.warn(
|
|
5075
|
+
log5.warn(logPrefix21, "Skip deploy flag is true, then the deploy command wasn't executed.");
|
|
4913
5076
|
process.exit(0);
|
|
4914
5077
|
}
|
|
4915
5078
|
}).middleware(({ lambdaExternals, lambdaInput }) => {
|
|
@@ -4922,7 +5085,7 @@ var deployCommand = {
|
|
|
4922
5085
|
});
|
|
4923
5086
|
const commands = [
|
|
4924
5087
|
deployLambdaLayerCommand,
|
|
4925
|
-
|
|
5088
|
+
reportDeployCommand,
|
|
4926
5089
|
deployBaseStackCommand,
|
|
4927
5090
|
deployStaticAppCommand,
|
|
4928
5091
|
deployCicdCommand,
|
|
@@ -4950,10 +5113,10 @@ var deployCommand = {
|
|
|
4950
5113
|
}
|
|
4951
5114
|
}, "handler")
|
|
4952
5115
|
};
|
|
4953
|
-
var
|
|
5116
|
+
var logPrefix22 = "cicd-ecs-task-report";
|
|
4954
5117
|
var sendEcsTaskReport = /* @__PURE__ */ __name(async ({ status }) => {
|
|
4955
5118
|
if (!process.env.ECS_TASK_REPORT_HANDLER_NAME) {
|
|
4956
|
-
log5.info(
|
|
5119
|
+
log5.info(logPrefix22, "ECS_TASK_REPORT_HANDLER_NAME not defined.");
|
|
4957
5120
|
return;
|
|
4958
5121
|
}
|
|
4959
5122
|
const lambda = new AWS.Lambda();
|
|
@@ -4970,7 +5133,7 @@ var sendEcsTaskReport = /* @__PURE__ */ __name(async ({ status }) => {
|
|
|
4970
5133
|
FunctionName: process.env.ECS_TASK_REPORT_HANDLER_NAME,
|
|
4971
5134
|
InvokeArgs: JSON.stringify(payload)
|
|
4972
5135
|
}).promise();
|
|
4973
|
-
log5.info(
|
|
5136
|
+
log5.info(logPrefix22, "Report sent.");
|
|
4974
5137
|
}, "sendEcsTaskReport");
|
|
4975
5138
|
var options7 = {
|
|
4976
5139
|
status: {
|
|
@@ -4993,7 +5156,7 @@ var ecsTaskReportCommand = {
|
|
|
4993
5156
|
return sendEcsTaskReport(args);
|
|
4994
5157
|
}, "handler")
|
|
4995
5158
|
};
|
|
4996
|
-
var
|
|
5159
|
+
var logPrefix23 = "generate-env";
|
|
4997
5160
|
var readEnvFile = /* @__PURE__ */ __name(async ({ envFileName, envsPath }) => {
|
|
4998
5161
|
try {
|
|
4999
5162
|
const content = await fs3.promises.readFile(path.resolve(process.cwd(), envsPath, envFileName), "utf8");
|
|
@@ -5013,14 +5176,14 @@ var generateEnv = /* @__PURE__ */ __name(async ({ defaultEnvironment, path: envs
|
|
|
5013
5176
|
envsPath
|
|
5014
5177
|
});
|
|
5015
5178
|
if (!envFile) {
|
|
5016
|
-
log5.info(
|
|
5179
|
+
log5.info(logPrefix23, "Env file %s doesn't exist. Skip generating env file.", envFileName);
|
|
5017
5180
|
return;
|
|
5018
5181
|
}
|
|
5019
5182
|
await writeEnvFile({
|
|
5020
5183
|
content: envFile,
|
|
5021
5184
|
envFileName: ".env"
|
|
5022
5185
|
});
|
|
5023
|
-
log5.info(
|
|
5186
|
+
log5.info(logPrefix23, "Generate env file %s from %s successfully.", ".env", envFileName);
|
|
5024
5187
|
}, "generateEnv");
|
|
5025
5188
|
|
|
5026
5189
|
// src/generateEnv/generateEnvCommand.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carlin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Pedro Arantes <arantespp@gmail.com> (https://twitter.com/arantespp)",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"uglify-js": "^3.19.3",
|
|
48
48
|
"vercel": "^39.1.1",
|
|
49
49
|
"yargs": "^17.7.2",
|
|
50
|
-
"@ttoss/cloudformation": "^0.
|
|
51
|
-
"@ttoss/config": "^1.
|
|
52
|
-
"@ttoss/read-config-file": "^2.0
|
|
50
|
+
"@ttoss/cloudformation": "^0.12.0",
|
|
51
|
+
"@ttoss/config": "^1.36.0",
|
|
52
|
+
"@ttoss/read-config-file": "^2.1.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/adm-zip": "^0.5.6",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@types/jest": "^30.0.0",
|
|
61
61
|
"@types/js-yaml": "^4.0.9",
|
|
62
62
|
"@types/mime-types": "^2.1.4",
|
|
63
|
-
"@types/node": "^
|
|
63
|
+
"@types/node": "^24.10.13",
|
|
64
64
|
"@types/npmlog": "^7.0.0",
|
|
65
65
|
"@types/semver": "^7.5.8",
|
|
66
66
|
"@types/uglify-js": "^3.17.5",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"jest": "^30.2.0",
|
|
70
70
|
"tsup": "^8.5.1",
|
|
71
71
|
"typescript": "~5.9.3",
|
|
72
|
-
"@ttoss/test-utils": "^4.0
|
|
72
|
+
"@ttoss/test-utils": "^4.1.0"
|
|
73
73
|
},
|
|
74
74
|
"keywords": [],
|
|
75
75
|
"publishConfig": {
|