-
Notifications
You must be signed in to change notification settings - Fork 2k
feat(dependencies
): support pinning of tags / revs when using .gitmodules
with foundry.lock
#9522
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
Open
yash-atreya
wants to merge
76
commits into
master
Choose a base branch
from
yash/fix-forge-update
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
76 commits
Select commit
Hold shift + click to select a range
3bcf1b9
parse submodule status output
yash-atreya a3a01ec
feat(`forge`): save submodules info on install
yash-atreya cd075f2
re-checkout to tag/rev after forge update
yash-atreya 917cc13
clippy
yash-atreya e7fb768
fmt
yash-atreya 8e4b543
fix
yash-atreya 53b362d
fix
yash-atreya f666e8c
test
yash-atreya 0ea584d
fix
yash-atreya cf88ef8
override using forge update
yash-atreya 2a55c67
nit
yash-atreya 19194c0
nit
yash-atreya 7f6d026
fix: update only untagged deps
yash-atreya ed3ccfe
allow overrides
yash-atreya f893917
clippy
yash-atreya dd072fd
remove + rename to foundry.lock
yash-atreya ed14a48
nit
yash-atreya 90d92bb
fix: sync foundry.lock on install
yash-atreya 5640bab
sync foundry lock using forge install
yash-atreya 7d9ce8d
fix: read_and_sync_foundry_lock
yash-atreya 92454f8
fix
yash-atreya 46ce492
fix test
yash-atreya 9ae88d6
fix
yash-atreya fdb1b32
fix
yash-atreya 00a887c
fix
yash-atreya ac922fa
Merge branch 'master' into yash/fix-forge-update
yash-atreya 07ce536
Merge branch 'master' into yash/fix-forge-update
yash-atreya 53eed1f
Merge branch 'master' into yash/fix-forge-update
grandizzy 3a7e7eb
Do not run can_sync_foundry_lock test on win (fails on master branch …
grandizzy b7954c3
Merge branch 'master' into yash/fix-forge-update
yash-atreya 8276b42
feat: introduce `LockFile` type, use it in forge install and forge re…
yash-atreya 2f82d39
fix: account for clean lib/ dir while syncing lockfile
yash-atreya c4a13fd
fix: integrate lockfile into update
yash-atreya 1859e4d
clippy
yash-atreya 97fba00
fix
yash-atreya 090da39
fix
yash-atreya 7787895
feat(`forge`): introduces a `Lockfile` type (#9781)
yash-atreya ddf425d
clean up forge update
yash-atreya 1cd019a
Merge branch 'yash/lockfile' into yash/fix-forge-update
yash-atreya 5fb8861
nits
yash-atreya 6fc5850
nit
yash-atreya a55709c
fix: update branch rev in lockfile and print updates
yash-atreya c42c915
fix
yash-atreya 9b93c00
clippy
yash-atreya 94b5238
nit
yash-atreya 29d846a
fix
yash-atreya f25c0da
assert foundry lock in tests
yash-atreya 883d048
nit
yash-atreya 94edb91
refac ExtTester and test uni v4 foundry lock sync
yash-atreya cb7e434
oz sync test
yash-atreya 8fc72e1
fix: run sync after submodule update on install
yash-atreya 4f91ecc
fix: tag_for_commit should return earliest tag that contains commit +…
yash-atreya 50e4a4a
Merge branch 'master' into yash/fix-forge-update
yash-atreya 4c87a7a
fix: write lockfile after git succeeds
yash-atreya 44e334f
Merge branch 'master' into yash/fix-forge-update
yash-atreya d8a1e53
feat: account for deps pinned to a branch in .gitmodules while syncing
yash-atreya 311d4bc
fix: SUBMODULE_BRANCH_REGEX
yash-atreya dc22515
fix: properly parse paths from .gitmodules
yash-atreya 068ae3c
nit
yash-atreya 3ecfcfa
Merge branch 'master' into yash/fix-forge-update
yash-atreya 69ffe4c
clippy
yash-atreya 16604d9
fix tests
yash-atreya 905c5a0
fix test
yash-atreya e1b1a05
Merge branch 'master' into yash/fix-forge-update
zerosnacks 7abb02b
Merge branch 'master' into yash/fix-forge-update
zerosnacks a53524f
fix imports
zerosnacks 196fd63
Merge branch 'master' into yash/fix-forge-update
yash-atreya 0bf2e64
pretty lockfile
yash-atreya cc0275c
Merge branch 'master' into yash/fix-forge-update
yash-atreya b0e035c
regex const
yash-atreya 98223b0
fix: always lock to rev on sync
yash-atreya f2b2f3b
Merge branch 'master' into yash/fix-forge-update
yash-atreya cbeefa1
fmt
yash-atreya 595231e
Merge branch 'master' into yash/fix-forge-update
zerosnacks 55bdff3
Merge branch 'master' into yash/fix-forge-update
zerosnacks b70251a
fix clippy
zerosnacks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,18 +1,20 @@ | ||||||||||||||||||||||
use alloy_json_abi::JsonAbi; | ||||||||||||||||||||||
use alloy_primitives::U256; | ||||||||||||||||||||||
use alloy_primitives::{map::HashMap, U256}; | ||||||||||||||||||||||
use alloy_provider::{network::AnyNetwork, Provider}; | ||||||||||||||||||||||
use eyre::{ContextCompat, Result}; | ||||||||||||||||||||||
use foundry_common::{ | ||||||||||||||||||||||
provider::{ProviderBuilder, RetryProvider}, | ||||||||||||||||||||||
shell, | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
use foundry_config::{Chain, Config}; | ||||||||||||||||||||||
use itertools::Itertools; | ||||||||||||||||||||||
use serde::de::DeserializeOwned; | ||||||||||||||||||||||
use std::{ | ||||||||||||||||||||||
ffi::OsStr, | ||||||||||||||||||||||
future::Future, | ||||||||||||||||||||||
path::{Path, PathBuf}, | ||||||||||||||||||||||
process::{Command, Output, Stdio}, | ||||||||||||||||||||||
str::FromStr, | ||||||||||||||||||||||
time::{Duration, SystemTime, UNIX_EPOCH}, | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
use tracing_subscriber::prelude::*; | ||||||||||||||||||||||
|
@@ -41,6 +43,13 @@ pub const STATIC_FUZZ_SEED: [u8; 32] = [ | |||||||||||||||||||||
0x5d, 0x64, 0x0b, 0x19, 0xad, 0xf0, 0xe3, 0x57, 0xb8, 0xd4, 0xbe, 0x7d, 0x49, 0xee, 0x70, 0xe6, | ||||||||||||||||||||||
]; | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Regex used to parse `.gitmodules` file and capture the submodule path and branch. | ||||||||||||||||||||||
const SUBMODULE_BRANCH_REGEX: &str = r#"\[submodule "([^"]+)"\](?:[^\[]*?branch = ([^\s]+))"#; | ||||||||||||||||||||||
/// Regex used to parse `git submodule status` output. | ||||||||||||||||||||||
const SUBMODULE_STATUS_REGEX: &str = r"^[\s+-]?([a-f0-9]+)\s+([^\s]+)(?:\s+\([^)]+\))?$"; | ||||||||||||||||||||||
/// Capture the HEAD / default branch of the git repository from `git remote show origin`. | ||||||||||||||||||||||
const DEFAULT_BRANCH_REGEX: &str = r"HEAD branch: (.*)"; | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Useful extensions to [`std::path::Path`]. | ||||||||||||||||||||||
pub trait FoundryPathExt { | ||||||||||||||||||||||
/// Returns true if the [`Path`] ends with `.t.sol` | ||||||||||||||||||||||
|
@@ -392,10 +401,21 @@ impl<'a> Git<'a> { | |||||||||||||||||||||
.map(drop) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn checkout_at(self, tag: impl AsRef<OsStr>, at: &Path) -> Result<()> { | ||||||||||||||||||||||
self.cmd_at(at).arg("checkout").arg(tag).exec().map(drop) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn init(self) -> Result<()> { | ||||||||||||||||||||||
self.cmd().arg("init").exec().map(drop) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn current_rev_branch(self, at: &Path) -> Result<(String, String)> { | ||||||||||||||||||||||
let rev = self.cmd_at(at).args(["rev-parse", "HEAD"]).get_stdout_lossy()?; | ||||||||||||||||||||||
let branch = | ||||||||||||||||||||||
self.cmd_at(at).args(["rev-parse", "--abbrev-ref", "HEAD"]).get_stdout_lossy()?; | ||||||||||||||||||||||
Ok((rev, branch)) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
#[expect(clippy::should_implement_trait)] // this is not std::ops::Add clippy | ||||||||||||||||||||||
pub fn add<I, S>(self, paths: I) -> Result<()> | ||||||||||||||||||||||
where | ||||||||||||||||||||||
|
@@ -469,6 +489,26 @@ impl<'a> Git<'a> { | |||||||||||||||||||||
.map(|stdout| !stdout.is_empty()) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn has_tag(self, tag: impl AsRef<OsStr>, at: &Path) -> Result<bool> { | ||||||||||||||||||||||
self.cmd_at(at) | ||||||||||||||||||||||
.args(["tag", "--list"]) | ||||||||||||||||||||||
.arg(tag) | ||||||||||||||||||||||
.get_stdout_lossy() | ||||||||||||||||||||||
.map(|stdout| !stdout.is_empty()) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn has_rev(self, rev: impl AsRef<OsStr>, at: &Path) -> Result<bool> { | ||||||||||||||||||||||
self.cmd_at(at) | ||||||||||||||||||||||
.args(["cat-file", "-t"]) | ||||||||||||||||||||||
.arg(rev) | ||||||||||||||||||||||
.get_stdout_lossy() | ||||||||||||||||||||||
.map(|stdout| &stdout == "commit") | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn get_rev(self, tag_or_branch: impl AsRef<OsStr>, at: &Path) -> Result<String> { | ||||||||||||||||||||||
self.cmd_at(at).args(["rev-list", "-n", "1"]).arg(tag_or_branch).get_stdout_lossy() | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn ensure_clean(self) -> Result<()> { | ||||||||||||||||||||||
if self.is_clean()? { | ||||||||||||||||||||||
Ok(()) | ||||||||||||||||||||||
|
@@ -497,6 +537,64 @@ ignore them in the `.gitignore` file." | |||||||||||||||||||||
self.cmd().arg("tag").get_stdout_lossy() | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Returns the tag the commit first appeared in. | ||||||||||||||||||||||
/// | ||||||||||||||||||||||
/// E.g Take rev = `abc1234`. This commit can be found in multiple releases (tags). | ||||||||||||||||||||||
/// Consider releases: `v0.1.0`, `v0.2.0`, `v0.3.0` in chronological order, `rev` first appeared | ||||||||||||||||||||||
/// in `v0.2.0`. | ||||||||||||||||||||||
/// | ||||||||||||||||||||||
/// Hence, `tag_for_commit("abc1234")` will return `v0.2.0`. | ||||||||||||||||||||||
pub fn tag_for_commit(self, rev: &str, at: &Path) -> Result<Option<String>> { | ||||||||||||||||||||||
self.cmd_at(at) | ||||||||||||||||||||||
.args(["tag", "--contains"]) | ||||||||||||||||||||||
.arg(rev) | ||||||||||||||||||||||
.get_stdout_lossy() | ||||||||||||||||||||||
.map(|stdout| stdout.lines().next().map(str::to_string)) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Returns a list of tuples of submodule paths and their respective branches. | ||||||||||||||||||||||
/// | ||||||||||||||||||||||
/// This function reads the `.gitmodules` file and returns the paths of all submodules that have | ||||||||||||||||||||||
/// a branch. The paths are relative to the Git::root_of(git.root) and not lib/ directory. | ||||||||||||||||||||||
/// | ||||||||||||||||||||||
/// `at` is the dir in which the `.gitmodules` file is located, this is the git root. | ||||||||||||||||||||||
/// `lib` is name of the directory where the submodules are located. | ||||||||||||||||||||||
pub fn read_submodules_with_branch( | ||||||||||||||||||||||
self, | ||||||||||||||||||||||
at: &Path, | ||||||||||||||||||||||
lib: &OsStr, | ||||||||||||||||||||||
) -> Result<HashMap<PathBuf, String>> { | ||||||||||||||||||||||
// Read the .gitmodules file | ||||||||||||||||||||||
let gitmodules = foundry_common::fs::read_to_string(at.join(".gitmodules"))?; | ||||||||||||||||||||||
let re = regex::Regex::new(SUBMODULE_BRANCH_REGEX)?; | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These regular expressions can be optimized at compile time as follows - there are several examples in Foundry
foundry/crates/cli/src/opts/dependency.rs Lines 7 to 16 in 8b04d0d
|
||||||||||||||||||||||
|
||||||||||||||||||||||
let paths = re | ||||||||||||||||||||||
.captures_iter(&gitmodules) | ||||||||||||||||||||||
.map(|cap| { | ||||||||||||||||||||||
let path_str = cap.get(1).unwrap().as_str(); | ||||||||||||||||||||||
let path = PathBuf::from_str(path_str).unwrap(); | ||||||||||||||||||||||
trace!(path = %path.display(), "unstripped path"); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// Keep only the components that come after the lib directory. | ||||||||||||||||||||||
// This needs to be done because the lockfile uses paths relative foundry project | ||||||||||||||||||||||
// root whereas .gitmodules use paths relative to the git root which may not be the | ||||||||||||||||||||||
// project root. e.g monorepo. | ||||||||||||||||||||||
// Hence, if path is lib/solady, then `lib/solady` is kept. if path is | ||||||||||||||||||||||
grandizzy marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
// packages/contract-bedrock/lib/solady, then `lib/solady` is kept. | ||||||||||||||||||||||
let lib_pos = path.components().find_position(|c| c.as_os_str() == lib); | ||||||||||||||||||||||
let path = path | ||||||||||||||||||||||
.components() | ||||||||||||||||||||||
.skip(lib_pos.map(|(i, _)| i).unwrap_or(0)) | ||||||||||||||||||||||
.collect::<PathBuf>(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
let branch = cap.get(2).unwrap().as_str().to_string(); | ||||||||||||||||||||||
(path, branch) | ||||||||||||||||||||||
}) | ||||||||||||||||||||||
.collect::<HashMap<_, _>>(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
Ok(paths) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn has_missing_dependencies<I, S>(self, paths: I) -> Result<bool> | ||||||||||||||||||||||
where | ||||||||||||||||||||||
I: IntoIterator<Item = S>, | ||||||||||||||||||||||
|
@@ -574,10 +672,35 @@ ignore them in the `.gitignore` file." | |||||||||||||||||||||
.map(drop) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// If the status is prefix with `-`, the submodule is not initialized. | ||||||||||||||||||||||
/// | ||||||||||||||||||||||
/// Ref: <https://git-scm.com/docs/git-submodule#Documentation/git-submodule.txt-status--cached--recursive--ltpathgt82308203> | ||||||||||||||||||||||
pub fn submodules_unintialized(self) -> Result<bool> { | ||||||||||||||||||||||
self.cmd() | ||||||||||||||||||||||
.args(["submodule", "status"]) | ||||||||||||||||||||||
.get_stdout_lossy() | ||||||||||||||||||||||
.map(|stdout| stdout.lines().any(|line| line.starts_with('-'))) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Initializes the git submodules. | ||||||||||||||||||||||
pub fn submodule_init(self) -> Result<()> { | ||||||||||||||||||||||
self.cmd().stderr(self.stderr()).args(["submodule", "init"]).exec().map(drop) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Gets the default branch of the git repository. | ||||||||||||||||||||||
pub fn default_branch(&self, at: &Path) -> Result<String> { | ||||||||||||||||||||||
self.cmd_at(at).args(["remote", "show", "origin"]).get_stdout_lossy().map(|stdout| { | ||||||||||||||||||||||
let re = regex::Regex::new(DEFAULT_BRANCH_REGEX)?; | ||||||||||||||||||||||
let caps = | ||||||||||||||||||||||
re.captures(&stdout).ok_or_else(|| eyre::eyre!("Could not find HEAD branch"))?; | ||||||||||||||||||||||
Ok(caps.get(1).unwrap().as_str().to_string()) | ||||||||||||||||||||||
})? | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn submodules(&self) -> Result<Submodules> { | ||||||||||||||||||||||
self.cmd().args(["submodule", "status"]).get_stdout_lossy().map(|stdout| stdout.parse())? | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn submodule_sync(self) -> Result<()> { | ||||||||||||||||||||||
self.cmd().stderr(self.stderr()).args(["submodule", "sync"]).exec().map(drop) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
@@ -610,13 +733,113 @@ ignore them in the `.gitignore` file." | |||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Deserialized `git submodule status lib/dep` output. | ||||||||||||||||||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] | ||||||||||||||||||||||
pub struct Submodule { | ||||||||||||||||||||||
/// Current commit hash the submodule is checked out at. | ||||||||||||||||||||||
rev: String, | ||||||||||||||||||||||
/// Relative path to the submodule. | ||||||||||||||||||||||
path: PathBuf, | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
impl Submodule { | ||||||||||||||||||||||
pub fn new(rev: String, path: PathBuf) -> Self { | ||||||||||||||||||||||
Self { rev, path } | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn rev(&self) -> &str { | ||||||||||||||||||||||
&self.rev | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn path(&self) -> &PathBuf { | ||||||||||||||||||||||
&self.path | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
impl FromStr for Submodule { | ||||||||||||||||||||||
type Err = eyre::Report; | ||||||||||||||||||||||
|
||||||||||||||||||||||
fn from_str(s: &str) -> Result<Self> { | ||||||||||||||||||||||
let re = regex::Regex::new(SUBMODULE_STATUS_REGEX)?; | ||||||||||||||||||||||
|
||||||||||||||||||||||
let caps = re.captures(s).ok_or_else(|| eyre::eyre!("Invalid submodule status format"))?; | ||||||||||||||||||||||
|
||||||||||||||||||||||
Ok(Self { | ||||||||||||||||||||||
rev: caps.get(1).unwrap().as_str().to_string(), | ||||||||||||||||||||||
path: PathBuf::from(caps.get(2).unwrap().as_str()), | ||||||||||||||||||||||
}) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Deserialized `git submodule status` output. | ||||||||||||||||||||||
#[derive(Debug, Clone, PartialEq, Eq)] | ||||||||||||||||||||||
pub struct Submodules(pub Vec<Submodule>); | ||||||||||||||||||||||
|
||||||||||||||||||||||
impl Submodules { | ||||||||||||||||||||||
pub fn len(&self) -> usize { | ||||||||||||||||||||||
self.0.len() | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
pub fn is_empty(&self) -> bool { | ||||||||||||||||||||||
self.0.is_empty() | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
impl FromStr for Submodules { | ||||||||||||||||||||||
type Err = eyre::Report; | ||||||||||||||||||||||
|
||||||||||||||||||||||
fn from_str(s: &str) -> Result<Self> { | ||||||||||||||||||||||
let subs = s.lines().map(str::parse).collect::<Result<Vec<Submodule>>>()?; | ||||||||||||||||||||||
Ok(Self(subs)) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
impl<'a> IntoIterator for &'a Submodules { | ||||||||||||||||||||||
type Item = &'a Submodule; | ||||||||||||||||||||||
type IntoIter = std::slice::Iter<'a, Submodule>; | ||||||||||||||||||||||
|
||||||||||||||||||||||
fn into_iter(self) -> Self::IntoIter { | ||||||||||||||||||||||
self.0.iter() | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||
mod tests { | ||||||||||||||||||||||
use super::*; | ||||||||||||||||||||||
use foundry_common::fs; | ||||||||||||||||||||||
use std::{env, fs::File, io::Write}; | ||||||||||||||||||||||
use tempfile::tempdir; | ||||||||||||||||||||||
|
||||||||||||||||||||||
#[test] | ||||||||||||||||||||||
fn parse_submodule_status() { | ||||||||||||||||||||||
let s = "+8829465a08cac423dcf59852f21e448449c1a1a8 lib/openzeppelin-contracts (v4.8.0-791-g8829465a)"; | ||||||||||||||||||||||
let sub = Submodule::from_str(s).unwrap(); | ||||||||||||||||||||||
assert_eq!(sub.rev(), "8829465a08cac423dcf59852f21e448449c1a1a8"); | ||||||||||||||||||||||
assert_eq!(sub.path(), Path::new("lib/openzeppelin-contracts")); | ||||||||||||||||||||||
|
||||||||||||||||||||||
let s = "-8829465a08cac423dcf59852f21e448449c1a1a8 lib/openzeppelin-contracts"; | ||||||||||||||||||||||
let sub = Submodule::from_str(s).unwrap(); | ||||||||||||||||||||||
assert_eq!(sub.rev(), "8829465a08cac423dcf59852f21e448449c1a1a8"); | ||||||||||||||||||||||
assert_eq!(sub.path(), Path::new("lib/openzeppelin-contracts")); | ||||||||||||||||||||||
|
||||||||||||||||||||||
let s = "8829465a08cac423dcf59852f21e448449c1a1a8 lib/openzeppelin-contracts"; | ||||||||||||||||||||||
let sub = Submodule::from_str(s).unwrap(); | ||||||||||||||||||||||
assert_eq!(sub.rev(), "8829465a08cac423dcf59852f21e448449c1a1a8"); | ||||||||||||||||||||||
assert_eq!(sub.path(), Path::new("lib/openzeppelin-contracts")); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
#[test] | ||||||||||||||||||||||
fn parse_multiline_submodule_status() { | ||||||||||||||||||||||
let s = r#"+d3db4ef90a72b7d24aa5a2e5c649593eaef7801d lib/forge-std (v1.9.4-6-gd3db4ef) | ||||||||||||||||||||||
+8829465a08cac423dcf59852f21e448449c1a1a8 lib/openzeppelin-contracts (v4.8.0-791-g8829465a) | ||||||||||||||||||||||
"#; | ||||||||||||||||||||||
let subs = Submodules::from_str(s).unwrap().0; | ||||||||||||||||||||||
assert_eq!(subs.len(), 2); | ||||||||||||||||||||||
assert_eq!(subs[0].rev(), "d3db4ef90a72b7d24aa5a2e5c649593eaef7801d"); | ||||||||||||||||||||||
assert_eq!(subs[0].path(), Path::new("lib/forge-std")); | ||||||||||||||||||||||
assert_eq!(subs[1].rev(), "8829465a08cac423dcf59852f21e448449c1a1a8"); | ||||||||||||||||||||||
assert_eq!(subs[1].path(), Path::new("lib/openzeppelin-contracts")); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
#[test] | ||||||||||||||||||||||
fn foundry_path_ext_works() { | ||||||||||||||||||||||
let p = Path::new("contracts/MyTest.t.sol"); | ||||||||||||||||||||||
|
@@ -653,4 +876,92 @@ mod tests { | |||||||||||||||||||||
assert_eq!(env::var("TESTCWDKEY").unwrap(), "cwd_val"); | ||||||||||||||||||||||
assert_eq!(env::var("TESTPRJKEY").unwrap(), "prj_val"); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
#[test] | ||||||||||||||||||||||
fn test_read_gitmodules_regex() { | ||||||||||||||||||||||
// Regex to read and return the paths of all submodules that have a branch | ||||||||||||||||||||||
let re = regex::Regex::new(SUBMODULE_BRANCH_REGEX).unwrap(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
let gitmodules = r#" | ||||||||||||||||||||||
[submodule "lib/solady"] | ||||||||||||||||||||||
path = lib/solady | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
branch = v0.1.0 | ||||||||||||||||||||||
[submodule "lib/openzeppelin-contracts"] | ||||||||||||||||||||||
path = lib/openzeppelin-contracts | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
branch = v4.8.0-791-g8829465a | ||||||||||||||||||||||
[submodule "lib/forge-std"] | ||||||||||||||||||||||
path = lib/forge-std | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
"#; | ||||||||||||||||||||||
|
||||||||||||||||||||||
let paths = re | ||||||||||||||||||||||
.captures_iter(gitmodules) | ||||||||||||||||||||||
.map(|cap| { | ||||||||||||||||||||||
( | ||||||||||||||||||||||
PathBuf::from_str(cap.get(1).unwrap().as_str()).unwrap(), | ||||||||||||||||||||||
String::from(cap.get(2).unwrap().as_str()), | ||||||||||||||||||||||
) | ||||||||||||||||||||||
}) | ||||||||||||||||||||||
.collect::<HashMap<_, _>>(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
assert_eq!(paths.get(Path::new("lib/solady")).unwrap(), "v0.1.0"); | ||||||||||||||||||||||
assert_eq!( | ||||||||||||||||||||||
paths.get(Path::new("lib/openzeppelin-contracts")).unwrap(), | ||||||||||||||||||||||
"v4.8.0-791-g8829465a" | ||||||||||||||||||||||
); | ||||||||||||||||||||||
|
||||||||||||||||||||||
let no_branch_gitmodules = r#" | ||||||||||||||||||||||
[submodule "lib/solady"] | ||||||||||||||||||||||
path = lib/solady | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
[submodule "lib/openzeppelin-contracts"] | ||||||||||||||||||||||
path = lib/openzeppelin-contracts | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
[submodule "lib/forge-std"] | ||||||||||||||||||||||
path = lib/forge-std | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
"#; | ||||||||||||||||||||||
let paths = re | ||||||||||||||||||||||
.captures_iter(no_branch_gitmodules) | ||||||||||||||||||||||
.map(|cap| { | ||||||||||||||||||||||
( | ||||||||||||||||||||||
PathBuf::from_str(cap.get(1).unwrap().as_str()).unwrap(), | ||||||||||||||||||||||
String::from(cap.get(2).unwrap().as_str()), | ||||||||||||||||||||||
) | ||||||||||||||||||||||
}) | ||||||||||||||||||||||
.collect::<HashMap<_, _>>(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
assert!(paths.is_empty()); | ||||||||||||||||||||||
|
||||||||||||||||||||||
let branch_in_between = r#" | ||||||||||||||||||||||
[submodule "lib/solady"] | ||||||||||||||||||||||
path = lib/solady | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
[submodule "lib/openzeppelin-contracts"] | ||||||||||||||||||||||
path = lib/openzeppelin-contracts | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
branch = v4.8.0-791-g8829465a | ||||||||||||||||||||||
[submodule "lib/forge-std"] | ||||||||||||||||||||||
path = lib/forge-std | ||||||||||||||||||||||
url = "" | ||||||||||||||||||||||
"#; | ||||||||||||||||||||||
|
||||||||||||||||||||||
let paths = re | ||||||||||||||||||||||
.captures_iter(branch_in_between) | ||||||||||||||||||||||
.map(|cap| { | ||||||||||||||||||||||
( | ||||||||||||||||||||||
PathBuf::from_str(cap.get(1).unwrap().as_str()).unwrap(), | ||||||||||||||||||||||
String::from(cap.get(2).unwrap().as_str()), | ||||||||||||||||||||||
) | ||||||||||||||||||||||
}) | ||||||||||||||||||||||
.collect::<HashMap<_, _>>(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
assert_eq!(paths.len(), 1); | ||||||||||||||||||||||
assert_eq!( | ||||||||||||||||||||||
paths.get(Path::new("lib/openzeppelin-contracts")).unwrap(), | ||||||||||||||||||||||
"v4.8.0-791-g8829465a" | ||||||||||||||||||||||
); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could possibly be replaced with the more lightweight
yields