Skip to content

Add TOML config file support#1

Merged
mfornet merged 6 commits intomfornet:masterfrom
El3ssar:master
Mar 9, 2026
Merged

Add TOML config file support#1
mfornet merged 6 commits intomfornet:masterfrom
El3ssar:master

Conversation

@El3ssar
Copy link
Copy Markdown
Contributor

@El3ssar El3ssar commented Mar 7, 2026

Introduce a layered configuration system loaded from:

  1. ./.terris.toml (project-local, highest priority)
  2. ~/.terris/terris.toml (user-global, fallback)

New config keys and their defaults:

[worktrees]
base_dir = "~/.terris-worktrees" # supports ~ expansion
use_random_suffix = true
suffix_length = 8

[behavior]
on_missing_branch = "error" # comma-separated: error | fetch | create
auto_prune = false

[display]
show_head = false

The on_missing_branch field is the most significant addition: it controls what happens when the requested branch does not exist locally. Setting it to "fetch, create" will first run git fetch origin <branch> over the network, use the remote tracking ref if found, and fall back to creating a fresh local branch from HEAD if it is still not found.

Introduce a layered configuration system loaded from:
  1. ./.terris.toml  (project-local, highest priority)
  2. ~/.terris/terris.toml  (user-global, fallback)

New config keys and their defaults:

  [worktrees]
  base_dir          = "~/.terris-worktrees"  # supports ~ expansion
  use_random_suffix = true
  suffix_length     = 8

  [behavior]
  on_missing_branch = "error"   # comma-separated: error | fetch | create
  auto_prune        = false

  [display]
  show_head = false

The `on_missing_branch` field is the most significant addition: it
controls what happens when the requested branch does not exist locally.
Setting it to "fetch, create" will first run `git fetch origin <branch>`
over the network, use the remote tracking ref if found, and fall back
to creating a fresh local branch from HEAD if it is still not found.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds TOML-based configuration support to terris, allowing behavior and output to be customized via config files instead of only CLI defaults.

Changes:

  • Introduces TOML config structs + loader and threads config through cmd_list / cmd_ensure_branch.
  • Adds on_missing_branch strategy (fetch/create) and optional auto_prune + show_head display toggle.
  • Adds serde + toml dependencies and expands tests for new config behavior.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/main.rs Adds config model/loading, new branch-missing behavior, auto-prune option, and optional HEAD column in listing + tests.
Cargo.toml Adds serde (derive) and toml dependencies required for config parsing.
Cargo.lock Locks transitive dependencies introduced by serde/toml.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread src/main.rs Outdated
Comment thread src/main.rs
Comment on lines +137 to +145
fn load_config() -> Result<Config> {
for path in config_file_candidates()? {
if path.exists() {
let content = std::fs::read_to_string(&path)
.with_context(|| format!("read config '{}'", path.display()))?;
let config: Config = toml::from_str(&content)
.with_context(|| format!("parse config '{}'", path.display()))?;
return Ok(config);
}
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load_config() currently returns the first config file it finds, so if both global and project-local configs exist the global one is ignored entirely. This doesn't implement the layered/override behavior described in the PR (global fallback with local taking precedence per-key). Consider loading both (when present) and merging them (e.g., merge toml::Value tables with local overriding global) before deserializing into Config.

Copilot uses AI. Check for mistakes.
Comment thread src/main.rs
Comment on lines +109 to +119
for part in s.split(',') {
match part.trim() {
"error" | "" => {} // explicit "error" or empty token → no-op (default)
"fetch" => strategy.fetch = true,
"create" => strategy.create = true,
other => {
return Err(serde::de::Error::custom(format!(
"unknown on_missing_branch value '{other}'; valid values: error, fetch, create"
)));
}
}
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The on_missing_branch parsing treats the token error as a no-op and ignores the configured order entirely (it only sets boolean flags). This means values like "error, fetch" will still fetch, and "create, fetch" behaves the same as "fetch, create", which conflicts with the docs/PR description that actions are tried in order. Consider deserializing into an ordered list/enum of actions and executing them sequentially, and/or rejecting combinations that include error with other actions.

Copilot uses AI. Check for mistakes.
Comment thread src/main.rs Outdated
mfornet and others added 5 commits March 8, 2026 17:48
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Issue: load_config() returned the first config file found, so global and project configs were never layered. Also, project-local config lookup depended on current working directory instead of repository root.

Fix: load both files when present, merge TOML tables with project values overriding global values, and resolve project-local .terris.toml from git root (with cwd fallback outside repos). Added tests covering merge behavior and candidate path ordering.
Issue: on_missing_branch was parsed into booleans, so action order was ignored and combinations like 'error, fetch' behaved unexpectedly. In addition, any fetch failure was treated as 'branch not found', which could silently fall through to branch creation.

Fix: parse on_missing_branch into an ordered action list, reject invalid combinations/duplicates, execute actions sequentially, and only treat explicit missing-remote-ref errors as 'not found' while surfacing other fetch failures. Updated parsing tests accordingly.
Issue: suffix_length accepted invalid values (e.g. 0 or excessively large), which could generate malformed or unreasonable worktree paths.

Fix: added explicit suffix-length validation (1..=64), invoked validation during config loading and path construction, and added a unit test that asserts invalid values produce a clear configuration error.
Issue: multiple tests changed HOME concurrently, creating race conditions and intermittent failures when tests ran with default parallelism.

Fix: added a shared test-only mutex in EnvGuard so HOME updates are serialized across tests while still restoring prior environment values on drop.
@mfornet mfornet merged commit e2b0c41 into mfornet:master Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants