ci

CI for all possible combinations of features in Cargo.toml.
git clone https://git.philomathiclife.com/repos/ci
Log | Files | Refs | README

commit c107e6ad7f7abbd3a3fa3dd26f82a66ce417fa61
parent 67653c1f63ee2c358ff79a420d2bdab07ea6027c
Author: Zack Newman <zack@philomathiclife.com>
Date:   Tue, 26 Nov 2024 19:39:26 -0700

make ci work in child directories

Diffstat:
MCargo.toml | 7++-----
MREADME.md | 3++-
Msrc/main.rs | 32++++++++++++++++++++++++++++++--
3 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -10,13 +10,10 @@ name = "ci" readme = "README.md" repository = "https://git.philomathiclife.com/repos/ci/" rust-version = "1.81.0" -version = "0.2.0" - -[badges] -maintenance = { status = "actively-developed" } +version = "0.2.1" [dependencies] -serde = { version = "1.0.214", default-features = false } +serde = { version = "1.0.215", default-features = false } toml = { version = "0.8.19", default-features = false, features = ["parse"] } [profile.release] diff --git a/README.md b/README.md @@ -26,7 +26,8 @@ invoking `cargo` with each possible combination of features, this handles it aut * `--color`: `--color always` is passed to the above commands; otherwise without this option, `--color never` is passed. * `--dir <path to directory Cargo.toml is in>`: `ci` changes the working directory to the passed path (after - canonicalizing it) before executing. Without this, the current directory is used. + canonicalizing it) before executing. Without this, the working directory and all ancestor directories are searched + for `Cargo.toml` before changing the working directory to its location. When `clippy`, `doc_tests`, and `tests` are not passed; then all three are invoked. diff --git a/src/main.rs b/src/main.rs @@ -87,7 +87,8 @@ use core::{ use std::{ collections::HashSet, env, fs, - io::{self, Write}, + io::{self, ErrorKind, Write}, + path::PathBuf, }; use toml::de::Error as TomlErr; /// Application error. @@ -130,8 +131,35 @@ impl Error for E {} reason = "asserts are fine when they indicate a bug" )] fn main() -> Result<(), E> { + /// Checks if `Cargo.toml` exists in `cur_dir`; if not, it recursively checks the ancestor + /// directories. + /// + /// We make this recursive in the rare (impossible?) case that traversal becomes circular; in which case, + /// we want a stack overflow to occur. + fn set_env(mut cur_dir: PathBuf) -> Result<bool, io::Error> { + if fs::exists("Cargo.toml")? { + Ok(true) + } else if cur_dir.pop() { + env::set_current_dir(cur_dir.as_path()).and_then(|()| set_env(cur_dir)) + } else { + Ok(false) + } + } Opts::from_args().and_then(|(mut opt, path)| { - path.map_or(Ok(()), |p| env::set_current_dir(p).map_err(E::Io)).and_then(|()| { + path.map_or_else( + || { + env::current_dir().and_then(|dir| { + set_env(dir).and_then(|exists| { + if exists { + Ok(()) + } else { + Err(io::Error::new(ErrorKind::NotFound, "Cargo.toml does not exist in the current directory nor any of the ancestor directories")) + } + }) + }) + }, + env::set_current_dir + ).map_err(E::Io).and_then(|()| { fs::read_to_string("Cargo.toml") .map_err(E::Io) .and_then(|toml| manifest::from_toml(toml.as_str()).map_err(E::Toml))