From 0560a8616e5b71024125d60e6e111eb6cb329dc2 Mon Sep 17 00:00:00 2001 From: Christopher Chang <51393127+chriscerie@users.noreply.github.com> Date: Sat, 14 Oct 2023 12:55:36 -0700 Subject: [PATCH] Abort fix with changes in working directory --- docs/src/cli/usage.md | 2 +- selene/src/main.rs | 36 ++++++++++++++++++++++++++++++++++++ selene/src/opts.rs | 10 ++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/docs/src/cli/usage.md b/docs/src/cli/usage.md index fc7f0a79..aac76636 100644 --- a/docs/src/cli/usage.md +++ b/docs/src/cli/usage.md @@ -9,7 +9,7 @@ USAGE: FLAGS: --allow-warnings Pass when only warnings occur --no-exclude Ignore excludes defined in config - --fix Automatically applies some lint suggestions + --fix Automatically fix applicable lint warnings -h, --help Prints help information -n, --no-summary Suppress summary information -q, --quiet Display only the necessary information. Equivalent to --display-style="quiet" diff --git a/selene/src/main.rs b/selene/src/main.rs index d3d4b26c..2516b2e2 100644 --- a/selene/src/main.rs +++ b/selene/src/main.rs @@ -3,6 +3,7 @@ use std::{ fmt, fs, io::{self, Read, Write}, path::{Path, PathBuf}, + process::Command, sync::{ atomic::{AtomicUsize, Ordering}, Arc, RwLock, @@ -553,6 +554,17 @@ fn start(mut options: opts::Options) { None => {} } + if options.fix && !options.allow_dirty { + if has_unstaged_changes() || (!options.allow_staged && has_staged_changes()) { + error( + "the working directory of this package has uncommitted changes, and `selene --fix` can potentially \ + perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, `--allow-staged`, \ + or commit the changes" + ); + std::process::exit(1); + } + } + let (config, config_directory): (CheckerConfig, Option) = match options.config { Some(config_file) => { @@ -830,6 +842,30 @@ fn get_opts_safe(mut args: Vec, luacheck: bool) -> Result bool { + let output = Command::new("git") + .arg("status") + .arg("--porcelain") + .output() + .expect("Failed to execute git"); + + let stdout = String::from_utf8(output.stdout).expect("Failed to convert git output to string"); + + stdout.lines().any(|line| line.chars().nth(1) != Some(' ')) +} + +fn has_staged_changes() -> bool { + let output = Command::new("git") + .arg("status") + .arg("--porcelain") + .output() + .expect("Failed to execute git"); + + let stdout = String::from_utf8(output.stdout).expect("Failed to convert git output to string"); + + stdout.lines().any(|line| line.chars().nth(0) != Some(' ')) +} + #[cfg(feature = "roblox")] fn generate_roblox_std() -> color_eyre::Result { let (contents, std) = roblox::RobloxGenerator::generate()?; diff --git a/selene/src/opts.rs b/selene/src/opts.rs index 8f83a3b1..58c13ebb 100644 --- a/selene/src/opts.rs +++ b/selene/src/opts.rs @@ -72,6 +72,16 @@ pub struct Options { #[structopt(long)] pub fix: bool, + + // Fix code even if the working directory has changes + // Only used with `fix` + #[structopt(long, hidden(true))] + pub allow_dirty: bool, + + // Fix code even if the working directory has staged changes + // Only used with `fix` + #[structopt(long, hidden(true))] + pub allow_staged: bool, } impl Options {