summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2020-12-11 11:36:11 +0100
committerMatthias Beyer <mail@beyermatthias.de>2020-12-11 11:41:20 +0100
commit746e0cf6a73f81b1f746948422b746a07b4621c0 (patch)
treeed0139aa2f971bb3fe5ae57ce709376c65a72d60
parentb25131ed65496c9cf0f616f761517590aa16d120 (diff)
Add "lint" subcommand
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--src/cli.rs18
-rw-r--r--src/commands/lint.rs87
-rw-r--r--src/commands/mod.rs3
-rw-r--r--src/main.rs5
4 files changed, 113 insertions, 0 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 7acd05e..a6b7843 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -596,6 +596,24 @@ pub fn cli<'a>() -> App<'a> {
)
)
+ .subcommand(App::new("lint")
+ .about("Release artifacts")
+ .arg(Arg::new("package_name")
+ .required(false)
+ .multiple(false)
+ .index(1)
+ .value_name("NAME")
+ .about("Package name to lint (if not present, every package will be linted")
+ )
+ .arg(Arg::new("package_version")
+ .required(false)
+ .multiple(false)
+ .index(2)
+ .value_name("VERSION_CONSTRAINT")
+ .about("A version constraint to search for (optional), E.G. '=1.0.0'")
+ )
+ )
+
}
fn script_arg_line_numbers<'a>() -> clap::Arg<'a> {
diff --git a/src/commands/lint.rs b/src/commands/lint.rs
new file mode 100644
index 0000000..8a3222b
--- /dev/null
+++ b/src/commands/lint.rs
@@ -0,0 +1,87 @@
+use anyhow::anyhow;
+use anyhow::Result;
+use clap::ArgMatches;
+use log::{error, info, trace};
+use tokio::stream::StreamExt;
+
+use crate::config::*;
+use crate::package::Shebang;
+use crate::repository::Repository;
+use crate::package::PackageName;
+use crate::package::PackageVersionConstraint;
+use crate::package::ScriptBuilder;
+use crate::util::progress::ProgressBars;
+
+pub async fn lint(matches: &ArgMatches, progressbars: ProgressBars, config: &Configuration, repo: Repository) -> Result<()> {
+ let linter = config.script_linter()
+ .as_ref()
+ .ok_or_else(|| anyhow!("No linting script configured"))?;
+
+ let shebang = Shebang::from(config.shebang().clone());
+ let pname = matches.value_of("package_name").map(String::from).map(PackageName::from);
+ let pvers = matches.value_of("package_version").map(String::from).map(PackageVersionConstraint::new).transpose()?;
+
+ let bar = progressbars.bar();
+ bar.set_message("Linting package scripts...");
+
+ let lint_results = repo.packages()
+ .filter(|p| pname.as_ref().map(|n| p.name() == n).unwrap_or(true))
+ .filter(|p| pvers.as_ref().map(|v| v.matches(p.version())).unwrap_or(true))
+ .map(|pkg| {
+ let shebang = shebang.clone();
+ let bar = bar.clone();
+ async move {
+ trace!("Linting script of {} {} with '{}'", pkg.name(), pkg.version(), linter.display());
+ let cmd = tokio::process::Command::new(linter);
+ let script = ScriptBuilder::new(&shebang)
+ .build(pkg, config.available_phases(), *config.strict_script_interpolation())?;
+
+ let (status, stdout, stderr) = script.lint(cmd).await?;
+ bar.inc(1);
+ Ok((pkg.name().clone(), pkg.version().clone(), status, stdout, stderr))
+ }
+ })
+ .collect::<futures::stream::FuturesUnordered<_>>()
+ .collect::<Result<Vec<_>>>()
+ .await?
+ .into_iter()
+ .map(|tpl| {
+ let pkg_name = tpl.0;
+ let pkg_vers = tpl.1;
+ let status = tpl.2;
+ let stdout = tpl.3;
+ let stderr = tpl.4;
+
+ if status.success() {
+ info!("Linting {pkg_name} {pkg_vers} script (exit {status}):\nstdout:\n{stdout}\n\nstderr:\n\n{stderr}",
+ pkg_name = pkg_name,
+ pkg_vers = pkg_vers,
+ status = status,
+ stdout = stdout,
+ stderr = stderr
+ );
+ true
+ } else {
+ error!("Linting {pkg_name} {pkg_vers} errored ({status}):\n\nstdout:\n{stdout}\n\nstderr:\n{stderr}\n\n",
+ pkg_name = pkg_name,
+ pkg_vers = pkg_vers,
+ status = status,
+ stdout = stdout,
+ stderr = stderr
+ );
+ false
+ }
+ })
+ .collect::<Vec<_>>();
+
+ let lint_ok = lint_results.iter().all(|b| *b);
+
+ if !lint_ok {
+ bar.finish_with_message("Linting errored");
+ return Err(anyhow!("Linting was not successful"))
+ } else {
+ bar.finish_with_message(&format!("Finished linting {} package scripts", lint_results.len()));
+ Ok(())
+ }
+}
+
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index c9c64c3..2fb124e 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -13,6 +13,9 @@ pub use find_pkg::find_pkg;
mod dependencies_of;
pub use dependencies_of::dependencies_of;
+mod lint;
+pub use lint::lint;
+
mod what_depends;
pub use what_depends::what_depends;
diff --git a/src/main.rs b/src/main.rs
index e00014b..da02de0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -142,6 +142,11 @@ async fn main() -> Result<()> {
crate::commands::release(db_connection_config, &config, matches).await?
}
+ Some(("lint", matches)) => {
+ let repo = load_repo()?;
+ crate::commands::lint(matches, progressbars, &config, repo).await?
+ }
+
Some((other, _)) => return Err(anyhow!("Unknown subcommand: {}", other)),
None => return Err(anyhow!("No subcommand")),
}