@swovohq/fuel 0.2.0-alpha.2 → 0.2.0-alpha.20

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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -0
  3. package/dist/bin/fuel.js +145 -49
  4. package/dist/bin/fuel.js.map +1 -1
  5. package/dist/commands/config-verify.d.ts +4 -0
  6. package/dist/commands/config-verify.d.ts.map +1 -0
  7. package/dist/commands/config-verify.js +66 -0
  8. package/dist/commands/config-verify.js.map +1 -0
  9. package/dist/commands/create-app.d.ts +7 -2
  10. package/dist/commands/create-app.d.ts.map +1 -1
  11. package/dist/commands/create-app.js +58 -24
  12. package/dist/commands/create-app.js.map +1 -1
  13. package/dist/commands/infra-deploy.d.ts +5 -0
  14. package/dist/commands/infra-deploy.d.ts.map +1 -1
  15. package/dist/commands/infra-deploy.js +51 -5
  16. package/dist/commands/infra-deploy.js.map +1 -1
  17. package/dist/commands/infra-destroy.d.ts +2 -0
  18. package/dist/commands/infra-destroy.d.ts.map +1 -1
  19. package/dist/commands/infra-destroy.js +2 -1
  20. package/dist/commands/infra-destroy.js.map +1 -1
  21. package/dist/commands/infra-init.d.ts +1 -0
  22. package/dist/commands/infra-init.d.ts.map +1 -1
  23. package/dist/commands/infra-init.js +95 -10
  24. package/dist/commands/infra-init.js.map +1 -1
  25. package/dist/commands/migrate-init.d.ts.map +1 -1
  26. package/dist/commands/migrate-init.js +28 -12
  27. package/dist/commands/migrate-init.js.map +1 -1
  28. package/dist/engines/template-source.d.ts.map +1 -1
  29. package/dist/engines/template-source.js +35 -7
  30. package/dist/engines/template-source.js.map +1 -1
  31. package/dist/infra/config-loader.d.ts.map +1 -1
  32. package/dist/infra/config-loader.js +10 -2
  33. package/dist/infra/config-loader.js.map +1 -1
  34. package/dist/infra/credentials.d.ts +10 -2
  35. package/dist/infra/credentials.d.ts.map +1 -1
  36. package/dist/infra/credentials.js +44 -25
  37. package/dist/infra/credentials.js.map +1 -1
  38. package/dist/infra/git-client.d.ts +2 -0
  39. package/dist/infra/git-client.d.ts.map +1 -1
  40. package/dist/infra/git-client.js +56 -2
  41. package/dist/infra/git-client.js.map +1 -1
  42. package/dist/infra/github-client.d.ts +6 -0
  43. package/dist/infra/github-client.d.ts.map +1 -0
  44. package/dist/infra/github-client.js +36 -0
  45. package/dist/infra/github-client.js.map +1 -0
  46. package/dist/infra/orchestrator.d.ts +5 -2
  47. package/dist/infra/orchestrator.d.ts.map +1 -1
  48. package/dist/infra/orchestrator.js +86 -58
  49. package/dist/infra/orchestrator.js.map +1 -1
  50. package/dist/infra/port-utils.d.ts +4 -0
  51. package/dist/infra/port-utils.d.ts.map +1 -0
  52. package/dist/infra/port-utils.js +77 -0
  53. package/dist/infra/port-utils.js.map +1 -0
  54. package/dist/infra/preflight.d.ts +1 -1
  55. package/dist/infra/preflight.d.ts.map +1 -1
  56. package/dist/infra/preflight.js +4 -4
  57. package/dist/infra/preflight.js.map +1 -1
  58. package/dist/infra/s3-state-bucket.d.ts +1 -1
  59. package/dist/infra/s3-state-bucket.d.ts.map +1 -1
  60. package/dist/infra/s3-state-bucket.js +21 -26
  61. package/dist/infra/s3-state-bucket.js.map +1 -1
  62. package/dist/infra/template-engine.d.ts +1 -1
  63. package/dist/infra/template-engine.d.ts.map +1 -1
  64. package/dist/infra/template-engine.js +12 -17
  65. package/dist/infra/template-engine.js.map +1 -1
  66. package/dist/infra/tofu-runner.d.ts +5 -7
  67. package/dist/infra/tofu-runner.d.ts.map +1 -1
  68. package/dist/infra/tofu-runner.js +42 -80
  69. package/dist/infra/tofu-runner.js.map +1 -1
  70. package/package.json +6 -2
  71. package/schema/fuel.schema.json +158 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Swovo HQ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,231 @@
