ctree 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8abbf0956853273481c598fe775b0da106a06f6fdd9a49d54598bd16e08cf566
4
+ data.tar.gz: b663bf6b45e76e44fc67cf93a3d49a2b287c3f184a6bdbc9520a9ea757a81c5e
5
+ SHA512:
6
+ metadata.gz: 37489240879d544d297168677500092b26e57d473af18b36ecdbdcc7bc68fa4d14600c0957f037ec24912fccb33129e8e351a1fb5ae849625fd084171731c274
7
+ data.tar.gz: 87db5c5490d551df4abba9572f1568d2fa4fd06e2474274e7c32338445931665114e3c9fcd56b62bad4220d0a6179fdd66ce8d5cf476f6642dedd5a10619e210
data/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.5.3] - 2026-10-12
11
+
12
+ - Added gemspec
13
+ - Added file/volume size to log file
14
+ - Cleaned up the codebase
15
+
16
+ ## [0.5.2] - 2026-10-06
17
+
18
+ - Added default-branch detection
19
+ - Fixed flakey specs
20
+
21
+ ## [0.5.1] - 2026-10-01
22
+
23
+ - Improved rebase and update log messages
24
+ - Removed summary blocks from log file
25
+ - Fixed duplicate "copied ..." lines in log file
26
+ - Removed display of spinner for post rebase/update hooks
27
+ - Fixed sync exiting after rebase without running update
28
+ - Added errors from post rebase/update hooks in log file
29
+
30
+ ## [0.5.0] - 2026-09-25
31
+
32
+ - Removed double action lines from log file
33
+ - Removed the boundary marker from appended log files
34
+ - Removed DEBUG: prefix from log file entries
35
+ - Changed --log-file to append to an existing file instead of overwriting it
36
+ - Removed switch and shell-init commands
37
+
38
+ ## [0.4.0] - 2026-09-18
39
+
40
+ - Fixed spinner in free command log
41
+ - Removed confirmation prompt from free command
42
+ - Added log prefix support for domain command
43
+ - Added no-current option for list command
44
+ - Added sync command and post-rebase hooks
45
+ - Added CTREE_LOG_PREFIX env var
46
+ - Added log prefix config option
47
+
48
+ ## [0.3.0] - 2026-09-10
49
+
50
+ - Added support for equals syntax for --config and --log-file
51
+ - Added an option to redirect command output to a log file
52
+ - Removed interactive prompts from log in non-interactive mode
53
+ - Added an option to run commands non-interactively
54
+
55
+ ## [0.2.0] - 2026-08-20
56
+
57
+ - Prevented creation of duplicate volumes for shared volumes
58
+ - Changed how .ctree is created in the worktree (it is no longer copied from the source repo)
59
+ - Added an option to use a custom config file during worktree creation
60
+
61
+ ## [0.1.0] - 2026-07-11
62
+
63
+ Initial commit.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Martin Yosifov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,491 @@
1
+ # ctree
2
+
3
+ Replicate any Docker-compose based git repo as a sibling git worktree,
4
+ with its own branch, docker volumes, and a tailored `.env`, so that
5
+ multiple branches can run side by side without interfering with
6
+ each other's state.
7
+
8
+ ## Install
9
+
10
+ ### From RubyGems (recommended)
11
+
12
+ ```bash
13
+ gem install ctree
14
+ ```
15
+
16
+ This places the `ctree` binary on your `PATH` automatically. Verify:
17
+
18
+ ```bash
19
+ ctree list # sanity-check that the binary runs
20
+ ```
21
+
22
+ ### From source
23
+
24
+ If you want to use the latest unreleased code, clone the repository
25
+ and add its `bin` directory to your `PATH` so the `ctree` binary
26
+ is available in every terminal:
27
+
28
+ ```bash
29
+ cd your_repositories_directory
30
+ git clone https://github.com/mytrak/ctree.git
31
+ ```
32
+
33
+ Execute the command below to add an entry for ctree to your
34
+ **`~/.zshrc`** (zsh, the macOS default) or **`~/.bashrc`** (bash).
35
+ Don't add an entry to `~/.bash_profile` or `~/.zprofile` as those run
36
+ only for login shells, but `ctree` needs to be on `PATH` for every
37
+ interactive shell.
38
+
39
+ ```bash
40
+ cd ctree
41
+ echo "export PATH=\"$(pwd)/bin:\$PATH\"" >> ~/.zshrc
42
+ ```
43
+
44
+ Then reload the file (or just open a new terminal tab):
45
+
46
+ ```bash
47
+ source ~/.zshrc
48
+ ```
49
+
50
+ Verify:
51
+
52
+ ```bash
53
+ type ctree # should print: ctree is /path/to/ctree/bin/ctree
54
+ ctree list # sanity-check that the binary runs
55
+ ```
56
+
57
+ #### Troubleshooting
58
+
59
+ **`command not found: ctree` in a fresh terminal** — `.zshrc` either
60
+ isn't being read or it bailed early, or the exported `PATH` entry
61
+ doesn't point at the directory containing the `ctree` executable.
62
+ Check `echo $0` (should be `-zsh`), then confirm the `export PATH`
63
+ line is correct.
64
+
65
+ ## Usage
66
+
67
+ Most commands run from the source repository.
68
+ `update`, `rebase`, `sync`, `free`, `env`, and `compose-config` run from inside
69
+ a worktree.
70
+
71
+ ```bash
72
+ ctree create <worktree_name> <branch_name>
73
+ ctree delete <worktree_name>
74
+ ctree list [all | free | used]
75
+ ctree rebase
76
+ ctree update
77
+ ctree free
78
+ ctree env [list | check | fix]
79
+ ctree version
80
+ ctree domain [list | [add | delete] <tld>]
81
+ ctree config [list | add | delete]
82
+ ctree compose-config [list | check | fix]
83
+ ctree sync
84
+ ctree help <command>
85
+ ```
86
+
87
+ Any command that normally prompt for confirmation — `create`, `delete`,
88
+ `rebase`, `update`, `env fix` and `config add/delete` — accepts
89
+ a global `--force` flag that skips the prompts and assumes the default
90
+ answer shown in brackets. For prompts with no bracket default (i.e. ones
91
+ that ask you to type the literal word "yes"), `--force` assumes yes.
92
+ Use `ctree help <command>` for per-command specifics, including the cases
93
+ where the assumed default means *not* proceeding (e.g. `rebase` and
94
+ `update` against a dirty or off-branch source).
95
+
96
+ ### create
97
+
98
+ Creates a sibling worktree at `../<worktree_name>`. The entire source
99
+ directory — tracked files AND gitignored runtime content (node_modules,
100
+ build artifacts, plugin lock files, etc.) — is cloned into the new
101
+ worktree in one operation. On macOS APFS this is near-instant CoW via
102
+ `clonefile(2)`; on Linux, `cp --reflink=auto` provides the same for
103
+ btrfs/xfs and falls back to a plain copy on ext4.
104
+
105
+ After the cloning, docker volumes are replicated under the new compose
106
+ prefix and a tailored `.env` is written with `COMPOSE_PROJECT_NAME`.
107
+ For each constant defined in the source `.env` you will be prompted
108
+ to either accept or update the value for the worktree.
109
+
110
+ The optional `--config <path>` flag lets you create a worktree with a
111
+ custom configuration that differs from the repo's shared `.ctree/config.yml`.
112
+ The file is merged over the shipped defaults (the repo config layer is
113
+ skipped for this worktree) and persisted into the worktree's own
114
+ `.ctree/config.yml` so that later commands like `update`, `rebase`,
115
+ `free`, and `env` continue to use it automatically. The path is
116
+ relative to the current directory.
117
+
118
+ ### delete
119
+
120
+ Reverses `create`: lists everything that would be deleted, prompts
121
+ for explicit `yes` confirmation, then tears it all down — the worktree
122
+ directory, its `git worktree` registration, the per-project docker
123
+ volumes, and any running compose stack.
124
+
125
+ **Branches are always preserved**, including the auto-derived branch
126
+ that `ctree create <name>` creates when no explicit branch is passed.
127
+ This is deliberately safe: an auto-derived branch can still carry
128
+ commits you want to keep, push, or re-check-out in a fresh worktree
129
+ later, and ctree can't tell from the branch itself whether that's the
130
+ case.
131
+
132
+ ### list
133
+
134
+ Lists all worktrees for the current source repo. Each entry shows the
135
+ worktree path and its checked-out branch.
136
+
137
+ ```bash
138
+ ctree list # all worktrees
139
+ ctree list all # same as above
140
+ ctree list free # only worktrees on a free placeholder branch
141
+ ctree list used # only worktrees on a non-free branch
142
+ ```
143
+
144
+ Worktrees on a branch whose name starts with `free_branch_prefix`
145
+ (default `FREE-`, configurable in `.ctree/config.yml`) are annotated
146
+ with `(free)`. `ctree list free` limits output to those worktrees;
147
+ `ctree list used` shows the rest.
148
+
149
+ ### rebase
150
+
151
+ Run **from inside a worktree** (no arguments). Does two things in order:
152
+
153
+ 1. Rebases the worktree's current branch onto the source repo's `master`.
154
+ Skips automatically if `master` is already an ancestor (i.e. the
155
+ branch is already up to date). Aborts and reports conflicts if the
156
+ rebase cannot complete cleanly.
157
+ 2. Rebases each embedded git repo listed under `rebase` in
158
+ `.ctree/config.yml` (e.g. `gems/plugins`) from its source
159
+ counterpart.
160
+
161
+ All operations are local — no network calls are made. Update the source
162
+ repo first, then run `ctree rebase` from the worktree to catch it up.
163
+
164
+ ### update
165
+
166
+ Run **from inside a worktree** (no arguments). ctree resolves the source repo
167
+ from git's common dir, then:
168
+
169
+ 1. Re-tags the source's images to the worktree's project name, so the worktree
170
+ picks up images the source rebuilt (e.g. after rebuilding source images).
171
+ 2. Rsyncs allowlisted volumes (`update_volumes`) from source to worktree.
172
+ 3. Copies or syncs any paths listed in `update` from source to worktree (see
173
+ schema below).
174
+
175
+ Shared volumes (`share_volumes`, e.g. gems/node_modules) are mounted
176
+ from the source via the override file, so they are already live — update
177
+ does not copy them.
178
+
179
+ Before doing anything, ctree warns and asks for confirmation (default **no**)
180
+ if the source is on a non-default branch.
181
+
182
+ ### sync
183
+
184
+ Run **from inside a worktree** (no arguments). Convenience wrapper that runs
185
+ `rebase` and then `update` in sequence, so a single command brings the
186
+ worktree fully up to date with the source. All operations are local — no
187
+ network calls. Update the source repo first, then run `ctree sync` from the
188
+ worktree to catch it up.
189
+
190
+ ### free
191
+
192
+ Run **from inside a worktree** (no arguments). Resets the worktree to a free
193
+ placeholder branch so it can be reused. ctree:
194
+
195
+ 1. Lists all local branches whose names start with the configured
196
+ `free_branch_prefix` (default `FREE-`).
197
+ 2. Checks out the first available branch — one not currently checked out in
198
+ any worktree — sorted alphabetically.
199
+ 3. If all free branches are occupied, creates the next sequential one,
200
+ filling gaps in the numeric sequence. For example, if `FREE-001` and
201
+ `FREE-003` both exist and are checked out, it creates and checks out
202
+ `FREE-002`. If no free branches exist at all, it creates `FREE-001`.
203
+ Numbers are zero-padded to at least 3 digits (`FREE-001`, `FREE-002`, …).
204
+ 4. Rebases the checked-out branch onto the source repo's `master` (skipped
205
+ automatically if already up to date). Aborts and reports on conflict —
206
+ resolve manually and re-run `ctree rebase` from the worktree.
207
+
208
+ ### env
209
+
210
+ Run **from inside a worktree**. Manages the worktree's `.env` relative to
211
+ the source repo's `.env`.
212
+
213
+ ```bash
214
+ ctree env list # print current worktree .env
215
+ ctree env check # report discrepancies
216
+ ctree env fix # resolve discrepancies interactively
217
+ ```
218
+
219
+ Prints all `KEY=VALUE` pairs from the worktree `.env`. No comparison
220
+ with the source is performed.
221
+
222
+ #### check
223
+
224
+ Reports discrepancies between the source and worktree `.env` and exits 1
225
+ if any are found:
226
+
227
+ - `[missing] KEY` — key present in the source `.env` but absent from the
228
+ worktree. All missing keys are reported, including those in
229
+ `skip_env_keys`.
230
+ - `[extra] KEY` — key present in the worktree `.env` but absent from the
231
+ source. ctree-managed keys (`COMPOSE_PROJECT_NAME`, `HOST_NAME`,
232
+ `HOST_NAME_SUFFIX`, and `host_domain_env_key` when configured) and keys
233
+ in `skip_env_keys` are never reported as extra.
234
+
235
+ Exits 0 if the envs are in sync.
236
+
237
+ #### fix
238
+
239
+ Resolves discrepancies interactively, acting only on actual gaps:
240
+
241
+ - **Missing keys** — prompts with the source value as the default; press
242
+ enter to accept or type a new value.
243
+ - **Extra keys** — offers deletion (`[y/N]`); defaults to keeping the key.
244
+ - **Keys already present in both** — left untouched, no prompt shown.
245
+
246
+ ctree-managed keys and `skip_env_keys` are never offered for deletion.
247
+ Missing keys are always prompted regardless of `skip_env_keys`.
248
+
249
+ ### compose-config
250
+
251
+ Run **from inside a worktree**. Manages shared-volume external references
252
+ in the compose override file (configured via `compose_override_file` in
253
+ `.ctree/config.yml`).
254
+
255
+ ```bash
256
+ ctree compose-config list # show current state of the override file
257
+ ctree compose-config check # report missing or incorrect external references
258
+ ctree compose-config fix # write corrections automatically
259
+ ```
260
+
261
+ `fix` rewrites the override file to add correct `external: true` and
262
+ `name: <source_project>_<suffix>` entries for any `share_volumes` that
263
+ are missing or incorrect.
264
+
265
+ ### domain
266
+
267
+ **macOS only.** Configures local DNS resolution for a TLD so that all
268
+ `*.tld` names resolve to `127.0.0.1`.
269
+
270
+ ```bash
271
+ ctree domain list # show configured TLDs and resolver status
272
+ ctree domain add <tld> # add wildcard DNS rule for <tld>
273
+ ctree domain delete <tld> # remove wildcard DNS rule for <tld>
274
+ ```
275
+
276
+ `add <tld>` writes a wildcard dnsmasq rule (`address=/.tld/127.0.0.1`)
277
+ and creates a macOS resolver file at `/etc/resolver/<tld>` (requires
278
+ `sudo`). `delete <tld>` removes both. `list` shows which TLDs are
279
+ configured in dnsmasq and whether their `/etc/resolver/<tld>` files are
280
+ present.
281
+
282
+ ## Configuration
283
+
284
+ Which docker volumes ctree copies, syncs, or treats as source-shared caches
285
+ is controlled by config — not by hardcoded values in the executable. There
286
+ are two layers, with an optional third that replaces the per-repo layer for
287
+ a single worktree:
288
+
289
+ 1. **Shipped defaults** in `lib/ctree/config.yml` (loaded automatically).
290
+ 2. **Per-repo override** at `<source_repo>/.ctree/config.yml` (optional).
291
+ Keys present here replace the shipped values; keys you omit fall through
292
+ to the defaults.
293
+ 3. **`ctree create --config <path>`** (optional, per-worktree). Skips the
294
+ per-repo layer entirely for that worktree and merges the named file over
295
+ shipped defaults instead. The custom file is persisted into the
296
+ worktree's own `.ctree/config.yml` so that later commands (`update`,
297
+ `rebase`, `free`, `env`, `compose-config`) continue to use it.
298
+
299
+ ### Schema
300
+
301
+ ```yaml
302
+ # <source_repo>/.ctree/config.yml — all keys are optional
303
+
304
+ # Volumes to share between the source and every worktree.
305
+ # List the SUFFIX only — the part after the leading `<project>_`.
306
+ #
307
+ # Full volume name Suffix to list
308
+ # ------------------------ ---------------
309
+ # myapp_yarn-cache → yarn-cache
310
+ # myapp_app-gems app-gems
311
+ #
312
+ # ctree rewrites the worktree's override file to mount the source volume
313
+ # directly (external: true) rather than creating a new empty one.
314
+ # Example:
315
+ # share_volumes:
316
+ # - yarn-cache
317
+ # - app-gems
318
+
319
+ # Volumes that `ctree update` is allowed to rsync from source into the worktree.
320
+ # List the SUFFIX only — the part after the leading `<project>_`.
321
+ #
322
+ # Full volume name Suffix to list
323
+ # ----------------- ---------------
324
+ # myapp_code → code
325
+ # myapp_bundle → bundle
326
+ #
327
+ # This is an allowlist — anything not listed is left untouched, so new
328
+ # stateful volumes fail closed rather than being silently overwritten.
329
+ # Example:
330
+ # update_volumes:
331
+ # - code
332
+ # - bundle
333
+
334
+ # Volume name suffixes created empty in the worktree during `ctree create`
335
+ # instead of being rsynced from source. Use for transient data that should
336
+ # always start fresh (logs, tmp files, reports). These volumes are also
337
+ # silently skipped by `ctree update`, regardless of update_volumes.
338
+ # Suffix = everything after the leading `<project>_`.
339
+ # Example:
340
+ # empty_volumes:
341
+ # - log
342
+ # - tmp
343
+
344
+ # Repo-relative files or directories copied/synced from source to worktree
345
+ # during `ctree create` and `ctree update`. Use for gitignored artifacts
346
+ # that must match the source after a volume sync (e.g. lock files for gem
347
+ # volumes). Files are copied directly; directories are synced
348
+ # recursively (additions, modifications, and deletions).
349
+ # Example:
350
+ # update:
351
+ # - Gemfile.plugins.lock # a gitignored lock file
352
+
353
+ # Top-level names (files or directories) to skip when cloning the source
354
+ # working tree during `ctree create`. Matched by basename.
355
+ # Example:
356
+ # exclude:
357
+ # - node_modules
358
+
359
+ # Directories (relative to repo root) scanned for embedded git repos
360
+ # during `ctree rebase`. Subdirectories with a .git entry that are not
361
+ # submodules are rebased from their source counterpart.
362
+ # Example:
363
+ # rebase:
364
+ # - gems/plugins
365
+
366
+ # The TLD appended to the worktree name to form HOST_NAME_SUFFIX
367
+ # in the worktree .env. Leave empty to disable.
368
+ # Example:
369
+ # host_name_suffix: docker
370
+
371
+ # The docker-compose override file ctree appends to the worktree's
372
+ # COMPOSE_FILE and rewrites shared-volume entries in.
373
+ # Example:
374
+ # compose_override_file: docker-compose.local.ctree.yml
375
+
376
+ # Branch name prefix used to identify free (placeholder) worktrees.
377
+ # `ctree list free/used` and `ctree free` use this to detect whether a
378
+ # worktree is available.
379
+ # Example:
380
+ # free_branch_prefix: "FREE-"
381
+
382
+ # Keys in the source .env that ctree never prompts to delete from the
383
+ # worktree — even if absent from the source. Use for per-worktree keys
384
+ # that are intentionally different from the source.
385
+ # Example:
386
+ # skip_env_keys:
387
+ # - SOME_KEY
388
+ ```
389
+
390
+ ### How to configure
391
+
392
+ 1. Scaffold an annotated starting file by running from inside
393
+ your source repo:
394
+
395
+ ```bash
396
+ ctree config add
397
+ ```
398
+
399
+ This writes `<source_repo>/.ctree/config.yml` with the shipped defaults
400
+ verbatim (comments included). It refuses to overwrite an existing file
401
+ without confirmation, so it's safe to run if you're unsure. Run
402
+ `ctree config delete` to delete the file and its `.ctree` directory.
403
+
404
+ 2. Edit the keys you want to change, and **remove the keys you want to
405
+ leave at the shipped defaults** — anything you delete falls through to
406
+ `lib/ctree/config.yml`. Example: a project that shares gems and
407
+ node_modules and also syncs a lock file:
408
+
409
+ ```yaml
410
+ share_volumes:
411
+ - gems
412
+ - node_modules
413
+ update:
414
+ - Gemfile.plugins.lock
415
+ ```
416
+
417
+ 3. Commit `.ctree/config.yml` so collaborators on the same repo share the
418
+ configuration.
419
+
420
+ ### Behavior on errors
421
+
422
+ - **No `.ctree.yml`** → silent; uses shipped defaults.
423
+ - **Malformed YAML** or **non-mapping top level** → ctree warns to stderr
424
+ and falls back to shipped defaults. The command continues.
425
+ - **Wrong type for a known key** (e.g. a string where a list is expected)
426
+ → ctree exits with an error pointing at the offending key. Fix the file
427
+ and re-run.
428
+ - **`--config <path>` errors** — unlike the optional per-repo config,
429
+ errors in a custom config file are always fatal: a missing file, invalid
430
+ YAML, or wrong key type causes ctree to exit immediately with no partial
431
+ state left behind.
432
+
433
+ ## Traefik / reverse-proxy domain routing
434
+
435
+ ctree sets two `.env` variables to give each worktree a unique routable domain:
436
+
437
+ | Variable | Set by | Value |
438
+ |---|---|---|
439
+ | `HOST_NAME` | ctree (always) | worktree name, e.g. `my-feature` |
440
+ | `HOST_NAME_SUFFIX` | ctree (when `host_name_suffix` is configured) | TLD, e.g. `docker` |
441
+
442
+ Together they form `${HOST_NAME}.${HOST_NAME_SUFFIX}` — e.g. `my-feature.docker` — which
443
+ your compose files can use in Traefik `Host()` rules and domain env vars.
444
+
445
+ ### Per-repo setup
446
+
447
+ In your repo's `.ctree.yml`:
448
+
449
+ ```yaml
450
+ host_name_suffix: docker # written to HOST_NAME_SUFFIX in every worktree .env
451
+ ```
452
+
453
+ Create a local compose override file (e.g. `docker-compose.local.ctree.yml`) that
454
+ references these variables:
455
+
456
+ ```yaml
457
+ services:
458
+ web:
459
+ labels:
460
+ - "traefik.http.routers.web.rule=Host(`myapp.${HOST_NAME}.${HOST_NAME_SUFFIX}`)"
461
+ environment:
462
+ APP_DOMAIN: myapp.${HOST_NAME}.${HOST_NAME_SUFFIX}
463
+ ```
464
+
465
+ ### Wiring up the override
466
+
467
+ ctree automatically appends the configured override file to `COMPOSE_FILE` in the
468
+ worktree's `.env` when the file exists in the source repo. No manual step required.
469
+
470
+ The source repo's `.env` and domain are left untouched — the override is only active
471
+ in worktrees, never in the source.
472
+
473
+ ### How it works
474
+
475
+ - **Source repo:** `HOST_NAME` and `HOST_NAME_SUFFIX` are unset; existing routing is unchanged.
476
+ - **Worktrees:** ctree writes `HOST_NAME=<name>` and `HOST_NAME_SUFFIX=<tld>`; the override
477
+ file's `Host()` rules resolve to unique per-worktree domains.
478
+ - All worktrees and the source share the same Traefik network without conflicts because
479
+ each `Host()` rule resolves to a distinct value.
480
+
481
+ ## Development
482
+
483
+ ```bash
484
+ bundle install
485
+ bundle exec rspec # default suite (no docker required)
486
+ bundle exec rspec --tag docker # opt-in real-docker integration tests
487
+ ```
488
+
489
+ ## License
490
+
491
+ MIT — see [LICENSE](LICENSE).
data/bin/ctree ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
5
+ require "ctree"
6
+
7
+ Ctree::CLI.run(ARGV)