diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8cfe1f152..cb15e8583 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1066,6 +1066,29 @@ jobs: run: | (& ./uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression + # Check for accidental cache layout and cache key changes + integration-test-cache-layout: + timeout-minutes: 10 + needs: build-binary-linux-libc + name: "integration test | cache layout" + runs-on: ubuntu-latest + if: ${{ !contains(github.event.pull_request.labels.*.name, 'cache-change') }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Download binary + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: uv-linux-libc-${{ github.sha }} + + - name: Prepare binary + run: chmod +x ./uv + + - name: Check for cache layout changes + run: ./uv run scripts/check-cache-changes/check-cache-changes.py ./uv + integration-test-nushell: timeout-minutes: 10 needs: build-binary-linux-libc diff --git a/scripts/check-cache-changes/check-cache-changes.py b/scripts/check-cache-changes/check-cache-changes.py new file mode 100644 index 000000000..e04b7a560 --- /dev/null +++ b/scripts/check-cache-changes/check-cache-changes.py @@ -0,0 +1,60 @@ +""" +Check that the uv cache keys and layout didn't change accidentally. + +This check may fail when intentionally changing the cache layout, it is intended to +catch accidental modification of cache keys. +""" + +import os +import shutil +import sys +from argparse import ArgumentParser +from pathlib import Path +from subprocess import CalledProcessError, check_call + + +def main(): + parser = ArgumentParser() + parser.add_argument("new_uv", help="The new uv binary") + parser.add_argument( + "--old-version", + help="Optionally, compare to a specific uv version instead of the latest version", + ) + + args = parser.parse_args() + # Ensure the uv path, if relative, has the right root + new_uv = Path(os.getcwd()).joinpath(args.new_uv) + work_dir = Path(__file__).parent + cache_dir = work_dir.joinpath(".cache") + if cache_dir.is_dir(): + shutil.rmtree(cache_dir) + env = {"UV_CACHE_DIR": str(cache_dir), **os.environ} + + if args.old_version: + old_uv = f"uv@{args.old_version}" + else: + old_uv = "uv@latest" + + # Prime the cache + print(f"\nPriming the cache with {old_uv}\n") + check_call([new_uv, "tool", "run", old_uv, "sync"], cwd=work_dir, env=env) + # This should always pass, even if the cache changed + print(f"\nUsing the cache offline with {old_uv}\n") + shutil.rmtree(work_dir.joinpath(".venv")) + check_call( + [new_uv, "tool", "run", old_uv, "sync", "--offline"], cwd=work_dir, env=env + ) + # Check that the new uv version can use the old cache + print(f"\nUsing the cache offline with {new_uv}\n") + shutil.rmtree(work_dir.joinpath(".venv")) + try: + check_call([new_uv, "sync", "--offline"], cwd=work_dir, env=env) + except CalledProcessError: + print( + 'Cache layout changed. If this is intentional, add the "cache-change" label to your PR' + ) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/check-cache-changes/pyproject.toml b/scripts/check-cache-changes/pyproject.toml new file mode 100644 index 000000000..285e9093b --- /dev/null +++ b/scripts/check-cache-changes/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "check-cache-changes" +version = "0.1.0" +requires-python = ">=3.12" +dependencies = [ + "anyio>=4.12.0", + "package", + "tqdm", +] + +[tool.uv.sources] +tqdm = { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl" } +package = { git = "https://github.com/astral-sh/uv-dynamic-metadata-test", rev = "6c5aa0a65db737c9e7e2e60dc865bd8087012e64" } +# TODO(konsti): Add a case with extra build dependencies +# TODO(konsti): Add a case with config settings diff --git a/scripts/check-cache-changes/uv.lock b/scripts/check-cache-changes/uv.lock new file mode 100644 index 000000000..62e3ac91a --- /dev/null +++ b/scripts/check-cache-changes/uv.lock @@ -0,0 +1,112 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "anyio" +version = "4.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" }, +] + +[[package]] +name = "check-cache-changes" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "anyio" }, + { name = "package" }, + { name = "tqdm" }, +] + +[package.metadata] +requires-dist = [ + { name = "anyio", specifier = ">=4.12.0" }, + { name = "package", git = "https://github.com/astral-sh/uv-dynamic-metadata-test?rev=6c5aa0a65db737c9e7e2e60dc865bd8087012e64" }, + { name = "tqdm", url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "dependency" +version = "0.1.0" +source = { git = "https://github.com/astral-sh/uv-dynamic-metadata-test?subdirectory=dependency&rev=6c5aa0a65db737c9e7e2e60dc865bd8087012e64#6c5aa0a65db737c9e7e2e60dc865bd8087012e64" } +dependencies = [ + { name = "iniconfig" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "package" +version = "0.1.0" +source = { git = "https://github.com/astral-sh/uv-dynamic-metadata-test?rev=6c5aa0a65db737c9e7e2e60dc865bd8087012e64#6c5aa0a65db737c9e7e2e60dc865bd8087012e64" } +dependencies = [ + { name = "dependency" }, + { name = "typing-extensions" }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2" }, +] + +[package.metadata] +requires-dist = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ipywidgets", marker = "extra == 'notebook'", specifier = ">=6" }, + { name = "nbval", marker = "extra == 'dev'" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=6" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24" }, + { name = "pytest-cov", marker = "extra == 'dev'" }, + { name = "pytest-timeout", marker = "extra == 'dev'" }, + { name = "requests", marker = "extra == 'discord'" }, + { name = "requests", marker = "extra == 'telegram'" }, + { name = "slack-sdk", marker = "extra == 'slack'" }, +] +provides-extras = ["dev", "discord", "slack", "telegram", "notebook"] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +]