deploy-stack 0.18.0 → 0.18.2

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.
Files changed (65) hide show
  1. package/.github/workflows/deploy-docs.yml +37 -0
  2. package/.github/workflows/iac-validation.yml +67 -6
  3. package/.github/workflows/publish.yml +5 -0
  4. package/.muserules +6 -1
  5. package/README.md +6 -6
  6. package/apps/docs/.astro/collections/docs.schema.json +644 -0
  7. package/apps/docs/.astro/content-assets.mjs +4 -0
  8. package/apps/docs/.astro/content-modules.mjs +4 -0
  9. package/apps/docs/.astro/content.d.ts +179 -0
  10. package/apps/docs/.astro/data-store.json +1 -0
  11. package/apps/docs/.astro/dev.json +14 -0
  12. package/apps/docs/.astro/settings.json +5 -0
  13. package/apps/docs/.astro/types.d.ts +2 -0
  14. package/apps/docs/astro.config.mjs +68 -0
  15. package/apps/docs/package.json +17 -0
  16. package/{docs/adr → apps/docs/src/content/docs/adrs}/0001-s3-native-state-locking.md +4 -1
  17. package/{docs/adr → apps/docs/src/content/docs/adrs}/0002-eject-mechanism-pure-iac.md +4 -1
  18. package/{docs/adr → apps/docs/src/content/docs/adrs}/0003-sync-ai-context-strategy.md +4 -1
  19. package/{docs/adr → apps/docs/src/content/docs/adrs}/0004-iac-driven-diagnostic-context.md +4 -1
  20. package/apps/docs/src/content/docs/cli/apply.md +29 -0
  21. package/apps/docs/src/content/docs/cli/destroy.md +29 -0
  22. package/apps/docs/src/content/docs/cli/diagnose.md +28 -0
  23. package/apps/docs/src/content/docs/cli/doctor.md +28 -0
  24. package/apps/docs/src/content/docs/cli/eject.md +29 -0
  25. package/apps/docs/src/content/docs/cli/init.md +42 -0
  26. package/apps/docs/src/content/docs/cli/secrets.md +36 -0
  27. package/apps/docs/src/content/docs/cli/sync-ai.md +27 -0
  28. package/apps/docs/src/content/docs/guides/aws-credentials.md +68 -0
  29. package/apps/docs/src/content/docs/guides/cicd-pipeline.md +46 -0
  30. package/{docs → apps/docs/src/content/docs}/guides/database-connections.md +4 -1
  31. package/apps/docs/src/content/docs/guides/docker-compose.md +37 -0
  32. package/apps/docs/src/content/docs/guides/dockerfiles.md +46 -0
  33. package/{docs → apps/docs/src/content/docs}/guides/ephemeral-pr-previews.md +4 -1
  34. package/{docs → apps/docs/src/content/docs/guides}/examples.md +15 -8
  35. package/apps/docs/src/content/docs/guides/frameworks.md +88 -0
  36. package/{docs → apps/docs/src/content/docs}/guides/headless.md +4 -1
  37. package/apps/docs/src/content/docs/guides/rerun-init.md +43 -0
  38. package/{docs → apps/docs/src/content/docs}/guides/secrets-management.md +4 -1
  39. package/apps/docs/src/content/docs/index.mdx +36 -0
  40. package/{docs/migration → apps/docs/src/content/docs/migrations}/astro-vercel-to-aws.md +4 -1
  41. package/{docs/migration → apps/docs/src/content/docs/migrations}/heroku-procfile-to-aws.md +4 -1
  42. package/{docs/migration → apps/docs/src/content/docs/migrations}/nextjs-vercel-to-aws.md +4 -1
  43. package/{docs/migration → apps/docs/src/content/docs/migrations}/sveltekit-vercel-to-aws.md +4 -1
  44. package/{docs/ROADMAP.md → apps/docs/src/content/docs/roadmap.md} +5 -2
  45. package/{docs → apps/docs/src/content/docs}/testing-strategy.md +13 -9
  46. package/apps/docs/src/content.config.ts +7 -0
  47. package/apps/docs/src/custom.css +14 -0
  48. package/apps/docs/tsconfig.json +6 -0
  49. package/bin/cli.js +1 -1
  50. package/package.json +7 -2
  51. package/src/commands/diagnose.js +21 -7
  52. package/src/commands/init.js +5 -5
  53. package/src/commands/secrets.js +23 -8
  54. package/src/core/telemetry.js +8 -5
  55. package/src/utils/aws.js +16 -0
  56. package/src/utils/generator.js +6 -5
  57. package/src/utils/prompts.js +1 -1
  58. package/templates/docker/nestjs.Dockerfile +15 -3
  59. package/templates/docker/svelte.Dockerfile +4 -2
  60. package/templates/terraform/backend.tf +14 -1
  61. package/templates/terraform/secrets.tf +0 -6
  62. package/tests/__snapshots__/generator.test.js.snap +4 -2
  63. package/tests/diagnose.test.js +2 -2
  64. package/tests/secrets.test.js +29 -0
  65. package/docs/frameworks.md +0 -34
@@ -1,16 +1,22 @@
1
- # 📦 Reference Implementations & Examples
1
+ ---
2
+ title: Reference Implementations & Examples
3
+ description: Example repositories demonstrating how deploy-stack handles frameworks and architectural patterns, plus ecosystem plugins and starters.
4
+ ---
2
5
 
3
6
  These repositories demonstrate how `deploy-stack` handles various frameworks and architectural patterns. Each example includes the auto-generated Terraform, GitHub Actions, and container configurations.
4
7
 
