Skip to content

Commit

Permalink
Using git branch/tag to pull dependencies (#147)
Browse files Browse the repository at this point in the history
* Using git branch, tag to pull dependencies

* fixed bug on showing wrong error if config was incorrect

* fixed janitor tests

* refactor: git branches and tags (#162)



* fix: rev in git dependency

* fix: behavior with no rev

* fixed clippy

---------

Co-authored-by: Valentin B. <[email protected]>
  • Loading branch information
mario-eth and beeb authored Aug 27, 2024
1 parent b86fdab commit f978305
Show file tree
Hide file tree
Showing 10 changed files with 519 additions and 142 deletions.
20 changes: 16 additions & 4 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ You can install a dependency from the Soldeer repository, a custom URL pointing
- **Example from Git:**
soldeer install @openzeppelin-contracts~2.3.0 [email protected]:OpenZeppelin/openzeppelin-contracts.git
- **Example from Git with a specified commit:**
soldeer install @openzeppelin-contracts~2.3.0 [email protected]:OpenZeppelin/openzeppelin-contracts.git --rev 05f218fb6617932e56bf5388c3b389c3028a7b73",
soldeer install @openzeppelin-contracts~2.3.0 [email protected]:OpenZeppelin/openzeppelin-contracts.git --rev 05f218fb6617932e56bf5388c3b389c3028a7b73
- **Example from Git with a specified tag:**
soldeer install @openzeppelin-contracts~2.3.0 [email protected]:OpenZeppelin/openzeppelin-contracts.git --tag my-tag
- **Example from Git with a specified branch:**
soldeer install @openzeppelin-contracts~2.3.0 [email protected]:OpenZeppelin/openzeppelin-contracts.git --branch my-branch",
after_help = "For more information, read the README.md"
)]
pub struct Install {
Expand All @@ -63,13 +67,21 @@ pub struct Install {
/// The URL to the dependency zip file, if not from the Soldeer repository
///
/// Example: https://my-domain/dep.zip
#[arg(value_name = "URL")]
#[arg(value_name = "URL", requires = "dependency")]
pub remote_url: Option<String>,

/// The revision of the dependency, if from Git
#[arg(long)]
/// A Git revision
#[arg(long, group = "identifier", requires = "remote_url")]
pub rev: Option<String>,

/// A Git tag
#[arg(long, group = "identifier", requires = "remote_url")]
pub tag: Option<String>,

/// A Git branch
#[arg(long, group = "identifier", requires = "remote_url")]
pub branch: Option<String>,

/// If set, this command will delete the existing remappings and re-create them
#[arg(short = 'g', long, default_value_t = false)]
pub regenerate_remappings: bool,
Expand Down
258 changes: 199 additions & 59 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,47 @@ impl Default for SoldeerConfig {
}
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum GitIdentifier {
Rev(String),
Branch(String),
Tag(String),
}

impl GitIdentifier {
pub fn from_rev(rev: impl Into<String>) -> Self {
let rev: String = rev.into();
GitIdentifier::Rev(rev)
}

pub fn from_branch(branch: impl Into<String>) -> Self {
let branch: String = branch.into();
GitIdentifier::Branch(branch)
}

pub fn from_tag(tag: impl Into<String>) -> Self {
let tag: String = tag.into();
GitIdentifier::Tag(tag)
}
}

impl fmt::Display for GitIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let val = match self {
GitIdentifier::Rev(rev) => rev,
GitIdentifier::Branch(branch) => branch,
GitIdentifier::Tag(tag) => tag,
};
write!(f, "{val}")
}
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct GitDependency {
pub name: String,
pub version: String,
pub git: String,
pub rev: Option<String>,
pub identifier: Option<GitIdentifier>,
}

impl fmt::Display for GitDependency {
Expand Down Expand Up @@ -144,48 +179,43 @@ impl Dependency {
None => value(&dep.version),
},
),
Dependency::Git(dep) => (
dep.name.clone(),
match &dep.rev {
Some(rev) => {
let mut table = InlineTable::new();
table.insert(
"version",
value(&dep.version)
.into_value()
.expect("version should be a valid toml value"),
);
table.insert(
"git",
value(&dep.git)
.into_value()
.expect("git URL should be a valid toml value"),
);
Dependency::Git(dep) => {
let mut table = InlineTable::new();
table.insert(
"version",
value(&dep.version).into_value().expect("version should be a valid toml value"),
);
table.insert(
"git",
value(&dep.git).into_value().expect("git URL should be a valid toml value"),
);

match &dep.identifier {
Some(GitIdentifier::Rev(rev)) => {
table.insert(
"rev",
value(rev).into_value().expect("rev should be a valid toml value"),
);
value(table)
}
None => {
let mut table = InlineTable::new();
Some(GitIdentifier::Branch(branch)) => {
table.insert(
"version",
value(&dep.version)
"branch",
value(branch)
.into_value()
.expect("version should be a valid toml value"),
.expect("branch should be a valid toml value"),
);
}
Some(GitIdentifier::Tag(tag)) => {
table.insert(
"git",
value(&dep.git)
.into_value()
.expect("git URL should be a valid toml value"),
"tag",
value(tag).into_value().expect("tag should be a valid toml value"),
);

value(table)
}
},
),
None => {}
}

(dep.name.clone(), value(table))
}
}
}

Expand Down Expand Up @@ -454,29 +484,6 @@ fn parse_dependency(name: impl Into<String>, value: &Item) -> Result<Dependency>
);
}

