diff --git a/.deny.toml b/.deny.toml index f946aeb..87f3c59 100644 --- a/.deny.toml +++ b/.deny.toml @@ -6,12 +6,24 @@ unsound = "deny" yanked = "deny" notice = "deny" git-fetch-with-cli = true -ignore = [] +ignore = [ +] # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html [bans] multiple-versions = "warn" wildcards = "allow" # https://github.com/EmbarkStudios/cargo-deny/issues/488 +build.allow-build-scripts = [ + { name = "proc-macro2" }, # via serde_derive + { name = "serde" }, + { name = "windows_aarch64_gnullvm" }, # via home + { name = "windows_aarch64_msvc" }, # via home + { name = "windows_i686_gnu" }, # via home + { name = "windows_i686_msvc" }, # via home + { name = "windows_x86_64_gnu" }, # via home + { name = "windows_x86_64_gnullvm" }, # via home + { name = "windows_x86_64_msvc" }, # via home +] # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] diff --git a/.github/.cspell/organization-dictionary.txt b/.github/.cspell/organization-dictionary.txt index ff1376e..03a1b79 100644 --- a/.github/.cspell/organization-dictionary.txt +++ b/.github/.cspell/organization-dictionary.txt @@ -160,6 +160,7 @@ esac euxo gsub libc +markdownlint moreutils msys noninteractive diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd1fce8..1461c49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: - main - dev schedule: - - cron: '0 1 * * *' + - cron: '0 2 * * *' workflow_dispatch: env: @@ -34,17 +34,17 @@ concurrency: jobs: check-external-types: - uses: taiki-e/workflows/.github/workflows/check-external-types.yml@main + uses: taiki-e/github-actions/.github/workflows/check-external-types.yml@main deny: - uses: taiki-e/workflows/.github/workflows/deny.yml@main + uses: taiki-e/github-actions/.github/workflows/deny.yml@main docs: - uses: taiki-e/workflows/.github/workflows/docs.yml@main + uses: taiki-e/github-actions/.github/workflows/docs.yml@main msrv: - uses: taiki-e/workflows/.github/workflows/msrv.yml@main + uses: taiki-e/github-actions/.github/workflows/msrv.yml@main with: event_name: ${{ github.event_name }} tidy: - uses: taiki-e/workflows/.github/workflows/tidy.yml@main + uses: taiki-e/github-actions/.github/workflows/tidy.yml@main test: strategy: diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000..7b67873 --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,6 @@ +# https://github.com/DavidAnson/markdownlint#rules--aliases + +MD013: false # line-length +MD024: false # no-duplicate-heading/no-duplicate-header +MD033: false # no-inline-html +MD036: false # no-emphasis-as-heading/no-emphasis-as-header diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d56c9b..b923a8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [Unreleased] +## [0.1.13] - 2023-10-17 + +- Improve compatibility with old Cargo. + ## [0.1.12] - 2023-09-14 - Improve robustness when new cfgs are added in the future. @@ -70,7 +74,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com Initial release -[Unreleased]: https://github.com/taiki-e/cargo-config2/compare/v0.1.12...HEAD +[Unreleased]: https://github.com/taiki-e/cargo-config2/compare/v0.1.13...HEAD +[0.1.13]: https://github.com/taiki-e/cargo-config2/compare/v0.1.12...v0.1.13 [0.1.12]: https://github.com/taiki-e/cargo-config2/compare/v0.1.11...v0.1.12 [0.1.11]: https://github.com/taiki-e/cargo-config2/compare/v0.1.10...v0.1.11 [0.1.10]: https://github.com/taiki-e/cargo-config2/compare/v0.1.9...v0.1.10 diff --git a/Cargo.toml b/Cargo.toml index cddbf43..e5347c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-config2" -version = "0.1.12" +version = "0.1.13" edition = "2021" rust-version = "1.66" license = "Apache-2.0 OR MIT" @@ -35,7 +35,6 @@ build-context = "0.1" clap = { version = "4", default-features = false, features = ["std", "derive"] } duct = "0.13" fs-err = "2" -ignore = "0.4" lexopt = "0.3" rustversion = "1" serde_json = "1" diff --git a/README.md b/README.md index 376d04c..133bc67 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,6 @@ be dual licensed as above, without any additional terms or conditions. This product includes copies and modifications of software developed by third parties: -- [`src/cfg_expr`](https://github.com/taiki-e/cargo-config2/tree/HEAD/src/cfg_expr) includes copies and modifications of [`cfg-expr` crate](https://github.com/EmbarkStudios/cfg-expr) by Embark Studios, licensed under "MIT OR Apache-2.0". +- [`src/cfg_expr`](https://github.com/taiki-e/cargo-config2/tree/HEAD/src/cfg_expr) includes copies and modifications of [`cfg-expr` crate](https://github.com/EmbarkStudios/cfg-expr) by Embark Studios, licensed under "Apache-2.0 OR MIT". See the license files included in these directories for more details. diff --git a/bench/bench.rs b/bench/bench.rs index 7890d21..276c016 100644 --- a/bench/bench.rs +++ b/bench/bench.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + #![allow(clippy::drop_non_drop)] use std::{collections::HashMap, hint::black_box, path::Path}; @@ -27,7 +29,7 @@ fn reference(c: &mut Criterion) { }); g.bench_function("apply_env_no_env", |b| { let config = &black_box(cargo_config2::de::Config::default()); - let cx = &mut black_box(test_options().into_context()); + let cx = &mut black_box(test_options().into_context(std::env::current_dir().unwrap())); b.iter(|| { let mut config = black_box(config.clone()); config.apply_env(cx).unwrap(); @@ -80,7 +82,7 @@ fn reference(c: &mut Criterion) { .env(env_list) .cargo_home(None) .rustc(PathAndArgs::new("rustc")) - .into_context(), + .into_context(std::env::current_dir().unwrap()), ); b.iter(|| { let mut config = black_box(config.clone()); diff --git a/examples/get.rs b/examples/get.rs index 85f330e..bc76973 100644 --- a/examples/get.rs +++ b/examples/get.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + // Partial re-implementation of `cargo config get` using cargo-config2. use std::{ @@ -86,7 +88,7 @@ fn print_config(writer: &mut dyn Write, format: Format, config: &Config) -> Resu fn print_value(writer: &mut dyn Write, path: &str, value: &toml::Value) -> Result<()> { match value { toml::Value::Table(table) => { - for (key, item) in table.iter() { + for (key, item) in table { print_value(writer, &format!("{path}.{key}"), item)?; } } diff --git a/src/cfg_expr/error.rs b/src/cfg_expr/error.rs index cb9add2..1db86c4 100644 --- a/src/cfg_expr/error.rs +++ b/src/cfg_expr/error.rs @@ -1,4 +1,6 @@ -use std::{error::Error, fmt}; +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use core::fmt; /// An error related to parsing of a cfg expression #[derive(Debug, PartialEq, Eq)] @@ -7,7 +9,7 @@ pub(crate) struct ParseError { pub(crate) original: String, /// The range of characters in the original string that result /// in this error - pub(crate) span: std::ops::Range, + pub(crate) span: core::ops::Range, /// The specific reason for the error pub(crate) reason: Reason, } @@ -91,35 +93,4 @@ impl fmt::Display for Reason { } } -impl Error for ParseError { - fn description(&self) -> &str { - use Reason::{ - Empty, InvalidNot, MultipleRootPredicates, UnclosedParens, UnclosedQuotes, Unexpected, - UnopenedParens, - }; - - match self.reason { - UnclosedParens => "unclosed parens", - UnopenedParens => "unopened parens", - UnclosedQuotes => "unclosed quotes", - Empty => "empty expression", - Unexpected(_) => "unexpected term", - InvalidNot(_) => "not() takes 1 predicate", - MultipleRootPredicates => "multiple root predicates", - } - } -} - -/// Error parsing a `target_has_atomic` predicate. -#[derive(Clone, Debug, Eq, PartialEq)] -pub(crate) struct HasAtomicParseError { - pub(crate) input: String, -} - -impl fmt::Display for HasAtomicParseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "expected integer or \"ptr\", found {}", self.input) - } -} - -impl Error for HasAtomicParseError {} +impl std::error::Error for ParseError {} diff --git a/src/cfg_expr/expr/lexer.rs b/src/cfg_expr/expr/lexer.rs index 1f9cbae..62a2264 100644 --- a/src/cfg_expr/expr/lexer.rs +++ b/src/cfg_expr/expr/lexer.rs @@ -1,3 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use core::fmt; + use crate::cfg_expr::error::{ParseError, Reason}; /// A single token in a cfg expression @@ -24,9 +28,9 @@ pub(crate) enum Token<'a> { Comma, } -impl std::fmt::Display for Token<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(self, f) +impl fmt::Display for Token<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) } } @@ -73,7 +77,7 @@ pub(crate) struct LexerToken<'a> { /// The token that was lexed pub(crate) token: Token<'a>, /// The range of the token characters in the original license expression - pub(crate) span: std::ops::Range, + pub(crate) span: core::ops::Range, } impl<'a> Iterator for Lexer<'a> { diff --git a/src/cfg_expr/expr/mod.rs b/src/cfg_expr/expr/mod.rs index 4b62a70..ba9df52 100644 --- a/src/cfg_expr/expr/mod.rs +++ b/src/cfg_expr/expr/mod.rs @@ -1,12 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + pub(crate) mod lexer; mod parser; -use std::ops::Range; +use core::ops::Range; /// A predicate function, used to combine 1 or more predicates /// into a single value #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] -pub(crate) enum Func { +enum Func { /// `not()` with a configuration predicate. It is true if its predicate /// is false and false if its predicate is true. Not, @@ -34,7 +36,7 @@ pub(crate) enum Predicate<'a> { } #[derive(Clone, Debug)] -pub(crate) struct InnerPredicate { +struct InnerPredicate { identifier: Range, value: Option>, } @@ -51,7 +53,7 @@ impl InnerPredicate { } #[derive(Clone, Debug)] -pub(crate) enum ExprNode { +enum ExprNode { Fn(Func), Predicate(InnerPredicate), } @@ -59,10 +61,10 @@ pub(crate) enum ExprNode { /// A parsed `cfg()` expression that can evaluated #[derive(Clone, Debug)] pub(crate) struct Expression { - pub(crate) expr: Vec, + expr: Vec, // We keep the original string around for providing the arbitrary // strings that can make up an expression - pub(crate) original: String, + original: String, } impl Expression { @@ -87,7 +89,7 @@ impl Expression { pub(crate) fn eval(&self, mut eval_predicate: EP) -> T where EP: FnMut(&Predicate<'_>) -> T, - T: Logic + std::fmt::Debug, + T: Logic + core::fmt::Debug, { let mut result_stack = Vec::with_capacity(8); diff --git a/src/cfg_expr/expr/parser.rs b/src/cfg_expr/expr/parser.rs index 6a87f5a..ea0c98a 100644 --- a/src/cfg_expr/expr/parser.rs +++ b/src/cfg_expr/expr/parser.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use crate::cfg_expr::{ error::{ParseError, Reason}, expr::{ @@ -14,7 +16,7 @@ impl Expression { struct FuncAndSpan { func: Func, parens_index: usize, - span: std::ops::Range, + span: core::ops::Range, predicates: Vec, nest_level: u8, } @@ -31,8 +33,8 @@ impl Expression { // Keep track of the last token to simplify validation of the token stream let mut last_token: Option> = None; - let parse_predicate = |key: (&str, std::ops::Range), - val: Option<(&str, std::ops::Range)>| + let parse_predicate = |key: (&str, core::ops::Range), + val: Option<(&str, core::ops::Range)>| -> Result { let span = key.1; Ok(InnerPredicate { identifier: span, value: val.map(|(_, span)| span) }) diff --git a/src/cfg_expr/mod.rs b/src/cfg_expr/mod.rs index 57e94d1..addf4b8 100644 --- a/src/cfg_expr/mod.rs +++ b/src/cfg_expr/mod.rs @@ -1,4 +1,6 @@ -// This module is a fork of cfg-expr, licensed under "MIT OR Apache-2.0". +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// This module is a fork of cfg-expr, licensed under "Apache-2.0 OR MIT". // // The code has been simplified and adjusted to match our use cases. // @@ -15,5 +17,3 @@ mod tests { mod lexer; mod parser; } - -pub(crate) use expr::{Expression, Predicate}; diff --git a/src/cfg_expr/tests/eval.rs b/src/cfg_expr/tests/eval.rs index cae891c..03d1a82 100644 --- a/src/cfg_expr/tests/eval.rs +++ b/src/cfg_expr/tests/eval.rs @@ -1,4 +1,6 @@ -use crate::{cfg_expr::Expression, resolve::CfgMap}; +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use crate::{cfg_expr::expr::Expression, resolve::CfgMap}; #[test] #[cfg_attr(miri, ignore)] // Miri doesn't support pipe2 (inside std::process::Command::output) diff --git a/src/cfg_expr/tests/lexer.rs b/src/cfg_expr/tests/lexer.rs index b45852b..b6826e8 100644 --- a/src/cfg_expr/tests/lexer.rs +++ b/src/cfg_expr/tests/lexer.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use crate::cfg_expr::expr::lexer::{Lexer, Token}; macro_rules! test_lex { diff --git a/src/cfg_expr/tests/parser.rs b/src/cfg_expr/tests/parser.rs index 967b642..730ebb0 100644 --- a/src/cfg_expr/tests/parser.rs +++ b/src/cfg_expr/tests/parser.rs @@ -1,7 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use crate::cfg_expr::{ error::{ParseError, Reason}, - expr::Predicate as P, - Expression, + expr::{Expression, Predicate as P}, }; macro_rules! test_validate { diff --git a/src/de.rs b/src/de.rs index f87a138..6c5b873 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1,18 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + //! Cargo configuration that environment variables, config overrides, and //! target-specific configurations have not been resolved. #[path = "gen/de.rs"] mod gen; +use core::{fmt, slice, str::FromStr}; use std::{ borrow::Cow, collections::BTreeMap, ffi::OsStr, - fmt::{self, Formatter}, fs, path::{Path, PathBuf}, - slice, - str::FromStr, }; use serde::{Deserialize, Serialize}; @@ -106,15 +106,15 @@ impl Config { } /// Read config files hierarchically from the given directory and merges them. - pub fn load_with_cwd(cwd: impl AsRef) -> Result { + pub fn load_with_cwd>(cwd: P) -> Result { let cwd = cwd.as_ref(); Self::_load_with_options(cwd, home::cargo_home_with_cwd(cwd).ok()) } /// Read config files hierarchically from the given directory and merges them. - pub fn load_with_options( - cwd: impl AsRef, - cargo_home: impl Into>, + pub fn load_with_options, Q: Into>>( + cwd: P, + cargo_home: Q, ) -> Result { Self::_load_with_options(cwd.as_ref(), cargo_home.into()) } @@ -143,7 +143,7 @@ impl Config { /// /// **Note:** Note: This just reads a file at the given path and does not /// respect the hierarchical structure of the cargo config. - pub fn load_file(path: impl AsRef) -> Result { + pub fn load_file>(path: P) -> Result { Self::_load_file(path.as_ref()) } fn _load_file(path: &Path) -> Result { @@ -482,7 +482,7 @@ pub struct RegistriesConfigValue { } impl fmt::Debug for RegistriesConfigValue { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { index, token, protocol } = self; let redacted_token = token .as_ref() @@ -549,7 +549,7 @@ pub struct RegistryConfig { } impl fmt::Debug for RegistryConfig { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { default, token } = self; let redacted_token = token .as_ref() diff --git a/src/easy.rs b/src/easy.rs index 502a803..6f0c090 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -1,10 +1,10 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use core::{cell::RefCell, fmt, ops}; use std::{ borrow::Cow, - cell::RefCell, collections::BTreeMap, ffi::{OsStr, OsString}, - fmt::{self, Formatter}, - ops, path::{Path, PathBuf}, process::Command, }; @@ -99,8 +99,6 @@ pub struct Config { // Resolve contexts. Completely ignored in serialization and deserialization. #[serde(skip)] cx: ResolveContext, - #[serde(skip)] - current_dir: PathBuf, } fn ref_cell_bree_map_is_empty(map: &RefCell>) -> bool { @@ -114,36 +112,32 @@ impl Config { } /// Read config files hierarchically from the given directory and merges them. - pub fn load_with_cwd(cwd: impl AsRef) -> Result { + pub fn load_with_cwd>(cwd: P) -> Result { let cwd = cwd.as_ref(); Self::load_with_options(cwd, ResolveOptions::default()) } /// Read config files hierarchically from the given directory and merges them. - pub fn load_with_options(cwd: impl AsRef, options: ResolveOptions) -> Result { + pub fn load_with_options>(cwd: P, options: ResolveOptions) -> Result { let cwd = cwd.as_ref(); - let cx = options.into_context(); + let cx = options.into_context(cwd.to_owned()); - let de = de::Config::_load_with_options(cwd, cx.cargo_home(cwd).clone())?; - Self::from_unresolved(de, cx, cwd.to_owned()) + let de = de::Config::_load_with_options(&cx.current_dir, cx.cargo_home(cwd).clone())?; + Self::from_unresolved(de, cx) } - pub(crate) fn from_unresolved( - mut de: de::Config, - cx: ResolveContext, - current_dir: PathBuf, - ) -> Result { + pub(crate) fn from_unresolved(mut de: de::Config, cx: ResolveContext) -> Result { de.apply_env(&cx)?; let mut alias = BTreeMap::new(); for (k, v) in de.alias { alias.insert(k, StringList::from_unresolved(v)); } - let build = BuildConfig::from_unresolved(de.build, ¤t_dir); - let doc = DocConfig::from_unresolved(de.doc, ¤t_dir); + let build = BuildConfig::from_unresolved(de.build, &cx.current_dir); + let doc = DocConfig::from_unresolved(de.doc, &cx.current_dir); let mut env = BTreeMap::new(); for (k, v) in de.env { - env.insert(k, EnvConfigValue::from_unresolved(v, ¤t_dir)); + env.insert(k, EnvConfigValue::from_unresolved(v, &cx.current_dir)); } let future_incompat_report = FutureIncompatReportConfig::from_unresolved(de.future_incompat_report); @@ -168,7 +162,6 @@ impl Config { de_target: de.target, term, cx, - current_dir, }) } @@ -265,7 +258,7 @@ impl Config { if !config_targets.is_empty() { return Ok(config_targets); } - Ok(vec![TargetTripleRef::from(self.cx.host_triple()?).into_owned()]) + Ok(vec![TargetTripleRef::from(self.cx.host_triple(&self.build)?).into_owned()]) } /// Selects target triples to pass to CLI. @@ -312,7 +305,7 @@ impl Config { &self.build, )? .unwrap_or_default(), - &self.current_dir, + &self.cx.current_dir, ); target_configs.insert(TargetTripleBorrow(target.clone().into_owned()), target_config); } @@ -371,7 +364,7 @@ impl Config { } /// Returns the host triple. pub fn host_triple(&self) -> Result<&str> { - self.cx.host_triple() + self.cx.host_triple(&self.build) } // TODO: add override instead? @@ -761,7 +754,7 @@ impl RegistriesConfigValue { } impl fmt::Debug for RegistriesConfigValue { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { index, token, protocol } = self; let redacted_token = token.as_ref().map(|_| "[REDACTED]"); f.debug_struct("RegistriesConfigValue") @@ -807,7 +800,7 @@ impl RegistryConfig { } impl fmt::Debug for RegistryConfig { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { default, token } = self; let redacted_token = token.as_ref().map(|_| "[REDACTED]"); f.debug_struct("RegistryConfig") diff --git a/src/env.rs b/src/env.rs index 6b2dd6f..c5b5b74 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + // Environment variables are prefer over config values. // https://doc.rust-lang.org/nightly/cargo/reference/config.html#environment-variables @@ -340,7 +342,8 @@ mod tests { fn empty_string_wrapper_envs() { let env_list = [("RUSTC_WRAPPER", ""), ("RUSTC_WORKSPACE_WRAPPER", "")]; let mut config = crate::de::BuildConfig::default(); - let cx = &ResolveOptions::default().env(env_list).into_context(); + let cx = + &ResolveOptions::default().env(env_list).into_context(std::env::current_dir().unwrap()); config.rustc_wrapper = Some(Value { val: "rustc_wrapper".to_string(), definition: None }); config.rustc_workspace_wrapper = Some(Value { val: "rustc_workspace_wrapper".to_string(), definition: None }); diff --git a/src/error.rs b/src/error.rs index a5445c0..80bb88b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use std::{ffi::OsString, fmt, io}; macro_rules! format_err { @@ -12,7 +14,7 @@ macro_rules! bail { }; } -pub(crate) type Result = std::result::Result; +pub(crate) type Result = core::result::Result; /// An error that occurred during loading or resolving the Cargo configuration. #[derive(Debug)] @@ -24,7 +26,6 @@ pub struct Error(ErrorKind); // don't expose dependencies' types directly in the public API. #[derive(Debug)] pub(crate) enum ErrorKind { - Env(std::env::VarError), Io(io::Error), CfgExprParse(crate::cfg_expr::error::ParseError), @@ -55,7 +56,6 @@ impl Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { - ErrorKind::Env(e) => fmt::Display::fmt(e, f), ErrorKind::Io(e) => fmt::Display::fmt(e, f), ErrorKind::CfgExprParse(e) => fmt::Display::fmt(e, f), ErrorKind::Other(e) | ErrorKind::WithContext(e, ..) => fmt::Display::fmt(e, f), @@ -66,7 +66,6 @@ impl fmt::Display for Error { impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self.0 { - ErrorKind::Env(e) => e.source(), ErrorKind::Io(e) => e.source(), ErrorKind::CfgExprParse(e) => e.source(), ErrorKind::Other(_) => None, @@ -79,7 +78,6 @@ impl std::error::Error for Error { impl From for io::Error { fn from(e: Error) -> Self { match e.0 { - ErrorKind::Env(e) => Self::new(io::ErrorKind::Other, e), ErrorKind::Io(e) => e, ErrorKind::CfgExprParse(e) => Self::new(io::ErrorKind::Other, e), ErrorKind::Other(e) | ErrorKind::WithContext(e, None) => { @@ -116,16 +114,17 @@ impl From for ErrorKind { // as it would be a breaking change to remove the implementation if the // conversion is no longer needed due to changes in the internal implementation. // TODO: consider removing them in the next breaking release -impl From for Error { - fn from(e: std::env::VarError) -> Self { - Self(ErrorKind::Env(e)) - } -} impl From for Error { fn from(e: io::Error) -> Self { Self(ErrorKind::Io(e)) } } +// TODO: this is no longer used in our code; remove in the next breaking release +impl From for Error { + fn from(e: std::env::VarError) -> Self { + Self(ErrorKind::Other(e.to_string())) + } +} // Inspired by anyhow::Context. pub(crate) trait Context { @@ -161,7 +160,7 @@ where } } } -impl Context for Option { +impl Context for Option { fn context(self, context: C) -> Result where C: fmt::Display, diff --git a/src/gen/assert_impl.rs b/src/gen/assert_impl.rs index 8768f8b..3616fef 100644 --- a/src/gen/assert_impl.rs +++ b/src/gen/assert_impl.rs @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT // This file is @generated by cargo-config2-internal-codegen // (gen_assert_impl function at tools/codegen/src/main.rs). // It is not intended for manual editing. diff --git a/src/gen/de.rs b/src/gen/de.rs index 0af2c55..635c3c7 100644 --- a/src/gen/de.rs +++ b/src/gen/de.rs @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT // This file is @generated by cargo-config2-internal-codegen // (gen_de function at tools/codegen/src/main.rs). // It is not intended for manual editing. diff --git a/src/gen/is_none.rs b/src/gen/is_none.rs index 74313aa..84501fe 100644 --- a/src/gen/is_none.rs +++ b/src/gen/is_none.rs @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT // This file is @generated by cargo-config2-internal-codegen // (gen_is_none function at tools/codegen/src/main.rs). // It is not intended for manual editing. diff --git a/src/lib.rs b/src/lib.rs index 958c554..ffa6c8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + /*! Load and resolve [Cargo configuration](https://doc.rust-lang.org/nightly/cargo/reference/config.html). @@ -36,20 +38,20 @@ See also the [`get` example](https://github.com/taiki-e/cargo-config2/blob/HEAD/ ))] #![forbid(unsafe_code)] #![warn( - missing_debug_implementations, - // missing_docs, rust_2018_idioms, single_use_lifetimes, - unreachable_pub -)] -#![warn( + unreachable_pub, clippy::pedantic, - // lints for public library + // Lints that may help when writing public library. + missing_debug_implementations, + // missing_docs, clippy::alloc_instead_of_core, clippy::exhaustive_enums, clippy::exhaustive_structs, + clippy::impl_trait_in_params, + // clippy::missing_inline_in_public_items, // clippy::std_instead_of_alloc, - // clippy::std_instead_of_core, + clippy::std_instead_of_core, )] #![allow( clippy::manual_assert, diff --git a/src/merge.rs b/src/merge.rs index e9b797f..9c9ccb7 100644 --- a/src/merge.rs +++ b/src/merge.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use std::collections::{btree_map, BTreeMap}; use crate::{ diff --git a/src/process.rs b/src/process.rs index 4abc2fe..c7b0bf3 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use std::{ ffi::OsStr, fmt, diff --git a/src/resolve.rs b/src/resolve.rs index 3cbea82..549688b 100644 --- a/src/resolve.rs +++ b/src/resolve.rs @@ -1,18 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use core::{cell::RefCell, hash::Hash, str::FromStr}; use std::{ borrow::Cow, - cell::RefCell, collections::{HashMap, HashSet}, ffi::{OsStr, OsString}, - hash::Hash, path::{Path, PathBuf}, - str::FromStr, }; use once_cell::unsync::OnceCell; use serde::{Deserialize, Serialize}; use crate::{ - cfg_expr::{Expression, Predicate}, + cfg_expr::expr::{Expression, Predicate}, easy, error::{Context as _, Error, Result}, process::ProcessBuilder, @@ -94,9 +94,8 @@ impl ResolveOptions { self } - #[allow(clippy::missing_panics_doc)] // false positive: this function is #[doc(hidden)] #[doc(hidden)] // Not public API. - pub fn into_context(mut self) -> ResolveContext { + pub fn into_context(mut self, current_dir: PathBuf) -> ResolveContext { if self.env.is_none() { self = self.env(std::env::vars_os()); } @@ -118,7 +117,15 @@ impl ResolveOptions { None => OnceCell::new(), }; - ResolveContext { env, rustc, cargo, cargo_home, host_triple, cfg: RefCell::default() } + ResolveContext { + env, + rustc, + cargo, + cargo_home, + host_triple, + cfg: RefCell::default(), + current_dir, + } } } @@ -132,6 +139,7 @@ pub struct ResolveContext { cargo_home: OnceCell>, host_triple: OnceCell, cfg: RefCell, + pub(crate) current_dir: PathBuf, } impl ResolveContext { @@ -159,8 +167,21 @@ impl ResolveContext { pub(crate) fn cargo_home(&self, cwd: &Path) -> &Option { self.cargo_home.get_or_init(|| home::cargo_home_with_cwd(cwd).ok()) } - pub(crate) fn host_triple(&self) -> Result<&str> { - Ok(self.host_triple.get_or_try_init(|| host_triple(&self.cargo))?) + pub(crate) fn host_triple(&self, build_config: &easy::BuildConfig) -> Result<&str> { + if let Some(host) = self.host_triple.get() { + return Ok(host); + } + let host = match host_triple(&self.cargo) { + Ok(host) => host, + Err(_) => { + let rustc = build_config + .rustc + .as_ref() + .map_or_else(|| rustc_path(&self.cargo), PathBuf::from); + host_triple(rustc.as_os_str())? + } + }; + Ok(self.host_triple.get_or_init(|| host)) } // micro-optimization for static name -- avoiding name allocation can speed up @@ -327,17 +348,17 @@ impl PartialEq for TargetTripleRef<'_> { } impl Eq for TargetTripleRef<'_> {} impl PartialOrd for TargetTripleRef<'_> { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for TargetTripleRef<'_> { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.cli_target().cmp(other.cli_target()) } } impl Hash for TargetTripleRef<'_> { - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { self.cli_target().hash(state); } } @@ -349,7 +370,7 @@ impl Hash for TargetTripleRef<'_> { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[serde(transparent)] pub(crate) struct TargetTripleBorrow<'a>(pub(crate) TargetTripleRef<'a>); -impl std::borrow::Borrow for TargetTripleBorrow<'_> { +impl core::borrow::Borrow for TargetTripleBorrow<'_> { fn borrow(&self) -> &OsStr { self.0.cli_target() } @@ -589,13 +610,16 @@ mod tests { ("CARGO_TERM_PROGRESS_WIDTH", "100"), ]; let mut config = crate::de::Config::default(); - let cx = &ResolveOptions::default().env(env_list).into_context(); + let cx = + &ResolveOptions::default().env(env_list).into_context(std::env::current_dir().unwrap()); config.apply_env(cx).unwrap(); // ResolveOptions::env attempts to avoid pushing unrelated envs. let mut env_list = env_list.to_vec(); env_list.push(("A", "B")); - let cx = &ResolveOptions::default().env(env_list.iter().copied()).into_context(); + let cx = &ResolveOptions::default() + .env(env_list.iter().copied()) + .into_context(std::env::current_dir().unwrap()); for (k, v) in env_list { if k == "A" { assert!(!cx.env.contains_key(k)); @@ -614,7 +638,7 @@ mod tests { .env([("CARGO_ALIAS_a", OsStr::from_bytes(&[b'f', b'o', 0x80, b'o']))]) .cargo_home(None) .rustc(PathAndArgs::new("rustc")) - .into_context(); + .into_context(std::env::current_dir().unwrap()); assert_eq!( cx.env("CARGO_ALIAS_a").unwrap_err().to_string(), "failed to parse environment variable `CARGO_ALIAS_a`" diff --git a/src/value.rs b/src/value.rs index 54d2abd..f2ea764 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,11 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + // Based on https://github.com/rust-lang/cargo/blob/0.67.0/src/cargo/util/config/value.rs. +use core::{fmt, mem, str::FromStr}; use std::{ borrow::Cow, collections::BTreeMap, - fmt, mem, path::{Path, PathBuf}, - str::FromStr, }; use serde::{Deserialize, Serialize}; diff --git a/src/walk.rs b/src/walk.rs index 4851ec8..fe4fc6b 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + // https://doc.rust-lang.org/nightly/cargo/reference/config.html#hierarchical-structure // // > Cargo allows local configuration for a particular package as well as global diff --git a/tests/helper/mod.rs b/tests/helper/mod.rs new file mode 100644 index 0000000..698e622 --- /dev/null +++ b/tests/helper/mod.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use std::{ + path::{Path, PathBuf}, + process::Command, + str, +}; + +use anyhow::{bail, Context as _, Result}; +pub use fs_err as fs; + +pub fn fixtures_path() -> &'static Path { + Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures")) +} + +pub fn test_project(model: &str) -> Result<(tempfile::TempDir, PathBuf)> { + let tmpdir = tempfile::tempdir()?; + let tmpdir_path = tmpdir.path(); + + let model_path; + let workspace_root; + if model.contains('/') { + let mut model = model.splitn(2, '/'); + model_path = fixtures_path().join(model.next().unwrap()); + workspace_root = tmpdir_path.join(model.next().unwrap()); + assert!(model.next().is_none()); + } else { + model_path = fixtures_path().join(model); + workspace_root = tmpdir_path.to_path_buf(); + } + + for (file_name, from) in git_ls_files(&model_path, &[])? { + let to = &tmpdir_path.join(file_name); + if !to.parent().unwrap().is_dir() { + fs::create_dir_all(to.parent().unwrap())?; + } + fs::copy(from, to)?; + } + + Ok((tmpdir, workspace_root)) +} + +fn git_ls_files(dir: &Path, filters: &[&str]) -> Result> { + let mut cmd = Command::new("git"); + cmd.arg("ls-files").args(filters).current_dir(dir); + let output = cmd.output().with_context(|| format!("could not execute process `{cmd:?}`"))?; + if !output.status.success() { + bail!( + "process didn't exit successfully: `{cmd:?}`:\n\nSTDOUT:\n{0}\n{1}\n{0}\n\nSTDERR:\n{0}\n{2}\n{0}\n", + "-".repeat(60), + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + } + Ok(str::from_utf8(&output.stdout)? + .lines() + .map(str::trim) + .filter_map(|f| { + if f.is_empty() { + return None; + } + let p = dir.join(f); + if !p.exists() { + return None; + } + Some((f.to_owned(), p)) + }) + .collect()) +} diff --git a/tests/test.rs b/tests/test.rs index 743e68a..98bb5bd 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,10 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + #![allow(clippy::bool_assert_comparison, clippy::needless_pass_by_value)] +mod helper; + use std::{collections::HashMap, path::Path, str}; use anyhow::{Context as _, Result}; use build_context::TARGET; use cargo_config2::*; +use helper::*; fn test_options() -> ResolveOptions { ResolveOptions::default() @@ -297,48 +302,3 @@ fn test_cargo_behavior() -> Result<()> { Ok(()) } - -use helper::*; -mod helper { - use std::path::{Path, PathBuf}; - - use anyhow::Result; - pub use fs_err as fs; - - pub fn fixtures_path() -> &'static Path { - Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures")) - } - - pub fn test_project(model: &str) -> Result<(tempfile::TempDir, PathBuf)> { - let tmpdir = tempfile::tempdir()?; - let tmpdir_path = tmpdir.path(); - - let model_path; - let workspace_root; - if model.contains('/') { - let mut model = model.splitn(2, '/'); - model_path = fixtures_path().join(model.next().unwrap()); - workspace_root = tmpdir_path.join(model.next().unwrap()); - assert!(model.next().is_none()); - } else { - model_path = fixtures_path().join(model); - workspace_root = tmpdir_path.to_path_buf(); - } - - for entry in - ignore::WalkBuilder::new(&model_path).hidden(false).build().filter_map(Result::ok) - { - let path = entry.path(); - let tmp_path = &tmpdir_path.join(path.strip_prefix(&model_path)?); - if !tmp_path.exists() { - if path.is_dir() { - fs::create_dir_all(tmp_path)?; - } else { - fs::copy(path, tmp_path)?; - } - } - } - - Ok((tmpdir, workspace_root)) - } -} diff --git a/tools/.tidy-check-license-headers b/tools/.tidy-check-license-headers index 3c5d9a0..627f8dd 100644 --- a/tools/.tidy-check-license-headers +++ b/tools/.tidy-check-license-headers @@ -1 +1 @@ -git ls-files '*.sh' # TODO: check more files +git ls-files | grep -v '^tests/fixtures/' diff --git a/tools/codegen/Cargo.toml b/tools/codegen/Cargo.toml index 69e0919..9612e19 100644 --- a/tools/codegen/Cargo.toml +++ b/tools/codegen/Cargo.toml @@ -7,8 +7,7 @@ publish = false [dependencies] anyhow = "1" fs-err = "2" -globset = "0.4" -ignore = "0.4" +globset = { version = "0.4", default-features = false } prettyplease = "0.2" proc-macro2 = "1" quote = "1" diff --git a/tools/codegen/src/file.rs b/tools/codegen/src/file.rs index 961bb55..d904396 100644 --- a/tools/codegen/src/file.rs +++ b/tools/codegen/src/file.rs @@ -1,9 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use std::{ path::{Path, PathBuf}, + process::Command, + str, sync::OnceLock, }; -use anyhow::{format_err, Result}; +use anyhow::{bail, format_err, Context as _, Result}; use fs_err as fs; use proc_macro2::TokenStream; @@ -31,7 +35,8 @@ pub fn header(function_name: &str) -> String { // rust-analyzer does not respect outer attribute (#[rustfmt::skip]) on // a module without a body. So use inner attribute under cfg(rustfmt). format!( - "// This file is @generated by {bin_name} + "// SPDX-License-Identifier: Apache-2.0 OR MIT +// This file is @generated by {bin_name} // ({function_name} function at {file}). // It is not intended for manual editing.\n #![cfg_attr(rustfmt, rustfmt::skip)] @@ -142,3 +147,31 @@ pub fn write_raw(function_name: &str, path: &Path, contents: impl AsRef<[u8]>) - eprintln!("updated {}", p.display()); Ok(()) } + +pub fn git_ls_files(dir: &Path, filters: &[&str]) -> Result> { + let mut cmd = Command::new("git"); + cmd.arg("ls-files").args(filters).current_dir(dir); + let output = cmd.output().with_context(|| format!("could not execute process `{cmd:?}`"))?; + if !output.status.success() { + bail!( + "process didn't exit successfully: `{cmd:?}`:\n\nSTDOUT:\n{0}\n{1}\n{0}\n\nSTDERR:\n{0}\n{2}\n{0}\n", + "-".repeat(60), + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + } + Ok(str::from_utf8(&output.stdout)? + .lines() + .map(str::trim) + .filter_map(|f| { + if f.is_empty() { + return None; + } + let p = dir.join(f); + if !p.exists() { + return None; + } + Some((f.to_owned(), p)) + }) + .collect()) +} diff --git a/tools/codegen/src/main.rs b/tools/codegen/src/main.rs index e7c5fab..3bdc49a 100644 --- a/tools/codegen/src/main.rs +++ b/tools/codegen/src/main.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + #![warn(rust_2018_idioms, single_use_lifetimes)] #![allow(clippy::match_on_vec_items, clippy::needless_pass_by_value, clippy::redundant_guards)] @@ -302,15 +304,11 @@ fn gen_assert_impl() -> Result<()> { let out_dir = &workspace_root.join("src/gen"); fs::create_dir_all(out_dir)?; - let files: BTreeSet = ignore::Walk::new(workspace_root.join("src")) - .filter_map(Result::ok) - .filter_map(|e| { - let path = e.path(); - if !path.is_file() || path.extension() != Some("rs".as_ref()) { - return None; - } + let files: BTreeSet = git_ls_files(&workspace_root.join("src"), &["*.rs"])? + .into_iter() + .filter_map(|(file_name, path)| { // Assertions are only needed for the library's public APIs. - if path.ends_with("main.rs") { + if file_name == "main.rs" || file_name.starts_with("bin/") { return None; } Some(path.to_string_lossy().into_owned()) diff --git a/tools/gen.sh b/tools/gen.sh index df6e69f..591bf23 100755 --- a/tools/gen.sh +++ b/tools/gen.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # SPDX-License-Identifier: Apache-2.0 OR MIT -set -euo pipefail +set -eEuo pipefail IFS=$'\n\t' cd "$(dirname "$0")"/.. diff --git a/tools/publish.sh b/tools/publish.sh index 82d8db2..3119f37 100755 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # SPDX-License-Identifier: Apache-2.0 OR MIT -set -euo pipefail +set -eEuo pipefail IFS=$'\n\t' cd "$(dirname "$0")"/.. diff --git a/tools/tidy.sh b/tools/tidy.sh index 4442e1d..efd2089 100755 --- a/tools/tidy.sh +++ b/tools/tidy.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # SPDX-License-Identifier: Apache-2.0 OR MIT # shellcheck disable=SC2046 -set -euo pipefail +set -eEuo pipefail IFS=$'\n\t' cd "$(dirname "$0")"/.. @@ -81,7 +81,7 @@ if [[ -n "$(git ls-files '*.rs')" ]]; then fi check_diff $(git ls-files '*.rs') else - warn "'rustup' is not installed" + warn "'rustup' is not installed; skipped Rust code style check" fi cast_without_turbofish=$(grep -n -E '\.cast\(\)' $(git ls-files '*.rs') || true) if [[ -n "${cast_without_turbofish}" ]]; then @@ -168,7 +168,7 @@ if [[ -n "$(git ls-files '*.c')$(git ls-files '*.cpp')" ]]; then clang-format -i $(git ls-files '*.c') $(git ls-files '*.cpp') check_diff $(git ls-files '*.c') $(git ls-files '*.cpp') else - warn "'clang-format' is not installed" + warn "'clang-format' is not installed; skipped C/C++ code style check" fi fi @@ -176,11 +176,11 @@ fi if [[ -n "$(git ls-files '*.yml')$(git ls-files '*.js')$(git ls-files '*.json')" ]]; then info "checking YAML/JavaScript/JSON code style" if type -P npm &>/dev/null; then - echo "+ npx prettier -l -w \$(git ls-files '*.yml') \$(git ls-files '*.js') \$(git ls-files '*.json')" - npx prettier -l -w $(git ls-files '*.yml') $(git ls-files '*.js') $(git ls-files '*.json') + echo "+ npx -y prettier -l -w \$(git ls-files '*.yml') \$(git ls-files '*.js') \$(git ls-files '*.json')" + npx -y prettier -l -w $(git ls-files '*.yml') $(git ls-files '*.js') $(git ls-files '*.json') check_diff $(git ls-files '*.yml') $(git ls-files '*.js') $(git ls-files '*.json') else - warn "'npm' is not installed" + warn "'npm' is not installed; skipped YAML/JavaScript/JSON code style check" fi # Check GitHub workflows. if [[ -d .github/workflows ]]; then @@ -190,7 +190,7 @@ if [[ -n "$(git ls-files '*.yml')$(git ls-files '*.js')$(git ls-files '*.json')" # The top-level permissions must be weak as they are referenced by all jobs. permissions=$(yq '.permissions' "${workflow}" | jq -c) case "${permissions}" in - '{"contents":"read"}' | '{"contents":"none"}' | '{}') ;; + '{"contents":"read"}' | '{"contents":"none"}') ;; null) error "${workflow}: top level permissions not found; it must be 'contents: read' or weaker permissions" ;; *) error "${workflow}: only 'contents: read' and weaker permissions are allowed at top level; if you want to use stronger permissions, please set job-level permissions" ;; esac @@ -210,7 +210,7 @@ if [[ -n "$(git ls-files '*.yml')$(git ls-files '*.js')$(git ls-files '*.json')" fi done else - warn "'jq' or 'yq' is not installed" + warn "'jq' or 'yq' is not installed; skipped GitHub workflow check" fi fi fi @@ -219,6 +219,21 @@ if [[ -n "$(git ls-files '*.yaml')" ]]; then git ls-files '*.yaml' fi +# Markdown (if exists) +if [[ -n "$(git ls-files '*.md')" ]]; then + info "checking Markdown style" + if type -P npm &>/dev/null; then + echo "+ npx -y markdownlint-cli2 \$(git ls-files '*.md')" + npx -y markdownlint-cli2 $(git ls-files '*.md') + else + warn "'npm' is not installed; skipped Markdown style check" + fi +fi +if [[ -n "$(git ls-files '*.markdown')" ]]; then + error "please use '.md' instead of '.markdown' for consistency" + git ls-files '*.markdown' +fi + # Shell scripts info "checking Shell scripts" if type -P shfmt &>/dev/null; then @@ -226,7 +241,7 @@ if type -P shfmt &>/dev/null; then shfmt -l -w $(git ls-files '*.sh') check_diff $(git ls-files '*.sh') else - warn "'shfmt' is not installed" + warn "'shfmt' is not installed; skipped Shell scripts style check" fi if type -P shellcheck &>/dev/null; then echo "+ shellcheck \$(git ls-files '*.sh')" @@ -241,7 +256,7 @@ if type -P shellcheck &>/dev/null; then fi fi else - warn "'shellcheck' is not installed" + warn "'shellcheck' is not installed; skipped Shell scripts style check" fi # License check @@ -250,24 +265,28 @@ if [[ -f tools/.tidy-check-license-headers ]]; then info "checking license headers (experimental)" failed_files='' for p in $(eval $(.cspell.json; echo >&2 "$0: trapped SIGINT"; exit 1' SIGINT echo "${config_new}" >.cspell.json if [[ -n "${has_rust}" ]]; then - dependencies_words=$(npx <<<"${dependencies}" cspell stdin --no-progress --no-summary --words-only --unique || true) + dependencies_words=$(npx <<<"${dependencies}" -y cspell stdin --no-progress --no-summary --words-only --unique || true) fi - all_words=$(npx cspell --no-progress --no-summary --words-only --unique $(git ls-files | (grep -v "${project_dictionary//\./\\.}" || true)) || true) - # TODO: handle SIGINT + all_words=$(npx -y cspell --no-progress --no-summary --words-only --unique $(git ls-files | (grep -v "${project_dictionary//\./\\.}" || true)) || true) echo "${config_old}" >.cspell.json + trap - SIGINT cat >.github/.cspell/rust-dependencies.txt <