fix(bundle): wrap malformed YAML in a local .zip bundle manifest - #4013
Merged
Merged
Conversation
`_local_manifest_source` handles three local bundle sources. The directory
and `bundle.yml` branches both go through `BundleManifest.from_file` ->
`load_yaml`, which converts a parse failure into a `BundlerError`. The `.zip`
branch instead parses inline with a bare `_yaml.safe_load`.
`yaml.YAMLError` derives directly from `Exception` -- it is neither a
`ValueError` nor an `OSError` -- so it escapes `bundle_install`'s
`except BundlerError` and reaches the user as a raw
`yaml.parser.ParserError` traceback.
The remote counterpart of this same call, `_download_manifest`, already
guards it and even names `_yaml.YAMLError` explicitly. Only the local zip
path was missed, so the same corrupt manifest is reported cleanly when
fetched from a catalog but crashes when installed from disk.
Before, for the identical malformed bundle.yml:
specify bundle install ./bundle-dir -> Error: Invalid YAML in ... (exit 1)
specify bundle install ./bundle.yml -> Error: Invalid YAML in ... (exit 1)
specify bundle install ./bundle.zip -> ParserError traceback
Two regression tests: one pins the `BundlerError` contract on the zip
branch, and one drives all three local sources through the CLI to assert
they now fail alike.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Wraps malformed YAML errors from local ZIP bundle manifests in BundlerError, aligning ZIP installation with other local sources.
Changes:
- Catches
yaml.YAMLErrorwhen parsing ZIP manifests. - Adds unit and CLI regression coverage across all local source types.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/commands/bundle/__init__.py |
Converts ZIP manifest YAML failures into actionable CLI errors. |
tests/integration/test_bundler_local_install.py |
Verifies consistent malformed-YAML handling. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Balanced
Collaborator
|
Thank you! |
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
_local_manifest_sourceaccepts three local bundle sources. Two of them reach YAML throughBundleManifest.from_file→load_yaml, which wraps a parse failure:The
.zipbranch parses inline instead, with no guard:yaml.YAMLErrorderives directly fromException— it is neither aValueErrornor anOSError:So it slips past
bundle_install'sexcept BundlerErrorand surfaces as a raw traceback.Reproduction
python -c "import zipfile; zipfile.ZipFile('bad.zip','w').writestr('bundle.yml','bundle: [unclosed\n id: x\n')" specify bundle install ./bad.zipFor the identical malformed
bundle.yml, the three local sources disagree:./bundle-dirError: Invalid YAML in bundle.yml: ...— exit 1./bundle.ymlError: Invalid YAML in standalone.yml: ...— exit 1./bundle.zipyaml.parser.ParserErrortracebackWhy this is the local path's bug, not a missing global handler
The remote counterpart of this exact call —
_download_manifest, which parses a manifest fetched from a catalog — already handles it, and even names the exception explicitly:So the same corrupt manifest is reported cleanly when it arrives over the network but crashes when installed from disk. This fix brings the local branch in line with both its in-function siblings and its remote twin.
Tests
Two regression tests in
tests/integration/test_bundler_local_install.py:test_local_zip_wraps_malformed_manifest_yaml— pins theBundlerErrorcontract on the zip branch.test_malformed_manifest_yaml_fails_alike_for_every_local_source— drives all three local sources through the CLI and asserts they now fail identically. This is the one that encodes the actual invariant; onmainit fails withartifact.zip leaked ParserErrorwhile the other two sources pass.Both fail before the change and pass after.
tests/integration/test_bundler_local_install.py+tests/contract/test_bundle_cli.py: 49 passed.ruff checkclean.🤖 Generated with Claude Code