Skip to content

Commit

Permalink
Merge pull request #77 from CoinFabrik/76-fix-soroban-version-error
Browse files Browse the repository at this point in the history
76 fix soroban version error
  • Loading branch information
tenuki authored Feb 27, 2024
2 parents 41f2bf4 + 1f53044 commit 589dbc3
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 34 deletions.
2 changes: 0 additions & 2 deletions detectors/soroban-version/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ crate-type = ["cdylib"]

[dependencies]
dylint_linting = { workspace = true }
if_chain = { workspace = true }
semver = "1.0.4"
serde_json = "1.0"
toml = "0.8.8"
ureq = { version = "2.7.1", features = ["json"] }

scout-audit-internal = { workspace = true }
Expand Down
90 changes: 62 additions & 28 deletions detectors/soroban-version/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ extern crate rustc_ast;
extern crate rustc_hir;
extern crate rustc_span;

use std::{env, fs, io::Error, path::Path};
use std::{io::Error, process::Command};

use rustc_ast::Crate;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use scout_audit_internal::Detector;
use semver::*;
use semver::Version;
use serde_json::Value;

dylint_linting::declare_early_lint! {
/// ### What it does
Expand All @@ -24,7 +25,7 @@ dylint_linting::declare_early_lint! {

impl EarlyLintPass for CheckSorobanVersion {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
let latest_version = match get_version() {
let latest_soroban_version = match get_latest_soroban_version() {
Ok(version) => version,
Err(e) => {
cx.sess()
Expand All @@ -34,54 +35,70 @@ impl EarlyLintPass for CheckSorobanVersion {
}
};

let manifest_dir = match env::var("CARGO_MANIFEST_DIR") {
Ok(dir) => dir,
let cargo_metadata = match get_cargo_metadata() {
Ok(metadata) => metadata,
Err(e) => {
cx.sess()
.struct_warn(format!(
"Environment variable CARGO_MANIFEST_DIR not found: {}",
e
))
.struct_warn(format!("Failed to get cargo metadata: {}", e))
.emit();
return;
}
};

let cargo_toml_path = Path::new(&manifest_dir).join("Cargo.toml");
let cargo_toml = match fs::read_to_string(cargo_toml_path) {
Ok(content) => content,
Err(e) => {
let cargo_toml_package_opt = match cargo_metadata["packages"].as_array() {
Some(packages) => packages.first(),
None => {
cx.sess()
.struct_warn(format!("Unable to read Cargo.toml: {}", e))
.struct_warn("Error parsing cargo metadata: packages not found")
.emit();
return;
}
};

let toml: toml::Value = match toml::from_str(&cargo_toml) {
Ok(value) => value,
Err(e) => {
let cargo_toml_package = match cargo_toml_package_opt {
Some(package) => package,
None => {
cx.sess()
.struct_warn("Error parsing cargo metadata: first package not found")
.emit();
return;
}
};

let dependencies = match cargo_toml_package["dependencies"].as_array() {
Some(dependencies) => dependencies,
None => {
cx.sess()
.struct_warn(format!("Error parsing Cargo.toml: {}", e))
.struct_warn("Error parsing cargo metadata: dependencies not found")
.emit();
return;
}
};

let soroban_version = match toml
.get("dependencies")
.and_then(|d| d.get("soroban-sdk").and_then(|i| i.get("version")))
let current_dependency = match dependencies
.iter()
.find(|&dep| dep["name"].as_str().unwrap_or("") == "soroban-sdk")
{
Some(version) => version.to_string(),
Some(current_dependency) => current_dependency,
None => {
cx.sess()
.struct_warn("Soroban dependency not found in Cargo.toml")
.struct_warn("Soroban dependency not found in dependencies")
.emit();
return;
}
};

let req = match Version::parse(&latest_version.replace('\"', "")) {
let current_dependency_version = match current_dependency["req"].as_str() {
Some(version) => version,
None => {
cx.sess()
.struct_warn("Error parsing current Soroban version")
.emit();
return;
}
};

let req = match Version::parse(&latest_soroban_version.replace('\"', "")) {
Ok(version) => version,
Err(e) => {
cx.sess()
Expand All @@ -91,7 +108,9 @@ impl EarlyLintPass for CheckSorobanVersion {
}
};

let soroban_version = match VersionReq::parse(&soroban_version.replace('\"', "")) {
let cleaned_version = current_dependency_version.replace(&['=', '^'][..], "");

let soroban_version = match Version::parse(&cleaned_version) {
Ok(version) => version,
Err(e) => {
cx.sess()
Expand All @@ -101,18 +120,20 @@ impl EarlyLintPass for CheckSorobanVersion {
}
};

if !soroban_version.matches(&req) {
if !soroban_version.eq(&req) {
Detector::SorobanVersion.span_lint_and_help(
cx,
CHECK_SOROBAN_VERSION,
rustc_span::DUMMY_SP,
&format!(r#"The latest Soroban version is {latest_version}, and your version is "{soroban_version}""#),
&format!(
r#"The latest Soroban version is {latest_soroban_version}, and your version is "{soroban_version}""#
),
);
}
}
}

fn get_version() -> Result<String, String> {
fn get_latest_soroban_version() -> Result<String, String> {
let response = ureq::get("https://crates.io/api/v1/crates/soroban-sdk")
.set("User-Agent", "Scout/1.0")
.call();
Expand All @@ -132,3 +153,16 @@ fn get_version() -> Result<String, String> {
Err(_) => Err("Failed to get Soroban version from crates.io".to_string()),
}
}

fn get_cargo_metadata() -> Result<Value, String> {
let output = Command::new("cargo")
.args(["metadata", "--format-version=1", "--no-deps"])
.output();

match output {
Ok(output) if output.status.success() => serde_json::from_slice(&output.stdout)
.map_err(|e| format!("Error parsing cargo metadata: {}", e)),
Ok(output) => Err(String::from_utf8_lossy(&output.stderr).into_owned()),
Err(e) => Err(e.to_string()),
}
}
9 changes: 5 additions & 4 deletions scripts/check-ci-detectors-to-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ def is_special_directory(directory):

with open(args.gh_workflow_path, "r") as f:
workflow = yaml.safe_load(f)
detectors_to_test = workflow["jobs"]["test"]["strategy"]["matrix"]["test"]
detectors_to_test = set(workflow["jobs"]["test"]["strategy"]["matrix"]["test"])

detectors = [ f.name for f in os.scandir(args.detectors_path) if f.is_dir() and not is_special_directory(f.name) ]
detectors.sort()
detectors = set(f.name for f in os.scandir(args.detectors_path) if f.is_dir() and not is_special_directory(f.name))
# detectors.sort()

if (detectors != detectors_to_test):
sym_diff = detectors ^ detectors_to_test
if len(sym_diff)!=0 and sym_diff!=set(['soroban-version']):
print("Detectors to test in the workflow are not the same as the detectors in the detectors folder.")
print("Detectors to test: ", detectors_to_test)
print("Detectors in the folder: ", detectors)
Expand Down

0 comments on commit 589dbc3

Please sign in to comment.