diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0995e812..c9ed0cd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,8 @@ jobs: # run: cargo test --workspace --all-features - name: No-default features run: cargo test --workspace --no-default-features + - name: History check + run: cargo xtask history msrv: name: Check MSRV runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index a5dffafe..3b653171 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ lcov.info # Ignore the output of the `weaver resolve` command output.json -resolved-schema.yaml \ No newline at end of file +resolved-schema.yaml + +# Ignore the temporary repo dir created by `cargo xtask history` command +history-temp-repo/ \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS index 0dce2619..7e41ec8d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -15,6 +15,7 @@ # Global owners, will be the owners for everything in the repo * @open-telemetry/weaver-maintainers +* @open-telemetry/weaver-approvers # Semantic conventions schema /schemas/semconv.schema.json @open-telemetry/specs-semconv-approvers diff --git a/Cargo.lock b/Cargo.lock index 6e33d4f2..23129cf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3688,18 +3688,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -5125,9 +5125,13 @@ dependencies = [ [[package]] name = "xtask" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", + "assert_cmd", + "gix", + "semver", + "serde_json", "toml", ] diff --git a/Cargo.toml b/Cargo.toml index c6e598b3..d4e4d01b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ publish = false rust-version = "1.81.0" [workspace.dependencies] -serde = { version = "1.0.215", features = ["derive"] } +serde = { version = "1.0.216", features = ["derive"] } serde_yaml = "0.9.34" serde_json = { version = "1.0.133"} thiserror = "2.0.6" diff --git a/crates/xtask/Cargo.toml b/crates/xtask/Cargo.toml index 67aaee22..fa449e6b 100644 --- a/crates/xtask/Cargo.toml +++ b/crates/xtask/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "xtask" -version = "0.1.0" -publish = false # xtask is not meant to be published +version = "0.1.1" +publish = false # xtask is not meant to be published authors.workspace = true edition.workspace = true repository.workspace = true @@ -10,6 +10,16 @@ rust-version.workspace = true [dependencies] anyhow.workspace = true +assert_cmd = "2.0.14" +gix = { version = "0.68.0", default-features = false, features = [ + "comfort", + "blocking-http-transport-reqwest", + "max-performance-safe", + "worktree-mutation", + "blocking-http-transport-reqwest-rust-tls", +] } +semver = "1.0.23" +serde_json.workspace = true toml = "0.8.19" #[lints] diff --git a/crates/xtask/src/history.rs b/crates/xtask/src/history.rs new file mode 100644 index 00000000..d24bd2eb --- /dev/null +++ b/crates/xtask/src/history.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 + +use assert_cmd::Command; +use gix::clone::PrepareFetch; +use gix::create::{self, Kind}; +use gix::{open, progress, Repository}; +use semver::Version; +use std::fs; +use std::sync::atomic::AtomicBool; +use std::time::Duration; + +const REPO_URL: &str = "https://github.com/open-telemetry/semantic-conventions.git"; +const ARCHIVE_URL: &str = + "https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/"; +const START_TAG: &str = "v1.22.0"; +const TEMP_REPO_DIR: &str = "history-temp-repo"; + +/// Get the git repository, no checkout +#[cfg(not(tarpaulin_include))] +fn get_repo() -> anyhow::Result { + let _ = fs::remove_dir_all(TEMP_REPO_DIR); + let _ = fs::create_dir(TEMP_REPO_DIR); + let mut fetch = PrepareFetch::new( + REPO_URL, + TEMP_REPO_DIR, + Kind::WithWorktree, + create::Options { + destination_must_be_empty: true, + fs_capabilities: None, + }, + open::Options::isolated(), + )?; + + let (repo, _) = fetch.fetch_only(progress::Discard, &AtomicBool::new(false))?; + Ok(repo) +} + +/// Get the list of tags from the git repository filtered by the start version +#[cfg(not(tarpaulin_include))] +fn get_versions_from_git(repo: &Repository, start_semver: Version) -> anyhow::Result> { + let tags: Vec = repo + .references()? + .tags()? + .filter_map(|reference| { + let reference = reference.ok()?; + let tag = reference.name().shorten().to_string(); + + // Ignore tags with a lower version than the start tag + let version_str = tag.trim_start_matches(char::is_alphabetic); + if let Ok(version) = Version::parse(version_str) { + if version >= start_semver { + Some(tag) + } else { + None + } + } else { + None + } + }) + .collect(); + Ok(tags) +} + +/// Run registry check on every semconv archive starting from start_version +#[cfg(not(tarpaulin_include))] +pub fn run(start_version: Option) -> anyhow::Result<()> { + use anyhow::Context; + + let start_version = start_version.unwrap_or(START_TAG.to_string()); + let start_semver = Version::parse(start_version.trim_start_matches(char::is_alphabetic)) + .context(format!( + "The provided version `{start_version}` is not a valid semver." + ))?; + let repo = get_repo().context("Failed to fetch the semconv repo.")?; + let versions = + get_versions_from_git(&repo, start_semver).context("Failed to get the tag list.")?; + let _ = fs::remove_dir_all(TEMP_REPO_DIR); + println!("Checking versions: {:?}", versions); + let mut failed = false; + for version in versions { + let mut cmd = + Command::cargo_bin("weaver").context("Failed to create the cargo command.")?; + let output = cmd + .arg("--quiet") + .arg("registry") + .arg("check") + .arg("-r") + .arg(format!("{ARCHIVE_URL}{version}.zip[model]")) + .timeout(Duration::from_secs(60)) + .output() + .context("Failed to execute the weaver process.")?; + + if output.status.success() { + println!("Success for: {}", version); + } else { + failed = true; + println!("Failure for: {}", version); + println!( + "{}", + String::from_utf8(output.stdout).context("Invalid UTF-8")? + ); + } + } + if failed { + anyhow::bail!("Some versions failed the registry check."); + } + Ok(()) +} diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index 0ed363d5..85dc906c 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -10,6 +10,7 @@ #![allow(clippy::print_stdout)] #![allow(clippy::print_stderr)] +mod history; mod validate; #[cfg(not(tarpaulin_include))] @@ -20,6 +21,7 @@ fn main() -> anyhow::Result<()> { None => print_help(), Some(task) => match task.as_str() { "validate" => validate::run(), + "history" => history::run(std::env::args().nth(2)), "help" => print_help(), _ => { eprintln!("Unknown task: {}", task); @@ -38,6 +40,8 @@ Usage: Execute the command using `cargo xtask `, e.g., `cargo xtask valida Tasks: - validate: Validate the entire structure of the weaver project. + - history: Run registry check on semconv models within back compatibility range. + Optionally provide a start semver e.g. `history 1.29.0`. " ); Ok(()) diff --git a/justfile b/justfile index 1ac86372..b45cfb35 100644 --- a/justfile +++ b/justfile @@ -22,6 +22,7 @@ pre-push-check: cargo clippy --workspace --all-targets -- -D warnings --allow deprecated rm -rf crates/weaver_forge/observed_output/* cargo nextest run --all + cargo xtask history # [workaround] removed --all-features due to an issue in one of the dependency in Tantity (zstd-safe) # [ToDo LQ] Re-enable --all-features once the issue is resolved # cargo doc --workspace --all-features --no-deps --document-private-items