mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
eval_cli: Make things a bit more resilient to different Docker envs (#52731)
Release Notes: - N/A
This commit is contained in:
parent
64315583c8
commit
0d5504e3d4
4 changed files with 213 additions and 107 deletions
|
|
@ -2309,7 +2309,7 @@ impl ConversationView {
|
|||
|
||||
fn play_notification_sound(&self, window: &Window, cx: &mut App) {
|
||||
let settings = AgentSettings::get_global(cx);
|
||||
let visible = window.is_window_active()
|
||||
let _visible = window.is_window_active()
|
||||
&& if let Some(mw) = window.root::<MultiWorkspace>().flatten() {
|
||||
self.agent_panel_visible(&mw, cx)
|
||||
} else {
|
||||
|
|
@ -2317,7 +2317,8 @@ impl ConversationView {
|
|||
.upgrade()
|
||||
.is_some_and(|workspace| AgentPanel::is_visible(&workspace, cx))
|
||||
};
|
||||
if settings.play_sound_when_agent_done && !visible {
|
||||
#[cfg(feature = "audio")]
|
||||
if settings.play_sound_when_agent_done && !_visible {
|
||||
Audio::play_sound(Sound::AgentDone, cx);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,8 @@ RUN rustup toolchain install 1.94.1 --profile minimal \
|
|||
# libraries (libgit2-sys, zstd-sys, libsqlite3-sys). No audio/GUI -dev
|
||||
# packages required — eval-cli runs headless with those features disabled.
|
||||
#
|
||||
# cargo-zigbuild cross-compiles against a specific glibc version (2.31 =
|
||||
# Debian Bullseye / Ubuntu Focal) so the resulting binary is portable to
|
||||
# any Linux distro with glibc >= 2.31.
|
||||
# cargo-zigbuild cross-compiles against musl libc, producing a fully
|
||||
# static binary that runs on any Linux distro (glibc or musl / Alpine).
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
cmake \
|
||||
build-essential \
|
||||
|
|
@ -43,8 +42,8 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
|||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
--mount=type=cache,target=/app/target \
|
||||
cargo zigbuild --release --package eval_cli \
|
||||
--target x86_64-unknown-linux-gnu.2.31 && \
|
||||
cp /app/target/x86_64-unknown-linux-gnu/release/eval-cli /eval-cli && \
|
||||
--target x86_64-unknown-linux-musl && \
|
||||
cp /app/target/x86_64-unknown-linux-musl/release/eval-cli /eval-cli && \
|
||||
strip /eval-cli
|
||||
|
||||
FROM scratch
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build eval-cli for x86_64 Linux from any host (macOS, Linux, etc.)
|
||||
# using Docker + cargo-zigbuild. Targets glibc 2.31 (Debian Bullseye /
|
||||
# Ubuntu Focal) so the binary is portable to any modern Linux distro.
|
||||
# using Docker + cargo-zigbuild. Targets musl libc, producing a fully
|
||||
# static binary that runs on any Linux distro (glibc or musl / Alpine).
|
||||
# The resulting binary is placed at the path printed on completion
|
||||
# (default: target/eval-cli).
|
||||
#
|
||||
|
|
@ -38,7 +38,7 @@ cd "$REPO_ROOT"
|
|||
|
||||
IMAGE_TAG="eval-cli-builder"
|
||||
|
||||
echo "Building eval-cli for x86_64-unknown-linux-gnu (glibc >= 2.31)..."
|
||||
echo "Building eval-cli for x86_64-unknown-linux-musl (static binary)..."
|
||||
echo " Repo root: $REPO_ROOT"
|
||||
echo " Output: $OUTPUT"
|
||||
echo ""
|
||||
|
|
|
|||
|
|
@ -84,110 +84,37 @@ class ZedAgent(BaseInstalledAgent):
|
|||
return workdir
|
||||
|
||||
async def install(self, environment: BaseEnvironment) -> None:
|
||||
# Detect the package manager and install base dependencies.
|
||||
# Supports Debian/Ubuntu (apt-get), Alpine (apk), and
|
||||
# Fedora/RHEL/CentOS (dnf/yum).
|
||||
await self.exec_as_root(
|
||||
environment,
|
||||
command=(
|
||||
"if command -v apt-get >/dev/null 2>&1; then "
|
||||
" apt-get update && "
|
||||
"apt-get install -y --no-install-recommends "
|
||||
"ca-certificates "
|
||||
"curl "
|
||||
"git"
|
||||
),
|
||||
env={"DEBIAN_FRONTEND": "noninteractive"},
|
||||
)
|
||||
|
||||
await self.exec_as_root(
|
||||
environment,
|
||||
command=(
|
||||
"curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && "
|
||||
"apt-get install -y --no-install-recommends nodejs"
|
||||
),
|
||||
env={"DEBIAN_FRONTEND": "noninteractive"},
|
||||
)
|
||||
|
||||
# Pre-install default LSPs so Zed doesn't have to download them at
|
||||
# runtime. Each gets its own subdirectory under $ZED_DATA_DIR/languages.
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
"set -euo pipefail; "
|
||||
'ZED_DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/zed"; '
|
||||
# basedpyright (Python - default type checker)
|
||||
'BASEDPYRIGHT_DIR="$ZED_DATA_DIR/languages/basedpyright"; '
|
||||
'mkdir -p "$BASEDPYRIGHT_DIR"; '
|
||||
'npm install --prefix "$BASEDPYRIGHT_DIR" --save-exact basedpyright; '
|
||||
# typescript-language-server (TypeScript/JS - default LSP)
|
||||
'TSSERVER_DIR="$ZED_DATA_DIR/languages/typescript-language-server"; '
|
||||
'mkdir -p "$TSSERVER_DIR"; '
|
||||
'npm install --prefix "$TSSERVER_DIR" --save-exact typescript typescript-language-server; '
|
||||
# vtsls (VS Code TypeScript language features)
|
||||
'VTSLS_DIR="$ZED_DATA_DIR/languages/vtsls"; '
|
||||
'mkdir -p "$VTSLS_DIR"; '
|
||||
'npm install --prefix "$VTSLS_DIR" --save-exact @vtsls/language-server typescript; '
|
||||
# tailwindcss-language-server
|
||||
'TAILWIND_DIR="$ZED_DATA_DIR/languages/tailwindcss-language-server"; '
|
||||
'mkdir -p "$TAILWIND_DIR"; '
|
||||
'npm install --prefix "$TAILWIND_DIR" --save-exact @tailwindcss/language-server'
|
||||
),
|
||||
)
|
||||
|
||||
# eslint LSP (downloaded from zed-industries/vscode-eslint GitHub release,
|
||||
# then compiled — this mirrors what Zed does at runtime).
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
"set -euo pipefail; "
|
||||
'ZED_DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/zed"; '
|
||||
'ESLINT_DIR="$ZED_DATA_DIR/languages/eslint/vscode-eslint-2.4.4"; '
|
||||
'mkdir -p "$ESLINT_DIR"; '
|
||||
'curl -fsSL "https://github.com/zed-industries/vscode-eslint/archive/refs/tags/release/2.4.4.tar.gz" '
|
||||
'| tar -xz -C "$ESLINT_DIR"; '
|
||||
'mv "$ESLINT_DIR"/vscode-eslint-release-2.4.4 "$ESLINT_DIR/vscode-eslint"; '
|
||||
'cd "$ESLINT_DIR/vscode-eslint" && npm install && npm run compile'
|
||||
),
|
||||
)
|
||||
|
||||
# gopls (Go - default LSP). Only install when Go is present in the
|
||||
# container (i.e. Go-related SWE-bench tasks).
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
"if command -v go >/dev/null 2>&1; then "
|
||||
"go install golang.org/x/tools/gopls@latest; "
|
||||
" apt-get install -y --no-install-recommends ca-certificates curl git; "
|
||||
"elif command -v apk >/dev/null 2>&1; then "
|
||||
" apk add --no-cache ca-certificates curl git bash coreutils gcompat libstdc++; "
|
||||
"elif command -v dnf >/dev/null 2>&1; then "
|
||||
" dnf install -y ca-certificates curl git; "
|
||||
"elif command -v yum >/dev/null 2>&1; then "
|
||||
" yum install -y ca-certificates curl git; "
|
||||
"else "
|
||||
" echo 'WARNING: No supported package manager found (apt-get, apk, dnf, yum)' >&2; "
|
||||
"fi"
|
||||
),
|
||||
env={"DEBIAN_FRONTEND": "noninteractive"},
|
||||
)
|
||||
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
"curl -LsSf https://astral.sh/uv/install.sh | sh && "
|
||||
'. "$HOME/.local/bin/env"'
|
||||
),
|
||||
)
|
||||
# ── Non-essential tooling ─────────────────────────────────────
|
||||
# Everything below here (Node.js, LSPs, uv/ruff) is nice-to-have.
|
||||
# If any step fails (e.g. musl incompatibility, network issues),
|
||||
# log a warning and continue — the agent can still work without
|
||||
# pre-installed language servers.
|
||||
|
||||
agent_home_result = await self.exec_as_agent(
|
||||
environment,
|
||||
command='printf %s "$HOME"',
|
||||
)
|
||||
agent_home = agent_home_result.stdout.strip()
|
||||
if not agent_home:
|
||||
raise RuntimeError("Could not determine agent home directory")
|
||||
|
||||
await self.exec_as_root(
|
||||
environment,
|
||||
command=(
|
||||
f"ln -sf {shlex.quote(agent_home + '/.local/bin/uv')} /usr/local/bin/uv && "
|
||||
f"ln -sf {shlex.quote(agent_home + '/.local/bin/uvx')} /usr/local/bin/uvx"
|
||||
),
|
||||
)
|
||||
|
||||
# Install a modern ruff so `ruff server` works without --preview.
|
||||
# This also makes it available as a CLI tool for the agent.
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=('export PATH="$HOME/.local/bin:$PATH" && uv tool install ruff'),
|
||||
)
|
||||
await self._install_node(environment)
|
||||
await self._install_lsps(environment)
|
||||
await self._install_uv_and_ruff(environment)
|
||||
|
||||
if self._binary_path:
|
||||
binary = Path(self._binary_path)
|
||||
|
|
@ -224,6 +151,183 @@ class ZedAgent(BaseInstalledAgent):
|
|||
"or set download_url=/EVAL_CLI_DOWNLOAD_URL."
|
||||
)
|
||||
|
||||
async def _install_node(self, environment: BaseEnvironment) -> None:
|
||||
"""Install Node.js from official binary tarballs.
|
||||
|
||||
Uses the musl build on Alpine and the glibc build elsewhere.
|
||||
Skips if node is already on PATH.
|
||||
"""
|
||||
try:
|
||||
await self.exec_as_root(
|
||||
environment,
|
||||
command=(
|
||||
"if command -v node >/dev/null 2>&1; then "
|
||||
' echo "Node.js already available: $(node --version)"; '
|
||||
"else "
|
||||
" NODE_VER=v22.14.0; "
|
||||
" ARCH=$(uname -m); "
|
||||
' case "$ARCH" in '
|
||||
" x86_64) NODE_ARCH=x64 ;; "
|
||||
" aarch64) NODE_ARCH=arm64 ;; "
|
||||
' *) echo "WARNING: unsupported arch $ARCH for Node.js" >&2; exit 0 ;; '
|
||||
" esac; "
|
||||
" if ldd /bin/sh 2>&1 | grep -qi musl; then "
|
||||
' NODE_URL="https://unofficial-builds.nodejs.org/download/release/${NODE_VER}/node-${NODE_VER}-linux-${NODE_ARCH}-musl.tar.gz"; '
|
||||
" else "
|
||||
' NODE_URL="https://nodejs.org/dist/${NODE_VER}/node-${NODE_VER}-linux-${NODE_ARCH}.tar.gz"; '
|
||||
" fi; "
|
||||
' echo "Downloading Node.js from $NODE_URL"; '
|
||||
' curl -fsSL "$NODE_URL" | tar -xz -C /usr/local --strip-components=1; '
|
||||
' echo "Installed Node.js $(node --version)"; '
|
||||
"fi"
|
||||
),
|
||||
)
|
||||
except Exception as exc:
|
||||
self.logger.warning("Node.js installation failed (non-fatal): %s", exc)
|
||||
|
||||
async def _install_lsps(self, environment: BaseEnvironment) -> None:
|
||||
"""Pre-install language servers so Zed doesn't download them at runtime.
|
||||
|
||||
Each LSP is installed independently so one failure doesn't block the rest.
|
||||
"""
|
||||
# npm-based LSPs — skip all if npm is not available.
|
||||
try:
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command="command -v npm >/dev/null 2>&1",
|
||||
)
|
||||
except Exception:
|
||||
self.logger.warning("npm not available — skipping npm-based LSP installs")
|
||||
return
|
||||
|
||||
lsp_installs = [
|
||||
(
|
||||
"basedpyright",
|
||||
'DIR="$ZED_DATA_DIR/languages/basedpyright"; '
|
||||
'mkdir -p "$DIR" && npm install --prefix "$DIR" --save-exact basedpyright',
|
||||
),
|
||||
(
|
||||
"typescript-language-server",
|
||||
'DIR="$ZED_DATA_DIR/languages/typescript-language-server"; '
|
||||
'mkdir -p "$DIR" && npm install --prefix "$DIR" --save-exact typescript typescript-language-server',
|
||||
),
|
||||
(
|
||||
"vtsls",
|
||||
'DIR="$ZED_DATA_DIR/languages/vtsls"; '
|
||||
'mkdir -p "$DIR" && npm install --prefix "$DIR" --save-exact @vtsls/language-server typescript',
|
||||
),
|
||||
(
|
||||
"tailwindcss-language-server",
|
||||
'DIR="$ZED_DATA_DIR/languages/tailwindcss-language-server"; '
|
||||
'mkdir -p "$DIR" && npm install --prefix "$DIR" --save-exact @tailwindcss/language-server',
|
||||
),
|
||||
]
|
||||
|
||||
for name, cmd in lsp_installs:
|
||||
try:
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
'ZED_DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/zed"; '
|
||||
+ cmd
|
||||
),
|
||||
)
|
||||
except Exception as exc:
|
||||
self.logger.warning(
|
||||
"LSP install '%s' failed (non-fatal): %s", name, exc
|
||||
)
|
||||
|
||||
# eslint — downloaded from GitHub and compiled separately.
|
||||
try:
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
"set -euo pipefail; "
|
||||
'ZED_DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/zed"; '
|
||||
'ESLINT_DIR="$ZED_DATA_DIR/languages/eslint/vscode-eslint-2.4.4"; '
|
||||
'mkdir -p "$ESLINT_DIR"; '
|
||||
'curl -fsSL "https://github.com/zed-industries/vscode-eslint/archive/refs/tags/release/2.4.4.tar.gz" '
|
||||
'| tar -xz -C "$ESLINT_DIR"; '
|
||||
'mv "$ESLINT_DIR"/vscode-eslint-release-2.4.4 "$ESLINT_DIR/vscode-eslint"; '
|
||||
'cd "$ESLINT_DIR/vscode-eslint" && npm install && npm run compile'
|
||||
),
|
||||
)
|
||||
except Exception as exc:
|
||||
self.logger.warning("eslint LSP install failed (non-fatal): %s", exc)
|
||||
|
||||
# gopls — only when Go is present. Guarded by a 120s timeout so slow
|
||||
# compilation can never eat the full setup budget.
|
||||
gopls_script = (
|
||||
"if command -v go >/dev/null 2>&1; then "
|
||||
"if go install golang.org/x/tools/gopls@latest 2>/dev/null; then "
|
||||
"echo 'Installed gopls@latest'; "
|
||||
"else "
|
||||
' MY_GO=$(go env GOVERSION | sed "s/^go//"); '
|
||||
" for v in $(curl -fsSL "
|
||||
"https://proxy.golang.org/golang.org/x/tools/gopls/@v/list 2>/dev/null"
|
||||
" | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+$' | sort -rV | head -5); do "
|
||||
" NEED=$(curl -fsSL "
|
||||
'"https://proxy.golang.org/golang.org/x/tools/gopls/@v/${v}.mod"'
|
||||
" 2>/dev/null | awk '/^go /{print $2; exit}'); "
|
||||
' if [ -n "$NEED" ] '
|
||||
' && [ "$(printf \'%s\\n%s\\n\' "$NEED" "$MY_GO" '
|
||||
' | sort -V | head -1)" = "$NEED" ]; then '
|
||||
' echo "Installing gopls $v (compatible with Go $MY_GO)"; '
|
||||
' go install "golang.org/x/tools/gopls@$v" && break; '
|
||||
" fi; "
|
||||
" done; "
|
||||
"fi; "
|
||||
"fi"
|
||||
)
|
||||
try:
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
"timeout 120 bash -c "
|
||||
+ shlex.quote(gopls_script)
|
||||
+ " || echo 'WARNING: gopls installation timed out or failed -- skipping'"
|
||||
),
|
||||
)
|
||||
except Exception as exc:
|
||||
self.logger.warning("gopls install failed (non-fatal): %s", exc)
|
||||
|
||||
async def _install_uv_and_ruff(self, environment: BaseEnvironment) -> None:
|
||||
"""Install uv and ruff for Python tooling."""
|
||||
try:
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
"curl -LsSf https://astral.sh/uv/install.sh | sh && "
|
||||
'. "$HOME/.local/bin/env"'
|
||||
),
|
||||
)
|
||||
|
||||
agent_home_result = await self.exec_as_agent(
|
||||
environment,
|
||||
command='printf %s "$HOME"',
|
||||
)
|
||||
agent_home = agent_home_result.stdout.strip()
|
||||
if not agent_home:
|
||||
self.logger.warning(
|
||||
"Could not determine agent home directory — skipping uv symlinks"
|
||||
)
|
||||
return
|
||||
|
||||
await self.exec_as_root(
|
||||
environment,
|
||||
command=(
|
||||
f"ln -sf {shlex.quote(agent_home + '/.local/bin/uv')} /usr/local/bin/uv && "
|
||||
f"ln -sf {shlex.quote(agent_home + '/.local/bin/uvx')} /usr/local/bin/uvx"
|
||||
),
|
||||
)
|
||||
|
||||
await self.exec_as_agent(
|
||||
environment,
|
||||
command='export PATH="$HOME/.local/bin:$PATH" && uv tool install ruff',
|
||||
)
|
||||
except Exception as exc:
|
||||
self.logger.warning("uv/ruff installation failed (non-fatal): %s", exc)
|
||||
|
||||
def populate_context_post_run(self, context: AgentContext) -> None:
|
||||
result_data = None
|
||||
for json_file in self.logs_dir.rglob("result.json"):
|
||||
|
|
@ -315,7 +419,9 @@ class ZedAgent(BaseInstalledAgent):
|
|||
await self.exec_as_agent(
|
||||
environment,
|
||||
command=(
|
||||
" ".join(parts) + " 2>&1 | stdbuf -oL tee /logs/agent/eval-cli.txt"
|
||||
" ".join(parts) + " 2>&1 | if command -v stdbuf >/dev/null 2>&1;"
|
||||
" then stdbuf -oL tee /logs/agent/eval-cli.txt;"
|
||||
" else tee /logs/agent/eval-cli.txt; fi"
|
||||
),
|
||||
env=env,
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue