deploy-stack 0.14.3 β 0.15.1
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 +10 -31
- package/docs/ROADMAP.md +23 -0
- package/docs/examples.md +23 -0
- package/package.json +2 -1
- package/src/commands/init.js +19 -1
- package/src/utils/dockerCompose.js +64 -0
- package/src/utils/generator.js +54 -0
- package/templates/docker/node.Dockerfile +8 -4
- package/templates/terraform/main.tf +2 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ You retain complete ownership of your infrastructure code without relying on bla
|
|
|
30
30
|
**π Zero-Config Deployments**
|
|
31
31
|
* **Framework Agnostic:** Tailored container presets for Next.js, Express.js, FastAPI, Go, Django, Rails, Nuxt 3, and Static Sites (React, Vue, SvelteKit, Astro).
|
|
32
32
|
* **Smart Discovery:** Automatically detects build output directories and generates highly optimized, multi-stage Dockerfiles.
|
|
33
|
-
* **
|
|
33
|
+
* **Migration Engines:** Natively parses Heroku `Procfile` configurations, `vercel.json` routing rules, and `docker-compose.yml` sidecar architectures to automatically translate them into standard AWS Fargate and Application Load Balancer topologies.
|
|
34
34
|
* **Database Scaffolding:** Automatically provisions fully isolated, zero-trust AWS RDS PostgreSQL databases for backend monoliths.
|
|
35
35
|
|
|
36
36
|
**π‘οΈ DevSecOps & Security**
|
|
@@ -124,18 +124,11 @@ your-project/
|
|
|
124
124
|
## π¦ Reference Implementations
|
|
125
125
|
|
|
126
126
|
* **[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.
|
|
127
|
-
* **[
|
|
128
|
-
* **[
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
* **[Astro Static Site](https://github.com/anton-codes-iac/deploy-stack-astro-example):** Demonstrates modern static site generation (SSG).
|
|
133
|
-
* **[SvelteKit Application](https://github.com/anton-codes-iac/deploy-stack-svelte-example):** Demonstrates static adapter integration and custom output folder detection.
|
|
134
|
-
* **[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.
|
|
135
|
-
* **[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.
|
|
136
|
-
* **[Django / Python](https://github.com/anton-codes-iac/deploy-stack-django-example):** A secure Gunicorn/WSGI implementation with PostgreSQL and unprivileged container adapters.
|
|
137
|
-
* **[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.
|
|
138
|
-
* **[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.
|
|
127
|
+
* **[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`.
|
|
128
|
+
* **[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.
|
|
129
|
+
|
|
130
|
+
π **[View all 14+ reference implementations in our Examples Gallery](./docs/examples.md)**
|
|
131
|
+
|
|
139
132
|
---
|
|
140
133
|
|
|
141
134
|
## π‘οΈ Telemetry & Privacy
|
|
@@ -150,28 +143,14 @@ npx deploy-stack --no-telemetry
|
|
|
150
143
|
|
|
151
144
|
## πΊοΈ Roadmap
|
|
152
145
|
|
|
153
|
-
### Phase
|
|
154
|
-
- [x] **Core MVP:** Interactive CLI, ECS Fargate + ALB generation, CI/CD, and Secrets sync.
|
|
155
|
-
- [x] **Production Readiness:** CloudFront CDN edge distribution, native S3 state locking, and secure OIDC integration.
|
|
156
|
-
- [x] **Smart Experience:** Zero-config framework auto-discovery for static output directories.
|
|
157
|
-
- [x] **Trust & Observability:** DevSecOps Trivy scanning, automated 5XX alarms, 14-day log retention, and safe local overwrite protections.
|
|
158
|
-
|
|
159
|
-
### Phase 4: Trust Anchors & TAM Expansion (Completed)
|
|
160
|
-
- [x] **Ecosystem Distribution:** Native GitHub Marketplace Action for rapid discovery.
|
|
161
|
-
- [x] **Cost Transparency:** Pre-flight AWS cost estimator injected directly into the CLI wizard.
|
|
162
|
-
- [x] **Zero Vendor Lock-In:** Explicit `npx deploy-stack eject` command to safely strip `ManagedBy` tags and CLI metadata, leaving behind pure IaC.
|
|
163
|
-
- [x] **Heavy Backend Monoliths:** Hardened, unprivileged container adapters for Go, Nuxt.js, Django, and Rails, complete with automated zero-trust RDS PostgreSQL provisioning.
|
|
164
|
-
|
|
165
|
-
### Phase 5: The Activation Engine (Completed)
|
|
166
|
-
- [x] **Local Execution Wrapper:** Native `deploy-stack apply` command with terminal-optimized streaming to eliminate Terraform context switching.
|
|
167
|
-
- [x] **Ecosystem Integrations:** Official plugins published to the Astro Integrations directory (`astro-deploy-stack`) and Nuxt module registry (`nuxt-deploy-stack`).
|
|
168
|
-
|
|
169
|
-
### Phase 6: Migration & Trust Engine (Current)
|
|
146
|
+
### Current Focus (Phase 6: Migration & Trust Engine)
|
|
170
147
|
- [x] **Dry-Run Visualization:** Interactive pre-flight terminal UI with ASCII topology maps and precise, dynamic AWS cost estimation.
|
|
171
148
|
- [x] **PaaS Importers:** Auto-parse `vercel.json` and Heroku `Procfile` configurations to map routing rules, web commands, and background workers automatically.
|
|
172
|
-
- [
|
|
149
|
+
- [x] **Docker Compose to ECS Translator:** Automatically converting a familiar local `docker-compose.yml` into production ECS task definitions.
|
|
173
150
|
- [ ] **AI Agent Rulesets:** Publishing `.cursorrules` and Copilot instructions that teach AI assistants exactly how to utilize the CLI on the user's behalf.
|
|
174
151
|
|
|
152
|
+
π **[See the full project history and future plans in ROADMAP.md](./ROADMAP.md)**
|
|
153
|
+
|
|
175
154
|
---
|
|
176
155
|
|
|
177
156
|
## π License
|
package/docs/ROADMAP.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# πΊοΈ deploy-stack Roadmap
|
|
2
|
+
|
|
3
|
+
### Phase 1β3: The Core Engine (Completed)
|
|
4
|
+
- [x] **Core MVP:** Interactive CLI, ECS Fargate + ALB generation, CI/CD, and Secrets sync.
|
|
5
|
+
- [x] **Production Readiness:** CloudFront CDN edge distribution, native S3 state locking, and secure OIDC integration.
|
|
6
|
+
- [x] **Smart Experience:** Zero-config framework auto-discovery for static output directories.
|
|
7
|
+
- [x] **Trust & Observability:** DevSecOps Trivy scanning, automated 5XX alarms, 14-day log retention, and safe local overwrite protections.
|
|
8
|
+
|
|
9
|
+
### Phase 4: Trust Anchors & TAM Expansion (Completed)
|
|
10
|
+
- [x] **Ecosystem Distribution:** Native GitHub Marketplace Action for rapid discovery.
|
|
11
|
+
- [x] **Cost Transparency:** Pre-flight AWS cost estimator injected directly into the CLI wizard.
|
|
12
|
+
- [x] **Zero Vendor Lock-In:** Explicit `npx deploy-stack eject` command to safely strip `ManagedBy` tags and CLI metadata, leaving behind pure IaC.
|
|
13
|
+
- [x] **Heavy Backend Monoliths:** Hardened, unprivileged container adapters for Go, Nuxt.js, Django, and Rails, complete with automated zero-trust RDS PostgreSQL provisioning.
|
|
14
|
+
|
|
15
|
+
### Phase 5: The Activation Engine (Completed)
|
|
16
|
+
- [x] **Local Execution Wrapper:** Native `deploy-stack apply` command with terminal-optimized streaming to eliminate Terraform context switching.
|
|
17
|
+
- [x] **Ecosystem Integrations:** Official plugins published to the Astro Integrations directory (`astro-deploy-stack`) and Nuxt module registry (`nuxt-deploy-stack`).
|
|
18
|
+
|
|
19
|
+
### Phase 6: Migration & Trust Engine (Current)
|
|
20
|
+
- [x] **Dry-Run Visualization:** Interactive pre-flight terminal UI with ASCII topology maps and precise, dynamic AWS cost estimation.
|
|
21
|
+
- [x] **PaaS Importers:** Auto-parse `vercel.json` and Heroku `Procfile` configurations to map routing rules, web commands, and background workers automatically.
|
|
22
|
+
- [x] **Docker Compose to ECS Translator:** Automatically converting a familiar local `docker-compose.yml` into production ECS task definitions.
|
|
23
|
+
- [ ] **AI Agent Rulesets:** Publishing `.cursorrules` and Copilot instructions that teach AI assistants exactly how to utilize the CLI on the user's behalf.
|
package/docs/examples.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# π¦ Reference Implementations & Examples
|
|
2
|
+
|
|
3
|
+
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
|
+
|
|
5
|
+
### Featured Migrations
|
|
6
|
+
* **[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
|
+
* **[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
|
+
* **[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
|
+
|
|
10
|
+
### Frontend & Fullstack Frameworks
|
|
11
|
+
* **[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.
|
|
12
|
+
* **[Vite / React SPA](https://github.com/anton-codes-iac/deploy-stack-vite-example):** Demonstrates SPA routing and `dist/` auto-detection.
|
|
13
|
+
* **[Create React App](https://github.com/anton-codes-iac/deploy-stack-cra-example):** Validates backward compatibility with legacy Webpack pipelines and `build/` auto-detection.
|
|
14
|
+
* **[Astro Static Site](https://github.com/anton-codes-iac/deploy-stack-astro-example):** Demonstrates modern static site generation (SSG).
|
|
15
|
+
* **[SvelteKit Application](https://github.com/anton-codes-iac/deploy-stack-svelte-example):** Demonstrates static adapter integration and custom output folder detection.
|
|
16
|
+
* **[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.
|
|
17
|
+
|
|
18
|
+
### Backend APIs & Monoliths
|
|
19
|
+
* **[Express.js API](https://github.com/anton-codes-iac/deploy-stack-express-example):** A standard Node.js backend setup.
|
|
20
|
+
* **[Python FastAPI](https://github.com/anton-codes-iac/deploy-stack-fastapi-example):** A Python API demonstrating unprivileged port mapping.
|
|
21
|
+
* **[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.
|
|
22
|
+
* **[Django / Python](https://github.com/anton-codes-iac/deploy-stack-django-example):** A secure Gunicorn/WSGI implementation with PostgreSQL and unprivileged container adapters.
|
|
23
|
+
* **[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.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deploy-stack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18.0.0"
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"@aws-sdk/client-sts": "3.1115.0",
|
|
47
47
|
"@clack/prompts": "1.7.0",
|
|
48
48
|
"dotenv": "17.4.2",
|
|
49
|
+
"js-yaml": "5.4.1",
|
|
49
50
|
"picocolors": "1.1.1"
|
|
50
51
|
}
|
|
51
52
|
}
|
package/src/commands/init.js
CHANGED
|
@@ -20,6 +20,7 @@ import { handleExistingFiles } from '../utils/backup.js';
|
|
|
20
20
|
import { estimateMonthlyCost } from '../utils/visualizer.js';
|
|
21
21
|
import { getTargetDirectory, getProjectConfig } from '../utils/prompts.js';
|
|
22
22
|
import { resolveDjangoWsgi, handleRailsCI } from '../utils/frameworks.js';
|
|
23
|
+
import { hasDockerCompose, parseDockerCompose } from '../utils/dockerCompose.js';
|
|
23
24
|
|
|
24
25
|
const pkg = JSON.parse(fsSync.readFileSync(new URL('../../package.json', import.meta.url)));
|
|
25
26
|
const CLI_VERSION = pkg.version;
|
|
@@ -42,11 +43,13 @@ export async function mainStack({ isHeadless = false, headlessOptions = {} } = {
|
|
|
42
43
|
const detectedFramework = detectFramework(dirConfig.targetDir);
|
|
43
44
|
const procfile = parseProcfile(dirConfig.targetDir);
|
|
44
45
|
const vercelRules = parseVercelConfig(dirConfig.targetDir);
|
|
46
|
+
const dockerCompose = parseDockerCompose(dirConfig.targetDir);
|
|
45
47
|
|
|
46
48
|
if (!isHeadless) {
|
|
47
49
|
if (detectedFramework) log.success(`Auto-detected framework: ${detectedFramework.name}`);
|
|
48
50
|
if (procfile && procfile.web) log.success(`Auto-detected Procfile (web command: ${procfile.web.join(' ')})`);
|
|
49
51
|
if (vercelRules) log.success(`Auto-detected vercel.json (Migrating edge network rules)`);
|
|
52
|
+
if (dockerCompose) log.success(`Auto-detected docker-compose.yml (${dockerCompose.length} services mapped)`);
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
if (isHeadless) console.log(color.cyan(`π€ Running deploy-stack in headless mode`));
|
|
@@ -56,6 +59,19 @@ export async function mainStack({ isHeadless = false, headlessOptions = {} } = {
|
|
|
56
59
|
const djangoWsgi = await resolveDjangoWsgi(dirConfig.targetDir, procfile, config.framework, isHeadless);
|
|
57
60
|
const disableDefaultCI = await handleRailsCI(dirConfig.targetDir, config.framework, isHeadless);
|
|
58
61
|
|
|
62
|
+
// 3.5 Docker Compose Overrides
|
|
63
|
+
if (dockerCompose && dockerCompose.length > 0) {
|
|
64
|
+
// Find the first service that has an exposed port
|
|
65
|
+
const webService = dockerCompose.find(s => s.port);
|
|
66
|
+
if (webService && webService.port) {
|
|
67
|
+
// Override the config port with the one from Docker Compose
|
|
68
|
+
config.port = webService.port.toString();
|
|
69
|
+
if (!isHeadless) {
|
|
70
|
+
console.log(color.cyan(` π³ Docker Compose overrides: Port set to ${config.port} via service "${webService.name}"`));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
59
75
|
// 4. Framework Migration Checks (Vercel Escape Hatch)
|
|
60
76
|
if (config.framework === 'nextjs') {
|
|
61
77
|
const nextConfig = analyzeNextConfig(dirConfig.targetDir);
|
|
@@ -126,7 +142,8 @@ export async function mainStack({ isHeadless = false, headlessOptions = {} } = {
|
|
|
126
142
|
DJANGO_WSGI: djangoWsgi,
|
|
127
143
|
DISABLE_DEFAULT_CI: disableDefaultCI,
|
|
128
144
|
PROCFILE: procfile,
|
|
129
|
-
VERCEL_RULES: vercelRules
|
|
145
|
+
VERCEL_RULES: vercelRules,
|
|
146
|
+
DOCKER_COMPOSE: dockerCompose
|
|
130
147
|
});
|
|
131
148
|
|
|
132
149
|
// 8. Telemetry
|
|
@@ -151,6 +168,7 @@ export async function mainStack({ isHeadless = false, headlessOptions = {} } = {
|
|
|
151
168
|
has_worker: !!(procfile && procfile.worker),
|
|
152
169
|
is_heroku_migration: !!procfile,
|
|
153
170
|
is_vercel_migration: !!vercelRules,
|
|
171
|
+
is_docker_compose: !!dockerCompose,
|
|
154
172
|
});
|
|
155
173
|
|
|
156
174
|
s.stop('Infrastructure provisioned successfully!');
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { load } from 'js-yaml';
|
|
4
|
+
|
|
5
|
+
// 1. Check if a Docker Compose file exists in the target directory.
|
|
6
|
+
export function hasDockerCompose(targetDir) {
|
|
7
|
+
return fs.existsSync(path.join(targetDir, 'docker-compose.yml')) ||
|
|
8
|
+
fs.existsSync(path.join(targetDir, 'docker-compose.yaml'));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 2. Parse docker-compose.yml and normalize it into an array of services.
|
|
12
|
+
export function parseDockerCompose(targetDir) {
|
|
13
|
+
let composePath = path.join(targetDir, 'docker-compose.yml');
|
|
14
|
+
if (!fs.existsSync(composePath)) {
|
|
15
|
+
composePath = path.join(targetDir, 'docker-compose.yaml');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!fs.existsSync(composePath)) return null;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const fileContents = fs.readFileSync(composePath, 'utf8');
|
|
22
|
+
const data = load(fileContents);
|
|
23
|
+
|
|
24
|
+
if (!data || !data.services) return null;
|
|
25
|
+
|
|
26
|
+
const services = [];
|
|
27
|
+
|
|
28
|
+
for (const [name, config] of Object.entries(data.services)) {
|
|
29
|
+
// Extract the container port (e.g., "8080:80" -> 80)
|
|
30
|
+
let exposedPort = null;
|
|
31
|
+
if (config.ports && config.ports.length > 0) {
|
|
32
|
+
// Handle different port formats like "3000" or "8000:8000" or "127.0.0.1:8001:8001"
|
|
33
|
+
const portMapping = config.ports[0].toString();
|
|
34
|
+
const parts = portMapping.split(':');
|
|
35
|
+
exposedPort = parseInt(parts[parts.length - 1].replace(/[^0-9]/g, ''), 10);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Normalize environment variables from Array or Object
|
|
39
|
+
let envVars = {};
|
|
40
|
+
if (Array.isArray(config.environment)) {
|
|
41
|
+
config.environment.forEach(env => {
|
|
42
|
+
const [key, ...val] = env.split('=');
|
|
43
|
+
if (key) envVars[key] = val.join('=');
|
|
44
|
+
});
|
|
45
|
+
} else if (typeof config.environment === 'object') {
|
|
46
|
+
envVars = config.environment;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
services.push({
|
|
50
|
+
name,
|
|
51
|
+
build: config.build || null,
|
|
52
|
+
image: config.image || null,
|
|
53
|
+
port: exposedPort,
|
|
54
|
+
command: config.command || null,
|
|
55
|
+
environment: envVars
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return services;
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.error('β οΈ Failed to parse Docker Compose file:', e.message);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/utils/generator.js
CHANGED
|
@@ -62,6 +62,60 @@ export async function generateTemplates(targetDir, config) {
|
|
|
62
62
|
config.DB_ENV_VARS = '';
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// 3.3. Docker Compose Overrides & Sidecars
|
|
66
|
+
let composeWebEnvVars = [];
|
|
67
|
+
let extraContainersHCL = "";
|
|
68
|
+
|
|
69
|
+
if (config.DOCKER_COMPOSE && config.DOCKER_COMPOSE.length > 0) {
|
|
70
|
+
// Identify the main web service (the one with the exposed port)
|
|
71
|
+
const webService = config.DOCKER_COMPOSE.find(s => s.port) || config.DOCKER_COMPOSE[0];
|
|
72
|
+
|
|
73
|
+
// Extract Web Env Vars
|
|
74
|
+
if (webService.environment) {
|
|
75
|
+
for (const [key, val] of Object.entries(webService.environment)) {
|
|
76
|
+
composeWebEnvVars.push(`{ "name": "${key}", "value": "${val}" }`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Extract Web Command Override (only if Procfile hasn't already set it)
|
|
81
|
+
if (webService.command && !config.TASK_COMMAND) {
|
|
82
|
+
config.TASK_COMMAND = `command = ${JSON.stringify(typeof webService.command === 'string' ? webService.command.split(' ') : webService.command)}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Process Sidecar Containers (e.g., Redis, Memcached)
|
|
86
|
+
const sidecars = config.DOCKER_COMPOSE.filter(s => s.name !== webService.name);
|
|
87
|
+
if (sidecars.length > 0) {
|
|
88
|
+
extraContainersHCL = sidecars.map(service => {
|
|
89
|
+
const sidecarEnv = Object.entries(service.environment || {})
|
|
90
|
+
.map(([k, v]) => `{ "name": "${k}", "value": "${String(v).replace(/"/g, '\\"')}" }`)
|
|
91
|
+
.join(',\n ');
|
|
92
|
+
|
|
93
|
+
const sidecarCmd = service.command ? `\n command = ${JSON.stringify(typeof service.command === 'string' ? service.command.split(' ') : service.command)}` : '';
|
|
94
|
+
|
|
95
|
+
return `,
|
|
96
|
+
{
|
|
97
|
+
name = "${service.name}"
|
|
98
|
+
image = "${service.image || 'alpine:latest'}"
|
|
99
|
+
essential = true
|
|
100
|
+
environment = [
|
|
101
|
+
${sidecarEnv}
|
|
102
|
+
]${sidecarCmd}
|
|
103
|
+
logConfiguration = {
|
|
104
|
+
logDriver = "awslogs"
|
|
105
|
+
options = {
|
|
106
|
+
"awslogs-group" = aws_cloudwatch_log_group.app_logs.name
|
|
107
|
+
"awslogs-region" = "${config.REGION}"
|
|
108
|
+
"awslogs-stream-prefix" = "ecs-${service.name}"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}`;
|
|
112
|
+
}).join('');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
config.COMPOSE_WEB_ENV_VARS = composeWebEnvVars.length > 0 ? composeWebEnvVars.join(',\n ') + ',' : '';
|
|
117
|
+
config.EXTRA_CONTAINERS = extraContainersHCL;
|
|
118
|
+
|
|
65
119
|
// 4. Configure AWS Secrets Manager Integration
|
|
66
120
|
// Build the initial HCL map for AWS Secrets Manager
|
|
67
121
|
let initialSecretMap = `{\n EXAMPLE_API_KEY = "replace_me_in_aws_console"`;
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
FROM node:22-alpine
|
|
2
2
|
|
|
3
|
-
# 1.
|
|
3
|
+
# 1. DevSecOps: Patch underlying Alpine OS vulnerabilities and update global npm
|
|
4
|
+
RUN apk update && apk upgrade --no-cache && \
|
|
5
|
+
npm install -g npm@latest
|
|
6
|
+
|
|
7
|
+
# 2. Set production environment (optimizes Node and prevents dev dependencies)
|
|
4
8
|
ENV NODE_ENV=production
|
|
5
9
|
|
|
6
10
|
WORKDIR /app
|
|
7
11
|
|
|
8
|
-
#
|
|
12
|
+
# 3. Copy dependency manifests with non-root ownership
|
|
9
13
|
COPY --chown=node:node package*.json ./
|
|
10
14
|
RUN npm ci --omit=dev
|
|
11
15
|
|
|
12
|
-
#
|
|
16
|
+
# 4. Copy application code with non-root ownership
|
|
13
17
|
COPY --chown=node:node . .
|
|
14
18
|
|
|
15
|
-
#
|
|
19
|
+
# 5. DevSecOps best practice: do not run the container as root
|
|
16
20
|
USER node
|
|
17
21
|
|
|
18
22
|
EXPOSE {{PORT}}
|
|
@@ -83,6 +83,7 @@ resource "aws_ecs_task_definition" "app" {
|
|
|
83
83
|
essential = true
|
|
84
84
|
|
|
85
85
|
environment = [
|
|
86
|
+
{{COMPOSE_WEB_ENV_VARS}}
|
|
86
87
|
{{DB_ENV_VARS}}
|
|
87
88
|
]
|
|
88
89
|
|
|
@@ -117,7 +118,7 @@ resource "aws_ecs_task_definition" "app" {
|
|
|
117
118
|
"awslogs-stream-prefix" = "ecs"
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
|
-
}
|
|
121
|
+
}{{EXTRA_CONTAINERS}}
|
|
121
122
|
])
|
|
122
123
|
}
|
|
123
124
|
|