1
+ # @swovohq/fuel
2
+
3
+ CLI tool to scaffold apps from the Fuel monorepo and provision cloud infrastructure on AWS with ECS.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npx @swovohq/fuel <command>
9
+ ```
10
+
11
+ Or install globally:
12
+
13
+ ```bash
14
+ npm install -g @swovohq/fuel
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ### Scaffold a new app
20
+
21
+ ```bash
22
+ fuel create:app my-app
23
+ fuel create:app my-app -m billing,documents
24
+ ```
25
+
26
+ ### Full ECS provisioning (scaffold + cloud infra)
27
+
28
+ ```bash
29
+ fuel infra:init # Step 1: generate fuel.yml + .fuel-credentials
30
+ fuel create:app my-app -c fuel.yml # Step 2: scaffold + provision infrastructure
31
+ ```
32
+
33
+ ### Add infra to an existing project
34
+
35
+ ```bash
36
+ fuel infra:init # Step 1: generate fuel.yml + .fuel-credentials
37
+ fuel infra:deploy --config fuel.yml # Step 2: provision infrastructure
38
+ ```
39
+
40
+ ## Commands
41
+
42
+ | Command | Description |
43
+ |---|---|
44
+ | `fuel list:modules` | List available modules |
45
+ | `fuel config:verify <path>` | Validate `fuel.yml` and run preflight checks |
46
+ | `fuel create:app <name>` | Scaffold a new app from the monorepo template |
47
+ | `fuel infra:init` | Generate `fuel.yml` and `.fuel-credentials` interactively |
48
+ | `fuel infra:deploy` | Run or resume infrastructure provisioning |
49
+ | `fuel infra:destroy` | Tear down all provisioned cloud resources |
50
+ | `fuel migrate:init` | Generate and run database migrations |
51
+
52
+ ## `create:app`
53
+
54
+ Creates a new project from the Fuel monorepo template.
55
+
56
+ ```bash
57
+ fuel create:app <name> [options]
58
+ ```
59
+
60
+ **Options:**
61
+
62
+ | Flag | Description |
63
+ |---|---|
64
+ | `-m, --modules <list>` | Comma-separated modules to include (e.g. `billing,documents`) |
65
+ | `-o, --output <path>` | Output directory (defaults to `./<name>`) |
66
+ | `-s, --source <path>` | Path to a local monorepo root |
67
+ | `-c, --config <path>` | Path to `fuel.yml` — triggers ECS provisioning |
68
+ | `--dry-run` | Preview without writing files |
69
+ | `--skip-install` | Skip `yarn install` (also skips migrations) |
70
+ | `--skip-build` | Skip `yarn build` (also skips migrations) |
71
+ | `--skip-git` | Skip `git init` and initial commit |
72
+ | `--skip-migration-generation` | Skip database migration generation |
73
+ | `--keep-migrations` | Keep existing migration files from the template and skip migration generation |
74
+ | `--target-branch <branch>` | Only push to this branch in the target repo (CI use) |
75
+ | `--force-push` | Force push to the target repo (for CI-managed repos) |
76
+ | `--destroy-on-failure` | Destroy provisioned infrastructure if OpenTofu apply fails (default: preserve) |
77
+ | `--skip-branch-protection` | Skip applying branch protection rules after pushing (useful in CI when protections are already set) |
78
+ | `--github-token <token>` | GitHub token |
79
+ | `--github-username <user>` | GitHub username |
80
+ | `--github-organization <org>` | GitHub organization |
81
+ | `--aws-access-key-id <key>` | AWS access key ID |
82
+ | `--aws-secret-access-key <secret>` | AWS secret access key |
83
+ | `--aws-region <region>` | AWS region |
84
+
85
+ **What it does:**
86
+
87
+ 1. Copies the monorepo template, removing unselected modules
88
+ 2. Installs dependencies and builds the project
89
+ 3. Starts Docker, generates and runs database migrations
90
+ 4. Creates an initial git commit
91
+
92
+ With `--config fuel.yml`, it additionally:
93
+
94
+ 5. Creates a GitHub repository
95
+ 6. Provisions AWS infrastructure (ECR, OIDC, VPC, RDS, ECS)
96
+ 7. Configures GitHub Actions secrets and workflows
97
+ 8. Pushes code to trigger the first deployment
98
+ 9. Applies branch protection rules
99
+
100
+ ## `config:verify`
101
+
102
+ Validates `fuel.yml` syntax/schema and optionally runs preflight checks (OpenTofu, credentials). Failures are reported as warnings — the command never exits with an error.
103
+
104
+ ```bash
105
+ fuel config:verify <path> [options]
106
+ ```
107
+
108
+ | Flag | Description |
109
+ |---|---|
110
+ | `--config-only` | Only validate the config file. Skip preflight checks. |
111
+
112
+ **Examples:**
113
+
114
+ ```bash
115
+ fuel config:verify fuel.yml # Validate config + preflight checks
116
+ fuel config:verify fuel.yml --config-only # Validate config only
117
+ ```
118
+
119
+ ## `infra:init`
120
+
121
+ Interactively generates `fuel.yml` and `.fuel-credentials`.
122
+
123
+ ```bash
124
+ fuel infra:init [options]
125
+ ```
126
+
127
+ | Flag | Description |
128
+ |---|---|
129
+ | `-o, --output <path>` | Output path for `fuel.yml` |
130
+ | `--template` | Write a minimal template without prompts |
131
+
132
+ ## `infra:deploy`
133
+
134
+ Runs or resumes infrastructure provisioning on an existing project.
135
+
136
+ ```bash
137
+ fuel infra:deploy [options]
138
+ ```
139
+
140
+ | Flag | Description |
141
+ |---|---|
142
+ | `-p, --path <path>` | Path to project root |
143
+ | `-c, --config <path>` | Path to `fuel.yml` (generates `fuel.config.json`) |
144
+ | `--regenerate` | Regenerate the `/infra` folder from templates |
145
+ | `--plan-only` | Run OpenTofu plan without creating resources |
146
+ | `--infra-only` | Run only OpenTofu on an existing `/infra` folder (no config, no git push) |
147
+ | `--destroy-on-failure` | Destroy provisioned resources if OpenTofu apply fails (default: preserve) |
148
+ | `--skip-branch-protection` | Skip applying branch protection rules after pushing (useful in CI when protections are already set) |
149
+ | `--github-token <token>` | GitHub token |
150
+ | `--github-username <user>` | GitHub username |
151
+ | `--github-organization <org>` | GitHub organization |
152
+ | `--aws-access-key-id <key>` | AWS access key ID |
153
+ | `--aws-secret-access-key <secret>` | AWS secret access key |
154
+ | `--aws-region <region>` | AWS region |
155
+
156
+ ## `infra:destroy`
157
+
158
+ Destroys all AWS infrastructure managed by OpenTofu.
159
+
160
+ ```bash
161
+ fuel infra:destroy [options]
162
+ ```
163
+
164
+ | Flag | Description |
165
+ |---|---|
166
+ | `-p, --path <path>` | Path to project root |
167
+ | `--github-token <token>` | GitHub token |
168
+ | `--github-username <user>` | GitHub username |
169
+ | `--github-organization <org>` | GitHub organization |
170
+ | `--aws-access-key-id <key>` | AWS access key ID |
171
+ | `--aws-secret-access-key <secret>` | AWS secret access key |
172
+ | `--aws-region <region>` | AWS region |
173
+
174
+ ## `migrate:init`
175
+
176
+ Generates and runs database migrations for an existing project.
177
+
178
+ ```bash
179
+ fuel migrate:init [options]
180
+ ```
181
+
182
+ | Flag | Description |
183
+ |---|---|
184
+ | `-p, --path <path>` | Path to project root |
185
+ | `--skip-commit` | Skip the git commit after migrations |
186
+
187
+ ## Configuration Schema
188
+
189
+ `fuel.yml` includes a JSON Schema reference for editor autocompletion. When using VS Code with the [YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml), you get autocomplete, validation, and inline documentation automatically.
190
+
191
+ The schema is bundled at `node_modules/@swovohq/fuel/schema/fuel.schema.json`.
192
+
193
+ ## Credentials
194
+
195
+ Infrastructure commands require AWS and GitHub credentials. Each key is resolved independently in this order (sources are merged):
196
+
197
+ 1. **CLI flags** (e.g., `--github-token`) — highest priority
198
+ 2. **`.fuel-credentials`** JSON file (project root, then CWD fallback)
199
+ 3. **Environment variables**: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `GITHUB_TOKEN`, `GITHUB_USERNAME`, `GITHUB_ORGANIZATION`
200
+
201
+ Sources complement each other — e.g., AWS keys from env vars + `GITHUB_TOKEN` from `.fuel-credentials` = valid.
202
+
203
+ Run `fuel infra:init --credentials-only` to generate `.fuel-credentials` interactively.
204
+
205
+ ## Infrastructure Architecture
206
+
207
+ When using `--config fuel.yml`, the CLI provisions:
208
+
209
+ - **ECR** - Container registry for app images
210
+ - **ECS** (Fargate) - Container orchestration with ALB
211
+ - **RDS** - PostgreSQL database
212
+ - **VPC** - Networking with public/private subnets
213
+ - **IAM** - OIDC role for GitHub Actions deployments
214
+ - **S3** - OpenTofu state storage
215
+ - **Secrets Manager** - Infrastructure configuration
216
+ - **CloudWatch** - Monitoring and alarms
217
+ - **GuardDuty** - Threat detection
218
+
219
+ GitHub resources (repository, secrets, branch protections) are managed directly by the CLI via the GitHub API.
220
+
221
+ ## Requirements
222
+
223
+ - Node.js >= 18
224
+ - Docker (for migration generation)
225
+ - [OpenTofu](https://opentofu.org/) >= 1.5 (for infrastructure provisioning)
226
+ - AWS credentials with appropriate permissions
227
+ - GitHub personal access token with `repo` and `admin:org` scopes
228
+
229
+ ## License
230
+
231
+ MIT - see [LICENSE](./LICENSE)
package/dist/bin/fuel.js CHANGED
@@ -37,6 +37,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
37
37
  const commander_1 = require("commander");
38
38
  const fs = __importStar(require("fs-extra"));
39
39
  const path = __importStar(require("path"));
40
+ const config_verify_1 = require("../commands/config-verify");
40
41
  const create_app_1 = require("../commands/create-app");
41
42
  const infra_deploy_1 = require("../commands/infra-deploy");
42
43
  const infra_destroy_1 = require("../commands/infra-destroy");
@@ -45,6 +46,31 @@ const list_modules_1 = require("../commands/list-modules");
45
46
  const migrate_init_1 = require("../commands/migrate-init");
46
47
  const packageJson = require(path.join(__dirname, '..', '..', 'package.json'));
47
48
  const program = new commander_1.Command();
49
+ function addCredentialOptions(cmd) {
50
+ return cmd
51
+ .option('--github-token <token>', 'GitHub token. Falls back to .fuel-credentials or GITHUB_TOKEN env var.')
52
+ .option('--github-username <username>', 'GitHub username. Falls back to .fuel-credentials or GITHUB_USERNAME env var.')
53
+ .option('--github-organization <org>', 'GitHub organization. Falls back to .fuel-credentials or GITHUB_ORGANIZATION env var.')
54
+ .option('--aws-access-key-id <key>', 'AWS access key ID. Falls back to .fuel-credentials or AWS_ACCESS_KEY_ID env var.')
55
+ .option('--aws-secret-access-key <secret>', 'AWS secret access key. Falls back to .fuel-credentials or AWS_SECRET_ACCESS_KEY env var.')
56
+ .option('--aws-region <region>', 'AWS region. Falls back to .fuel-credentials or AWS_REGION env var.');
57
+ }
58
+ function buildOverrides(flags) {
59
+ const overrides = {};
60
+ if (flags.githubToken)
61
+ overrides.GITHUB_TOKEN = flags.githubToken;
62
+ if (flags.githubUsername)
63
+ overrides.GITHUB_USERNAME = flags.githubUsername;
64
+ if (flags.githubOrganization)
65
+ overrides.GITHUB_ORGANIZATION = flags.githubOrganization;
66
+ if (flags.awsAccessKeyId)
67
+ overrides.AWS_ACCESS_KEY_ID = flags.awsAccessKeyId;
68
+ if (flags.awsSecretAccessKey)
69
+ overrides.AWS_SECRET_ACCESS_KEY = flags.awsSecretAccessKey;
70
+ if (flags.awsRegion)
71
+ overrides.AWS_REGION = flags.awsRegion;
72
+ return Object.keys(overrides).length > 0 ? overrides : undefined;
73
+ }
48
74
  program
49
75
  .name('fuel')
50
76
  .description('Fuel CLI – scaffold apps from the fuel monorepo and provision cloud infrastructure.')
@@ -56,12 +82,12 @@ Workflows:
56
82
  fuel create:app <name> [-m modules]
57
83
 
58
84
  2. Full ECS provisioning (scaffold + cloud infra):
59
- fuel infra:init Step 1: generate config.json + .fuel-credentials
60
- fuel create:app <name> -d ecs -c config.json Step 2: scaffold code and provision infrastructure
85
+ fuel infra:init Step 1: generate fuel.yml + .fuel-credentials
86
+ fuel create:app <name> -c fuel.yml Step 2: scaffold code and provision infrastructure
61
87
 
62
- 3. Add infra to an existing project (created without -d ecs):
63
- fuel infra:init Step 1: generate config.json + .fuel-credentials
64
- fuel infra:deploy --config config.json Step 2: generate fuel.config.json + provision infrastructure
88
+ 3. Add infra to an existing project (created without -c):
89
+ fuel infra:init Step 1: generate fuel.yml + .fuel-credentials
90
+ fuel infra:deploy --config fuel.yml Step 2: generate fuel.config.json + provision infrastructure
65
91
 
66
92
  4. Resume / retry infra (after a failure in step 2 or 3):
67
93
  fuel infra:deploy [--path <project>] Re-run infrastructure provisioning without re-scaffolding
@@ -85,9 +111,9 @@ Quick examples:
85
111
  fuel create:app my-app Scaffold base app
86
112
  fuel create:app my-app -m billing,documents Scaffold with modules
87
113
  fuel infra:init Interactive infra config setup
88
- fuel create:app my-app -d ecs -c config.json Full ECS provisioning
114
+ fuel create:app my-app -c fuel.yml Full ECS provisioning
89
115
  fuel infra:deploy Resume failed infra provisioning
90
- fuel infra:deploy --config config.json Add infra to existing project
116
+ fuel infra:deploy --config fuel.yml Add infra to existing project
91
117
  fuel infra:deploy --path ./my-app --regenerate Regenerate /infra and re-deploy
92
118
  fuel migrate:init Re-run migration generation
93
119
  fuel infra:destroy Tear down all infrastructure`);
@@ -103,6 +129,29 @@ Examples:
103
129
  .action(async (opts) => {
104
130
  try {
105
131
  await (0, list_modules_1.listModules)(opts.source, opts.githubToken);
132
+ process.exit(0);
133
+ }
134
+ catch (error) {
135
+ console.error(`\n Error: ${error instanceof Error ? error.message : String(error)}\n`);
136
+ process.exit(1);
137
+ }
138
+ });
139
+ program
140
+ .command('config:verify <configPath>')
141
+ .description('Validate a fuel.yml config file and optionally run preflight checks.\n\n' +
142
+ ' By default, validates the config syntax/schema AND runs preflight checks\n' +
143
+ ' (OpenTofu version, credentials). Preflight failures are reported as\n' +
144
+ ' warnings — the command never exits with an error.\n\n' +
145
+ ' Use --config-only to skip preflight checks and only validate the file.')
146
+ .option('--config-only', 'Only validate the config file syntax and schema. Skip preflight checks (OpenTofu, credentials).')
147
+ .addHelpText('after', `
148
+ Examples:
149
+ fuel config:verify fuel.yml Validate config + run preflight checks
150
+ fuel config:verify fuel.yml --config-only Validate config syntax/schema only`)
151
+ .action(async (configPath, opts) => {
152
+ try {
153
+ await (0, config_verify_1.configVerify)(configPath, opts);
154
+ process.exit(0);
106
155
  }
107
156
  catch (error) {
108
157
  console.error(`\n Error: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -117,14 +166,14 @@ program
117
166
  ' Copies the monorepo template, removes unselected modules, installs\n' +
118
167
  ' dependencies, builds, starts Docker, generates database migrations,\n' +
119
168
  ' and creates an initial git commit.\n\n' +
120
- ' ECS mode (-d ecs):\n' +
169
+ ' ECS mode (-c fuel.yml):\n' +
121
170
  ' Performs all scaffolding steps above, then provisions cloud infrastructure:\n' +
122
171
  ' writes fuel.config.json, generates /infra (OpenTofu) and .github/workflows/,\n' +
123
- ' creates the S3 state bucket, writes secrets to AWS Secrets Manager, runs\n' +
124
- ' tofu apply in 3 passes (ECR + GitHub repo, GitHub Actions, full ECS),\n' +
125
- ' and prints ALB/ECR/GitHub URLs.\n' +
126
- ' Requires --config. Run "fuel infra:init" first to generate config.json.')
172
+ ' creates a GitHub repo, provisions all AWS infrastructure via OpenTofu,\n' +
173
+ ' configures GitHub Actions secrets, pushes code, and applies branch protections.\n' +
174
+ ' Run "fuel infra:init" first to generate fuel.yml.')
127
175
  .option('-m, --modules <list>', 'Comma-separated modules to include (e.g. billing,documents). Omit for base only.', '')
