feat(xtask): allow alternate branch for fixture grammars

Set tree-sitter-php fixture to `upstream_test_fixture` branch.
Also remove the `--update` flag, as it does nothing.
This commit is contained in:
Will Lillis 2026-01-19 22:48:30 -05:00
parent 5d290a2a75
commit 29281b1f93
3 changed files with 155 additions and 73 deletions

View file

@ -1,19 +1,74 @@
use crate::{bail_on_err, root_dir, FetchFixtures, EMSCRIPTEN_VERSION};
use crate::{bail_on_err, root_dir, EMSCRIPTEN_VERSION};
use anyhow::Result;
use std::{fs, process::Command};
use std::{fs, path::Path, process::Command};
pub fn run_fixtures(args: &FetchFixtures) -> Result<()> {
enum FixtureRef<'a> {
Tag(&'a str),
Branch(&'a str),
}
impl<'a> FixtureRef<'a> {
#[allow(clippy::use_self)]
const fn new(tag: &'a str, branch: Option<&'a str>) -> FixtureRef<'a> {
if let Some(b) = branch {
Self::Branch(b)
} else {
Self::Tag(tag)
}
}
const fn ref_type(&self) -> &'static str {
match self {
FixtureRef::Tag(_) => "tag",
FixtureRef::Branch(_) => "branch",
}
}
}
impl std::fmt::Display for FixtureRef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FixtureRef::Tag(tag) => write!(f, "{tag}"),
FixtureRef::Branch(branch) => write!(f, "{branch}"),
}
}
}
fn current_ref_name(grammar_dir: &Path) -> Result<(String, Option<&'static str>)> {
let tag_args = ["describe", "--tags", "--exact-match", "HEAD"];
let branch_args = ["rev-parse", "--abbrev-ref", "HEAD"];
for (args, ref_type) in [tag_args.as_ref(), branch_args.as_ref()]
.iter()
.zip(&["tag", "branch"])
{
let name_cmd = Command::new("git")
.current_dir(grammar_dir)
.args(*args)
.output()?;
let name = String::from_utf8_lossy(&name_cmd.stdout);
let name = name.trim();
if !name.is_empty() {
return Ok((name.to_string(), Some(ref_type)));
}
}
Ok(("<unknown>".to_string(), None))
}
pub fn run_fixtures() -> Result<()> {
let fixtures_dir = root_dir().join("test").join("fixtures");
let grammars_dir = fixtures_dir.join("grammars");
let fixtures_path = fixtures_dir.join("fixtures.json");
// grammar name, tag
let mut fixtures: Vec<(String, String)> =
// grammar name, tag, [branch]
let fixtures: Vec<(String, String, Option<String>)> =
serde_json::from_str(&fs::read_to_string(&fixtures_path)?)?;
for (grammar, tag) in &mut fixtures {
let grammar_dir = grammars_dir.join(&grammar);
for (grammar, tag, branch) in &fixtures {
let grammar_dir = grammars_dir.join(grammar);
let grammar_url = format!("https://github.com/tree-sitter/tree-sitter-{grammar}");
let target_ref = FixtureRef::new(tag, branch.as_deref());
println!("Fetching the {grammar} grammar...");
@ -24,7 +79,7 @@ pub fn run_fixtures(args: &FetchFixtures) -> Result<()> {
"--depth",
"1",
"--branch",
tag,
&target_ref.to_string(),
&grammar_url,
&grammar_dir.to_string_lossy(),
]);
@ -33,31 +88,71 @@ pub fn run_fixtures(args: &FetchFixtures) -> Result<()> {
&format!("Failed to clone the {grammar} grammar"),
)?;
} else {
let mut describe_command = Command::new("git");
describe_command.current_dir(&grammar_dir).args([
"describe",
"--tags",
"--exact-match",
"HEAD",
]);
let (current_ref, current_ref_type) = current_ref_name(&grammar_dir)?;
if current_ref != target_ref.to_string() {
println!(
"Updating {grammar} grammar from {} {current_ref} to {} {target_ref}...",
current_ref_type.unwrap_or("<unknown>"),
target_ref.ref_type(),
);
let output = describe_command.output()?;
let current_tag = String::from_utf8_lossy(&output.stdout);
let current_tag = current_tag.trim();
if current_tag != tag {
println!("Updating {grammar} grammar from {current_tag} to {tag}...");
let mut fetch_command = Command::new("git");
fetch_command.current_dir(&grammar_dir).args([
"fetch",
"origin",
&format!("refs/tags/{tag}:refs/tags/{tag}"),
]);
bail_on_err(
&fetch_command.spawn()?.wait_with_output()?,
&format!("Failed to fetch tag {tag} for {grammar} grammar"),
)?;
match target_ref {
FixtureRef::Branch(branch) => {
let mut fetch_cmd = Command::new("git");
fetch_cmd.current_dir(&grammar_dir).args([
"fetch",
"--update-shallow",
"origin",
&format!("+refs/heads/{branch}:refs/remotes/origin/{branch}"),
]);
bail_on_err(
&fetch_cmd.spawn()?.wait_with_output()?,
&format!("Failed to fetch branch {branch}"),
)?;
let mut switch_cmd = Command::new("git");
switch_cmd
.current_dir(&grammar_dir)
.args(["switch", branch]);
bail_on_err(
&switch_cmd.spawn()?.wait_with_output()?,
&format!("Failed to checkout branch {branch}"),
)?;
let mut set_upstream_cmd = Command::new("git");
set_upstream_cmd.current_dir(&grammar_dir).args([
"branch",
"--set-upstream-to",
&format!("origin/{branch}"),
branch,
]);
bail_on_err(
&set_upstream_cmd.spawn()?.wait_with_output()?,
&format!("Failed to set upstream for branch {branch}"),
)?;
let mut pull_cmd = Command::new("git");
pull_cmd
.current_dir(&grammar_dir)
.args(["pull", "origin", branch]);
bail_on_err(
&pull_cmd.spawn()?.wait_with_output()?,
&format!("Failed to pull latest from branch {branch}"),
)?;
}
FixtureRef::Tag(tag) => {
let mut fetch_command = Command::new("git");
fetch_command.current_dir(&grammar_dir).args([
"fetch",
"origin",
&format!("refs/tags/{tag}:refs/tags/{tag}"),
]);
bail_on_err(
&fetch_command.spawn()?.wait_with_output()?,
&format!(
"Failed to fetch {} {target_ref} for {grammar} grammar",
target_ref.ref_type()
),
)?;
}
}
let mut reset_command = Command::new("git");
reset_command
@ -71,29 +166,23 @@ pub fn run_fixtures(args: &FetchFixtures) -> Result<()> {
let mut checkout_command = Command::new("git");
checkout_command
.current_dir(&grammar_dir)
.args(["checkout", tag]);
.args(["checkout", &target_ref.to_string()]);
bail_on_err(
&checkout_command.spawn()?.wait_with_output()?,
&format!("Failed to checkout tag {tag} for {grammar} grammar"),
&format!(
"Failed to checkout {} {target_ref} for {grammar} grammar",
target_ref.ref_type()
),
)?;
} else {
println!("{grammar} grammar is already at tag {tag}");
println!(
"{grammar} grammar is already at {} {target_ref}",
target_ref.ref_type()
);
}
}
}
if args.update {
println!("Updating the fixtures lock file");
fs::write(
&fixtures_path,
// format the JSON without extra newlines
serde_json::to_string(&fixtures)?
.replace("[[", "[\n [")
.replace("],", "],\n ")
.replace("]]", "]\n]"),
)?;
}
Ok(())
}

View file

@ -36,7 +36,7 @@ enum Commands {
/// Fetches emscripten.
FetchEmscripten,
/// Fetches the fixtures for testing tree-sitter.
FetchFixtures(FetchFixtures),
FetchFixtures,
/// Generate the Rust bindings from the C library.
GenerateBindings,
/// Generates the fixtures for testing tree-sitter.
@ -118,13 +118,6 @@ struct Clippy {
package: Option<String>,
}
#[derive(Args)]
struct FetchFixtures {
/// Update all fixtures to the latest tag
#[arg(long, short)]
update: bool,
}
#[derive(Args)]
struct GenerateFixtures {
/// Generates the parser to Wasm
@ -232,8 +225,8 @@ fn run() -> Result<()> {
Commands::CheckWasmExports(check_options) => check_wasm_exports::run(&check_options)?,
Commands::Clippy(clippy_options) => clippy::run(&clippy_options)?,
Commands::FetchEmscripten => fetch::run_emscripten()?,
Commands::FetchFixtures(fetch_fixture_options) => {
fetch::run_fixtures(&fetch_fixture_options)?;
Commands::FetchFixtures => {
fetch::run_fixtures()?;
}
Commands::GenerateBindings => generate::run_bindings()?,
Commands::GenerateFixtures(generate_fixtures_options) => {

View file

@ -1,17 +1,17 @@
[
["bash","v0.25.0"],
["c","v0.24.1"],
["cpp","v0.23.4"],
["embedded-template","v0.25.0"],
["go","v0.25.0"],
["html","v0.23.2"],
["java","v0.23.5"],
["javascript","v0.25.0"],
["jsdoc","v0.23.2"],
["json","v0.24.8"],
["php","v0.24.2"],
["python","v0.23.6"],
["ruby","v0.23.1"],
["rust","v0.24.0"],
["typescript","v0.23.2"]
]
["bash","v0.25.0", null],
["c","v0.24.1", null],
["cpp","v0.23.4", null],
["embedded-template","v0.25.0", null],
["go","v0.25.0", null],
["html","v0.23.2", null],
["java","v0.23.5", null],
["javascript","v0.25.0", null],
["jsdoc","v0.23.2", null],
["json","v0.24.8", null],
["php","v0.24.2", "upstream_test_fixture"],
["python","v0.23.6", null],
["ruby","v0.23.1", null],
["rust","v0.24.0", null],
["typescript","v0.23.2", null]
]