Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
a61d910
ci: gates that fail on main: strict alloc budgets, Python tool pins, …
FumingPower3925 Oct 26, 2026
e0379d2
ci: CodSpeed runs on a pull request only with the performance label, …
FumingPower3925 Oct 26, 2026
b90001a
ci, test: io_uring base-tier job, full-size large-response job, probe…
FumingPower3925 Oct 26, 2026
234e18b
ci, test: the fixes for the gates of the first commit: postgres budge…
FumingPower3925 Oct 26, 2026
b56c8fd
ci: the base-tier allow-lists, measured on the first run of the job (…
FumingPower3925 Oct 26, 2026
e7a797c
DO NOT MERGE: bad inputs for the gates of items 87 and 92 (a misspell…
FumingPower3925 Oct 26, 2026
6793859
docs(contributing): the performance label also starts the CodSpeed ru…
FumingPower3925 Oct 26, 2026
d8a006e
Revert "DO NOT MERGE: bad inputs for the gates of items 87 and 92 (a …
FumingPower3925 Oct 26, 2026
08464be
ci: remove the temporary hosted twin of the CodSpeed gate (Refs #890 …
FumingPower3925 Oct 26, 2026
5556e7f
ci: the base-tier root allow-list names the probe-gated arms by patte…
FumingPower3925 Oct 26, 2026
3438221
ci: base-tier pattern evidence is the probe's own skip message, short…
FumingPower3925 Oct 26, 2026
a136253
ci: self-test rows that fail on the scripts of 34382216 (review r1 of…
FumingPower3925 Oct 27, 2026
a979806
ci, test: the tally follows NAME/CONT and wants the probe's message, …
FumingPower3925 Oct 27, 2026
c804f3d
Merge origin/main into l18-ci-tests (Refs #890)
FumingPower3925 Oct 27, 2026
bc707c6
ci: the base-tier job sets CELERIS_REQUIRE_ZONED_PEER=1, floors from …
FumingPower3925 Oct 27, 2026
e36f540
Merge branch 'main' into l18-ci-tests
FumingPower3925 Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/base-tier-skips-iouring.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Tests that skip at CELERIS_MAX_IOURING_TIER=base on purpose, for .github/scripts/base-tier-tally.sh
# (celeris#890 item 87): the full test or subtest name, then why. A name covers its own
# subtests. Measured on the run of the job iouring-base-tier cited in the pull request:
# 554 top-level tests passed, 4 skipped at the top level, 12 SKIP lines in all. On main
# 8936e89 (with #1123 and #1127) a docker run (kernel without an fe80:: address) gave 577
# passed and 13 SKIP lines, the 13th being TestListenOnAZonedLinkLocalServes885, which
# the job does not allow: it sets CELERIS_REQUIRE_ZONED_PEER=1, so the test fails rather
# than skips on a runner without one.
min-pass 540

# Tier base has no multishot recv with provided buffers (the tests say so in their skip).
TestSpentDetectBudgetReturnsItsRingBuffer/check no provided buffer ring at the base tier
TestSpentDetectBudgetReturnsItsRingBuffer/recheck no provided buffer ring at the base tier
TestSpentDetectBudgetReturnsItsRingBuffer/check-async no provided buffer ring at the base tier
TestSpentDetectBudgetReturnsItsRingBuffer/recheck-async no provided buffer ring at the base tier
TestStartupLineReportsTheMultishotRecvThatIsInUse/unset tier base has no multishot recv
TestStartupLineReportsTheMultishotRecvThatIsInUse/zero tier base has no multishot recv
TestStartupLineReportsTheMultishotRecvThatIsInUse/true-is-not-1 tier base has no multishot recv
TestStartupLineReportsTheMultishotRecvThatIsInUse/one tier base has no multishot recv
TestMultishotRecvDecidedOnceAtNew/on_at_New,_off_after tier base has no multishot recv
TestMultishotRecvDecidedOnceAtNew/off_at_New,_on_after tier base has no multishot recv

# They need net.ipv4.tcp_synack_retries=0; the runner has 5. The step "celeris#662
# tcp_synack_retries=0 guard" of the job adaptive sets it and forbids the skip.
TestPauseAcceptServesDeferredIdleConnectionsSynackRetriesZero needs net.ipv4.tcp_synack_retries=0
TestPauseAcceptNoGuardLosesAtSynackRetriesZero needs net.ipv4.tcp_synack_retries=0
25 changes: 25 additions & 0 deletions .github/base-tier-skips-root.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Tests that skip at CELERIS_MAX_IOURING_TIER=base on purpose, for .github/scripts/base-tier-tally.sh
# (celeris#890 item 87): the full test or subtest name, then why. A name covers its own
# subtests. Measured on the run of the job iouring-base-tier cited in the pull request:
# 107 top-level tests passed, 3 skipped at the top level, 19 SKIP lines in all (and
# 117 passed with 46 SKIP lines once main had brought in nine more tests of the kind below).
# On main 8936e89 (docker, merged tree): 118 passed, 46 SKIP lines, all allowed.
min-pass 105

# The probes of the tests that gate their io_uring arms (c714ProbeIOUring,
# keptProbeIOUring, h2_handler_return_889's) call io_uring "usable" only at the high
# tier with provided buffers, so with the cap their io_uring and adaptive arms skip with
# "io_uring tier=base kernel=...: no usable io_uring". The std and epoll arms of the
# same tests run. A new test of this kind joins them without an edit here, because the
# entry names the arms, not the tests (run 40419578127 met nine new ones that a merge
# with main brought in: the TestH1Unclosed...889 family); the arm must have logged the
# probe's own skip message ("io_uring tier=base kernel=...") while it was the current test, so an
# arm that starts an engine and skips for another reason still fails the step.
pattern:^.+/(io_uring|adaptive|adaptive-iouring)$ the io_uring, adaptive and adaptive-iouring arms of probe-gated tests

# Tier base has no multishot recv, and these two tests are about it.
TestHijackCopiesRequestValuesUnderMultishotRecv tier base has no multishot recv
TestMultishotRingSurvivesHijacks tier base has no multishot recv

# Its probe (keptProbeIOUring) calls io_uring usable only at the high tier: "no usable io_uring".
TestServerAsyncRouteDetachThenGoexitOnIOUring no usable io_uring at the base tier
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ updates:
commit-message:
prefix: "deps"

# ----- CI Python tools -----
# zizmor, the workflow security audit of the Lint job, is the one CI tool
# that is not a `tool` directive of .github/tools/go.mod. Its pin is
# .github/tools/requirements.txt, installed by `pip install -r`. The Lint step
# "Tool pins agree" (.github/scripts/tool-pins-check.sh, check 4) fails when a
# workflow names a `name==version` of its own, and when a requirements file
# under .github/ has no entry like this one. No `allow` rule: the file lists
# direct dependencies only. A bump of zizmor can add or change findings of the
# Lint job; the pull request shows them.
- package-ecosystem: pip
directory: /.github/tools
schedule:
interval: daily
groups:
ci-python-tools:
patterns:
- "*"
commit-message:
prefix: "deps"

# ----- GitHub Actions -----
# Watches every workflow under .github/workflows. Group all action
# version bumps into ONE PR per scheduled run.
Expand Down
113 changes: 113 additions & 0 deletions .github/scripts/base-tier-tally-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env bash
# Self-test of base-tier-tally.sh on fixture logs (celeris#890 item 87). Run from
# the repository root; the Lint job runs it, so a change to the tally that stops
# it failing on a skip nobody allowed, on a cap that did not take effect or on a
# test that is missing is a red Lint.
#
# BASE_TIER_TALLY=path the tally to test (default .github/scripts/base-tier-tally.sh)
set -uo pipefail

tally=${BASE_TIER_TALLY:-.github/scripts/base-tier-tally.sh}
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
fail=0
n=0
export BASE_TIER_SKIPS_DIR=$dir
printf '# header\nmin-pass 2\nTestNeedsHighTier needs a provided buffer ring\nTestDot.Name a name with a regular expression character\n' >"$dir/base-tier-skips-iouring.txt"
printf '# header\nmin-pass 1\npattern:^.+/(io_uring|adaptive)$ the arms of probe-gated tests\nTestNeedsHighTier needs a provided buffer ring\n' >"$dir/base-tier-skips-root.txt"

# expect NAME WANT_STATUS WANT_STDERR_SUBSTRING LABEL LOG [TESTS...]
expect() {
local name=$1 want=$2 sub=$3 status=0
shift 3
"$tally" "$@" >"$dir/out" 2>"$dir/err" || status=$?
n=$((n + 1))
if [ "$status" -ne "$want" ]; then
echo "FAIL $name: exit $status, want $want"; cat "$dir/out" "$dir/err"; fail=1
elif [ -n "$sub" ] && ! grep -qF -e "$sub" "$dir/err" "$dir/out"; then
echo "FAIL $name: output does not say '$sub':"; cat "$dir/out" "$dir/err"; fail=1
else
echo "ok $name"
fi
}

# good.log: the shape of an iouring log at the base tier. 806 and the tier test passed,
# one allowed test skipped (with a subtest).
good() {
printf '%s\n' \
'=== RUN TestShutdownDetachedPartialWriteIsFlushed806' '--- PASS: TestShutdownDetachedPartialWriteIsFlushed806 (0.10s)' \
'=== RUN TestReportTier844' \
' async_abort_844_engine_linux_test.go:160: celeris844 PROBE kernel=6.11.0 io_uring_tier=base CELERIS_MAX_IOURING_TIER="base"' \
'--- PASS: TestReportTier844 (0.00s)' \
'=== RUN TestNeedsHighTier' '=== RUN TestNeedsHighTier/sub' ' --- SKIP: TestNeedsHighTier/sub (0.00s)' '--- SKIP: TestNeedsHighTier (0.00s)' \
' async_abort_844_linux_test.go:518: celeris844 RESULT engine=io_uring tier=base arm=park inject=true' \
'PASS'
}
good >"$dir/good.log"
expect "good iouring log" 0 "" iouring "$dir/good.log" TestShutdownDetachedPartialWriteIsFlushed806 TestReportTier844
sed 's/NeedsHighTier/NeedsSomething/' "$dir/good.log" >"$dir/unlisted.log"
expect "a skip nobody allowed fails" 1 "TestNeedsSomething" iouring "$dir/unlisted.log" TestShutdownDetachedPartialWriteIsFlushed806
sed 's/tier=base arm=park/tier=high arm=park/' "$dir/good.log" >"$dir/high.log"
expect "an arm at tier=high fails" 1 "the cap did not take effect" iouring "$dir/high.log" TestShutdownDetachedPartialWriteIsFlushed806
# A name is literal: the dot of an allowed name is no wildcard.
{ cat "$dir/good.log"; printf '%s\n' '--- SKIP: TestDot.Name (0.00s)' '--- SKIP: TestDotXName (0.00s)'; } >"$dir/dot.log"
expect "a regular expression character in an allowed name is literal" 1 "TestDotXName" iouring "$dir/dot.log"
grep -v 'TestDotXName' "$dir/dot.log" >"$dir/dot-ok.log"
expect "the allowed name itself passes" 0 "" iouring "$dir/dot-ok.log"
grep -v 'celeris844 PROBE' "$dir/good.log" >"$dir/noprobe.log"
expect "a missing capped probe line fails" 1 "the cap did not take effect" iouring "$dir/noprobe.log" TestShutdownDetachedPartialWriteIsFlushed806
sed 's/CELERIS_MAX_IOURING_TIER="base"/CELERIS_MAX_IOURING_TIER=""/' "$dir/good.log" >"$dir/nocap.log"
expect "a probe line of an uncapped run fails" 1 "the cap did not take effect" iouring "$dir/nocap.log" TestShutdownDetachedPartialWriteIsFlushed806
grep -v 'TestShutdownDetachedPartialWriteIsFlushed806' "$dir/good.log" >"$dir/no806.log"
expect "a named test that never ran fails" 1 "TestShutdownDetachedPartialWriteIsFlushed806 ran 0" iouring "$dir/no806.log" TestShutdownDetachedPartialWriteIsFlushed806
sed 's/^--- PASS: TestShutdownDetachedPartialWriteIsFlushed806/--- SKIP: TestShutdownDetachedPartialWriteIsFlushed806/' "$dir/good.log" >"$dir/skip806.log"
expect "a named test that skipped fails" 1 "TestShutdownDetachedPartialWriteIsFlushed806" iouring "$dir/skip806.log" TestShutdownDetachedPartialWriteIsFlushed806
# min-pass: a run that lost its tests (a -run pattern that stopped matching).
grep -v 'TestShutdownDetachedPartialWriteIsFlushed806' "$dir/good.log" | sed 's/^--- PASS: TestReportTier844/--- XXX: TestReportTier844/' >"$dir/shrunk.log"
expect "fewer top-level PASS lines than min-pass fails" 1 "fewer than the 2" iouring "$dir/shrunk.log"
# The root label has no tier lines. A pattern entry allows an arm only while the log
# shows tier=base under that arm's RUN; a top-level test, or an arm that skips for
# another reason, is not allowed by it.
printf '%s\n' '=== RUN TestA' '--- PASS: TestA (0.00s)' 'PASS' >"$dir/root.log"
expect "root: a clean log" 0 "" root "$dir/root.log"
printf '%s\n' '=== RUN TestA' '--- SKIP: TestA (0.00s)' '--- PASS: TestB (0.00s)' 'PASS' >"$dir/rootskip.log"
expect "root: a skip no entry names fails" 1 "TestA" root "$dir/rootskip.log"
arm() { # arm NAME MESSAGE: a probe-gated test whose NAME arm skipped with MESSAGE
printf '%s\n' '=== RUN TestP' "=== RUN TestP/$1" " p_test.go:9: $2" '=== RUN TestP/std' '--- PASS: TestP (0.00s)' \
" --- SKIP: TestP/$1 (0.00s)" ' --- PASS: TestP/std (0.00s)' 'PASS'
}
arm io_uring 'io_uring tier=base kernel=6.17: no usable io_uring' >"$dir/arm.log"
expect "root: an arm that logged tier=base is allowed by the pattern" 0 "" root "$dir/arm.log"
# An arm that started an engine (the engine's INFO line says tier=base) and then skipped
# for another reason is not excused by the pattern.
printf '%s\n' '=== RUN TestP' '=== RUN TestP/io_uring' '2026/10/26 23:34:23 INFO io_uring engine selected tier=base multishot_accept=false' \
' p_test.go:9: the peer never connected' '--- PASS: TestP (0.00s)' ' --- SKIP: TestP/io_uring (0.00s)' >"$dir/arm-engine.log"
expect "root: an arm that started an engine and skipped for another reason fails" 1 "TestP/io_uring" root "$dir/arm-engine.log"
# Parallel subtests interleave their output and go test marks the switch with `=== NAME`
# (and `=== CONT` after a pause): a probe message printed under one arm must not be
# credited to another arm that skipped for a different reason (review r1 of #1124, m1).
printf '%s\n' '=== RUN TestP' '=== RUN TestP/io_uring' '=== PAUSE TestP/io_uring' '=== RUN TestQ' '=== RUN TestQ/io_uring' '=== PAUSE TestQ/io_uring' \
'=== CONT TestP/io_uring' ' p_test.go:9: io_uring tier=base kernel=6.17: no usable io_uring' \
'=== NAME TestQ/io_uring' ' q_test.go:12: listener never became ready' \
'--- PASS: TestP (0.00s)' ' --- SKIP: TestP/io_uring (0.00s)' '--- PASS: TestQ (0.00s)' ' --- SKIP: TestQ/io_uring (0.00s)' >"$dir/arm-interleaved.log"
expect "root: a probe message of one parallel arm does not excuse another's skip" 1 "TestQ/io_uring" root "$dir/arm-interleaved.log"
printf '%s\n' '=== RUN TestP' '=== RUN TestP/io_uring' '=== PAUSE TestP/io_uring' '=== RUN TestP/adaptive' '=== PAUSE TestP/adaptive' \
'=== CONT TestP/io_uring' ' p_test.go:9: io_uring tier=base kernel=6.17: no usable io_uring' \
'=== CONT TestP/adaptive' ' p_test.go:9: io_uring tier=base kernel=6.17: no usable io_uring' \
'--- PASS: TestP (0.00s)' ' --- SKIP: TestP/io_uring (0.00s)' ' --- SKIP: TestP/adaptive (0.00s)' >"$dir/arm-interleaved-ok.log"
expect "root: parallel arms that each logged the probe message are allowed" 0 "" root "$dir/arm-interleaved-ok.log"
# The pattern's evidence is the message the comments name, not any tier=base line of a probe:
# a Logf that merely says so, or another tier=base message, is no excuse (m2).
arm io_uring 'io_uring tier=base kernel=6.17: io_uring arms not run' >"$dir/arm-notrun.log"
expect "root: another tier=base message is not the probe's skip message" 1 "TestP/io_uring" root "$dir/arm-notrun.log"
arm io_uring 'the kernel has no io_uring' >"$dir/arm-other.log"
expect "root: an arm that skipped for another reason fails" 1 "TestP/io_uring" root "$dir/arm-other.log"
arm epoll 'io_uring tier=base kernel=6.17: no usable io_uring' >"$dir/arm-epoll.log"
expect "root: an arm the pattern does not name fails" 1 "TestP/epoll" root "$dir/arm-epoll.log"
printf '%s\n' '=== RUN io_uring' ' p_test.go:9: io_uring tier=base: no usable io_uring' '--- SKIP: io_uring (0.00s)' '--- PASS: TestB (0.00s)' >"$dir/arm-top.log"
expect "root: a top-level test named like an arm fails" 1 "SKIP: io_uring" root "$dir/arm-top.log"
expect "a label that is neither fails with usage" 2 "neither iouring nor root" other "$dir/root.log"
expect "an unreadable log is a usage error" 2 "cannot read" root "$dir/absent.log"

echo "$n cases"
exit "$fail"
105 changes: 105 additions & 0 deletions .github/scripts/base-tier-tally.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
# base-tier-tally.sh LABEL LOG [NAME...]: the tally of a `go test -v` log run
# with CELERIS_MAX_IOURING_TIER=base (celeris#890 item 87).
#
# LABEL is "iouring" (the log of ./internal/engine/iouring) or "root" (the log of
# the root package and ./test/server). Three checks, each failing the step:
#
# 1. No test skips except the ones the allow-list of that label names, each
# with the reason it may skip at the base tier. A SKIP line is matched by
# the test's full name, a subtest included (`--- SKIP: Test/sub (`), and
# an entry covers its own subtests. A skip is not coverage; a test that
# needs the high tier says so here.
# 2. Every NAME is a top-level test that ran once and PASSED, with no SKIP or
# FAIL line (named-tally.sh).
# 3. For "iouring": the cap took effect. TestReportTier844 printed the capped
# probe once as io_uring_tier=base with the variable set to "base", and no
# engine arm of the celeris#844 tests reported tier=high. A value the
# probe does not know counts as "none", which would skip every io_uring
# test and leave check 1 as the only witness; this one names the cause.
#
# 4. At least `min-pass` top-level tests passed (the data file of the label).
#
# Prints one summary line on stdout. Exit status 0 all hold, 1 some do not,
# 2 usage or an unreadable log.
set -euo pipefail

[ "$#" -ge 2 ] || { echo "usage: base-tier-tally.sh LABEL LOG [NAME...]" >&2; exit 2; }
label=$1
log=$2
shift 2
[ -r "$log" ] || { echo "base-tier-tally: cannot read $log" >&2; exit 2; }
here=$(cd "$(dirname "$0")" && pwd)

# Allowed skips: .github/base-tier-skips-LABEL.txt, one entry per line, then the reason
# (blank lines and # lines are ignored; BASE_TIER_SKIPS_DIR points elsewhere for the
# self-test). An entry is
# NAME a test or subtest by its full name; it covers its own subtests, or
# pattern:REGEX a skipped test or subtest whose full name matches REGEX, and only
# when the test itself logged the probe's skip message while it was the
# current test ("file_test.go:N: io_uring tier=base kernel=...: no
# usable io_uring"). The engine's own startup line ("INFO io_uring engine
# selected tier=base ...") does not count: an arm that started an
# engine and then skipped for another reason must still fail.
# min-pass N the top-level tests that must PASS at least, so that a -run pattern
# that stops matching (a rename) or a package that stops compiling its
# tests in cannot shrink the run unseen; the figure is measured.
case "$label" in
iouring | root) ;;
*) echo "base-tier-tally: label '$label' is neither iouring nor root" >&2; exit 2 ;;
esac
list_file=${BASE_TIER_SKIPS_DIR:-$here/..}/base-tier-skips-$label.txt
[ -r "$list_file" ] || { echo "base-tier-tally: cannot read $list_file" >&2; exit 2; }
# Names are literal (compared as strings below, not as regular expressions).
names=$(awk 'NF && $1 !~ /^#/ && $1 != "min-pass" && $1 !~ /^pattern:/ { print $1 }' "$list_file" | paste -sd'|' -)
patterns=$(awk 'NF && $1 ~ /^pattern:/ { sub(/^pattern:/, "", $1); print $1 }' "$list_file" | paste -sd'|' -)
minpass=$(awk '$1 == "min-pass" { print $2 }' "$list_file")

fail=0
skipped=$("$here"/skip-count.sh "$log") || fail=1
# Each SKIP line is judged by the name of the test: allowed by an exact entry (or as a
# subtest of one), or by a pattern while the test logged the probe's tier=base skip message.
unlisted=$(awk -v names="$names" -v pats="$patterns" 'BEGIN { nn = (names == "") ? 0 : split(names, nm, "|") }
# Parallel subtests interleave their output; go test names the owner of the lines that
# follow with RUN, and with NAME or CONT when it switches back to a test.
/^=== (RUN|NAME|CONT) / { cur = $3; next }
/^[[:space:]]+[A-Za-z0-9_.]+\.go:[0-9]+: .*io_uring tier=base kernel=.*: no usable io_uring/ && cur != "" { base[cur] = 1 }
/^[[:space:]]*--- SKIP: / {
n = $3
ok = 0
for (i = 1; i <= nn; i++) if (n == nm[i] || index(n, nm[i] "/") == 1) ok = 1
if (!ok && pats != "" && n ~ pats && base[n]) ok = 1
if (!ok) print
}
' "$log")
n_unlisted=0
[ -n "$unlisted" ] && n_unlisted=$(printf '%s\n' "$unlisted" | wc -l | tr -d '[:space:]')

tiers=
if [ "$label" = iouring ]; then
probe=$(grep -acE 'celeris844 PROBE .* io_uring_tier=base CELERIS_MAX_IOURING_TIER="base"$' "$log" || true)
high=$(grep -acE 'celeris844 RESULT engine=io_uring tier=high ' "$log" || true)
base=$(grep -acE 'celeris844 RESULT engine=io_uring tier=base ' "$log" || true)
tiers=", capped probe lines $probe (want 1), engine arms at tier=base $base (want > 0) and tier=high $high (want 0)"
if [ "$probe" -ne 1 ] || [ "$high" -ne 0 ] || [ "$base" -eq 0 ]; then
fail=1
echo "base-tier-tally: the cap did not take effect: CELERIS_MAX_IOURING_TIER=base is not the tier the io_uring arms ran on" >&2
fi
fi

passed=$(grep -acE '^--- PASS: Test' "$log" || true)
if [ -n "$minpass" ] && [ "$passed" -lt "$minpass" ]; then
fail=1
echo "base-tier-tally: $passed top-level tests passed, fewer than the $minpass of $list_file: did the run shrink (a renamed test, a -run pattern that no longer matches)?" >&2
fi
echo "base-tier-tally $label: top-level PASS $passed (min ${minpass:-none}), SKIP lines $skipped, not in the allow-list $n_unlisted$tiers"
if [ "$n_unlisted" -ne 0 ]; then
fail=1
printf '%s\n' "$unlisted" >&2
echo "base-tier-tally: tests skipped at the base tier that the allow-list of '$label' does not name;" >&2
echo "add a name only with the reason it must skip at this tier (a skip is not coverage)" >&2
fi
if [ "$#" -gt 0 ]; then
"$here"/named-tally.sh "base tier $label" "$log" "$@" || fail=1
fi
exit "$fail"
Loading
Loading