Commit ddc42c9
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
File tree
36 files changed
+1647
-866
lines changed- bin
- commands
- docker
- scripts
- src
- tests
- lib/types
- test/bin/commands
36 files changed
+1647
-866
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
8 | 23 | | |
9 | 24 | | |
10 | 25 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
| 63 | + | |
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | | - | |
| 104 | + | |
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| |||
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
125 | | - | |
| 125 | + | |
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
| |||
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
177 | | - | |
| 177 | + | |
178 | 178 | | |
179 | 179 | | |
180 | 180 | | |
| |||
254 | 254 | | |
255 | 255 | | |
256 | 256 | | |
257 | | - | |
| 257 | + | |
258 | 258 | | |
259 | 259 | | |
260 | 260 | | |
| |||
300 | 300 | | |
301 | 301 | | |
302 | 302 | | |
303 | | - | |
| 303 | + | |
304 | 304 | | |
305 | 305 | | |
306 | 306 | | |
| |||
362 | 362 | | |
363 | 363 | | |
364 | 364 | | |
365 | | - | |
| 365 | + | |
366 | 366 | | |
367 | 367 | | |
368 | 368 | | |
| |||
377 | 377 | | |
378 | 378 | | |
379 | 379 | | |
380 | | - | |
| 380 | + | |
381 | 381 | | |
382 | 382 | | |
383 | 383 | | |
| |||
414 | 414 | | |
415 | 415 | | |
416 | 416 | | |
417 | | - | |
| 417 | + | |
418 | 418 | | |
419 | 419 | | |
420 | 420 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
877 | 877 | | |
878 | 878 | | |
879 | 879 | | |
880 | | - | |
881 | | - | |
882 | | - | |
883 | | - | |
884 | | - | |
885 | | - | |
886 | | - | |
887 | | - | |
888 | | - | |
889 | | - | |
890 | | - | |
891 | | - | |
892 | | - | |
| 880 | + | |
893 | 881 | | |
894 | 882 | | |
895 | 883 | | |
896 | 884 | | |
897 | | - | |
| 885 | + | |
898 | 886 | | |
899 | 887 | | |
900 | 888 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
6 | 10 | | |
7 | 11 | | |
8 | 12 | | |
| |||
36 | 40 | | |
37 | 41 | | |
38 | 42 | | |
39 | | - | |
| 43 | + | |
40 | 44 | | |
41 | 45 | | |
42 | | - | |
| 46 | + | |
43 | 47 | | |
44 | 48 | | |
45 | 49 | | |
| |||
55 | 59 | | |
56 | 60 | | |
57 | 61 | | |
58 | | - | |
| 62 | + | |
59 | 63 | | |
60 | 64 | | |
61 | 65 | | |
| |||
135 | 139 | | |
136 | 140 | | |
137 | 141 | | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
138 | 155 | | |
139 | 156 | | |
140 | 157 | | |
| |||
152 | 169 | | |
153 | 170 | | |
154 | 171 | | |
155 | | - | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
156 | 185 | | |
157 | 186 | | |
158 | | - | |
| 187 | + | |
159 | 188 | | |
160 | 189 | | |
161 | 190 | | |
| |||
303 | 332 | | |
304 | 333 | | |
305 | 334 | | |
| 335 | + | |
| 336 | + | |
306 | 337 | | |
307 | 338 | | |
308 | 339 | | |
309 | 340 | | |
310 | 341 | | |
311 | 342 | | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
312 | 349 | | |
313 | 350 | | |
314 | 351 | | |
315 | 352 | | |
316 | 353 | | |
317 | 354 | | |
318 | 355 | | |
| 356 | + | |
| 357 | + | |
319 | 358 | | |
320 | 359 | | |
321 | 360 | | |
322 | 361 | | |
323 | 362 | | |
324 | 363 | | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
325 | 370 | | |
326 | 371 | | |
327 | | - | |
| 372 | + | |
328 | 373 | | |
329 | 374 | | |
330 | 375 | | |
| 376 | + | |
331 | 377 | | |
332 | 378 | | |
333 | 379 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
48 | 49 | | |
49 | 50 | | |
50 | 51 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
208 | 208 | | |
209 | 209 | | |
210 | 210 | | |
211 | | - | |
212 | | - | |
| 211 | + | |
| 212 | + | |
213 | 213 | | |
214 | 214 | | |
215 | 215 | | |
216 | 216 | | |
217 | 217 | | |
218 | | - | |
| 218 | + | |
| 219 | + | |
219 | 220 | | |
220 | 221 | | |
221 | 222 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| |||
0 commit comments