Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/bootstrap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

# devenv no longer manages uv; linux users need to install it themselves
- uses: astral-sh/setup-uv@884ad927a57e558e7a70b92f2bccf9198a4be546 # v6
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: '0.12.10'
version: latest

- name: install
run: |
Expand Down
8 changes: 3 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-python@7f4fc3e22c37d6ff65e88745f38bd3157c663f7c # v4
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
python-version: 3.11
version: latest
- name: "Prepare Artifacts"
run: |
pip install build
python -m build
run: uv build
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ github.sha }}
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:
"${{ github.event.pull_request && github.head_ref || github.ref_name }}"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# devenv doesn't manage uv; use the latest
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: latest
- name: install devenv
run: |
set -u
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- run: uv sync
- uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507 # v3.0.0
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- run: uv sync
- run: uv run coverage erase && uv run coverage run -m pytest && uv run coverage report
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ repos:
entry: .venv/bin/mypy
language: system
types: [python]
# parallel mypy processes share .mypy_cache, and mypy 2 can crash
# with an INTERNAL ERROR when they race on it
require_serial: true
- id: shellcheck
name: shellcheck
entry: .venv/bin/shellcheck
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ Keep the file name `install-devenv.sh`. The script only runs its installer when

The script installs the global devenv to `~/.local/share/sentry-devenv/bin/devenv`.

