Skip to main content

devenv

managing dev environments since '24

devenv is an extensible execution framework and library for authoring a simple set of high level commands - bootstrap, fetch, sync, doctor - that manage a repository's dev environment.

Prerequisites

If you are a Sentry employee, make sure your GitHub account is in a getsentry/engineering team. If it is not, open an IT ticket before you continue.

If you are not a Sentry employee, set the SENTRY_EXTERNAL_CONTRIBUTOR environment variable. devenv bootstrap and devenv fetch both read it.

Install

Download install-devenv.sh and run it:

curl https://raw.githubusercontent.com/getsentry/devenv/main/install-devenv.sh > install-devenv.sh
bash install-devenv.sh

Keep the file name install-devenv.sh. The script only runs its installer when it has that name.

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

devenv uses 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

Run devenv --help for the full list.

devenv bootstrap

Sets up a new machine. It writes the global configuration, configures GitHub SSH access, and installs the global tools.

devenv fetch [repository name]

Clones any GitHub repository in the form [org]/[reponame] over SSH into your coderoot, then runs devenv sync in it. If you omit the org, it defaults to getsentry.

sentry and ops are special names:

  • devenv fetch sentry sets up getsentry/sentry, and also getsentry/getsentry unless SENTRY_EXTERNAL_CONTRIBUTOR is set.
  • devenv fetch ops sets up getsentry/ops and clones getsentry/terraform-modules.

devenv sync

Runs the repo's sync.py from the repo config directory. sync.py defines main(context: dict[str, str]) -> int. The context keys are reporoot, repo, and coderoot. sync.py should:

  • install any tools the repo needs
  • create the dev environment, or bring it up to date

sync.py runs in devenv's runtime, so it can import the devenv.lib modules. There are no API docs. Start from the examples. If you have a feature request, open an issue.

The library keeps a repo's dev environment inside [reporoot]/.devenv where it can. For example, gcloud is installed to [reporoot]/.devenv/bin/gcloud, with the gcloud SDK at [reporoot]/.devenv/bin/google-cloud-sdk. Python virtualenvs are an exception because they predate [reporoot]/.devenv.

devenv doctor

Diagnoses and fixes common issues. Use --check-only to skip fixes, and --tag or --exclude-tag to select checks.

Builtin checks live in devenv.checks. A repo can add its own checks in the checks directory of its repo config directory.

devenv update [version]

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: uv pip install --python ~/.local/share/sentry-devenv/venv/bin/python -U sentry-devenv

devenv colima start

Starts or creates devenv's Colima VM with resources sized for your machine. Existing VM settings are kept. The other subcommands are restart, check, and stop.

On affected Apple M4/M5 systems, this and devenv colima restart check whether the VM kernel reports SVE support the CPU does not have. Devenv upgrades the VM kernel when needed, restarts once, and verifies the result. devenv doctor checks this too.

devenv pin_gha [file ...]

Pins each uses: reference in the given GitHub Actions workflow files to a commit SHA.

Technical overview

Everything devenv needs is in ~/.local/share/sentry-devenv.

~/.local/share/sentry-devenv/bin contains:

  • devenv itself
  • direnv, if direnv was not already on your PATH. devenv relies on direnv and a minimal [reporoot]/.envrc to add [reporoot]/.devenv/bin to PATH.
  • on macOS only, the global tools: the docker CLI, colima, and lima. On Linux, install docker engine yourself. devenv bootstrap exits if it is missing.

Runtime

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

~/.config/sentry-devenv/config.ini

[devenv]
# the parent directory of all devenv-managed repos
coderoot = ~/code

Repository configuration

devenv reads config.ini, sync.py, and checks from the repo config directory. The repo config directory is [reporoot]/.devenv if [reporoot]/.devenv/config.ini exists. Otherwise, it is [reporoot]/devenv if [reporoot]/devenv/config.ini exists. If neither file exists, it is [reporoot]/.devenv. The examples below use [reporoot]/devenv.

[reporoot]/devenv/config.ini

[devenv]
# optionally require a minimum version to run sync.py
minimum_version = 1.22.1

The examples show the other sections.

Examples

Skip to:

direnv

Every repo needs a minimal [reporoot]/.envrc:

if [[ -f "${PWD}/.env" ]]; then
    dotenv
fi

PATH_add "${HOME}/.local/share/sentry-devenv/bin"

if ! command -v devenv >/dev/null; then
    echo "install devenv: https://github.com/getsentry/devenv#install"
    return 1
fi

PATH_add "${PWD}/.devenv/bin"

python

devenv does not manage uv installations. Run brew install uv.

To activate the virtualenv with direnv and run uv sync as part of devenv sync:

[reporoot]/.envrc

export VIRTUAL_ENV="${PWD}/.venv"

PATH_add "${PWD}/.venv/bin"

[reporoot]/devenv/sync.py

from devenv.lib import proc


def main(context: dict[str, str]) -> int:
    # reporoot/.venv is the default venv location
    print(f"syncing .venv ...")
    proc.run(("uv", "sync", "--frozen", "--quiet"))

    return 0

Use [reporoot]/.python-version to pin a python version for uv:

3.13.16

node

node.install_pnpm installs the pnpm version named in the packageManager field of [reporoot]/package.json.

[reporoot]/.envrc

PATH_add "${PWD}/node_modules/.bin"

[reporoot]/devenv/sync.py

from devenv import constants
from devenv.lib import config, node, proc


def main(context: dict[str, str]) -> int:
    reporoot = context["reporoot"]
    cfg = config.get_repo(reporoot)

    node.install(
        cfg["node"]["version"],
        cfg["node"][constants.SYSTEM_MACHINE],
        cfg["node"][f"{constants.SYSTEM_MACHINE}_sha256"],
        reporoot,
    )

    node.install_pnpm(reporoot)

    print("installing node dependencies...")
    proc.run(
        (
            f"{reporoot}/.devenv/bin/pnpm",
            "install",
            "--frozen-lockfile",
            "--reporter=append-only",
        )
    )

    return 0

If you want a different node version, first fill in the matching URLs from https://nodejs.org/dist/ in config.ini. Then ask dev-infra to mirror them to GCS.

[reporoot]/devenv/config.ini

[node]
# upstream (https://nodejs.org/dist/) is not reliable enough so we've mirrored it to GCS
darwin_x86_64 = https://storage.googleapis.com/sentry-dev-infra-assets/node/node-v20.13.1-darwin-x64.tar.xz
darwin_x86_64_sha256 = c83bffeb4eb793da6cb61a44c422b399048a73d7a9c5eb735d9c7f5b0e8659b6
darwin_arm64 = https://storage.googleapis.com/sentry-dev-infra-assets/node/node-v20.13.1-darwin-arm64.tar.xz
darwin_arm64_sha256 = e8a8e78b91485bc95d20f2aa86201485593685c828ee609245ce21c5680d07ce
linux_x86_64 = https://storage.googleapis.com/sentry-dev-infra-assets/node/node-v20.13.1-linux-x64.tar.xz
linux_x86_64_sha256 = efc0f295dd878e510ab12ea36bbadc3db03c687ab30c07e86c7cdba7eed879a9
# used for autoupdate
version = v20.13.1

brew

[reporoot]/devenv/sync.py

from devenv import constants
from devenv.lib import brew, proc


def main(context: dict[str, str]) -> int:
    reporoot = context["reporoot"]

    brew.install()

    proc.run((f"{constants.homebrew_bin}/brew", "bundle"), cwd=reporoot)

    return 0

[reporoot]/Brewfile

# whatever you want, but we generally discourage installing
# things via brew as it's very difficult to pin a particular
# version of something

colima

devenv bootstrap and devenv update install Colima v0.10.3 and Lima v2.2.1 on macOS.

New macOS installations

Colima 0.10.3 creates new VMs from its Ubuntu 24.04.4 image, whose kernel is expected to report SVE correctly. M4 Pro validation confirmed this on that host. New VMs use a separate persistent 512 GiB container data disk and Docker's containerd image store. Inotify is off by default.

Existing macOS installations

Updating devenv keeps the existing VM, Docker images, containers, and named volumes. It does not recreate the VM or convert its disk layout. Colima 0.8.1 VMs keep Docker data on their root disk; newer VMs use a separate persistent data disk. Do not delete a legacy VM as routine cleanup: its containers and named volumes can be lost with its root disk.

devenv update does not stop a running VM. Before the next upgraded start, devenv checks a running daemon through the colima Docker context and records its store choice in ~/.colima/default/colima.yaml. For a stopped VM, it uses Colima's persisted store and Lima disk metadata to identify the layout. If the evidence is ambiguous, startup stops with instructions to set docker.features.containerd-snapshotter explicitly. Existing explicit values are preserved, and devenv does not remove Docker data.

Affected M4/M5 Macs may need an in-VM kernel update because existing VMs keep their original kernel. Devenv installs and boots the update when needed. If a previous repair installed the kernel but did not boot it, run devenv colima restart. This does not convert the VM to the new data disk layout.

Lima 2.1 changed the instance disk layout. After starting a VM with Lima 2.x, Lima 1.x cannot start that instance. Downgrading devenv or its binaries does not restore the old layout. See Lima's compatibility discussion.

Check or change the Docker image store

Run docker --context=colima info to see the active storage driver. The classic store reports overlay2. The containerd store reports io.containerd.snapshotter.v1 in the driver status. Use the explicit colima context so Docker Desktop's current context does not affect the result.

If images or containers appear missing after an upgrade, inspect docker --context=colima info, docker --context=colima images, docker --context=colima ps -a, and docker --context=colima volume ls. Check docker.features.containerd-snapshotter in ~/.colima/default/colima.yaml against the store containing your data. For a legacy overlay2 VM, use:

docker:
  features:
    buildkit: true
    containerd-snapshotter: false

Set it to true only after confirming the VM's data is in the containerd store. Start Colima after correcting the value. Switching stores hides images and containers in the other store. Do not delete the VM or its disks to diagnose the problem.

Enable inotify propagation

To propagate host file events into the VM, set mountInotify: true in ~/.colima/default/colima.yaml or start Colima with --mount-inotify. Devenv preserves an existing true setting. Inotify is off for new VMs because Colima watches configured mounts, including devenv's writable home mount. Watching a large home directory can increase host CPU and memory use; see Colima issue 1569.

gcloud

[reporoot]/devenv/sync.py

from devenv import constants
from devenv.lib import config, gcloud


def main(context: dict[str, str]) -> int:
    reporoot = context["reporoot"]
    cfg = config.get_repo(reporoot)

    gcloud.install(
        cfg["gcloud"]["version"],
        cfg["gcloud"][constants.SYSTEM_MACHINE],
        cfg["gcloud"][f"{constants.SYSTEM_MACHINE}_sha256"],
        reporoot,
    )

    return 0

[reporoot]/devenv/config.ini The SDK uses devenv's internal Python 3.13 because custom Python versions aren't supported yet.

[gcloud]
darwin_x86_64 = https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-490.0.0-darwin-x86_64.tar.gz
darwin_x86_64_sha256 = fa396909acc763cf831dd5d89e778999debf37ceadccb3c1bdec606e59ba2694
darwin_arm64 = https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-490.0.0-darwin-arm.tar.gz
darwin_arm64_sha256 = a3a098a5f067b561e003c37284a9b164f28f37fd0d6371bb55e326679f48641c
linux_x86_64 = https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-490.0.0-linux-x86_64.tar.gz
linux_x86_64_sha256 = 40ce41958236f76d9cb08f377ccb9fd6502d2df4da14b36d9214bcb620e2b020
# used for autoupdate
version = 490.0.0

terraform

devenv installs tenv and keeps TENV_ROOT at [reporoot]/.devenv/bin/tenv-root. It also writes tenv, terraform, tofu, and terragrunt shims to [reporoot]/.devenv/bin that use that TENV_ROOT. It does not write a tf shim, so a repo's own tf wrapper is not shadowed.

Define [reporoot]/.terraform-version, and optionally [reporoot]/.terragrunt-version. After devenv sync, run terraform and tenv installs the pinned version.

[reporoot]/devenv/sync.py

from devenv import constants
from devenv.lib import config, tenv


def main(context: dict[str, str]) -> int:
    reporoot = context["reporoot"]
    cfg = config.get_repo(reporoot)

    tenv.install(
        cfg["tenv"]["version"],
        cfg["tenv"][constants.SYSTEM_MACHINE],
        cfg["tenv"][f"{constants.SYSTEM_MACHINE}_sha256"],
        reporoot,
    )

    return 0

[reporoot]/devenv/config.ini

[tenv]
darwin_x86_64 = https://github.com/tofuutils/tenv/releases/download/v1.3.0/tenv_v1.3.0_Darwin_x86_64.tar.gz
darwin_x86_64_sha256 = 994100d26f4de6de4eebc7691ca4ea7b424e2fd73e6d5d77c5bf6dfd4af94752
darwin_arm64 =  https://github.com/tofuutils/tenv/releases/download/v1.3.0/tenv_v1.3.0_Darwin_arm64.tar.gz
darwin_arm64_sha256 = c31d2b8412147316a0cadb684408bc123e567852d7948091be7e4303fc19397a
# used for autoupdate
version = v1.3.0

Get help

Run devenv doctor first. If that does not fix it, see the devenv troubleshooting guide.

Metadata

Release files for sentry-devenv 1.30.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sentry-devenv 1.30.0
File Size Uploaded Provenance
sentry_devenv-1.30.0.tar.gz 77.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for sentry-devenv 1.30.0
File Interpreter ABI Platform
sentry_devenv-1.30.0-py3-none-any.whl Python 3 none any Details

Total release size: 178.7 kB

Release files / sentry_devenv-1.30.0.tar.gz

Download URL sentry_devenv-1.30.0.tar.gz
Size 77.8 kB
Tags Source
SHA-256 checksum
How to use checksums
6f108d32145176ef929c77e531d14aee16138f1fd5c0920320cc33acce82cd87
BLAKE2b-256 checksum
How to use checksums
436cfa4a0420099a1c2360a3299790abc0ffa6fb1c39e9a72540f43e93c64eb7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.2

Release files / sentry_devenv-1.30.0-py3-none-any.whl

Download URL sentry_devenv-1.30.0-py3-none-any.whl
Size 100.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
df1177fb4a59b75f4efd5e68531ccd25ae0019969bb6be366d6ad8d7493a0363
BLAKE2b-256 checksum
How to use checksums
722b0b7f3747bbf1989317dfa83aba3da0f3c52dad2d3b2eb48b6db3132b6633
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.2

Security for sentry-devenv 1.30.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

1.30.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page