176
+ .option('-a, --all-modules', 'Include all available modules. Cannot be combined with -m.')
128
177
  .option('-o, --output <path>', 'Output directory. Defaults to ./<appName> in the current directory.')
129
178
  .option('-s, --source <path>', 'Path to the monorepo root containing fuel.modules.json. If omitted, walks up from the current directory.')
130
179
  .option('--dry-run', 'Preview what would be done without writing any files or provisioning resources.')
@@ -132,9 +181,18 @@ program
132
181
  .option('--skip-git', 'Skip git init and the initial commit.')
133
182
  .option('--skip-build', 'Skip yarn build. Also implicitly skips migration generation (requires build).')
134
183
  .option('--skip-migration-generation', 'Skip database migration generation and execution. Docker will not be started.')
135
- .option('-d, --deployment <type>', 'Deployment target. Only "ecs" is supported. Cannot be combined with --skip-install, --skip-build, or --skip-migration-generation.')
136
- .option('-c, --config <path>', 'Path to config.json for ECS provisioning (required when -d ecs is set). Generate one with "fuel infra:init".')
137
- .option('--github-token <token>', 'GitHub token for downloading template assets from private repositories. Falls back to GITHUB_TOKEN env var.')
184
+ .option('--keep-migrations', 'Keep existing migration files from the template and skip migration generation. Docker will not be started.')
185
+ .option('-c, --config <path>', 'Path to fuel.yml for ECS provisioning. When provided, infrastructure is provisioned after scaffolding. Generate one with "fuel infra:init". Cannot be combined with --skip-install, --skip-build, or --skip-migration-generation (unless --keep-migrations is also set).')
186
+ .option('--target-branch <branch>', 'Only push to this branch in the target repo. Useful in CI to deploy a specific environment.')
187
+ .option('--force-push', 'Force push to the target repo. Use when each run scaffolds a fresh project and the target repo already has commits.')
188
+ .option('--destroy-on-failure', 'Destroy provisioned infrastructure if OpenTofu apply fails. Default: false (resources are preserved for re-run).')
189
+ .option('--skip-branch-protection', 'Skip applying branch protection rules after pushing.')
190
+ .option('--github-token <token>', 'GitHub token. Falls back to .fuel-credentials or GITHUB_TOKEN env var.')
191
+ .option('--github-username <username>', 'GitHub username. Falls back to .fuel-credentials or GITHUB_USERNAME env var.')
192
+ .option('--github-organization <org>', 'GitHub organization. Falls back to .fuel-credentials or GITHUB_ORGANIZATION env var.')
193
+ .option('--aws-access-key-id <key>', 'AWS access key ID. Falls back to .fuel-credentials or AWS_ACCESS_KEY_ID env var.')
194
+ .option('--aws-secret-access-key <secret>', 'AWS secret access key. Falls back to .fuel-credentials or AWS_SECRET_ACCESS_KEY env var.')
195
+ .option('--aws-region <region>', 'AWS region. Falls back to .fuel-credentials or AWS_REGION env var.')
138
196
  .addHelpText('after', `
139
197
  Scaffolding examples:
140
198
  fuel create:app my-app Base app, no optional modules
@@ -145,8 +203,8 @@ Scaffolding examples:
145
203
  fuel create:app my-app --skip-install --skip-git Scaffold only, no install/git/migrations
146
204
 
147
205
  ECS provisioning (full workflow):
148
- fuel infra:init Step 1: generate config.json + .fuel-credentials
149
- fuel create:app my-app -d ecs -c config.json Step 2: scaffold + provision cloud infrastructure
206
+ fuel infra:init Step 1: generate fuel.yml + .fuel-credentials
207
+ fuel create:app my-app -c fuel.yml Step 2: scaffold + provision cloud infrastructure
150
208
 
151
209
  Recovery commands:
152
210
  cd my-app && fuel infra:deploy Resume failed infra provisioning
@@ -163,18 +221,27 @@ Recovery commands:
163
221
  throw new Error('Missing app name. Provide it as a positional argument or use --config to read it from a config file.');
164
222
  }
165
223
  const configPath = path.resolve(opts.config);
166
- const raw = await fs.readJson(configPath);
224
+ const content = await fs.readFile(configPath, 'utf-8');
225
+ const YAML = require('yaml');
226
+ const raw = configPath.endsWith('.json')
227
+ ? JSON.parse(content)
228
+ : YAML.parse(content);
167
229
  if (!raw.name || typeof raw.name !== 'string') {
168
230
  throw new Error(`Config file "${opts.config}" does not contain a valid "name" field.`);
169
231
  }
170
232
  resolvedAppName = raw.name;
171
233
  }
172
- const modules = opts.modules
173
- ? opts.modules
174
- .split(',')
175
- .map((m) => m.trim())
176
- .filter(Boolean)
177
- : [];
234
+ if (opts.allModules && opts.modules) {
235
+ throw new Error('Cannot use --all-modules and --modules together. Use one or the other.');
236
+ }
237
+ const modules = opts.allModules
238
+ ? '__all__'
239
+ : opts.modules
240
+ ? opts.modules
241
+ .split(',')
242
+ .map((m) => m.trim())
243
+ .filter(Boolean)
244
+ : [];
178
245
  await (0, create_app_1.createApp)(resolvedAppName, {
179
246
  modules,
180
247
  output: opts.output,
@@ -184,10 +251,16 @@ Recovery commands:
184
251
  skipGit: opts.skipGit,
185
252
  skipBuild: opts.skipBuild,
186
253
  skipMigrationGeneration: opts.skipMigrationGeneration,
187
- deployment: opts.deployment,
254
+ keepMigrations: opts.keepMigrations,
255
+ targetBranch: opts.targetBranch,
256
+ forcePush: opts.forcePush,
257
+ destroyOnFailure: opts.destroyOnFailure,
258
+ skipBranchProtection: opts.skipBranchProtection,
188
259
  config: opts.config,
189
- githubToken: opts.githubToken
260
+ githubToken: opts.githubToken,
261
+ credentialOverrides: buildOverrides(opts)
190
262
  });
263
+ process.exit(0);
191
264
  }
192
265
  catch (error) {
193
266
  console.error(`\n Error: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -196,39 +269,41 @@ Recovery commands:
196
269
  });
197
270
  program
198
271
  .command('infra:init')
199
- .description('Generate config.json and .fuel-credentials for ECS infrastructure provisioning.\n\n' +
272
+ .description('Generate fuel.yml and .fuel-credentials for ECS infrastructure provisioning.\n\n' +
200
273
  ' Interactive mode (default):\n' +
201
274
  ' Prompts for AWS credentials, GitHub token, app configuration (API/Web,\n' +
202
275
  ' PostgreSQL, Redis, CPU/memory), environments (dev/staging/production),\n' +
203
276
  ' domains, and environment variables. Validates credentials against AWS\n' +
204
- ' and GitHub APIs. Writes config.json and .fuel-credentials (auto-added\n' +
277
+ ' and GitHub APIs. Writes fuel.yml and .fuel-credentials (auto-added\n' +
205
278
  ' to .gitignore).\n\n' +
206
279
  ' Template mode (--template):\n' +
207
- ' Writes a minimal config.json template for manual editing. Does not\n' +
280
+ ' Writes a minimal fuel.yml template for manual editing. Does not\n' +
208
281
  ' prompt for credentials or validate anything.')
209
- .option('-o, --output <path>', 'Output path for config.json. Defaults to ./config.json in the current directory.')
210
- .option('--template', 'Write a minimal config.json template without prompts or credential validation.')
282
+ .option('-o, --output <path>', 'Output path for fuel.yml. Defaults to ./fuel.yml in the current directory.')
283
+ .option('--template', 'Write a minimal fuel.yml template without prompts or credential validation.')
284
+ .option('--credentials-only', 'Only generate .fuel-credentials (prompt for AWS + GitHub credentials, verify, and write). Skips fuel.yml generation.')
211
285
  .addHelpText('after', `
212
286
  Generated files:
213
- config.json Infrastructure configuration (apps, environments, domains, resources)
287
+ fuel.yml Infrastructure configuration (apps, environments, domains, resources)
214
288
  .fuel-credentials AWS + GitHub credentials (auto-added to .gitignore, never commit this file)
215
289
 
216
290
  Workflows using this command:
217
291
  New project (scaffold + infra in one step):
218
- 1. fuel infra:init Generate config.json + .fuel-credentials
219
- 2. fuel create:app <name> -d ecs -c config.json Scaffold + provision infrastructure
292
+ 1. fuel infra:init Generate fuel.yml + .fuel-credentials
293
+ 2. fuel create:app <name> -c fuel.yml Scaffold + provision infrastructure
220
294
 
221
295
  Existing project (add infra later):
222
- 1. fuel infra:init Generate config.json + .fuel-credentials
223
- 2. fuel infra:deploy -c config.json Generate fuel.config.json + provision infrastructure
296
+ 1. fuel infra:init Generate fuel.yml + .fuel-credentials
297
+ 2. fuel infra:deploy -c fuel.yml Generate fuel.config.json + provision infrastructure
224
298
 
225
299
  Examples:
226
300
  fuel infra:init Interactive setup
227
301
  fuel infra:init --template Write base template (no prompts, no credentials)
228
- fuel infra:init -o ./infra/config.json Write config.json to a custom path`)
302
+ fuel infra:init -o ./infra/fuel.yml Write fuel.yml to a custom path`)
229
303
  .action(async (opts) => {
230
304
  try {
231
305
  await (0, infra_init_1.infraInit)(opts);
306
+ process.exit(0);
232
307
  }
233
308
  catch (error) {
234
309
  console.error(`\n Error: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -238,26 +313,35 @@ Examples:
238
313
  program
239
314
  .command('infra:deploy')
240
315
  .description('Run or resume infrastructure provisioning on an already-generated project.\n\n' +
241
- ' Use this command after "fuel create:app -d ecs" fails during the infrastructure\n' +
316
+ ' Use this command after "fuel create:app -c fuel.yml" fails during the infrastructure\n' +
242
317
  ' phase (e.g. missing AWS credentials, network timeout, OpenTofu errors).\n' +
243
318
  ' It re-runs only the infrastructure steps without re-scaffolding the project.\n\n' +
244
319
  ' You can also use this to add infrastructure to a project that was originally\n' +
245
- ' created without -d ecs, by passing --config <path> to generate fuel.config.json.\n\n' +
320
+ ' created without -c, by passing --config <path> to generate fuel.config.json.\n\n' +
246
321
  ' Prerequisites:\n' +
247
322
  ' - The project must contain a fuel.config.json, or --config must be provided\n' +
248
323
  ' - Credentials must be available via env vars or .fuel-credentials file\n\n' +
249
324
  ' On failure, partial cloud resources are preserved (no rollback), so you\n' +
250
325
  ' can fix the issue and re-run this command as many times as needed.')
251
326
  .option('-p, --path <path>', 'Path to the project root. Defaults to the current directory.')
252
- .option('-c, --config <path>', 'Path to config.json. Generates fuel.config.json in the project and provisions infrastructure. Use this to add infra to a project created without -d ecs.')
253
- .option('--regenerate', 'Delete and regenerate the /infra folder from templates before deploying. Use this if you changed config.json and need fresh OpenTofu files.')
327
+ .option('-c, --config <path>', 'Path to fuel.yml. Generates fuel.config.json in the project and provisions infrastructure. Use this to add infra to a project created without -c.')
328
+ .option('--regenerate', 'Delete and regenerate the /infra folder from templates before deploying. Use this if you changed fuel.yml and need fresh OpenTofu files.')
254
329
  .option('--plan-only', 'Run OpenTofu plan instead of apply. Shows what would change without creating any resources.')
330
+ .option('--infra-only', 'Run only OpenTofu init + apply on an existing /infra folder. Skips config validation, secret upload, GitHub repo creation, and code push. Reads all variables from default.tfvars.')
331
+ .option('--destroy-on-failure', 'Destroy all provisioned resources if OpenTofu apply fails. Default: false (resources are preserved for re-run).')
332
+ .option('--skip-branch-protection', 'Skip applying branch protection rules after pushing. Useful in CI when protections are already set.')
333
+ .option('--github-token <token>', 'GitHub token. Falls back to .fuel-credentials or GITHUB_TOKEN env var.')
334
+ .option('--github-username <username>', 'GitHub username. Falls back to .fuel-credentials or GITHUB_USERNAME env var.')
335
+ .option('--github-organization <org>', 'GitHub organization. Falls back to .fuel-credentials or GITHUB_ORGANIZATION env var.')
336
+ .option('--aws-access-key-id <key>', 'AWS access key ID. Falls back to .fuel-credentials or AWS_ACCESS_KEY_ID env var.')
337
+ .option('--aws-secret-access-key <secret>', 'AWS secret access key. Falls back to .fuel-credentials or AWS_SECRET_ACCESS_KEY env var.')
338
+ .option('--aws-region <region>', 'AWS region. Falls back to .fuel-credentials or AWS_REGION env var.')
255
339
  .addHelpText('after', `
256
340
  Credential resolution (in order of priority):
257
- 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION,
258
- GITHUB_TOKEN, GITHUB_USERNAME, GITHUB_ORGANIZATION)
341
+ 1. CLI flags (--github-token, --aws-access-key-id, etc.)
259
342
  2. .fuel-credentials file in the project root (--path)
260
343
  3. .fuel-credentials file in the current working directory
344
+ 4. Environment variables (AWS_ACCESS_KEY_ID, GITHUB_TOKEN, etc.)
261
345
 
262
346
  What this command does:
263
347
  1. If --config is provided, writes fuel.config.json to the project root
@@ -265,14 +349,14 @@ What this command does:
265
349
  3. Generates the /infra folder if missing (or if --regenerate / --config is passed)
266
350
  4. Creates the S3 state bucket if it does not exist (with a unique suffix)
267
351
  5. Writes infrastructure secrets to AWS Secrets Manager
268
- 6. Runs OpenTofu in 3 passes: ECR + GitHub repo, GitHub Actions, full ECS
269
- 7. Wires the git remote and prints ALB/ECR/GitHub URLs
352
+ 6. Creates a GitHub repo and runs OpenTofu apply (all AWS resources)
353
+ 7. Sets GitHub Actions secrets, pushes code, applies branch protections
270
354
 
271
355
  Examples:
272
356
  cd my-app && fuel infra:deploy Resume from inside the project
273
357
  fuel infra:deploy -p ./my-app Resume from outside the project
274
- fuel infra:deploy -c config.json Add infra to an existing project
275
- fuel infra:deploy -c config.json --plan-only Preview what infra would be created
358
+ fuel infra:deploy -c fuel.yml Add infra to an existing project
359
+ fuel infra:deploy -c fuel.yml --plan-only Preview what infra would be created
276
360
  fuel infra:deploy --regenerate Regenerate /infra folder, then deploy
277
361
  fuel infra:deploy --plan-only Preview changes without deploying
278
362
 
@@ -280,7 +364,11 @@ To tear down provisioned resources:
280
364
  fuel infra:destroy Destroy all infrastructure`)
281
365
  .action(async (opts) => {
282
366
  try {
283
- await (0, infra_deploy_1.infraDeploy)(opts);
367
+ await (0, infra_deploy_1.infraDeploy)({
368
+ ...opts,
369
+ credentialOverrides: buildOverrides(opts)
370
+ });
371
+ process.exit(0);
284
372
  }
285
373
  catch (error) {
286
374
  console.error(`\n Error: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -319,6 +407,7 @@ Examples:
319
407
  .action(async (opts) => {
320
408
  try {
321
409
  await (0, migrate_init_1.migrateInit)(opts);
410
+ process.exit(0);
322
411
  }
323
412
  catch (error) {
324
413
  console.error(`\n Error: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -334,6 +423,12 @@ program
334
423
  ' - The project must contain fuel.config.json and an /infra folder\n' +
335
424
  ' - Credentials must be available via env vars or .fuel-credentials file')
336
425
  .option('-p, --path <path>', 'Path to the project root. Defaults to the current directory.')
426
+ .option('--github-token <token>', 'GitHub token. Falls back to .fuel-credentials or GITHUB_TOKEN env var.')
427
+ .option('--github-username <username>', 'GitHub username. Falls back to .fuel-credentials or GITHUB_USERNAME env var.')
428
+ .option('--github-organization <org>', 'GitHub organization. Falls back to .fuel-credentials or GITHUB_ORGANIZATION env var.')
429
+ .option('--aws-access-key-id <key>', 'AWS access key ID. Falls back to .fuel-credentials or AWS_ACCESS_KEY_ID env var.')
430
+ .option('--aws-secret-access-key <secret>', 'AWS secret access key. Falls back to .fuel-credentials or AWS_SECRET_ACCESS_KEY env var.')
431
+ .option('--aws-region <region>', 'AWS region. Falls back to .fuel-credentials or AWS_REGION env var.')
337
432
  .addHelpText('after', `
338
433
  What this command does:
339
434
  1. Validates fuel.config.json, OpenTofu version, and credentials
@@ -348,7 +443,8 @@ To re-provision after destroying:
348
443
  fuel infra:deploy Re-create all infrastructure`)
349
444
  .action(async (opts) => {
350
445
  try {
351
- await (0, infra_destroy_1.infraDestroy)(opts);
446
+ await (0, infra_destroy_1.infraDestroy)({ ...opts, credentialOverrides: buildOverrides(opts) });
447
+ process.exit(0);
352
448
  }
353
449
  catch (error) {
354
450
  console.error(`\n Error: ${error instanceof Error ? error.message : String(error)}\n`);