Skip to content

Commit

Permalink
feat(forge): shallow clones (again) (foundry-rs#5209)
Browse files Browse the repository at this point in the history
* feat(forge): shallow clones

* chore: rm debuggings

* chore: clippy

* fix: use proper path for .gitmodules

* chore: restore default behaviour
  • Loading branch information
DaniPopes authored Jun 24, 2023
1 parent ed5eb97 commit e6574c9
Show file tree
Hide file tree
Showing 15 changed files with 718 additions and 694 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 3 additions & 14 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ repository = "https://github.com/foundry-rs/foundry"
keywords = ["ethereum", "web3"]

[build-dependencies]
vergen = { version = "8", default-features = false, features = [
"build",
"git",
"git2",
] }
vergen = { version = "8", default-features = false, features = ["build", "git", "git2"] }

[dependencies]
# foundry internal
Expand All @@ -36,11 +32,7 @@ clap_complete = "4"
clap_complete_fig = "4"
yansi = "0.5"
tracing-error = "0.2"
tracing-subscriber = { version = "0.3", features = [
"registry",
"env-filter",
"fmt",
] }
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter", "fmt"] }
tracing = "0.1"
console = "0.15"
watchexec = "2"
Expand Down Expand Up @@ -86,7 +78,6 @@ bytes = "1.4"
strum = { version = "0.25", features = ["derive"] }
thiserror = "1"
indicatif = "0.17"
which = "4"
parking_lot = "0.12"

[dev-dependencies]
Expand All @@ -97,9 +88,7 @@ pretty_assertions = "1"
toml = "0.7"
serial_test = "2"
criterion = "0.4"
svm = { package = "svm-rs", version = "0.2", default-features = false, features = [
"rustls",
] }
svm = { package = "svm-rs", version = "0.2", default-features = false, features = ["rustls"] }

[features]
default = ["rustls"]
Expand Down
16 changes: 8 additions & 8 deletions cli/src/cmd/cast/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ mod tests {
match args {
WalletSubcommands::Sign { message, data, from_file, .. } => {
assert_eq!(message, "deadbeef".to_string());
assert_eq!(data, false);
assert_eq!(from_file, false);
assert!(!data);
assert!(!from_file);
}
_ => panic!("expected WalletSubcommands::Sign"),
}
Expand All @@ -206,8 +206,8 @@ mod tests {
match args {
WalletSubcommands::Sign { message, data, from_file, .. } => {
assert_eq!(message, "0xdeadbeef".to_string());
assert_eq!(data, false);
assert_eq!(from_file, false);
assert!(!data);
assert!(!from_file);
}
_ => panic!("expected WalletSubcommands::Sign"),
}
Expand All @@ -219,8 +219,8 @@ mod tests {
match args {
WalletSubcommands::Sign { message, data, from_file, .. } => {
assert_eq!(message, "{ ... }".to_string());
assert_eq!(data, true);
assert_eq!(from_file, false);
assert!(data);
assert!(!from_file);
}
_ => panic!("expected WalletSubcommands::Sign"),
}
Expand All @@ -238,8 +238,8 @@ mod tests {
match args {
WalletSubcommands::Sign { message, data, from_file, .. } => {
assert_eq!(message, "tests/data/typed_data.json".to_string());
assert_eq!(data, true);
assert_eq!(from_file, true);
assert!(data);
assert!(from_file);
}
_ => panic!("expected WalletSubcommands::Sign"),
}
Expand Down
2 changes: 1 addition & 1 deletion cli/src/cmd/forge/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl Cmd for BuildArgs {
let mut config = self.try_load_config_emit_warnings()?;
let mut project = config.project()?;

if install::install_missing_dependencies(&mut config, &project, self.args.silent) &&
if install::install_missing_dependencies(&mut config, self.args.silent) &&
config.auto_detect_remappings
{
// need to re-configure here to also catch additional remappings
Expand Down
3 changes: 1 addition & 2 deletions cli/src/cmd/forge/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,9 @@ impl Cmd for CoverageArgs {

fn run(self) -> eyre::Result<Self::Output> {
let (mut config, evm_opts) = self.load_config_and_evm_opts_emit_warnings()?;
let project = config.project()?;

// install missing dependencies
if install::install_missing_dependencies(&mut config, &project, self.build_args().silent) &&
if install::install_missing_dependencies(&mut config, self.build_args().silent) &&
config.auto_detect_remappings
{
// need to re-configure here to also catch additional remappings
Expand Down
9 changes: 1 addition & 8 deletions cli/src/cmd/forge/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,7 @@ impl Cmd for DocArgs {
}
}

let commit =
Command::new("git").args(["rev-parse", "HEAD"]).output().ok().and_then(|output| {
if !output.stdout.is_empty() {
String::from_utf8(output.stdout).ok().map(|commit| commit.trim().to_owned())
} else {
None
}
});
let commit = crate::utils::Git::new(&root).commit_hash(false).ok();

let mut builder = DocBuilder::new(root.clone(), config.project_paths().sources)
.with_should_build(self.build)
Expand Down
136 changes: 38 additions & 98 deletions cli/src/cmd/forge/init.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
//! init command

use crate::{
cmd::{
forge::install::{ensure_git_status_clean, install, DependencyInstallOpts},
Cmd,
},
opts::Dependency,
utils::{p_println, CommandUtils},
cmd::{forge::install::DependencyInstallOpts, Cmd},
utils::{p_println, Git},
};
use clap::{Parser, ValueHint};
use ethers::solc::remappings::Remapping;
use foundry_common::fs;
use foundry_config::Config;
use std::{
path::{Path, PathBuf},
process::{Command, Stdio},
str::FromStr,
};
use std::path::{Path, PathBuf};
use yansi::Paint;

/// CLI arguments for `forge init`.
Expand All @@ -30,17 +22,8 @@ pub struct InitArgs {
#[clap(long, short)]
template: Option<String>,

/// Do not create a git repository.
#[clap(long, conflicts_with = "template")]
no_git: bool,

/// Do not create an initial commit.
#[clap(long, conflicts_with = "template")]
no_commit: bool,

/// Do not print any messages.
#[clap(long, short)]
quiet: bool,
#[clap(flatten)]
opts: DependencyInstallOpts,

/// Do not install dependencies from the network.
#[clap(long, conflicts_with = "template", visible_alias = "no-deps")]
Expand All @@ -60,49 +43,40 @@ impl Cmd for InitArgs {
type Output = ();

fn run(self) -> eyre::Result<Self::Output> {
let InitArgs { root, template, no_git, no_commit, quiet, offline, force, vscode } = self;
let InitArgs { root, template, opts, offline, force, vscode } = self;
let DependencyInstallOpts { shallow, no_git, no_commit, quiet } = opts;

// create the root dir if it does not exist
if !root.exists() {
fs::create_dir_all(&root)?;
}
let root = dunce::canonicalize(root)?;
let git = Git::new(&root).quiet(quiet).shallow(shallow);

// if a template is provided, then this command clones the template repo, removes the .git
// folder, and initializes a new git repo—-this ensures there is no history from the
// template and the template is not set as a remote.
if let Some(template) = template {
let template = if template.starts_with("https://") {
let template = if template.contains("://") {
template
} else {
"https://github.com/".to_string() + &template
};
p_println!(!quiet => "Initializing {} from {}...", root.display(), template);

Command::new("git")
.args(["clone", "--recursive", &template, &root.display().to_string()])
.exec()?;

// Navigate to the newly cloned repo.
let initial_dir = std::env::current_dir()?;
std::env::set_current_dir(&root)?;
Git::clone(shallow, &template, Some(&root))?;

// Modify the git history.
let git_output =
Command::new("git").args(["rev-parse", "--short", "HEAD"]).output()?.stdout;
let commit_hash = String::from_utf8(git_output)?;
let commit_hash = git.commit_hash(true)?;
std::fs::remove_dir_all(".git")?;
Command::new("git").arg("init").exec()?;
Command::new("git").args(["add", "--all"]).exec()?;

git.init()?;
git.add(Some("--all"))?;
let commit_msg = format!("chore: init from {template} at {commit_hash}");
Command::new("git").args(["commit", "-m", &commit_msg]).exec()?;

// Navigate back.
std::env::set_current_dir(initial_dir)?;
git.commit(&commit_msg)?;
} else {
// if target is not empty
if root.read_dir().map(|mut i| i.next().is_some()).unwrap_or(false) {
if root.read_dir().map_or(false, |mut i| i.next().is_some()) {
if !force {
eyre::bail!(
"Cannot run `init` on a non-empty directory.\n\
Expand All @@ -114,8 +88,8 @@ impl Cmd for InitArgs {
}

// ensure git status is clean before generating anything
if !no_git && !no_commit && !force && is_git(&root)? {
ensure_git_status_clean(&root)?;
if !no_git && !no_commit && !force && git.is_in_repo()? {
git.ensure_clean()?;
}

p_println!(!quiet => "Initializing {}...", root.display());
Expand Down Expand Up @@ -146,22 +120,21 @@ impl Cmd for InitArgs {
if !dest.exists() {
fs::write(dest, config.clone().into_basic().to_string_pretty()?)?;
}
let git = self.opts.git(&config);

// sets up git
// set up the repo
if !no_git {
init_git_repo(&root, no_commit)?;
init_git_repo(git, no_commit)?;
}

// install forge-std
if !offline {
let opts = DependencyInstallOpts { no_git, no_commit, quiet };

if root.join("lib/forge-std").exists() {
p_println!(!quiet => "\"lib/forge-std\" already exists, skipping install....");
install(&mut config, vec![], opts)?;
self.opts.install(&mut config, vec![])?;
} else {
Dependency::from_str("https://github.com/foundry-rs/forge-std")
.and_then(|dependency| install(&mut config, vec![dependency], opts))?;
let dep = "https://github.com/foundry-rs/forge-std".parse()?;
self.opts.install(&mut config, vec![dep])?;
}
}

Expand All @@ -171,68 +144,44 @@ impl Cmd for InitArgs {
}
}

p_println!(!quiet => " {} forge project.", Paint::green("Initialized"));
p_println!(!quiet => " {} forge project", Paint::green("Initialized"));
Ok(())
}
}

/// Returns `true` if `root` is already in an existing git repository
fn is_git(root: &Path) -> eyre::Result<bool> {
let is_git = Command::new("git")
.args(["rev-parse", "--is-inside-work-tree"])
.current_dir(root)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.status()?;

Ok(is_git.success())
}

/// Returns the commit hash of the project if it exists
pub fn get_commit_hash(root: &Path) -> Option<String> {
if is_git(root).ok()? {
let output = Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.current_dir(root)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.get_stdout_lossy()
.ok()?;
Some(output)
} else {
None
}
Git::new(root).commit_hash(true).ok()
}

/// Initialises `root` as a git repository, if it isn't one already.
///
/// Creates `.gitignore` and `.github/workflows/test.yml`, if they don't exist already.
///
/// Commits everything in `root` if `no_commit` is false.
fn init_git_repo(root: &Path, no_commit: bool) -> eyre::Result<()> {
fn init_git_repo(git: Git<'_>, no_commit: bool) -> eyre::Result<()> {
// git init
if !is_git(root)? {
Command::new("git").arg("init").current_dir(root).exec()?;
if !git.is_in_repo()? {
git.init()?;
}

// .gitignore
let gitignore = root.join(".gitignore");
let gitignore = git.root.join(".gitignore");
if !gitignore.exists() {
fs::write(gitignore, include_str!("../../../assets/.gitignoreTemplate"))?;
}

// github workflow
let gh = root.join(".github").join("workflows");
if !gh.exists() {
fs::create_dir_all(&gh)?;
let workflow_path = gh.join("test.yml");
fs::write(workflow_path, include_str!("../../../assets/workflowTemplate.yml"))?;
let workflow = git.root.join(".github/workflows/test.yml");
if !workflow.exists() {
fs::create_dir_all(workflow.parent().unwrap())?;
fs::write(workflow, include_str!("../../../assets/workflowTemplate.yml"))?;
}

// commit everything
if !no_commit {
Command::new("git").args(["add", "."]).current_dir(root).exec()?;
Command::new("git").args(["commit", "-m", "chore: forge init"]).current_dir(root).exec()?;
git.add(Some("--all"))?;
git.commit("chore: forge init")?;
}

Ok(())
Expand All @@ -242,12 +191,12 @@ fn init_git_repo(root: &Path, no_commit: bool) -> eyre::Result<()> {
fn init_vscode(root: &Path) -> eyre::Result<()> {
let remappings_file = root.join("remappings.txt");
if !remappings_file.exists() {
let mut remappings = relative_remappings(&root.join("lib"), root)
let mut remappings = Remapping::find_many(root.join("lib"))
.into_iter()
.map(|r| r.to_string())
.map(|r| r.into_relative(root).to_relative_remapping().to_string())
.collect::<Vec<_>>();
remappings.sort();
if !remappings.is_empty() {
remappings.sort();
let content = remappings.join("\n");
fs::write(remappings_file, content)?;
}
Expand Down Expand Up @@ -280,12 +229,3 @@ fn init_vscode(root: &Path) -> eyre::Result<()> {

Ok(())
}

/// Returns all remappings found in the `lib` path relative to `root`
fn relative_remappings(lib: &Path, root: &Path) -> Vec<Remapping> {
Remapping::find_many(lib)
.into_iter()
.map(|r| r.into_relative(root).to_relative_remapping())
.map(Into::into)
.collect()
}
Loading

0 comments on commit e6574c9

Please sign in to comment.