Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: automatic package manager install when autofixing #71

Merged
merged 23 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b7cce73
feat: add auto install functionality
Willem-Jaap Jun 29, 2024
f9197fe
fix: update don't install message
Willem-Jaap Jun 29, 2024
aeb492d
refactor: handle package manager detection in a more logical manner
Willem-Jaap Jun 29, 2024
1b18253
test: add install fixtures with package-lock.json
Willem-Jaap Jun 29, 2024
560528f
test: add package.json test file
Willem-Jaap Jun 29, 2024
33a8691
test: add test for missing package manager
Willem-Jaap Jun 29, 2024
d763638
feat: add short version of `--fix`
Willem-Jaap Jun 30, 2024
9132057
feat: run install automatically, add `--no-install` flag
Willem-Jaap Jun 30, 2024
0086b72
fix: empty package lock
Willem-Jaap Jul 7, 2024
177e431
feat: add no_install to all args
Willem-Jaap Jul 7, 2024
298966c
test: add install run test
Willem-Jaap Jul 7, 2024
b9b7233
docs: add install info to readme
Willem-Jaap Jul 7, 2024
5330925
feat: write command child process `stdout` & `stderr`, correct messag…
Willem-Jaap Jul 7, 2024
ad87ea8
fix: stream `stdout` & `stderr` to parent io
Willem-Jaap Jul 7, 2024
1a4e2ed
docs: update wording in readme
Willem-Jaap Aug 11, 2024
85a435e
docs: update title of no install mode
Willem-Jaap Aug 11, 2024
348e940
docs: update wording
Willem-Jaap Aug 11, 2024
d2632ee
fix: formatting
QuiiBz Aug 24, 2024
a5c0433
refactor: move install before printing issues
QuiiBz Aug 24, 2024
cd603aa
refactor: add support for bun, improve styling
QuiiBz Aug 24, 2024
28424c1
chore: update docs
QuiiBz Aug 24, 2024
6aad0af
Merge branch 'main' into auto-install
QuiiBz Aug 24, 2024
96adbc4
fix: missing field
QuiiBz Aug 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,20 @@ jobs:

## Autofix

Most issues can be automatically fixed by using the `--fix` flag. Note that autofix is disabled in CI environments (when `$CI` is set):
Most issues can be automatically fixed by using the `--fix` flag. Sherif tries to auto-detect your package manager and runs install. Note that autofix is disabled in CI environments (when `$CI` is set):
Willem-Jaap marked this conversation as resolved.
Show resolved Hide resolved

```bash
sherif --fix
```

## No-install mode
Willem-Jaap marked this conversation as resolved.
Show resolved Hide resolved

When you don't want Sherif to install packages after running autofix, you can use the `--no-install` flag:
Willem-Jaap marked this conversation as resolved.
Show resolved Hide resolved

```bash
sherif --fix --no-install
```

## Rules

