-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathle
More file actions
executable file
·305 lines (282 loc) · 11.9 KB
/
Copy pathle
File metadata and controls
executable file
·305 lines (282 loc) · 11.9 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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
#!/bin/sh
set -eu
root=$(CDPATH= cd "$(dirname "$0")" && pwd)
# Builds the le personality into $1. Every caller shares this one block, so the
# feature tags, the caches and the toolchain pin cannot drift between the shared
# binary and a session-private one.
build_le() {
out=$1
# One reader of the manifest for every shell caller, and one Go reader beside
# it (feature-tags). An empty answer is an error there, so no branch here
# tolerates a feature-less build.
. "$root/feature-tags"
tags=ze_le,$(feature_tags "$root/feature-gates.txt" ",")
# cache/ is a SYMLINK onto another filesystem on the development hosts, so
# it resolves for them and dangles anywhere the checkout is shared without
# it: a QEMU guest that mounts only /workspace gets `mkdir cache: file
# exists` from the dangling link and no build at all. Prove the directory is
# usable rather than assuming the layout, and say so when it is not, because
# a cache silently in the wrong place is the shape of the full-disk false
# reds this repository records in plan/journal/full-disk-false-red.md.
GOCACHE=$root/cache/go-cache
if ! mkdir -p "$GOCACHE" 2>/dev/null; then
GOCACHE=$root/tmp/go-cache
mkdir -p "$GOCACHE"
echo "le: $root/cache is not usable here, building through $GOCACHE" >&2
fi
GOLANGCI_LINT_CACHE=$root/tmp/golangci-lint-cache
CGO_ENABLED=0
export GOCACHE GOLANGCI_LINT_CACHE CGO_ENABLED
# A toolchain line wins; the go directive is the fallback, because a module
# stating `go 1.27.0` and no toolchain line still pins go1.27.0. Reading only
# the toolchain line left this build with no pin at all.
toolchain=$(awk '$1 == "toolchain" { print $2; exit }' "$root/go.mod")
if [ -z "$toolchain" ]; then
toolchain=$(awk '$1 == "go" { print "go" $2; exit }' "$root/go.mod")
fi
if [ -n "$toolchain" ]; then
GOTOOLCHAIN=$toolchain
export GOTOOLCHAIN
fi
mkdir -p "$(dirname "$out")"
(cd "$root" && go build -tags "$tags" -o "$out" ./cmd/ze)
}
# Replaces $1 with a fresh build without writing through the file a peer session
# may be executing. go build truncates its output in place, which corrupts a
# running process; building beside the target and renaming leaves that process
# on the old inode until it exits.
update_le() {
# Not `out`: sh has no local scope, so build_le's own `out=$1` would rewrite
# this one and the rename below would name the staging path twice.
target=$1
staging=$target.new.$$
# A failed build leaves a 95 MB partial object beside the binary, and every
# later call would walk past it, so the staging path is cleared on the way
# out whichever way the build went.
if ! build_le "$staging"; then
rm -f "$staging"
echo "le: the build failed, so bin/le is unchanged" >&2
return 1
fi
mv -f "$staging" "$target"
}
# The build inputs of the le personality: the module's own Go, the vendored
# dependencies, and the three files that decide how any of it compiles.
# gokrazy/modcache holds 57,000 more .go files that no package imports, so it is
# not an input and walking it would cost 2.5 s a call instead of 40 ms.
le_sources='internal cmd pkg api vendor'
le_build_config='go.mod go.sum feature-gates.txt'
# True when $1 is older than any build input. `-print -quit` stops at the first
# newer file, so the answer costs about 40 ms when nothing changed and less when
# something did.
# The extensions a build input can carry. Go is not the whole set: the binary
# embeds 221 .yang files and a handful of .json, .txt, .md, .js, .css and
# .version assets, and go:embed bakes each one INTO the binary. A walk that
# looks only at *.go therefore reports a current binary after a schema edit.
#
# That is not theoretical. On 2026-09-13 four .yang edits left `le doc yang-contract
# help-shape` answering the PRE-edit result, because it reads the embedded
# copy, while `le cli grammar` went green at once because it greps the disk.
# Two gates disagreed about one edit and neither was wrong: they were reading
# different copies of the same file (plan/journal/stale-artifact-reused.md).
# The predicate is written out at each call site rather than held in a variable.
# An unquoted `$names` expansion lets the shell glob `*.go` against the current
# directory before find ever sees it, which silently empties the predicate: the
# first version of this fix matched 0 files where the old walk matched 3.
le_find_sources() {
# $1 is the reference file; every later argument goes to find unchanged.
reference=$1
shift
find $le_sources \( -name '*.go' -o -name '*.yang' -o -name '*.json' \
-o -name '*.txt' -o -name '*.md' -o -name '*.js' -o -name '*.css' \
-o -name '*.version' \) -newer "$reference" "$@" 2>/dev/null
}
le_is_stale() {
[ -n "$(
cd "$root" && le_find_sources "$1" -print -quit
find $le_build_config -newer "$1" -print -quit 2>/dev/null
)" ]
}
# True when a build input newer than $1 is one git holds unmodified. Several
# sessions share this checkout, so a peer's half-written edit is newer than the
# binary all day and says nothing: only a file that has LANDED means the binary
# missed a change. This walk cannot stop at the first hit the way le_is_stale
# does, because that hit can be the dirty file, so le_is_stale gates it.
committed_change_is_newer() {
[ -n "$(
{
git -C "$root" status --porcelain -- $le_sources $le_build_config 2>/dev/null |
sed -e 's/^...//' -e 's/.* -> //'
echo '--'
(cd "$root" && le_find_sources "$1"
find $le_build_config -newer "$1" 2>/dev/null)
} | awk '
$0 == "--" { landed = 1; next }
!landed { dirty[$0] = 1; next }
!($0 in dirty) { print; exit }
'
)" ]
}
# One call in sixteen pays for the staleness walk. The pid is the sample,
# because a counter would be state several sessions write at once and the only
# thing the sample has to be is roughly one in sixteen.
le_sampled() {
[ $(($$ % 16)) -eq 0 ]
}
# Says the binary is behind the tree. It never rebuilds and never fails: a
# session that wanted the new binary asked for it with --update, and one that
# did not is running a command whose answer this must not replace.
warn_when_stale() {
[ -x "$1" ] || return 0
le_is_stale "$1" || return 0
committed_change_is_newer "$1" || return 0
echo "le: bin/le is older than committed sources; refresh it with './le --update'" >&2
}
# True when $1 is a binary this kernel can exec, judged by its magic bytes.
#
# Four bytes settle it: ELF opens 7f 45 4c 46, and a Mach-O opens cf fa ed fe
# or its byte-swapped and universal forms. `od` is POSIX and is in busybox, so
# this answers inside an Alpine guest as well as on the development hosts.
#
# An OS this does not know about answers TRUE, deliberately. The question here
# is whether a cached binary is for another platform, and an unrecognised pair
# is not evidence that it is: refusing one would rebuild on every call for no
# reason. The failure that matters is the one this catches.
#
# A FORMAT this does not know answers TRUE for the same reason, and a shebang is
# that case. `#!` opens a script the kernel execs on any platform, so it is never
# the wrong-architecture binary this looks for; judging it by the Mach-O table
# below called an executable file unrunnable and rebuilt over it.
le_runs_here() {
magic=$(od -An -tx1 -N4 "$1" 2>/dev/null | tr -d ' \n')
case $magic in
2321*) return 0 ;;
esac
case $(uname -s) in
Linux)
[ "$magic" = "7f454c46" ]
;;
Darwin)
case $magic in
cffaedfe | feedfacf | cafebabe | bebafeca) return 0 ;;
*) return 1 ;;
esac
;;
*)
return 0
;;
esac
}
# A build name becomes one path component, so it accepts letters, digits, dot,
# underscore and hyphen, it is never empty, and `..` is refused on top of that.
# A rejected name is never repaired into something safe.
check_name() {
case $1 in
'' | *[!A-Za-z0-9._-]* | *..*)
echo "le: --name accepts letters, digits, dot, underscore and hyphen, and refuses '..': got '$1'" >&2
exit 2
;;
esac
}
# `./le --name <name> <command>...` builds bin/le-<name>/le and runs it. The
# option is consumed here and only here, so everything after it reaches the
# binary untouched. It has to come first, which is unambiguous because every le
# command starts with a keyword and no keyword starts with a hyphen.
#
# ZE_LE_BUILD_NAME carries the choice to the process and to its children. It is
# the launcher's own channel rather than an interface: cmd/ze/le_build_name.go
# reads it and refuses to answer when the running binary is not the one the
# caller named, which is what turns a stale or hardcoded binary into an error.
# `./le --update` replaces bin/le with a build of the working tree, and runs
# whatever follows it against that build. With no command after it, updating IS
# the command. --name already builds on every call, so the option is satisfied
# there and writes nothing to the shared binary.
update=
if [ "${1:-}" = "--update" ]; then
update=yes
shift
fi
name=
case ${1:-} in
--name)
name=${2:-}
check_name "$name"
shift 2
;;
--name=*)
name=${1#--name=}
check_name "$name"
shift
;;
*)
if [ -n "${ZE_LE_BUILD_NAME:-}" ]; then
name=$ZE_LE_BUILD_NAME
check_name "$name"
fi
;;
esac
if [ -n "$name" ]; then
# The file name stays `le`, so the name goes on the DIRECTORY. defaultDispatch
# in cmd/ze/dispatch.go picks the personality with LookupRoot(binaryName()),
# and a binary called le-probe answers `le session ...` with "unknown
# command: session" and prints the ze tree instead.
binary=$root/bin/le-$name/le
ZE_LE_BUILD_NAME=$name
export ZE_LE_BUILD_NAME
# Built on EVERY call, so the toolchain decides what is stale and the session
# reads its own edits. Sessions share this checkout, so this never writes
# bin/le and never waits for a peer to stop using it.
build_le "$binary"
else
binary=$root/bin/le
# This is an existence cache, not a freshness check. An existing binary is
# deliberately executed without asking the toolchain whether sources changed,
# because a peer session's unfinished edit would then break every call in
# every session. --update picks a change up on demand, and --name builds a
# session its own binary. Deleting this one never does: peers execute it.
#
# The cache is per PLATFORM as well as per existence, and it has to be. A
# QEMU guest mounts this checkout at /workspace and finds a bin/le the HOST
# built, so `exec` gets ENOEXEC, the shell falls back to reading the Mach-O
# header as a script, and the answer is `line 1: not found` followed by a
# syntax error. The format is read from the file rather than stamped beside
# it, so an existing binary needs no rebuild to be judged.
if [ -x "$binary" ] && ! le_runs_here "$binary"; then
binary=$root/bin/le-$(uname -s)-$(uname -m)/le
fi
if [ ! -x "$binary" ]; then
build_le "$binary"
elif [ -n "$update" ]; then
update_le "$binary"
fi
fi
if [ $# -eq 0 ] && [ -n "$update" ]; then
exit 0
fi
# The staleness check runs AFTER the command, so a session waiting on le waits
# on le and not on a find, and reads the line under the answer it asked for
# rather than above it. Paying that with the exec is what costs this branch a
# shell process; every other call keeps the exec and costs nothing.
#
# --name is exempt: that binary was built from the working tree one line ago.
if [ -z "$name" ] && le_sampled; then
status=0
"$binary" "$@" || status=$?
warn_when_stale "$binary"
# This branch gave up `exec`, so the binary ran as a CHILD and a signal that
# killed it arrives here as 128+signal. Exiting with that NUMBER would tell
# the caller the binary chose it: a `timeout` that sends SIGTERM, a CI runner
# that kills a hung gate, and a shell reporting `Terminated` all read a
# terminating signal differently from an exit code, and every other call to
# this launcher execs and so reports the signal. One call in sixteen must not
# answer differently from the other fifteen.
#
# Re-raise it on this shell instead. The `exit` below stays as the fallback
# for a signal the shell will not take, so this can only ever restore the
# exec'd behavior, never lose the status.
if [ "$status" -gt 128 ] && [ "$status" -lt 160 ]; then
kill -"$((status - 128))" $$ 2>/dev/null
fi
exit $status
fi
exec "$binary" "$@"