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

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