Skip to content

Commit 7007a5b

Browse files
azlekovclaude
andcommitted
refactor(repo): move the dev-install symlinking into its own guarded script
D-0004 guarded the whole `justfile`, which is the only guard in the repo pointing at a multi-purpose file — every other one names a Rust module, a workflow, or a single-purpose install script. About six of the justfile's 190 lines are D-0004's business, so the guard fired on every unrelated recipe: it tripped while editing `pricing-sync`, which fetches model prices and has nothing to do with installs. That is not just noise. The guard wall also requires a decision trailer on any commit touching a guarded path, so unrelated justfile edits had to cite D-0004 in their commit message — polluting `git log --grep` and training readers to skim past a guard that is usually irrelevant. The protection was real, though, and is kept intact rather than dropped: nothing else in the repo would notice `just link` changing from `ln -sf` to `cp`, which would leave a dev install indistinguishable from a managed one and silently defeat every refusal D-0004 describes. So the symlinking moves to `dev-install.sh` and the guard follows it there. Every path D-0004 now names is install-path logic end to end. Extracting it also brings that logic under the shellcheck/shfmt standard the repo already applies to install.sh — it had been escaping it only by hiding in a justfile — and the script gains a missing-build check the inline recipe never had, where `ln -sf` would happily create a dangling symlink. `just link` produces byte-identical symlinks; verified against a scratch bin dir, along with idempotent re-runs and both failure paths. Why: a guard that fires on edits it does not govern teaches readers to ignore it. Rejected: dropping `justfile` from the guards outright — the `ln -sf` to `cp` regression would then go unguarded, which is the one thing D-0004 must catch. Refs: D-0004 Refs: D-0012 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Asen Lekov <asenlekoff@gmail.com>
1 parent b8214dd commit 7007a5b

4 files changed

Lines changed: 66 additions & 13 deletions

File tree

.github/workflows/ci.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,14 @@ jobs:
2626
with:
2727
configFile: commitlint.config.mjs
2828

29-
# Lint install.sh: POSIX-only shellcheck plus canonical shfmt formatting.
29+
# Lint the shell that puts a binary on someone's PATH: POSIX-only shellcheck
30+
# plus canonical shfmt formatting. Both scripts here are install-path logic
31+
# guarded by D-0004 — install.sh places a released binary, dev-install.sh
32+
# symlinks a build-tree one — which is why they are held to a standard the
33+
# rest of the repo's shell is not.
3034
# Lightweight — stays on GitHub-hosted runners, same as commit-lint.
3135
shell-lint:
32-
name: Shell Lint (install.sh)
36+
name: Shell Lint (install scripts)
3337
runs-on: ubuntu-latest
3438
timeout-minutes: 5
3539
if: github.event.pull_request.draft == false
@@ -39,10 +43,10 @@ jobs:
3943
uses: taiki-e/install-action@v2
4044
with:
4145
tool: shfmt
42-
- name: shellcheck -s sh install.sh
43-
run: shellcheck -s sh install.sh
44-
- name: shfmt -d -s -ln posix -i 2 install.sh
45-
run: shfmt -d -s -ln posix -i 2 install.sh
46+
- name: shellcheck -s sh install.sh dev-install.sh
47+
run: shellcheck -s sh install.sh dev-install.sh
48+
- name: shfmt -d -s -ln posix -i 2 install.sh dev-install.sh
49+
run: shfmt -d -s -ln posix -i 2 install.sh dev-install.sh
4650

4751
# Lint install.ps1: PSScriptAnalyzer at Warning severity, install.sh's Windows sibling.
4852
# Lightweight — stays on GitHub-hosted runners, same as shell-lint above. `shell: pwsh`

.zavet/decisions/D-0004-never-overwrite-a-development-install.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ guards:
66
- cli/dira/src/update/replace.rs
77
- install.sh
88
- install.ps1
9-
- justfile
9+
- dev-install.sh
1010
origin: recorded
1111
verified: true
1212
---
@@ -20,8 +20,8 @@ only. Never a build-tree binary.
2020

2121
## Why
2222

