diff --git a/CHANGELOG.md b/CHANGELOG.md index c145666a..10ea744f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## v0.29.0 - 2024-05-30 + +### Added + +- added `--excluded-paths` flag and `excluded_paths = ["example.sql"]` configuration option to ignore paths when searching for files (#350). Thanks @rdaniels6813! + ## v0.28.0 - 2024-01-12 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 7d6665fd..ba147cd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,9 +470,9 @@ checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" @@ -1534,11 +1534,12 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "squawk" -version = "0.28.0" +version = "0.29.0" dependencies = [ "atty", "base64 0.12.3", "console", + "glob", "insta", "log", "serde", diff --git a/README.md b/README.md index 868c551d..0e0eb2ec 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,13 @@ OPTIONS: --dump-ast Output AST in JSON [possible values: Raw, Parsed, Debug] + --exclude-path ... + Paths to exclude + + For example: --exclude-path=005_user_ids.sql --exclude-path=009_account_emails.sql + + --exclude-path='*user_ids.sql' + -e, --exclude ... Exclude specific warnings diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ad827431..f385cf5f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "squawk" -version = "0.28.0" +version = "0.29.0" authors = ["Steve Dignam "] edition = "2018" license = "GPL-3.0" @@ -27,6 +27,7 @@ squawk-parser = { version = "0.0.0", path = "../parser" } squawk-linter = { version = "0.0.0", path = "../linter" } squawk-github = { version = "0.0.0", path = "../github" } toml = "0.5.9" +glob = "0.3.1" [dev-dependencies] insta = "0.16.0" diff --git a/cli/src/config.rs b/cli/src/config.rs index 430e6107..8f2c8003 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -38,6 +38,8 @@ impl std::fmt::Display for ConfigError { #[derive(Debug, Default, Deserialize)] pub struct Config { + #[serde(default)] + pub excluded_paths: Vec, #[serde(default)] pub excluded_rules: Vec, #[serde(default)] @@ -98,6 +100,7 @@ mod test_config { let squawk_toml = NamedTempFile::new().expect("generate tempFile"); let file = r#" pg_version = "19.1" +excluded_paths = ["example.sql"] excluded_rules = ["require-concurrent-index-creation"] assume_in_transaction = true @@ -126,6 +129,16 @@ excluded_rules = ["require-concurrent-index-creation"] assert_debug_snapshot!(Config::parse(Some(squawk_toml.path().to_path_buf()))); } #[test] + fn test_load_excluded_paths() { + let squawk_toml = NamedTempFile::new().expect("generate tempFile"); + let file = r#" +excluded_paths = ["example.sql"] + + "#; + fs::write(&squawk_toml, file).expect("Unable to write file"); + assert_debug_snapshot!(Config::parse(Some(squawk_toml.path().to_path_buf()))); + } + #[test] fn test_load_assume_in_transaction() { let squawk_toml = NamedTempFile::new().expect("generate tempFile"); let file = r#" diff --git a/cli/src/main.rs b/cli/src/main.rs index 35f65a51..76fe3bf9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -14,6 +14,7 @@ use crate::reporter::{ use crate::subcommand::{check_and_comment_on_pr, Command}; use atty::Stream; use config::Config; +use glob::Pattern; use log::info; use simplelog::CombinedLogger; use squawk_linter::versions::Version; @@ -40,6 +41,14 @@ struct Opt { /// Paths to search #[structopt(value_name = "path")] paths: Vec, + /// Paths to exclude + /// + /// For example: + /// --exclude-path=005_user_ids.sql --exclude-path=009_account_emails.sql + /// + /// --exclude-path='*user_ids.sql' + #[structopt(long = "exclude-path")] + excluded_path: Option>, /// Exclude specific warnings /// /// For example: @@ -117,6 +126,22 @@ fn main() { conf.excluded_rules }; + // the --exclude-path flag completely overrides the configuration file. + let excluded_paths = if let Some(excluded_paths) = opts.excluded_path { + excluded_paths + } else { + conf.excluded_paths + }; + let excluded_path_patterns = excluded_paths + .iter() + .map(|excluded_path| { + Pattern::new(excluded_path).unwrap_or_else(|e| { + eprintln!("Pattern error: {e}"); + process::exit(1); + }) + }) + .collect::>(); + let pg_version = if let Some(pg_version) = opts.pg_version { Some(pg_version) } else { @@ -135,6 +160,7 @@ fn main() { info!("pg version: {:?}", pg_version); info!("excluded rules: {:?}", &excluded_rules); + info!("excluded paths: {:?}", &excluded_paths); info!("assume in a transaction: {:?}", assume_in_transaction); let mut clap_app = Opt::clap(); @@ -149,6 +175,7 @@ fn main() { is_stdin, opts.stdin_filepath, &excluded_rules, + &excluded_path_patterns, pg_version, assume_in_transaction, ), @@ -167,6 +194,7 @@ fn main() { read_stdin, opts.stdin_filepath, &excluded_rules, + &excluded_path_patterns, pg_version, assume_in_transaction, ) { diff --git a/cli/src/reporter.rs b/cli/src/reporter.rs index 137482b8..7d73c98c 100644 --- a/cli/src/reporter.rs +++ b/cli/src/reporter.rs @@ -1,5 +1,6 @@ use console::strip_ansi_codes; use console::style; +use glob::Pattern; use log::info; use serde::Serialize; use squawk_linter::errors::CheckSqlError; @@ -164,6 +165,7 @@ pub fn check_files( read_stdin: bool, stdin_path: Option, excluded_rules: &[RuleViolationKind], + excluded_paths: &[Pattern], pg_version: Option, assume_in_transaction: bool, ) -> Result, CheckFilesError> { @@ -188,15 +190,25 @@ pub fn check_files( } for path in paths { - info!("checking file path: {}", path); - let sql = get_sql_from_path(path)?; - output_violations.push(process_violations( - &sql, - path, - excluded_rules, - pg_version, - assume_in_transaction, - )); + if let Some(pattern) = excluded_paths + .iter() + .find(|&excluded| excluded.matches(path)) + { + info!( + "skipping excluded file path: {}. pattern: {}", + path, pattern + ); + } else { + info!("checking file path: {}", path); + let sql = get_sql_from_path(path)?; + output_violations.push(process_violations( + &sql, + path, + excluded_rules, + pg_version, + assume_in_transaction, + )); + } } Ok(output_violations) } @@ -520,7 +532,7 @@ pub fn get_comment_body(files: &[ViolationContent], version: &str) -> String { violation_count = violations_count, file_count = files.len(), sql_file_content = files - .into_iter() + .iter() .filter_map(|x| get_sql_file_content(x).ok()) .collect::>() .join("\n"), diff --git a/cli/src/snapshots/squawk__config__test_config__load_assume_in_transaction.snap b/cli/src/snapshots/squawk__config__test_config__load_assume_in_transaction.snap index de2f6dc9..4483bf79 100644 --- a/cli/src/snapshots/squawk__config__test_config__load_assume_in_transaction.snap +++ b/cli/src/snapshots/squawk__config__test_config__load_assume_in_transaction.snap @@ -1,10 +1,12 @@ --- source: cli/src/config.rs expression: "Config::parse(Some(squawk_toml.path().to_path_buf()))" + --- Ok( Some( Config { + excluded_paths: [], excluded_rules: [], pg_version: None, assume_in_transaction: Some( diff --git a/cli/src/snapshots/squawk__config__test_config__load_cfg_full.snap b/cli/src/snapshots/squawk__config__test_config__load_cfg_full.snap index f20fe005..a7ff10ce 100644 --- a/cli/src/snapshots/squawk__config__test_config__load_cfg_full.snap +++ b/cli/src/snapshots/squawk__config__test_config__load_cfg_full.snap @@ -1,10 +1,14 @@ --- source: cli/src/config.rs expression: "Config::parse(Some(squawk_toml.path().to_path_buf()))" + --- Ok( Some( Config { + excluded_paths: [ + "example.sql", + ], excluded_rules: [ RequireConcurrentIndexCreation, ], diff --git a/cli/src/snapshots/squawk__config__test_config__load_excluded_paths.snap b/cli/src/snapshots/squawk__config__test_config__load_excluded_paths.snap new file mode 100644 index 00000000..7b5bd36a --- /dev/null +++ b/cli/src/snapshots/squawk__config__test_config__load_excluded_paths.snap @@ -0,0 +1,17 @@ +--- +source: cli/src/config.rs +expression: "Config::parse(Some(squawk_toml.path().to_path_buf()))" + +--- +Ok( + Some( + Config { + excluded_paths: [ + "example.sql", + ], + excluded_rules: [], + pg_version: None, + assume_in_transaction: None, + }, + ), +) diff --git a/cli/src/snapshots/squawk__config__test_config__load_excluded_rules.snap b/cli/src/snapshots/squawk__config__test_config__load_excluded_rules.snap index b050c336..8e801e57 100644 --- a/cli/src/snapshots/squawk__config__test_config__load_excluded_rules.snap +++ b/cli/src/snapshots/squawk__config__test_config__load_excluded_rules.snap @@ -1,10 +1,12 @@ --- source: cli/src/config.rs expression: "Config::parse(Some(squawk_toml.path().to_path_buf()))" + --- Ok( Some( Config { + excluded_paths: [], excluded_rules: [ RequireConcurrentIndexCreation, ], diff --git a/cli/src/snapshots/squawk__config__test_config__load_pg_version.snap b/cli/src/snapshots/squawk__config__test_config__load_pg_version.snap index 426c42dc..168b9bbb 100644 --- a/cli/src/snapshots/squawk__config__test_config__load_pg_version.snap +++ b/cli/src/snapshots/squawk__config__test_config__load_pg_version.snap @@ -1,10 +1,12 @@ --- source: cli/src/config.rs expression: "Config::parse(Some(squawk_toml.path().to_path_buf()))" + --- Ok( Some( Config { + excluded_paths: [], excluded_rules: [], pg_version: Some( Version { diff --git a/cli/src/subcommand.rs b/cli/src/subcommand.rs index fbe86e0a..3d0f9f57 100644 --- a/cli/src/subcommand.rs +++ b/cli/src/subcommand.rs @@ -1,5 +1,6 @@ use crate::reporter::{check_files, get_comment_body, CheckFilesError}; +use glob::Pattern; use log::info; use squawk_github::{actions, app, comment_on_pr, GitHubApi, GithubError}; use squawk_linter::{versions::Version, violations::RuleViolationKind}; @@ -157,6 +158,7 @@ pub fn check_and_comment_on_pr( is_stdin: bool, stdin_path: Option, root_cmd_exclude: &[RuleViolationKind], + root_cmd_exclude_paths: &[Pattern], pg_version: Option, assume_in_transaction: bool, ) -> Result<(), SquawkError> { @@ -188,6 +190,7 @@ pub fn check_and_comment_on_pr( is_stdin, stdin_path, &concat(&exclude.unwrap_or_default(), root_cmd_exclude), + root_cmd_exclude_paths, pg_version, assume_in_transaction, )?; diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 61b44281..f3a4f48c 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -21,6 +21,14 @@ Individual rules can be disabled via the `--exclude` flag squawk --exclude=adding-field-with-default,disallowed-unique-constraint example.sql ``` +## files + +Files can be excluded from linting via the `--exclude-path` flag. Glob matching is supported and the flag can be provided multiple times. + +```shell +squawk --exclude-path=005_user_ids.sql --exclude-path='*user_ids.sql' migrations/* +``` + ## `.squawk.toml` configuration file Rules can be disabled with a configuration file. @@ -31,7 +39,7 @@ By default, Squawk will traverse up from the current directory to find a `.squaw squawk --config=~/.squawk.toml example.sql ``` -The `--exclude` and `--pg-version` flags will always be prioritized over the configuration file. +The `--exclude`, `--exclude-path`, and `--pg-version` flags will always be prioritized over the configuration file. ## example `.squawk.toml` configurations @@ -70,6 +78,10 @@ excluded_rules = [ "require-concurrent-index-deletion", ] assume_in_transaction = true +excluded_paths = [ + "005_user_ids.sql", + "*user_ids.sql", +] ``` diff --git a/flake.nix b/flake.nix index 47ecb351..d2371cec 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,7 @@ { squawk = final.rustPlatform.buildRustPackage { pname = "squawk"; - version = "0.28.0"; + version = "0.29.0"; cargoLock = { lockFile = ./Cargo.lock; diff --git a/package.json b/package.json index e3dee940..010326fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "squawk-cli", - "version": "0.28.0", + "version": "0.29.0", "description": "linter for PostgreSQL, focused on migrations", "repository": "git@github.com:sbdchd/squawk.git", "author": "Steve Dignam ",