deploy-stack 0.3.2 → 0.4.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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- ![deploy-stack CLI demonstration](./docs/demo.gif)
7
+ <!-- ![deploy-stack CLI demonstration](./docs/demo.gif) -->
8
8
 
9
9
  ---
10
10
 
@@ -25,7 +25,8 @@ You retain complete ownership of your infrastructure code without relying on bla
25
25
  ## ✨ Features
26
26
 
27
27
  * **Zero Vendor Lock-In:** Generates standard, clean `.tf` files. Modify, expand, or decouple them at any time.
28
- * **Framework Agnostic:** Tailored container presets for Next.js (standalone), Express.js, FastAPI, and custom Docker setups.
28
+ * **Framework Agnostic:** Tailored container presets for Next.js, Express.js, FastAPI, and **Zero-Config Static Sites** (React, Vue, SvelteKit, Astro, Vite).
29
+ * **Smart Git Integration:** Automatically detects your working branch (`main`, `master`, `develop`) and binds it directly to the generated GitHub Actions pipeline.
29
30
  * **Production-Grade Defaults:** Automatically provisions an Amazon ECS Fargate cluster fronted by an Application Load Balancer across multiple availability zones.
30
31
  * **Global Edge Acceleration:** Includes an integrated AWS CloudFront CDN distribution with SSL termination and optimized caching.
31
32
  * **Keyless, Zero-Secret CI/CD:** Uses AWS IAM OpenID Connect (OIDC) for automated GitHub Actions deployments—no long-lived AWS keys stored in GitHub Secrets.
@@ -46,7 +47,7 @@ npx deploy-stack
46
47
  The interactive CLI will guide you through the setup:
47
48
  1. **Target Directory / Name:** (Type `.` to bootstrap your current directory)
48
49
  2. **Setup Mode:** (Choose **Quickstart** for sensible defaults, or **Advanced** for custom scaling and branch names)
49
- 3. **Zero-Config Detection:** The CLI automatically scans your `package.json` or `requirements.txt` to detect your framework (Next.js, Express, FastAPI, etc.) and dynamically configures your container port.
50
+ 3. **Zero-Config Detection:** The CLI automatically scans your project to detect your framework. For static sites, it intelligently discovers your build output directory (`dist`, `build`, `.output`, etc.) and dynamically configures the hardened Nginx container.
50
51
  4. **AWS Region & Compute Tier:** (Select your target region and Fargate size with live cost estimates)
51
52
 
52
53
  ---
@@ -104,8 +105,13 @@ your-project/
104
105
 
105
106
  ## 📦 Reference Implementations
106
107
 
