OILS / soil / host-shim.sh View on Github | oilshell.org

388 lines, 226 significant
1#!/usr/bin/env bash
2#
3# Shell functions run on the host machine, OUTSIDE the container.
4#
5# Usage:
6# soil/host-shim.sh <function name>
7#
8# Examples:
9# soil/host-shim.sh local-test-uke cpp-spec
10
11set -o nounset
12set -o pipefail
13set -o errexit
14
15REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
16
17source soil/common.sh
18source test/tsv-lib.sh
19
20live-image-tag() {
21 ### image ID -> Docker tag name
22 local image_id=$1
23
24 case $image_id in
25 (app-tests)
26 # rebuild with curl
27 echo 'v-2023-10-05'
28 ;;
29 (wild)
30 # rebuild with curl, then g++
31 echo 'v-2023-10-05a'
32 ;;
33 (bloaty)
34 # new image and task
35 echo 'v-2024-06-08'
36 ;;
37 (benchmarks)
38 # freshen up
39 echo 'v-2023-07-15'
40 ;;
41 (benchmarks2)
42 # debian 12, python3, new R-libs
43 echo 'v-2024-06-08b'
44 ;;
45 (cpp-spec)
46 # Rebuild with jq, procps
47 echo 'v-2023-07-17'
48 ;;
49 (pea)
50 # freshen up
51 echo 'v-2024-06-08'
52 ;;
53 (cpp-small)
54 # Rebuild with Docker, remove dead code
55 echo 'v-2023-07-15'
56 ;;
57 (clang)
58 # Rebuild with wedges
59 echo 'v-2023-08-09'
60 ;;
61 (ovm-tarball)
62 # freshen up, still need spec-bin wedge
63 echo 'v-2024-06-08'
64 ;;
65 (other-tests)
66 # freshen up
67 echo 'v-2023-07-15'
68 ;;
69 (dummy)
70 # freshen up
71 echo 'v-2024-06-08'
72 ;;
73 (dev-minimal)
74 # Use python3 wedge and mypy-0.780 repo
75 echo 'v-2023-07-15'
76 ;;
77
78 # Not run directly
79 (common)
80 # Rebuild with wedges
81 echo 'v-2023-02-28f'
82 ;;
83 (*)
84 die "Invalid image $image"
85 ;;
86 esac
87}
88
89make-soil-dir() {
90 log-context 'make-soil-dir'
91
92 mkdir --verbose -p _tmp/soil
93 ls -l -d . _tmp _tmp/soil
94
95 # Match what mount-perms does
96 chmod --changes 777 _tmp _tmp/soil
97 ls -l -d . _tmp _tmp/soil
98}
99
100show-disk-info() {
101 # Debug 'no space left on device' issue
102 echo 'DISKS'
103 df -h
104 echo
105
106 # Useful but many permissions errors
107 if false; then
108 echo 'SPACE FOR IMAGES?'
109 du --si -s ~/.local/share/ || true
110 echo
111 fi
112}
113
114podman-prune() {
115 ### Should this work on Debian?
116
117 if ! command -v podman; then
118 echo 'no podman'
119 return
120 fi
121
122 echo 'IMAGES'
123 podman images --all
124 echo
125
126 if false; then
127 # This causes an interactive prompt
128 echo 'PRUNE'
129 podman system prune || true
130 echo
131
132 show-disk-info
133
134 echo 'PRUNE AS ROOT'
135 sudo podman system prune || true
136 echo
137
138 show-disk-info
139 fi
140}
141
142mount-perms() {
143 ### Ensure that the guest can write to bind mount
144
145 local repo_root=$1
146
147 #show-disk-info
148
149 log-context 'mount-perms'
150
151 # We have to chmod all dirs because 'build/py.sh all' creates
152 # build/temp.linux-*, for example. Also can't exclude .git/ because
153 # submodules need it.
154 time find "$repo_root" -type d -a -print \
155 | xargs -d $'\n' -- chmod --changes 777 \
156 | wc -l
157 echo
158}
159
160job-reset() {
161 ### Called between jobs
162
163 #show-disk-info
164
165 log-context 'job-reset'
166
167 # The VM runs as the 'build' user on sourcehut. The podman container runs as
168 # 'uke' user, which apparently gets UID 100999.
169 #
170 # Running as 'build', we can't remove files created by the guest, so use
171 # 'sudo'.
172 #
173 # It's really these three dirs.
174 # ls -l -d _tmp/soil _tmp/soil/logs _devbuild/bin || true
175
176 sudo $0 mount-perms $PWD
177 echo
178
179 git status .
180 echo
181
182 # Similar to functions in 'build/clean.sh'
183 local -a dirs=(_tmp _bin _build _devbuild _test)
184 #local -a dirs=(_tmp)
185
186 log 'Removing temp dirs'
187 log ''
188
189 du --si -s "${dirs[@]}" || true
190 rm -r -f "${dirs[@]}"
191 echo
192
193 show-disk-info
194}
195
196save-image-stats() {
197 local soil_dir=${1:-_tmp/soil}
198 local docker=${2:-docker}
199 local image=${3:-oilshell/soil-dummy}
200 local tag=${4:-latest}
201
202 # TODO: write image.json with the name and tag?
203
204 mkdir -p $soil_dir
205
206 # NOTE: Works on my dev machine, but produces an empty table on CI?
207 $docker images "$image:v-*" > $soil_dir/images-tagged.txt
208 log "Wrote $soil_dir/images-tagged.txt"
209
210 $docker history $image:$tag > $soil_dir/image-layers.txt
211 log "Wrote $soil_dir/image-layers.txt"
212
213 # NOTE: Works with docker but not podman! podman doesn't support --format ?
214 {
215 # --human=0 gives us raw bytes and ISO timestamps
216 # --no-trunc shows the full command line
217 echo $'num_bytes\tcreated_at\tcreated_by'
218 $docker history --no-trunc --human=0 --format '{{.Size}}\t{{.CreatedAt}}\t{{.CreatedBy}}' $image:$tag
219 } > $soil_dir/image-layers.tsv
220 log "Wrote $soil_dir/image-layers.tsv"
221
222 # TODO: sum into image-layers.json
223 # - total size
224 # - earliest and layer date?
225
226 here-schema-tsv >$soil_dir/image-layers.schema.tsv <<EOF
227column_name type
228num_bytes integer
229created_at string
230created_by string
231EOF
232
233 log "Wrote $soil_dir/image-layers.schema.tsv"
234}
235
236run-job-uke() {
237 local docker=$1 # docker or podman
238 local repo_root=$2
239 local job_name=$3 # e.g. dev-minimal
240 local debug_shell=${4:-}
241
242 log-context 'run-job-uke'
243
244 # Do this on the HOST because we write the pull time into it as well. It's
245 # shared between guest and host.
246 make-soil-dir
247 local soil_dir=$repo_root/_tmp/soil
248
249 local -a flags=()
250
251 local image_id=$job_name
252
253 # Some jobs don't have their own image, and some need docker -t
254 case $job_name in
255 app-tests)
256 # to run ble.sh tests
257 flags=( -t )
258 ;;
259 cpp-coverage)
260 image_id='clang'
261 ;;
262 cpp-tarball)
263 image_id='cpp-small'
264 ;;
265 interactive)
266 # to run 'interactive-osh' with job control enabled
267 flags=( -t )
268
269 # Reuse for now
270 image_id='benchmarks'
271 ;;
272 esac
273
274 local image="docker.io/oilshell/soil-$image_id"
275
276 local tag=$(live-image-tag $image_id)
277
278 local pull_status
279 # Use external time command in POSIX format, so it's consistent between hosts
280 set -o errexit
281 command time -p -o $soil_dir/image-pull-time.txt \
282 $docker pull $image:$tag
283 pull_status=$?
284 set +o errexit
285
286 if test $pull_status -ne 0; then
287 log "$docker pull failed with status $pull_status"
288
289 # Save status for a check later
290 mkdir -p _soil-jobs
291 echo "$pull_status" > _soil-jobs/$job_name.status.txt
292
293 # Return success
294 return
295 fi
296
297 save-image-stats $soil_dir $docker $image $tag
298
299 show-disk-info
300
301 podman-prune
302
303 local -a args
304 if test -n "$debug_shell"; then
305 # launch interactive shell
306 flags+=( -i -t )
307
308 # So we can run GDB
309 # https://stackoverflow.com/questions/35860527/warning-error-disabling-address-space-randomization-operation-not-permitted
310 flags+=( --cap-add SYS_PTRACE --security-opt seccomp=unconfined )
311
312 # can mount other tools for debugging, like clang
313 #local clang_dir=~/git/oilshell/oil_DEPS/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04
314 #flags+=( --mount "type=bind,source=$clang_dir,target=/home/uke/oil_DEPS/$(basename $clang_dir)" )
315
316 args=(bash)
317 else
318 args=(sh -c "cd /home/uke/oil; soil/worker.sh JOB-$job_name")
319 fi
320
321 $docker run "${flags[@]}" \
322 --mount "type=bind,source=$repo_root,target=/home/uke/oil" \
323 $image:$tag \
324 "${args[@]}"
325}
326
327did-all-succeed() {
328 ### Check if the given jobs succeeded
329
330 local max_status=0
331 for job_name in "$@"; do
332 local status
333 read status unused_job_id < "_soil-jobs/$job_name.status.txt"
334
335 echo "$job_name status: $status"
336 if test $status -gt $max_status; then
337 max_status=$status
338 fi
339 done
340
341 log ''
342 log "Exiting with max job status $max_status"
343
344 return "$max_status"
345}
346
347local-test-uke() {
348 ### Something I can run locally. This is fast.
349
350 # Simulate sourcehut with 'local-test-uke dummy dummy'
351 local job_name=${1:-dummy}
352 local job2=${2:-}
353 local debug_shell=${3:-} # add 'bash' to change it to a debug shell
354 local docker=${4:-docker}
355
356 local branch=$(git rev-parse --abbrev-ref HEAD)
357
358 local fresh_clone=/tmp/soil-$job_name
359 rm -r -f -v $fresh_clone
360
361 local this_repo=$PWD
362 git clone $this_repo $fresh_clone
363 cd $fresh_clone
364 git checkout $branch
365
366 sudo $0 mount-perms $fresh_clone
367 sudo $0 run-job-uke "$docker" $fresh_clone $job_name "$debug_shell"
368
369 # Run another job in the same container, to test interactions
370
371 if test -n "$job2"; then
372 $0 job-reset
373 sudo $0 run-job-uke "$docker" $fresh_clone $job2
374 fi
375}
376
377local-shell() {
378 local job_name=${1:-cpp}
379
380 # no job 2
381 local-test-uke $job_name '' bash
382}
383
384cleanup() {
385 sudo rm -r -f -v _tmp/soil /tmp/soil-*
386}
387
388"$@"