Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- `upsert: <pattern>` + `with: <line>` — replaces if pattern matches, otherwise appends
- MFA required to publish this gem

### ⚠️ Behavior changes

- `.worktree.yml` in the linked worktree now drives **all** setup steps, not just the tail end. Previously `copy:` and `link:` always came from the main worktree's config while the rest came from the linked one if present — split that produced surprising results. Now: linked config wins entirely if present; main is the fallback. No effect unless you have a linked `.worktree.yml` whose `copy:`/`link:` differs from main's.

## 0.6.0

### ✨ Features
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ setup: mise exec -- bin/setup

`bonchi create` auto-runs setup when `.worktree.yml` exists. Skip with `--no-setup`.

If `.worktree.yml` exists in the linked worktree, it takes precedence over the main worktree's — useful for trying out config changes in a single worktree without touching main, or for committing a per-branch config. The main worktree's config is the fallback when no local config is present.

To pin every new worktree to its own copy of the config (full isolation, no propagation of later edits in main), add it to `copy:`:

```yaml
copy:
- .worktree.yml
```

Default is to leave it out, so edits to main's `.worktree.yml` automatically apply to every subsequent `bonchi setup` run.

### Edit

Use `edit` to modify files during setup. Three actions are available; entries run in order, so you can interleave them. Env vars (`$VAR`) are expanded in replacement values.
Expand Down
17 changes: 7 additions & 10 deletions lib/bonchi/setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ def run(args = [], upto: nil)
abort "#{color(:red)}Error:#{reset} already in the main worktree"
end

config = Config.from_main_worktree
abort "#{color(:red)}Error:#{reset} .worktree.yml not found in main worktree" unless config
config = Config.from_worktree(@worktree)
if config
puts "Using .worktree.yml from linked worktree"
else
config = Config.from_main_worktree
abort "#{color(:red)}Error:#{reset} .worktree.yml not found in main worktree" unless config
end

last_step = upto || STEPS.last
run_steps = STEPS[0..STEPS.index(last_step)]
Expand All @@ -37,14 +42,6 @@ def run(args = [], upto: nil)

copy_files(config.copy) if run_steps.include?("copy")
link_files(config.link) if run_steps.include?("link")

# Prefer linked worktree's .worktree.yml if it was copied or already exists
linked_config = Config.from_worktree(@worktree)
if linked_config
puts "Using .worktree.yml from linked worktree"
config = linked_config
end

allocate_ports(config.ports) if run_steps.include?("ports") && config.ports.any?
replace_in_files(config.replace) if run_steps.include?("replace") && config.replace.any?
run_pre_setup(config.pre_setup) if run_steps.include?("pre_setup")
Expand Down
81 changes: 81 additions & 0 deletions test/test_setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,84 @@ def test_missing_file_aborts
end
end
end

class TestSetupConfigSource < Minitest::Test
def setup
@tmpdir = Dir.mktmpdir
@main = File.join(@tmpdir, "main")
@linked = File.join(@tmpdir, "linked")
FileUtils.mkdir_p(@main)
FileUtils.mkdir_p(@linked)
@setup = Bonchi::Setup.allocate
@setup.instance_variable_set(:@worktree, @linked)
@setup.instance_variable_set(:@main_worktree, @main)
ENV["WORKTREE_ROOT"] = @tmpdir
end

def teardown
FileUtils.remove_entry(@tmpdir)
%w[WORKTREE_ROOT WORKTREE_BRANCH WORKTREE_BRANCH_SLUG WORKTREE_MAIN WORKTREE_LINKED].each { |k| ENV.delete(k) }
end

def write(dir, file, content)
path = File.join(dir, file)
FileUtils.mkdir_p(File.dirname(path))
File.write(path, content)
end

def silenced
out = $stdout
$stdout = StringIO.new
yield
ensure
$stdout = out
end

def run_setup(upto:)
Bonchi::Git.stub(:main_worktree, @main) do
Bonchi::Git.stub(:current_branch, "feat/x") do
silenced { @setup.run([], upto: upto) }
end
end
end

def test_linked_copy_takes_precedence_over_main
write(@main, "from-main.txt", "main\n")
write(@main, "from-linked.txt", "linked\n")
write(@main, ".worktree.yml", "copy:\n - from-main.txt\n")
write(@linked, ".worktree.yml", "copy:\n - from-linked.txt\n")

run_setup(upto: "copy")

refute File.exist?(File.join(@linked, "from-main.txt")), "main's copy entry should be ignored when linked config exists"
assert_equal "linked\n", File.read(File.join(@linked, "from-linked.txt"))
end

def test_linked_link_takes_precedence_over_main
write(@main, "from-main", "m\n")
write(@main, "from-linked", "l\n")
write(@main, ".worktree.yml", "link:\n - from-main\n")
write(@linked, ".worktree.yml", "link:\n - from-linked\n")

run_setup(upto: "link")

refute File.exist?(File.join(@linked, "from-main"))
assert File.symlink?(File.join(@linked, "from-linked"))
assert_equal File.join(@main, "from-linked"), File.readlink(File.join(@linked, "from-linked"))
end

def test_falls_back_to_main_when_linked_has_no_config
write(@main, "tool.toml", "m\n")
write(@main, ".worktree.yml", "copy:\n - tool.toml\n")

run_setup(upto: "copy")

assert_equal "m\n", File.read(File.join(@linked, "tool.toml"))
end

def test_aborts_when_neither_worktree_has_config
assert_raises(SystemExit) do
run_setup(upto: "copy")
end
end
end
Loading