-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·209 lines (199 loc) · 8.59 KB
/
Copy pathbuild.sh
File metadata and controls
executable file
·209 lines (199 loc) · 8.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env bash
# Concatenate src/ into the single-file `5dive` binary the installer fetches.
#
# Why a build step: the installed artifact is a single file (curl install.5dive.com
# | sudo bash drops one binary into /usr/local/bin). The source repo is split for
# readability — see CONTRIBUTING in README.md. CI runs ./build.sh && git diff
# --exit-code 5dive on every push to catch "edited the bundle, forgot to edit
# src/" drift in either direction.
#
# Order matters: header.sh has `set -euo pipefail` + every global / declare -A
# map, so it must come first. main.sh has the EXIT trap and `main "$@"`, so it
# must come last. The middle is grouped by concern (lib/ helpers → cmd_*
# subcommands). state.sh / audit.sh / registry.sh look interleaved because the
# original script's audit block sat between ensure_state and with_registry_lock;
# keeping that order makes the bundle byte-identical with the pre-refactor file.
set -euo pipefail
cd "$(dirname "$0")"
# Output path is overridable (BUILD_OUT) so tests can build a throwaway binary to a
# temp dir without dirtying the tracked ./5dive artifact. Defaults to the repo ./5dive.
OUT="${BUILD_OUT:-5dive}"
# DIVE-2603: FIVE_VERSION is assigned only when a release tag is cut, so a
# working-tree bundle permanently says 0.0.0-dev. Carry the source identity as
# a separate fact that remains meaningful both before and after tag time. A
# dirty tree is deliberately stamped <sha>-dirty: the artifact contains bytes
# HEAD does not, so install.sh must reject the stamp as ancestry evidence and
# fall back to the version path instead of trusting a false identity.
BUILD_SHA="$(git rev-parse --verify 'HEAD^{commit}' 2>/dev/null)" || {
echo "error: cannot resolve the source commit for $OUT" >&2
exit 1
}
if [[ ! "$BUILD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
echo "error: source commit is not a full git sha: $BUILD_SHA" >&2
exit 1
fi
if [[ -n "$(git status --porcelain --untracked-files=normal 2>/dev/null)" ]]; then
BUILD_SHA="${BUILD_SHA}-dirty"
fi
# DIVE-2681: BUILD_OUT may name the bundle ANYTHING, and .gitignore only knows
# about `/5dive` + `/5dive.sha256` (DIVE-2091). So `BUILD_OUT=./5dive-fix` builds
# a 3.3MB bundle that git happily tracks, and one `git add -A` puts it on main —
# which is exactly what happened in #434. The ignore rule was never wrong; the
# output name walked around it.
#
# Refuse rather than widen the glob: a name list only ever covers the names
# someone already used, and `5dive-agent-start` / `5dive-refresh-*.sh` are real
# tracked files, so `/5dive-*` would be worse than the disease. Build inside the
# tree ONLY as the ignored ./5dive; anywhere else, build outside it.
_out_abs=$(cd "$(dirname "$OUT")" 2>/dev/null && pwd)/$(basename "$OUT")
_repo_abs=$(pwd)
if [[ "$_out_abs" == "$_repo_abs"/* && "$(basename "$OUT")" != "5dive" ]]; then
printf 'build.sh: refusing to write %s inside the repo.\n' "$(basename "$OUT")" >&2
printf ' Only ./5dive is gitignored here (DIVE-2091); any other in-tree name is TRACKED\n' >&2
printf ' and a `git add -A` will commit a multi-megabyte bundle (DIVE-2681, PR #434).\n' >&2
printf ' Build to a path outside the repo instead: BUILD_OUT=/tmp/5dive-test ./build.sh\n' >&2
exit 2
fi
unset _out_abs _repo_abs
cat \
src/header.sh \
src/lib/error_codes.sh \
src/lib/self.sh \
src/lib/output.sh \
src/lib/validation.sh \
src/lib/models.sh \
src/lib/agent_setup.sh \
src/lib/state.sh \
src/lib/env_overrides.sh \
src/lib/capability.sh \
src/lib/broker.sh \
src/lib/durable.sh \
src/lib/disk.sh \
src/lib/audit.sh \
src/lib/reap.sh \
src/lib/registry.sh \
src/lib/agent_env.sh \
src/lib/tasks_db.sh \
src/lib/actor.sh \
src/lib/routing_receipt.sh \
src/lib/a2a_rounds.sh \
src/cmd_auth.sh \
src/cmd_account.sh \
src/cmd_agent.sh \
src/cmd_agent_create.sh \
src/cmd_agent_lifecycle.sh \
src/cmd_agent_config.sh \
src/cmd_agent_buzz.sh \
src/cmd_agent_buzz_join.sh \
src/cmd_agent_buzz_whois.sh \
src/cmd_agent_buzz_pair.sh \
src/cmd_buzz.sh \
src/cmd_agent_buzz_bridge.sh \
src/cmd_agent_telegram.sh \
src/cmd_agent_teambot.sh \
src/cmd_agent_pairing.sh \
src/cmd_agent_runtime.sh \
src/cmd_cos.sh \
src/cmd_acp.sh \
src/cmd_skill.sh \
src/cmd_init.sh \
src/cmd_doctor.sh \
src/cmd_host.sh \
src/cmd_watch.sh \
src/cmd_compose.sh \
src/cmd_whoami.sh \
src/task/dispatch.sh \
src/task/routing.sh \
src/task/crud.sh \
src/task/gate_evidence.sh \
src/task/status.sh \
src/task/delivery.sh \
src/task/loops.sh \
src/task/need.sh \
src/task/notify.sh \
src/task/inbox.sh \
src/task/answer.sh \
src/task/doctor.sh \
src/cmd_task.sh \
src/cmd_trace.sh \
src/cmd_org.sh \
src/cmd_human.sh \
src/cmd_ui.sh \
src/cmd_hire.sh \
src/cmd_project.sh \
src/cmd_goal.sh \
src/cmd_objective.sh \
src/cmd_company.sh \
src/cmd_council.sh \
src/cmd_constitution.sh \
src/cmd_loop.sh \
src/cmd_loop_pack.sh \
src/cmd_crew.sh \
src/cmd_heartbeat.sh \
src/cmd_supervisor.sh \
src/cmd_liveness.sh \
src/cmd_fleet.sh \
src/cmd_usage.sh \
src/cmd_digest.sh \
src/cmd_proof.sh \
src/cmd_selfcheck.sh \
src/cmd_push.sh \
src/cmd_deploy.sh \
src/cmd_gh.sh \
src/cmd_bug.sh \
src/cmd_memory.sh \
src/cmd_pack.sh \
src/cmd_secret.sh \
src/cmd_selfupdate.sh \
src/main.sh \
| sed -E "s/^readonly FIVE_BUILD_SHA=\"[^\"]*\"/readonly FIVE_BUILD_SHA=\"$BUILD_SHA\"/" \
> "$OUT"
# DIVE-1261: publish a sha256 of the bundle so the installer can verify the fetched binary before
# swapping it in. Regenerated on every build and committed alongside the bundle; CI's build+diff
# drift check keeps the two in sync.
#
# CNCL-23 regression: generate the sha IMMEDIATELY after writing the bundle, BEFORE any step that
# could abort under `set -e` (the chmod below, the FIVE_VERSION check) — otherwise the bundle and
# its committed sha can DRIFT. A `chmod: Operation not permitted` (building a claude-owned worktree
# as another user) once aborted right before the old sha line, shipping a 0.12.7 bundle carrying
# 0.12.6's sha (PR #95 — CI drift-gate RED, host-roll refused on the mismatch). Order now
# guarantees: whenever $OUT exists on disk post-build, $OUT.sha256 matches it.
sha256sum "$OUT" | awk '{print $1}' > "$OUT.sha256"
# +x is a local convenience (the installer re-chmods the fetched binary); a cross-user perms
# failure must NOT abort the build and re-open the sha-drift window above.
chmod +x "$OUT" 2>/dev/null || true
# Sanity-check the version line landed in the bundle. CI's bundle-drift check
# already catches missing src→bundle plumbing, but this gives a tighter error
# when someone empties out FIVE_VERSION by accident.
if ! grep -qE '^readonly FIVE_VERSION="[^"]+"' "$OUT"; then
echo "error: $OUT is missing FIVE_VERSION — check src/header.sh" >&2
exit 1
fi
if ! grep -qE "^readonly FIVE_BUILD_SHA=\"${BUILD_SHA}\"$" "$OUT"; then
echo "error: $OUT is missing FIVE_BUILD_SHA=$BUILD_SHA — check src/header.sh" >&2
exit 1
fi
# DIVE-2097: src/lib/self.sh must precede every five_self_bundle consumer in the cat
# list above. Each consumer opens with `declare -F five_self_bundle ||
# source ".../lib/self.sh"` — dead code in the bundle, load-bearing in the split tree
# (see src/lib/self.sh and community/wiki/command-v-answers-the-wrong-question.md
# rule 5). That property only held because the cat list ABOVE was hand-ordered with a
# prose comment ("Order matters: ... lib/ helpers -> cmd_*"); nothing enforced it. If a
# future consumer ever lands ahead of lib/self.sh in the list, the guard fires inside
# the bundle where dirname "$BASH_SOURCE" is the INSTALL dir and lib/self.sh does not
# exist — the source fails, and `set -euo pipefail` (src/header.sh) takes the whole CLI
# down. Checked on the built artifact rather than the cat list text so this also catches
# a future refactor that stops concatenating from a fixed file list.
def_line="$(grep -n '^five_self_bundle() {' "$OUT" | head -1 | cut -d: -f1)"
if [[ -z "$def_line" ]]; then
echo "error: $OUT has no five_self_bundle definition — check src/lib/self.sh" >&2
exit 1
fi
guard_line="$(grep -n 'declare -F five_self_bundle' "$OUT" | head -1 | cut -d: -f1)"
if [[ -n "$guard_line" && "$def_line" -gt "$guard_line" ]]; then
echo "error: $OUT defines five_self_bundle at line $def_line, AFTER the first" >&2
echo " consumer guard at line $guard_line. src/lib/self.sh must be cat'd before" >&2
echo " every five_self_bundle consumer — fix the file order in build.sh." >&2
exit 1
fi
echo "built $OUT ($(wc -l < "$OUT") lines, $(grep -oE '^readonly FIVE_VERSION="[^"]+"' "$OUT" | cut -d'"' -f2) at ${BUILD_SHA:0:12}) + $OUT.sha256 ($(cut -c1-16 "$OUT.sha256")…)"