Skip to content

Commit fc66f06

Browse files
drernieclaude
andauthored
fix(config): pass complete config via environment variables to CDK (#307)
* cleanup a07 specs Co-Authored-By: Claude <noreply@anthropic.com> * chore: lint markdown tables * docs(spec): add A07 analysis and implementation plan Comprehensive analysis of A07 config validation issue: - 01: Original library-config review - 02: Original library-config spec - 03: Postmortem of script breakage - 04: Analysis of the right fix approach - 05: Complete implementation plan Key findings: - A07 added validation not in spec (broke scripts) - Root cause: deploy.ts doesn't pass config via env vars - Solution: Pass complete config via environment variables - Architecture: config always complete, parameters are optional overrides Implementation plan includes: - Detailed code changes (add ~15 env vars to deploy.ts) - Testing strategy - Rollout phases - Why alternatives were rejected - Success criteria and risk assessment 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude <noreply@anthropic.com> * fix(config): pass complete config via environment variables to CDK Fixes A07 config validation break in NPM script deployments. Problem: - A07 added validation that broke `npm run deploy:dev` - deploy.ts wasn't passing config fields to benchling-webhook.ts - Stack received empty config, validation failed Solution: - Pass complete config via environment variables from deploy.ts - benchling-webhook.ts already reads these env vars - No code changes needed in benchling-webhook.ts Added environment variables: - QUILT_CATALOG, QUILT_DATABASE, QUEUE_URL (required) - ICEBERG_DATABASE, ICEBERG_WORKGROUP (optional) - ATHENA_USER_WORKGROUP, ATHENA_RESULTS_BUCKET (optional) - QUILT_USER_BUCKET, PKG_PREFIX, PKG_KEY (package config) - BENCHLING_TENANT, BENCHLING_CLIENT_ID, BENCHLING_APP_DEFINITION_ID - LOG_LEVEL, IMAGE_TAG, ECR_REPOSITORY_NAME (optional) Benefits: - Clean architecture: config is always complete - Preserves A07 validation benefits for library users - Parameters remain optional overrides - No magic bypasses or workarounds See: spec/a07-library-config/05-implementation-plan.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: sync versions to 0.9.7 * chore: update CHANGELOG for v0.9.7 Documents A07 config validation fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: complete pending TODOs and improve test coverage This commit addresses all pending TODO items in the codebase: **bin/benchling-webhook.ts:** - Remove legacy `legacyConfigToProfileConfig()` function marked for Phase 4 removal - Inline Config-to-ProfileConfig conversion in `createStack()` for clarity - Update documentation to reflect current implementation state - Remove outdated "Phase 4" TODO comments **bin/commands/config-profiles.ts:** - Update file header documentation to confirm v0.7.0+ migration is complete - Remove obsolete warning comments about refactoring needed - File already correctly uses ProfileConfig, XDGConfig, and readProfile API **test/multi-environment-fargate-service.test.ts:** - Re-enable LOG_LEVEL environment variable test (was test.skip) - Fix assertion to match actual implementation (LOG_LEVEL not LogLevel) - Add value assertion to verify LOG_LEVEL="DEBUG" - Test now passes and verifies correct environment variable configuration **test/integration/stack-resource-discovery.test.ts:** - Replace TODO comments with architectural documentation - Document AWS SDK v3 dynamic import issues with Jest - Explain why getStackResources() wrapper is preferred over direct SDK calls - Clarify how test coverage is maintained through wrapper functions All tests pass (357 Python tests + TypeScript tests). No TODOs/FIXMEs remaining in modified files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs(spec): document Docker double-wrapping observation for CMD syntax bug - Add observation that test:minimal passes despite buggy Dockerfile - Document hypothesis: Docker double-wraps shell form CMD locally - Explain why ECS fails but local tests pass - Note testing limitation: cannot reproduce locally with realistic test - Fix is still necessary but won't be caught by local tests * fix(docker): quote create_app() to prevent shell parsing error The unquoted parentheses in 'src.app:create_app()' cause shell syntax errors in ECS when the shell interprets them as subshell operators. Quoting the factory function as 'src.app:create_app()' prevents this while preserving PORT variable expansion for multi-stack support. Note: Local tests pass despite the bug due to Docker's double-wrapping behavior with shell form CMD. The bug only manifests in ECS production. Fixes: A07-07 Docker CMD Shell Syntax Error Related: spec/a07-library-config/07-docker-cmd-shell-syntax-fix.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(test): always rebuild Docker image in test:minimal Changes: - Replace conditional build check with unconditional build - Use `make -C docker docker-build-local` for consistency with build infrastructure - Ensures test:minimal always runs against latest code changes Rationale: - Previous approach only built if image didn't exist, causing stale image issues - Using Makefile target ensures consistency with other build processes - Mirrors CI behavior where fresh builds are always performed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(config): correct stack name resolution in legacy createStack function Fix TypeScript compilation errors caused by accessing non-existent properties on the legacy Config type (config.profile and config.stackName). Changes: - Use hardcoded "default" profile for legacy compatibility - Read custom stack name from profileConfig.deployment?.stackName - Maintains backwards compatibility with existing deployments This ensures the legacy code path works correctly while supporting the multi-stack feature when using the XDG profile system. Related: spec/a07-library-config/06-multi-stack-support.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: catch PENDING:REVIEW * fix(deploy): detect and auto-destroy REVIEW_IN_PROGRESS stacks REVIEW_IN_PROGRESS is an unrecoverable CloudFormation state that occurs when a change set fails validation during creation. The stack exists but cannot be updated or deployed. Changes: - Add automatic detection of REVIEW_IN_PROGRESS state after stack check - Prompt user to destroy and recreate (only viable option) - Auto-destroy with proper CDK environment variables - Continue with fresh deployment after successful destroy - Extract getCdkAppPath() and buildCdkEnv() helpers to eliminate 100+ lines of duplication Fixes issue where deploy command would fail with: "Failed to create ChangeSet: AWS::EarlyValidation::ResourceExistenceCheck failed" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(cli): add clean command to remove configuration profiles Add new 'clean' command that allows users to safely remove configuration profiles from the system. The command: - Removes profile configuration and deployment tracking - Shows warnings for profiles with active deployments - Prevents accidental deletion with confirmation prompt - Supports --yes flag for non-interactive use - Protects the default profile from deletion (enforced by XDGBase) Usage: npm run setup:clean -- --profile dev npx @quiltdata/benchling-webhook clean --profile dev --yes IMPORTANT: This command only removes local configuration files. AWS resources (CloudFormation stacks) are NOT destroyed and must be removed separately using the 'destroy' command. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(lint): remove unused hasExistingDeployment variable Remove dead code that was checking for existing deployments but never using the result. The variable was assigned but never read, causing a lint error. This was likely leftover from incomplete implementation or refactoring where the logic to use this variable was removed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * lint deploy.ts Co-Authored-By: Claude <noreply@anthropic.com> * fix(deploy): use direct deployment method and handle REVIEW_IN_PROGRESS state - Add --method=direct flag to skip changeset creation entirely - Prevents new stacks from getting stuck in REVIEW_IN_PROGRESS state - Improved REVIEW_IN_PROGRESS stack recovery: - Delete failed changeset first - Use direct AWS CLI commands instead of CDK destroy - Add explicit wait for deletion completion - Fix lint error: rename unused changesetError to _changesetError 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(config): enable multi-stack deployments by removing hardcoded resource names **Problem:** Multiple profiles couldn't deploy to the same AWS account/region because: 1. CloudFormation Export names were hardcoded (e.g., "BenchlingWebhookServiceName") 2. Physical resource names were hardcoded (e.g., clusterName: "benchling-webhook-cluster") **Root Cause:** - Exports must be globally unique per region/account - Physical resource names must be unique per account/region - Hardcoded names caused conflicts when deploying BenchlingWebhookStack-dev alongside BenchlingWebhookStack **Solution:** 1. Made CloudFormation Exports profile-aware using stack name prefix: - lib/fargate-service.ts: "BenchlingWebhookServiceName" → "\${stackName}-ServiceName" - lib/network-load-balancer.ts: "BenchlingWebhookNLBDnsName" → "\${stackName}-NLBDnsName" 2. Removed hardcoded physical resource names (let CloudFormation auto-generate): - lib/fargate-service.ts: Removed clusterName and serviceName - lib/rest-api-gateway.ts: Removed logGroupName and restApiName 3. Made utility scripts profile-aware: - bin/commands/test.ts: Now uses XDGConfig to get stack name from profile - scripts/send-event.ts: Added --profile argument support **Impact:** - ✅ Multiple profiles can now deploy to same account/region - ✅ Each stack gets unique auto-generated resource names - ✅ Exports are scoped per stack (no conflicts) - ✅ Utility scripts work with any profile **Testing:** - npm run lint passes - Ready for deployment test Closes #189 * fix(deploy): record deployment endpoint even when CDK exits with error When CDK deployment succeeds but exits with non-zero code, the deployment endpoint was never recorded to deployments.json. This caused subsequent commands to fail because they couldn't find the endpoint. Now check actual CloudFormation stack status after CDK command fails. If stack reached CREATE_COMPLETE or UPDATE_COMPLETE, treat as successful and record the deployment endpoint. Fixes issue where deployments.json was not created for new profiles. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): add missing ListStackResourcesCommand mock to infer-quilt-config tests The CI was failing because the test file was missing mock responses for ListStackResourcesCommand, which is called by getStackResources() in stack-inference.ts. When unmocked, the AWS SDK client returns undefined, causing 'Cannot read properties of undefined' errors. This was not caught locally because we ran 'npm test' which uses test:ts, while CI runs 'npm run test:ci' which uses test:unit with different Jest configuration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): update CDK stack tests to remove hardcoded resource name expectations The tests were failing because they expected hardcoded ClusterName and ServiceName values that were removed in the multi-stack deployment changes (v0.9.8+). These hardcoded names prevented multiple stacks from being deployed in the same AWS account/region. Updated all affected tests to check for resource existence instead of specific hardcoded names: - test/multi-environment-fargate-service.test.ts - test/benchling-webhook-stack.test.ts - test/multi-environment-stack.test.ts This allows the infrastructure to support multiple independent deployments while maintaining test coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): remove hardcoded LogGroupName expectation in REST API Gateway test The test was expecting a hardcoded LogGroupName property, but the implementation intentionally removed this in v0.9.8 to support multiple stacks per AWS account. The LogGroup construct now uses auto-generated names to prevent conflicts when multiple webhook stacks are deployed in the same account/region. Updated test to only verify the retention policy (7 days), not the exact log group name, which aligns with the current implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 30bc868 commit fc66f06

File tree

144 files changed

+3440
-448
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+3440
-448
lines changed

AGENTS.md

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ See [spec/2025-11-26-architecture/11-arch-30.md](spec/2025-11-26-architecture/11
220220
**Monthly Fixed Costs (us-east-1):**
221221

222222
| Component | Cost |
223-
|-----------|------|
223+
| ----------- | ------ |
224224
| REST API v1 | $0.00 |
225225
| Resource Policy | $0.00 |
226226
| VPC Link | $0.00 |
@@ -232,7 +232,7 @@ See [spec/2025-11-26-architecture/11-arch-30.md](spec/2025-11-26-architecture/11
232232
**Variable Costs (per million requests):**
233233

234234
| Component | Cost |
235-
|-----------|------|
235+
| ----------- | ------ |
236236
| REST API v1 | ~$3.50 |
237237
| Resource Policy | $0.00 |
238238
| ECS Fargate | Included in fixed cost |
@@ -284,7 +284,7 @@ npm run lint # Auto-fix formatting
284284
### Quick Reference
285285

286286
| Command | Docker? | AWS? | When to Use |
287-
|---------|---------|------|-------------|
287+
| --------- | --------- | ------ | ------------- |
288288
| `test` | No | Mocked | Daily development, pre-commit |
289289
| `test:local` | Yes (dev) | Real | Before PR, local integration testing |
290290
| `test:local:prod` | Yes (prod) | Real | Test prod Docker config locally |
@@ -333,6 +333,97 @@ Version 0.7.0 introduces a completely redesigned configuration architecture with
333333

334334
**Independence**: Profiles and stages are independent - you can deploy any profile to any stage
335335

336+
#### Multi-Stack Support (v0.9.8+)
337+
338+
Starting with version 0.9.8, the webhook supports **multiple CloudFormation stacks per AWS account/region**. This enables parallel deployments for different customers, environments, or configurations.
339+
340+
**Stack Naming Strategy:**
341+
342+
1. **Default profile**`BenchlingWebhookStack` (backwards compatible, legacy name)
343+
2. **Named profiles**`BenchlingWebhookStack-{profile}` (auto-generated)
344+
3. **Custom name** → Use `deployment.stackName` in config.json (explicit override)
345+
346+
**Examples:**
347+
348+
```bash
349+
# Default profile uses legacy stack name
350+
npm run deploy:prod -- --profile default
351+
# Creates: BenchlingWebhookStack
352+
353+
# Sales profile uses profile-suffixed name
354+
npm run deploy:prod -- --profile sales
355+
# Creates: BenchlingWebhookStack-sales
356+
357+
# Customer profile with custom name
358+
# In ~/.config/benchling-webhook/customer-acme/config.json:
359+
{
360+
"deployment": {
361+
"stackName": "AcmeWebhookStack",
362+
...
363+
}
364+
}
365+
npm run deploy:prod -- --profile customer-acme
366+
# Creates: AcmeWebhookStack
367+
```
368+
369+
**Use Cases:**
370+
371+
- **Multi-tenant deployments** - Separate stacks for each customer
372+
- **Environment isolation** - Dev, staging, prod in same account
373+
- **A/B testing** - Parallel stacks with different configurations
374+
- **Regional deployments** - Multiple stacks in different regions
375+
376+
**Commands support profile-based stacks:**
377+
378+
```bash
379+
# Deploy with profile (auto-generated stack name)
380+
npm run deploy:prod -- --profile sales
381+
382+
# Check status of profile's stack
383+
npm run status -- --profile sales
384+
385+
# View logs from profile's stack
386+
npm run logs -- --profile sales
387+
388+
# Destroy profile's stack
389+
npm run destroy -- --profile sales
390+
```
391+
392+
**Stack name resolution logic:**
393+
394+
```typescript
395+
import { getStackName } from "./lib/types/config";
396+
397+
// Returns "BenchlingWebhookStack" (backwards compatible)
398+
getStackName("default")
399+
400+
// Returns "BenchlingWebhookStack-sales"
401+
getStackName("sales")
402+
403+
// Returns custom name if specified in config
404+
getStackName("sales", "CustomStack") // "CustomStack"
405+
```
406+
407+
**Deployment tracking:**
408+
409+
Each profile's `deployments.json` stores the stack name used for each deployment:
410+
411+
```json
412+
{
413+
"active": {
414+
"prod": {
415+
"stackName": "BenchlingWebhookStack-sales",
416+
"endpoint": "https://xyz.execute-api.us-east-1.amazonaws.com/prod",
417+
...
418+
}
419+
}
420+
}
421+
```
422+
423+
**Migration from single stack:**
424+
425+
Existing "default" profile deployments continue to use `BenchlingWebhookStack` with no changes required. New profiles automatically get unique stack names.
426+
336427
#### Configuration File Structure
337428

338429
Each profile's `config.json` contains:
@@ -362,7 +453,8 @@ Each profile's `config.json` contains:
362453
"deployment": {
363454
"region": "us-east-1",
364455
"account": "123456789012",
365-
"imageTag": "latest"
456+
"imageTag": "latest",
457+
"stackName": "BenchlingWebhookStack-sales"
366458
},
367459
"logging": {
368460
"level": "INFO"
@@ -371,7 +463,7 @@ Each profile's `config.json` contains:
371463
"enableVerification": true
372464
},
373465
"_metadata": {
374-
"version": "0.7.0",
466+
"version": "0.9.8",
375467
"createdAt": "2025-11-04T10:00:00Z",
376468
"updatedAt": "2025-11-04T10:00:00Z",
377469
"source": "wizard"
@@ -407,12 +499,14 @@ Each profile's `deployments.json` tracks deployment history:
407499
"dev": {
408500
"endpoint": "https://xxx.execute-api.us-east-1.amazonaws.com/dev",
409501
"imageTag": "latest",
410-
"deployedAt": "2025-11-04T10:30:00Z"
502+
"deployedAt": "2025-11-04T10:30:00Z",
503+
"stackName": "BenchlingWebhookStack-sales"
411504
},
412505
"prod": {
413506
"endpoint": "https://xxx.execute-api.us-east-1.amazonaws.com/prod",
414-
"imageTag": "0.7.0",
415-
"deployedAt": "2025-11-03T14:20:00Z"
507+
"imageTag": "0.9.8",
508+
"deployedAt": "2025-11-03T14:20:00Z",
509+
"stackName": "BenchlingWebhookStack-sales"
416510
}
417511
},
418512
"history": [
@@ -421,7 +515,7 @@ Each profile's `deployments.json` tracks deployment history:
421515
"timestamp": "2025-11-04T10:30:00Z",
422516
"imageTag": "latest",
423517
"endpoint": "https://...",
424-
"stackName": "BenchlingWebhookStack",
518+
"stackName": "BenchlingWebhookStack-sales",
425519
"region": "us-east-1"
426520
}
427521
]
@@ -516,7 +610,7 @@ class XDGConfig {
516610
Stored in AWS Secrets Manager and referenced in profile config:
517611

518612
| Field | Required | Default | Description |
519-
|-------|----------|---------|-------------|
613+
| ------- | ---------- | --------- | ------------- |
520614
| `benchling.tenant` | Yes | - | Benchling tenant name |
521615
| `benchling.clientId` | Yes | - | OAuth client ID |
522616
| `benchling.clientSecret` | Via Secrets Manager | - | OAuth client secret |
@@ -531,6 +625,7 @@ Stored in AWS Secrets Manager and referenced in profile config:
531625
| `packages.bucket` | Yes | - | S3 bucket for package storage |
532626
| `packages.prefix` | No | `benchling` | S3 key prefix |
533627
| `packages.metadataKey` | No | `experiment_id` | Package metadata key |
628+
| `deployment.stackName` | No | Auto-generated | CloudFormation stack name |
534629
| `security.enableVerification` | No | `true` | Enable webhook signature verification |
535630
| `security.webhookAllowList` | No | `""` | IP allowlist (comma-separated) |
536631
| `logging.level` | No | `INFO` | Python logging level |
@@ -668,7 +763,7 @@ npx @quiltdata/benchling-webhook logs --profile default
668763
## Configuration Failure Modes
669764

670765
| Failure | Cause | Mitigation |
671-
|----------|--------|-------------|
766+
| ---------- | -------- | ------------- |
672767
| Missing profile | Profile not found | Run `npm run setup` to create profile |
673768
| Missing Quilt catalog | Quilt3 not configured | Run `quilt3 config` and retry |
674769
| Profile config corrupted | Manual file edit | Validate JSON schema; re-run `npm run setup` |
@@ -680,6 +775,7 @@ npx @quiltdata/benchling-webhook logs --profile default
680775
| 403 Forbidden (HMAC) | Invalid signature | Check ECS logs for HMAC verification errors; verify Benchling secret |
681776
| 403 Forbidden (Resource Policy) | IP not in allowlist | Add IP to webhookAllowList or remove IP filtering |
682777
| NLB unhealthy targets | ECS health check failing | Check ECS logs and container health status |
778+
| Stack name conflict | Multiple profiles, same custom name | Use unique stackName per profile or rely on auto-generation |
683779

684780
---
685781

@@ -696,6 +792,7 @@ npx @quiltdata/benchling-webhook logs --profile default
696792

697793
- **Profile-Based Configuration**: Each profile is self-contained with its own settings and deployment tracking
698794
- **Profile/Stage Independence**: Deploy any profile to any stage for maximum flexibility
795+
- **Multi-Stack Support**: Multiple CloudFormation stacks per AWS account/region (v0.9.8+)
699796
- **Single Source of Truth**: Profile's `config.json` defines all configuration
700797
- **Per-Profile Deployment Tracking**: Each profile tracks its own deployments independently
701798
- **Fail Fast**: Validation before deployment prevents partial stacks

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33

44
All notable changes to this project will be documented in this file, one line per user-visible change.
55

6+
## [0.9.7] - 2025-12-18
7+
8+
### Fixed
9+
10+
- **NPM script deployment validation** - Fixed A07 config validation break in `npm run deploy:dev` (#A07)
11+
- `deploy.ts` now passes complete config to CDK via environment variables
12+
- Fixes validation errors: "Required field missing: quilt.catalog"
13+
- No changes needed to `benchling-webhook.ts` (already reads env vars)
14+
- Preserves A07 validation benefits for library users
15+
- Parameters remain optional overrides as intended
16+
- See [spec/a07-library-config/05-implementation-plan.md](spec/a07-library-config/05-implementation-plan.md) for details
17+
618
## [0.9.6] - 2025-12-18
719

820
### Added

README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,66 @@ In Benchling:
201201
A Quilt package will be automatically created and linked to your notebook entry.
202202
If you run into problems, contact [Quilt Support](support@quilt.bio)
203203

204+
## Multi-Stack Deployments (v0.9.8+)
205+
206+
Starting with version 0.9.8, you can deploy **multiple webhook stacks** in the same AWS account/region. This is useful for:
207+
208+
- **Multi-tenant deployments** - Separate stacks for each customer
209+
- **Environment isolation** - Dev, staging, prod in same account
210+
- **A/B testing** - Parallel stacks with different configurations
211+
212+
### Profile-Based Stack Names
213+
214+
Each profile automatically gets its own CloudFormation stack:
215+
216+
```bash
217+
# Default profile uses legacy name (backwards compatible)
218+
npx @quiltdata/benchling-webhook@latest deploy --profile default
219+
# Creates: BenchlingWebhookStack
220+
221+
# Other profiles get unique names
222+
npx @quiltdata/benchling-webhook@latest deploy --profile sales
223+
# Creates: BenchlingWebhookStack-sales
224+
225+
npx @quiltdata/benchling-webhook@latest deploy --profile customer-acme
226+
# Creates: BenchlingWebhookStack-customer-acme
227+
```
228+
229+
### Custom Stack Names
230+
231+
You can also specify a custom stack name in your profile configuration:
232+
233+
```json
234+
{
235+
"deployment": {
236+
"stackName": "MyCustomWebhookStack",
237+
...
238+
}
239+
}
240+
```
241+
242+
### Managing Multiple Stacks
243+
244+
All commands support the `--profile` flag:
245+
246+
```bash
247+
# Deploy a specific profile
248+
npx @quiltdata/benchling-webhook@latest deploy --profile sales
249+
250+
# Check status
251+
npx @quiltdata/benchling-webhook@latest status --profile sales
252+
253+
# View logs
254+
npx @quiltdata/benchling-webhook@latest logs --profile sales
255+
256+
# Destroy stack
257+
npx @quiltdata/benchling-webhook@latest destroy --profile sales
258+
```
259+
260+
### Migration from Single Stack
261+
262+
Existing "default" profile deployments continue to use `BenchlingWebhookStack` with no changes required. New profiles automatically get unique stack names.
263+
204264
## Monitoring
205265

206266
### CloudWatch Logs
@@ -233,6 +293,9 @@ npx @quiltdata/benchling-webhook@latest status [--profile <name>]
233293
# View CloudWatch logs
234294
npx @quiltdata/benchling-webhook@latest logs [--profile <name>]
235295

296+
# Destroy stack
297+
npx @quiltdata/benchling-webhook@latest destroy [--profile <name>]
298+
236299
# Show all available commands
237300
npx @quiltdata/benchling-webhook@latest --help
238301
```

0 commit comments

Comments
 (0)