1 | #!/usr/bin/env bash
|
2 | #
|
3 | # Functions to invoke soil/web remotely.
|
4 | #
|
5 | # soil/web is deployed manually, and then this runs at HEAD in the repo. Every
|
6 | # CI run has an up-to-date copy.
|
7 | #
|
8 | # Usage:
|
9 | # soil/web-worker.sh <function name>
|
10 |
|
11 | set -o nounset
|
12 | set -o pipefail
|
13 | set -o errexit
|
14 |
|
15 | REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
|
16 |
|
17 | source soil/common.sh
|
18 | source test/tsv-lib.sh # tsv2html
|
19 | source web/table/html.sh # table-sort-{begin,end}
|
20 |
|
21 | # ~/
|
22 | # soil-web/ # executable files
|
23 | # doctools/
|
24 | # html_head.py
|
25 | # soil/
|
26 | # web.py
|
27 | # web.sh
|
28 | # travis-ci.oilshell.org/ # served over HTTP
|
29 | # index.html
|
30 | # web/
|
31 | # base.css
|
32 | # soil.css
|
33 | # github-jobs/
|
34 | # index.html
|
35 | # 3619/ # $GITHUB_RUN_NUMBER
|
36 | # dev-minimal.wwz
|
37 | # cpp-small.wwz
|
38 | # sourcehut-jobs/
|
39 | # index.html
|
40 | # 22/ # $JOB_ID
|
41 | # dev-minimal.wwz
|
42 | # 23 # $JOB_ID
|
43 | # cpp-small.wwz
|
44 |
|
45 | sshq() {
|
46 | # Don't need commands module as I said here!
|
47 | # https://www.oilshell.org/blog/2017/01/31.html
|
48 | #
|
49 | # This is Bernstein chaining through ssh.
|
50 |
|
51 | my-ssh $SOIL_USER_HOST "$(printf '%q ' "$@")"
|
52 | }
|
53 |
|
54 | remote-rewrite-jobs-index() {
|
55 | sshq soil-web/soil/web.sh rewrite-jobs-index "$@"
|
56 | }
|
57 |
|
58 | remote-cleanup-jobs-index() {
|
59 | local prefix=$1
|
60 | # clean it up for real!
|
61 | sshq soil-web/soil/web.sh cleanup-jobs-index "$prefix" false
|
62 | }
|
63 |
|
64 | remote-cleanup-status-api() {
|
65 | #sshq soil-web/soil/web.sh cleanup-status-api false
|
66 | # 2024-07 - work around bug by doing dry_run only.
|
67 | #
|
68 | # TODO: Fix the logic in soil/web.sh
|
69 |
|
70 | if false; then
|
71 | sshq soil-web/soil/web.sh cleanup-status-api true
|
72 | else
|
73 | curl --include --fail-with-body \
|
74 | --form 'run-hook=soil-cleanup-status-api' \
|
75 | --form 'arg1=true' \
|
76 | $WWUP_URL
|
77 | fi
|
78 | }
|
79 |
|
80 | my-scp() {
|
81 | scp -o StrictHostKeyChecking=no "$@"
|
82 | }
|
83 |
|
84 | my-ssh() {
|
85 | ssh -o StrictHostKeyChecking=no "$@"
|
86 | }
|
87 |
|
88 | scp-status-api() {
|
89 | local run_id=${1:-TEST2-github-run-id}
|
90 | local job_name=$2
|
91 |
|
92 | local status_file="_soil-jobs/$job_name.status.txt"
|
93 | local remote_path="$SOIL_REMOTE_DIR/status-api/github/$run_id/$job_name"
|
94 |
|
95 | # We could make this one invocation of something like:
|
96 | # cat $status_file | sshq soil/web.sh PUT $remote_path
|
97 |
|
98 | if false; then
|
99 | my-ssh $SOIL_USER_HOST "mkdir -p $(dirname $remote_path)"
|
100 |
|
101 | # the consumer should check if these are all zero
|
102 | # note: the file gets RENAMED
|
103 | my-scp $status_file "$SOIL_USER_HOST:$remote_path"
|
104 | else
|
105 | # Note: we don't need to change the name of the file, because we just glob
|
106 | # the dir
|
107 | curl --include --fail-with-body \
|
108 | --form 'payload-type=status-api' \
|
109 | --form "subdir=github/$run_id" \
|
110 | --form "file1=@$status_file" \
|
111 | $WWUP_URL
|
112 | fi
|
113 | }
|
114 |
|
115 | scp-results() {
|
116 | # could also use Travis known_hosts addon?
|
117 | local prefix=$1 # sourcehut- or ''
|
118 | shift
|
119 |
|
120 | my-scp "$@" "$SOIL_USER_HOST:$SOIL_REMOTE_DIR/${prefix}jobs/"
|
121 | }
|
122 |
|
123 | # Dummy that doesn't depend on results
|
124 | deploy-test-wwz() {
|
125 | set -x
|
126 | local out_name="$(date +%Y-%m-%d__%H-%M-%S)_test"
|
127 |
|
128 | local wwz=$out_name.wwz
|
129 |
|
130 | cat >index.html <<EOF
|
131 | <a href="build/oil-manifest.txt">build/oil-manifest.txt</a> <br/>
|
132 | <a href="build/opy-manifest.txt">build/opy-manifest.txt</a> <br/>
|
133 | <a href="env.txt">env.txt</a> <br/>
|
134 | EOF
|
135 |
|
136 | dump-env > env.txt
|
137 |
|
138 | zip -q $wwz env.txt index.html build/*.txt
|
139 |
|
140 | scp-results '' $wwz
|
141 | }
|
142 |
|
143 | format-wwz-index() {
|
144 | ### What's displayed in $ID.wwz/index.html
|
145 |
|
146 | local job_id=$1
|
147 | local tsv=${2:-_tmp/soil/INDEX.tsv}
|
148 |
|
149 | soil-html-head "$job_id.wwz" /uuu/web
|
150 |
|
151 | cat <<EOF
|
152 | <body class="width40">
|
153 | <p id="home-link">
|
154 | <a href="..">Up</a>
|
155 | | <a href="/">Home</a>
|
156 | | <a href="//oils.pub/">oils.pub</a>
|
157 | </p>
|
158 |
|
159 | <h1>$job_id.wwz</h1>
|
160 | EOF
|
161 |
|
162 | echo '<ul>'
|
163 | cat <<EOF
|
164 | <li>
|
165 | <a href="_tmp/soil/INDEX.tsv">_tmp/soil/INDEX.tsv</a>, also copied to
|
166 | <a href="../$job_id.tsv">../$job_id.tsv</a>.
|
167 | </li>
|
168 | <li>
|
169 | <a href="../$job_id.json">../$job_id.json</a>
|
170 | </li>
|
171 | EOF
|
172 |
|
173 | if test -f _tmp/soil/image.html; then
|
174 | echo '
|
175 | <li>
|
176 | <a href="_tmp/soil/image.html">Container Image Stats</a>
|
177 | </li>
|
178 | '
|
179 | fi
|
180 | echo '</ul>'
|
181 |
|
182 | cat <<EOF
|
183 | </body>
|
184 | </html>
|
185 | EOF
|
186 | }
|
187 |
|
188 | format-image-stats() {
|
189 | local soil_dir=${1:-_tmp/soil}
|
190 | local web_base_url=${2:-'/web'} # for production
|
191 |
|
192 | table-sort-html-head "Image Stats" $web_base_url
|
193 |
|
194 | # prints <body>; make it wide for the shell commands
|
195 | table-sort-begin "width60"
|
196 |
|
197 | # TODO:
|
198 | # - Format the TSV as an HTML table
|
199 | # - Save the name and tag and show it
|
200 |
|
201 | cat <<EOF
|
202 | <p id="home-link">
|
203 | <a href="/">Home</a>
|
204 | | <a href="//oils.pub/">oils.pub</a>
|
205 | </p>
|
206 |
|
207 | <h1>Images Tagged</h1>
|
208 |
|
209 | <a href="images-tagged.txt">images-tagged.txt</a> <br/>
|
210 |
|
211 | <h1>Image Layers</h1>
|
212 | EOF
|
213 |
|
214 | tsv2html3 $soil_dir/image-layers.tsv
|
215 |
|
216 | # First column is number of bytes; ignore header
|
217 | local total_bytes=$(awk '
|
218 | { sum += $1 }
|
219 | END { printf("%.1f", sum / 1000000) }
|
220 | ' $soil_dir/image-layers.tsv)
|
221 |
|
222 | echo "<p>Total Size: <b>$total_bytes MB</b></p>"
|
223 |
|
224 |
|
225 | cat <<EOF
|
226 | <h2>Raw Data</h2>
|
227 |
|
228 | <a href="image-layers.txt">image-layers.txt</a> <br/>
|
229 | <a href="image-layers.tsv">image-layers.tsv</a> <br/>
|
230 | EOF
|
231 |
|
232 | table-sort-end image-layers
|
233 | }
|
234 |
|
235 | make-job-wwz() {
|
236 | local job_id=${1:-test-job}
|
237 |
|
238 | local wwz=$job_id.wwz
|
239 |
|
240 | # Doesn't exist when we're not using a container
|
241 | if test -f _tmp/soil/image-layers.tsv; then
|
242 | format-image-stats _tmp/soil > _tmp/soil/image.html
|
243 | fi
|
244 |
|
245 | format-wwz-index $job_id > index.html
|
246 |
|
247 | # _tmp/soil: Logs are in _tmp, see soil/worker.sh
|
248 | # web/ : spec test HTML references this.
|
249 | # Note that that index references /web/{base,soil}.css, outside the .wwz
|
250 | # osh-summary.html uses table-sort.js and ajax.js
|
251 | #
|
252 | # TODO:
|
253 | # - Could move _tmp/{spec,stateful,syscall} etc. to _test
|
254 | # - Create _tmp/benchmarks/{compute,gc,gc-cachegrind,osh-parser,mycpp-examples,...}
|
255 | # - would require release/$VERSION/pub/benchmarks.wwz, like we have
|
256 | # pub/metrics.wwz, for consistent links
|
257 |
|
258 | zip -q -r $wwz \
|
259 | index.html \
|
260 | _build/wedge/logs \
|
261 | _gen/mycpp/examples \
|
262 | _test \
|
263 | _tmp/{soil,spec,src-tree-www,wild-www,stateful,process-table,syscall,benchmark-data,metrics,mycpp-examples,compute,gc,gc-cachegrind,perf,vm-baseline,osh-runtime,osh-parser,host-id,shell-id} \
|
264 | _tmp/uftrace/{index.html,stage2} \
|
265 | web/{base,src-tree,spec-tests,spec-cpp,line-counts,benchmarks,wild}.css web/ajax.js \
|
266 | web/table/table-sort.{css,js} \
|
267 | _release/oil*.tar _release/*.xshar _release/VERSION/
|
268 | }
|
269 |
|
270 | test-collect-json() {
|
271 | soil/collect_json.py _tmp/soil PATH
|
272 | }
|
273 |
|
274 | deploy-job-results() {
|
275 | ### Copy .wwz, .tsv, and .json to a new dir
|
276 |
|
277 | local prefix=$1 # e.g. github- for example.com/github-jobs/
|
278 | local run_dir=$2 # e.g. 1234 # make this dir
|
279 | local job_name=$3 # e.g. cpp-small for example.com/github-jobs/1234/cpp-small.wwz
|
280 | shift 2
|
281 | # rest of args are more env vars
|
282 |
|
283 | # writes $job_name.wwz
|
284 | make-job-wwz $job_name
|
285 |
|
286 | # Debug permissions. When using docker rather than podman, these dirs can be
|
287 | # owned by root and we can't write into them.
|
288 | ls -l -d _tmp/soil
|
289 | ls -l _tmp/soil
|
290 |
|
291 | date +%s > _tmp/soil/task-deploy-start-time.txt
|
292 |
|
293 | soil/collect_json.py _tmp/soil "$@" > $job_name.json
|
294 |
|
295 | # So we don't have to unzip it
|
296 | cp _tmp/soil/INDEX.tsv $job_name.tsv
|
297 |
|
298 | if false; then
|
299 | local remote_dest_dir="$SOIL_REMOTE_DIR/${prefix}jobs/$run_dir"
|
300 | my-ssh $SOIL_USER_HOST "mkdir -p $remote_dest_dir"
|
301 |
|
302 | # Do JSON last because that's what 'list-json' looks for
|
303 | my-scp $job_name.{wwz,tsv,json} "$SOIL_USER_HOST:$remote_dest_dir"
|
304 | else
|
305 | curl --include --fail-with-body \
|
306 | --form "payload-type=${prefix}jobs" \
|
307 | --form "subdir=$run_dir" \
|
308 | --form "file1=@${job_name}.wwz" \
|
309 | --form "file2=@${job_name}.tsv" \
|
310 | --form "file3=@${job_name}.json" \
|
311 | $WWUP_URL
|
312 | fi
|
313 |
|
314 | log ''
|
315 | log 'View CI results here:'
|
316 | log ''
|
317 | log "https://$SOIL_HOST/uuu/${prefix}jobs/$run_dir/"
|
318 | log "https://$SOIL_HOST/uuu/${prefix}jobs/$run_dir/$job_name.wwz/"
|
319 | log ''
|
320 | }
|
321 |
|
322 | publish-cpp-tarball() {
|
323 | local prefix=${1:-'github-'} # e.g. example.com/github-jobs/
|
324 |
|
325 | # Example of dir structure we need to cleanup:
|
326 | #
|
327 | # sourcehut-jobs/
|
328 | # git-$hash/
|
329 | # index.html
|
330 | # oils-for-unix.tar
|
331 | # github-jobs/
|
332 | # git-$hash/
|
333 | # oils-for-unix.tar
|
334 | #
|
335 | # Algorithm
|
336 | # 1. List all JSON, finding commit date and commit hash
|
337 | # 2. Get the OLDEST commit dates, e.g. all except for 50
|
338 | # 3. Delete all commit hash dirs not associated with them
|
339 |
|
340 | if false; then
|
341 | # Note: don't upload code without auth
|
342 | # TODO: Move it to a different dir.
|
343 |
|
344 | local commit_hash
|
345 | commit_hash=$(cat _tmp/soil/commit-hash.txt)
|
346 |
|
347 | local tar=_release/oils-for-unix.tar
|
348 | curl --include --fail-with-body \
|
349 | --form 'payload-type=github-jobs' \
|
350 | --form "subdir=git-$commit_hash" \
|
351 | --form "file1=@$tar" \
|
352 | $WWUP_URL
|
353 |
|
354 | log 'Tarball:'
|
355 | log ''
|
356 | log "https://$SOIL_HOST/code/github-jobs/git-$commit_hash/"
|
357 |
|
358 | else
|
359 | # Fix subtle problem here !!!
|
360 | shopt -s inherit_errexit
|
361 |
|
362 | local git_commit_dir
|
363 | git_commit_dir=$(git-commit-dir "$prefix")
|
364 |
|
365 | my-ssh $SOIL_USER_HOST "mkdir -p $git_commit_dir"
|
366 |
|
367 | # Do JSON last because that's what 'list-json' looks for
|
368 |
|
369 | local tar=_release/oils-for-unix.tar
|
370 |
|
371 | # Permission denied because of host/guest issue
|
372 | #local tar_gz=$tar.gz
|
373 | #gzip -c $tar > $tar_gz
|
374 |
|
375 | # Avoid race condition
|
376 | # Crappy UUID: seconds since epoch, plus PID
|
377 | local timestamp
|
378 | timestamp=$(date +%s)
|
379 |
|
380 | local temp_name="tmp-$timestamp-$$.tar"
|
381 |
|
382 | my-scp $tar "$SOIL_USER_HOST:$git_commit_dir/$temp_name"
|
383 |
|
384 | my-ssh $SOIL_USER_HOST \
|
385 | "mv -v $git_commit_dir/$temp_name $git_commit_dir/oils-for-unix.tar"
|
386 |
|
387 | log 'Tarball:'
|
388 | log ''
|
389 | log "https://$git_commit_dir"
|
390 | fi
|
391 |
|
392 | }
|
393 |
|
394 | remote-event-job-done() {
|
395 | ### "Client side" handler: a job calls this when it's done
|
396 |
|
397 | local prefix=$1 # 'github-' or 'sourcehut-'
|
398 | local run_id=$2 # $GITHUB_RUN_NUMBER or git-$hash
|
399 |
|
400 | log "remote-event-job-done $prefix $run_id"
|
401 |
|
402 | # Deployed code dir
|
403 | if false; then
|
404 | sshq soil-web/soil/web.sh event-job-done "$@"
|
405 | else
|
406 | # Note: I think curl does URL escaping of arg1= arg2= ?
|
407 | curl --include --fail-with-body \
|
408 | --form 'run-hook=soil-event-job-done' \
|
409 | --form "arg1=$prefix" \
|
410 | --form "arg2=$run_id" \
|
411 | $WWUP_URL
|
412 | fi
|
413 | }
|
414 |
|
415 | filename=$(basename $0)
|
416 | if test $filename = 'web-worker.sh'; then
|
417 | "$@"
|
418 | fi
|