mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
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:
parent
ff8fa053ff
commit
d4f06ee374
4 changed files with 254 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,6 +2,7 @@
|
|||
**/*.proptest-regressions
|
||||
**/cargo-target
|
||||
**/target
|
||||
.webrtc-sys/
|
||||
**/venv
|
||||
**/.direnv
|
||||
*.wasm
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
249
tooling/xtask/src/tasks/setup_webrtc.rs
Normal file
249
tooling/xtask/src/tasks/setup_webrtc.rs
Normal 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(())
|
||||
}
|
||||
Loading…
Reference in a new issue