You can ignore a specific rule by using `--ignore-rule <name>` (or `-r <name>`):
Expand Down
3 changes: 3 additions & 0 deletions fixtures/install/apps/abc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "abc"
}
3 changes: 3 additions & 0 deletions fixtures/install/apps/def/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "def"
}
1 change: 1 addition & 0 deletions fixtures/install/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions fixtures/install/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "install",
"workspaces": [
"apps/*"
]
}
6 changes: 5 additions & 1 deletion src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ pub struct Args {
pub path: PathBuf,

/// Fix the issues automatically, if possible.
#[arg(long)]
#[arg(long, short)]
pub fix: bool,

/// Don't run the package manager install command.
#[arg(long)]
pub no_install: bool,

/// Ignore the `multiple-dependency-versions` rule for the given dependency name.
#[arg(long, short)]
pub ignore_dependency: Vec<String>,
Expand Down
13 changes: 13 additions & 0 deletions src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ mod test {
let args = Args {
path: "unknown".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand All @@ -301,6 +302,7 @@ mod test {
let args = Args {
path: "fixtures/empty".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand All @@ -320,6 +322,7 @@ mod test {
let args = Args {
path: "fixtures/basic".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand All @@ -345,6 +348,7 @@ mod test {
let args = Args {
path: "fixtures/pnpm".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand All @@ -370,6 +374,7 @@ mod test {
let args = Args {
path: "fixtures/yarn-nohoist".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand All @@ -395,6 +400,7 @@ mod test {
let args = Args {
path: "fixtures/no-workspace-pnpm".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand All @@ -414,6 +420,7 @@ mod test {
let args = Args {
path: "fixtures/without-package-json".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand All @@ -440,6 +447,7 @@ mod test {
let args = Args {
path: "fixtures/ignore-paths".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand Down Expand Up @@ -472,6 +480,7 @@ mod test {
let args = Args {
path: "fixtures/root-issues".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand Down Expand Up @@ -506,6 +515,7 @@ mod test {
fn collect_root_issues_fixed() {
let args = Args {
fix: false,
no_install: true,
path: "fixtures/root-issues-fixed".into(),
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
Expand All @@ -524,6 +534,7 @@ mod test {
let args = Args {
path: "fixtures/dependencies".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand Down Expand Up @@ -552,6 +563,7 @@ mod test {
let args = Args {
path: "fixtures/dependencies-star".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand All @@ -576,6 +588,7 @@ mod test {
let args = Args {
path: "fixtures/pnpm-glob".into(),
fix: false,
no_install: true,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
Expand Down
123 changes: 123 additions & 0 deletions src/install.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use std::process::Stdio;
use std::{fs, process::Command};

use colored::Colorize;
use inquire::Select;


pub fn run () {
let mut package_manager = detect_package_manager();

if package_manager.is_empty() {
println!("Could not auto-detect package manager.");
package_manager = manual_select_package_manager();
}

println!("Running install using: {}...", package_manager);

let mut command = Command::new(package_manager)
.arg("install")
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()
.unwrap();

let status = command.wait().unwrap();

if !status.success() {
println!("{} Failed to run `install`.", "✗".red());
std::process::exit(1);
}

println!("{} Install completed.", "✓".green());
}

fn detect_package_manager () -> String {
QuiiBz marked this conversation as resolved.
Show resolved Hide resolved
if fs::metadata("package-lock.json").is_ok() {
return "npm".to_string();
}

if fs::metadata("yarn.lock").is_ok() {
return "yarn".to_string();
}

if fs::metadata("pnpm-lock.yaml").is_ok() {
return "pnpm".to_string();
}

Willem-Jaap marked this conversation as resolved.
Show resolved Hide resolved
return "".to_string();
}

fn manual_select_package_manager () -> String {
let package_manager = Select::new("Select a package manager", vec!["npm", "yarn", "pnpm"])
.prompt();

match package_manager {
Ok("npm") => {
return "npm".to_string();
}
Ok("yarn") => {
return "yarn".to_string();
}
Ok("pnpm") => {
return "pnpm".to_string();
}
_ => {
println!("Invalid package manager selected. Exiting...");
std::process::exit(1);
}
}
}

#[cfg(test)]

mod test {
use std::fs;
use serde_json::Value;
use crate::{args::Args, collect::collect_packages};

#[test]
fn test_detect_package_manager() {
use super::*;
use std::fs;

fs::File::create("package-lock.json").unwrap();
assert_eq!(detect_package_manager(), "npm");

fs::remove_file("package-lock.json").unwrap();
fs::File::create("yarn.lock").unwrap();
assert_eq!(detect_package_manager(), "yarn");

fs::remove_file("yarn.lock").unwrap();
fs::File::create("pnpm-lock.yaml").unwrap();
assert_eq!(detect_package_manager(), "pnpm");

fs::remove_file("pnpm-lock.yaml").unwrap();
assert_eq!(detect_package_manager(), "");
}

#[test]
fn test_install_run() {
let args = Args {
path: "fixtures/install".into(),
fix: false,
no_install: false,
ignore_rule: Vec::new(),
ignore_package: Vec::new(),
ignore_dependency: Vec::new(),
};

let _ = collect_packages(&args);

std::env::set_current_dir("fixtures/install").unwrap();
super::run();

// Test if the previously empty package-lock.json now contains the "install" name to indicate that the install command was run
let file = fs::File::open("package-lock.json");
let json: Result<Value, serde_json::Error> = serde_json::from_reader(file.unwrap());
assert_eq!(json.unwrap()["name"], "install");

std::env::set_current_dir("../../").unwrap();
}

}
14 changes: 14 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ use crate::rules::IssueLevel;
use crate::{args::Args, printer::print_error};
use clap::Parser;
use collect::{collect_issues, collect_packages};
use colored::Colorize;
use printer::{print_footer, print_issues};
use std::time::Instant;

mod args;
mod collect;
mod install;
mod json;
mod packages;
mod plural;
Expand Down Expand Up @@ -69,4 +71,16 @@ fn main() {
if errors > 0 {
std::process::exit(1);
}

if args.fix {
if args.no_install {
println!(
"{}",
" Don't forget to run `install` to apply the changes.".bright_black()
);
return;
}

install::run();
}
}
2 changes: 1 addition & 1 deletion src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub fn print_footer(
);
println!(
"{}",
" Note: use `-i` to ignore dependencies, `-r` to ignore rules, `-p` to ignore packages, and `--fix` to autofix fixable issues."
" Note: use `-i` to ignore dependencies, `-r` to ignore rules, `-p` to ignore packages, and `-f` to autofix fixable issues."
.bright_black()
);
}