vinta-ai-workflows 0.7.0-alpha8 → 0.7.0
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.
- package/CHANGELOG.md +472 -595
- package/README.md +33 -19
- package/package.json +4 -1
- package/schemas/README.md +11 -3
- package/schemas/ai-models.v1.schema.json +5 -0
- package/schemas/interview-answers.v1.schema.json +122 -0
- package/schemas/interview-round.v1.schema.json +346 -0
- package/schemas/plan-review.v1.schema.json +342 -0
- package/schemas/postmortem.v1.schema.json +848 -3
- package/schemas/prs-context-comments.v1.schema.json +1 -1
- package/schemas/prs-context-frontmatter.v1.schema.json +18 -7
- package/schemas/vinta-ai-workflows-config.v1.schema.json +677 -18
- package/schemas/workflow.v1.schema.json +751 -26
- package/skills/vinta-analyze-codebase/SKILL.md +5 -0
- package/skills/vinta-bootstrap-ai-tools/SKILL.md +154 -31
- package/skills/vinta-bootstrap-ai-tools/resources/stacks/README.md +1 -1
- package/skills/vinta-bootstrap-ai-tools/resources/stacks/django/notes.md +36 -1
- package/skills/vinta-bootstrap-ai-tools/resources/worktree-command/README.md +85 -0
- package/skills/vinta-bootstrap-ai-tools/resources/worktree-command/lib-template.sh +160 -0
- package/skills/vinta-bootstrap-ai-tools/resources/worktree-command/prepare-template.sh +250 -0
- package/skills/vinta-bootstrap-ai-tools/resources/worktree-command/teardown-template.sh +96 -0
- package/skills/vinta-derive-skills/SKILL.md +23 -15
- package/skills/vinta-derive-skills/resources/dependency-license-block.md +8 -9
- package/skills/vinta-derive-skills/resources/foundation-skills/add-one-off-script/SKILL.md +1 -1
- package/skills/vinta-derive-skills/resources/foundation-skills/create-qa-use-cases/SKILL.md +3 -3
- package/skills/vinta-derive-skills/resources/foundation-skills/create-spec/SKILL.md +21 -2
- package/skills/vinta-derive-skills/resources/foundation-skills/handoff/SKILL.md +3 -3
- package/skills/vinta-derive-skills/resources/foundation-skills/interview-ui/SKILL.md +121 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/interview-ui/resources/examples/docs/D4-concurrency.md +28 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/interview-ui/resources/examples/round-01.json +60 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/open-pr-from-context/SKILL.md +1 -1
- package/skills/vinta-derive-skills/resources/foundation-skills/open-pr-from-context/scripts/open-pr.sh +34 -7
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +363 -142
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/resources/ai-models.yaml +19 -13
- package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md +39 -21
- package/skills/vinta-derive-skills/resources/foundation-skills/thermo-nuclear-code-quality-review/SKILL.md +8 -6
- package/skills/vinta-derive-skills/resources/foundation-skills/thermo-nuclear-review-loop/SKILL.md +181 -0
- package/skills/vinta-derive-skills/resources/plan-execution/README.md +40 -7
- package/skills/vinta-derive-skills/resources/plan-execution/partials/agent-models.md +14 -35
- package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/stacked.md +21 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/dispatched-agent.md +31 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/implementer-prompt.md +67 -6
- package/skills/vinta-derive-skills/resources/plan-execution/partials/maestro-live-run.md +44 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/model-pick.md +9 -3
- package/skills/vinta-derive-skills/resources/plan-execution/partials/parallel-lanes.md +95 -50
- package/skills/vinta-derive-skills/resources/plan-execution/partials/pr-context.md +4 -2
- package/skills/vinta-derive-skills/resources/plan-execution/partials/relay-questions.md +13 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/review-loop.md +81 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/worktree-seam.md +86 -17
- package/skills/vinta-derive-skills/resources/plan-execution/shell/amend-plan-template.md +35 -16
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md +12 -1
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +49 -33
- package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md +2 -1
- package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +19 -27
- package/skills/vinta-derive-skills/resources/prs-context-template.md +5 -4
- package/skills/vinta-derive-skills/resources/systematic-debugging-mcp-tools.md +1 -1
- package/skills/vinta-derive-skills/resources/systematic-debugging-template.md +8 -8
- package/skills/vinta-derive-skills/resources/write-unit-test-packs/stacks/django.md +4 -1
- package/skills/vinta-derive-skills/resources/write-unit-test-template.md +3 -0
- package/skills/vinta-derive-subagents/SKILL.md +25 -9
- package/skills/vinta-install-ai-tools-setup/SKILL.md +29 -2
- package/skills/vinta-migrate-plans-specs/SKILL.md +7 -7
- package/skills/vinta-sync-ai-tools/SKILL.md +19 -2
- package/skills/vinta-update-project-skills/SKILL.md +1 -0
- package/skills/vinta-write-agents-md/SKILL.md +10 -6
- package/skills/vinta-write-agents-md/resources/asking-the-human.md +56 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/review-layers.md +0 -101
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Generating the project's worktree command
|
|
2
|
+
|
|
3
|
+
How [vinta-bootstrap-ai-tools](../../SKILL.md) writes a `prepare.sh` / `teardown.sh` pair into a target project. Once written, the pair is the project's `commands.worktree_prepare` / `commands.worktree_teardown`, and `implement-plan` runs it instead of the `prepare-worktree` skill. It runs only when the user answered **`Yes — generate a provisioning script for this project`** to the bootstrap's `prepare-worktree` question.
|
|
4
|
+
|
|
5
|
+
The point is that provisioning becomes deterministic. The decisions the `prepare-worktree` skill makes on every run (what to copy, what to fork, how to isolate compose) are made **once**, here, with the user, and frozen into a script the team owns, reviews and edits.
|
|
6
|
+
|
|
7
|
+
## What ships
|
|
8
|
+
|
|
9
|
+
| Template | Becomes | Contents |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| [lib-template.sh](lib-template.sh) | `<dir>/lib.sh` | Resolves the worktree from the `VINTA_WORKTREE_*` environment (or a name given on the command line), plus helpers: `run` (honors `--dry-run`), `clone_dir`, `copy_file`, `set_env`, `forked_db_name`, `require_local_url`, `require_forked_name`, and YAML quoting. |
|
|
12
|
+
| [prepare-template.sh](prepare-template.sh) | `<dir>/prepare.sh` | Sanity checks, `git worktree add`, the **PROJECT STEPS** regions, generic compose isolation, the summary YAML, and the report. On failure it removes the half-made worktree. |
|
|
13
|
+
| [teardown-template.sh](teardown-template.sh) | `<dir>/teardown.sh` | Refuses a dirty worktree without `--force`, runs the **PROJECT STEPS** regions in reverse, stops compose (never `down -v`), removes the worktree, its branch when that branch holds no commits of its own, and the summary. |
|
|
14
|
+
| [gen-compose-worktree-override.sh](../../../vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/gen-compose-worktree-override.sh) | `<dir>/gen-compose-worktree-override.sh` | Copied **only when the project uses docker compose**. `prepare.sh` calls it to fork leaky volumes and strip fixed host ports. |
|
|
15
|
+
|
|
16
|
+
Everything outside the PROJECT STEPS regions is contract plumbing. It is tested in this repo (`tests/worktree-command.test.mjs`), so leave it alone. The regions are where the project lives.
|
|
17
|
+
|
|
18
|
+
## Steps
|
|
19
|
+
|
|
20
|
+
### 1. Location
|
|
21
|
+
|
|
22
|
+
The directory was chosen in the bootstrap interview (the `prepare-worktree` generate follow-up) and is already in `.vinta-ai-workflows.yaml` as the parent of `commands.worktree_prepare`. When this step runs standalone and that key is unset, ask now (header `Script dir`): `scripts/worktree (Recommended)`, `bin/worktree`, plus any existing scripts directory the inventory found. Free text covers other paths.
|
|
23
|
+
|
|
24
|
+
**Read before write.** If `prepare.sh`, `teardown.sh` or `lib.sh` already exists there, show the first lines of each and ask (header `Overwrite?`): `Pick another directory (Recommended)`, `Overwrite them`, `Stop — keep my scripts and use them as the command`. The last option records the existing files as `commands.worktree_prepare` / `commands.worktree_teardown` and skips generation.
|
|
25
|
+
|
|
26
|
+
### 2. Render
|
|
27
|
+
|
|
28
|
+
Copy the three templates (and the compose generator, when compose is in use) and substitute:
|
|
29
|
+
|
|
30
|
+
| Placeholder | Value |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `{{WORKTREE_ROOT}}` | `skills.prepare-worktree.worktree_root` (`.claude/worktrees` or `../<repo>-wt-`) |
|
|
33
|
+
| `{{DEFAULT_BRANCH}}` | `project.default_branch` |
|
|
34
|
+
| `{{SUMMARY_DIR}}` | `skills.prepare-worktree.summary_dir` (default `.vinta-ai-workflows/worktrees`) |
|
|
35
|
+
| `{{PROJECT_NAME}}` | `project.name` |
|
|
36
|
+
| `{{SCRIPT_DIR}}` | the chosen directory, repo-relative |
|
|
37
|
+
|
|
38
|
+
Then `chmod +x` all of them. No `{{…}}` may survive in the written files.
|
|
39
|
+
|
|
40
|
+
### 3. Fill the PROJECT STEPS regions
|
|
41
|
+
|
|
42
|
+
Each region in `prepare.sh` sits between `# >>> <region>` and `# <<< <region>`, with commented examples. Replace the examples with this project's real commands, and set the state variables at the top of `prepare.sh` that the summary reads. Then write the **matching** inverse region in `teardown.sh`. A prepare step whose teardown is missing leaks state on every lane recycle.
|
|
43
|
+
|
|
44
|
+
The decisions are the same ones the `prepare-worktree` skill makes. Read [its body](../../../vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md) for the reasoning; the short version for each region:
|
|
45
|
+
|
|
46
|
+
| Region | Decide from | Rules that stay non-negotiable |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `deps` | inventory package managers + lockfiles | Every dependency dir is the worktree's own: `clone_dir` it, or reinstall. **Never symlink.** Virtualenvs and yarn PnP always reinstall, because they store absolute paths into main. In a workspace, clone every member's `node_modules`, not only the root's. Set `DEPS_STRATEGY` / `DEPS_PATHS`. |
|
|
49
|
+
| `env` | inventory env model (`.env*`, `.envrc`) | `copy_file`, never symlink, because later regions and compose isolation append to it. Set `ENV_FILES`. Never print an env file's contents. |
|
|
50
|
+
| `dev_db` | inventory DB engine + how it is delivered | Fork with `forked_db_name`, and call `require_local_url` first, every time. A compose-delivered DB is forked by compose isolation (its volume), so the region only points the env at it. Set `DEV_DB_*`, including `DEV_DB_RESET`: a lane with no reset command cannot be reused across a migration. Add compose DB service names to `LOCAL_DB_HOSTS` in `lib.sh`. |
|
|
51
|
+
| `test_db` | inventory test runner | Give the runner its own DB name through the env channel it already reads. Never edit tracked test config. Set `TEST_DB_*` and `TEST_DB_RESET`. |
|
|
52
|
+
| `compose` | a compose file in the repo | `USES_COMPOSE=1`. List in `SHARED_VOLUMES` only read-only caches the user confirms, **never** a data volume. Step 4 of `prepare.sh` does the rest generically. |
|
|
53
|
+
| `other` | Redis / S3 / queues / search / cron / tunnels in the env model | Per-worktree Redis index or key prefix, `S3_PREFIX=wt-$NAME/`, queue and index suffixes, cron off. Run migrations against the forked DB last. |
|
|
54
|
+
|
|
55
|
+
Ask the user only about what the inventory cannot settle, through `AskUserQuestion` with the candidates found: which DBs to fork vs stub, which compose volumes are safe to share, whether cron must stay on. Don't ask about anything the table above already decides.
|
|
56
|
+
|
|
57
|
+
Teardown regions drop only what `prepare.sh` created: call `require_forked_name` before every `dropdb` / `DROP DATABASE`, and make every step tolerate state that is already gone.
|
|
58
|
+
|
|
59
|
+
### 4. Verify
|
|
60
|
+
|
|
61
|
+
1. `bash -n` on all three files. Also run `shellcheck` when it is installed.
|
|
62
|
+
2. Dry run, and show the user the output:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
<dir>/prepare.sh vinta-smoke --dry-run
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
3. Ask (header `Smoke test`): `Dry run is enough (Recommended)`, `Run a real smoke test now`, `Skip`. The real smoke test creates a worktree named `vinta-smoke-<yyyymmdd>`, really forking the local databases the script forks. It then runs the project's lint command inside the worktree and tears it down. When it runs:
|
|
69
|
+
- `prepare.sh` exits 0, and the three conductor checks from the `commands.worktree_prepare` contract hold: the branch, the registered worktree, and main's `git status` unchanged.
|
|
70
|
+
- The summary YAML parses.
|
|
71
|
+
- `teardown.sh` exits 0, and `git worktree list` no longer shows it.
|
|
72
|
+
|
|
73
|
+
On failure, show the failing step's output, run `teardown.sh` with the same name, and ask (header `Fix`): `Let me fix the region and retry (Recommended)`, `Keep the scripts as they are — I'll fix them later`, `Drop the scripts — use the prepare-worktree skill instead`. The last option deletes the generated files, and flips `foundation_skills.prepare-worktree` to `enabled` and the two `commands.worktree_*` keys off.
|
|
74
|
+
|
|
75
|
+
### 5. Record
|
|
76
|
+
|
|
77
|
+
Set `commands.worktree_prepare: <dir>/prepare.sh` and `commands.worktree_teardown: <dir>/teardown.sh` in `.vinta-ai-workflows.yaml` (Step 0.5 already wrote them from the chosen directory; confirm they match). Tell the user the scripts are theirs to commit and edit, and that `teardown.sh <name>` is also how a human removes a worktree they made by hand with `prepare.sh <name>`.
|
|
78
|
+
|
|
79
|
+
## Pitfalls
|
|
80
|
+
|
|
81
|
+
- **Hard-coding the main checkout's path, a branch or a DB name.** Everything per-worktree derives from `$NAME` through `forked_db_name` / `COMPOSE_PROJECT`, so lanes never collide.
|
|
82
|
+
- **Writing outside `$WT_PATH`.** The conductor fails provisioning when main's `git status` changes. The only out-of-tree writes allowed are forked databases, compose volumes, and files under the summary dir.
|
|
83
|
+
- **Forgetting `run`.** A bare command executes under `--dry-run` too. Wrap pipes and redirections as `run sh -c '…'`.
|
|
84
|
+
- **A partial summary.** `prepare.sh` writes every key. When you add state, add it to the variables at the top rather than appending keys to the heredoc.
|
|
85
|
+
- **Logging secrets.** Connection strings carry passwords. `require_local_url` never prints the URL; keep it that way in any message you add.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared plumbing for prepare.sh and teardown.sh: resolves the worktree's
|
|
3
|
+
# identity from the VINTA_WORKTREE_* environment (or from a name given on the
|
|
4
|
+
# command line), and provides the small helpers both scripts use. Sourced, never
|
|
5
|
+
# run. Generated by the vinta-ai-workflows bootstrap; owned by this project now.
|
|
6
|
+
#
|
|
7
|
+
# Bash 3.2 compatible (the macOS system bash): no associative arrays, no
|
|
8
|
+
# ${var,,}, no mapfile.
|
|
9
|
+
|
|
10
|
+
DRY_RUN="${VINTA_WORKTREE_DRY_RUN:-0}"
|
|
11
|
+
FORCE="${VINTA_WORKTREE_FORCE:-0}"
|
|
12
|
+
name_arg=""
|
|
13
|
+
for arg in "$@"; do
|
|
14
|
+
case "$arg" in
|
|
15
|
+
--dry-run) DRY_RUN=1 ;;
|
|
16
|
+
--force) FORCE=1 ;;
|
|
17
|
+
-h|--help) sed -n '2,/^set -euo/p' "$0" | sed '$d'; exit 0 ;;
|
|
18
|
+
-*) echo "unknown option: $arg" >&2; exit 2 ;;
|
|
19
|
+
*) name_arg="$arg" ;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
# Resolves symlinks in the deepest part of a path that exists. `git worktree
|
|
24
|
+
# list` prints resolved paths, so every comparison against it needs this (on
|
|
25
|
+
# macOS /var and /tmp are symlinks into /private).
|
|
26
|
+
canon_path() {
|
|
27
|
+
local p="$1" rest=""
|
|
28
|
+
while [ ! -d "$p" ]; do rest="/$(basename "$p")$rest"; p="$(dirname "$p")"; done
|
|
29
|
+
printf '%s%s' "$(cd "$p" && pwd -P)" "$rest"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
MAIN="$(canon_path "${VINTA_MAIN_CHECKOUT:-$(git rev-parse --show-toplevel)}")"
|
|
33
|
+
NAME="${VINTA_WORKTREE_NAME:-$name_arg}"
|
|
34
|
+
if [ -z "$NAME" ]; then
|
|
35
|
+
echo "usage: $0 <worktree-name> [--dry-run] (or set VINTA_WORKTREE_NAME)" >&2
|
|
36
|
+
exit 2
|
|
37
|
+
fi
|
|
38
|
+
case "$NAME" in
|
|
39
|
+
*[!A-Za-z0-9._-]*) echo "worktree name may only use [A-Za-z0-9._-]: $NAME" >&2; exit 2 ;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
# Project defaults, baked in at generation time. implement-plan always passes
|
|
43
|
+
# the VINTA_WORKTREE_* values explicitly; these only serve a human running the
|
|
44
|
+
# script by hand.
|
|
45
|
+
WORKTREE_ROOT="{{WORKTREE_ROOT}}"
|
|
46
|
+
DEFAULT_BASE_REF="origin/{{DEFAULT_BRANCH}}"
|
|
47
|
+
SUMMARY_DIR="{{SUMMARY_DIR}}"
|
|
48
|
+
|
|
49
|
+
case "$WORKTREE_ROOT" in /*) root="$WORKTREE_ROOT" ;; *) root="$MAIN/$WORKTREE_ROOT" ;; esac
|
|
50
|
+
# A root ending in "-" is the `../<repo>-wt-` sibling convention: no slash.
|
|
51
|
+
case "$root" in *-) default_path="$root$NAME" ;; *) default_path="$root/$NAME" ;; esac
|
|
52
|
+
|
|
53
|
+
WT_PATH="$(canon_path "${VINTA_WORKTREE_PATH:-$default_path}")"
|
|
54
|
+
BRANCH="${VINTA_WORKTREE_BRANCH:-$NAME}"
|
|
55
|
+
BASE_REF="${VINTA_WORKTREE_BASE_REF:-$DEFAULT_BASE_REF}"
|
|
56
|
+
KIND="${VINTA_WORKTREE_KIND:-single}"
|
|
57
|
+
PLAN_PATH="${VINTA_PLAN_PATH:-}"
|
|
58
|
+
SUMMARY="${VINTA_WORKTREE_SUMMARY:-$MAIN/$SUMMARY_DIR/$NAME.yaml}"
|
|
59
|
+
|
|
60
|
+
# Names derived from the worktree name. Every forked database, compose project,
|
|
61
|
+
# volume and cache key comes from these, so prepare.sh and teardown.sh always
|
|
62
|
+
# agree on what belongs to this worktree.
|
|
63
|
+
REPO_SLUG="$(basename "$MAIN" | tr -c 'A-Za-z0-9_\n' '_' | tr 'A-Z' 'a-z')"
|
|
64
|
+
WT_SLUG="$(printf '%s' "$NAME" | tr -c 'A-Za-z0-9_' '_' | tr 'A-Z' 'a-z')"
|
|
65
|
+
COMPOSE_PROJECT="${REPO_SLUG}_wt_${WT_SLUG}"
|
|
66
|
+
COMPOSE_OVERRIDE="$(dirname "$SUMMARY")/$NAME.docker-compose.override.yml"
|
|
67
|
+
|
|
68
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
69
|
+
|
|
70
|
+
log() { printf '[%s %s] %s\n' "$(basename "$0" .sh)" "$NAME" "$*" >&2; }
|
|
71
|
+
|
|
72
|
+
# Runs a command, or prints it under --dry-run. For pipes or redirections, wrap
|
|
73
|
+
# them: run sh -c '...'.
|
|
74
|
+
run() {
|
|
75
|
+
if [ "$DRY_RUN" = 1 ]; then
|
|
76
|
+
printf '+ %s\n' "$*" >&2
|
|
77
|
+
else
|
|
78
|
+
"$@"
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# The forked name for one of main's databases, e.g. app_dev -> app_dev_wt_lane_1.
|
|
83
|
+
forked_db_name() { printf '%s_wt_%s' "$1" "$WT_SLUG"; }
|
|
84
|
+
|
|
85
|
+
# Hosts a database URL may point at for this script to fork or drop it. Add the
|
|
86
|
+
# project's compose service names (e.g. "db") when the DB runs in compose.
|
|
87
|
+
LOCAL_DB_HOSTS="localhost 127.0.0.1 ::1"
|
|
88
|
+
|
|
89
|
+
# Refuses anything but a local database. Forking or dropping is only ever done
|
|
90
|
+
# against a developer's own server, never a shared or remote one. Never prints
|
|
91
|
+
# the URL: it can carry a password.
|
|
92
|
+
require_local_url() {
|
|
93
|
+
local rest host allowed
|
|
94
|
+
case "$1" in sqlite:*|file:*) return 0 ;; esac
|
|
95
|
+
rest="${1#*://}"
|
|
96
|
+
rest="${rest##*@}"
|
|
97
|
+
case "$rest" in
|
|
98
|
+
\[*) host="${rest#\[}"; host="${host%%\]*}" ;;
|
|
99
|
+
*) host="${rest%%[:/?]*}" ;;
|
|
100
|
+
esac
|
|
101
|
+
for allowed in $LOCAL_DB_HOSTS; do
|
|
102
|
+
[ "$host" = "$allowed" ] && return 0
|
|
103
|
+
done
|
|
104
|
+
echo "refusing to fork or drop a database on non-local host '$host' (allowed: $LOCAL_DB_HOSTS)" >&2
|
|
105
|
+
exit 1
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Refuses to drop a database this worktree did not create.
|
|
109
|
+
require_forked_name() {
|
|
110
|
+
case "$1" in
|
|
111
|
+
*_wt_"$WT_SLUG") return 0 ;;
|
|
112
|
+
esac
|
|
113
|
+
echo "refusing to drop '$1': it is not a '*_wt_$WT_SLUG' database" >&2
|
|
114
|
+
exit 1
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Copy-on-write clone of a directory from main into the worktree: APFS clonefile
|
|
118
|
+
# on macOS, reflink on btrfs / XFS, plain copy elsewhere. Links inside the tree
|
|
119
|
+
# stay links, which keeps pnpm's relative-link store valid.
|
|
120
|
+
clone_dir() {
|
|
121
|
+
[ -e "$MAIN/$1" ] || { log "skip $1 (absent in main)"; return 0; }
|
|
122
|
+
run mkdir -p "$(dirname "$WT_PATH/$1")"
|
|
123
|
+
if [ "$(uname)" = Darwin ]; then
|
|
124
|
+
run cp -ac "$MAIN/$1" "$WT_PATH/$1"
|
|
125
|
+
else
|
|
126
|
+
run cp -a --reflink=auto "$MAIN/$1" "$WT_PATH/$1"
|
|
127
|
+
fi
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
copy_file() {
|
|
131
|
+
[ -e "$MAIN/$1" ] || { log "skip $1 (absent in main)"; return 0; }
|
|
132
|
+
run mkdir -p "$(dirname "$WT_PATH/$1")"
|
|
133
|
+
run cp -p "$MAIN/$1" "$WT_PATH/$1"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Sets KEY=VALUE in a worktree env file, replacing an existing line for KEY.
|
|
137
|
+
set_env() {
|
|
138
|
+
local file="$WT_PATH/$1" key="$2" value="$3"
|
|
139
|
+
if [ "$DRY_RUN" = 1 ]; then printf '+ set %s in %s\n' "$key" "$1" >&2; return 0; fi
|
|
140
|
+
touch "$file"
|
|
141
|
+
grep -v "^$key=" "$file" > "$file.tmp" || true
|
|
142
|
+
printf '%s=%s\n' "$key" "$value" >> "$file.tmp"
|
|
143
|
+
mv "$file.tmp" "$file"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# YAML scalar: null for empty, single-quoted otherwise.
|
|
147
|
+
yaml_str() {
|
|
148
|
+
if [ -z "$1" ] || [ "$1" = null ]; then
|
|
149
|
+
printf 'null'
|
|
150
|
+
else
|
|
151
|
+
printf "'%s'" "$(printf '%s' "$1" | sed "s/'/''/g")"
|
|
152
|
+
fi
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# YAML flow list from space-separated words.
|
|
156
|
+
yaml_list() {
|
|
157
|
+
local out="" sep="" item
|
|
158
|
+
for item in $1; do out="$out$sep$(yaml_str "$item")"; sep=", "; done
|
|
159
|
+
printf '[%s]' "$out"
|
|
160
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Provision a runnable git worktree for {{PROJECT_NAME}}.
|
|
3
|
+
#
|
|
4
|
+
# implement-plan runs this as `commands.worktree_prepare`, with the
|
|
5
|
+
# VINTA_WORKTREE_* environment set. You can also run it by hand:
|
|
6
|
+
#
|
|
7
|
+
# {{SCRIPT_DIR}}/prepare.sh <worktree-name> [--dry-run]
|
|
8
|
+
#
|
|
9
|
+
# --dry-run (or VINTA_WORKTREE_DRY_RUN=1) prints every command instead of
|
|
10
|
+
# running it, and writes nothing.
|
|
11
|
+
#
|
|
12
|
+
# What it guarantees (the commands.worktree_prepare contract):
|
|
13
|
+
# - a worktree at $VINTA_WORKTREE_PATH, on the new branch
|
|
14
|
+
# $VINTA_WORKTREE_BRANCH, based on $VINTA_WORKTREE_BASE_REF;
|
|
15
|
+
# - runnable on its own: dependencies, env files, databases and compose
|
|
16
|
+
# project are the worktree's, never shared writable state with main;
|
|
17
|
+
# - a non-zero exit on any failure, and never a write to main's tracked files;
|
|
18
|
+
# - running again for the same name works once teardown.sh has run.
|
|
19
|
+
#
|
|
20
|
+
# Generated by the vinta-ai-workflows bootstrap. This project owns it now: edit
|
|
21
|
+
# the PROJECT STEPS regions freely, and keep teardown.sh in step with them.
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
. "$(cd "$(dirname "$0")" && pwd)/lib.sh" "$@"
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# What this worktree got. The PROJECT STEPS below set these; the summary at the
|
|
27
|
+
# end records them so teardown and implement-plan's lane reset can read them.
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
DEPS_STRATEGY=copy # copy | reinstall
|
|
30
|
+
DEPS_PATHS="" # space-separated, relative to the worktree
|
|
31
|
+
ENV_STRATEGY=copy # copy | symlink
|
|
32
|
+
ENV_FILES=""
|
|
33
|
+
DEV_DB_ENGINE="" # postgres | mysql | sqlite | mongo | redis; empty = no dev DB
|
|
34
|
+
DEV_DB_STRATEGY=fork # fork | share | stub
|
|
35
|
+
DEV_DB_FORKED=""
|
|
36
|
+
DEV_DB_URL_VAR=""
|
|
37
|
+
DEV_DB_RESET="" # command that returns the dev DB to a fresh state
|
|
38
|
+
TEST_DB_ENGINE="" # empty = no test DB
|
|
39
|
+
TEST_DB_STRATEGY=fork # fork | share
|
|
40
|
+
TEST_DB_FORKED=""
|
|
41
|
+
TEST_DB_URL_VAR=""
|
|
42
|
+
TEST_DB_RESET=""
|
|
43
|
+
USES_COMPOSE=0 # 1 when the project runs services through docker compose
|
|
44
|
+
COMPOSE_NETWORK=per-worktree
|
|
45
|
+
SHARED_VOLUMES="" # compose volume keys safe to share: read-only caches, never data
|
|
46
|
+
REDIS_DB_INDEX=""
|
|
47
|
+
S3_PREFIX=""
|
|
48
|
+
CRON_DISABLED=false
|
|
49
|
+
NOTES=""
|
|
50
|
+
|
|
51
|
+
created_worktree=0
|
|
52
|
+
on_exit() {
|
|
53
|
+
local status=$?
|
|
54
|
+
[ "$status" -eq 0 ] && return 0
|
|
55
|
+
[ "$DRY_RUN" = 1 ] && return 0
|
|
56
|
+
log "provisioning failed (exit $status)"
|
|
57
|
+
if [ "$created_worktree" = 1 ]; then
|
|
58
|
+
log "removing the half-made worktree at $WT_PATH"
|
|
59
|
+
git -C "$MAIN" worktree remove --force "$WT_PATH" >/dev/null 2>&1 || true
|
|
60
|
+
git -C "$MAIN" branch -D "$BRANCH" >/dev/null 2>&1 || true
|
|
61
|
+
fi
|
|
62
|
+
log "databases or volumes created before the failure are removed by teardown.sh, run with the same VINTA_WORKTREE_* environment"
|
|
63
|
+
}
|
|
64
|
+
trap on_exit EXIT
|
|
65
|
+
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
# 1. Sanity
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
if git -C "$MAIN" worktree list --porcelain | grep -Fxq "worktree $WT_PATH"; then
|
|
70
|
+
echo "a worktree is already registered at $WT_PATH; run teardown.sh first" >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
if [ -e "$WT_PATH" ]; then
|
|
74
|
+
echo "$WT_PATH already exists and is not a registered worktree; move it aside first" >&2
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
if git -C "$MAIN" show-ref --verify --quiet "refs/heads/$BRANCH"; then
|
|
78
|
+
echo "branch $BRANCH already exists; pick another name or delete it" >&2
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
# 2. Create the worktree
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
log "creating $WT_PATH on $BRANCH from $BASE_REF"
|
|
86
|
+
case "$BASE_REF" in
|
|
87
|
+
origin/*) run git -C "$MAIN" fetch --quiet origin || log "fetch failed; using the local $BASE_REF" ;;
|
|
88
|
+
esac
|
|
89
|
+
run mkdir -p "$(dirname "$WT_PATH")"
|
|
90
|
+
run git -C "$MAIN" worktree add --quiet -b "$BRANCH" "$WT_PATH" "$BASE_REF"
|
|
91
|
+
created_worktree=1
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# 3. PROJECT STEPS. Each region is filled in for this project at generation
|
|
95
|
+
# time. Write only inside "$WT_PATH", to databases named by
|
|
96
|
+
# `forked_db_name`, or under the summary dir. Use `run` for every command so
|
|
97
|
+
# --dry-run stays honest.
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
# >>> deps: every dependency dir is the worktree's own; never a symlink to main.
|
|
101
|
+
# clone_dir node_modules # copy-on-write; also each workspace member's
|
|
102
|
+
# (cd "$WT_PATH" && run uv sync) # virtualenvs always reinstall
|
|
103
|
+
# DEPS_PATHS="node_modules"
|
|
104
|
+
# <<< deps
|
|
105
|
+
|
|
106
|
+
# >>> env: copy env files (compose isolation below appends to .env, so it must
|
|
107
|
+
# be a copy), then point their connection strings at this worktree's state.
|
|
108
|
+
# copy_file .env
|
|
109
|
+
# ENV_FILES=".env"
|
|
110
|
+
# <<< env
|
|
111
|
+
|
|
112
|
+
# >>> dev_db: fork the local dev database (or leave DEV_DB_ENGINE empty).
|
|
113
|
+
# require_local_url "$main_database_url"
|
|
114
|
+
# DEV_DB_FORKED="$(forked_db_name app_dev)"
|
|
115
|
+
# run createdb -T app_dev "$DEV_DB_FORKED"
|
|
116
|
+
# set_env .env DATABASE_URL "postgres://localhost:5432/$DEV_DB_FORKED"
|
|
117
|
+
# DEV_DB_ENGINE=postgres DEV_DB_URL_VAR=DATABASE_URL
|
|
118
|
+
# DEV_DB_RESET="dropdb --if-exists $DEV_DB_FORKED && createdb -T app_dev $DEV_DB_FORKED"
|
|
119
|
+
# <<< dev_db
|
|
120
|
+
|
|
121
|
+
# >>> test_db: give the test runner its own database name.
|
|
122
|
+
# TEST_DB_FORKED="$(forked_db_name app_test)"
|
|
123
|
+
# set_env .env TEST_DATABASE_URL "postgres://localhost:5432/$TEST_DB_FORKED"
|
|
124
|
+
# TEST_DB_ENGINE=postgres TEST_DB_URL_VAR=TEST_DATABASE_URL
|
|
125
|
+
# TEST_DB_RESET="dropdb --if-exists $TEST_DB_FORKED"
|
|
126
|
+
# <<< test_db
|
|
127
|
+
|
|
128
|
+
# >>> compose: set USES_COMPOSE=1 when the project runs services through
|
|
129
|
+
# docker compose. Step 4 then isolates the stack generically.
|
|
130
|
+
# USES_COMPOSE=1
|
|
131
|
+
# SHARED_VOLUMES=""
|
|
132
|
+
# <<< compose
|
|
133
|
+
|
|
134
|
+
# >>> other: Redis index / key prefix, S3 prefix, queue and index suffixes,
|
|
135
|
+
# cron off, migrations against the forked DB.
|
|
136
|
+
# S3_PREFIX="wt-$NAME/"; set_env .env S3_PREFIX "$S3_PREFIX"
|
|
137
|
+
# (cd "$WT_PATH" && run python manage.py migrate)
|
|
138
|
+
# <<< other
|
|
139
|
+
|
|
140
|
+
# ---------------------------------------------------------------------------
|
|
141
|
+
# 4. Compose isolation. COMPOSE_PROJECT_NAME alone does not isolate external or
|
|
142
|
+
# fixed-name volumes, or fixed host ports; the generated out-of-tree override
|
|
143
|
+
# does. Never edit the project's tracked compose files.
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
FORKED_VOLUMES_YAML="[]"
|
|
146
|
+
PORTS_STRIPPED=""
|
|
147
|
+
BASE_COMPOSE_FILE=""
|
|
148
|
+
if [ "$USES_COMPOSE" = 1 ]; then
|
|
149
|
+
run mkdir -p "$(dirname "$COMPOSE_OVERRIDE")"
|
|
150
|
+
share_args=""
|
|
151
|
+
for vol in $SHARED_VOLUMES; do share_args="$share_args --share-volume $vol"; done
|
|
152
|
+
report="$(mktemp)"
|
|
153
|
+
# shellcheck disable=SC2086 # share_args is a deliberate word list
|
|
154
|
+
run "$SCRIPT_DIR/gen-compose-worktree-override.sh" --main "$MAIN" --worktree "$WT_PATH" \
|
|
155
|
+
--name "$NAME" --out "$COMPOSE_OVERRIDE" $share_args 2> "$report" >/dev/null
|
|
156
|
+
cat "$report" >&2
|
|
157
|
+
for f in compose.yaml compose.yml docker-compose.yaml docker-compose.yml; do
|
|
158
|
+
if [ -f "$MAIN/$f" ]; then BASE_COMPOSE_FILE="$f"; break; fi
|
|
159
|
+
done
|
|
160
|
+
set_env .env COMPOSE_PROJECT_NAME "$COMPOSE_PROJECT"
|
|
161
|
+
set_env .env COMPOSE_FILE "$BASE_COMPOSE_FILE:$COMPOSE_OVERRIDE"
|
|
162
|
+
case " $ENV_FILES " in *" .env "*) ;; *) ENV_FILES="$ENV_FILES .env" ;; esac
|
|
163
|
+
# The generator reports "- <key> (<reason>) -> <volume>" per forked volume.
|
|
164
|
+
FORKED_VOLUMES_YAML="$(sed -n 's/^ - \([^ ]*\) (\(.*\)) -> \(.*\)$/\1|\2|\3/p' "$report" | {
|
|
165
|
+
out="" sep=""
|
|
166
|
+
while IFS='|' read -r key reason pinned; do
|
|
167
|
+
case "$reason" in external*) reason=external ;; *) reason=fixed-name ;; esac
|
|
168
|
+
out="$out$sep{volume_key: $(yaml_str "$key"), reason: $reason, forked_name: $(yaml_str "$pinned")}"
|
|
169
|
+
sep=", "
|
|
170
|
+
done
|
|
171
|
+
printf '[%s]' "$out"
|
|
172
|
+
})"
|
|
173
|
+
PORTS_STRIPPED="$(sed -n 's/^ stripped host-port publishing from: //p' "$report" | sed 's/^none$//; s/,//g')"
|
|
174
|
+
rm -f "$report"
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# ---------------------------------------------------------------------------
|
|
178
|
+
# 5. Summary: read by teardown, by implement-plan's lane reset, and by
|
|
179
|
+
# vinta-ai-maestro. Every key is present; null means none.
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
db_yaml() { # engine strategy forked url_var reset
|
|
182
|
+
if [ -z "$1" ]; then printf 'null'; return 0; fi
|
|
183
|
+
printf '\n engine: %s\n strategy: %s\n forked_name: %s\n connection_url_var: %s\n reset_cmd: %s' \
|
|
184
|
+
"$1" "$2" "$(yaml_str "$3")" "$(yaml_str "$4")" "$(yaml_str "$5")"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if [ "$USES_COMPOSE" = 1 ]; then
|
|
188
|
+
compose_yaml="$(printf '\n project_name: %s\n network_strategy: %s\n override_path: %s\n base_compose_file: %s\n forked_volumes: %s\n shared_volumes: %s\n ports_stripped_from: %s' \
|
|
189
|
+
"$(yaml_str "$COMPOSE_PROJECT")" "$COMPOSE_NETWORK" "$(yaml_str "$COMPOSE_OVERRIDE")" \
|
|
190
|
+
"$(yaml_str "$BASE_COMPOSE_FILE")" "$FORKED_VOLUMES_YAML" "$(yaml_list "$SHARED_VOLUMES")" \
|
|
191
|
+
"$(yaml_list "$PORTS_STRIPPED")")"
|
|
192
|
+
else
|
|
193
|
+
compose_yaml=null
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
if [ "$DRY_RUN" = 1 ]; then
|
|
197
|
+
log "dry run: would write the summary to $SUMMARY"
|
|
198
|
+
else
|
|
199
|
+
mkdir -p "$(dirname "$SUMMARY")"
|
|
200
|
+
cat > "$SUMMARY" <<EOF
|
|
201
|
+
# Written by $(basename "$0"); read by teardown.sh and implement-plan.
|
|
202
|
+
name: $(yaml_str "$NAME")
|
|
203
|
+
path: $(yaml_str "$WT_PATH")
|
|
204
|
+
branch: $(yaml_str "$BRANCH")
|
|
205
|
+
base_ref: $(yaml_str "$BASE_REF")
|
|
206
|
+
created_at: $(yaml_str "$(date -u +%Y-%m-%dT%H:%M:%SZ)")
|
|
207
|
+
plan_path: $(yaml_str "$PLAN_PATH")
|
|
208
|
+
flags:
|
|
209
|
+
deps_change: true
|
|
210
|
+
schema_change: true
|
|
211
|
+
env_change: true
|
|
212
|
+
test_infra_change: true
|
|
213
|
+
compose_change: $([ "$USES_COMPOSE" = 1 ] && echo true || echo false)
|
|
214
|
+
state:
|
|
215
|
+
deps:
|
|
216
|
+
strategy: $DEPS_STRATEGY
|
|
217
|
+
paths: $(yaml_list "$DEPS_PATHS")
|
|
218
|
+
env:
|
|
219
|
+
strategy: $ENV_STRATEGY
|
|
220
|
+
files: $(yaml_list "$ENV_FILES")
|
|
221
|
+
dev_db: $(db_yaml "$DEV_DB_ENGINE" "$DEV_DB_STRATEGY" "$DEV_DB_FORKED" "$DEV_DB_URL_VAR" "$DEV_DB_RESET")
|
|
222
|
+
test_db: $(db_yaml "$TEST_DB_ENGINE" "$TEST_DB_STRATEGY" "$TEST_DB_FORKED" "$TEST_DB_URL_VAR" "$TEST_DB_RESET")
|
|
223
|
+
compose: $compose_yaml
|
|
224
|
+
other:
|
|
225
|
+
redis_db_index: $(yaml_str "$REDIS_DB_INDEX")
|
|
226
|
+
s3_prefix: $(yaml_str "$S3_PREFIX")
|
|
227
|
+
cron_disabled: $CRON_DISABLED
|
|
228
|
+
sandbox:
|
|
229
|
+
tier: none
|
|
230
|
+
launcher: null
|
|
231
|
+
deny: []
|
|
232
|
+
allow: []
|
|
233
|
+
notes: $(yaml_str "Provisioned by $(basename "$0") (kind: $KIND). $NOTES")
|
|
234
|
+
EOF
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
# ---------------------------------------------------------------------------
|
|
238
|
+
# 6. Report (implement-plan quotes the tail of this)
|
|
239
|
+
# ---------------------------------------------------------------------------
|
|
240
|
+
cat <<EOF
|
|
241
|
+
worktree: $WT_PATH
|
|
242
|
+
branch: $BRANCH (from $BASE_REF)
|
|
243
|
+
deps: $DEPS_STRATEGY ${DEPS_PATHS:-(none)}
|
|
244
|
+
env: $ENV_STRATEGY ${ENV_FILES:-(none)}
|
|
245
|
+
dev db: ${DEV_DB_FORKED:-(none)}
|
|
246
|
+
test db: ${TEST_DB_FORKED:-(none)}
|
|
247
|
+
compose: $([ "$USES_COMPOSE" = 1 ] && echo "$COMPOSE_PROJECT" || echo "(not used)")
|
|
248
|
+
summary: $SUMMARY
|
|
249
|
+
teardown: $SCRIPT_DIR/teardown.sh $NAME
|
|
250
|
+
EOF
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remove a worktree prepare.sh made for {{PROJECT_NAME}}, and everything it
|
|
3
|
+
# created outside git.
|
|
4
|
+
#
|
|
5
|
+
# implement-plan runs this as `commands.worktree_teardown`, with the same
|
|
6
|
+
# VINTA_WORKTREE_* environment the worktree was provisioned with. You can also
|
|
7
|
+
# run it by hand:
|
|
8
|
+
#
|
|
9
|
+
# {{SCRIPT_DIR}}/teardown.sh <worktree-name> [--dry-run] [--force]
|
|
10
|
+
#
|
|
11
|
+
# Safe to run more than once: every step skips what is already gone. It refuses
|
|
12
|
+
# a worktree with uncommitted changes unless --force (or VINTA_WORKTREE_FORCE=1)
|
|
13
|
+
# is given, and only drops databases named `*_wt_<worktree>`. It never runs
|
|
14
|
+
# `docker compose down -v`, which would also delete volumes shared with main.
|
|
15
|
+
#
|
|
16
|
+
# Generated by the vinta-ai-workflows bootstrap. Keep the PROJECT STEPS regions
|
|
17
|
+
# in step with prepare.sh's.
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
. "$(cd "$(dirname "$0")" && pwd)/lib.sh" "$@"
|
|
20
|
+
|
|
21
|
+
registered=0
|
|
22
|
+
if git -C "$MAIN" worktree list --porcelain | grep -Fxq "worktree $WT_PATH"; then
|
|
23
|
+
registered=1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# 1. Refuse to lose work
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
if [ "$registered" = 1 ] && [ "$FORCE" != 1 ]; then
|
|
30
|
+
if [ -n "$(git -C "$WT_PATH" status --porcelain)" ]; then
|
|
31
|
+
echo "$WT_PATH has uncommitted changes; commit or stash them, or pass --force to discard them" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# 2. PROJECT STEPS, in reverse order of prepare.sh. Each must tolerate state
|
|
38
|
+
# that is already gone.
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
# >>> other: undo what prepare.sh's "other" region created outside the worktree.
|
|
42
|
+
# <<< other
|
|
43
|
+
|
|
44
|
+
# >>> test_db: drop the forked test database.
|
|
45
|
+
# db="$(forked_db_name app_test)"; require_forked_name "$db"
|
|
46
|
+
# run dropdb --if-exists "$db"
|
|
47
|
+
# <<< test_db
|
|
48
|
+
|
|
49
|
+
# >>> dev_db: drop the forked dev database.
|
|
50
|
+
# db="$(forked_db_name app_dev)"; require_forked_name "$db"
|
|
51
|
+
# run dropdb --if-exists "$db"
|
|
52
|
+
# <<< dev_db
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# 3. Compose: stop the stack, then remove only the volumes the override pinned
|
|
56
|
+
# for this worktree. Shared volumes stay.
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
if [ -f "$COMPOSE_OVERRIDE" ]; then
|
|
59
|
+
if [ "$registered" = 1 ] && command -v docker >/dev/null 2>&1; then
|
|
60
|
+
(cd "$WT_PATH" && run docker compose -p "$COMPOSE_PROJECT" down) || log "compose down failed; continuing"
|
|
61
|
+
fi
|
|
62
|
+
# The override's volumes: block lists each pinned name; that is the manifest.
|
|
63
|
+
for vol in $(sed -n '/^volumes:/,/^[^ ]/s/^ name: "\(.*\)"$/\1/p' "$COMPOSE_OVERRIDE"); do
|
|
64
|
+
case "$vol" in
|
|
65
|
+
*_wt_"$NAME"_*) run docker volume rm "$vol" >/dev/null 2>&1 || log "volume $vol already gone" ;;
|
|
66
|
+
*) log "skip volume $vol: not named for this worktree" ;;
|
|
67
|
+
esac
|
|
68
|
+
done
|
|
69
|
+
run rm -f "$COMPOSE_OVERRIDE"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# 4. The worktree and its branch
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
if [ "$registered" = 1 ]; then
|
|
76
|
+
if [ "$FORCE" = 1 ]; then
|
|
77
|
+
run git -C "$MAIN" worktree remove --force "$WT_PATH"
|
|
78
|
+
else
|
|
79
|
+
run git -C "$MAIN" worktree remove "$WT_PATH"
|
|
80
|
+
fi
|
|
81
|
+
else
|
|
82
|
+
log "no worktree registered at $WT_PATH"
|
|
83
|
+
fi
|
|
84
|
+
# The branch goes only when it holds no commits of its own (its tip is already
|
|
85
|
+
# in the base ref, or merged into main's HEAD). Anything else is kept.
|
|
86
|
+
if git -C "$MAIN" show-ref --verify --quiet "refs/heads/$BRANCH"; then
|
|
87
|
+
if git -C "$MAIN" merge-base --is-ancestor "$BRANCH" "$BASE_REF" 2>/dev/null \
|
|
88
|
+
|| git -C "$MAIN" merge-base --is-ancestor "$BRANCH" HEAD 2>/dev/null; then
|
|
89
|
+
run git -C "$MAIN" branch -D "$BRANCH" >/dev/null
|
|
90
|
+
else
|
|
91
|
+
log "kept branch $BRANCH: it has commits not in $BASE_REF (delete it with git branch -D once you are sure)"
|
|
92
|
+
fi
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
run rm -f "$SUMMARY"
|
|
96
|
+
log "done"
|