5
- ### Featured Migrations
8
+ ## Featured migrations
9
+
6
10
  * **[Heroku to AWS Migration (Django)](https://github.com/anton-codes-iac/deploy-stack-heroku-django-example):** A classic Heroku-style monolith migrated via the Procfile Importer, demonstrating a multi-container Web and Celery Worker architecture deployed from a single codebase.
7
11
  * **[Vercel to AWS Migration (Next.js)](https://github.com/anton-codes-iac/deploy-stack-vercel-nextjs-example):** Demonstrates automatic translation of Vercel edge routing (`vercel.json`) to native AWS Application Load Balancer rules.
8
12
  * **[Docker Compose to AWS Migration](https://github.com/anton-codes-iac/deploy-stack-docker-compose-example):** Demonstrates automatic translation of local `docker-compose.yml` sidecars (like Redis) into a multi-container AWS ECS Task Definition communicating over `localhost`.
9
13
 
10
- ### DevSecOps & Security Architectures
14
+ ## DevSecOps & security architectures
15
+
11
16
  * **[Zero-Secret AWS Secrets Manager Injection](https://github.com/anton-codes-iac/deploy-stack-secrets-example):** A production-grade Node.js architecture demonstrating zero-plaintext secret injection. It pushes local `.env` variables directly to AWS and maps them into ECS memory at runtime, exposing a live endpoint querying GitHub's API.
12
17
 
13
- ### Frontend & Fullstack Frameworks
18
+ ## Frontend & fullstack frameworks
19
+
14
20
  * **[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.
15
21
  * **[Vite / React SPA](https://github.com/anton-codes-iac/deploy-stack-vite-example):** Demonstrates SPA routing and `dist/` auto-detection.
16
22
  * **[Create React App](https://github.com/anton-codes-iac/deploy-stack-cra-example):** Validates backward compatibility with legacy Webpack pipelines and `build/` auto-detection.
@@ -18,17 +24,18 @@ These repositories demonstrate how `deploy-stack` handles various frameworks and
18
24
  * **[SvelteKit Application](https://github.com/anton-codes-iac/deploy-stack-svelte-example):** Demonstrates static adapter integration and custom output folder detection.
19
25
  * **[Nuxt 3 (SSR)](https://github.com/anton-codes-iac/deploy-stack-nuxt-example):** Demonstrates a fully server-side rendered Nuxt application using Nitro's optimized Node output.
20
26
 
21
- ### Backend APIs & Monoliths
27
+ ## Backend APIs & monoliths
28
+
22
29
  * **[Express.js API](https://github.com/anton-codes-iac/deploy-stack-express-example):** A standard Node.js backend setup.
23
30
  * **[NestJS API](https://github.com/anton-codes-iac/deploy-stack-nest-example):** A robust NestJS architecture utilizing AST code-patching and highly optimized multi-stage TypeScript builds.
24
31
  * **[Python FastAPI](https://github.com/anton-codes-iac/deploy-stack-fastapi-example):** A Python API demonstrating unprivileged port mapping.
25
- * **[Ruby on Rails](https://github.com/anton-codes-iac/deploy-stack-rails-example):** A production Rails 7+ setup featuring an auto-provisioned PostgreSQL database and secure `.auto.tfvars` Master Key injection.
32
+ * **[Ruby on Rails](https://github.com/anton-codes-iac/deploy-stack-rails-example):** A production Rails 7+ setup featuring an auto-provisioned PostgreSQL database and secure `RAILS_MASTER_KEY` string-literal injection into the initial Secrets Manager placeholder.
26
33
  * **[Django / Python](https://github.com/anton-codes-iac/deploy-stack-django-example):** A secure Gunicorn/WSGI implementation with PostgreSQL and unprivileged container adapters.
27
34
  * **[Go / Fiber](https://github.com/anton-codes-iac/deploy-stack-go-example):** A distroless, compiled Go binary deployment demonstrating ultra-low memory footprints and instant boot times.
28
35
 
29
36
  ---
30
37
 
31
- ## 🧩 Ecosystem Plugins & Starters
38
+ ## Ecosystem plugins & starters
32
39
 
33
40
  In addition to standalone reference repositories, `deploy-stack` provides native integrations that hook directly into framework build pipelines and community template engines:
34
41
 
@@ -38,4 +45,4 @@ In addition to standalone reference repositories, `deploy-stack` provides native
38
45
  * **[svelte-adapter-deploy-stack](https://www.npmjs.com/package/svelte-adapter-deploy-stack):** Native SvelteKit adapter producing optimized Fargate container builds.
39
46
  * **[nest-deploy-stack](https://www.npmjs.com/package/nest-deploy-stack):** Native Angular DevKit schematic for NestJS, installable via `nest add`.
40
47
  * **[cookiecutter-django-deploy-stack](https://github.com/anton-codes-iac/cookiecutter-django-deploy-stack):** Community Django starter listed on Django Packages.
41
- * **[cookiecutter-fastapi-deploy-stack](https://github.com/anton-codes-iac/cookiecutter-fastapi-deploy-stack):** Instant scaffolding for modern, async FastAPI deployments.
48
+ * **[cookiecutter-fastapi-deploy-stack](https://github.com/anton-codes-iac/cookiecutter-fastapi-deploy-stack):** Instant scaffolding for modern, async FastAPI deployments.
@@ -0,0 +1,88 @@
1
+ ---
2
+ title: Supported Frameworks & Detection
3
+ description: Which frameworks deploy-stack detects, the signals it looks for, the valid --framework ids, and per-framework requirements.
4
+ sidebar:
5
+ order: 2
6
+ ---
7
+
8
+ `deploy-stack` is designed to be as "zero-config" as possible. During setup, it inspects your repo (`src/utils/detector.js`) and preselects a framework preset. However, because different frameworks have unique internal architectures (especially around network binding and build outputs), a few frameworks require minor application-level tweaks to run securely in a Dockerized AWS Fargate environment.
9
+
10
+ ## The 3-tier support philosophy
11
+
12
+ We handle framework requirements using a 3-tier strategy so you are never left guessing why a deployment failed:
13
+
14
+ 1. **Zero-touch plugins (Tier 1):** If you use one of our ecosystem plugins (e.g., `nest add nest-deploy-stack` or `cookiecutter-django-deploy-stack`), your code is automatically patched and configured. Zero manual intervention required.
15
+ 2. **Intelligent CLI pre-flight (Tier 2):** If you run the standalone `deploy-stack` CLI against a raw repository, the CLI statically analyzes your code. If it detects a missing production requirement (like a localhost binding), it will flag it inline in your terminal with the exact copy-paste fix.
16
+ 3. **In-repo docs (Tier 3):** The generated `DEPLOYMENT.md` file always contains a framework-specific checklist before you push to CI/CD.
17
+
18
+ ## Detection precedence
19
+
20
+ Checks run top-down; the first match wins.
21
+
22
+ | # | Signal | Preset (`id` / name) |
23
+ | - | ------ | -------------------- |
24
+ | 1 | `package.json` depends on `@nestjs/core` | `nestjs` / NestJS |
25
+ | 2 | `package.json` depends on `next` | `nextjs` / Next.js |
26
+ | 3 | `package.json` depends on `nuxt` | `nuxt` / Nuxt 3 (SSR) |
27
+ | 4 | `package.json` depends on `express` | `node` / Node.js / Express |
28
+ | 5 | `package.json` depends on `@sveltejs/kit` | `svelte` / SvelteKit SSR |
29
+ | 6 | `package.json` depends on `react-scripts`, `gatsby`, `astro`, `vite`, `@vue/cli-service`, or `@angular/cli` | `static` / (that generator) |
30
+ | 7 | `requirements.txt` contains `fastapi` | `python` / Python FastAPI |
31
+ | 8 | `requirements.txt` contains `django`, or `manage.py` exists | `django` / Django |
32
+ | 9 | `Gemfile` contains a `rails` gem | `rails` / Ruby on Rails |
33
+ | 10 | `go.mod` exists | `go` / Go |
34
+ | 11 | No match | No preset — you pick from the interactive list |
35
+
36
+ Notes from the actual code:
37
+
38
+ - Both `dependencies` and `devDependencies` are searched, so a framework listed only under dev dependencies still matches.
39
+ - A malformed `package.json` or `vercel.json` is silently ignored (no match), never fatal.
40
+ - An empty `vercel.json` (no `redirects`, `headers`, or `rewrites`) is treated as absent.
41
+
42
+ ## Valid `--framework` ids
43
+
44
+ The interactive picker and the headless `--framework` flag accept: `node`, `nestjs`, `nextjs`, `nuxt`, `svelte`, `python`, `django`, `rails`, `go`, `static`. In headless mode with no `--framework`, detection applies and anything unmatched falls back to `static`.
45
+
46
+ ## Per-framework defaults
47
+
48
+ - **Static build directory** (`buildDir`): SvelteKit `build`, Gatsby `public`, everything else (`astro`, `vite`, Vue, Angular) `dist`. This selects the folder the generated `Dockerfile` serves.
49
+ - **Default container port**: `8080` for `static` and `go`, `8000` for `python` and `django`, `3000` for everything else (headless uses `8080` only when `--framework=static`, else `3000`).
50
+ - **Database prompt**: offered only for backend presets (`node`, `nestjs`, `nextjs`, `nuxt`, `python`, `django`, `rails`, `go`).
51
+
52
+ ## Framework requirements cheat sheet
53
+
54
+ | Framework | What `deploy-stack` automates | Application code requirement | Zero-click starter / plugin |
55
+ |---|---|---|---|
56
+ | **Next.js** | Multi-stage Dockerfile, CloudFront edge routing, `vercel.json` parsing | `output: 'standalone'` must be set in `next.config.js` | Built-in CLI detection |
57
+ | **NestJS** | Multi-stage TypeScript build (`dist/`), unprivileged Node runtime | `await app.listen(port, '0.0.0.0')` in `src/main.ts` | `nest-deploy-stack` (`nest add`) |
58
+ | **FastAPI** | Alpine Python container, Uvicorn CLI args, unprivileged port mapping | None (0.0.0.0 set via Docker CMD) | `cookiecutter-fastapi-deploy-stack` |
59
+ | **Django** | Gunicorn WSGI adapter, Celery worker topologies, RDS bindings | None (0.0.0.0 set via Docker CMD) | `cookiecutter-django-deploy-stack` |
60
+ | **Ruby on Rails** | Puma adapter, `RAILS_MASTER_KEY` injection into Secrets Manager placeholder, Kamal Dockerfile replaced with 0-CVE Alpine build | None (0.0.0.0 set via Docker CMD) | `rails-template-deploy-stack` |
61
+ | **Nuxt 3** | Nitro-optimized Node output | None (`NITRO_HOST=0.0.0.0` injected automatically) | `nuxt-deploy-stack` |
62
+ | **SvelteKit** | Node adapter conversion | None (`HOST=0.0.0.0` injected automatically) | `svelte-adapter-deploy-stack` |
63
+ | **Static Sites** *(Vite, Astro, React)* | Output folder detection (`dist/`, `build/`), Nginx routing | None | `vite-plugin-deploy-stack` |
64
+
65
+ ## Post-detection checks
66
+
67
+ After detection, setup validates framework-specific requirements and warns before generating:
68
+
69
+ - **NestJS**: `src/main.ts` (or `main.js`) must bind `0.0.0.0`, e.g. `await app.listen(process.env.PORT ?? 3000, '0.0.0.0')`.
70
+ - **Next.js**: config must set `output: 'standalone'` (`.js/.mjs/.cjs/.ts` checked).
71
+ - **SvelteKit**: adapter must not be `@sveltejs/adapter-vercel` or `adapter-auto`.
72
+ - **Astro**: adapter must not be `@astrojs/vercel`.
73
+
74
+ Alongside detection, setup also auto-detects `Procfile` (web/worker commands), `vercel.json` edge rules (translated to ALB listener rules), and `docker-compose.yml` services (port override plus sidecars).
75
+
76
+ ## The golden rule: 0.0.0.0 vs localhost
77
+
78
+ The most common reason a newly deployed container fails its ALB health check is network binding.
79
+
80
+ In local development, frameworks bind to `localhost` (or `127.0.0.1`) for security. However, inside a Docker container on AWS ECS, binding to `localhost` means the web server only listens to internal container traffic. The AWS Application Load Balancer (ALB) trying to route traffic from the outside world will hit a closed port, resulting in a `502 Bad Gateway` or `503 Service Temporarily Unavailable`.
81
+
82
+ **Always ensure your application explicitly binds to `0.0.0.0`.**
83
+
84
+ ## See also
85
+
86
+ - [Dockerfiles & the container contract](/guides/dockerfiles/) for what your app must do at runtime.
87
+ - [Headless Mode](/guides/headless/) for automating framework selection.
88
+ - [Examples](/examples/) for reference repositories and ecosystem plugins per framework.
@@ -1,4 +1,7 @@
1
- # Headless Mode & Automation Guide
1
+ ---
2
+ title: "Headless Mode & Automation Guide"
3
+ description: "The deploy-stack CLI is designed to be fully automatable for CI/CD pipelines, custom scripts, Cookiecutters, and framework plugins (like vite-plugin-deploy-stac"
4
+ ---
2
5
 
3
6
  The `deploy-stack` CLI is designed to be fully automatable for CI/CD pipelines, custom scripts, Cookiecutters, and framework plugins (like `vite-plugin-deploy-stack`).
4
7
 
@@ -0,0 +1,43 @@
1
+ ---
2
+ title: Re-running Init Safely
3
+ description: What happens when setup finds existing files — backups, regeneration, and how to recover.
4
+ sidebar:
5
+ order: 5
6
+ ---
7
+
8
+ Re-running `npx deploy-stack` to change region, size, or framework is safe and predictable: setup never merges with your existing generated files. It backs them up, regenerates from scratch, and tells you exactly what moved.
9
+
10
+ ## The conflict prompt
11
+
12
+ When setup finds any of `terraform/`, `Dockerfile`, or `.github/workflows/deploy.yml` in the target directory (`src/utils/backup.js`), it lists the conflicts and offers two choices:
13
+
14
+ - **Backup & Regenerate** — each conflicting path is renamed with a timestamp suffix (e.g. `terraform.bak.1726771200000`), then fresh files are generated.
15
+ - **Cancel** — exits immediately with no changes.
16
+
17
+ In `--headless` mode there is no prompt: existing files are backed up automatically. Either way, nothing is ever merged or partially overwritten.
18
+
19
+ ## Backups stay local
20
+
21
+ After backing up, setup appends a `# deploy-stack backups` block (`*.bak.*`) to `.gitignore` (creating the file if needed), so backup clutter never reaches GitHub. To recover a previous configuration, compare with `diff -r terraform.bak.<timestamp> terraform/` and copy back what you need — then delete the `.bak.*` directory when you are satisfied. (`npx deploy-stack eject` removes all `*.bak.*` files as part of decoupling.)
22
+
23
+ ## What regeneration touches
24
+
25
+ `src/utils/generator.js` writes a fixed file set and handles pre-existing files explicitly:
26
+
27
+ - `terraform/*.tf`, `Dockerfile`, `.github/workflows/deploy.yml`, plus `preview.yml`/`teardown.yml` only when PR previews are enabled.
28
+ - `terraform/secret_keys.json` is reset to `[]` — re-push secrets afterward with `npx deploy-stack secrets push`.
29
+ - If your repo already has a `README.md`, it is kept and gets a short Deployment pointer appended; the generated guide goes to `DEPLOYMENT.md` instead.
30
+ - Existing `.gitignore` / `.dockerignore` files are preserved with only the deploy-stack entries appended (Terraform state paths, `.env`); missing ones are created with framework-appropriate presets.
31
+ - **Rails only:** if `ci.yml` or `dependabot.yml` exist, setup asks whether to disable them by renaming to `.bak` (default CI usually crashes without a database service); in headless mode they are disabled automatically.
32
+
33
+ ## Suggested workflow
34
+
35
+ 1. Commit your work before re-running, so `git status` shows exactly what regeneration changed.
36
+ 2. Re-run, review the diff (`git diff`, plus `diff -r` against the `.bak` copies for untracked files like `terraform/` internals).
37
+ 3. Run `npx deploy-stack apply` to converge AWS with the new configuration.
38
+ 4. Delete the `.bak.<timestamp>` copies once the new infrastructure is verified.
39
+
40
+ ## See also
41
+
42
+ - [apply](/cli/apply/) for converging AWS after regeneration.
43
+ - [eject](/cli/eject/) for what happens to backups on decoupling.
@@ -1,4 +1,7 @@
1
- # Secrets Management in deploy-stack
1
+ ---
2
+ title: "Secrets Management in deploy-stack"
3
+ description: "Managing .env files across a team and syncing them to the cloud is a notorious pain point. deploy-stack solves this by natively integrating with AWS Secrets Man"
4
+ ---
2
5
 
3
6
  Managing `.env` files across a team and syncing them to the cloud is a notorious pain point. `deploy-stack` solves this by natively integrating with **AWS Secrets Manager**, ensuring zero plaintext secrets ever touch your GitHub repository or CI/CD pipelines.
4
7
 
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: deploy-stack docs
3
+ description: Official developer portal for deploy-stack — concepts, guides, and CLI reference.
4
+ template: splash
5
+ hero:
6
+ tagline: Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.
7
+ actions:
8
+ - text: Read the guides
9
+ link: /guides/headless/
10
+ icon: right-arrow
11
+ - text: CLI reference
12
+ link: /cli/init/
13
+ variant: minimal
14
+ ---
15
+
16
+ import { LinkCard, CardGrid } from '@astrojs/starlight/components';
17
+
18
+ ## Start here
19
+
20
+ <CardGrid stagger>
21
+ <LinkCard
22
+ title="Deployment Guides"
23
+ description="Learn how to deploy Rails, NestJS, and SvelteKit applications."
24
+ href="/guides/aws-credentials/"
25
+ />
26
+ <LinkCard
27
+ title="CLI Reference"
28
+ description="Explore the full list of commands and headless flags."
29
+ href="/cli/init/"
30
+ />
31
+ <LinkCard
32
+ title="Architecture Decisions"
33
+ description="Read the ADRs detailing our infrastructure choices."
34
+ href="/adrs/001-initial-architecture/"
35
+ />
36
+ </CardGrid>
@@ -1,4 +1,7 @@
1
- # Migrating Astro from Vercel to AWS Fargate
1
+ ---
2
+ title: "Migrating Astro from Vercel to AWS Fargate"
3
+ description: "If you are seeing a warning from deploy-stack about your Astro adapter, it means your project is currently configured to build specifically for Vercel's proprie"
4
+ ---
2
5
 
3
6
  If you are seeing a warning from `deploy-stack` about your Astro adapter, it means your project is currently configured to build specifically for Vercel's proprietary serverless network.
4
7
 
@@ -1,4 +1,7 @@
1
- # Migrating from Heroku to AWS (Procfile Support)
1
+ ---
2
+ title: "Migrating from Heroku to AWS (Procfile Support)"
3
+ description: "When migrating from Heroku or Render, you likely rely on a Procfile to define your application's architecture (e.g., a web server and a background worker like C"
4
+ ---
2
5
 
3
6
  When migrating from Heroku or Render, you likely rely on a `Procfile` to define your application's architecture (e.g., a web server and a background worker like Celery or Sidekiq).
4
7
 
@@ -1,4 +1,7 @@
1
- # Migrating Next.js from Vercel to AWS Fargate
1
+ ---
2
+ title: "Migrating Next.js from Vercel to AWS Fargate"
3
+ description: "If you are seeing a warning from deploy-stack about output: 'standalone', your Next.js configuration is missing a crucial setting required for containerized env"
4
+ ---
2
5
 
3
6
  If you are seeing a warning from `deploy-stack` about `output: 'standalone'`, your Next.js configuration is missing a crucial setting required for containerized environments.
4
7
 
@@ -1,4 +1,7 @@
1
- # Migrating SvelteKit from Vercel to AWS Fargate
1
+ ---
2
+ title: "Migrating SvelteKit from Vercel to AWS Fargate"
3
+ description: "If you are seeing a warning from deploy-stack about your SvelteKit adapter, your project is currently using @sveltejs/adapter-auto (which often defaults to Verc"
4
+ ---
2
5
 
3
6
  If you are seeing a warning from `deploy-stack` about your SvelteKit adapter, your project is currently using `@sveltejs/adapter-auto` (which often defaults to Vercel) or the explicit `@sveltejs/adapter-vercel`.
4
7
 
@@ -1,4 +1,7 @@
1
- # 🗺️ deploy-stack Roadmap
1
+ ---
2
+ title: Roadmap
3
+ description: Where deploy-stack has been and what comes next — completed phases and the current platform-hardening milestone.
4
+ ---
2
5
 
3
6
  ### Phase 1–3: The Core Engine (Completed)
4
7
  - [x] **Core MVP:** Interactive CLI, ECS Fargate + ALB generation, CI/CD, and Secrets sync.
@@ -40,4 +43,4 @@
40
43
  - [ ] **Documentation Hub:** Launch a dedicated Astro Starlight documentation site featuring interactive architecture diagrams, core concept deep-dives, and detailed CLI references.
41
44
  - [ ] **Continuous Infrastructure Validation:** Implement a GitHub Actions matrix pipeline that automatically generates, compiles, and validates Terraform syntax (`terraform validate`, `tflint`) against all supported frameworks on every commit.
42
45
  - [ ] **Automated Security & Compliance Proving:** Integrate DevSecOps infrastructure scanning (`trivy` or `tfsec`) directly into the CI pipeline to mathematically guarantee zero-CVE, secure-by-default AWS provisioning.
43
- - [ ] **Integration Stability Suite:** Expand Vitest coverage to enforce strict contracts for headless execution flags (`--preconfigured`, `--headless`), ensuring seamless interoperability with third-party scaffolding tools.
46
+ - [ ] **Integration Stability Suite:** Expand Vitest coverage to enforce strict contracts for headless execution flags (`--preconfigured`, `--headless`), ensuring seamless interoperability with third-party scaffolding tools.
@@ -1,27 +1,31 @@
1
- # deploy-stack Testing Strategy
1
+ ---
2
+ title: Testing Strategy
3
+ description: How deploy-stack prevents regressions — unit tests, snapshot harness, API mocking, and CI validation.
4
+ ---
2
5
 
3
6
  To ensure zero regressions in infrastructure generation and safe local execution, `deploy-stack` relies on a multi-layered testing strategy split between fast local snapshots and rigid CI/CD validation.
4
7
 
5
- ## 1. Unit & Argument Testing
8
+ ## 1. Unit & argument testing
6
9
  We use pure Node.js unit tests (via Vitest) to validate the CLI argument parser (`src/core/parser.js`). This ensures that flags (like `--headless` or `--no-telemetry`) are routed correctly and never hijack positional arguments like file paths.
7
10
 
8
- ## 1.5. Ecosystem Integration Contracts
11
+ ## 1.5. Ecosystem integration contracts
9
12
  Because `deploy-stack` acts as the underlying engine for ecosystem wrappers (e.g., `nest-deploy-stack`, `cookiecutter-fastapi`), we strictly test execution flags that bypass interactive prompts:
10
13
  * **Headless Validation:** Vitest specifically asserts that when `--headless` and `--preconfigured` are passed, the CLI never initializes the `inquirer` prompt module and never throws interactive warnings. This guarantees stability for automated ecosystem integrations.
11
14
 
12
- ## 2. Infrastructure Snapshot Harness (The Static Contract)
13
- Because `deploy-stack` generates highly dynamic Terraform (`.tf`), GitHub Actions (`.yml`), and `Dockerfile` configurations, we use **Vitest Snapshots** to lock in the expected text outputs.
15
+ ## 2. Infrastructure snapshot harness (the static contract)
16
+ Because `deploy-stack` generates highly dynamic Terraform (`.tf`), GitHub Actions (`.yml`), and `Dockerfile` configurations, we use **Vitest Snapshots** to lock in the expected text outputs.
14
17
  * **The Matrix:** The test suite generates dummy projects across 11 architectural topologies (including Django, Rails, Go, Nuxt, Next.js, SvelteKit, and Vercel/Heroku migrations).
15
18
  * **Negative Testing:** The suite explicitly checks for the *absence* of files (e.g., ensuring `database.tf` or `worker.tf` are not generated for static sites).
16
19
  * **Updating Snapshots:** If a template change is intentional, developers must run `npm run test:update` to overwrite the baseline `__snapshots__`.
17
20
 
18
- ## 3. External API Mocking
21
+ ## 3. External API mocking
19
22
  To ensure tests run sub-second and deterministically without requiring real AWS credentials, we intercept network boundaries:
20
23
  * **AWS Secrets Manager:** `tests/secrets.test.js` uses Vitest's `vi.hoisted()` and `vi.mock()` to intercept `@aws-sdk/client-secrets-manager`. This verifies the CLI correctly formats payloads and handles network exceptions (like `ResourceNotFoundException`) completely offline.
21
24
  * **Telemetry:** PostHog tracking is mocked to prevent test executions from polluting production analytics.
22
25
 
23
- ## 4. Continuous Integration & Execution Validation (CI)
26
+ ## 4. Continuous integration & execution validation (CI)
24
27
  While Vitest proves the CLI generates the *correct* files, GitHub Actions proves those files *actually work*. Unit and snapshot tests are gated via `.github/workflows/test.yml`; live template compilation is gated via `.github/workflows/iac-validation.yml`.
25
28
  * **Phase 1 (Generation):** Vitest runs unit and snapshot tests to verify the CLI contract.
26
- * **Phase 2 (Static Application Security Testing - SAST):** CI runs `trivy config` against the generated `Dockerfile` and Terraform snapshots to guarantee they remain compliant with strict security policies.
27
- * **Phase 3 (IaC Validation):** The `iac-validation` matrix workflow scaffolds all 10 supported frameworks headlessly (`--headless --preconfigured`), then runs `terraform init -backend=false` + `terraform validate`, `tflint`, and a Trivy filesystem scan (fails on HIGH/CRITICAL) fully offline with no AWS credentials.
29
+ * **Phase 2 (Static Application Security Testing - SAST):** CI runs a pinned Trivy filesystem scan (`aquasecurity/trivy-action` by SHA) against each generated project directory, writing advisory `trivy-fs-results.txt` reports (`HIGH,CRITICAL`, `exit-code: 0`) instead of failing the build.
30
+ * **Phase 3 (IaC Validation):** The `iac-validation` matrix workflow scaffolds all 10 supported frameworks headlessly (`--headless --preconfigured`), then runs `terraform init -backend=false` + `terraform validate`, `tflint`, the advisory filesystem scan, a stripped-Dockerfile `docker build`, and an advisory container-image scan (`trivy-image-results.txt`).
31
+ * **Phase 4 (Release gate):** `.github/workflows/publish.yml` reuses `iac-validation.yml` via `workflow_call` as a `validate` job; `build-and-publish` has `needs: [validate]`, so NPM publishing on release is blocked until the full matrix passes.
@@ -0,0 +1,7 @@
1
+ import { defineCollection } from 'astro:content';
2
+ import { docsLoader } from '@astrojs/starlight/loaders';
3
+ import { docsSchema } from '@astrojs/starlight/schema';
4
+
5
+ export const collections = {
6
+ docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
7
+ };
@@ -0,0 +1,14 @@
1
+ :root {
2
+ /* Make all base text slightly smaller */
3
+ --sl-text-base: 0.95rem;
4
+ --sl-line-height: 1.6;
5
+
6
+ /* Shrink the headings */
7
+ --sl-text-h1: 2.2rem;
8
+ --sl-text-h2: 1.75rem;
9
+ --sl-text-h3: 1.35rem;
10
+
11
+ /* Tighten the left sidebar padding */
12
+ --sl-nav-pad-y: 0.25rem;
13
+ --sl-nav-gap: 0.5rem;
14
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "compilerOptions": {
4
+ "strictNullChecks": true
5
+ }
6
+ }
package/bin/cli.js CHANGED
@@ -35,7 +35,7 @@ if (positionalArgs[0] === 'secrets' && positionalArgs[1] === 'push') {
35
35
  } else if (positionalArgs[0] === 'sync-ai') {
36
36
  syncAi().catch(e => { console.error(e); process.exit(1); });
37
37
  } else if (positionalArgs[0] === 'diagnose' || positionalArgs[0] === 'wtf') {
38
- runDiagnose().catch(e => { console.error(e); process.exit(1); });
38
+ runDiagnose(headlessOptions).catch(e => { console.error(e); process.exit(1); });
39
39
  } else {
40
40
  mainStack({ isHeadless, headlessOptions }).catch(e => { console.error(e); process.exit(1); });
41
41
  }
package/package.json CHANGED
@@ -1,18 +1,23 @@
1
1
  {
2
2
  "name": "deploy-stack",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
7
7
  },
8
8
  "type": "module",
9
+ "workspaces": [
10
+ "apps/*"
11
+ ],
9
12
  "bin": {
10
13
  "deploy-stack": "bin/cli.js"
11
14
  },
12
15
  "scripts": {
13
16
  "test": "vitest run",
14
17
  "test:watch": "vitest",
15
- "test:update": "vitest run -u"
18
+ "test:update": "vitest run -u",
19
+ "docs:dev": "npm run dev --workspace=apps/docs",
20
+ "docs:build": "npm run build --workspace=apps/docs"
16
21
  },
17
22
  "author": "anton-codes-iac",
18
23
  "repository": {
@@ -35,7 +35,7 @@ export async function runDiagnose(options = {}) {
35
35
  const projectName = path.basename(process.cwd());
36
36
 
37
37
  // Attempt to read the region from the generated Terraform variables
38
- let autoRegion = 'us-east-1';
38
+ let autoRegion = 'us-east-2';
39
39
  try {
40
40
  const mainTfPath = path.join(process.cwd(), 'terraform', 'main.tf');
41
41
  if (fsSync.existsSync(mainTfPath)) {
@@ -48,7 +48,7 @@ export async function runDiagnose(options = {}) {
48
48
  // Fallback silently
49
49
  }
50
50
 
51
- const region = options.region || process.env.AWS_REGION || autoRegion;
51
+ const region = options.region || process.env.AWS_REGION || 'us-east-2';
52
52
  const cluster = options.cluster || process.env.ECS_CLUSTER || `${projectName}-cluster`;
53
53
  const logGroup = options.logGroup || process.env.ECS_LOG_GROUP || `/ecs/${projectName}`;
54
54
 
@@ -57,8 +57,13 @@ export async function runDiagnose(options = {}) {
57
57
  const s = spinner();
58
58
  s.start('Looking up recent stopped ECS tasks...');
59
59
 
60
- const ecsClient = options.ecsClient || new ECSClient({ region });
61
- const logsClient = options.logsClient || new CloudWatchLogsClient({ region });
60
+ const ecsClient = (options.ecsClient && typeof options.ecsClient.send === 'function')
61
+ ? options.ecsClient
62
+ : new ECSClient({ region });
63
+
64
+ const logsClient = (options.logsClient && typeof options.logsClient.send === 'function')
65
+ ? options.logsClient
66
+ : new CloudWatchLogsClient({ region });
62
67
 
63
68
  try {
64
69
  const listResp = await ecsClient.send(
@@ -157,12 +162,21 @@ export async function runDiagnose(options = {}) {
157
162
  };
158
163
  } catch (error) {
159
164
  s.stop(color.red('❌ Diagnose failed.'));
160
- console.log(color.red(`✖ ${error.message || error}`));
161
- console.log(color.dim('Check your AWS credentials and region, then try again.'));
165
+
166
+ if (error.name === 'UnrecognizedClientException' || error.name === 'ExpiredTokenException') {
167
+ console.log(color.yellow('\n⚠️ AWS Session Expired / Invalid Credentials'));
168
+ console.log(`Run ${color.cyan('aws sso login')} or ${color.cyan('aws configure')} to refresh your credentials.`);
169
+ console.log(color.blue(`\n📘 Troubleshooting Guide: ${color.underline('https://github.com/anton-codes-iac/deploy-stack/blob/main/apps/docs/src/content/docs/guides/aws-credentials.md')}\n`));
170
+ } else {
171
+ console.log(color.red(`✖ ${error.message || error}`));
172
+ console.log(color.dim('Check your AWS credentials and region, then try again.'));
173
+ }
174
+
162
175
  trackEvent('diagnose_run', {
163
176
  success: false,
164
177
  error_code: error.name || 'UNKNOWN',
165
- error_message: error.message
178
+ error_message: error.message,
179
+ stack_trace: error.name === 'TypeError' ? error.stack : undefined
166
180
  });
167
181
  await flushTelemetry();
168
182
  throw error;
@@ -86,19 +86,19 @@ export async function mainStack({ isHeadless = false, headlessOptions = {} } = {
86
86
  const nextConfig = analyzeNextConfig(dirConfig.targetDir);
87
87
  if (nextConfig.hasConfig && !nextConfig.isStandalone) {
88
88
  log.warn(color.yellow('⚠️ Next.js config is missing "output: \'standalone\'".'));
89
- console.log(color.cyan(' Fix it here: https://github.com/anton-codes-iac/deploy-stack/blob/main/docs/migrations/nextjs-vercel-to-aws.md'));
89
+ console.log(color.cyan(' Fix it here: https://github.com/anton-codes-iac/deploy-stack/blob/main/apps/docs/src/content/docs/migrations/nextjs-vercel-to-aws.md'));
90
90
  }
91
91
  } else if (detectedFramework?.name === 'SvelteKit') {
92
92
  const svelteConfig = analyzeSvelteConfig(dirConfig.targetDir);
93
93
  if (svelteConfig.adapter === 'vercel' || svelteConfig.adapter === 'auto') {
94
94
  log.warn(color.yellow('⚠️ SvelteKit is locked into the Vercel/Auto adapter.'));
95
- console.log(color.cyan(' Fix it here: https://github.com/anton-codes-iac/deploy-stack/blob/main/docs/migrations/sveltekit-vercel-to-aws.md'));
95
+ console.log(color.cyan(' Fix it here: https://github.com/anton-codes-iac/deploy-stack/blob/main/apps/docs/src/content/docs/migrations/sveltekit-vercel-to-aws.md'));
96
96
  }
97
97
  } else if (detectedFramework?.name === 'Astro') {
98
98
  const astroConfig = analyzeAstroConfig(dirConfig.targetDir);
99
99
  if (astroConfig.adapter === 'vercel') {
100
100
  log.warn(color.yellow('⚠️ Astro is locked into the Vercel adapter.'));
101
- console.log(color.cyan(' Fix it here: https://github.com/anton-codes-iac/deploy-stack/blob/main/docs/migrations/astro-vercel-to-aws.md'));
101
+ console.log(color.cyan(' Fix it here: https://github.com/anton-codes-iac/deploy-stack/blob/main/apps/docs/src/content/docs/migrations/astro-vercel-to-aws.md'));
102
102
  }
103
103
  }
104
104
 
@@ -285,9 +285,9 @@ export async function mainStack({ isHeadless = false, headlessOptions = {} } = {
285
285
 
286
286
  let docsTip = '';
287
287
  if (procfile) {
288
- docsTip = `\n ${color.blue('📘 Read the Heroku Migration Guide:')} ${color.underline('https://github.com/anton-codes-iac/deploy-stack/blob/main/docs/migrations/heroku-procfile-to-aws.md')}`;
288
+ docsTip = `\n ${color.blue('📘 Read the Heroku Migration Guide:')} ${color.underline('https://github.com/anton-codes-iac/deploy-stack/blob/main/apps/docs/src/content/docs/migrations/heroku-procfile-to-aws.md')}`;
289
289
  } else if (config.needsDatabase) {
290
- docsTip = `\n ${color.blue('📘 Read the Database Connections Guide:')} ${color.underline('https://github.com/anton-codes-iac/deploy-stack/blob/main/docs/guides/database-connections.md')}`;
290
+ docsTip = `\n ${color.blue('📘 Read the Database Connections Guide:')} ${color.underline('https://github.com/anton-codes-iac/deploy-stack/blob/main/apps/docs/src/content/docs/guides/database-connections.md')}`;
291
291
  }
292
292
 
293
293
  outro(`${color.green('✅ Templates generated!')} ${color.blue('🛡️ DevSecOps scanning enabled.')}
@@ -7,20 +7,29 @@ import path from 'path';
7
7
  import { trackEvent, flushTelemetry } from '../core/telemetry.js';
8
8
 
9
9
  export async function pushSecrets(envFilePath, projectName) {
10
+ // Ensure envFilePath is a valid string, defaulting to '.env' if undefined or an object
11
+ const resolvedFilePath = (typeof envFilePath === 'string' && envFilePath.trim())
12
+ ? envFilePath.trim()
13
+ : '.env';
14
+
15
+ const resolvedProjectName = (typeof projectName === 'string' && projectName.trim())
16
+ ? projectName.trim()
17
+ : path.basename(process.cwd());
18
+
10
19
  const s = spinner();
11
20
  s.start(`Reading ${envFilePath} and pushing to AWS Secrets Manager...`);
12
21
 
13
22
  try {
14
23
  // 1. Read and parse the local .env file
15
- const envPath = path.resolve(process.cwd(), envFilePath);
24
+ const envPath = path.resolve(process.cwd(), resolvedFilePath);
16
25
  let envContent;
17
26
  try {
18
27
  envContent = await fs.readFile(envPath, 'utf-8');
19
28
  } catch (fsError) {
20
29
  if (fsError.code === 'ENOENT') {
21
- throw new Error(`File not found: ${envFilePath}. Please ensure the file exists before pushing.`);
30
+ throw new Error(`File not found: "${resolvedFilePath}". Please ensure the file exists before pushing.`);
22
31
  }
23
- throw fsError; // Re-throw if it's a permissions issue
32
+ throw fsError;
24
33
  }
25
34
 
26
35
  const parsedSecrets = dotenv.parse(envContent);
@@ -62,7 +71,7 @@ export async function pushSecrets(envFilePath, projectName) {
62
71
  s.stop(`✅ Successfully pushed ${Object.keys(parsedSecrets).length} secrets to AWS (${targetRegion || 'default region'})!`);
63
72
  console.log(color.cyan(`\nUpdated ${keysFilePath}`));
64
73
  console.log(color.green('Commit this file and push to GitHub to trigger a deployment with your new variables.'));
65
- console.log(color.blue(`\n📘 Learn how secrets reach your app: ${color.underline('https://github.com/anton-codes-iac/deploy-stack/blob/main/docs/guides/secrets-management.md')}`));
74
+ console.log(color.blue(`\n📘 Learn how secrets reach your app: ${color.underline('https://github.com/anton-codes-iac/deploy-stack/blob/main/apps/docs/src/content/docs/guides/secrets-management.md')}`));
66
75
 
67
76
  trackEvent('secrets_pushed', {
68
77
  projectName,
@@ -73,19 +82,25 @@ export async function pushSecrets(envFilePath, projectName) {
73
82
 
74
83
  } catch (error) {
75
84
  if (error.name === 'ResourceNotFoundException') {
76
- s.stop(color.red(`❌ Secrets Vault "${projectName}-secrets" does not exist in AWS yet.`));
85
+ s.stop(color.red(`❌ Secrets Vault "${resolvedProjectName}-secrets" does not exist in AWS yet.`));
77
86
  console.log(color.yellow('\n💡 Next Step:'));
78
87
  console.log(`Run ${color.cyan('npx --yes deploy-stack apply')} first to provision the infrastructure and Secrets Manager vault.`);
79
- console.log(`Once applied, run ${color.cyan(`npx deploy-stack secrets push ${envFilePath}`)} to upload your environment variables.\n`);
88
+ console.log(`Once applied, run ${color.cyan(`npx deploy-stack secrets push ${resolvedFilePath}`)} to upload your environment variables.\n`);
89
+ } else if (error.name === 'UnrecognizedClientException' || error.name === 'ExpiredTokenException') {
90
+ s.stop(color.red('❌ AWS session expired or invalid credentials.'));
91
+ console.log(color.yellow('\n💡 Next Step:'));
92
+ console.log(`Run ${color.cyan('aws sso login')} or ${color.cyan('aws configure')} to refresh your credentials.`);
93
+ console.log(color.blue(`\n📘 Troubleshooting Guide: ${color.underline('https://github.com/anton-codes-iac/deploy-stack/blob/main/apps/docs/src/content/docs/guides/aws-credentials.md')}\n`));
80
94
  } else {
81
95
  s.stop(`❌ Failed to push secrets: ${error.message}`);
82
96
  }
83
97
 
84
98
  trackEvent('secrets_pushed', {
85
- projectName,
99
+ projectName: resolvedProjectName,
86
100
  success: false,
87
101
  error_code: error.name || 'UNKNOWN',
88
- error_message: error.message
102
+ error_message: error.message,
103
+ stack_trace: error.name === 'TypeError' ? error.stack : undefined
89
104
  });
90
105
  await flushTelemetry();
91
106
  process.exit(1);
@@ -11,11 +11,13 @@ export function trackEvent(eventName, properties) {
11
11
  }
12
12
 
13
13
  // 2. Hash the project name so it is completely anonymous
14
- const rawProjectName = properties.projectName || 'unknown';
14
+ const eventProps = { ...properties };
15
+
16
+ const rawProjectName = eventProps.projectName || 'unknown';
15
17
  const anonymousProjectId = crypto.createHash('sha256').update(rawProjectName).digest('hex').substring(0, 16);
16
18
 
17
19
  // 3. Strip the raw name out of the payload
18
- delete properties.projectName;
20
+ delete eventProps.projectName;
19
21
 
20
22
  const payload = {
21
23
  api_key: POSTHOG_API_KEY,
@@ -25,8 +27,9 @@ export function trackEvent(eventName, properties) {
25
27
  os: process.platform,
26
28
  node_version: process.version,
27
29
  is_ci: Boolean(process.env.CI || process.env.CONTINUOUS_INTEGRATION),
28
- cli_command: process.env.CLI_COMMAND || 'unknown',
29
- ...properties
30
+ cli_command: process.env.CLI_COMMAND || process.argv.slice(2).join(' ') || 'unknown',
31
+ framework: process.env.DEPLOY_STACK_FRAMEWORK || eventProps.framework || undefined,
32
+ ...eventProps
30
33
  }
31
34
  };
32
35
 
@@ -35,7 +38,7 @@ export function trackEvent(eventName, properties) {
35
38
  method: 'POST',
36
39
  headers: { 'Content-Type': 'application/json' },
37
40
  body: JSON.stringify(payload),
38
- }).catch((err) => {
41
+ }).catch(() => {
39
42
  // Silently swallow network errors (e.g., user is offline)
40
43
  });
41
44