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: support reading commit message from a file #198

Merged
merged 1 commit into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ pub struct Opt {
)]
pub init: Option<InitOption>,

#[arg(
long = "file",
value_name = "FILE",
help = "Read commit message from file",
conflicts_with = "commit_message"
)]
pub commit_file: Option<String>,

/// Outputs enabled rules' description as bash comments for the prepare-commit-msg hook.
#[arg(long, num_args = 0, hide = true)]
pub prepare_commit_message: bool,
Expand Down
3 changes: 3 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ pub enum SumiError {
#[error("{details}")]
GeneralError { details: String },

#[error("Failed to read commit message from file '{path}': {error}")]
CommitFileError { path: String, error: String },

#[error("{lines_with_errors} out of {total_lines} {line_or_lines} failed linting. See the errors above")]
SplitLinesErrors {
lines_with_errors: usize,
Expand Down
23 changes: 17 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn run() -> Result<(), SumiError> {
return Err(SumiError::NoRulesEnabled);
}

let commit_message = get_commit_from_arg_or_stdin(args.commit_message)?;
let commit_message = get_commit_from_arg_or_stdin(args.commit_message, args.commit_file)?;

let lint_result = if config.split_lines {
run_lint_on_each_line(&commit_message, &config)
Expand Down Expand Up @@ -85,14 +85,25 @@ fn init_logger_from_config(config: &Config) {
.init();
}

fn get_commit_from_arg_or_stdin(commit: Option<String>) -> Result<String, SumiError> {
if let Some(commit) = commit {
Ok(commit)
} else {
get_commit_from_stdin()
fn get_commit_from_arg_or_stdin(
commit: Option<String>,
commit_file: Option<String>,
) -> Result<String, SumiError> {
match (commit, commit_file) {
(Some(message), _) => Ok(message),
(None, Some(path)) => get_commit_from_file(&path),
(None, None) => get_commit_from_stdin(),
}
}

fn get_commit_from_file(path: &str) -> Result<String, SumiError> {
std::fs::read_to_string(path)
.map(|content| content.trim().to_string())
.map_err(|e| SumiError::GeneralError {
details: format!("Could not read commit message from '{}': {}", path, e),
})
}

fn get_commit_from_stdin() -> Result<String, SumiError> {
let mut buffer = String::new();
io::stdin().read_to_string(&mut buffer)?;
Expand Down
1 change: 1 addition & 0 deletions tests/lint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod test_commit_changes;
mod test_config;
mod test_conventional_commits;
mod test_display;
mod test_file_input;
mod test_gitmoji;
mod test_single_rule;

Expand Down
84 changes: 84 additions & 0 deletions tests/lint/test_file_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use crate::run_isolated_git_sumi;
use predicates::str::contains;
use tempfile::tempdir;

#[test]
fn success_read_from_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("commit-msg.txt");
std::fs::write(&file_path, "feat: add new feature").unwrap();
let mut cmd = run_isolated_git_sumi("");
cmd.arg("--file")
.arg(file_path)
.arg("-C")
.assert()
.success();
}

#[test]
fn error_nonexistent_file() {
let mut cmd = run_isolated_git_sumi("");
cmd.arg("--file")
.arg("nonexistent-file.txt")
.arg("-C")
.assert()
.failure()
.stderr(contains(
"Could not read commit message from 'nonexistent-file.txt'",
));
}

#[test]
fn error_empty_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("empty.txt");
std::fs::write(&file_path, "").unwrap();
let mut cmd = run_isolated_git_sumi("");
cmd.arg("--file")
.arg(file_path)
.arg("-C")
.assert()
.failure()
.stderr(contains("Header must not be empty"));
}

#[test]
fn error_conflict_file_and_message() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("commit.txt");
std::fs::write(&file_path, "feat: test").unwrap();

let mut cmd = run_isolated_git_sumi("");
cmd.arg("--file")
.arg(file_path)
.arg("direct message")
.assert()
.failure()
.stderr(contains("cannot be used with"));
}

#[test]
fn success_file_with_comments() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("commit-with-comments.txt");
std::fs::write(&file_path, "feat: add feature\n# This is a comment\n").unwrap();
let mut cmd = run_isolated_git_sumi("");
cmd.arg("--file")
.arg(file_path)
.arg("-C")
.assert()
.success();
}

#[test]
fn success_file_with_multiline() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("multiline.txt");
std::fs::write(&file_path, "feat: add feature\n\nDetailed description").unwrap();
let mut cmd = run_isolated_git_sumi("");
cmd.arg("--file")
.arg(file_path)
.arg("-C")
.assert()
.success();
}
2 changes: 2 additions & 0 deletions website/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ git-sumi [OPTIONS] [--] [COMMIT_MESSAGE]
Path to a TOML configuration file [env: GIT_SUMI_CONFIG=]
-f, --format <FORMAT>
Sets display format: cli, json, table, toml [env: GIT_SUMI_FORMAT=]
--file <FILE>
Read commit message from file
```

### Rules
Expand Down