107
- * **[Next.js Fullstack Reference App](https://github.com/anton-codes-iac/deploy-stack-nextjs-example):** A complete Next.js deployment showcasing the generated Terraform, CloudFront setup, and automated OIDC workflow.
108
-
108
+ * **[Next.js Fullstack App](https://github.com/anton-codes-iac/deploy-stack-nextjs-example):** A complete Next.js deployment showcasing the generated Terraform, CloudFront setup, and automated OIDC workflow.
109
+ * **[Express.js API](https://github.com/anton-codes-iac/deploy-stack-express-example):** A standard Node.js backend setup.
110
+ * **[Python FastAPI](https://github.com/anton-codes-iac/deploy-stack-fastapi-example):** A Python API demonstrating unprivileged port mapping.
111
+ * **[Vite / React SPA](https://github.com/anton-codes-iac/deploy-stack-vite-example):** Demonstrates SPA routing and `dist/` auto-detection.
112
+ * **[Create React App](https://github.com/anton-codes-iac/deploy-stack-cra-example):** Validates backward compatibility with legacy Webpack pipelines and `build/` auto-detection.
113
+ * **[Astro Static Site](https://github.com/anton-codes-iac/deploy-stack-astro-example):** Demonstrates modern static site generation (SSG).
114
+ * **[SvelteKit Application](https://github.com/anton-codes-iac/deploy-stack-svelte-example):** Demonstrates static adapter integration and custom output folder detection.
109
115
  ---
110
116
 
111
117
  ## 🛡️ Telemetry & Privacy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploy-stack",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,6 +3,7 @@ import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
  import { intro, outro, group, text, select, spinner, cancel, confirm, log } from '@clack/prompts';
5
5
  import color from 'picocolors';
6
+ import { execSync } from 'child_process';
6
7
 
7
8
  import { checkDependency } from '../utils/system.js';
8
9
  import { detectFramework } from '../utils/detector.js';
@@ -88,6 +89,14 @@ export async function mainStack() {
88
89
  if (finalFramework === 'static') defaultPort = '8080';
89
90
  if (finalFramework === 'python') defaultPort = '8000';
90
91
 
92
+ // 2.8 Check current Git branch
93
+ let currentGitBranch = 'main';
94
+ try {
95
+ currentGitBranch = execSync('git symbolic-ref --short HEAD', { cwd: targetDir, stdio: 'pipe' }).toString().trim();
96
+ } catch (e) {
97
+ // Not a git repo yet, fallback to 'main'
98
+ }
99
+
91
100
  // 3. Prompt Configuration Group
92
101
  const project = await group(
93
102
  {
@@ -140,8 +149,8 @@ export async function mainStack() {
140
149
  if (setupType === 'quick') return undefined;
141
150
  return text({
142
151
  message: 'Primary Git deployment branch for CI/CD:',
143
- placeholder: 'main',
144
- defaultValue: 'main',
152
+ placeholder: currentGitBranch,
153
+ defaultValue: currentGitBranch,
145
154
  });
146
155
  },
147
156
  },
@@ -162,7 +171,9 @@ export async function mainStack() {
162
171
 
163
172
  const healthCheckPath = project.healthCheckPath || '/';
164
173
  const desiredCount = project.desiredCount || '1';
165
- const deployBranch = project.branch || 'main';
174
+ const deployBranch = project.branch || currentGitBranch;
175
+
176
+ const buildDir = detectedFramework?.buildDir || 'dist';
166
177
 
167
178
  // 5. Check for existing files that might be overwritten
168
179
  const dockerfilePath = path.join(targetDir, 'Dockerfile');
@@ -213,6 +224,7 @@ export async function mainStack() {
213
224
  HEALTH_CHECK_PATH: healthCheckPath,
214
225
  DESIRED_COUNT: desiredCount,
215
226
  DEPLOY_BRANCH: deployBranch,
227
+ BUILD_DIR: buildDir,
216
228
  finalFramework: finalFramework
217
229
  });
218
230
 
@@ -230,7 +242,10 @@ export async function mainStack() {
230
242
  s.stop('Infrastructure provisioned successfully!');
231
243
 
232
244
  // 9. Provide the Outro, Framework Warnings and Next Steps
233
- const frameworkWarnings = getFrameworkWarning(finalFramework);
245
+ let frameworkWarnings = '';
246
+ if (!(finalFramework === 'static' && detectedFramework?.buildDir)) {
247
+ frameworkWarnings = getFrameworkWarning(finalFramework);
248
+ }
234
249
 
235
250
  const isGitInitialized = fsSync.existsSync(path.join(targetDir, '.git'));
236
251
 
@@ -240,7 +255,7 @@ export async function mainStack() {
240
255
 
241
256
  const gitInstructions = isGitInitialized
242
257
  ? `git add .\n git commit -m "chore: add AWS infrastructure and CI/CD"\n git push`
243
- : `git init\n git add .\n git commit -m "chore: add AWS infrastructure and CI/CD"\n git branch -M main\n git remote add origin https://github.com/your-username/your-repo.git\n git push -u origin main`;
258
+ : `git init\n git add .\n git commit -m "chore: add AWS infrastructure and CI/CD"\n git branch -M ${deployBranch}\n git remote add origin https://github.com/your-username/your-repo.git\n git push -u origin ${deployBranch}`;
244
259
 
245
260
  outro(`
246
261
  ${color.green('✅ Project provisioned successfully!')}
@@ -12,9 +12,19 @@ export function detectFramework(targetDir) {
12
12
  // Merge dependencies and devDependencies to check both
13
13
  const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
14
14
 
15
+ // Next.js & Express
15
16
  if (deps['next']) return { id: 'nextjs', name: 'Next.js' };
16
- if (deps['vite'] || deps['astro'] || deps['@sveltejs/kit'] || deps['gatsby'] || deps['react-scripts'] || deps['@vue/cli-service']) return { id: 'static', name: 'Static Site (Vite, Astro, React)' };
17
17
  if (deps['express']) return { id: 'node', name: 'Node.js / Express' };
18
+
19
+ // Static Site Generators & SPAs (with precise build directories)
20
+ if (deps['@sveltejs/kit']) return { id: 'static', name: 'SvelteKit', buildDir: 'build' };
21
+ if (deps['react-scripts']) return { id: 'static', name: 'Create React App', buildDir: 'build' };
22
+ if (deps['gatsby']) return { id: 'static', name: 'Gatsby', buildDir: 'public' };
23
+ if (deps['nuxt']) return { id: 'static', name: 'Nuxt', buildDir: '.output/public' };
24
+ if (deps['astro']) return { id: 'static', name: 'Astro', buildDir: 'dist' };
25
+ if (deps['vite']) return { id: 'static', name: 'Vite', buildDir: 'dist' };
26
+ if (deps['@vue/cli-service']) return { id: 'static', name: 'Vue.js', buildDir: 'dist' };
27
+ if (deps['@angular/cli']) return { id: 'static', name: 'Angular', buildDir: 'dist' };
18
28
  } catch (e) {
19
29
  // Silently fail if package.json is malformed
20
30
  }
@@ -103,6 +103,8 @@ env/
103
103
  node_modules/
104
104
  dist/
105
105
  build/
106
+ out/
107
+ .output/
106
108
  .cache/
107
109
  public/
108
110
  `;
@@ -25,10 +25,9 @@ export function getFrameworkWarning(frameworkId) {
25
25
  return (
26
26
  color.bgYellow(color.black(' ⚠️ IMPORTANT: STATIC SITE SETUP REQUIRED ')) +
27
27
  color.yellow('\n 1. Open your generated Dockerfile.') +
28
- color.yellow('\n 2. Ensure the "COPY --from=builder" command matches your framework:') +
29
- color.yellow('\n Vite / Astro: COPY --from=builder /app/dist /usr/share/nginx/html') +
30
- color.yellow('\n Create React App: COPY --from=builder /app/build /usr/share/nginx/html') +
31
- color.yellow('\n 3. Ensure your package.json has a "build" script (e.g., "vite build").\n\n')
28
+ color.yellow('\n 2. We defaulted your output folder to /app/dist.') +
29
+ color.yellow('\n 3. If your framework uses a different folder (like build/ or out/), change it in the COPY command.') +
30
+ color.yellow('\n 4. Ensure your package.json has a "build" script (e.g., "vite build").\n\n')
32
31
  );
33
32
  default:
34
33
  return '';
@@ -1,5 +1,5 @@
1
1
  # Stage 1: Install dependencies and build the app
2
- FROM node:20-alpine AS builder
2
+ FROM node:22-alpine AS builder
3
3
  WORKDIR /app
4
4
 
5
5
  # Copy package files and install dependencies
@@ -14,7 +14,7 @@ COPY . .
14
14
  RUN npm run build
15
15
 
16
16
  # Stage 2: Production environment
17
- FROM node:20-alpine AS runner
17
+ FROM node:22-alpine AS runner
18
18
  WORKDIR /app
19
19
 
20
20
  ENV NODE_ENV=production
@@ -1,4 +1,4 @@
1
- FROM node:20-alpine
1
+ FROM node:22-alpine
2
2
 
3
3
  # 1. Set production environment (optimizes Node and prevents dev dependencies)
4
4
  ENV NODE_ENV=production
@@ -1,5 +1,5 @@
1
1
  # STAGE 1: Build the static assets
2
- FROM node:20-alpine AS builder
2
+ FROM node:22-alpine AS builder
3
3
  WORKDIR /app
4
4
 
5
5
  COPY package*.json ./
@@ -13,9 +13,8 @@ RUN npm run build
13
13
  # STAGE 2: Serve with Hardened Nginx
14
14
  FROM nginx:alpine
15
15
 
16
- # ⚠️ CRITICAL: Adjust 'dist' to match your framework's output folder!
17
- # Vite/Astro = dist | Create React App/Gatsby = build | Next.js Static = out
18
- COPY --from=builder /app/dist /usr/share/nginx/html
16
+ # Adjusts BUILD_DIR to match your framework's output folder
17
+ COPY --from=builder /app/{{BUILD_DIR}} /usr/share/nginx/html
19
18
 
20
19
  # Inject custom Nginx configuration for unprivileged ports and SPA routing
21
20
  RUN echo "server {" > /etc/nginx/conf.d/default.conf && \