devenv uses [uv](https://docs.astral.sh/uv/) to install itself and to manage virtualenvs; it does not use pip.
Install the latest uv first with `brew install uv`. If the script can't find uv, it installs the latest uv to `~/.local/bin` with uv's official installer.

To update the global devenv, run `devenv update`.

## Commands
Expand Down Expand Up @@ -68,7 +71,7 @@ Builtin checks live in `devenv.checks`. A repo can add its own checks in the `ch
Updates the global devenv installation and the global tools. Pass a version to install that version.

If your devenv is old enough that it has no `update` command, run:
`~/.local/share/sentry-devenv/venv/bin/pip install -U sentry-devenv`
`uv pip install --python ~/.local/share/sentry-devenv/venv/bin/python -U sentry-devenv`

`devenv colima start`

Expand All @@ -90,8 +93,9 @@ Everything devenv needs is in `~/.local/share/sentry-devenv`.

### Runtime

`devenv` is installed in a virtualenv at `~/.local/share/sentry-devenv/venv`.
`devenv` is installed with uv in a virtualenv at `~/.local/share/sentry-devenv/venv`.
That virtualenv uses the Python at `~/.local/share/sentry-devenv/python`.
It has no pip; `devenv update` uses `uv pip` to update it.

### Global configuration

Expand Down
2 changes: 1 addition & 1 deletion ci/devenv-bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -euxo pipefail

# we don't have permissions to clone getsentry which is a good thing
# eventually we should move this bootstrap testing over to getsentry repo
# in the meantime, mock it so that pip install -e has something to chew on
# in the meantime, mock it so that the editable install has something to chew on
mkdir -p "$HOME/code/getsentry"
cat <<EOF > "$HOME/code/getsentry/pyproject.toml"
[project]
Expand Down
24 changes: 24 additions & 0 deletions devenv/lib/uv.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,34 @@
import shutil
import tempfile

from devenv import constants
from devenv.lib import archive
from devenv.lib import fs
from devenv.lib import proc

install_help = """\
uv is required but wasn't found on PATH.
Install it with `brew install uv`, or see
https://docs.astral.sh/uv/getting-started/installation/
"""


def find() -> str:
"""Returns the path to the uv on PATH.

Falls back to ~/.local/bin/uv, which is where astral's installer
(and install-devenv.sh) puts uv, in case that isn't on PATH yet.
"""
found = shutil.which("uv")
if found is not None:
return found

fallback = f"{constants.home}/.local/bin/uv"
if os.access(fallback, os.X_OK):
return fallback

raise SystemExit(install_help)


def _install(url: str, sha256: str, into: str) -> None:
with tempfile.TemporaryDirectory(dir=into) as tmpd:
Expand Down
31 changes: 20 additions & 11 deletions devenv/lib/venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from devenv.lib import config
from devenv.lib import fs
from devenv.lib import proc
from devenv.lib import uv

VenvStatus = Enum(
"VenvStatus", ("OK", "VERSION_MISMATCH", "NOT_PRESENT", "NOT_CONFIGURED")
Expand Down Expand Up @@ -57,14 +58,12 @@ def sync(
bins: Optional[tuple[str, ...]] = None,
) -> None:
cmd: tuple[str, ...] = (
f"{venv_dir}/bin/python",
"-m",
uv.find(),
"pip",
"--disable-pip-version-check",
"--no-color",
"--quiet",
"--require-virtualenv",
"install",
"--quiet",
"--python",
f"{venv_dir}/bin/python",
"-r",
requirements,
)
Expand Down Expand Up @@ -92,10 +91,14 @@ def check(venv: str, python_version: str) -> VenvStatus:

with open(f"{venv}/pyvenv.cfg", "r") as f:
for line in f:
if line.startswith("version"):
venv_version = line.split("=")[1].strip()
if venv_version != python_version:
return VenvStatus.VERSION_MISMATCH
key, sep, value = line.partition("=")
# python -m venv writes `version = 3.11.6`, uv writes
# `version_info = 3.11.6` (or e.g. `3.11.6.final.0`)
if not sep or key.strip() not in ("version", "version_info"):
continue
venv_version = ".".join(value.strip().split(".")[:3])
if venv_version != python_version:
return VenvStatus.VERSION_MISMATCH

return VenvStatus.OK

Expand All @@ -112,7 +115,13 @@ def ensure(venv: str, python_version: str, url: str, sha256: str) -> None:
shutil.rmtree(venv)

proc.run(
(pythons.get(python_version, url, sha256), "-m", "venv", venv),
(
uv.find(),
"venv",
"--python",
pythons.get(python_version, url, sha256),
venv,
),
Comment thread
cursor[bot] marked this conversation as resolved.
exit=True,
)

Expand Down
15 changes: 10 additions & 5 deletions devenv/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from devenv.lib import docker
from devenv.lib import limactl
from devenv.lib import proc
from devenv.lib import uv
from devenv.lib.context import Context
from devenv.lib.modules import DevModuleInfo

Expand Down Expand Up @@ -61,11 +62,15 @@ def main(context: Context, argv: Sequence[str] | None = None) -> int:
version = f"sentry-devenv=={args.version}"

proc.run(
(f"{constants.root}/venv/bin/pip", "install", "-U", version),
env={
# better than cli flag (who knows if it'll break in the future)
"PIP_DISABLE_PIP_VERSION_CHECK": "1"
},
(
uv.find(),
"pip",
"install",
"--python",
f"{constants.root}/venv/bin/python",
"--upgrade",
version,
)
)

print(f"""\
Expand Down
26 changes: 23 additions & 3 deletions install-devenv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
# ./install-devenv.sh

# shellcheck disable=SC2016 # I know that single-quotes are hardquotes
# remembered so we can find a uv the user already installed (e.g. via brew)
ORIG_PATH="${PATH:-}"
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
ME="$(basename "$0")"

Expand Down Expand Up @@ -168,6 +170,21 @@ install_python() {
ln -sfn "$SNTY_DEVENV_HOME/python${version}" "$SNTY_DEVENV_HOME/python"
}

find_uv() {
PATH="$ORIG_PATH:${XDG_BIN_HOME:-$HOME/.local/bin}:/opt/homebrew/bin:/usr/local/bin" \
command -v uv
}

ensure_uv() {
# devenv doesn't manage uv, but it needs one to install itself.
if ! uv="$(find_uv)"; then
info "uv not found, installing the latest uv (https://docs.astral.sh/uv/)..."
http-get https://astral.sh/uv/install.sh | sh
uv="$(find_uv)" || error "Failed to install uv; please install it yourself (brew install uv) and try again."
fi
show "$uv" --version
}

main() {
constants
parseopt "$@"
Expand All @@ -176,14 +193,17 @@ main() {
devenv_python="$SNTY_DEVENV_HOME/python/bin/python3"

info "Installing dependencies..."
ensure_uv
install_python "$SNTY_DEVENV_PY_RELEASE" "$SNTY_DEVENV_PY_VERSION"

show "$devenv_python" -m venv --clear "$devenv_venv"
rm -rf "$devenv_venv"
show "$uv" venv --python "$devenv_python" "$devenv_venv"

if [[ "${CI:-}" ]]; then
show "$devenv_venv"/bin/pip install "git+$SNTY_DEVENV_REPO@$SNTY_DEVENV_BRANCH"
show "$uv" pip install --python "$devenv_venv/bin/python" \
"git+$SNTY_DEVENV_REPO@$SNTY_DEVENV_BRANCH"
else
show "$devenv_venv"/bin/pip install \
show "$uv" pip install --python "$devenv_venv/bin/python" \
--index-url https://pypi.devinfra.sentry.io/simple \
sentry-devenv
fi
Expand Down
1 change: 0 additions & 1 deletion lib/gh-act/hostedtoolcache-python.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ ln -sfT "$src" "$dst"
touch "$dst.complete" # actions/setup-python keys on this file's existence

ls -l "$dst/bin"
"$dst"/bin/python -m pip install --upgrade pip
39 changes: 39 additions & 0 deletions tests/lib/test_uv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from __future__ import annotations

import os
import pathlib
from unittest.mock import patch

import pytest

from devenv.lib import uv


def test_find_on_path() -> None:
with patch("devenv.lib.uv.shutil.which", return_value="/opt/bin/uv"):
assert uv.find() == "/opt/bin/uv"


def test_find_falls_back_to_local_bin(tmp_path: pathlib.Path) -> None:
os.makedirs(f"{tmp_path}/.local/bin")
binpath = f"{tmp_path}/.local/bin/uv"
with open(binpath, "w"):
pass
os.chmod(binpath, 0o755)

with (
patch("devenv.lib.uv.shutil.which", return_value=None),
patch("devenv.lib.uv.constants.home", str(tmp_path)),
):
assert uv.find() == binpath


def test_find_missing(tmp_path: pathlib.Path) -> None:
with (
patch("devenv.lib.uv.shutil.which", return_value=None),
patch("devenv.lib.uv.constants.home", str(tmp_path)),
pytest.raises(SystemExit) as excinfo,
):
uv.find()

assert "brew install uv" in str(excinfo.value)
Loading
Loading