|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import os |
| 4 | +import shutil |
| 5 | +import tempfile |
| 6 | + |
| 7 | +from devenv.lib import archive |
| 8 | +from devenv.lib import fs |
| 9 | +from devenv.lib import proc |
| 10 | + |
| 11 | + |
| 12 | +def _install(url: str, sha256: str, into: str) -> None: |
| 13 | + with tempfile.TemporaryDirectory(dir=into) as tmpd: |
| 14 | + archive_file = archive.download(url, sha256, dest=f"{tmpd}/download") |
| 15 | + archive.unpack(archive_file, tmpd, perform_strip1=True) |
| 16 | + |
| 17 | + # the archive was atomically placed into tmpd so |
| 18 | + # these are on the same fs and can be atomically moved too |
| 19 | + os.replace(f"{tmpd}/uv", f"{into}/uv") |
| 20 | + os.replace(f"{tmpd}/uvx", f"{into}/uvx") |
| 21 | + |
| 22 | + |
| 23 | +def uninstall(binroot: str) -> None: |
| 24 | + for fp in (f"{binroot}/uv", f"{binroot}/uvx"): |
| 25 | + try: |
| 26 | + os.remove(fp) |
| 27 | + except FileNotFoundError: |
| 28 | + # it's better to do this than to guard with |
| 29 | + # os.path.exists(fp) because if it's an invalid or circular |
| 30 | + # symlink the result'll be False! |
| 31 | + pass |
| 32 | + |
| 33 | + |
| 34 | +def _version(binpath: str) -> str: |
| 35 | + stdout = proc.run((binpath, "--version"), stdout=True) |
| 36 | + # uv 0.7.21 (77c771c7f 2025-07-14) |
| 37 | + return stdout.split()[1] |
| 38 | + |
| 39 | + |
| 40 | +def install(version: str, url: str, sha256: str, reporoot: str) -> None: |
| 41 | + binroot = fs.ensure_binroot(reporoot) |
| 42 | + binpath = f"{binroot}/uv" |
| 43 | + |
| 44 | + if shutil.which("uv", path=binroot) == binpath: |
| 45 | + installed_version = _version(binpath) |
| 46 | + if version == installed_version: |
| 47 | + return |
| 48 | + print(f"installed uv {installed_version} is unexpected!") |
| 49 | + |
| 50 | + print(f"installing uv {version}...") |
| 51 | + uninstall(binroot) |
| 52 | + _install(url, sha256, binroot) |
| 53 | + |
| 54 | + installed_version = _version(binpath) |
| 55 | + if version != installed_version: |
| 56 | + raise SystemExit("Failed to install uv {version}!") |
0 commit comments