@kirschbaum-development/sst-laravel 0.1.5 → 0.2.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
@@ -38,6 +38,21 @@ To get started quickly, you can use the `init` command:
38
38
  npx sst-laravel init
39
39
  ```
40
40
 
41
+ Running `init` now also prompts you to install the SST Laravel Initial Setup AI skill. Accepting the prompt will automatically detect whether `laravel/boost` ≥ 2.0 is available via Composer; if so, the skill is copied into `.ai/skills/sst-laravel-initial-setup/SKILL.md` and `php artisan boost:update` is executed. Otherwise, the command falls back to `npx skills add` with the bundled skill file.
42
+
43
+ ## AI Skill for Guided Setup
44
+
45
+ Projects that rely on AI copilots (like OpenCode) can import the `skills/laravel-initial-setup/SKILL.md` file from this package. The skill walks an assistant through:
46
+
47
+ - Auditing prerequisites (Node, AWS CLI, credentials, `sst-laravel` CLI)
48
+ - Bootstrapping your repo by running `npx sst-laravel init` before any config changes
49
+ - Choosing the right environment strategy (`RemoteEnvVault`, SST Secrets, or `.env` files)
50
+ - Inspecting/creating VPC resources through the AWS CLI
51
+ - Iteratively editing `sst.config.ts` until your Laravel service is deployable
52
+ - Producing clear summaries after every step plus follow-up tasks and cautions
53
+
54
+ Point your assistant at that file to get a prescriptive, secure onboarding workflow tailored for SST Laravel.
55
+
41
56
  ## Usage
42
57
 
43
58
  To start using, you only need to import the component in your `sst.config.ts` file:
@@ -137,6 +152,8 @@ There are multiple ways to configure environment variables. If you want SST Lara
137
152
 
138
153
  The below configuration would copy a file named `.env.$STAGE` (e.g. `.env.production`) into the deployment containers as your `.env` file.
139
154
 
155
+ ### Environment File
156
+
140
157
  ```js
