diff --git a/.golangci.yaml b/.golangci.yaml index 23ade79..447ea76 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -16,6 +16,7 @@ linters: - goconst - gocyclo - gocognit + - dupl - unconvert - unparam - prealloc @@ -53,6 +54,21 @@ linters: # here; a genuinely path-heavy-but-flat function may carry an explained # //nolint:gocyclo (nolintlint requires the explanation). min-complexity: 15 + dupl: + # Cross-file duplication was the one quality axis nothing measured. The + # tool's default of 150 tokens over-fires on idiomatic Go (error-handling + # chains, flat switch bodies); 200 was chosen by measurement, not taste. + # VALIDATED AGAINST KNOWN FINDINGS before being enabled, which the fleet's + # vacuous-gate rule requires: at 200 with tests exempt a sweep of 37 Go + # repos reported exactly 2 findings, both the insertLines/deleteLines pair + # in web-terminal-engine/vt/csi.go. Those were resolved by extracting the + # shared prologue (prepareLineShift), and the fleet then measured clean. + # So this gate now guards against future drift rather than clearing a + # backlog. Duplication is genuinely rare here; do not read a green result + # as the linter being broken. Tests are exempt below for the same reason + # gocyclo/gocognit are: table-driven subtests are structurally repetitive + # by design. + threshold: 200 goconst: ignore-tests: true perfsprint: @@ -89,6 +105,7 @@ linters: # smell catalog (Eager Test, Conditional Test Logic) plus human review. - path: _test\.go linters: + - dupl - gocyclo - gocognit - errcheck diff --git a/tests/image-smoke.sh b/tests/image-smoke.sh index f27da8d..2e763c9 100644 --- a/tests/image-smoke.sh +++ b/tests/image-smoke.sh @@ -36,6 +36,15 @@ # wait shares the SMOKE_TIMEOUT deadline: the container must # be healthy AND have logged the pattern before it expires. # +# A .conf may also override smoke_verify() (default: no-op) for app-specific +# assertions that need the RUNNING healthy container - e.g. asserting that +# every target of a served importmap answers 200, which no static check can +# prove because the targets are produced during the image build. It runs once, +# after health (and SMOKE_LOG_PATTERN, when set), with $SMOKE_CONTAINER holding +# the container name; a non-zero return fails the smoke test. The harness never +# publishes ports, so probe from INSIDE the container (`docker exec +# "$SMOKE_CONTAINER" curl ...`) rather than assuming host reachability. +# # A .conf that creates host state of its own (a `mktemp -d` fixture dir, a # generated key) overrides the smoke_cleanup() function to remove it; the # harness's EXIT trap calls it after removing the container, so acquisition and @@ -68,6 +77,13 @@ SMOKE_LOG_PATTERN="" smoke_cleanup() { : } +# Default post-health verification hook: a .conf overrides it for assertions +# that need the running healthy container (see the header). Same +# define-before-source shape as smoke_cleanup. +# shellcheck disable=SC2329 # invoked only when health is reached +smoke_verify() { + : +} CONF="$SMOKE_DIR/image-smoke.conf" if [ -f "$CONF" ]; then # shellcheck disable=SC1090 # per-app config path, resolved at runtime @@ -131,6 +147,15 @@ while [ "$(date +%s)" -lt "$deadline" ]; do sleep 1 continue fi + # App-specific verification against the running container, once, after + # health. A failure is a real verdict, not a retry: health said up, so + # anything smoke_verify finds missing is missing from the image. + # shellcheck disable=SC2034 # consumed by the sourced .conf's smoke_verify + SMOKE_CONTAINER="$NAME" + if ! smoke_verify; then + printf 'FAIL: %s smoke_verify failed (see output above)\n' "$APP" >&2 + exit 1 + fi printf '%s image smoke: ok (healthy after %ss)\n' "$APP" "$(($(date +%s) - start))" exit 0 ;;