build: Allow pinning of webrtc build artifacts (✈️ edition) (#55336)

tl;dr: you can now run `cargo xtask setup_webrtc`, which:
1. Fetches webrtc artifacts into a gitignored directory in Zed repo
2. Adds a [env] section to `~/.cargo/config.toml` on your box which
forces
   LK to NOT download webrtc artifacts as a part of its build script

The end result: `cargo clean` is no longer a horrid experience with in
horrid network environments.

Caveats:
1. This does not handle appending to existing cargo config. The setup
   script will fail if there's one in place.
2. You need to redo this thing (fetch env var and whatnot) whenever LK
   version is bumped.
3. This is not mandatory for builds to work. You only really have to do
this for your own convenience, but builds will work just fine without it
(unless your connection sucks).

Self-Review Checklist:

- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Closes #ISSUE

Release Notes:

- N/A
This commit is contained in:
Piotr Osiewicz 2026-05-04 08:22:51 +02:00 committed by GitHub
parent ff8fa053ff
commit d4f06ee374
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 254 additions and 0 deletions

1
.gitignore vendored
View file

@ -2,6 +2,7 @@
**/*.proptest-regressions
**/cargo-target
**/target
.webrtc-sys/
**/venv
**/.direnv
*.wasm

View file

@ -21,6 +21,8 @@ enum CliCommand {
PackageConformity(tasks::package_conformity::PackageConformityArgs),
/// Publishes GPUI and its dependencies to crates.io.
PublishGpui(tasks::publish_gpui::PublishGpuiArgs),
/// Downloads the pinned `webrtc-sys` release and configures `LK_CUSTOM_WEBRTC`.
SetupWebrtc(tasks::setup_webrtc::SetupWebrtcArgs),
/// Builds GPUI web examples and serves them.
WebExamples(tasks::web_examples::WebExamplesArgs),
Workflows(tasks::workflows::GenerateWorkflowArgs),
@ -38,6 +40,7 @@ fn main() -> Result<()> {
tasks::package_conformity::run_package_conformity(args)
}
CliCommand::PublishGpui(args) => tasks::publish_gpui::run_publish_gpui(args),
CliCommand::SetupWebrtc(args) => tasks::setup_webrtc::run_setup_webrtc(args),
CliCommand::WebExamples(args) => tasks::web_examples::run_web_examples(args),
CliCommand::Workflows(args) => tasks::workflows::run_workflows(args),
CliCommand::CheckWorkflows(args) => tasks::workflow_checks::validate(args),

View file

@ -3,6 +3,7 @@ pub mod compliance;
pub mod licenses;
pub mod package_conformity;
pub mod publish_gpui;
pub mod setup_webrtc;
pub mod web_examples;
pub mod workflow_checks;
pub mod workflows;

View file

@ -0,0 +1,249 @@
#![allow(clippy::disallowed_methods, reason = "tooling is exempt")]
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{Context as _, Result, bail};
use cargo_toml::Manifest;
use clap::Parser;
use regex::Regex;
use toml_edit::{DocumentMut, Item, Table, value};
use crate::workspace::load_workspace;
const GITIGNORE_ENTRY: &str = ".webrtc-sys/";
const LOCAL_DIR_NAME: &str = ".webrtc-sys";
const ENV_VAR: &str = "LK_CUSTOM_WEBRTC";
#[derive(Parser)]
pub struct SetupWebrtcArgs {
/// Re-download even if the target directory already exists.
#[arg(long)]
force: bool,
/// Override the host triple component used for the release artifact
/// (e.g. `mac-arm64-release`). Defaults to the current host.
#[arg(long)]
triple: Option<String>,
/// Skip writing to `~/.cargo/config.toml`. Useful when you only want the
/// archive on disk and intend to set `LK_CUSTOM_WEBRTC` yourself.
#[arg(long)]
no_cargo_config: bool,
}
pub fn run_setup_webrtc(args: SetupWebrtcArgs) -> Result<()> {
let metadata = load_workspace()?;
let workspace_root = metadata.workspace_root.as_std_path().to_path_buf();
let rev = read_webrtc_sys_rev(&workspace_root)?;
eprintln!("Pinned livekit-rust-sdks rev: {rev}");
let tag = fetch_webrtc_tag(&rev)?;
eprintln!("WEBRTC_TAG for that rev: {tag}");
let triple = match args.triple {
Some(triple) => triple,
None => host_webrtc_triple()?,
};
eprintln!("Target triple: {triple}");
let local_root = workspace_root.join(LOCAL_DIR_NAME);
let tag_dir = local_root.join(&tag);
let extracted_dir = tag_dir.join(&triple);
if extracted_dir.exists() && !args.force {
eprintln!(
"Already present at {}, skipping download.",
extracted_dir.display()
);
} else {
if extracted_dir.exists() {
fs::remove_dir_all(&extracted_dir)
.with_context(|| format!("removing stale {}", extracted_dir.display()))?;
}
fs::create_dir_all(&tag_dir).with_context(|| format!("creating {}", tag_dir.display()))?;
download_and_extract(&tag, &triple, &tag_dir)?;
}
let absolute = extracted_dir
.canonicalize()
.with_context(|| format!("canonicalizing {}", extracted_dir.display()))?;
ensure_gitignore_entry(&workspace_root)?;
if args.no_cargo_config {
eprintln!(
"Skipping ~/.cargo/config.toml update. Set {ENV_VAR}={} yourself.",
absolute.display()
);
} else {
update_cargo_config(&absolute)?;
}
eprintln!();
eprintln!("Done. {ENV_VAR} -> {}", absolute.display());
Ok(())
}
fn read_webrtc_sys_rev(workspace_root: &Path) -> Result<String> {
let manifest_path = workspace_root.join("Cargo.toml");
let manifest = Manifest::from_path(&manifest_path)
.with_context(|| format!("parsing {}", manifest_path.display()))?;
let patch = manifest
.patch
.get("crates-io")
.context("workspace Cargo.toml has no [patch.crates-io] section")?;
let dep = patch
.get("webrtc-sys")
.context("[patch.crates-io] is missing webrtc-sys")?;
let detail = dep
.detail()
.context("webrtc-sys patch entry is not a table")?;
detail
.git
.as_ref()
.context("webrtc-sys patch is missing a git source")?;
detail
.rev
.clone()
.context("webrtc-sys patch is missing a `rev`")
}
fn fetch_webrtc_tag(rev: &str) -> Result<String> {
let url = format!(
"https://raw.githubusercontent.com/zed-industries/livekit-rust-sdks/{rev}/webrtc-sys/build/src/lib.rs"
);
let body = curl_text(&url).with_context(|| format!("fetching {url}"))?;
let re =
Regex::new(r#"pub\s+const\s+WEBRTC_TAG\s*:\s*&str\s*=\s*"([^"]+)""#).expect("static regex");
let captures = re
.captures(&body)
.with_context(|| format!("could not find WEBRTC_TAG in {url}"))?;
Ok(captures[1].to_string())
}
fn host_webrtc_triple() -> Result<String> {
let os = match std::env::consts::OS {
"macos" => "mac",
"linux" => "linux",
"windows" => "win",
other => bail!("unsupported host OS: {other}"),
};
let arch = match std::env::consts::ARCH {
"aarch64" => "arm64",
"x86_64" => "x64",
other => bail!("unsupported host arch: {other}"),
};
Ok(format!("{os}-{arch}-release"))
}
fn download_and_extract(tag: &str, triple: &str, into: &Path) -> Result<()> {
let url = format!(
"https://github.com/zed-industries/livekit-rust-sdks/releases/download/{tag}/webrtc-{triple}.zip"
);
let zip_path = into.join(format!("webrtc-{triple}.zip"));
eprintln!("Downloading {url}");
let status = Command::new("curl")
.args(["-fL", "--retry", "3", "--progress-bar", "-o"])
.arg(&zip_path)
.arg(&url)
.status()
.context("running curl")?;
if !status.success() {
bail!("curl exited with {status} while downloading {url}");
}
eprintln!("Extracting into {}", into.display());
let status = Command::new("unzip")
.arg("-q")
.arg("-o")
.arg(&zip_path)
.arg("-d")
.arg(into)
.status()
.context("running unzip")?;
if !status.success() {
bail!(
"unzip exited with {status} while extracting {}",
zip_path.display()
);
}
fs::remove_file(&zip_path).ok();
Ok(())
}
fn curl_text(url: &str) -> Result<String> {
let output = Command::new("curl")
.args(["-fsSL", url])
.output()
.context("running curl")?;
if !output.status.success() {
bail!(
"curl failed for {url} (exit {}): {}",
output.status,
String::from_utf8_lossy(&output.stderr).trim(),
);
}
String::from_utf8(output.stdout).context("curl returned non-UTF-8 body")
}
fn ensure_gitignore_entry(workspace_root: &Path) -> Result<()> {
let path = workspace_root.join(".gitignore");
let existing =
fs::read_to_string(&path).with_context(|| format!("reading {}", path.display()))?;
if existing
.lines()
.any(|line| line.trim() == GITIGNORE_ENTRY || line.trim() == LOCAL_DIR_NAME)
{
return Ok(());
}
let mut updated = existing;
if !updated.ends_with('\n') {
updated.push('\n');
}
updated.push_str(GITIGNORE_ENTRY);
updated.push('\n');
fs::write(&path, updated).with_context(|| format!("writing {}", path.display()))?;
eprintln!("Added {GITIGNORE_ENTRY} to .gitignore");
Ok(())
}
fn update_cargo_config(webrtc_path: &Path) -> Result<()> {
let home = std::env::var_os("HOME")
.or_else(|| std::env::var_os("USERPROFILE"))
.context("could not determine home directory")?;
let config_path = PathBuf::from(home).join(".cargo").join("config.toml");
if config_path.exists() {
bail!(
"{} already exists; refusing to modify it. \
Add `[env]\\n{ENV_VAR} = \"{}\"` yourself, \
or re-run with --no-cargo-config.",
config_path.display(),
webrtc_path.display(),
);
}
if let Some(parent) = config_path.parent() {
fs::create_dir_all(parent).with_context(|| format!("creating {}", parent.display()))?;
}
let mut doc = DocumentMut::new();
let mut env_table = Table::new();
env_table.set_implicit(false);
let path_str = webrtc_path
.to_str()
.context("webrtc path is not valid UTF-8")?;
env_table.insert(ENV_VAR, value(path_str));
doc.insert("env", Item::Table(env_table));
fs::write(&config_path, doc.to_string())
.with_context(|| format!("writing {}", config_path.display()))?;
eprintln!("Wrote {} with {ENV_VAR}={path_str}", config_path.display());
Ok(())
}