Skip to content

Add custom ruff/ty binary paths with structured resolution#414

Merged
manzt merged 3 commits intomainfrom
manzt/custom-binaries
Mar 24, 2026
Merged

Add custom ruff/ty binary paths with structured resolution#414
manzt merged 3 commits intomainfrom
manzt/custom-binaries

Conversation

@manzt
Copy link
Collaborator

@manzt manzt commented Feb 17, 2026

Closes #408

Users in offline or restricted environments cannot use the extension's managed ruff/ty language servers because the uv pip install fallback requires internet access. This adds two new settings (marimo.ruff.path and marimo.ty.path) and a companion extension discovery mechanism so these users can point to locally-installed binaries instead.

New settings

  • marimo.ruff.path — absolute path to a custom ruff binary
  • marimo.ty.path — absolute path to a custom ty binary

Leave empty (default) to use automatic discovery.

Resolution strategy

Binary resolution follows this priority for both ruff and ty:

  1. User-configured pathmarimo.ruff.path / marimo.ty.path
  2. Companion extension — checks the charliermarsh.ruff or
    astral-sh.ty extension's own path setting (ruff.path /
    ty.path), then its bundled binary at
    <extensionPath>/bundled/libs/bin/<binary>
  3. uv pip install — existing behavior, unchanged

Each candidate is validated by running <binary> --version and checking it meets the minimum version (ruff >= 0.15.6, ty >= 0.0.23). If validation fails at any tier, resolution falls through to the next.

The resolution pipeline is generic: resolveBinary() accepts an ordered list of ResolutionSource values and a fallback, tries each in sequence, and returns a BinarySource tagged enum describing where the binary came from:

type BinarySource = Data.TaggedEnum<{
  UserConfigured: { path: string };
  CompanionExtension: {
    extensionId: string;
    path: string;
    kind: "configured" | "bundled";
  };
  UvInstalled: { path: string };
}>;

@manzt manzt force-pushed the manzt/custom-binaries branch 2 times, most recently from 4263f08 to b9e5589 Compare February 27, 2026 20:15
@manzt manzt force-pushed the manzt/custom-binaries branch from b9e5589 to 384623e Compare March 2, 2026 20:46
@nojaf
Copy link

nojaf commented Mar 16, 2026

Hi @manzt, we are running into this problem.
We have a /etc/uv/uv.toml with

index-url = "https://our.domain.internal/api/pypi/pypi-all/simple"
python-install-mirror = "https://our.domain.internal/generic-github-remote/astral-sh/python-build-standalone/releases/download"
native-tls = true

Will this PR use https://docs.astral.sh/uv/reference/settings/#index-url and not override it when

UV_DEFAULT_INDEX: "https://pypi.org/simple/",
gets set?

Related issue #437

@manzt manzt force-pushed the manzt/custom-binaries branch 2 times, most recently from c39bce5 to 671e255 Compare March 20, 2026 15:42
Closes #408

Users in offline environments cannot use the extension's managed ruff/ty
language servers because `uv pip install` tries to download packages
from the internet. This adds a 3-tier binary resolution strategy so
these users can point to locally-installed binaries instead.

Binary resolution now follows this priority for both ruff and ty:

1. User-configured path via `marimo.ruff.path` / `marimo.ty.path`
2. Companion extension discovery — checks the `charliermarsh.ruff` or
   `astral-sh.ty` extension's configured path setting, then its bundled
   binary at `<extensionPath>/bundled/libs/bin/<binary>`
3. Existing `uv pip install` fallback (unchanged behavior)

Each candidate binary is validated by running `<binary> --version` and
checking the result meets the minimum version requirement (ruff >=
0.15.0, ty >= 0.0.15). If validation fails at any tier, resolution falls
through to the next.
@manzt manzt force-pushed the manzt/custom-binaries branch 4 times, most recently from 4be5cca to 25c24c6 Compare March 24, 2026 15:43
The ruff and ty language servers resolve their binaries through a 3-tier
strategy (user-configured path, companion extension, uv install), but
previously each server implemented this inline with ad-hoc string labels
to identify which source was used. This made it hard to display
consistent diagnostics, track resolution paths in telemetry, or test the
fallthrough logic in isolation.

This introduces a `BinarySource` tagged enum (modeled after `UvBin`)
with three variants: `UserConfigured`, `CompanionExtension` (with a
`kind` field distinguishing "configured" vs "bundled"), and
`UvInstalled`. A generic `resolveBinary()` function tries composable
`ResolutionSource` values in order and returns the first match, with
structured log annotations on every attempt.

```ts
BinarySource.$match(source, {
  UserConfigured: ({ path }) => ...,
  CompanionExtension: ({ extensionId, path, kind }) => ...,
  UvInstalled: ({ path }) => ...,
});
```

The `Running` status for both language servers now carries the full
`BinarySource` value, which the health diagnostics format via exhaustive
`$match`. A telemetry event type `lsp_binary_resolved` is defined for
future instrumentation using `_tag` as the discriminant.
@manzt manzt force-pushed the manzt/custom-binaries branch from 25c24c6 to 69187df Compare March 24, 2026 15:49
The `lsp_binary_resolved` event type was defined but never captured.
This adds `reportBinaryResolved` to the `Telemetry` service and calls
it from both `RuffLanguageServer` and `TyLanguageServer` after the
server starts successfully. The event includes the server name, the
`BinarySource._tag` discriminant, the companion extension `kind` when
applicable, and the resolved server version — enough to see the
distribution of resolution paths across users.

```ts
telemetry.reportBinaryResolved("ruff", resolved, serverVersion)
```
@manzt manzt force-pushed the manzt/custom-binaries branch from 5f3f522 to 26a6e14 Compare March 24, 2026 16:16
@manzt manzt changed the title Add custom ruff/ty binary path support Add custom ruff/ty binary paths with structured resolution Mar 24, 2026
@manzt manzt added the enhancement New feature or request label Mar 24, 2026
@manzt
Copy link
Collaborator Author

manzt commented Mar 24, 2026

This PR doesn't fix the UV_DEFAULT_INDEX override (#437). That's a separate issue. But it does let you bypass uv pip install entirely for ruff/ty.

The easiest path forward: install the charliermarsh.ruff and astral-sh.ty companion extensions. The marimo extension will discover their bundled binaries automatically. Alternatively, install the binaries yourself and point to them:

{
  "marimo.ruff.path": "/usr/local/bin/ruff",
  "marimo.ty.path": "/usr/local/bin/ty"
}

@manzt manzt merged commit 10d02b9 into main Mar 24, 2026
8 of 9 checks passed
@manzt manzt deleted the manzt/custom-binaries branch March 24, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Vscode extension - support offline installation with local Python environment

2 participants