Skip to content

Commit ddc42c9

Browse files
drernieclaude
andauthored
feat: add browse buttons for linked packages (#250)
* chore: bump version to 0.8.4 Co-Authored-By: Claude <noreply@anthropic.com> * feat(canvas): add browse buttons for linked packages Implement browse linked packages feature per spec A03, enabling users to browse linked packages directly within the Benchling Canvas interface. Changes: - Add package name encoding/decoding functions to handle button IDs - Store linked packages as instance variable in CanvasManager - Create browse button section for linked packages - Add button routing and handler for browse-linked buttons - Implement comprehensive unit tests (44 new tests) Technical details: - Button ID format: browse-linked-{entry_id}-pkg-{encoded_pkg}-p{page}-s{size} - Package name encoding: replace "/" with "--" for button-safe IDs - Handler overrides CanvasManager package to browse linked package - Reuses existing browser blocks and pagination logic Testing: - All 317 tests pass (273 Python + 44 new browse-linked tests) - Comprehensive coverage of encoding, parsing, and button creation - Edge cases: special characters, multiple slashes, invalid formats Fixes #84 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(config): allow additional properties in config schema for backward compatibility Changed ProfileConfigSchema.additionalProperties from false to true to allow extra fields that may exist from: - Legacy configurations during migration - Future configuration fields - Resolved/derived fields (e.g., resolvedServices) This prevents aggressive "must NOT have additional properties" validation errors while still validating required fields and types. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(test): report Docker image tag and deployment time in test output Enhanced test-deployed-dev-direct and test-deployed-prod targets to display: - Docker image tag being tested (e.g., 0.8.0-20251118T054240Z) - Deployment timestamp for debugging This makes it clear which version is deployed when running: - npm run test:dev - make test-deployed-dev - make test-deployed-prod 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: update CHANGELOG for browse-linked feature and improvements * fix(ci): use docker buildx build instead of docker build The validation script uses 'docker buildx imagetools inspect' which requires images to be built with 'docker buildx build'. This change ensures consistency between the build and validation steps. Changes: - Replace 'docker build' with 'docker buildx build' in docker.py - Add --load flag to load the built image into Docker - Update comment to clarify why buildx is required Fixes validation failure in CI: https://github.com/quiltdata/benchling-webhook/actions/runs/19492518695/job/55787434722 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(status): allow status command for standalone profiles with stackArn Remove the requirement that profiles must have integratedStack=true to use the status command. The status command can now work for any profile that has a Quilt stack ARN configured, regardless of whether it's an integrated or standalone deployment. Changes: - Remove integratedStack check from statusCommand - Update error message to be more generic - Fix tests to reflect new behavior - Add test case for profiles without stackArn This allows users to check stack status for standalone deployments as long as they have the Quilt stack ARN configured. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: update CHANGELOG with status command fix * fix(cli): fix Commander.js option ordering to use correct property names Commander.js uses the LAST option name as the property name when multiple aliases are provided. All commands were using "--profile, --config" which created options.config instead of options.profile, causing commands to ignore the --profile flag and always use "default". Fixed by reordering to "--config, --profile" so commands receive the correct options.profile property. Changes: - deploy command: --config, --profile - setup command: --config, --profile - status command: --config, --profile - logs command: --config, --profile - validate command: --config, --profile - manifest command: --config, --profile - health-check command: --config, --profile - config command: --config-name, --profile This was a critical bug that broke all profile-based operations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: add critical CLI option fix to CHANGELOG * refactor(cli): remove --config alias, use only --profile Simplifies CLI by removing the confusing --config alias. All commands now use only --profile which is clearer and less error-prone. Changes: - Removed --config alias from all commands (deploy, setup, status, logs, validate, manifest, health-check, config) - Removed --config-name alias from config command - Removed --config parsing from install command argument handler - All commands now use consistent --profile <name> syntax This eliminates confusion and potential bugs from having two names for the same thing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: update CHANGELOG for --config alias removal * fix(license): update Python package license from MIT to Apache-2.0 Update docker/pyproject.toml to reflect Apache-2.0 license, consistent with the main LICENSE file and package.json. Changes license metadata and PyPI classifier. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: streamline CHANGELOG for 0.8.4 release Make changelog entries more concise and focused on user-visible changes. Remove internal implementation details that don't impact users. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * ui: cleanup button/footer layout Co-Authored-By: Claude <noreply@anthropic.com> * chore(lint): add pyright type checking to lint pipeline - Add pyright 1.1.398 to dev dependencies in pyproject.toml - Create pyrightconfig.json with VSCode Pylance-compatible settings - Integrate pyright into 'make lint' target for automated type checking - Fix import inconsistency: use relative import in package_query.py This ensures type errors are caught during 'make lint' and CI, preventing mismatches like the Config type issue that was only visible in VSCode. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * style: apply black formatting to canvas.py Auto-formatted by black as part of lint pipeline. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(ngrok): require NGROK_DOMAIN env var and display public URL - Load .env from project root for environment variables - Require NGROK_DOMAIN for run-ngrok and run-native-ngrok targets - Display public URL on startup using static domain - Update help text to indicate NGROK_DOMAIN requirement - Fix run-native-ngrok to explicitly set PORT 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(config): remove CloudFormation-based ConfigResolver, use service-specific envars BREAKING CHANGE: Removed config_resolver.py ConfigResolver class that queried CloudFormation The v0.8.0+ architecture uses service-specific environment variables (QUILT_WEB_HOST, ATHENA_USER_DATABASE, PACKAGER_SQS_URL, etc.) passed directly from xdg-launch, eliminating the need for CloudFormation queries. Changes: - Extract Secrets Manager utilities into new secrets_manager.py module - Remove ConfigResolver class and CloudFormation stack querying logic - Update config.py to use fetch_benchling_secret() for Benchling credentials only - Update tests to import from secrets_manager instead of config_resolver - All 317 tests pass Docker containers now receive full XDG configuration via environment variables from xdg-launch, with only Benchling credentials fetched from AWS Secrets Manager at runtime. * chore(lint): apply formatting and remove unused fixtures - Remove unused mock_config_resolver fixture from conftest.py - Apply black formatting to various Python files - Remove unused imports - All tests still pass * fix(canvas): wrap footer markdown in block to prevent AttributeError Fixed bug where canvas footer was being added as a raw string instead of a MarkdownUiBlockUpdate object, causing AttributeError when Benchling SDK tried to serialize blocks. Root cause: Using .extend() on a string iterates over each character, adding them individually to the blocks list. When Benchling SDK called .to_dict() on these string characters, it failed. Solution: Wrap footer string in create_markdown_block() before adding to blocks list using .append(). Added regression tests to verify: - Footer is properly wrapped in MarkdownUiBlockUpdate - All blocks have required .to_dict() method - blocks_to_dict() successfully serializes without errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(canvas): use SDK .to_dict() for more reliable serialization Replaced manual dictionary construction with Benchling SDK's built-in .to_dict() method for block serialization. This makes the code: 1. More maintainable - stays in sync with SDK changes automatically 2. Less fragile - no manual field mapping that could drift 3. Better tested - added explicit error handling for invalid blocks 4. More robust - fails fast with clear error messages The previous implementation manually reconstructed dicts for each block type, which was error-prone and could silently break if the SDK changed its serialization format. Benefits: - Reduces code duplication (40+ lines → 8 lines) - SDK compatibility guaranteed - Type validation with helpful error messages - Prevents bugs like the footer string issue 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(canvas): add update_canvas_with_blocks method for flexibility Added new method to allow updating canvas with externally-provided blocks. This is used by the linked package browser feature in app.py. Separates concerns: - update_canvas() - generates blocks internally from entry state - update_canvas_with_blocks() - accepts blocks from caller Both methods share the same error handling and logging patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore(lint): remove unused imports and apply formatting - Remove unused 're' import from secrets_manager.py - Remove unused 'MagicMock' and 'patch' imports from conftest.py - Apply black formatting to test files - Fix import ordering per isort 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(canvas): implement complete context-aware navigation for linked packages Implements full package context preservation throughout the entire linked package browsing experience. All navigation (Next/Previous/Metadata/Back) now correctly maintains the package being viewed. **Changes:** Infrastructure: - Updated parse_browse_linked_button_id() to handle all linked button formats (next-page-linked-, prev-page-linked-, view-metadata-linked-) - Added 3 new test cases for navigation button parsing Canvas Blocks: - Enhanced create_browser_navigation_buttons() with package_name parameter - Enhanced create_metadata_navigation_buttons() with package_name parameter - Navigation buttons now embed package context in button IDs Canvas Manager: - Updated get_package_browser_blocks() to accept optional package_name - Updated get_metadata_blocks() to accept optional package_name - Updated _make_navigation_buttons() to propagate package_name to all contexts - Updated helper methods to support package name overrides Request Handlers: - Added handle_page_navigation_linked() for Next/Previous on linked packages - Updated handle_view_metadata_linked() to pass package_name to metadata view - Optimized handle_back_to_main() to avoid triggering export workflow **Fixes:** - Next/Previous buttons now stay with linked package instead of reverting to main - View Metadata shows metadata for linked package, not main package - Back to Browser returns to linked package browser, not main package browser - Back to Package button is now fast (no unnecessary AWS calls) **Testing:** - All 324 tests pass - No TODO/FIXME items remaining 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore(deps): update pyright from 1.1.398 to 1.1.407 Update pyright to latest version to resolve version warning. 🤖 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 2162d34 commit ddc42c9

36 files changed

+1647
-866
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
55

66
## [Unreleased]
77

8+
### Added
9+
10+
- Browse buttons for linked packages in Benchling Canvas
11+
12+
### Changed
13+
14+
- **BREAKING**: Removed `--config` alias, use only `--profile` for all commands
15+
16+
### Fixed
17+
18+
- **CRITICAL**: `--profile` flag now works correctly for all commands
19+
- Configuration validation allows additional properties for backward compatibility
20+
- Status command works with any profile containing stackArn (not just integrated stacks)
21+
- Python package license corrected to Apache-2.0
22+
823
## [0.8.3] - 2025-11-18
924

1025
### Fixed

bin/cli.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ program
6060
)
6161
.option("--env-file <path>", "Path to .env file", ".env")
6262
// Multi-environment options (v0.7.0)
63-
.option("--profile, --config <name>", "Configuration profile to use (default: default)")
63+
.option("--profile <name>", "Configuration profile to use (default: default)")
6464
.option("--stage <name>", "API Gateway stage: dev or prod (default: prod)")
6565
// Common options
6666
.option("--no-bootstrap-check", "Skip CDK bootstrap verification")
@@ -101,7 +101,7 @@ For more information: https://github.com/quiltdata/benchling-webhook#deployment
101101
program
102102
.command("setup")
103103
.description("Run setup wizard only (without deployment)")
104-
.option("--profile, --config <name>", "Configuration profile to use (default: default)")
104+
.option("--profile <name>", "Configuration profile to use (default: default)")
105105
.option("--inherit-from <name>", "Base profile to inherit from")
106106
.option("--region <region>", "AWS region")
107107
.option("--aws-profile <name>", "AWS credentials profile")
@@ -122,7 +122,7 @@ program
122122
"Check CloudFormation stack status and BenchlingIntegration parameter",
123123
)
124124
.option(
125-
"--profile, --config <name>",
125+
"--profile <name>",
126126
"Configuration profile to check (default: default)",
127127
)
128128
.option("--aws-profile <name>", "AWS credentials profile")
@@ -174,7 +174,7 @@ Use --no-exit to keep monitoring indefinitely.
174174
program
175175
.command("logs")
176176
.description("View CloudWatch logs from deployed webhook integration")
177-
.option("--profile, --config <name>", "Configuration profile to use (default: default)")
177+
.option("--profile <name>", "Configuration profile to use (default: default)")
178178
.option("--aws-profile <name>", "AWS credentials profile")
179179
.option(
180180
"--type <type>",
@@ -254,7 +254,7 @@ program
254254
program
255255
.command("validate")
256256
.description("Validate configuration without deploying")
257-
.option("--profile, --config <name>", "Configuration profile to validate", "default")
257+
.option("--profile <name>", "Configuration profile to validate", "default")
258258
.option("--verbose", "Show detailed validation information")
259259
.action(async (options) => {
260260
try {
@@ -300,7 +300,7 @@ program
300300
.command("manifest")
301301
.description("Generate Benchling app manifest file")
302302
.option("--output <path>", "Output file path", "app-manifest.yaml")
303-
.option("--profile, --config <name>", "Configuration profile to use (optional)")
303+
.option("--profile <name>", "Configuration profile to use (optional)")
304304
.option("--catalog <url>", "Catalog URL to use (optional, overrides profile)")
305305
.action(async (options) => {
306306
try {
@@ -362,7 +362,7 @@ For more information: https://github.com/quiltdata/benchling-webhook#multi-envir
362362
program
363363
.command("health-check")
364364
.description("Check configuration health and secrets sync status")
365-
.option("--profile, --config <name>", "Configuration profile to check", "default")
365+
.option("--profile <name>", "Configuration profile to check", "default")
366366
.option("--json", "Output in JSON format")
367367
.action(async (options) => {
368368
try {
@@ -377,7 +377,7 @@ program
377377
program
378378
.command("config")
379379
.description("Show configuration for a profile as JSON")
380-
.option("--profile, --config-name <name>", "Configuration profile to show", "default")
380+
.option("--profile <name>", "Configuration profile to show", "default")
381381
.action(async (options) => {
382382
try {
383383
await configShowCommand(options);
@@ -414,7 +414,7 @@ if (
414414
options.yes = true;
415415
} else if (args[i] === "--setup-only") {
416416
options.setupOnly = true;
417-
} else if ((args[i] === "--profile" || args[i] === "--config") && i + 1 < args.length) {
417+
} else if (args[i] === "--profile" && i + 1 < args.length) {
418418
options.profile = args[i + 1];
419419
i++;
420420
} else if (args[i] === "--inherit-from" && i + 1 < args.length) {

bin/commands/status.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -877,24 +877,12 @@ export async function statusCommand(options: StatusCommandOptions = {}): Promise
877877
};
878878
}
879879

880-
// Check if integrated stack
881-
if (!config.integratedStack) {
882-
const errorMsg = "Status command is only available for integrated stack mode";
883-
console.log(chalk.yellow(`\n⚠️ ${errorMsg}\n`));
884-
console.log(chalk.dim("This profile is configured for standalone deployment."));
885-
console.log(chalk.dim("Use CloudFormation console to check webhook stack status.\n"));
886-
return {
887-
success: false,
888-
error: errorMsg,
889-
};
890-
}
891-
892-
// Extract stack info
880+
// Extract stack info - if stackArn is present, status should work regardless of integratedStack flag
893881
const stackArn = config.quilt.stackArn;
894882
if (!stackArn) {
895883
return {
896884
success: false,
897-
error: "Quilt stack ARN not found in configuration. This command requires a Quilt stack ARN to check integration status.",
885+
error: "Quilt stack ARN not found in configuration. This command requires a Quilt stack ARN to check stack status.",
898886
};
899887
}
900888
const region = config.deployment.region;

docker/Makefile

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
# Configuration is now managed via XDG (~/.config/benchling-webhook/{profile}/)
44
# with unified config.json and deployments.json per profile (v0.7.0)
55

6+
# Load .env file from project root if it exists (for NGROK_DOMAIN and other env vars)
7+
-include ../.env
8+
export
9+
610
# Include deployment targets
711
include make.deploy
812

@@ -36,10 +40,10 @@ help:
3640
@echo "Development Servers (all use xdg-launch):"
3741
@echo " run / run-dev - Docker dev with hot-reload (port $(PORT_DOCKER_DEV))"
3842
@echo " run-prod - Docker production mode (port $(PORT_DOCKER_PROD))"
39-
@echo " run-ngrok - Docker dev + ngrok tunnel (port $(PORT_DOCKER_DEV))"
43+
@echo " run-ngrok - Docker dev + ngrok tunnel (requires NGROK_DOMAIN env var)"
4044
@echo " run-native - Native Flask with dev profile (port $(PORT_NATIVE))"
4145
@echo " run-native-verbose - Alias for run-native (always verbose)"
42-
@echo " run-native-ngrok - Native server + ngrok tunnel (port $(PORT_NATIVE))"
46+
@echo " run-native-ngrok - Native server + ngrok tunnel (requires NGROK_DOMAIN env var)"
4347
@echo " run-ecr - Pull and run image from ECR (port $(PORT_DOCKER_PROD))"
4448
@echo ""
4549
@echo "Testing (xdg-launch-based tests marked with *):"
@@ -55,7 +59,7 @@ help:
5559
@echo " test-benchling - Test Benchling OAuth credentials"
5660
@echo " test-query - Test Quilt package query (defaults to BENCHLING_TEST_ENTRY)"
5761
@echo " test-integration - Full integration tests (requires real Benchling creds)"
58-
@echo " lint - Auto-fix code formatting"
62+
@echo " lint - Auto-fix code formatting (black + isort) and type check (pyright)"
5963
@echo ""
6064
@echo "Health & Monitoring:"
6165
@echo " health - Check docker prod health (port $(PORT_DOCKER_PROD))"
@@ -135,6 +139,19 @@ run: check-xdg
135139
run-dev: run
136140

137141
run-ngrok:
142+
@if [ -z "$(NGROK_DOMAIN)" ]; then \
143+
echo "❌ Error: NGROK_DOMAIN environment variable is required"; \
144+
echo ""; \
145+
echo "Get your static domain from: https://dashboard.ngrok.com/domains"; \
146+
echo ""; \
147+
echo "Usage:"; \
148+
echo " export NGROK_DOMAIN=uniformly-alive-halibut.ngrok-free.app"; \
149+
echo " make run-ngrok"; \
150+
exit 1; \
151+
fi
152+
@echo "🚀 Starting dev server and ngrok tunnel..."
153+
@echo "📡 Public URL: https://$(NGROK_DOMAIN)/"
154+
@echo ""
138155
@make run &
139156
@make ngrok PORT=$(PORT_DOCKER_DEV) SLEEP=10
140157

@@ -152,10 +169,22 @@ run-native-verbose: run-native
152169

153170
# Native server + ngrok tunnel
154171
run-native-ngrok: check-xdg
155-
@echo "Starting native server and ngrok tunnel..."
172+
@if [ -z "$(NGROK_DOMAIN)" ]; then \
173+
echo "❌ Error: NGROK_DOMAIN environment variable is required"; \
174+
echo ""; \
175+
echo "Get your static domain from: https://dashboard.ngrok.com/domains"; \
176+
echo ""; \
177+
echo "Usage:"; \
178+
echo " export NGROK_DOMAIN=uniformly-alive-halibut.ngrok-free.app"; \
179+
echo " make run-native-ngrok"; \
180+
exit 1; \
181+
fi
182+
@echo "🚀 Starting native server and ngrok tunnel..."
183+
@echo "📡 Public URL: https://$(NGROK_DOMAIN)/"
184+
@echo ""
156185
@echo "Press Ctrl+C to stop both ngrok and server"
157186
@make run-native-verbose &
158-
@make ngrok
187+
@make ngrok PORT=$(PORT_NATIVE)
159188

160189
# Run ECR image locally (assumes image exists in ECR)
161190
run-ecr: check-xdg docker-ecr-login
@@ -303,31 +332,48 @@ test-deployed-dev: check-xdg
303332
test-deployed-dev-direct: check-xdg
304333
@echo "🧪 Testing deployed dev stack (profile: $(PROFILE))..."
305334
@DEV_ENDPOINT=$$(jq -r '.active.dev.endpoint // empty' $(PROFILE_DIR)/deployments.json 2>/dev/null); \
335+
IMAGE_TAG=$$(jq -r '.active.dev.imageTag // empty' $(PROFILE_DIR)/deployments.json 2>/dev/null); \
336+
DEPLOYED_AT=$$(jq -r '.active.dev.timestamp // empty' $(PROFILE_DIR)/deployments.json 2>/dev/null); \
306337
if [ -z "$$DEV_ENDPOINT" ]; then \
307338
echo "❌ No dev endpoint found in $(PROFILE_DIR)/deployments.json"; \
308339
echo "💡 Run 'npm run deploy:dev -- --profile $(PROFILE)' first to deploy the dev stack"; \
309340
exit 1; \
310341
fi; \
311342
echo "📡 Testing endpoint: $$DEV_ENDPOINT"; \
343+
if [ -n "$$IMAGE_TAG" ]; then \
344+
echo "🐳 Image tag: $$IMAGE_TAG"; \
345+
fi; \
346+
if [ -n "$$DEPLOYED_AT" ]; then \
347+
echo "📅 Deployed at: $$DEPLOYED_AT"; \
348+
fi; \
312349
uv run python scripts/test_webhook.py "$$DEV_ENDPOINT" --health-only
313350

314351
# Test deployed prod stack via API Gateway endpoint
315352
# v0.7.0: Updated to use deployments.json with profile/stage structure
316353
test-deployed-prod: check-xdg
317354
@echo "🧪 Testing deployed prod stack (profile: $(PROFILE))..."
318355
@PROD_ENDPOINT=$$(jq -r '.active.prod.endpoint // empty' $(PROFILE_DIR)/deployments.json 2>/dev/null); \
356+
IMAGE_TAG=$$(jq -r '.active.prod.imageTag // empty' $(PROFILE_DIR)/deployments.json 2>/dev/null); \
357+
DEPLOYED_AT=$$(jq -r '.active.prod.timestamp // empty' $(PROFILE_DIR)/deployments.json 2>/dev/null); \
319358
if [ -z "$$PROD_ENDPOINT" ]; then \
320359
echo "❌ No prod endpoint found in $(PROFILE_DIR)/deployments.json"; \
321360
echo "💡 Run 'npm run deploy:prod -- --profile $(PROFILE)' first to deploy the prod stack"; \
322361
exit 1; \
323362
fi; \
324363
echo "📡 Testing endpoint: $$PROD_ENDPOINT"; \
364+
if [ -n "$$IMAGE_TAG" ]; then \
365+
echo "🐳 Image tag: $$IMAGE_TAG"; \
366+
fi; \
367+
if [ -n "$$DEPLOYED_AT" ]; then \
368+
echo "📅 Deployed at: $$DEPLOYED_AT"; \
369+
fi; \
325370
uv run python scripts/test_webhook.py "$$PROD_ENDPOINT" --health-only
326371

327-
# Auto-fix code formatting (black + isort)
372+
# Auto-fix code formatting (black + isort) and type check (pyright)
328373
lint:
329374
uv run black src/ tests/ scripts/
330375
uv run isort src/ tests/ scripts/
376+
uv run pyright src/ tests/ scripts/
331377

332378
# Deployment
333379

docker/app-manifest.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ manifestVersion: 1
22
info:
33
name: quilt-docker
44
description: Packaging Benchling Notebooks as Quilt packages
5-
version: 0.8.3
5+
version: 0.8.4
66
features:
77
- name: Quilt Connector
88
id: quilt_entry

docker/pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "benchling-quilt-integration"
7-
version = "0.8.3"
7+
version = "0.8.4"
88
description = "Benchling-Quilt Integration Webhook Service"
9-
license = {text = "MIT"}
9+
license = {text = "Apache-2.0"}
1010
authors = [
1111
{name = "Ernest Prabhakar", email = "ernest@quilt.bio"},
1212
]
@@ -15,7 +15,7 @@ classifiers = [
1515
"Environment :: Web Environment",
1616
"Framework :: Flask",
1717
"Intended Audience :: Developers",
18-
"License :: OSI Approved :: MIT License",
18+
"License :: OSI Approved :: Apache Software License",
1919
"Operating System :: OS Independent",
2020
"Programming Language :: Python :: 3",
2121
"Programming Language :: Python :: 3.11",
@@ -45,6 +45,7 @@ dev = [
4545
"black==25.11.0",
4646
"flake8==7.3.0",
4747
"isort==7.0.0",
48+
"pyright==1.1.407",
4849
]
4950
docs = [
5051
"sphinx==8.2.3",

docker/pyrightconfig.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
3+
"include": [
4+
"src",
5+
"tests",
6+
"scripts"
7+
],
8+
"exclude": [
9+
"**/__pycache__",
10+
"**/.pytest_cache",
11+
"**/node_modules",
12+
"**/.venv",
13+
"**/venv"
14+
],
15+
"pythonVersion": "3.11",
16+
"pythonPlatform": "All",
17+
"typeCheckingMode": "basic",
18+
"reportMissingImports": true,
19+
"reportMissingTypeStubs": false,
20+
"reportGeneralTypeIssues": true,
21+
"reportIncompatibleMethodOverride": true,
22+
"reportIncompatibleVariableOverride": true,
23+
"reportUntypedFunctionDecorator": false,
24+
"reportUntypedClassDecorator": false,
25+
"reportUntypedBaseClass": false,
26+
"reportUnusedImport": true,
27+
"reportUnusedClass": true,
28+
"reportUnusedFunction": true,
29+
"reportUnusedVariable": true,
30+
"reportDuplicateImport": true,
31+
"reportOptionalSubscript": true,
32+
"reportOptionalMemberAccess": true,
33+
"reportOptionalCall": true,
34+
"reportOptionalIterable": true,
35+
"reportOptionalContextManager": true,
36+
"reportOptionalOperand": true,
37+
"stubPath": "",
38+
"venvPath": ".",
39+
"venv": ".venv"
40+
}

docker/scripts/docker.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,14 +208,15 @@ def build(self, tag: str, platform: Optional[str] = None, version: Optional[str]
208208

209209
os.chdir(self.project_root)
210210

211-
# Build docker build command
212-
build_cmd = ["docker", "build", "--platform", build_platform, "--file", "Dockerfile"]
211+
# Build docker buildx command (required for multi-platform builds and validation)
212+
build_cmd = ["docker", "buildx", "build", "--platform", build_platform, "--file", "Dockerfile"]
213213

214214
# Add VERSION build arg if provided
215215
if version:
216216
build_cmd.extend(["--build-arg", f"VERSION={version}"])
217217

218-
build_cmd.extend(["--tag", tag, "."])
218+
# Use --load to load the image into Docker (instead of just building to cache)
219+
build_cmd.extend(["--load", "--tag", tag, "."])
219220

220221
result = self._run_command(build_cmd, check=False)
221222

docker/scripts/experiment_search_syntax.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# Add src to path for local imports
1717
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
18-
from config import get_config
18+
from config import get_config # type: ignore
1919

2020
# Known values to search for
2121
ENTRY_ID = "etr_EK1AQMQiQn"

0 commit comments

Comments
 (0)