Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using git branch/tag to pull dependencies #147

Merged
merged 7 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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