fix(bundle): resolve file:// download_url via the file-URL helper#3344
fix(bundle): resolve file:// download_url via the file-URL helper#3344jawwad-ali wants to merge 2 commits into
Conversation
_download_manifest built the local path from raw parsed.path, which keeps the leading slash of file:///C:/x (yielding a \C:\x path that never exists on Windows) and skips percent-decoding (my%20bundles stays encoded on every OS) — so a catalog entry whose download_url is the canonical URI Python itself produces via Path.as_uri() always fails with 'Bundle manifest not found'. Route the file scheme through the existing bundler.services.adapters._file_url_to_path helper, which already handles drive letters, UNC hosts, and percent-decoding for catalog file:// URLs (make_catalog_fetcher). The bare-path branch is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for the careful writeup — the diagnosis is genuinely good. You correctly spotted that Digging into this against the broader catalog architecture, though, we've realized the fix should go the other direction — and that's on us, not you. The real problem is that Context: Spec Kit has four catalog systems — bundles, extensions, presets, workflows — and the other three deliberately enforce HTTPS-only for catalog and download URLs (HTTP only for localhost). Local/dev sources go through a separate, explicit route: a real filesystem path (e.g. The bundle command already follows that convention — local installs are the positional path argument: specify bundle install ./path/to/bundle.yml # or a bundle dir, or a .zipThat's handled by Rather than close this, would you be up for pivoting the PR to remove that branch instead of repairing it? You've already built the ideal harness for it. Concretely:
If you'd rather not take it that direction, no problem at all — we can close this and track the removal separately, still crediting your report. But since you did the hard part (finding the divergence), we'd love to have you land the fix. 🙏 Happy to answer questions on any of the above. |
|
Thanks @mnriem — that's a much better framing, and I'm happy to pivot. HTTPS-only catalog One wrinkle I hit while prototyping the removal, so we can decide scope deliberately: dropping the whole So there are two ways to land this, and I'd like your call:
I've got the harness ready for either and can turn it around quickly. My lean is A (fixes the scheme divergence + the Windows/percent-encoding bug you confirmed, with zero collateral), but if you'd rather have the strict HTTPS-only contract everywhere I'm glad to do B and handle the (AI-assisted: analysis + prototyping with Claude Code under my direction; I verified the 3-test breakage locally before writing this.) |
|
I would prefer route B and then we probably should validate all catalog systems work similarly |
…TPS-only Per maintainer review (route B): file:// in a catalog download_url was never intended — catalog URLs are HTTPS-only (http for localhost) across the extensions/presets/workflows catalog systems, and disk installs go through the positional path (specify bundle install <path>), handled by _local_manifest_source before catalog resolution. Remove the file:///bare-path branch from _download_manifest and route everything through _download_remote_manifest (HTTPS-only via _require_https), with an actionable error pointing at the positional install. Invert the file:// tests to assert rejection (+ a positional-path resolution test), and migrate the three bundle-info contract tests off local download_urls onto an HTTPS-only entry with a mocked manifest fetch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Done — pushed route B in Implementation:
On "validate all catalog systems work similarly" — I audited the other three, and the good news is they already enforce HTTPS on both the catalog URL and the per-entry download URL:
Bundle's (AI-assisted: implemented + audited with Claude Code under my direction; verified the full contract/local-install suites locally.) |
Description
A catalog entry whose
download_urlis afile://URL — the canonical URI Python itself produces viaPath.as_uri()— always fails to resolve:Raw
parsed.pathhas two problems:file:///C:/Users/x/bundle.yml→parsed.path=/C:/Users/x/bundle.yml→Path(...)=\C:\Users\x\bundle.yml, a leading-slash drive path that never exists.file:///home/user/my%20bundles/bundle.ymlkeeps the literal%20.Reproduced on main @
bba473c:The repo already fixed this exact class of bug for catalog
file://URLs:bundler/services/adapters.py::_file_url_to_pathpercent-decodes viaurl2pathnameand preservesnetlocfor UNC/drive URLs (used bymake_catalog_fetcher)._download_manifestis the one call site that never got the same treatment.Fix
Route the
filescheme through the existing helper (lazy import, matching the file's style); the bare-path/Windows-drive branch keepsPath(url)unchanged.Testing
test_download_manifest_resolves_file_url: manifest undermy bundles/(space in dirname so the percent-encoding failure reproduces on POSIX CI too),download_url=path.as_uri()→ resolvesbundle.id. Fails before (Bundle manifest not found: \C:\...my%20bundles\..., verified by source-stash), passes after.test_download_manifest_bare_windows_path_still_resolves: pins the non-URL branch (passes before and after).tests/integration/test_bundler_local_install.py: 8 passed.uvx ruff checkclean.AI Disclosure
Found and fixed with Claude Code (Claude Fable 5) under my direction. AI identified the raw-
parsed.pathdivergence from the adapters helper; I reproduced the failure on Windows, verified fail-before/pass-after, and reviewed the diff before submitting.