Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
91d504c
Switched from interfacing to git2 to gitoxide for basic repostiory
philocalyst Dec 7, 2025
41d70f0
Switched to using gitoxide and converted logic to work within that in…
philocalyst Dec 7, 2025
dd14870
Switched the integrations tests to make use of gitoxide
philocalyst Dec 7, 2025
7c488dd
Added gix dependency
philocalyst Dec 7, 2025
9534ce9
yo hi gix
philocalyst Feb 4, 2026
451614d
removed vendored openssl
philocalyst Feb 4, 2026
9107ecd
gix update
philocalyst Feb 4, 2026
a72c600
added scoping here
philocalyst Feb 21, 2026
f5a758f
added testing for scoping
philocalyst Feb 21, 2026
168645e
git2 for testing
philocalyst Feb 21, 2026
c2aeff3
ci(deps): update actions/cache action to v5 (#166)
renovate[bot] Dec 15, 2025
1bc60bb
ci(deps): update codecov/codecov-action action to v5.5.2 (#165)
renovate[bot] Dec 15, 2025
4999896
ci(deps): update actions/checkout action to v6.0.1 (#163)
renovate[bot] Dec 15, 2025
4f0b43f
test: fix rexpect windows failures, more in ci (#168)
cococonscious Dec 15, 2025
ebab035
feat: replace git2 with gix (#174)
philocalyst Feb 18, 2026
14cdf12
ci: replace release-plz with release-please (#176)
cococonscious Feb 20, 2026
2143b9f
ci(release-please): don't include component in tag (#178)
cococonscious Feb 20, 2026
2e766da
added scoping here
philocalyst Feb 21, 2026
5c01a20
feat: add force_scope and allow_empty_scope options
philocalyst Feb 28, 2026
ebdec6e
test: cleanup and add integration tests
philocalyst Feb 28, 2026
82f611d
deps: added thiserror
philocalyst Mar 11, 2026
06c162c
questions: added much better error handling, and prompt config diagno…
philocalyst Mar 11, 2026
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
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ authors = [
"Finley Thomalla <[email protected]>",
"Danny Tatom <[email protected]>",
]
description = "An interactive CLI for creating conventional commits."
documentation = "https://docs.rs/koji"
description = "An interactive CLI for creating conventional commits."
repository = "https://github.com/cococonscious/koji"
license = "MIT"

Expand All @@ -29,16 +29,17 @@ emojis = "0.8"
indexmap = "2.10"
serde = { version = "1.0", features = ["derive"] }
inquire = "0.9"
thiserror = "2"
clap_complete_command = { version = "0.6", features = ["nushell"]}
config = { version = "0.15", features = ["toml"] }
xdg = "3.0"
gix = "0.80.0"

[dev-dependencies]
git2 = "0.20.4"
assert_cmd = "2.0.16"
predicates = "3.1.2"
tempfile = "3.12.0"
git2 = "0.20.3"

[target.'cfg(not(windows))'.dev-dependencies]
rexpect = "0.6.2"
Expand Down
2 changes: 2 additions & 0 deletions meta/config/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ breaking_changes = true
issues = true
emoji = false
sign = false
force_scope = false
allow_empty_scope = true

[[commit_types]]
name = "feat"
Expand Down
1 change: 1 addition & 0 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ fn main() -> Result<()> {
sign,
_user_config_path: None,
_current_dir: Some(current_dir.clone()),
..Default::default()
}))?;

// Get answers from interactive prompt
Expand Down
70 changes: 70 additions & 0 deletions src/lib/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ pub struct Config {
pub autocomplete: bool,
pub breaking_changes: bool,
pub commit_types: IndexMap<String, CommitType>,
pub commit_scopes: IndexMap<String, CommitScope>,
pub emoji: bool,
pub issues: bool,
pub sign: bool,
pub force_scope: bool,
pub allow_empty_scope: bool,
pub workdir: PathBuf,
}

Expand All @@ -26,15 +29,25 @@ pub struct CommitType {
pub name: String,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
pub struct CommitScope {
pub name: String,
pub description: Option<String>,
}

#[derive(Clone, Debug, Deserialize)]
struct ConfigTOML {
pub autocomplete: bool,
pub breaking_changes: bool,
#[serde(default)]
commit_types: Vec<CommitType>,
#[serde(default)]
commit_scopes: Vec<CommitScope>,
pub emoji: bool,
pub issues: bool,
pub sign: bool,
pub force_scope: bool,
pub allow_empty_scope: bool,
}

#[derive(Default)]
Expand All @@ -45,6 +58,8 @@ pub struct ConfigArgs {
pub emoji: Option<bool>,
pub issues: Option<bool>,
pub sign: Option<bool>,
pub force_scope: Option<bool>,
pub allow_empty_scope: Option<bool>,
pub _user_config_path: Option<PathBuf>,
pub _current_dir: Option<PathBuf>,
}
Expand All @@ -59,6 +74,8 @@ impl Config {
emoji,
issues,
sign,
force_scope,
allow_empty_scope,
_user_config_path,
_current_dir,
} = args.unwrap_or_default();
Expand Down Expand Up @@ -101,13 +118,22 @@ impl Config {
commit_types.insert(commit_type.name.clone(), commit_type.to_owned());
}

// Gather up commit scopes
let mut commit_scopes = IndexMap::new();
for commit_scope in config.commit_scopes.iter() {
commit_scopes.insert(commit_scope.name.clone(), commit_scope.to_owned());
}

Ok(Config {
autocomplete: autocomplete.unwrap_or(config.autocomplete),
breaking_changes: breaking_changes.unwrap_or(config.breaking_changes),
commit_types,
commit_scopes,
emoji: emoji.unwrap_or(config.emoji),
issues: issues.unwrap_or(config.issues),
sign: sign.unwrap_or(config.sign),
force_scope: force_scope.unwrap_or(config.force_scope),
allow_empty_scope: allow_empty_scope.unwrap_or(config.allow_empty_scope),
workdir,
})
}
Expand Down Expand Up @@ -285,4 +311,48 @@ mod tests {

Ok(())
}

#[test]
fn test_commit_scopes() -> Result<(), Box<dyn Error>> {
let tempdir = tempfile::tempdir()?;
std::fs::write(
tempdir.path().join(".koji.toml"),
"[[commit_scopes]]\nname=\"app\"\ndescription=\"Application code\"",
)?;
let config = Config::new(Some(ConfigArgs {
_current_dir: Some(tempdir.path().to_path_buf()),
..Default::default()
}))?;
assert!(config.commit_scopes.get("app").is_some());
assert_eq!(
config.commit_scopes.get("app"),
Some(&CommitScope {
name: "app".into(),
description: Some("Application code".into())
})
);
tempdir.close()?;
Ok(())
}
#[test]
fn test_commit_scopes_from_config() -> Result<(), Box<dyn Error>> {
let tempdir_config = tempfile::tempdir()?;
std::fs::create_dir(tempdir_config.path().join("koji"))?;
std::fs::write(
tempdir_config.path().join("koji").join("config.toml"),
"[[commit_scopes]]\nname=\"server\"\ndescription=\"Server code\"\n[[commit_scopes]]\nname=\"shared\"",
)?;
let tempdir_current = tempfile::tempdir()?;
let config = Config::new(Some(ConfigArgs {
_user_config_path: Some(tempdir_config.path().to_path_buf()),
_current_dir: Some(tempdir_current.path().to_path_buf()),
..Default::default()
}))?;
assert!(config.commit_scopes.get("server").is_some());
assert!(config.commit_scopes.get("shared").is_some());
assert_eq!(config.commit_scopes.len(), 2);
tempdir_current.close()?;
tempdir_config.close()?;
Ok(())
}
}
Loading
Loading