// else if value.is_inline_table() && // TODO: Hacky way of doing this, might need rewritten
// !value.as_inline_table().unwrap().contains_key("url") &&
// !value.as_inline_table().unwrap().contains_key("git")
// {
// // this function does not retrieve the url, only version
// return Ok(HttpDependency {
// name: name.clone(),
// version: match value.as_inline_table() {
// // we normalize to inline table
// Some(table) => {
// let version = table.get("version").unwrap().to_string();
// version.replace("\"", "").trim().to_string()
// }
// None => {
// return Err(ConfigError::InvalidDependency(name));
// }
// },
// url: None,
// checksum: None,
// }
// .into());
// }

// we should have a table or inline table
let table = {
match value.as_inline_table() {
Expand Down Expand Up @@ -508,15 +515,46 @@ fn parse_dependency(name: impl Into<String>, value: &Item) -> Result<Dependency>
return Err(ConfigError::InvalidField { field: "git".to_string(), dep: name });
}
Some(Some(git)) => {
// rev field is optional but needs to be a string if present
// rev/branch/tag fields are optional but need to be a string if present
let rev = match table.get("rev").map(|v| v.as_str()) {
Some(Some(rev)) => Some(rev.to_string()),
Some(None) => {
return Err(ConfigError::InvalidField { field: "rev".to_string(), dep: name });
}
None => None,
};
return Ok(Dependency::Git(GitDependency { name, git: git.to_string(), version, rev }));
let branch = match table.get("branch").map(|v| v.as_str()) {
Some(Some(tag)) => Some(tag.to_string()),
Some(None) => {
return Err(ConfigError::InvalidField {
field: "branch".to_string(),
dep: name,
});
}
None => None,
};
let tag = match table.get("tag").map(|v| v.as_str()) {
Some(Some(tag)) => Some(tag.to_string()),
Some(None) => {
return Err(ConfigError::InvalidField { field: "tag".to_string(), dep: name });
}
None => None,
};
let identifier = match (rev, branch, tag) {
(Some(rev), None, None) => Some(GitIdentifier::from_rev(rev)),
(None, Some(branch), None) => Some(GitIdentifier::from_branch(branch)),
(None, None, Some(tag)) => Some(GitIdentifier::from_tag(tag)),
(None, None, None) => None,
_ => {
return Err(ConfigError::GitIdentifierConflict(name));
}
};
return Ok(Dependency::Git(GitDependency {
name,
git: git.to_string(),
version,
identifier,
}));
}
None => {}
}
Expand Down Expand Up @@ -1431,7 +1469,7 @@ gas_reports = ['*']
name: "dep1".to_string(),
version: "1.0.0".to_string(),
git: "[email protected]:foundry-rs/forge-std.git".to_string(),
rev: Some("07263d193d621c4b2b0ce8b4d54af58f6957d97d".to_string()),
identifier: Some(GitIdentifier::from_rev("07263d193d621c4b2b0ce8b4d54af58f6957d97d")),
});

