Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

## [0.15.0] - 2026-04-10

### Changed

- Setup now treats disabled integrated webhooks as an IaC-managed concern: it instructs users to enable the Quilt stack setting through CloudFormation/Terraform instead of mutating the stack directly, avoiding stack drift for customers who manage Quilt via IaC

### Added

- `setup --force` and install-time `--force` support for emergency overrides when direct stack mutation is absolutely necessary
- Focused setup wizard coverage for the new IaC-first integrated enablement flow

### Fixed

- Manual catalog entry in setup now accepts full Quilt catalog URLs and normalizes them to bare DNS names before validation and storage
- Deploy now passes `PackagePrefix` to CDK so the EventBridge rule matches the actual runtime package prefix (previously hardcoded to `"benchling"`)
- Pending canvas now shows full package content with disabled links instead of a bare "Preparing package..." placeholder

## [0.14.0] - 2026-04-08

### Added
Expand Down
3 changes: 3 additions & 0 deletions bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ program
.option("--inherit-from <name>", "Base profile to inherit from")
.option("--region <region>", "AWS region")
.option("--aws-profile <name>", "AWS credentials profile")
.option("--force", "Allow direct stack mutation instead of requiring IaC-managed changes")
.action(async (options, command) => {
try {
await setupWizardCommand({
Expand Down Expand Up @@ -517,6 +518,8 @@ if (
i++;
} else if (args[i] === "--skip-validation") {
options.skipValidation = true;
} else if (args[i] === "--force") {
(options as typeof options & { force?: boolean }).force = true;
}
}

Expand Down
7 changes: 7 additions & 0 deletions bin/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ export interface InstallCommandOptions {
*/
yes?: boolean;

/**
* Allow direct stack mutation instead of requiring IaC-managed changes
*/
force?: boolean;

}

/**
Expand All @@ -94,6 +99,7 @@ export async function installCommand(options: InstallCommandOptions = {}): Promi
awsRegion,
setupOnly = false,
yes = false,
force = false,
} = options;

// Validate flags
Expand All @@ -113,6 +119,7 @@ export async function installCommand(options: InstallCommandOptions = {}): Promi
awsRegion,
yes: yes,
isPartOfInstall: true, // Suppress next steps from setup wizard
force,
});
} catch (error) {
const err = error as Error;
Expand Down
29 changes: 29 additions & 0 deletions bin/commands/setup-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ export interface SetupWizardOptions {
isPartOfInstall?: boolean;
/** Config storage implementation (for testing) */
configStorage?: XDGBase;
/** Allow direct stack mutation instead of requiring IaC-managed changes */
force?: boolean;

// CLI argument overrides
catalogUrl?: string;
Expand Down Expand Up @@ -174,6 +176,7 @@ export async function runSetupWizard(options: SetupWizardOptions = {}): Promise<
awsRegion,
setupOnly = false,
configStorage,
force = false,
} = options;

const xdg = configStorage || new XDGConfig();
Expand Down Expand Up @@ -350,6 +353,20 @@ export async function runSetupWizard(options: SetupWizardOptions = {}): Promise<
console.log(chalk.green(`✓ Configuration saved to: ~/.config/benchling-webhook/${profile}/config.json\n`));
};

const throwIntegrationIacGuidance = (): never => {
const setupCommand = profile === "default"
? "npm run setup -- --force"
: `npm run setup -- --profile ${profile} --force`;

throw new Error(
"Enabling or disabling the integrated Benchling Webhook must be done through your infrastructure-as-code workflow\n" +
"so CloudFormation/Terraform stays authoritative.\n\n" +
"Ask your IT/platform team to update the Quilt stack configuration and re-apply it.\n\n" +
"If you must bypass IaC temporarily, rerun setup with --force:\n" +
` ${setupCommand}`,
);
};

const requireConfig = async (integratedStack: boolean): Promise<ProfileConfig> => {
try {
return buildProfileConfigFromExisting({
Expand Down Expand Up @@ -439,6 +456,10 @@ export async function runSetupWizard(options: SetupWizardOptions = {}): Promise<
break;
}
case "disable-integration": {
if (!force) {
throwIntegrationIacGuidance();
}

// User already confirmed in Phase 5 - proceed with disabling
console.log(chalk.dim("Disabling integrated webhook..."));

Expand Down Expand Up @@ -491,6 +512,10 @@ export async function runSetupWizard(options: SetupWizardOptions = {}): Promise<
};
}

if (!force) {
throwIntegrationIacGuidance();
}

const updateResult = await updateStackParameter({
stackArn: stackQuery.stackArn,
region: stackQuery.region,
Expand Down Expand Up @@ -550,6 +575,10 @@ export async function runSetupWizard(options: SetupWizardOptions = {}): Promise<
};
}

if (!force) {
throwIntegrationIacGuidance();
}

const updateResult = await updateStackParameter({
stackArn: stackQuery.stackArn,
region: stackQuery.region,
Expand Down
2 changes: 1 addition & 1 deletion bin/commands/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,7 @@ function displayStatusResult(
if (mode === "integrated" && !result.benchlingIntegrationEnabled) {
console.log(chalk.bold("Action Required:"));
console.log(chalk.yellow(" BenchlingIntegration is Disabled"));
console.log(chalk.dim(" Enable it via CloudFormation console or re-run setup\n"));
console.log(chalk.dim(" Enable it via CloudFormation/Terraform, or rerun setup with --force only as an emergency override\n"));
}
} else if (result.stackStatus?.includes("FAILED") || result.stackStatus?.includes("ROLLBACK")) {
console.log(chalk.bold("Status:"));
Expand Down
2 changes: 1 addition & 1 deletion docker/app-manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ manifestVersion: 1
info:
name: nightly-quilttest-com
description: Packaging Benchling Notebooks as Quilt packages
version: 0.14.0
version: 0.15.0
features:
- name: Quilt Connector
id: quilt_entry
Expand Down
2 changes: 1 addition & 1 deletion docker/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "benchling-quilt-integration"
version = "0.14.0"
version = "0.15.0"
description = "Benchling-Quilt Integration Webhook Service"
license = {text = "Apache-2.0"}
authors = [
Expand Down
2 changes: 1 addition & 1 deletion docker/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions lib/wizard/phase1-catalog-discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function normalizeCatalogDns(catalogUrl: string): string {
* @returns True if valid, error message otherwise
*/
function validateCatalogDns(catalogDns: string): boolean | string {
const trimmed = catalogDns.trim();
const trimmed = normalizeCatalogDns(catalogDns).trim();
if (trimmed.length === 0) {
return "Catalog DNS name is required";
}
Expand Down Expand Up @@ -149,7 +149,7 @@ export async function runCatalogDiscovery(
{
type: "input",
name: "manualCatalog",
message: "Enter catalog DNS name:",
message: "Enter catalog DNS name or URL:",
validate: validateCatalogDns,
filter: normalizeCatalogDns,
},
Expand Down Expand Up @@ -201,7 +201,7 @@ export async function runCatalogDiscovery(
{
type: "input",
name: "manualCatalog",
message: "Enter catalog DNS name:",
message: "Enter catalog DNS name or URL:",
validate: validateCatalogDns,
filter: normalizeCatalogDns,
},
Expand Down Expand Up @@ -229,7 +229,7 @@ export async function runCatalogDiscovery(
{
type: "input",
name: "manualCatalog",
message: "Enter Quilt Catalog DNS name (e.g., open.quiltdata.com):",
message: "Enter Quilt Catalog DNS name or URL (e.g., open.quiltdata.com or https://open.quiltdata.com):",
validate: validateCatalogDns,
filter: normalizeCatalogDns,
},
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@quiltdata/benchling-webhook",
"version": "0.14.0",
"version": "0.15.0",
"description": "AWS CDK deployment for Benchling webhook processing using Fargate - Deploy directly with npx",
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",
Expand Down
Loading