Fixed publish
This commit is contained in:
13
crates/gitignore_inner/Cargo.toml
Normal file
13
crates/gitignore_inner/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "gitignore_inner"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.0.17", features = ["env", "unicode", "string"] }
|
||||
console = "0.15.2"
|
||||
eyre = "0.6.8"
|
||||
tracing = { version = "0.1.37", features = ["log"] }
|
||||
tracing-subscriber = { version = "0.3.16", features = ["local-time", "env-filter"] }
|
191
crates/gitignore_inner/src/.main.rs.rustfmt
Normal file
191
crates/gitignore_inner/src/.main.rs.rustfmt
Normal file
@@ -0,0 +1,191 @@
|
||||
use clap::{Arg, Command};
|
||||
use eyre::{Context, ContextCompat};
|
||||
use std::io::prelude::*;
|
||||
use std::{env::current_dir, io::Read, path::PathBuf};
|
||||
|
||||
fn main() -> eyre::Result<()> {
|
||||
let matches = Command::new("gitignore")
|
||||
.version("0.1")
|
||||
.author("Kasper J. Hermansen <contact@kjuulh.io>")
|
||||
.about("Easily ignore items and remove from git state")
|
||||
.long_about("git ignore is a utility tool for easily adding patterns to your .gitignore file.
|
||||
Easily add patterns using `git ignore <pattern>` this will by default also help you remove committed code violating these patterns
|
||||
")
|
||||
.propagate_version(true)
|
||||
.arg(
|
||||
Arg::new("pattern")
|
||||
.help("the pattern you want to ignore")
|
||||
.long_help("the pattern you want to ignore in the nearest .gitignore file")
|
||||
.required(true),
|
||||
).arg(
|
||||
Arg::new("log-level").long("log-level").help("choose a log level and get more messages").long_help("Choose a log level and get more message, defaults to [INFO]"))
|
||||
.get_matches();
|
||||
|
||||
let pattern = matches
|
||||
.get_one::<String>("pattern")
|
||||
.context("missing [pattern]")?;
|
||||
|
||||
add_gitignore_pattern(pattern)
|
||||
}
|
||||
|
||||
enum GitActions {
|
||||
AddPattern {
|
||||
git_path: PathBuf,
|
||||
gitignore_path: PathBuf,
|
||||
},
|
||||
CreateIgnoreAndAddPattern {
|
||||
git_path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
fn add_gitignore_pattern(pattern: &String) -> eyre::Result<()> {
|
||||
let curdir = current_dir().context(
|
||||
"could not find current_dir, you may not have permission to access that directory",
|
||||
)?;
|
||||
let actions = match search_for_dotgitignore(&curdir)? {
|
||||
// If we have an ignore path, make sure it is in a git repo as well
|
||||
GitSearchResult::GitIgnore(ignorepath) => match search_for_git_root(&curdir)? {
|
||||
GitSearchResult::Git(gitpath) => GitActions::AddPattern {
|
||||
git_path: gitpath,
|
||||
gitignore_path: ignorepath,
|
||||
},
|
||||
_ => return Err(eyre::anyhow!("could not find parent git directory")),
|
||||
},
|
||||
// Find the nearest git repo
|
||||
GitSearchResult::Git(gitpath) => {
|
||||
GitActions::CreateIgnoreAndAddPattern { git_path: gitpath }
|
||||
} // We will always have either above, or an error so we have no default arm
|
||||
};
|
||||
|
||||
match actions {
|
||||
GitActions::AddPattern {
|
||||
git_path,
|
||||
gitignore_path,
|
||||
} => {
|
||||
let mut gitignore_file = open_gitignore_file(&gitignore_path)?;
|
||||
// TODO: search for pattern in file
|
||||
let mut gitignore_content = String::new();
|
||||
gitignore_file
|
||||
.read_to_string(&mut gitignore_content)
|
||||
.context(format!(
|
||||
"could not read file: {}",
|
||||
gitignore_path.to_string_lossy()
|
||||
))?;
|
||||
if gitignore_content.contains(pattern) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
writeln!(gitignore_file, "{}", pattern).context("could not write contents to file")?;
|
||||
gitignore_file
|
||||
.sync_all()
|
||||
.context("failed to write data to disk")?;
|
||||
}
|
||||
GitActions::CreateIgnoreAndAddPattern { git_path } => {
|
||||
// TODO: Create gitignore file in root
|
||||
let mut gitignore_file = create_gitignore_file(&git_path)?;
|
||||
// TODO: do same as above
|
||||
writeln!(gitignore_file, "{}", pattern).context("could not write contents to file")?;
|
||||
gitignore_file
|
||||
.sync_all()
|
||||
.context("failed to write data to disk")?;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Run git rm -r --cached on the .git root
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_gitignore_file(gitroot: &PathBuf) -> eyre::Result<std::fs::File> {
|
||||
let mut ignore_path = gitroot.clone();
|
||||
if !ignore_path.pop() {
|
||||
return Err(eyre::anyhow!("could not open parent dir"));
|
||||
}
|
||||
ignore_path.push(".gitignore");
|
||||
let file = std::fs::File::create(ignore_path.clone()).context(format!(
|
||||
"could not create file at path: {}",
|
||||
ignore_path.to_string_lossy()
|
||||
))?;
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
fn open_gitignore_file(gitignore: &PathBuf) -> eyre::Result<std::fs::File> {
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(gitignore)
|
||||
.context(format!(
|
||||
"could not create file at path: {}",
|
||||
gitignore.to_string_lossy()
|
||||
))?;
|
||||
|
||||
return Ok(file);
|
||||
}
|
||||
|
||||
enum GitSearchResult {
|
||||
GitIgnore(PathBuf),
|
||||
Git(PathBuf),
|
||||
}
|
||||
|
||||
fn search_for_git_root(path: &PathBuf) -> eyre::Result<GitSearchResult> {
|
||||
if !path.is_dir() {
|
||||
return Err(eyre::anyhow!(
|
||||
"path is not a dir: {}",
|
||||
path.to_string_lossy()
|
||||
));
|
||||
}
|
||||
|
||||
let direntries = std::fs::read_dir(path)
|
||||
.context(format!("could not open dir: {}", path.to_string_lossy()))?;
|
||||
for direntry in direntries {
|
||||
let entry = direntry.context("could not access file")?;
|
||||
|
||||
let file_name = entry.file_name().to_os_string();
|
||||
match file_name.to_str().context("could not convert to str")? {
|
||||
".git" => return Ok(GitSearchResult::Git(entry.path())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut upwards_par = path.clone();
|
||||
if !upwards_par.pop() {
|
||||
return Err(eyre::anyhow!(
|
||||
"no parent exists, cannot check further, you may not be in a git repository"
|
||||
));
|
||||
}
|
||||
|
||||
search_for_git_root(&upwards_par)
|
||||
}
|
||||
|
||||
fn search_for_dotgitignore(path: &PathBuf) -> eyre::Result<GitSearchResult> {
|
||||
if !path.is_dir() {
|
||||
return Err(eyre::anyhow!(
|
||||
"path is not a dir: {}",
|
||||
path.to_string_lossy()
|
||||
));
|
||||
}
|
||||
|
||||
let direntries = std::fs::read_dir(path)
|
||||
.context(format!("could not open dir: {}", path.to_string_lossy()))?;
|
||||
for direntry in direntries {
|
||||
let entry = direntry.context("could not access file")?;
|
||||
|
||||
let file_name = entry.file_name().to_os_string();
|
||||
|
||||
match file_name.to_str().context("could not convert to str")? {
|
||||
".gitignore" => return Ok(GitSearchResult::GitIgnore(entry.path())),
|
||||
".git" => return Ok(GitSearchResult::Git(entry.path())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut upwards_par = path.clone();
|
||||
if !upwards_par.pop() {
|
||||
return Err(eyre::anyhow!(
|
||||
"no parent exists, cannot check further, you may not be in a git repository"
|
||||
));
|
||||
}
|
||||
|
||||
search_for_dotgitignore(&upwards_par)
|
||||
}
|
227
crates/gitignore_inner/src/lib.rs
Normal file
227
crates/gitignore_inner/src/lib.rs
Normal file
@@ -0,0 +1,227 @@
|
||||
use clap::{Arg, Command};
|
||||
use eyre::{Context, ContextCompat};
|
||||
use std::io::prelude::*;
|
||||
use std::{env::current_dir, io::Read, path::PathBuf};
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
pub fn main() -> eyre::Result<()> {
|
||||
let matches = Command::new("gitignore")
|
||||
.version("0.1")
|
||||
.author("Kasper J. Hermansen <contact@kjuulh.io>")
|
||||
.about("Easily ignore items and remove from git state")
|
||||
.long_about("git ignore is a utility tool for easily adding patterns to your .gitignore file.
|
||||
Easily add patterns using `git ignore <pattern>` this will by default also help you remove committed code violating these patterns
|
||||
")
|
||||
.propagate_version(true)
|
||||
.arg(
|
||||
Arg::new("pattern")
|
||||
.help("the pattern you want to ignore")
|
||||
.long_help("the pattern you want to ignore in the nearest .gitignore file")
|
||||
.required(true),
|
||||
).arg(
|
||||
Arg::new("log-level").long("log-level").help("choose a log level and get more messages").long_help("Choose a log level and get more message, defaults to [fatal]"))
|
||||
.get_matches();
|
||||
|
||||
let log_level = match matches.get_one::<String>("log-level").map(|f| f.as_str()) {
|
||||
Some("off") => "off",
|
||||
Some("info") => "info",
|
||||
Some("debug") => "debug",
|
||||
Some("warn") => "warn",
|
||||
Some("error") => "error",
|
||||
_ => "error",
|
||||
};
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::EnvFilter::new(format!(
|
||||
"gitignore={}",
|
||||
log_level
|
||||
)))
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
let term = console::Term::stdout();
|
||||
|
||||
let pattern = matches
|
||||
.get_one::<String>("pattern")
|
||||
.context("missing [pattern]")?;
|
||||
|
||||
add_gitignore_pattern(term, pattern)
|
||||
}
|
||||
|
||||
enum GitActions {
|
||||
AddPattern { gitignore_path: PathBuf },
|
||||
CreateIgnoreAndAddPattern { git_path: PathBuf },
|
||||
}
|
||||
|
||||
fn add_gitignore_pattern(term: console::Term, pattern: &String) -> eyre::Result<()> {
|
||||
term.write_line("git ignore: Add pattern")?;
|
||||
let curdir = current_dir().context(
|
||||
"could not find current_dir, you may not have permission to access that directory",
|
||||
)?;
|
||||
let actions = match search_for_dotgitignore(&curdir)? {
|
||||
// If we have an ignore path, make sure it is in a git repo as well
|
||||
GitSearchResult::GitIgnore(ignorepath) => match search_for_git_root(&curdir)? {
|
||||
GitSearchResult::Git(_gitpath) => GitActions::AddPattern {
|
||||
gitignore_path: ignorepath,
|
||||
},
|
||||
_ => return Err(eyre::anyhow!("could not find parent git directory")),
|
||||
},
|
||||
// Find the nearest git repo
|
||||
GitSearchResult::Git(gitpath) => {
|
||||
GitActions::CreateIgnoreAndAddPattern { git_path: gitpath }
|
||||
} // We will always have either above, or an error so we have no default arm
|
||||
};
|
||||
|
||||
match actions {
|
||||
GitActions::AddPattern { gitignore_path } => {
|
||||
term.write_line("Found existing .gitignore")?;
|
||||
let mut gitignore_file = open_gitignore_file(&gitignore_path)?;
|
||||
let mut gitignore_content = String::new();
|
||||
gitignore_file
|
||||
.read_to_string(&mut gitignore_content)
|
||||
.context(format!(
|
||||
"could not read file: {}",
|
||||
gitignore_path.to_string_lossy()
|
||||
))?;
|
||||
if gitignore_content.contains(pattern) {
|
||||
term.write_line(".gitignore already contains pattern, skipping")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
term.write_line("adding pattern to file")?;
|
||||
writeln!(gitignore_file, "{}", pattern).context("could not write contents to file")?;
|
||||
gitignore_file
|
||||
.sync_all()
|
||||
.context("failed to write data to disk")?;
|
||||
}
|
||||
GitActions::CreateIgnoreAndAddPattern { git_path } => {
|
||||
term.write_line(
|
||||
"could not find .gitignore file, creating one in the root of the git repository",
|
||||
)?;
|
||||
let mut gitignore_file = create_gitignore_file(&git_path)?;
|
||||
term.write_line("adding pattern to file")?;
|
||||
writeln!(gitignore_file, "{}", pattern).context("could not write contents to file")?;
|
||||
gitignore_file
|
||||
.sync_all()
|
||||
.context("failed to write data to disk")?;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Run git rm -r --cached --pathspec <pattern> on the .git root
|
||||
let output = std::process::Command::new("git")
|
||||
.arg("rm")
|
||||
.arg("-r")
|
||||
.arg("--cached")
|
||||
.arg("--ignore-unmatch")
|
||||
.arg(pattern)
|
||||
.output()
|
||||
.context("could not process git remove from index command")?;
|
||||
String::from_utf8(output.stdout)?
|
||||
.lines()
|
||||
.chain(String::from_utf8(output.stderr)?.lines())
|
||||
.try_for_each(|l| term.write_line(l))
|
||||
.context("could not print all output to terminal")?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(eyre::anyhow!("failed to run git index command"));
|
||||
}
|
||||
|
||||
term.write_line("git successfully removed files")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_gitignore_file(gitroot: &PathBuf) -> eyre::Result<std::fs::File> {
|
||||
let mut ignore_path = gitroot.clone();
|
||||
if !ignore_path.pop() {
|
||||
return Err(eyre::anyhow!("could not open parent dir"));
|
||||
}
|
||||
ignore_path.push(".gitignore");
|
||||
let file = std::fs::File::create(ignore_path.clone()).context(format!(
|
||||
"could not create file at path: {}",
|
||||
ignore_path.to_string_lossy()
|
||||
))?;
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
fn open_gitignore_file(gitignore: &PathBuf) -> eyre::Result<std::fs::File> {
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(gitignore)
|
||||
.context(format!(
|
||||
"could not create file at path: {}",
|
||||
gitignore.to_string_lossy()
|
||||
))?;
|
||||
|
||||
return Ok(file);
|
||||
}
|
||||
|
||||
enum GitSearchResult {
|
||||
GitIgnore(PathBuf),
|
||||
Git(PathBuf),
|
||||
}
|
||||
|
||||
fn search_for_git_root(path: &PathBuf) -> eyre::Result<GitSearchResult> {
|
||||
if !path.is_dir() {
|
||||
return Err(eyre::anyhow!(
|
||||
"path is not a dir: {}",
|
||||
path.to_string_lossy()
|
||||
));
|
||||
}
|
||||
|
||||
let direntries = std::fs::read_dir(path)
|
||||
.context(format!("could not open dir: {}", path.to_string_lossy()))?;
|
||||
for direntry in direntries {
|
||||
let entry = direntry.context("could not access file")?;
|
||||
|
||||
let file_name = entry.file_name().to_os_string();
|
||||
match file_name.to_str().context("could not convert to str")? {
|
||||
".git" => return Ok(GitSearchResult::Git(entry.path())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut upwards_par = path.clone();
|
||||
if !upwards_par.pop() {
|
||||
return Err(eyre::anyhow!(
|
||||
"no parent exists, cannot check further, you may not be in a git repository"
|
||||
));
|
||||
}
|
||||
|
||||
search_for_git_root(&upwards_par)
|
||||
}
|
||||
|
||||
fn search_for_dotgitignore(path: &PathBuf) -> eyre::Result<GitSearchResult> {
|
||||
if !path.is_dir() {
|
||||
return Err(eyre::anyhow!(
|
||||
"path is not a dir: {}",
|
||||
path.to_string_lossy()
|
||||
));
|
||||
}
|
||||
|
||||
let direntries = std::fs::read_dir(path)
|
||||
.context(format!("could not open dir: {}", path.to_string_lossy()))?;
|
||||
for direntry in direntries {
|
||||
let entry = direntry.context("could not access file")?;
|
||||
|
||||
let file_name = entry.file_name().to_os_string();
|
||||
|
||||
match file_name.to_str().context("could not convert to str")? {
|
||||
".gitignore" => return Ok(GitSearchResult::GitIgnore(entry.path())),
|
||||
".git" => return Ok(GitSearchResult::Git(entry.path())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut upwards_par = path.clone();
|
||||
if !upwards_par.pop() {
|
||||
return Err(eyre::anyhow!(
|
||||
"no parent exists, cannot check further, you may not be in a git repository"
|
||||
));
|
||||
}
|
||||
|
||||
search_for_dotgitignore(&upwards_par)
|
||||
}
|
Reference in New Issue
Block a user