add_to_config(&dependency, &target_config).unwrap();
Expand All @@ -1449,6 +1487,108 @@ gas_reports = ['*']
[dependencies]
dep1 = { version = "1.0.0", git = "[email protected]:foundry-rs/forge-std.git", rev = "07263d193d621c4b2b0ce8b4d54af58f6957d97d" }
# we don't have [dependencies] declared
"#;

assert_eq!(read_file_to_string(&target_config), content);

let _ = remove_file(target_config);
Ok(())
}

#[test]
fn add_to_config_foundry_github_with_tag() -> Result<()> {
let mut content = r#"
# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config
[profile.default]
script = "script"
solc = "0.8.26"
src = "src"
test = "test"
libs = ["dependencies"]
gas_reports = ['*']
# we don't have [dependencies] declared
"#;

let target_config = define_config(true);

write_to_config(&target_config, content);

let dependency = Dependency::Git(GitDependency {
name: "dep1".to_string(),
version: "1.0.0".to_string(),
git: "https://gitlab.com/mario4582928/Mario.git".to_string(),
identifier: Some(GitIdentifier::from_tag("custom-tag")),
});

add_to_config(&dependency, &target_config).unwrap();
content = r#"
# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config
[profile.default]
script = "script"
solc = "0.8.26"
src = "src"
test = "test"
libs = ["dependencies"]
gas_reports = ['*']
[dependencies]
dep1 = { version = "1.0.0", git = "https://gitlab.com/mario4582928/Mario.git", tag = "custom-tag" }
# we don't have [dependencies] declared
"#;

assert_eq!(read_file_to_string(&target_config), content);

let _ = remove_file(target_config);
Ok(())
}

#[test]
fn add_to_config_foundry_github_with_branch() -> Result<()> {
let mut content = r#"
# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config
[profile.default]
script = "script"
solc = "0.8.26"
src = "src"
test = "test"
libs = ["dependencies"]
gas_reports = ['*']
# we don't have [dependencies] declared
"#;

let target_config = define_config(true);

write_to_config(&target_config, content);

let dependency = Dependency::Git(GitDependency {
name: "dep1".to_string(),
version: "1.0.0".to_string(),
git: "https://gitlab.com/mario4582928/Mario.git".to_string(),
identifier: Some(GitIdentifier::from_branch("custom-branch")),
});

add_to_config(&dependency, &target_config).unwrap();
content = r#"
# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config
[profile.default]
script = "script"
solc = "0.8.26"
src = "src"
test = "test"
libs = ["dependencies"]
gas_reports = ['*']
[dependencies]
dep1 = { version = "1.0.0", git = "https://gitlab.com/mario4582928/Mario.git", branch = "custom-branch" }
# we don't have [dependencies] declared
"#;

Expand Down Expand Up @@ -1485,7 +1625,7 @@ dep1 = { version = "1.0.0", git = "[email protected]:foundry-rs/forge-std.git" }
name: "dep1".to_string(),
version: "1.0.0".to_string(),
git: "[email protected]:foundry-rs/forge-std.git".to_string(),
rev: Some("07263d193d621c4b2b0ce8b4d54af58f6957d97d".to_string()),
identifier: Some(GitIdentifier::from_rev("07263d193d621c4b2b0ce8b4d54af58f6957d97d")),
});

add_to_config(&dependency, &target_config).unwrap();
Expand Down
Loading

0 comments on commit f978305

Please sign in to comment.