- hello: vet, every test and the console tests by name - docs: the charm decision note, TestDocsTree, docs:build --check, no application names, examples on every summer command - plugins: 15 swept repositories (vet, short, TestCommandsDocumented, GOWORK=off build) and the additive check on each sweep commit - apps: go build, summer build leaves main.go unchanged, list and help - landing: nuxt generate and its TAP suite, terminal and roadmap tests, sync-roadmap --check against origin/master - docker: serve lifecycle, migration checklist flags, token JSON - self-test: TAP detector, additive and NOT SWEPT rules on planted repos - go stage: the new surf, conga, lagoon, flare, dev and summer names
884 lines
34 KiB
Bash
Executable File
884 lines
34 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# check-phase19.8: fail-closed gate for Phase 19.8, the console commands
|
|
# overhaul. It proves the phase across this framework (diary, bonfire and the
|
|
# swept framework commands), the example application, the docs, every shared
|
|
# plugin swept in plan 03, every application that runs the new console, the
|
|
# landing site and the Docker-backed paths.
|
|
#
|
|
# Stages:
|
|
# self-test the go test -json and node --test (TAP) detectors on planted
|
|
# output (pass, fail, skip, a subtest-only pass, zero tests, "no
|
|
# tests to run", a missing required name), the additive check
|
|
# and the NOT SWEPT rule on planted git repositories, the docker
|
|
# NOT RUN rule on a faked PATH, and that every mode is wired.
|
|
# go go vet ./... and go test -short ./... here, the required
|
|
# smoke and unit tests by name per package, then go test -race
|
|
# -short of diary, sunscreen, bonfire and docsite.
|
|
# hello the example application (its own module in the workspace):
|
|
# vet, every test, and its console tests by name.
|
|
# docs the console decision note names the six charm modules,
|
|
# TestDocsTree, summer docs:build --check, no consuming
|
|
# application named in the console and logging sources and
|
|
# pages, and every command in summer's JSON listing has
|
|
# examples.
|
|
# plugins each swept shared plugin (PLUGINS_ROOT, and the user, golem
|
|
# and feedback checkouts under FONOTEKA): vet, short tests, its
|
|
# warn-only TestCommandsDocumented by name, a build without the
|
|
# workspace (GOWORK=off), and the additive check: in the diff of
|
|
# its sweep commit (found by subject) against its parent no
|
|
# removed line declares an exported func, method or type, and
|
|
# no removed Name: "<x>" literal (a command, flag or argument
|
|
# name) is missing from the added lines. A repository listed in
|
|
# SWEEP_OPTIONAL without a sweep commit prints NOT SWEPT; any
|
|
# other one without it fails. Only the short suites run here:
|
|
# the Docker suites of sitemap, faq, menu and the user plugin's
|
|
# updates package fail before the sweep too (deferred-items.md)
|
|
# and are not part of this gate.
|
|
# apps each application: go build ./... (binaries into a temporary
|
|
# directory), summer build (this checkout's summer) leaves the
|
|
# committed main.go and plugins.gen.go unchanged, and
|
|
# bin/<binary> list and help migrate exit 0.
|
|
# landing the landing site: nuxt generate, then its node --test suite
|
|
# (TAP: every test passes, none skipped or todo); the
|
|
# application's terminal Go tests by name (the end-to-end
|
|
# TestTerminalCommands only with SUMMERCMS_TERMINAL_CHECK=1,
|
|
# NOT RUN otherwise); the demo
|
|
# application's roadmap tests by name and
|
|
# scripts/sync-roadmap.sh --check against ROADMAP_REF (default
|
|
# origin/master: local master carries other sessions' unpushed
|
|
# planning commits).
|
|
# docker Docker host only: the testcontainers tests by name without
|
|
# -short (serve's lifecycle, the migration checklist flags, the
|
|
# admin token JSON).
|
|
#
|
|
# A Go stage exits non-zero on a failing command, or on a go test -json run
|
|
# that fails, skips a test (a subtest included), matches zero tests, prints
|
|
# "no tests to run" or misses a required name (a subtest pass never counts
|
|
# for its parent's name).
|
|
#
|
|
# --all runs self-test, go, hello, docs, plugins, apps and landing, then
|
|
# docker when a Docker daemon answers (NOT RUN docker otherwise, a failure
|
|
# with PHASE198_REQUIRE_DOCKER=1).
|
|
#
|
|
# Environment overrides (default: the sibling checkouts next to this
|
|
# repository): PHASE198_ROOT (this repository), PLUGINS_ROOT (the directory
|
|
# holding the sm-*-plugin checkouts), FONOTEKA (the application workspace
|
|
# that carries the user, golem and feedback plugin checkouts), DEMO_APP,
|
|
# BM_APP, GRZYBY_APP, SUMMERCMSIO_APP, ROADMAP_REF, PHASE198_REQUIRE_DOCKER.
|
|
set -euo pipefail
|
|
|
|
unset FORCE_COLOR
|
|
export NO_COLOR=1
|
|
|
|
ROOT="${PHASE198_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
PLUGINS_ROOT="${PLUGINS_ROOT:-$ROOT/..}"
|
|
FONOTEKA="${FONOTEKA:-$ROOT/../fono""teka.go}"
|
|
DEMO_APP="${DEMO_APP:-$ROOT/../sm-demo-app}"
|
|
BM_APP="${BM_APP:-$ROOT/../sm-bm-app}"
|
|
GRZYBY_APP="${GRZYBY_APP:-$ROOT/../sm-grzybyfunkcjonalne-app}"
|
|
SUMMERCMSIO_APP="${SUMMERCMSIO_APP:-$ROOT/../sm-summercmsio-app}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Required test names. Each list runs with -run '^(a|b|...)$' and every name
|
|
# must pass; a name that skips or does not run fails the stage.
|
|
|
|
# go stage (-short).
|
|
DIARY_TESTS=(
|
|
# Plan 01.
|
|
TestDiaryTracerSmoke TestConfigEnvSmoke TestDailyDegradesSmoke
|
|
# Plan 04: config rules, floors, verbosity, quiet (D-08, D-09).
|
|
TestConfigRules TestConfigEnvStrings TestDefaultConfig
|
|
TestComponentFloors TestVerbosityLevels TestQuietConsoleOnly
|
|
# Plan 04: daily files (D-10).
|
|
TestDailyRotation TestDailyRetention TestDailyPermissions
|
|
TestDailyUnwritable TestDailyLazyOpen TestPathConfinement
|
|
# Plan 04: redaction, the console handler, lifecycle, concurrency (D-12).
|
|
TestRedactionEveryDriver TestConsoleStripsControlChars
|
|
TestConsoleComponentPrefix TestConsoleStyledOnlyOnTerminal
|
|
TestConsoleLevelFromOptions TestForBeforeSetup TestResetRestoresDefault
|
|
TestNoRecursionWithoutState TestHideOnTTY TestConcurrentWrites
|
|
# Plan 04: testing/slogtest conformance.
|
|
TestSlogtestConsoleHandler TestSlogtestPipeline
|
|
)
|
|
SUNSCREEN_TESTS=(
|
|
# Plan 04: the compatibility entry point.
|
|
TestInstallDefault TestInstallDefaultUnchanged
|
|
)
|
|
BONFIRE_TESTS=(
|
|
# Plan 02.
|
|
TestListTracerSmoke TestHelpSmoke TestListJSONSmoke TestErrorRenderSmoke
|
|
TestWidgetMatrixSmoke
|
|
# Plan 04: global options, duplicates, Call (SC-2, D-13).
|
|
TestPeekGlobals TestGlobalsArgs TestGlobalFlagsAgree
|
|
TestReservedFlags TestDuplicateNames TestCallNoGlobalsHook
|
|
# Plan 04: goldens for list, help and errors (D-13, D-14, D-16, D-18).
|
|
TestListGolden TestListAllGolden TestListJSONGolden TestListStyledGolden
|
|
TestHelpGolden TestHelpPassthroughGolden TestHelpEqualsHelpFlag
|
|
TestErrorGolden TestExitCodes TestReportedSilent TestErrorScrubbed
|
|
TestWrongDirectoryHint TestCheckDocs TestVersionHeader
|
|
# Plan 04: widgets, live views and prompts (D-03, D-27, D-28).
|
|
TestWidgetMatrix TestLiveModels TestPromptsNoInteraction
|
|
TestSecretWithNoInteraction TestConfirmProceed TestPrintAbove
|
|
)
|
|
DOCSITE_TESTS=(
|
|
# Plan 04: the docs checker reads past the global options.
|
|
TestCommandWord TestCommandWordGlobals
|
|
)
|
|
CONGA_TESTS=(
|
|
# Plan 01.
|
|
TestRiverLoggerUsesDiarySmoke
|
|
# Plan 04 (D-09).
|
|
TestRiverLoggerComponent TestRiverLoggerPublishedOverride
|
|
# Plan 04: worker counters and queue:clear's production question
|
|
# (D-28, D-29).
|
|
TestWorkerStats TestQueueClearConfirm
|
|
)
|
|
LAGOON_TESTS=(
|
|
# Plan 01.
|
|
TestGormLoggerUsesDiarySmoke
|
|
# Plan 04 (D-09).
|
|
TestGormLoggerLevels
|
|
# Plan 04: the production question and key:generate (D-21, D-25, D-28).
|
|
TestConfirmProductionNonTTYProceeds TestConfirmProductionInteractiveNo
|
|
TestForceSkipsConfirm TestKeyGenerateJSON TestKeyGenerateQuiet
|
|
)
|
|
SURF_TESTS=(
|
|
# Plan 03.
|
|
TestServeSummarySmoke TestShutdownReportSmoke TestAccessLogSmoke
|
|
# Plan 04: summary, records, report, graceful stop, route:list --json
|
|
# (D-04, D-25, D-29).
|
|
TestServeSummaryLines TestServeRecords TestShutdownReportAborted
|
|
TestServeGracefulNil TestRouteListJSON
|
|
# Plan 04: the request log (D-23).
|
|
TestAccessLogRequestIDCap TestAccessLogDefaultOn TestAccessLogDisabled
|
|
TestAccessLogNeverLogsPath TestAccessLogComponentHTTP
|
|
)
|
|
FLARE_TESTS=(
|
|
# Plan 04: Reported failures (D-18).
|
|
TestFlareReported
|
|
)
|
|
DEV_TESTS=(
|
|
# Plan 04: summer dev runs serve (D-29).
|
|
TestStartBinServe TestDevReport TestDevGracefulNil
|
|
)
|
|
SUMMER_TESTS=(
|
|
# Plan 03.
|
|
TestCommandsDocumented TestDocsTree
|
|
# Plan 04: the delegates' help (D-17).
|
|
TestDelegateExamples
|
|
)
|
|
|
|
# hello stage.
|
|
HELLO_TESTS=(
|
|
TestBuiltBinaryListsCommands TestGreeterDocumented TestHelpListsServe
|
|
TestBuiltBinaryUnknownCommandExitsNonZero TestMigrateWithoutDSNFailsLoudly
|
|
)
|
|
|
|
# docker stage: testcontainers suites (Postgres).
|
|
DOCKER_SURF_TESTS=(TestServeLifecycle)
|
|
DOCKER_LAGOON_TESTS=(
|
|
TestPendingMigrations TestMigratePretend TestRollbackPretend
|
|
TestDeferredPurgePretend TestMigrateStatusJSON
|
|
)
|
|
DOCKER_CABANA_TESTS=(TestTokenCreateJSON TestTokenListJSON TestTokenCreateQuietKeepsToken)
|
|
|
|
# plugins stage: the swept repositories (19.8-03-SUMMARY.md, D-20), as
|
|
# "<label>|<path>".
|
|
PLUGIN_REPOS=(
|
|
"sm-paymentgateway-plugin|$PLUGINS_ROOT/sm-paymentgateway-plugin"
|
|
"sm-pgstripe-plugin|$PLUGINS_ROOT/sm-pgstripe-plugin"
|
|
"sm-oauthserver-plugin|$PLUGINS_ROOT/sm-oauthserver-plugin"
|
|
"sm-journal-plugin|$PLUGINS_ROOT/sm-journal-plugin"
|
|
"sm-sitemap-plugin|$PLUGINS_ROOT/sm-sitemap-plugin"
|
|
"sm-faq-plugin|$PLUGINS_ROOT/sm-faq-plugin"
|
|
"sm-menu-plugin|$PLUGINS_ROOT/sm-menu-plugin"
|
|
"sm-chat-plugin|$PLUGINS_ROOT/sm-chat-plugin"
|
|
"sm-chatchannels-plugin|$PLUGINS_ROOT/sm-chatchannels-plugin"
|
|
"sm-chatvideo-plugin|$PLUGINS_ROOT/sm-chatvideo-plugin"
|
|
"sm-forum-plugin|$PLUGINS_ROOT/sm-forum-plugin"
|
|
"sm-herald-plugin|$PLUGINS_ROOT/sm-herald-plugin"
|
|
"sm-golem-plugin|$FONOTEKA/plugins/golem15/golem"
|
|
"sm-feedback-plugin|$FONOTEKA/plugins/golem15/feedback"
|
|
"sm-user-plugin|$FONOTEKA/plugins/golem15/user"
|
|
)
|
|
# Repositories plan 03 may have deferred: without a sweep commit they print
|
|
# NOT SWEPT instead of failing. Plan 03 swept every repository (D-20
|
|
# complete), so the list is empty.
|
|
SWEEP_OPTIONAL=()
|
|
# The sweep commit's subject carries both strings.
|
|
SWEEP_SUBJECT=("console help" "(SummerCMS v")
|
|
|
|
# apps stage: "<label>|<path>".
|
|
APP_REPOS=(
|
|
"sm-demo-app|$DEMO_APP"
|
|
"sm-bm-app|$BM_APP"
|
|
"sm-grzybyfunkcjonalne-app|$GRZYBY_APP"
|
|
"sm-summercmsio-app|$SUMMERCMSIO_APP"
|
|
"fono""teka.go|$FONOTEKA"
|
|
)
|
|
|
|
# landing stage.
|
|
# TestTerminalCommands runs the landing page's commands against the deployed
|
|
# bootstrap script and needs network access, Docker and ports 54320 and
|
|
# 8080: it runs only with SUMMERCMS_TERMINAL_CHECK=1 and is NOT RUN otherwise.
|
|
LANDING_TERMINAL_TESTS=(TestTerminalRunScript TestLoadTerminal TestTerminalScript TestTerminalEnv)
|
|
ROADMAP_TESTS=(TestRoadmapParse TestRoadmapDeckOrder TestRoadmapNoClientNames TestRoadmapPolishTitles)
|
|
|
|
# docs stage: names of consuming applications (split so this file does not
|
|
# match itself), the decision note and the modules it must name.
|
|
APP_NAMES=("p[lł]yta""rium" "fono""teka" "wave""path" "horo""skopia" "bm\\.jakub""zych" "grzyby""funkcjonalne")
|
|
DECISION_NOTE=".planning/notes/2026-10-21-console-charm-stack.md"
|
|
DECISION_NAMES=(
|
|
"charm.land/lipgloss/v2" "github.com/charmbracelet/colorprofile" "charm.land/bubbletea/v2"
|
|
"charm.land/bubbles/v2" "charm.land/huh/v2" "charm.land/fang/v2"
|
|
)
|
|
NAME_SCOPE=(modules/diary modules/bonfire docs/console docs/services/logging.md)
|
|
|
|
usage() {
|
|
cat >&2 <<'EOF'
|
|
usage:
|
|
check-phase19.8.sh --self-test
|
|
check-phase19.8.sh go | hello | docs | plugins | apps | landing | docker
|
|
check-phase19.8.sh --go | --hello | --docs | --plugins | --apps | --landing | --docker
|
|
check-phase19.8.sh --all
|
|
EOF
|
|
exit 2
|
|
}
|
|
|
|
# detect_go reads go test -json. Exit 1 fail/build, 2 skip, 3 zero/no-tests,
|
|
# 4 non-JSON, 5 missing required name.
|
|
detect_go() {
|
|
python3 - "$1" <<'PY'
|
|
import json, os, sys
|
|
path = sys.argv[1]
|
|
require = [n for n in os.environ.get("REQUIRE_TESTS", "").split() if n]
|
|
passed = set()
|
|
failed = []
|
|
with open(path, encoding="utf-8", errors="replace") as fh:
|
|
for raw in fh:
|
|
line = raw.strip()
|
|
if not line.startswith("{"):
|
|
continue
|
|
try:
|
|
ev = json.loads(line)
|
|
except json.JSONDecodeError:
|
|
print("refuse: non-json test output", file=sys.stderr)
|
|
sys.exit(4)
|
|
action = ev.get("Action")
|
|
test = ev.get("Test") or ""
|
|
pkg = ev.get("Package") or ev.get("ImportPath") or ""
|
|
if action == "build-fail" or (action == "fail" and ev.get("FailedBuild")):
|
|
print(f"refuse: build failed {pkg}", file=sys.stderr)
|
|
sys.exit(1)
|
|
text = ev.get("Output") or ""
|
|
if action == "output" and "no tests to run" in text:
|
|
print(f"refuse: no tests to run in {pkg}", file=sys.stderr)
|
|
sys.exit(3)
|
|
if action == "skip" and test:
|
|
print(f"refuse: skipped {pkg} {test}", file=sys.stderr)
|
|
sys.exit(2)
|
|
if action == "fail":
|
|
failed.append(f"{pkg} {test}".strip())
|
|
if action == "pass" and test and "/" not in test:
|
|
passed.add(test)
|
|
if failed:
|
|
print("refuse: failed " + ", ".join(failed), file=sys.stderr)
|
|
sys.exit(1)
|
|
if not passed:
|
|
print("refuse: zero tests", file=sys.stderr)
|
|
sys.exit(3)
|
|
missing = [n for n in require if n not in passed]
|
|
if missing:
|
|
print("refuse: required tests did not pass: " + ", ".join(missing), file=sys.stderr)
|
|
sys.exit(5)
|
|
PY
|
|
}
|
|
|
|
# detect_tap reads node --test TAP output. Exit 1 a failed, skipped, todo
|
|
# or cancelled test (or a "not ok" line), 3 no passing summary.
|
|
detect_tap() {
|
|
python3 - "$1" <<'PY'
|
|
import re, sys
|
|
text = open(sys.argv[1], encoding="utf-8", errors="replace").read()
|
|
counts = {k: int(v) for k, v in re.findall(r"^# (tests|pass|fail|cancelled|skipped|todo) (\d+)\s*$", text, re.M)}
|
|
if re.search(r"^\s*not ok\b", text, re.M) or any(counts.get(k, 0) for k in ("fail", "cancelled", "skipped", "todo")):
|
|
print("refuse: TAP reports failed, cancelled, skipped or todo tests", file=sys.stderr)
|
|
sys.exit(1)
|
|
if counts.get("pass", 0) == 0 or counts.get("pass") != counts.get("tests"):
|
|
print("refuse: no all-passing TAP summary", file=sys.stderr)
|
|
sys.exit(3)
|
|
PY
|
|
}
|
|
|
|
# go_json DIR ARGS... runs go test -json ARGS in DIR and checks the stream
|
|
# with detect_go.
|
|
go_json() {
|
|
local dir="$1"
|
|
shift
|
|
local log err rc=0 dc=0
|
|
log="$(mktemp)"
|
|
err="$(mktemp)"
|
|
(cd "$dir" && go test -json "$@") >"$log" 2>"$err" || rc=$?
|
|
detect_go "$log" || dc=$?
|
|
if [[ "$rc" -ne 0 || "$dc" -ne 0 ]]; then
|
|
cat "$err" >&2 || true
|
|
grep -v '^{' "$log" | tail -n 40 >&2 || true
|
|
python3 -c 'import json,sys
|
|
for raw in open(sys.argv[1], errors="replace"):
|
|
try: ev = json.loads(raw)
|
|
except ValueError: continue
|
|
if ev.get("Action") == "output" and ("FAIL" in ev.get("Output", "") or "SKIP" in ev.get("Output", "") or "_test.go:" in ev.get("Output", "")):
|
|
sys.stderr.write(ev["Output"])' "$log" 2>&1 | tail -n 40 >&2 || true
|
|
rm -f "$log" "$err"
|
|
echo "refuse: go test $* in $dir (test=$rc detect=$dc)" >&2
|
|
return 1
|
|
fi
|
|
echo "go: $(grep -c '"Action":"pass","Package":"[^"]*","Test":"[^"/]*","Elapsed"' "$log") top-level tests passed in $dir ($*)"
|
|
rm -f "$log" "$err"
|
|
}
|
|
|
|
# go_named DIR PACKAGES FLAGS NAME... runs exactly the named top-level tests
|
|
# and requires every one of them to pass. PACKAGES and FLAGS are word-split.
|
|
go_named() {
|
|
local dir="$1" pkgs="$2" flags="$3"
|
|
shift 3
|
|
if [[ "$#" -eq 0 ]]; then
|
|
echo "refuse: go_named $pkgs in $dir without required names" >&2
|
|
return 1
|
|
fi
|
|
local names=("$@") regex
|
|
regex="^($(IFS='|'; echo "${names[*]}"))\$"
|
|
# shellcheck disable=SC2086
|
|
REQUIRE_TESTS="${names[*]}" go_json "$dir" $flags -count=1 -run "$regex" $pkgs
|
|
}
|
|
|
|
# go_short DIR PACKAGES runs go test -short PACKAGES in DIR; on failure it
|
|
# prints the tail of the output and fails.
|
|
go_short() {
|
|
local dir="$1" pkgs="${2:-./...}" out
|
|
# shellcheck disable=SC2086
|
|
if ! out="$(cd "$dir" && go test -count=1 -short $pkgs 2>&1)"; then
|
|
printf '%s\n' "$out" | grep -v '^ok ' | tail -n 60 >&2
|
|
echo "refuse: go test -short $pkgs in $dir" >&2
|
|
return 1
|
|
fi
|
|
echo "short: $dir ($pkgs)"
|
|
}
|
|
|
|
go_vet() {
|
|
local dir="$1"
|
|
shift
|
|
(cd "$dir" && go vet "$@")
|
|
echo "vet: $dir ($*)"
|
|
}
|
|
|
|
require_dir() {
|
|
[[ -d "$1" ]] || {
|
|
echo "refuse: $2 $1 not found" >&2
|
|
return 1
|
|
}
|
|
}
|
|
|
|
expect_detector() {
|
|
local detector="$1" name="$2" want="$3" payload="$4" log dc=0
|
|
log="$(mktemp)"
|
|
printf '%s\n' "$payload" >"$log"
|
|
"$detector" "$log" >/dev/null 2>&1 || dc=$?
|
|
rm -f "$log"
|
|
if [[ "$dc" -ne "$want" ]]; then
|
|
echo "refuse: self-test $detector $name: exit $dc, want $want" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# expect_mode NAME WANT_EXIT WANT_TEXT MODE ENV... runs this script in MODE
|
|
# with ENV and checks its exit status and that its output carries WANT_TEXT.
|
|
expect_mode() {
|
|
local name="$1" want="$2" text="$3" mode="$4"
|
|
shift 4
|
|
local out rc=0
|
|
out="$(env "$@" bash "${BASH_SOURCE[0]}" "$mode" 2>&1)" || rc=$?
|
|
if [[ "$rc" -ne "$want" ]] || ! grep -qF -- "$text" <<<"$out"; then
|
|
printf '%s\n' "$out" | tail -n 10 >&2
|
|
echo "refuse: self-test $name: exit $rc (want $want), output must carry \"$text\"" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# removed_exports reads a unified diff and prints every removed line that
|
|
# declares an exported func, method or type.
|
|
removed_exports() {
|
|
grep -E '^-(func ([A-Z]|\([^)]*\) [A-Z])|type [A-Z])' | grep -vE '^--- ' || true
|
|
}
|
|
|
|
# removed_names reads a unified diff and prints every Name: "<x>" literal
|
|
# (a command, flag or argument name) on a removed line that no added line
|
|
# carries.
|
|
removed_names() {
|
|
python3 -c '
|
|
import re, sys
|
|
removed, added = set(), set()
|
|
for line in sys.stdin:
|
|
if line.startswith(("---", "+++")):
|
|
continue
|
|
names = re.findall(r"\bName:\s*\"([^\"]+)\"", line)
|
|
if line.startswith("-"):
|
|
removed.update(names)
|
|
elif line.startswith("+"):
|
|
added.update(names)
|
|
for n in sorted(removed - added):
|
|
print(n)
|
|
'
|
|
}
|
|
|
|
# sweep_commit DIR prints the hash of the newest commit whose subject
|
|
# carries every SWEEP_SUBJECT string, or nothing.
|
|
sweep_commit() {
|
|
local args=() s
|
|
for s in "${SWEEP_SUBJECT[@]}"; do
|
|
args+=(--grep="$s")
|
|
done
|
|
git -C "$1" log -F --all-match "${args[@]}" --format=%H -n 1 HEAD
|
|
}
|
|
|
|
# additive_check LABEL DIR checks the sweep commit of the repository at DIR:
|
|
# no exported declaration and no Name literal removed. Without a sweep
|
|
# commit it prints NOT SWEPT when LABEL is in SWEEP_OPTIONAL and fails
|
|
# otherwise.
|
|
additive_check() {
|
|
local label="$1" dir="$2" sweep diff rc=0 removed names optional
|
|
sweep="$(sweep_commit "$dir")" || {
|
|
echo "refuse: git log in $dir failed" >&2
|
|
return 1
|
|
}
|
|
if [[ -z "$sweep" ]]; then
|
|
for optional in "${SWEEP_OPTIONAL[@]}"; do
|
|
if [[ "$optional" == "$label" ]]; then
|
|
echo "NOT SWEPT $label (no sweep commit; plan 03 deferred it)"
|
|
return 0
|
|
fi
|
|
done
|
|
echo "refuse: $label has no sweep commit (subject with: ${SWEEP_SUBJECT[*]})" >&2
|
|
return 1
|
|
fi
|
|
diff="$(git -C "$dir" diff "$sweep^" "$sweep" -- '*.go' ':(exclude)*_test.go' 2>&1)" || rc=$?
|
|
if [[ "$rc" -ne 0 ]]; then
|
|
printf '%s\n' "$diff" | tail -n 5 >&2
|
|
echo "refuse: git diff of the sweep commit $sweep in $dir failed (exit $rc)" >&2
|
|
return 1
|
|
fi
|
|
removed="$(removed_exports <<<"$diff")"
|
|
if [[ -n "$removed" ]]; then
|
|
printf '%s\n' "$removed" >&2
|
|
echo "refuse: $label's sweep commit ${sweep:0:7} removed or changed exported declarations" >&2
|
|
return 1
|
|
fi
|
|
names="$(removed_names <<<"$diff")"
|
|
if [[ -n "$names" ]]; then
|
|
printf '%s\n' "$names" >&2
|
|
echo "refuse: $label's sweep commit ${sweep:0:7} removed command, flag or argument names" >&2
|
|
return 1
|
|
fi
|
|
echo "additive: $label sweep ${sweep:0:7} removes no export and no command, flag or argument name"
|
|
}
|
|
|
|
# planted_repo DIR builds a git repository at DIR with a base commit (an
|
|
# exported func, a command name and a flag name), then the commit SUBJECT
|
|
# whose file content is BODY.
|
|
planted_repo() {
|
|
local dir="$1" subject="$2" body="$3"
|
|
mkdir -p "$dir"
|
|
git -C "$dir" init -q
|
|
git -C "$dir" config user.email gate@example.test
|
|
git -C "$dir" config user.name gate
|
|
printf '%s\n' 'package p' '' 'func Exported() {}' '' 'var c = Command{Name: "acme:sync", Flags: []Flag{{Name: "dry-run"}}}' >"$dir/p.go"
|
|
git -C "$dir" add p.go
|
|
git -C "$dir" commit -q -m "base"
|
|
printf '%s\n' "$body" >"$dir/p.go"
|
|
git -C "$dir" add p.go
|
|
git -C "$dir" commit -q -m "$subject"
|
|
}
|
|
|
|
run_self_test() {
|
|
bash -n "${BASH_SOURCE[0]}"
|
|
|
|
expect_detector detect_go pass 0 '{"Action":"pass","Package":"p","Test":"TestDiaryTracerSmoke"}'
|
|
expect_detector detect_go fail 1 '{"Action":"pass","Package":"p","Test":"TestA"}
|
|
{"Action":"fail","Package":"p","Test":"TestHelpSmoke"}'
|
|
expect_detector detect_go package-fail 1 '{"Action":"pass","Package":"p","Test":"TestA"}
|
|
{"Action":"fail","Package":"p"}'
|
|
expect_detector detect_go build 1 '{"Action":"build-fail","ImportPath":"p"}'
|
|
expect_detector detect_go skip 2 '{"Action":"pass","Package":"p","Test":"TestA"}
|
|
{"Action":"skip","Package":"p","Test":"TestServeLifecycle"}'
|
|
expect_detector detect_go subtest-skip 2 '{"Action":"skip","Package":"p","Test":"TestPeekGlobals/env"}
|
|
{"Action":"pass","Package":"p","Test":"TestPeekGlobals"}'
|
|
expect_detector detect_go subtest-only-pass 3 '{"Action":"pass","Package":"p","Test":"TestOuter/TestListTracerSmoke"}
|
|
{"Action":"pass","Package":"p"}'
|
|
expect_detector detect_go zero 3 '{"Action":"pass","Package":"p"}'
|
|
expect_detector detect_go no-tests 3 '{"Action":"output","Package":"p","Output":"testing: warning: no tests to run\n"}
|
|
{"Action":"pass","Package":"p"}'
|
|
expect_detector detect_go nonjson 4 '{"Action":"pass",'
|
|
REQUIRE_TESTS="TestDiaryTracerSmoke TestConfigEnvSmoke" expect_detector detect_go missing-named 5 \
|
|
'{"Action":"pass","Package":"p","Test":"TestDiaryTracerSmoke"}'
|
|
REQUIRE_TESTS="TestHelp" expect_detector detect_go prefix-is-not-the-test 5 \
|
|
'{"Action":"pass","Package":"p","Test":"TestHelpSmoke"}'
|
|
REQUIRE_TESTS="TestListTracerSmoke" expect_detector detect_go subtest-is-not-the-test 5 \
|
|
'{"Action":"pass","Package":"p","Test":"TestOuter/TestListTracerSmoke"}
|
|
{"Action":"pass","Package":"p","Test":"TestOther"}'
|
|
|
|
local tap_ok tap_fail tap_skip tap_todo tap_none
|
|
tap_ok=$'ok 1 - a\nok 2 - b\n1..2\n# tests 2\n# suites 0\n# pass 2\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 0'
|
|
tap_fail=$'ok 1 - a\nnot ok 2 - b\n1..2\n# tests 2\n# pass 1\n# fail 1\n# cancelled 0\n# skipped 0\n# todo 0'
|
|
tap_skip=$'ok 1 - a\nok 2 - b # SKIP\n1..2\n# tests 2\n# pass 1\n# fail 0\n# cancelled 0\n# skipped 1\n# todo 0'
|
|
tap_todo=$'ok 1 - a\nok 2 - b # TODO\n1..2\n# tests 2\n# pass 1\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 1'
|
|
tap_none=$'1..0\n# tests 0\n# pass 0\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 0'
|
|
expect_detector detect_tap pass 0 "$tap_ok"
|
|
expect_detector detect_tap fail 1 "$tap_fail"
|
|
expect_detector detect_tap skip 1 "$tap_skip"
|
|
expect_detector detect_tap todo 1 "$tap_todo"
|
|
expect_detector detect_tap zero 3 "$tap_none"
|
|
expect_detector detect_tap no-summary 3 'ok 1 - a'
|
|
|
|
# The NOT RUN rule, on a faked host: a docker binary that never answers.
|
|
local tmp
|
|
tmp="$(mktemp -d)"
|
|
mkdir -p "$tmp/bin"
|
|
printf '#!/bin/sh\nexit 1\n' >"$tmp/bin/docker"
|
|
chmod +x "$tmp/bin/docker"
|
|
expect_mode docker-not-run 0 "NOT RUN docker" --docker-gate PATH="$tmp/bin:$PATH" PHASE198_REQUIRE_DOCKER=0
|
|
expect_mode docker-required 1 "FAIL docker" --docker-gate PATH="$tmp/bin:$PATH" PHASE198_REQUIRE_DOCKER=1
|
|
expect_mode docker-stage-needs-daemon 1 "needs a reachable Docker daemon" --docker PATH="$tmp/bin:$PATH"
|
|
rm -rf "$tmp"
|
|
|
|
# The additive check on planted repositories: a sweep that adds is
|
|
# accepted; one that removes an export, a command name or a flag name is
|
|
# refused; a repository without a sweep commit prints NOT SWEPT only when
|
|
# it is optional.
|
|
local repos sweep_subject="feat: console help, examples and named loggers (SummerCMS v0.4.0)"
|
|
repos="$(mktemp -d)"
|
|
planted_repo "$repos/ok" "$sweep_subject" "$(printf '%s\n' 'package p' '' 'func Exported() {}' 'func Added() {}' '' 'var c = Command{' ' Name: "acme:sync",' ' Flags: []Flag{{Name: "dry-run"}, {Name: "json", Bare: true}},' '}')"
|
|
planted_repo "$repos/export" "$sweep_subject" "$(printf '%s\n' 'package p' '' 'func exported() {}' '' 'var c = Command{Name: "acme:sync", Flags: []Flag{{Name: "dry-run"}}}')"
|
|
planted_repo "$repos/command" "$sweep_subject" "$(printf '%s\n' 'package p' '' 'func Exported() {}' '' 'var c = Command{Name: "acme:synchronise", Flags: []Flag{{Name: "dry-run"}}}')"
|
|
planted_repo "$repos/flag" "$sweep_subject" "$(printf '%s\n' 'package p' '' 'func Exported() {}' '' 'var c = Command{Name: "acme:sync", Flags: []Flag{{Name: "pretend"}}}')"
|
|
planted_repo "$repos/unswept" "feat: something else" "$(printf '%s\n' 'package p' '' 'func Exported() {}')"
|
|
local case_name want rc
|
|
for case_name in ok:0 export:1 command:1 flag:1 unswept:1; do
|
|
want="${case_name#*:}"
|
|
case_name="${case_name%%:*}"
|
|
rc=0
|
|
additive_check "$case_name" "$repos/$case_name" >/dev/null 2>&1 || rc=$?
|
|
if [[ "$rc" -ne "$want" ]]; then
|
|
rm -rf "$repos"
|
|
echo "refuse: self-test additive_check $case_name: exit $rc, want $want" >&2
|
|
return 1
|
|
fi
|
|
done
|
|
local out
|
|
out="$(SWEEP_OPTIONAL=(unswept); additive_check unswept "$repos/unswept" 2>&1)" || {
|
|
rm -rf "$repos"
|
|
echo "refuse: self-test additive_check refused an optional unswept repository" >&2
|
|
return 1
|
|
}
|
|
if [[ "$out" != "NOT SWEPT unswept"* ]]; then
|
|
rm -rf "$repos"
|
|
echo "refuse: self-test additive_check did not print NOT SWEPT: $out" >&2
|
|
return 1
|
|
fi
|
|
rm -rf "$repos"
|
|
|
|
local mode
|
|
for mode in self-test go hello docs plugins apps landing docker all; do
|
|
grep -q -- "^ --$mode" "${BASH_SOURCE[0]}" || {
|
|
echo "refuse: missing mode --$mode" >&2
|
|
return 1
|
|
}
|
|
done
|
|
echo "phase19.8 self-test passed"
|
|
}
|
|
|
|
run_go() {
|
|
go_vet "$ROOT" ./...
|
|
go_short "$ROOT"
|
|
go_named "$ROOT" ./modules/diary -short "${DIARY_TESTS[@]}"
|
|
go_named "$ROOT" ./modules/sunscreen -short "${SUNSCREEN_TESTS[@]}"
|
|
go_named "$ROOT" ./modules/bonfire -short "${BONFIRE_TESTS[@]}"
|
|
go_named "$ROOT" ./internal/docsite -short "${DOCSITE_TESTS[@]}"
|
|
go_named "$ROOT" ./modules/conga -short "${CONGA_TESTS[@]}"
|
|
go_named "$ROOT" ./modules/lagoon -short "${LAGOON_TESTS[@]}"
|
|
go_named "$ROOT" ./modules/surf -short "${SURF_TESTS[@]}"
|
|
go_named "$ROOT" ./modules/flare -short "${FLARE_TESTS[@]}"
|
|
go_named "$ROOT" ./internal/dev -short "${DEV_TESTS[@]}"
|
|
go_named "$ROOT" ./cmd/summer -short "${SUMMER_TESTS[@]}"
|
|
local out
|
|
if ! out="$(cd "$ROOT" && go test -count=1 -race -short ./modules/diary/... ./modules/sunscreen/... ./modules/bonfire/... ./internal/docsite/... 2>&1)"; then
|
|
printf '%s\n' "$out" | tail -n 60 >&2
|
|
echo "refuse: go test -race -short of diary, sunscreen, bonfire and docsite" >&2
|
|
return 1
|
|
fi
|
|
echo "race: modules/diary, modules/sunscreen, modules/bonfire, internal/docsite"
|
|
echo "phase19.8 go passed"
|
|
}
|
|
|
|
run_hello() {
|
|
local hello="$ROOT/examples/hello"
|
|
require_dir "$hello" "example application"
|
|
go_vet "$hello" ./...
|
|
go_json "$hello" -count=1 ./...
|
|
go_named "$hello" . "" "${HELLO_TESTS[@]}"
|
|
echo "phase19.8 hello passed"
|
|
}
|
|
|
|
run_docs() {
|
|
local name
|
|
for name in "${DECISION_NAMES[@]}"; do
|
|
if ! grep -qF -- "$name" "$ROOT/$DECISION_NOTE"; then
|
|
echo "refuse: the decision note $DECISION_NOTE does not name $name" >&2
|
|
return 1
|
|
fi
|
|
done
|
|
echo "note: $DECISION_NOTE names the six charm modules"
|
|
go_named "$ROOT" ./cmd/summer "" TestDocsTree
|
|
(cd "$ROOT" && go run ./cmd/summer docs:build --check)
|
|
local pattern hits files=() scope
|
|
pattern="$(IFS='|'; echo "${APP_NAMES[*]}")"
|
|
for scope in "${NAME_SCOPE[@]}"; do
|
|
[[ -e "$ROOT/$scope" ]] || {
|
|
echo "refuse: $scope not found" >&2
|
|
return 1
|
|
}
|
|
while IFS= read -r -d '' f; do
|
|
files+=("$f")
|
|
done < <(find "$ROOT/$scope" -type f -print0)
|
|
done
|
|
hits="$(grep -inHIE "$pattern" "${files[@]}" || true)"
|
|
if [[ -n "$hits" ]]; then
|
|
printf '%s\n' "$hits" | head -n 20 >&2
|
|
echo "refuse: a console or logging source or page names a consuming application" >&2
|
|
return 1
|
|
fi
|
|
echo "names: ${#files[@]} files under ${NAME_SCOPE[*]} checked, no consuming application named"
|
|
local listing rc=0
|
|
listing="$(cd "$ROOT" && go run ./cmd/summer list --format=json)" || rc=$?
|
|
if [[ "$rc" -ne 0 ]]; then
|
|
echo "refuse: summer list --format=json failed (exit $rc)" >&2
|
|
return 1
|
|
fi
|
|
python3 -c '
|
|
import json, sys
|
|
listing = json.load(sys.stdin)
|
|
commands = listing.get("commands") or []
|
|
missing = [c["name"] for c in commands if not c.get("examples")]
|
|
if not commands or missing:
|
|
print("refuse: summer commands without examples: " + ", ".join(missing or ["(no commands)"]), file=sys.stderr)
|
|
sys.exit(1)
|
|
print(f"examples: every one of the {len(commands)} summer commands has examples")
|
|
' <<<"$listing"
|
|
echo "phase19.8 docs passed"
|
|
}
|
|
|
|
run_plugins() {
|
|
local entry label dir
|
|
for entry in "${PLUGIN_REPOS[@]}"; do
|
|
label="${entry%%|*}"
|
|
dir="${entry#*|}"
|
|
require_dir "$dir" "$label"
|
|
echo "== $label"
|
|
if ! {
|
|
go_vet "$dir" ./... &&
|
|
go_short "$dir" &&
|
|
go_named "$dir" . -short TestCommandsDocumented &&
|
|
(cd "$dir" && GOWORK=off go build ./...) &&
|
|
echo "build: GOWORK=off go build ./... in $dir" &&
|
|
additive_check "$label" "$dir"
|
|
}; then
|
|
echo "refuse: plugin $label" >&2
|
|
return 1
|
|
fi
|
|
done
|
|
echo "phase19.8 plugins passed (${#PLUGIN_REPOS[@]} repositories)"
|
|
}
|
|
|
|
run_apps() {
|
|
local tmp summer entry label dir binary dirty out
|
|
tmp="$(mktemp -d)"
|
|
summer="$tmp/summer"
|
|
(cd "$ROOT" && go build -o "$summer" ./cmd/summer)
|
|
for entry in "${APP_REPOS[@]}"; do
|
|
label="${entry%%|*}"
|
|
dir="${entry#*|}"
|
|
require_dir "$dir" "$label" || {
|
|
rm -rf "$tmp"
|
|
return 1
|
|
}
|
|
echo "== $label"
|
|
binary="$(sed -nE 's/^binary:[[:space:]]*"?([^"[:space:]]+)"?.*/\1/p' "$dir/summer.yaml" | head -n 1)"
|
|
if [[ -z "$binary" ]]; then
|
|
rm -rf "$tmp"
|
|
echo "refuse: $label/summer.yaml names no binary" >&2
|
|
return 1
|
|
fi
|
|
dirty="$(git -C "$dir" status --porcelain -- main.go plugins.gen.go)"
|
|
if [[ -n "$dirty" ]]; then
|
|
printf '%s\n' "$dirty" >&2
|
|
rm -rf "$tmp"
|
|
echo "refuse: $label has uncommitted generated files before the build" >&2
|
|
return 1
|
|
fi
|
|
mkdir -p "$tmp/$label"
|
|
if ! (cd "$dir" && go build -o "$tmp/$label/" ./...); then
|
|
rm -rf "$tmp"
|
|
echo "refuse: go build ./... in $label" >&2
|
|
return 1
|
|
fi
|
|
echo "build: go build ./... in $dir"
|
|
if ! out="$(cd "$dir" && "$summer" build 2>&1)"; then
|
|
printf '%s\n' "$out" | tail -n 30 >&2
|
|
rm -rf "$tmp"
|
|
echo "refuse: summer build in $label" >&2
|
|
return 1
|
|
fi
|
|
if ! git -C "$dir" diff --exit-code --stat -- main.go plugins.gen.go >&2; then
|
|
rm -rf "$tmp"
|
|
echo "refuse: summer build changed $label's committed main.go or plugins.gen.go (report it; plan 03 committed the regenerated main)" >&2
|
|
return 1
|
|
fi
|
|
echo "generated: summer build leaves $label's main.go and plugins.gen.go unchanged"
|
|
# The user plugin needs a JWT secret to boot; a throwaway value when
|
|
# the application does not set one.
|
|
if ! out="$(cd "$dir" && SUMMER_GOLEM15__USER__JWT__SECRET="${SUMMER_GOLEM15__USER__JWT__SECRET:-gate-only-throwaway-value-0123456789abcdef}" "./bin/$binary" list 2>&1)" ||
|
|
! grep -q '^Available commands:' <<<"$out"; then
|
|
printf '%s\n' "$out" | tail -n 30 >&2
|
|
rm -rf "$tmp"
|
|
echo "refuse: bin/$binary list in $label" >&2
|
|
return 1
|
|
fi
|
|
if ! out="$(cd "$dir" && SUMMER_GOLEM15__USER__JWT__SECRET="${SUMMER_GOLEM15__USER__JWT__SECRET:-gate-only-throwaway-value-0123456789abcdef}" "./bin/$binary" help migrate 2>&1)" ||
|
|
! grep -q '^Usage:' <<<"$out"; then
|
|
printf '%s\n' "$out" | tail -n 30 >&2
|
|
rm -rf "$tmp"
|
|
echo "refuse: bin/$binary help migrate in $label" >&2
|
|
return 1
|
|
fi
|
|
echo "boot: bin/$binary list and help migrate exit 0"
|
|
done
|
|
rm -rf "$tmp"
|
|
echo "phase19.8 apps passed (${#APP_REPOS[@]} applications)"
|
|
}
|
|
|
|
run_landing() {
|
|
local vue="$SUMMERCMSIO_APP/vue-summercmsio-app" log rc=0 dc=0
|
|
require_dir "$vue" "landing site"
|
|
require_dir "$DEMO_APP" "demo application"
|
|
log="$(mktemp)"
|
|
if ! npm --prefix "$vue" run generate >"$log" 2>&1; then
|
|
tail -n 30 "$log" >&2
|
|
rm -f "$log"
|
|
echo "refuse: npm run generate in $vue" >&2
|
|
return 1
|
|
fi
|
|
echo "generate: $vue/.output/public"
|
|
npm --prefix "$vue" run test >"$log" 2>&1 || rc=$?
|
|
detect_tap "$log" || dc=$?
|
|
if [[ "$rc" -ne 0 || "$dc" -ne 0 ]]; then
|
|
grep -E '^\s*not ok|^# ' "$log" | tail -n 30 >&2
|
|
rm -f "$log"
|
|
echo "refuse: npm run test in $vue (test=$rc detect=$dc)" >&2
|
|
return 1
|
|
fi
|
|
echo "tap: $(grep -E '^# (tests|pass) ' "$log" | tr '\n' ' ')"
|
|
rm -f "$log"
|
|
go_named "$SUMMERCMSIO_APP" . -short "${LANDING_TERMINAL_TESTS[@]}"
|
|
if [[ "${SUMMERCMS_TERMINAL_CHECK:-}" == "1" ]]; then
|
|
go_named "$SUMMERCMSIO_APP" . "-timeout 15m" TestTerminalCommands
|
|
else
|
|
echo "NOT RUN TestTerminalCommands (the deployed one-liner end to end: needs network, Docker and ports 54320 and 8080; set SUMMERCMS_TERMINAL_CHECK=1)"
|
|
fi
|
|
go_named "$DEMO_APP" ./plugins/demo/roadmap -short "${ROADMAP_TESTS[@]}"
|
|
local ref="${ROADMAP_REF:-origin/master}"
|
|
if ! ROADMAP_REF="$ref" "$DEMO_APP/scripts/sync-roadmap.sh" --check; then
|
|
echo "refuse: the demo roadmap snapshot differs from $ref" >&2
|
|
return 1
|
|
fi
|
|
echo "roadmap: snapshot matches $ref"
|
|
echo "phase19.8 landing passed"
|
|
}
|
|
|
|
docker_up() {
|
|
command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1
|
|
}
|
|
|
|
run_docker() {
|
|
if ! docker_up; then
|
|
echo "refuse: the docker stage needs a reachable Docker daemon (testcontainers); run it on a Docker host" >&2
|
|
return 1
|
|
fi
|
|
go_named "$ROOT" ./modules/surf "-timeout 10m" "${DOCKER_SURF_TESTS[@]}"
|
|
go_named "$ROOT" ./modules/lagoon "-timeout 10m" "${DOCKER_LAGOON_TESTS[@]}"
|
|
go_named "$ROOT" ./modules/cabana "-timeout 10m" "${DOCKER_CABANA_TESTS[@]}"
|
|
echo "phase19.8 docker passed"
|
|
}
|
|
|
|
# docker_gate is the docker step of --all: run the stage when a daemon
|
|
# answers, fail with PHASE198_REQUIRE_DOCKER=1, print NOT RUN otherwise.
|
|
docker_gate() {
|
|
if docker_up; then
|
|
if bash "${BASH_SOURCE[0]}" --docker; then
|
|
echo "PASS docker"
|
|
return 0
|
|
fi
|
|
echo "FAIL docker"
|
|
return 1
|
|
fi
|
|
if [[ "${PHASE198_REQUIRE_DOCKER:-0}" == "1" ]]; then
|
|
echo "FAIL docker: no reachable Docker daemon and PHASE198_REQUIRE_DOCKER=1"
|
|
return 1
|
|
fi
|
|
cat >&2 <<'EOF'
|
|
************************************************************************
|
|
NOT RUN docker: no reachable Docker daemon on this host.
|
|
The testcontainers suites (Postgres) of the framework did NOT run. Run on
|
|
a Docker host (through sudo -u golem when the socket is denied) before
|
|
/msd-verify-work:
|
|
bash scripts/check-phase19.8.sh docker
|
|
************************************************************************
|
|
EOF
|
|
echo "NOT RUN docker (Docker-host only; see the notice above)"
|
|
}
|
|
|
|
run_all() {
|
|
local stage
|
|
for stage in self-test go hello docs plugins apps landing; do
|
|
if bash "${BASH_SOURCE[0]}" "--$stage"; then
|
|
echo "PASS $stage"
|
|
else
|
|
echo "FAIL $stage"
|
|
exit 1
|
|
fi
|
|
done
|
|
docker_gate || exit 1
|
|
echo "Phase 19.8 gate passed"
|
|
}
|
|
|
|
case "${1:---all}" in
|
|
--self-test) run_self_test ;;
|
|
--go) run_go ;;
|
|
--hello) run_hello ;;
|
|
--docs) run_docs ;;
|
|
--plugins) run_plugins ;;
|
|
--apps) run_apps ;;
|
|
--landing) run_landing ;;
|
|
--docker) run_docker ;;
|
|
--docker-gate) docker_gate ;;
|
|
--all) run_all ;;
|
|
self-test) run_self_test ;;
|
|
go) run_go ;;
|
|
hello) run_hello ;;
|
|
docs) run_docs ;;
|
|
plugins) run_plugins ;;
|
|
apps) run_apps ;;
|
|
landing) run_landing ;;
|
|
docker) run_docker ;;
|
|
all) run_all ;;
|
|
*) usage ;;
|
|
esac
|