23-
`just install` symlinks `target/release/{dira,dirad}` into `~/.local/bin`, so
24-
a contributor's PATH entry points into their build tree. Silently replacing
23+
`just install` symlinks `target/release/{dira,dirad}` into `~/.local/bin` (via
24+
`dev-install.sh`), so a contributor's PATH entry points into their build tree. Silently replacing
2525
that symlink with a released binary destroys their dev loop in a way that is
2626
confusing to diagnose: `cargo build` keeps succeeding, the binary on PATH
2727
just stops changing. Overwriting a file *inside* `target/` is worse. The
@@ -47,5 +47,11 @@ sees it.
4747
never treat `current_exe()` as authoritative for this check.
4848
- Any new install-like path must reuse `discover_install` rather than
4949
re-deriving the rule.
50+
- The dev install must stay a **symlink**. `dev-install.sh` exists so that
51+
rule sits in one guarded, shellcheck'd file: copying the binaries onto PATH
52+
instead would leave a dev install indistinguishable from a managed one and
53+
silently defeat every refusal above. This guard used to name the whole
54+
`justfile`, which fired on unrelated recipes and taught readers to skim past
55+
it.
5056
- Error text must name `just install` as the way to update a dev build, and
5157
give the exact commands to switch to released binaries.

dev-install.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/bin/sh
2+
# Create the development install: point the PATH entry at the build tree.
3+
#
4+
# Driven by `just link` (and so by `just install`); not a user-facing
5+
# installer. install.sh is the one contributors and users run — this is its
6+
# opposite number, the thing that deliberately puts a build-tree binary on
7+
# PATH.
8+
#
9+
# The symlink is load-bearing, not a convenience. D-0004 has install.sh and
10+
# `dira update` refuse to overwrite a development install, and the only thing
11+
# that distinguishes one is that the PATH entry is a symlink into `target/`:
12+
# `discover_install` reads it with `symlink_metadata`, because `current_exe()`
13+
# resolves symlinks on both Linux and macOS and would see an ordinary managed
14+
# install. Copy the binaries here instead of linking them and that detection
15+
# goes blind — `dira update` would overwrite a contributor's dev build and
16+
# `cargo build` would silently stop affecting the binary on PATH, which is the
17+
# exact failure D-0004 exists to prevent. Keep it a symlink.
18+
#
19+
# Idempotent: safe to re-run, and `ln -sf` re-points an existing link rather
20+
# than nesting one inside it.
21+
set -eu
22+
23+
repo_dir="${1:?usage: dev-install.sh <repo-dir> <bin-dir>}"
24+
bin_dir="${2:?usage: dev-install.sh <repo-dir> <bin-dir>}"
25+
26+
for name in dira dirad; do
27+
built="$repo_dir/target/release/$name"
28+
if [ ! -x "$built" ]; then
29+
echo "dev-install: $built is missing — run \`just release\` first." >&2
30+
exit 1
31+
fi
32+
done
33+
34+
mkdir -p "$bin_dir"
35+
for name in dira dirad; do
36+
ln -sf "$repo_dir/target/release/$name" "$bin_dir/$name"
37+
done
38+
39+
echo "Linked dira + dirad -> $bin_dir"

justfile

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,15 @@ install: release link daemon-restart
104104
@echo "Installed. `dira` + `dirad` are live from {{bin_dir}} (latest build)."
105105

106106
# Symlink dira + dirad into {{bin_dir}} (idempotent; safe to re-run).
107+
#
108+
# The symlinking itself lives in dev-install.sh rather than inline here. It is
109+
# install-path logic — D-0004's dev-install detection reads the PATH entry with
110+
# `symlink_metadata` and goes blind the moment these become copies — so it
111+
# belongs in a shellcheck'd script beside install.sh, under that decision's
112+
# guard, instead of in a recipe the guard could only reach by claiming the
113+
# whole justfile and every unrelated recipe in it.
107114
link:
108-
mkdir -p "{{bin_dir}}"
109-
ln -sf "{{justfile_directory()}}/target/release/dira" "{{bin_dir}}/dira"
110-
ln -sf "{{justfile_directory()}}/target/release/dirad" "{{bin_dir}}/dirad"
111-
@echo "Linked dira + dirad -> {{bin_dir}}"
115+
sh dev-install.sh "{{justfile_directory()}}" "{{bin_dir}}"
112116

113117
# Restart the resident daemon from the freshly built binary.
114118
#

0 commit comments

Comments
 (0)