deploy-stack 0.6.0 → 0.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploy-stack",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,6 +37,7 @@
37
37
  ],
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
+ "@aws-sdk/client-dynamodb": "3.1119.0",
40
41
  "@aws-sdk/client-s3": "3.1115.0",
41
42
  "@aws-sdk/client-secrets-manager": "3.1112.0",
42
43
  "@aws-sdk/client-sts": "3.1115.0",
@@ -44,4 +45,4 @@
44
45
  "dotenv": "17.4.2",
45
46
  "picocolors": "1.1.1"
46
47
  }
47
- }
48
+ }
@@ -11,6 +11,7 @@ import { trackEvent, flushTelemetry } from '../core/telemetry.js';
11
11
  import { getFrameworkWarning } from '../utils/warnings.js';
12
12
  import { provisionStateBucket } from '../utils/aws.js';
13
13
  import { generateTemplates } from '../utils/generator.js';
14
+ import { handleExistingFiles } from '../utils/backup.js';
14
15
 
15
16
  export async function mainStack() {
16
17
  const startTime = Date.now();
@@ -175,21 +176,8 @@ export async function mainStack() {
175
176
 
176
177
  const buildDir = detectedFramework?.buildDir || 'dist';
177
178
 
178
- // 5. Check for existing files that might be overwritten
179
- const dockerfilePath = path.join(targetDir, 'Dockerfile');
180
- const tfDirPath = path.join(targetDir, 'terraform');
181
-
182
- if (fsSync.existsSync(dockerfilePath) || fsSync.existsSync(tfDirPath)) {
183
- const overwrite = await confirm({
184
- message: color.yellow('⚠️ A Dockerfile or terraform/ folder already exists here. Overwrite them?'),
185
- initialValue: false,
186
- });
187
-
188
- if (!overwrite) {
189
- cancel('Provisioning cancelled to protect existing files.');
190
- process.exit(0);
191
- }
192
- }
179
+ // 5. Safely handle existing files (Backup and auto-prune)
180
+ await handleExistingFiles(targetDir);
193
181
 
194
182
  const s = spinner();
195
183
  s.start('Provisioning infrastructure...');
@@ -0,0 +1,60 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { select, isCancel, cancel } from '@clack/prompts';
4
+ import color from 'picocolors';
5
+
6
+ export async function handleExistingFiles(targetDir) {
7
+ const tfPath = path.join(targetDir, 'terraform');
8
+ const dockerfilePath = path.join(targetDir, 'Dockerfile');
9
+ const wfDir = path.join(targetDir, '.github', 'workflows');
10
+ const wfPath = path.join(wfDir, 'deploy.yml');
11
+
12
+ const tfExists = fs.existsSync(tfPath);
13
+ const dockerfileExists = fs.existsSync(dockerfilePath);
14
+ const wfExists = fs.existsSync(wfPath);
15
+
16
+ if (!tfExists && !dockerfileExists && !wfExists) {
17
+ return; // Clean slate
18
+ }
19
+
20
+ const overwriteDecision = await select({
21
+ message: color.yellow('⚠️ deploy-stack configurations already exist. What would you like to do?'),
22
+ options: [
23
+ { value: 'cancel', label: 'Cancel', hint: 'Exit without making changes' },
24
+ { value: 'backup', label: 'Backup & Regenerate', hint: 'Move old configs to .bak (ignored by Git) and regenerate' }
25
+ ]
26
+ });
27
+
28
+ if (isCancel(overwriteDecision) || overwriteDecision === 'cancel') {
29
+ cancel('Operation cancelled to protect existing files.');
30
+ process.exit(0);
31
+ }
32
+
33
+ // Execute the safe backup
34
+ const timestamp = Date.now();
35
+
36
+ if (tfExists) fs.renameSync(tfPath, `${tfPath}.bak.${timestamp}`);
37
+ if (dockerfileExists) fs.renameSync(dockerfilePath, `${dockerfilePath}.bak.${timestamp}`);
38
+ if (wfExists) fs.renameSync(wfPath, `${wfPath}.bak.${timestamp}`);
39
+
40
+ ensureGitIgnore(targetDir);
41
+
42
+ console.log(color.cyan(`ℹ️ Existing configuration files were safely backed up locally.`));
43
+ }
44
+
45
+ /**
46
+ * Appends backup patterns to .gitignore so local clutter never reaches GitHub.
47
+ */
48
+ function ensureGitIgnore(targetDir) {
49
+ const gitignorePath = path.join(targetDir, '.gitignore');
50
+ const ignoreRules = '\n# deploy-stack backups\n*.bak.*\n';
51
+
52
+ if (fs.existsSync(gitignorePath)) {
53
+ const content = fs.readFileSync(gitignorePath, 'utf8');
54
+ if (!content.includes('*.bak.*')) {
55
+ fs.appendFileSync(gitignorePath, ignoreRules);
56
+ }
57
+ } else {
58
+ fs.writeFileSync(gitignorePath, ignoreRules);
59
+ }
60
+ }
@@ -1,6 +1,12 @@
1
1
  # deploy-stack generated infrastructure
2
2
  provider "aws" {
3
3
  region = "{{REGION}}"
4
+
5
+ default_tags {
6
+ tags = {
7
+ ManagedBy = "deploy-stack"
8
+ }
9
+ }
4
10
  }
5
11
 
6
12
  locals {
@@ -191,4 +197,21 @@ output "website_url" {
191
197
  output "ecr_repository_url" {
192
198
  description = "The URL of the ECR repository"
193
199
  value = aws_ecr_repository.app.repository_url
200
+ }
201
+
202
+ # --- CloudWatch Alarms ---
203
+ resource "aws_cloudwatch_metric_alarm" "alb_5xx_errors" {
204
+ alarm_name = "{{PROJECT_NAME}}-high-5xx-errors"
205
+ comparison_operator = "GreaterThanThreshold"
206
+ evaluation_periods = "2"
207
+ metric_name = "HTTPCode_Target_5XX_Count"
208
+ namespace = "AWS/ApplicationELB"
209
+ period = "60"
210
+ statistic = "Sum"
211
+ threshold = "10"
212
+ alarm_description = "Triggers if the ALB receives more than 10 5XX errors in 2 minutes."
213
+
214
+ dimensions = {
215
+ LoadBalancer = aws_lb.main.arn_suffix
216
+ }
194
217
  }