deploy-stack 0.17.5 → 0.17.7

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
@@ -132,6 +132,7 @@ your-project/
132
132
  * **[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.
133
133
  * **[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`.
134
134
  * **[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.
135
+ * **[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. Encrypts local `.env` variables directly into AWS and maps them into ECS memory at container boot, verified against GitHub's API.
135
136
 
136
137
  šŸ‘‰ **[View all 14+ reference implementations in our Examples Gallery](./docs/examples.md)**
137
138
 
@@ -165,7 +166,13 @@ npx deploy-stack --no-telemetry
165
166
  ### Current Focus (Phase 7: Team Workflows & Ecosystem Integrations)
166
167
  - [x] **Ephemeral PR Previews:** Generate GitHub Actions workflows that spin up temporary ECS Fargate tasks and post live preview URLs directly in pull request comments to streamline team code reviews.
167
168
  - [x] **AI Context Synchronization:** Implement `deploy-stack sync-ai` to automatically generate `.cursorrules` and AI context files, ensuring coding assistants generate accurate deployment commands tailored to the project.
168
- - [ ] **Native Ecosystem Integrations:** Publish seamless, push-button plugins across major frameworks. Targets include a `svelte-adapter-deploy-stack`, an official `create-next-app` AWS template, a `vite-plugin-deploy-stack`, a NestJS schematic, and a Django Cookiecutter template.
169
+ - [ ] **Native Ecosystem Integrations & Scaffolding:** Publish seamless, push-button plugins and templates across major framework package registries:
170
+ - [x] `vite-plugin-deploy-stack` (Vite / React / Vue SPA ecosystem)
171
+ - [x] `svelte-adapter-deploy-stack` (SvelteKit adapter integration)
172
+ - [x] `cookiecutter-django-deploy-stack` (Listed on Django Packages)
173
+ - [ ] `cookiecutter-fastapi-deploy-stack` (Cookiecutter for modern async Python)
174
+ - [ ] `nest-deploy-stack` (Native `nest add` schematic for NestJS)
175
+ - [ ] `rails-template-deploy-stack` (Zero-click Ruby on Rails application template)
169
176
  - [ ] **Automated Troubleshooting:** Build `deploy-stack diagnose` (alias: `wtf`) to automatically analyze and troubleshoot common day-2 AWS operational issues (e.g., Fargate OOM kills, ALB 502s) directly from the terminal.
170
177
 
171
178
  šŸ‘‰ **[See the full project history and future plans in ROADMAP.md](./ROADMAP.md)**
package/bin/cli.js CHANGED
@@ -10,20 +10,29 @@ import { syncAi } from '../src/commands/sync-ai.js';
10
10
 
11
11
  // 1. Extract the telemetry flag and set the environment variable
12
12
  const rawArgs = process.argv.slice(2);
13
- const hasNoTelemetry = rawArgs.includes('--no-telemetry');
13
+ const hasNoTelemetry = rawArgs.some(arg => arg === '--no-telemetry' || arg.startsWith('--no-telemetry='));
14
14
 
15
15
  if (hasNoTelemetry) {
16
16
  process.env.DO_NOT_TRACK = '1';
17
17
  }
18
18
 
19
19
  // 2. Filter out the telemetry flag from the args so the subcommands don't see it
20
- const args = rawArgs.filter((arg) => arg !== '--no-telemetry');
20
+ const args = rawArgs.filter((arg) => arg !== '--no-telemetry' && !arg.startsWith('--no-telemetry='));
21
21
 
22
- // 3. Parse headless flags
22
+ // 3. Isolate positional commands from flags
23
+ // This ensures flags (e.g., --dry-run) don't accidentally become file paths
24
+ const positionalArgs = args.filter(arg => !arg.startsWith('--'));
25
+
26
+ // Safely capture the base command for telemetry (ignoring file paths)
27
+ const baseCommand = positionalArgs.length > 0 ? positionalArgs.slice(0, 2).join(' ') : 'init';
28
+ process.env.CLI_COMMAND = baseCommand;
29
+
30
+ // 4. Parse headless flags
23
31
  const isHeadless = args.includes('--headless');
24
32
  const isDryRun = args.includes('--dry-run');
25
33
  const getFlag = (flagName) => {
26
- const match = args.find(a => a.startsWith(`--${flagName}=`));
34
+ const match = args.find(a => a === `--${flagName}` || a.startsWith(`--${flagName}=`));
35
+ if (match === `--${flagName}`) return true;
27
36
  return match ? match.split('=')[1] : undefined;
28
37
  };
29
38
  const headlessOptions = isHeadless ? {
@@ -34,10 +43,12 @@ const headlessOptions = isHeadless ? {
34
43
  size: getFlag('size'),
35
44
  healthCheckPath: getFlag('healthCheckPath'),
36
45
  desiredCount: getFlag('desiredCount'),
37
- branch: getFlag('branch')
46
+ branch: getFlag('branch'),
47
+ needsDatabase: getFlag('needsDatabase'),
48
+ enablePrPreviews: getFlag('enablePrPreviews')
38
49
  } : {};
39
50
 
40
- // 4. Handle commands
51
+ // 5. Handle commands
41
52
  if (args[0] === 'secrets' && args[1] === 'push') {
42
53
  const envFile = args[2] || '.env';
43
54
  const projectName = path.basename(process.cwd());
package/docs/ROADMAP.md CHANGED
@@ -24,7 +24,13 @@
24
24
 
25
25
  ### Phase 7: Team Workflows & Ecosystem Integrations (Current)
26
26
  *Focus: Enhance collaborative development and expand native support across major framework ecosystems.*
27
- - [x] **Ephemeral PR Previews:** Generate GitHub Actions workflows that spin up temporary ECS Fargate tasks and post live URLs directly in PR comments to streamline team code reviews.
28
- - [x] **AI Context Synchronization:** Implement `deploy-stack sync-ai` to automatically generate `.cursorrules` and AI context files, ensuring IDE assistants understand the infrastructure.
29
- - [ ] **Native Ecosystem Integrations:** Publish push-button plugins across major frameworks (e.g., `svelte-adapter-deploy-stack`, `create-next-app` AWS template, `vite-plugin-deploy-stack`, NestJS deployment schematic, Django Cookiecutter).
30
- - [ ] **Automated Troubleshooting:** Build `deploy-stack diagnose` to auto-diagnose common day-2 AWS failures (e.g., Fargate OOM kills, ALB 502s) to establish immediate technical credibility and simplify maintenance.
27
+ - [x] **Ephemeral PR Previews:** Generate GitHub Actions workflows that spin up temporary ECS Fargate tasks and post live preview URLs directly in pull request comments to streamline team code reviews.
28
+ - [x] **AI Context Synchronization:** Implement `deploy-stack sync-ai` to automatically generate `.cursorrules` and AI context files, ensuring coding assistants generate accurate deployment commands tailored to the project.
29
+ - [ ] **Native Ecosystem Integrations:** Publish seamless, push-button plugins across major frameworks.
30
+ - [x] `vite-plugin-deploy-stack` (Live on NPM)
31
+ - [x] `svelte-adapter-deploy-stack` (SvelteKit adapter integration)
32
+ - [x] `cookiecutter-django-deploy-stack` (Listed on Django Packages)
33
+ - [ ] `cookiecutter-fastapi-deploy-stack` (Cookiecutter for modern async Python)
34
+ - [ ] `nest-deploy-stack` (Native `nest add` schematic for NestJS)
35
+ - [ ] `rails-template-deploy-stack` (Zero-click Ruby on Rails application template)
36
+ - [ ] **Automated Troubleshooting:** Build `deploy-stack diagnose` (alias: `wtf`) to automatically analyze and troubleshoot common day-2 AWS operational issues (e.g., Fargate OOM kills, ALB 502s) directly from the terminal.
package/docs/examples.md CHANGED
@@ -7,6 +7,9 @@ These repositories demonstrate how `deploy-stack` handles various frameworks and
7
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
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
9
 
10
+ ### DevSecOps & Security Architectures
11
+ * **[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
+
10
13
  ### Frontend & Fullstack Frameworks
11
14
  * **[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
15
  * **[Vite / React SPA](https://github.com/anton-codes-iac/deploy-stack-vite-example):** Demonstrates SPA routing and `dist/` auto-detection.
@@ -20,4 +23,16 @@ These repositories demonstrate how `deploy-stack` handles various frameworks and
20
23
  * **[Python FastAPI](https://github.com/anton-codes-iac/deploy-stack-fastapi-example):** A Python API demonstrating unprivileged port mapping.
21
24
  * **[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
25
  * **[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.
26
+ * **[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.
27
+
28
+ ---
29
+
30
+ ## 🧩 Ecosystem Plugins & Starters
31
+
32
+ In addition to standalone reference repositories, `deploy-stack` provides native integrations that hook directly into framework build pipelines and community template engines:
33
+
34
+ * **[astro-deploy-stack](https://www.npmjs.com/package/astro-deploy-stack):** Push-button deployment plugin for Astro sites.
35
+ * **[nuxt-deploy-stack](https://www.npmjs.com/package/nuxt-deploy-stack):** Nitro-optimized deployment integration for Nuxt 3 applications.
36
+ * **[vite-plugin-deploy-stack](https://www.npmjs.com/package/vite-plugin-deploy-stack):** Zero-config Vite build plugin for single-page applications.
37
+ * **[svelte-adapter-deploy-stack](https://www.npmjs.com/package/svelte-adapter-deploy-stack):** Native SvelteKit adapter producing optimized Fargate container builds.
38
+ * **[cookiecutter-django-deploy-stack](https://github.com/anton-codes-iac/cookiecutter-django-deploy-stack):** Community Django starter listed on [Django Packages](https://djangopackages.org/packages/p/cookiecutter-django-deploy-stack/) with built-in Fargate and managed RDS scaffolding.
@@ -1,6 +1,6 @@
1
1
  # Headless Mode & Automation Guide
2
2
 
3
- The `deploy-stack` CLI is designed to be fully automatable for CI/CD pipelines, custom scripts, and framework plugins (like `vite-plugin-deploy-stack`).
3
+ 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
4
 
5
5
  By passing the `--headless` flag, you bypass all interactive terminal prompts.
6
6
 
@@ -24,10 +24,13 @@ You can append any of these flags to customize the generated architecture. These
24
24
  | `--desiredCount=<number>` | Number of container replicas to run (`1` or `2`). | `1` |
25
25
  | `--branch=<name>` | The primary Git deployment branch for CI/CD. | `main` |
26
26
  | `--dir=<path>` | The directory to generate files into (use `.` for current).| `.` |
27
+ | `--needsDatabase` | Provisions a managed AWS RDS PostgreSQL database alongside Fargate. | `false` |
27
28
  | `--enablePrPreviews` | Generates workflows for Ephemeral PR Previews. | `false` |
28
29
  | `--yes` | Automatically bypasses confirmation prompts during apply/destroy. | `false` |
29
30
  | `--no-telemetry` | Disables anonymous usage analytics. | `false` |
30
31
 
32
+ *(Note: Boolean flags like `--needsDatabase` and `--enablePrPreviews` can be passed alone or as `--flag=true`).*
33
+
31
34
  ## Example Usage
32
35
 
33
36
  **Standard Static Site Automation (e.g., Vite/React):**
@@ -40,7 +43,7 @@ npx deploy-stack --headless --framework=static --region=eu-west-1 --size=micro
40
43
  npx deploy-stack --headless --framework=nextjs --size=small --desiredCount=2 --yes
41
44
  ```
42
45
 
43
- **Node.js Backend with PR Previews:**
46
+ **Django Setup with Managed RDS Database:**
44
47
  ```bash
45
- npx deploy-stack --headless --framework=node --port=4000 --enablePrPreviews
48
+ npx deploy-stack --headless --framework=django --needsDatabase
46
49
  ```
@@ -2,23 +2,49 @@
2
2
 
3
3
  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
4
 
5
- ## Pushing Secrets to AWS
5
+ ## The Secrets Lifecycle
6
6
 
7
- Instead of manually clicking through the AWS Console, use the built-in secrets command:
7
+ To maintain zero-secret Git repositories and safe infrastructure provisioning, secrets follow a strict 4-step lifecycle:
8
+
9
+ ```text
10
+ 1. Scaffold ───▶ 2. Provision Vault ───▶ 3. Push Secrets ───▶ 4. Deploy to App
11
+ (deploy-stack) (deploy-stack apply) (secrets push .env) (git push)
12
+ Generates Terraform Creates empty vault Uploads encrypted keys ECS container boots
13
+ & secret_keys.json in AWS Secrets Mgr & updates secret_keys with injected env
14
+ ```
15
+
16
+ ---
17
+
18
+ ### Step 1: Provision the Vault (Day 1)
19
+ Your Secrets Manager vault is declared in `terraform/secrets.tf`. Provision the base infrastructure first:
20
+
21
+ ```bash
22
+ npx deploy-stack apply
23
+ ```
24
+ *This creates an empty, secure secret vault named `<project-name>-secrets` in your AWS account.*
25
+
26
+ ### Step 2: Push Secrets to AWS
27
+ Once the vault exists, push your local `.env` values directly to AWS:
8
28
 
9
29
  ```bash
10
- npx deploy-stack secrets push .env.production
30
+ npx deploy-stack secrets push .env
11
31
  ```
12
32
 
13
- ### What happens under the hood?
14
- 1. The CLI reads your local `.env.production` file.
33
+ **What happens under the hood?**
34
+ 1. The CLI reads your local `.env` file.
15
35
  2. It encrypts the key-value pairs and pushes them securely into AWS Secrets Manager under your project's namespace (e.g., `my-project-secrets`).
16
36
  3. It generates a local `terraform/secret_keys.json` file containing *only the names* of your keys (e.g., `["API_KEY", "STRIPE_SECRET"]`), **not the values**.
17
37
 
18
- ## How Secrets Reach Your App
38
+ > šŸ’” **Tip:** The `secrets push` command takes the file path as the first argument. If you need to use other flags, ensure they are appended at the end of the command:
39
+ > `npx deploy-stack secrets push .env --any-other-flags`
19
40
 
20
- When you commit `terraform/secret_keys.json` and push to GitHub, your CI/CD pipeline runs Terraform.
41
+ ### Step 3: Map Secrets into the Container
42
+ Commit the updated `terraform/secret_keys.json` and push to GitHub:
21
43
 
22
- Terraform reads the JSON array of key names and dynamically maps them to your ECS Task Definition. When your AWS Fargate container boots up, it automatically injects those secrets directly into your application's environment as standard environment variables (e.g., `process.env.STRIPE_SECRET` or `os.getenv("API_KEY")`).
44
+ ```bash
45
+ git add terraform/secret_keys.json
46
+ git commit -m "chore: map new secrets to ECS"
47
+ git push origin main
48
+ ```
23
49
 
24
- *Note: Because Terraform maps the secrets at runtime, updating a secret value in AWS and running an empty GitHub deployment will instantly cycle your containers with the new keys!*
50
+ Terraform reads `secret_keys.json` during the GitHub Actions deployment and maps each key directly into your ECS Task Definition. When your Fargate container boots up, AWS injects the secret values into `process.env` (Node) or `os.environ` (Python) in memory.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploy-stack",
3
- "version": "0.17.5",
3
+ "version": "0.17.7",
4
4
  "description": "Provision production-ready AWS infrastructure and CI/CD pipelines in seconds.",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -117,7 +117,7 @@ export async function mainStack({ isHeadless = false, headlessOptions = {} } = {
117
117
  } catch (error) {
118
118
  s.stop('āŒ Failed to provision remote state or authenticate with AWS.');
119
119
  console.error(color.red(`AWS Error: ${error.message}`));
120
- trackEvent('cli-error', { step: 'aws_provisioning', error_code: error.name || 'UNKNOWN' });
120
+ trackEvent('cli-error', { step: 'aws_provisioning', error_code: error.name || 'UNKNOWN', error_message: error.message });
121
121
  await flushTelemetry();
122
122
  process.exit(1);
123
123
  }
@@ -13,7 +13,16 @@ export async function pushSecrets(envFilePath, projectName) {
13
13
  try {
14
14
  // 1. Read and parse the local .env file
15
15
  const envPath = path.resolve(process.cwd(), envFilePath);
16
- const envContent = await fs.readFile(envPath, 'utf-8');
16
+ let envContent;
17
+ try {
18
+ envContent = await fs.readFile(envPath, 'utf-8');
19
+ } catch (fsError) {
20
+ if (fsError.code === 'ENOENT') {
21
+ throw new Error(`File not found: ${envFilePath}. Please ensure the file exists before pushing.`);
22
+ }
23
+ throw fsError; // Re-throw if it's a permissions issue
24
+ }
25
+
17
26
  const parsedSecrets = dotenv.parse(envContent);
18
27
 
19
28
  if (Object.keys(parsedSecrets).length === 0) {
@@ -21,12 +30,23 @@ export async function pushSecrets(envFilePath, projectName) {
21
30
  return;
22
31
  }
23
32
 
24
- // 2. Initialize the AWS Client
25
- // It automatically uses the AWS credentials the user set in their terminal
26
- const client = new SecretsManagerClient({});
33
+ // 2. Dynamically resolve the exact region from Terraform
34
+ let targetRegion = process.env.AWS_REGION;
35
+ try {
36
+ const mainTfPath = path.join(process.cwd(), 'terraform', 'main.tf');
37
+ const mainTfContent = await fs.readFile(mainTfPath, 'utf-8');
38
+ const regionMatch = mainTfContent.match(/region\s*=\s*"([^"]+)"/);
39
+ if (regionMatch) {
40
+ targetRegion = regionMatch[1];
41
+ }
42
+ } catch (e) {
43
+ // Silently fallback to AWS profile defaults if file read fails
44
+ }
27
45
 
28
- // 3. Update the secret string in AWS
29
- // The SecretId matches the name we generated in secrets.tf
46
+ // 3. Initialize the AWS Client locked to the correct region
47
+ const client = new SecretsManagerClient(targetRegion ? { region: targetRegion } : {});
48
+
49
+ // 4. Update the secret string in AWS
30
50
  const command = new UpdateSecretCommand({
31
51
  SecretId: `${projectName}-secrets`,
32
52
  SecretString: JSON.stringify(parsedSecrets),
@@ -39,7 +59,7 @@ export async function pushSecrets(envFilePath, projectName) {
39
59
 
40
60
  await fs.writeFile(keysFilePath, JSON.stringify(keys, null, 2));
41
61
 
42
- s.stop(`āœ… Successfully pushed ${Object.keys(parsedSecrets).length} secrets to AWS!`);
62
+ s.stop(`āœ… Successfully pushed ${Object.keys(parsedSecrets).length} secrets to AWS (${targetRegion || 'default region'})!`);
43
63
  console.log(color.cyan(`\nUpdated ${keysFilePath}`));
44
64
  console.log(color.green('Commit this file and push to GitHub to trigger a deployment with your new variables.'));
45
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')}`));
@@ -52,13 +72,22 @@ export async function pushSecrets(envFilePath, projectName) {
52
72
  await flushTelemetry();
53
73
 
54
74
  } catch (error) {
55
- s.stop(`āŒ Failed to push secrets: ${error.message}`);
75
+ if (error.name === 'ResourceNotFoundException') {
76
+ s.stop(color.red(`āŒ Secrets Vault "${projectName}-secrets" does not exist in AWS yet.`));
77
+ console.log(color.yellow('\nšŸ’” Next Step:'));
78
+ 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`);
80
+ } else {
81
+ s.stop(`āŒ Failed to push secrets: ${error.message}`);
82
+ }
56
83
 
57
84
  trackEvent('secrets_pushed', {
58
85
  projectName,
59
86
  success: false,
60
- error_code: error.name || 'UNKNOWN'
87
+ error_code: error.name || 'UNKNOWN',
88
+ error_message: error.message
61
89
  });
62
90
  await flushTelemetry();
91
+ process.exit(1);
63
92
  }
64
93
  }
@@ -1,9 +1,7 @@
1
1
  import crypto from 'crypto';
2
2
 
3
3
  const TELEMETRY_ENDPOINT = 'https://eu.i.posthog.com/capture/';
4
-
5
4
  const POSTHOG_API_KEY = 'phc_o2wgA3jVT9rVDiGSDzFAR42zZeiVGhhCY53HXVHUcYGT';
6
-
7
5
  const pendingRequests = [];
8
6
 
9
7
  export function trackEvent(eventName, properties) {
@@ -27,6 +25,7 @@ export function trackEvent(eventName, properties) {
27
25
  os: process.platform,
28
26
  node_version: process.version,
29
27
  is_ci: Boolean(process.env.CI || process.env.CONTINUOUS_INTEGRATION),
28
+ cli_command: process.env.CLI_COMMAND || 'unknown',
30
29
  ...properties
31
30
  }
32
31
  };
@@ -46,7 +46,7 @@ export async function getProjectConfig(isHeadless, headlessOptions, targetDir, d
46
46
  desiredCount: headlessOptions.desiredCount || '1',
47
47
  branch: headlessOptions.branch || 'main',
48
48
  needsDatabase: headlessOptions.needsDatabase === 'true' || headlessOptions.needsDatabase === true,
49
- enablePrPreviews: headlessOptions.enablePrPreviews || false,
49
+ enablePrPreviews: headlessOptions.enablePrPreviews === 'true' || headlessOptions.enablePrPreviews === true,
50
50
  setupType: 'headless'
51
51
  };
52
52
  }