141
158
  const app = new LaravelService('MyLaravelApp', {
142
159
  // ...
@@ -161,6 +178,105 @@ const app = new LaravelService('MyLaravelApp', {
161
178
  });
162
179
  ```
163
180
 
181
+ ### SST Secrets
182
+
183
+ You can also use SST Secrets to store your environment variables. This is a more secure way to store your environment variables.
184
+
185
+ ```js
186
+ const APP_KEY = new sst.Secret("APP_KEY");
187
+ const DB_PASSWORD = new sst.Secret("DB_PASSWORD");
188
+
189
+ const app = new LaravelService('MyLaravelApp', {
190
+ link: [APP_KEY, DB_PASSWORD],
191
+ });
192
+ ```
193
+
194
+ This will automatically inject the environment variables into the `.env` file of your Laravel application. Read more about SST Secrets [here](https://sst.dev/docs/component/secret/).
195
+
196
+ ### AWS Secrets Manager (RemoteEnvVault)
197
+
198
+ For a more robust environment variable management solution similar to Laravel Vapor, you can use the `RemoteEnvVault` component. This stores your environment variables in AWS Secrets Manager and provides CLI commands to push and pull secrets.
199
+
200
+ ```js
201
+ import { RemoteEnvVault, LaravelService } from "@kirschbaum-development/sst-laravel";
202
+
203
+ const env = new RemoteEnvVault("Env");
204
+ const app = new LaravelService('MyLaravelApp', {
205
+ // ...
206
+ config: {
207
+ environment: {
208
+ secrets: env,
209
+ }
210
+ }
211
+ });
212
+ ```
213
+
214
+ The secrets are stored in AWS Secrets Manager at the path `/{app-name}/{stage}/env`.
215
+
216
+ #### Large Environment Files
217
+
218
+ Large environment files that exceed AWS Secrets Manager's 64KB limit are automatically handled. The CLI will:
219
+ - Split large `.env` files into multiple chunks when pushing
220
+ - Automatically merge all chunks when pulling or deploying
221
+
222
+ This is completely transparent - you don't need to do anything special.
223
+
224
+ #### Pushing Secrets
225
+
226
+ To push your local `.env` file to AWS Secrets Manager:
227
+
228
+ ```bash
229
+ # Push .env.production to the production stage
230
+ npx sst-laravel env:push --stage production --input .env.production
231
+
232
+ # Push .env to staging (interactive)
233
+ npx sst-laravel env:push --stage staging
234
+ ```
235
+
236
+ #### Pulling Secrets
237
+
238
+ To pull secrets from AWS Secrets Manager to a local file:
239
+
240
+ ```bash
241
+ # Pull from production to .env.production (default)
242
+ npx sst-laravel env:pull --stage production
243
+
244
+ # Pull from staging to a custom file
245
+ npx sst-laravel env:pull --stage staging --output .env.local
246
+ ```
247
+
248
+ #### Deploying with Secrets
249
+
250
+ When using `RemoteEnvVault`, deploy using the `sst-laravel deploy` command which automatically fetches secrets before building:
251
+
252
+ ```bash
253
+ npx sst-laravel deploy --stage production
254
+ ```
255
+
256
+ #### Workflow Example
257
+
258
+ ```bash
259
+ # 1. Initial setup - push your environment file
260
+ npx sst-laravel env:push --stage production --input .env.production
261
+
262
+ # 2. Deploy (secrets are automatically fetched)
263
+ npx sst-laravel deploy --stage production
264
+
265
+ # 3. Update secrets later
266
+ npx sst-laravel env:pull --stage production # Creates .env.production
267
+ # Edit .env.production
268
+ npx sst-laravel env:push --stage production --input .env.production
269
+ npx sst-laravel deploy --stage production
270
+ ```
271
+
272
+ You can also use a custom path for the secrets:
273
+
274
+ ```js
275
+ const env = new RemoteEnvVault("Env", {
276
+ path: "/custom/path/env"
277
+ });
278
+ ```
279
+
164
280
  ### Resources
165
281
 
166
282
  In SST, you can [link resources](https://sst.dev/docs/linking). If you link resources to your Laravel component, SST Laravel will automatically inject and configure environment variables using sensible defaults for all the linked resources.
@@ -266,7 +382,7 @@ php artisan migrate --force
266
382
 
267
383
  ## Deploying
268
384
 
269
- To deploy your application, you can use the `sst deploy` command. You must be authenticated with AWS in your terminal session to deploy.
385
+ To deploy your application, you can use the `sst-laravel deploy` command. You must be authenticated with AWS in your terminal session to deploy.
270
386
 
271
387
  ```bash
272
388
  npx sst-laravel deploy --stage {stage}
@@ -274,6 +390,8 @@ npx sst-laravel deploy --stage sandbox
274
390
  npx sst-laravel deploy --stage production
275
391
  ```
276
392
 
393
+ > **Note:** If you're using `RemoteEnvVault` for secrets management, you should use `sst-laravel deploy` instead of `sst deploy` directly. This ensures secrets are fetched from AWS Secrets Manager before the Docker build.
394
+
277
395
  ## Accessing Containers
278
396
 
279
397
  Using the `sst-laravel` CLI tool, you can easily connect to your running ECS containers for debugging and troubleshooting.
@@ -316,10 +434,6 @@ To send logs to AWS CloudWatch, you need to set the `LOG_CHANNEL` environment va
316
434
 
317
435
  ## Troubleshooting
318
436
 
319
- **APP_URL**
320
-
321
- In case your specified environment file does not contain the `APP_URL` variable, SST Laravel will automatically add it to the environment file with the value of the `web.domain` property.
322
-
323
437
  **Load Balancer and trusted proxies**
324
438
 
325
439
  SST Laravel puts the container behind a load balancer, so you must configure your Laravel application to trust the load balancer's IP addresses. You can do this by configuring the trusted proxies in `bootstrap/app.php`. If you deployed your app and it's trying to load assets using HTTP instead of HTTPS, this is likely the issue.
@@ -327,12 +441,6 @@ SST Laravel puts the container behind a load balancer, so you must configure you
327
441
  ```php
328
442
  ->withMiddleware(function (Middleware $middleware) {
329
443
  $middleware->trustProxies(at: '*');
330
- $middleware->trustProxies(headers: Request::HEADER_X_FORWARDED_FOR |
331
- Request::HEADER_X_FORWARDED_HOST |
332
- Request::HEADER_X_FORWARDED_PORT |
333
- Request::HEADER_X_FORWARDED_PROTO |
334
- Request::HEADER_X_FORWARDED_AWS_ELB
335
- );
336
444
  })
337
445
  ```
338
446
 
@@ -345,12 +453,25 @@ In case you get the following error when running SST commands, run `npx sst-lara
345
453
  - node_modules/@kirschbaum-development/sst-laravel/laravel-sst.ts:6:26 Could not resolve "../../../.sst/platform/src/components/component.js"
346
454
  ```
347
455
 
456
+ **CD: AWS credentials are not configured**
457
+
458
+ If you are getting the following error when deploying (usually via CI/CD), the issue is usually that you have a `.env` or `.env.{stage}` that contains the `AWS_ACCESS_KEY_ID` and
459
+ `AWS_SECRET_ACCESS_KEY` keys. They should be removed from the environment file and you should be relying on the IAM role to give your app permissions to access AWS resources (which is more secure anyway).
460
+
461
+ ```
462
+ ✕ AWS credentials are not configured. Try configuring your profile in `~/.aws/config` and setting the `AWS_PROFILE` environment variable or specifying `providers.aws.profile` in your sst.config.ts
463
+ aws: failed to refresh cached credentials, no EC2 IMDS role found, operation error ec2imds: GetMetadata, failed to get API token, operation error ec2imds: getToken, http response error StatusCode: 400, request to EC2 IMDS failed
464
+ ```
465
+
466
+ **APP_URL**
467
+
468
+ In case your specified environment file does not contain the `APP_URL` variable, SST Laravel will automatically add it to the environment file with the value of the `web.domain` property.
469
+
348
470
  ***
349
471
 
350
472
  ### Roadmap
351
473
 
352
- * Support for [SST Secrets](https://sst.dev/docs/component/secret/);
353
- * Extend base Docker images;
474
+ * Ability to extend base Docker images;
354
475
  * Add support for Inertia SSR;
355
476
  * Add support for Octane with FrankedPHP;
356
477
  * Add support for Laravel Reverb;
@@ -363,7 +484,7 @@ If you discover any security related issues, please email security@kirschbaumdev
363
484
 
364
485
  ## Sponsorship
365
486
 
366
- Development of this package is developed and sponsored by Kirschbaum Development Group, a developer driven company focused on problem solving, team building, and community. Learn more [about us](https://kirschbaumdevelopment.com) or [join us](https://careers.kirschbaumdevelopment.com)!
487
+ Development of this package is sponsored by Kirschbaum Development Group, a developer driven company focused on problem solving, team building, and community. Learn more [about us](https://kirschbaumdevelopment.com) or [join us](https://careers.kirschbaumdevelopment.com)!
367
488
 
368
489
  ## License
369
490
 
package/dist/bin/cli.js CHANGED
@@ -8,6 +8,8 @@ import { sshCommand } from './commands/ssh.js';
8
8
  import { logsCommand } from './commands/logs.js';
9
9
  import { githubIamCommand } from './commands/github-iam.js';
10
10
  import { installCommand } from './commands/install.js';
11
+ import { envPullCommand } from './commands/env-pull.js';
12
+ import { envPushCommand } from './commands/env-push.js';
11
13
  import { getPackageRoot } from './utils/sst-config.js';
12
14
  const packageJsonPath = path.join(getPackageRoot(), 'package.json');
13
15
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
@@ -23,5 +25,7 @@ program.addCommand(sshCommand);
23
25
  program.addCommand(logsCommand);
24
26
  program.addCommand(githubIamCommand);
25
27
  program.addCommand(installCommand);
28
+ program.addCommand(envPullCommand);
29
+ program.addCommand(envPushCommand);
26
30
  program.parse();
27
31
  //# sourceMappingURL=cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,cAAc,CAAC,CAAC;AACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1E,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;AAEpC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,uCAAuC,CAAC;KACpD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AAEnC,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,cAAc,CAAC,CAAC;AACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1E,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;AAEpC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,uCAAuC,CAAC;KACpD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AAEnC,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -1,16 +1,62 @@
1
1
  import { Command } from 'commander';
2
2
  import { spawn } from 'child_process';
3
- import { validateDeployment } from '../utils/sst-config.js';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import { validateDeployment, findSstConfig, extractSstProjectName, extractSecretsConfig, getPackageRoot } from '../utils/sst-config.js';
6
+ import { pullSecrets, getSecretPath, getSecretInfo, toEnvFileContent } from '../utils/secrets-manager.js';
4
7
  export const deployCommand = new Command('deploy')
5
8
  .description('Deploy the application using SST')
6
9
  .requiredOption('-s, --stage <stage>', 'SST stage name')
7
10
  .action(async (options) => {
8
11
  try {
9
12
  validateDeployment(options.stage);
13
+ const configPath = findSstConfig();
14
+ if (configPath) {
15
+ const appName = extractSstProjectName(configPath);
16
+ const secretsConfig = extractSecretsConfig(configPath);
17
+ if (appName && secretsConfig) {
18
+ console.log('RemoteEnvVault detected, fetching secrets from AWS Secrets Manager...');
19
+ // Determine the secret path
20
+ const secretPath = secretsConfig.path || getSecretPath(appName, options.stage);
21
+ try {
22
+ // Get info about the secret structure first
23
+ const secretInfo = await getSecretInfo(secretPath);
24
+ if (!secretInfo) {
25
+ console.warn(`Warning: No secrets found at ${secretPath}`);
26
+ console.log('Continuing with deployment without secrets...');
27
+ }
28
+ else {
29
+ if (secretInfo.chunked) {
30
+ console.log(`Found ${secretInfo.totalKeys} variables in ${secretInfo.chunks} chunks`);
31
+ }
32
+ const secrets = await pullSecrets(secretPath);
33
+ if (secrets) {
34
+ // Ensure the .sst/laravel/deploy directory exists
35
+ const deployDir = path.join(process.cwd(), '.sst', 'laravel', 'deploy');
36
+ fs.mkdirSync(deployDir, { recursive: true });
37
+ // Write secrets to .env file
38
+ const envFilePath = path.join(deployDir, '.env');
39
+ const envContent = toEnvFileContent(secrets);
40
+ fs.writeFileSync(envFilePath, envContent + '\n');
41
+ fs.chmodSync(envFilePath, 0o755);
42
+ console.log(`Fetched ${Object.keys(secrets).length} variables from ${secretPath}`);
43
+ }
44
+ }
45
+ }
46
+ catch (error) {
47
+ console.error(`Warning: Failed to fetch secrets from ${secretPath}:`, error.message);
48
+ console.log('Continuing with deployment without secrets...');
49
+ }
50
+ }
51
+ }
10
52
  const deployProcess = spawn('npx', ['sst', 'deploy', '--stage', options.stage], {
11
53
  cwd: process.cwd(),
12
54
  stdio: 'inherit',
13
- shell: true
55
+ shell: true,
56
+ env: {
57
+ ...process.env,
58
+ SST_LARAVEL_PACKAGE_ROOT: getPackageRoot(),
59
+ },
14
60
  });
15
61
  await new Promise((resolve, reject) => {
16
62
  deployProcess.on('exit', (code) => {
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../../bin/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kCAAkC,CAAC;KAC/C,cAAc,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;KACvD,MAAM,CAAC,KAAK,EAAE,OAA0B,EAAE,EAAE;IAC3C,IAAI,CAAC;QACH,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAElC,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE;YAC9E,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;YAClB,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;QAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBAChC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,OAAO,EAAE,CAAC;gBACZ,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC,CAAC,CAAC;YACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAG,KAAe,CAAC,OAAO,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../../bin/commands/deploy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const envPullCommand: Command;
@@ -0,0 +1,81 @@
1
+ import { Command } from 'commander';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { select, confirm } from '@inquirer/prompts';
5
+ import { findSstConfig, extractSstProjectName } from '../utils/sst-config.js';
6
+ import { pullSecrets, getSecretPath, getSecretInfo, toEnvFileContent, listAvailableStages } from '../utils/secrets-manager.js';
7
+ export const envPullCommand = new Command('env:pull')
8
+ .description('Pull environment variables from AWS Secrets Manager')
9
+ .option('-s, --stage <stage>', 'SST stage name')
10
+ .option('-o, --output <file>', 'Output file path (default: .env.{stage})')
11
+ .option('-f, --force', 'Overwrite existing file without confirmation')
12
+ .action(async (options) => {
13
+ try {
14
+ const configPath = findSstConfig();
15
+ if (!configPath) {
16
+ console.error('Error: Could not find sst.config.ts or sst.config.js in current directory.');
17
+ process.exit(1);
18
+ }
19
+ const appName = extractSstProjectName(configPath);
20
+ if (!appName) {
21
+ console.error('Error: Could not extract app name from SST config.');
22
+ process.exit(1);
23
+ }
24
+ // Determine stage
25
+ let stage = options.stage;
26
+ if (!stage) {
27
+ const availableStages = await listAvailableStages(appName);
28
+ if (availableStages.length === 0) {
29
+ console.log('No stages found in AWS Secrets Manager for this app.');
30
+ console.log('Run this command again with the --stage <stage> flag to create the environment file.');
31
+ process.exit(1);
32
+ }
33
+ stage = await select({
34
+ message: 'Select the stage to pull from:',
35
+ choices: availableStages.map(s => ({ name: s, value: s })),
36
+ });
37
+ }
38
+ const secretPath = getSecretPath(appName, stage);
39
+ const outputFile = options.output || `.env.${stage}`;
40
+ const outputPath = path.resolve(process.cwd(), outputFile);
41
+ console.log(`Pulling environment variables from: ${secretPath}`);
42
+ // Get info about the secret structure
43
+ const secretInfo = await getSecretInfo(secretPath);
44
+ if (!secretInfo) {
45
+ console.error(`Error: No secrets found at ${secretPath}`);
46
+ console.log('');
47
+ console.log('To create secrets for this environment, run:');
48
+ console.log(` sst-laravel env:push --stage ${stage} --input .env.example`);
49
+ process.exit(1);
50
+ }
51
+ if (secretInfo.chunked) {
52
+ console.log(`Found ${secretInfo.totalKeys} variables in ${secretInfo.chunks} chunks`);
53
+ }
54
+ // Check if output file exists
55
+ if (fs.existsSync(outputPath) && !options.force) {
56
+ const shouldOverwrite = await confirm({
57
+ message: `File ${outputFile} already exists. Overwrite?`,
58
+ default: false,
59
+ });
60
+ if (!shouldOverwrite) {
61
+ console.log('Aborted.');
62
+ process.exit(0);
63
+ }
64
+ }
65
+ // Pull secrets from AWS
66
+ const secrets = await pullSecrets(secretPath);
67
+ if (!secrets) {
68
+ console.error(`Error: Failed to pull secrets from ${secretPath}`);
69
+ process.exit(1);
70
+ }
71
+ // Convert to .env format and write
72
+ const envContent = toEnvFileContent(secrets);
73
+ fs.writeFileSync(outputPath, envContent + '\n');
74
+ console.log(`Successfully pulled ${Object.keys(secrets).length} variables to ${outputFile}`);
75
+ }
76
+ catch (error) {
77
+ console.error('Error:', error.message);
78
+ process.exit(1);
79
+ }
80
+ });
81
+ //# sourceMappingURL=env-pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-pull.js","sourceRoot":"","sources":["../../../bin/commands/env-pull.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const envPushCommand: Command;
@@ -0,0 +1,93 @@
1
+ import { Command } from 'commander';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { select, confirm } from '@inquirer/prompts';
5
+ import { findSstConfig, extractSstProjectName } from '../utils/sst-config.js';
6
+ import { pushSecrets, getSecretInfo, getSecretPath, parseEnvFile, needsChunking, listAvailableStages } from '../utils/secrets-manager.js';
7
+ export const envPushCommand = new Command('env:push')
8
+ .description('Push environment variables to AWS Secrets Manager')
9
+ .option('-s, --stage <stage>', 'SST stage name')
10
+ .option('-i, --input <file>', 'Input file path (default: .env)')
11
+ .option('-f, --force', 'Push without confirmation')
12
+ .action(async (options) => {
13
+ try {
14
+ const configPath = findSstConfig();
15
+ if (!configPath) {
16
+ console.error('Error: Could not find sst.config.ts or sst.config.js in current directory.');
17
+ process.exit(1);
18
+ }
19
+ const appName = extractSstProjectName(configPath);
20
+ if (!appName) {
21
+ console.error('Error: Could not extract app name from SST config.');
22
+ process.exit(1);
23
+ }
24
+ // Determine stage
25
+ let stage = options.stage;
26
+ if (!stage) {
27
+ const availableStages = await listAvailableStages(appName);
28
+ if (availableStages.length === 0) {
29
+ console.log('No stages found in AWS Secrets Manager for this app.');
30
+ console.log('Run this command again with the --stage <stage> flag so the environment file can be created.');
31
+ process.exit(1);
32
+ }
33
+ stage = await select({
34
+ message: 'Select the stage to push to:',
35
+ choices: availableStages.map(s => ({ name: s, value: s })),
36
+ });
37
+ }
38
+ const inputFile = options.input || '.env';
39
+ const inputPath = path.resolve(process.cwd(), inputFile);
40
+ // Check if input file exists
41
+ if (!fs.existsSync(inputPath)) {
42
+ console.error(`Error: Input file ${inputFile} not found.`);
43
+ process.exit(1);
44
+ }
45
+ const secretPath = getSecretPath(appName, stage);
46
+ // Parse the .env file
47
+ const content = fs.readFileSync(inputPath, 'utf-8');
48
+ const vars = parseEnvFile(content);
49
+ const varCount = Object.keys(vars).length;
50
+ if (varCount === 0) {
51
+ console.error('Error: No variables found in the input file.');
52
+ process.exit(1);
53
+ }
54
+ console.log(`Found ${varCount} variables in ${inputFile}`);
55
+ console.log(`Target: ${secretPath}`);
56
+ // Check if chunking will be needed
57
+ if (needsChunking(vars)) {
58
+ console.log(`\nNote: Environment file exceeds AWS Secrets Manager limit and will be split into multiple chunks.`);
59
+ }
60
+ // Check if secrets already exist
61
+ const existingInfo = await getSecretInfo(secretPath);
62
+ if (existingInfo && !options.force) {
63
+ if (existingInfo.chunked) {
64
+ console.log(`\nWarning: ${existingInfo.totalKeys} variables already exist at this path (in ${existingInfo.chunks} chunks).`);
65
+ }
66
+ else {
67
+ console.log(`\nWarning: ${existingInfo.totalKeys} variables already exist at this path.`);
68
+ }
69
+ const shouldOverwrite = await confirm({
70
+ message: 'Do you want to overwrite the existing secrets?',
71
+ default: false,
72
+ });
73
+ if (!shouldOverwrite) {
74
+ console.log('Aborted.');
75
+ process.exit(0);
76
+ }
77
+ }
78
+ // Push secrets to AWS
79
+ console.log('\nPushing secrets to AWS Secrets Manager...');
80
+ const result = await pushSecrets(secretPath, vars);
81
+ if (result.chunked) {
82
+ console.log(`Successfully pushed ${varCount} variables to ${secretPath} (split into ${result.chunks} chunks)`);
83
+ }
84
+ else {
85
+ console.log(`Successfully pushed ${varCount} variables to ${secretPath}`);
86
+ }
87
+ }
88
+ catch (error) {
89
+ console.error('Error:', error.message);
90
+ process.exit(1);
91
+ }
92
+ });
93
+ //# sourceMappingURL=env-push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-push.js","sourceRoot":"","sources":["../../../bin/commands/env-push.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAE1I,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;KAClD,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;KAC/C,MAAM,CAAC,oBAAoB,EAAE,iCAAiC,CAAC;KAC/D,MAAM,CAAC,aAAa,EAAE,2BAA2B,CAAC;KAClD,MAAM,CAAC,KAAK,EAAE,OAA4D,EAAE,EAAE;IAC7E,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;QAEnC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,4EAA4E,CAAC,CAAC;YAC5F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;QAElD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,kBAAkB;QAClB,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,eAAe,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC;YAE3D,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;gBACpE,OAAO,CAAC,GAAG,CAAC,8FAA8F,CAAC,CAAC;gBAC5G,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,KAAK,GAAG,MAAM,MAAM,CAAC;gBACnB,OAAO,EAAE,8BAA8B;gBACvC,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;aAC3D,CAAC,CAAC;QACL,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;QAEzD,6BAA6B;QAC7B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,KAAK,CAAC,qBAAqB,SAAS,aAAa,CAAC,CAAC;YAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAEjD,sBAAsB;QACtB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QAE1C,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,SAAS,QAAQ,iBAAiB,SAAS,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,GAAG,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC;QAErC,mCAAmC;QACnC,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,oGAAoG,CAAC,CAAC;QACpH,CAAC;QAED,iCAAiC;QACjC,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;QAErD,IAAI,YAAY,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;gBACzB,OAAO,CAAC,GAAG,CAAC,cAAc,YAAY,CAAC,SAAS,6CAA6C,YAAY,CAAC,MAAM,WAAW,CAAC,CAAC;YAC/H,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,cAAc,YAAY,CAAC,SAAS,wCAAwC,CAAC,CAAC;YAC5F,CAAC;YAED,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC;gBACpC,OAAO,EAAE,gDAAgD;gBACzD,OAAO,EAAE,KAAK;aACf,CAAC,CAAC;YAEH,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAEnD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,CAAC,uBAAuB,QAAQ,iBAAiB,UAAU,gBAAgB,MAAM,CAAC,MAAM,UAAU,CAAC,CAAC;QACjH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,uBAAuB,QAAQ,iBAAiB,UAAU,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAG,KAAe,CAAC,OAAO,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}