From 2432dce0f2599a803518331d6d4a2ee6ac2ffe11 Mon Sep 17 00:00:00 2001 From: Noritaka Sekiyama Date: Thu, 10 Sep 2026 09:26:18 +0900 Subject: [PATCH 1/3] fix: normalize package URLs in check_uv_lock_public_urls --fix `--fix` rewrote the 114 source registries but left the 1512 artifact URLs pointing at whichever index produced the lock, so on a mirrored index the check it runs immediately afterwards still failed and the URLs had to be substituted by hand. Only the host is rewritten, and only for URLs shaped like the public artifact tree: the /packages/ path identifies the artifact and the hash recorded beside it is what guarantees integrity. A URL without that path is left for the check to report rather than rewritten on a guess. Co-authored-by: Isaac --- scripts/check_uv_lock_public_urls.py | 35 ++++++-- tests/unit/test_check_uv_lock_public_urls.py | 90 ++++++++++++++++++++ 2 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 tests/unit/test_check_uv_lock_public_urls.py diff --git a/scripts/check_uv_lock_public_urls.py b/scripts/check_uv_lock_public_urls.py index 94340d76c..e59796b65 100644 --- a/scripts/check_uv_lock_public_urls.py +++ b/scripts/check_uv_lock_public_urls.py @@ -10,9 +10,14 @@ PUBLIC_REGISTRY = "https://pypi.org/simple" PUBLIC_PACKAGE_URL_PREFIX = "https://files.pythonhosted.org/packages/" +PUBLIC_PACKAGE_HOST = PUBLIC_PACKAGE_URL_PREFIX.removesuffix("/packages/") REGISTRY_PATTERN = re.compile(r'registry = "([^"]+)"') SOURCE_REGISTRY_PATTERN = re.compile(r'(source = \{ registry = ")([^"]+)(" \})') URL_PATTERN = re.compile(r'url = "([^"]+)"') +# A mirror serves the same artifact tree, so only the host differs: the /packages/ path and +# the hash recorded next to it are identical to public PyPI's. Anything not shaped like that +# is left alone for the check to report rather than rewritten on a guess. +MIRROR_PACKAGE_URL_PATTERN = re.compile(r'(url = ")https?://[^"/]+(/packages/[^"]+")') def check_uv_lock(lockfile: Path) -> list[str]: @@ -30,21 +35,31 @@ def check_uv_lock(lockfile: Path) -> list[str]: return failures -def fix_uv_lock(lockfile: Path) -> int: +def fix_uv_lock(lockfile: Path) -> tuple[int, int]: contents = lockfile.read_text() - change_count = 0 + registry_count = 0 + url_count = 0 def normalize_registry(match: re.Match[str]) -> str: - nonlocal change_count + nonlocal registry_count if match.group(2) == PUBLIC_REGISTRY: return match.group(0) - change_count += 1 + registry_count += 1 return f"{match.group(1)}{PUBLIC_REGISTRY}{match.group(3)}" + def normalize_package_url(match: re.Match[str]) -> str: + nonlocal url_count + rewritten = f"{match.group(1)}{PUBLIC_PACKAGE_HOST}{match.group(2)}" + if rewritten == match.group(0): + return match.group(0) + url_count += 1 + return rewritten + normalized = SOURCE_REGISTRY_PATTERN.sub(normalize_registry, contents) - if change_count: + normalized = MIRROR_PACKAGE_URL_PATTERN.sub(normalize_package_url, normalized) + if registry_count or url_count: lockfile.write_text(normalized) - return change_count + return registry_count, url_count def main() -> int: @@ -63,8 +78,12 @@ def main() -> int: return 1 if args.fix: - change_count = fix_uv_lock(lockfile) - print(f"Normalized {change_count} source registry entries in {lockfile}", flush=True) + registry_count, url_count = fix_uv_lock(lockfile) + print( + f"Normalized {registry_count} source registry entries and " + f"{url_count} package URLs in {lockfile}", + flush=True, + ) failures = check_uv_lock(lockfile) if failures: diff --git a/tests/unit/test_check_uv_lock_public_urls.py b/tests/unit/test_check_uv_lock_public_urls.py new file mode 100644 index 000000000..1f6d0d0dc --- /dev/null +++ b/tests/unit/test_check_uv_lock_public_urls.py @@ -0,0 +1,90 @@ +import importlib.util +import sys +from pathlib import Path + +_SCRIPTS_DIR = Path(__file__).resolve().parents[2] / "scripts" +if str(_SCRIPTS_DIR) not in sys.path: + sys.path.insert(0, str(_SCRIPTS_DIR)) + +_SCRIPT = _SCRIPTS_DIR / "check_uv_lock_public_urls.py" +_spec = importlib.util.spec_from_file_location("check_uv_lock_public_urls", _SCRIPT) +assert _spec is not None and _spec.loader is not None +checker = importlib.util.module_from_spec(_spec) +sys.modules["check_uv_lock_public_urls"] = checker +_spec.loader.exec_module(checker) + +MIRROR = "https://pypi-proxy.example.com" +PACKAGE_PATH = "/packages/29/77/6f5df1c68bf/agate-1.9.1.tar.gz" + +PUBLIC_LOCK = f"""\ +[[package]] +name = "agate" +version = "1.9.1" +source = {{ registry = "https://pypi.org/simple" }} +sdist = {{ url = "https://files.pythonhosted.org{PACKAGE_PATH}", hash = "sha256:abc" }} +""" + +MIRROR_LOCK = f"""\ +[[package]] +name = "agate" +version = "1.9.1" +source = {{ registry = "{MIRROR}/simple" }} +sdist = {{ url = "{MIRROR}{PACKAGE_PATH}", hash = "sha256:abc" }} +""" + + +class TestCheckUvLock: + def test_public_lock_passes(self, tmp_path): + lockfile = tmp_path / "uv.lock" + lockfile.write_text(PUBLIC_LOCK) + assert checker.check_uv_lock(lockfile) == [] + + def test_mirror_registry_and_url_both_reported(self, tmp_path): + lockfile = tmp_path / "uv.lock" + lockfile.write_text(MIRROR_LOCK) + failures = checker.check_uv_lock(lockfile) + assert len(failures) == 2 + assert any("registry URL is not public PyPI" in failure for failure in failures) + assert any("package URL is not public PyPI" in failure for failure in failures) + + +class TestFixUvLock: + def test_fix_normalizes_registry_and_package_url(self, tmp_path): + lockfile = tmp_path / "uv.lock" + lockfile.write_text(MIRROR_LOCK) + + registry_count, url_count = checker.fix_uv_lock(lockfile) + + assert (registry_count, url_count) == (1, 1) + assert lockfile.read_text() == PUBLIC_LOCK + assert checker.check_uv_lock(lockfile) == [] + + def test_fix_is_a_no_op_on_a_public_lock(self, tmp_path): + lockfile = tmp_path / "uv.lock" + lockfile.write_text(PUBLIC_LOCK) + + assert checker.fix_uv_lock(lockfile) == (0, 0) + assert lockfile.read_text() == PUBLIC_LOCK + + def test_fix_preserves_the_artifact_path_and_hash(self, tmp_path): + """Only the host may change: the /packages/ path identifies the artifact and the hash + next to it is what actually guarantees integrity.""" + lockfile = tmp_path / "uv.lock" + lockfile.write_text(MIRROR_LOCK) + + checker.fix_uv_lock(lockfile) + + contents = lockfile.read_text() + assert PACKAGE_PATH in contents + assert 'hash = "sha256:abc"' in contents + + def test_fix_leaves_urls_that_are_not_mirror_shaped(self, tmp_path): + """A URL without a /packages/ path is not a mirror of the public artifact tree, so + rewriting its host would be a guess. Leave it for the check to report.""" + lockfile = tmp_path / "uv.lock" + original = 'sdist = { url = "https://example.com/downloads/agate-1.9.1.tar.gz" }\n' + lockfile.write_text(original) + + assert checker.fix_uv_lock(lockfile) == (0, 0) + assert lockfile.read_text() == original + assert len(checker.check_uv_lock(lockfile)) == 1 From 96bba30b362bb6db8e495d89bae02ac884af8e1d Mon Sep 17 00:00:00 2001 From: Noritaka Sekiyama Date: Thu, 10 Sep 2026 09:46:13 +0900 Subject: [PATCH 2/3] docs: changelog entry for the uv.lock --fix URL normalization Co-authored-by: Isaac --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 468953f51..1a64076cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Under the Hood - Document serverless environment configuration for Python models (thanks @TangoEnSkai!) ([#1649](https://github.com/databricks/dbt-databricks/pull/1649) resolves [#1055](https://github.com/databricks/dbt-databricks/issues/1055)) +- Normalize artifact URLs as well as source registries in `check_uv_lock_public_urls.py --fix`, so regenerating `uv.lock` against a mirrored index no longer needs a manual edit (tooling-only, no runtime impact) ([#1675](https://github.com/databricks/dbt-databricks/pull/1675)) ## dbt-databricks 1.12.5 (Sep 1, 2026) From 515805061546a875c276ba29be3ce22dfb598741 Mon Sep 17 00:00:00 2001 From: Shubham Dhal Date: Thu, 10 Sep 2026 12:42:09 +0530 Subject: [PATCH 3/3] docs: clarify uv lock fix help text --- scripts/check_uv_lock_public_urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_uv_lock_public_urls.py b/scripts/check_uv_lock_public_urls.py index e59796b65..3e67f306b 100644 --- a/scripts/check_uv_lock_public_urls.py +++ b/scripts/check_uv_lock_public_urls.py @@ -67,7 +67,7 @@ def main() -> int: parser.add_argument( "--fix", action="store_true", - help="normalize source registries before checking the lock file", + help="normalize source registries and package URLs before checking the lock file", ) parser.add_argument("lockfile", nargs="?", type=Path, default=Path("uv.lock")) args = parser.parse_args()