diff --git a/crates/ty_wasm/build.rs b/crates/ty_wasm/build.rs new file mode 100644 index 0000000000..5355a7c669 --- /dev/null +++ b/crates/ty_wasm/build.rs @@ -0,0 +1,90 @@ +use std::{ + fs, + path::{Path, PathBuf}, + process::Command, +}; + +fn main() { + // The workspace root directory is not available without walking up the tree + // https://github.com/rust-lang/cargo/issues/3946 + let workspace_root = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join(".."); + + commit_info(&workspace_root); +} + +/// Retrieve commit information from the Git repository. +fn commit_info(workspace_root: &Path) { + // If not in a git repository, do not attempt to retrieve commit information + let git_dir = workspace_root.join(".git"); + if !git_dir.exists() { + return; + } + + if let Some(git_head_path) = git_head(&git_dir) { + println!("cargo:rerun-if-changed={}", git_head_path.display()); + + let git_head_contents = fs::read_to_string(git_head_path); + if let Ok(git_head_contents) = git_head_contents { + // The contents are either a commit or a reference in the following formats + // - "" when the head is detached + // - "ref " when working on a branch + // If a commit, checking if the HEAD file has changed is sufficient + // If a ref, we need to add the head file for that ref to rebuild on commit + let mut git_ref_parts = git_head_contents.split_whitespace(); + git_ref_parts.next(); + if let Some(git_ref) = git_ref_parts.next() { + let git_ref_path = git_dir.join(git_ref); + println!("cargo:rerun-if-changed={}", git_ref_path.display()); + } + } + } + + let output = match Command::new("git") + .arg("log") + .arg("-1") + .arg("--date=short") + .arg("--abbrev=9") + .arg("--format=%H %h %cd %(describe:tags)") + .current_dir(workspace_root) + .output() + { + Ok(output) if output.status.success() => output, + _ => return, + }; + + let stdout = String::from_utf8(output.stdout).unwrap(); + + let mut parts = stdout.split_whitespace(); + let mut next = || parts.next().unwrap(); + let _commit_hash = next(); + println!("cargo::rustc-env=TY_WASM_COMMIT_SHORT_HASH={}", next()); +} + +fn git_head(git_dir: &Path) -> Option { + // The typical case is a standard git repository. + let git_head_path = git_dir.join("HEAD"); + if git_head_path.exists() { + return Some(git_head_path); + } + if !git_dir.is_file() { + return None; + } + // If `.git/HEAD` doesn't exist and `.git` is actually a file, + // then let's try to attempt to read it as a worktree. If it's + // a worktree, then its contents will look like this, e.g.: + // + // gitdir: /home/andrew/astral/uv/main/.git/worktrees/pr2 + // + // And the HEAD file we want to watch will be at: + // + // /home/andrew/astral/uv/main/.git/worktrees/pr2/HEAD + let contents = fs::read_to_string(git_dir).ok()?; + let (label, worktree_path) = contents.split_once(':')?; + if label != "gitdir" { + return None; + } + let worktree_path = worktree_path.trim(); + Some(PathBuf::from(worktree_path)) +} diff --git a/crates/ty_wasm/src/lib.rs b/crates/ty_wasm/src/lib.rs index 809309edcb..4ce30936e6 100644 --- a/crates/ty_wasm/src/lib.rs +++ b/crates/ty_wasm/src/lib.rs @@ -23,6 +23,14 @@ use ty_project::{Db, ProjectDatabase}; use ty_python_semantic::Program; use wasm_bindgen::prelude::*; +#[wasm_bindgen] +pub fn version() -> String { + option_env!("TY_WASM_COMMIT_SHORT_HASH") + .or_else(|| option_env!("CARGO_PKG_VERSION")) + .unwrap_or("unknown") + .to_string() +} + #[wasm_bindgen(start)] pub fn run() { use log::Level; diff --git a/playground/ruff/src/Editor/Chrome.tsx b/playground/ruff/src/Editor/Chrome.tsx index 43d798b0d0..3fc9ddca2a 100644 --- a/playground/ruff/src/Editor/Chrome.tsx +++ b/playground/ruff/src/Editor/Chrome.tsx @@ -88,7 +88,7 @@ export default function Chrome() { edit={revision} theme={theme} tool="ruff" - version={ruffVersion} + version={`v${ruffVersion}`} onChangeTheme={setTheme} onShare={handleShare} onReset={handleResetClicked} diff --git a/playground/shared/src/Header.tsx b/playground/shared/src/Header.tsx index 694ec20a25..128f8f0afd 100644 --- a/playground/shared/src/Header.tsx +++ b/playground/shared/src/Header.tsx @@ -42,7 +42,7 @@ export default function Header({
{version ? (
- v{version} + {version}
) : null} diff --git a/playground/shared/src/VersionTag.tsx b/playground/shared/src/VersionTag.tsx index dd80a9fc7c..89a7e3c81f 100644 --- a/playground/shared/src/VersionTag.tsx +++ b/playground/shared/src/VersionTag.tsx @@ -4,6 +4,7 @@ import { ReactNode } from "react"; export default function VersionTag({ children }: { children: ReactNode }) { return (
("0.0.0"); + const [version, setVersion] = useState(null); const [error, setError] = useState(null); const workspacePromiseRef = useRef | null>(null); const [workspace, setWorkspace] = useState(null); @@ -453,6 +453,7 @@ export interface InitializedPlayground { async function startPlayground(): Promise { const ty = await import("../ty_wasm"); await ty.default(); + const version = ty.version(); const monaco = await loader.init(); setupMonaco(monaco, { @@ -466,7 +467,7 @@ async function startPlayground(): Promise { const workspace = restored ?? DEFAULT_WORKSPACE; return { - version: "0.0.0", + version, workspace, }; }