Merge pull request 'The catalogue's check type-checks and tests every touched TypeScript module' (#151) from check/typescript-modules-tested into main

This commit was merged in pull request #151.
This commit is contained in:
2026-10-09 07:29:35 +00:00
2 changed files with 101 additions and 3 deletions
+96
View File
@@ -0,0 +1,96 @@
#!/bin/sh
# The catalogue's TypeScript part of its own check (novox/hq issue 302's `mesh-check-also`), declared in
# merge-check.sh's header and run by the build seat in the TypeScript toolchain, after the Go part. The media
# catalogue's twin is issue 311. Before this, a TypeScript module's change ran nothing of its own before it
# merged: the Go part said "NOT TESTED HERE", and a type error in the forge module would first have shown in
# its walk.
#
# For every touched module that has a package.json, in a copy of it outside the clone:
#
# 1. its dependencies are the toolchain's own (/app/node_modules: the mesh's SDK, Node's types and what the
# runtime carries), copied beside it, never fetched: the check holds no credential for the package
# registry. A module that depends on a package the toolchain does not hold is said NOT CHECKED, by name;
# 2. every source that is not a test is type-checked with the toolchain's compiler and the flags a build
# compiles with — the same check the build makes, before the merge rather than in the walk;
# 3. its tests (test/*.test.ts) run under Node's own runner with types stripped, as its own `npm test` does
# after its build: the sources are compiled to dist/ and beside themselves first, so a test that imports
# the compiled module, or a source that imports its sibling by its .js name, finds it.
#
# Said, never passed silently: a module with no tests is said NOT TESTED on every run that touches it.
#
# By hand: MESH_CHECK_MODULES=gitea sh merge-check-typescript.sh, in the toolchain image (it reads
# /app/node_modules), or with TOOLCHAIN_MODULES pointing at a node_modules that holds the same.
set -eu
deps="${TOOLCHAIN_MODULES:-/app/node_modules}"
tsc="$deps/typescript/bin/tsc"
if [ ! -f "$tsc" ]; then
echo "NOT CHECKED: no TypeScript compiler at $tsc — this part runs in the TypeScript toolchain"
exit 1
fi
# The modules the change reaches: the planner's answer (hq ADR 0238), and every module a changed file is in —
# a change to a module's tests alone moves no build (hq ADR 0267), and its tests must still run.
touched=$({
printf '%s\n' "${MESH_CHECK_MODULES:-}" | tr ',' '\n' | sed -e 's#^modules/##' -e '/\//d'
printf '%s\n' "${MESH_CHECK_CHANGED:-}" | tr ',' '\n' | sed -n 's#^modules/\([^/]*\)/.*#\1#p'
} | sed '/^$/d' | sort -u)
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
failed=""
for m in $touched; do
d="modules/$m"
[ -f "$d/package.json" ] || continue
copy="$work/$m"
mkdir -p "$copy"
(cd "$d" && tar --exclude=./node_modules --exclude=./dist -cf - .) | (cd "$copy" && tar -xf -)
# What it depends on, against what the toolchain holds.
missing=$(node -e '
const p = require(process.argv[1]); const have = require("fs").readdirSync(process.argv[2]);
const scoped = (n) => n.startsWith("@") ? n.split("/")[0] : n;
const names = Object.keys({ ...(p.dependencies || {}), ...(p.devDependencies || {}) });
const held = (n) => have.includes(n) || (n.startsWith("@") && have.includes(scoped(n)) &&
require("fs").existsSync(process.argv[2] + "/" + n));
console.log(names.filter((n) => n !== "typescript" && !held(n)).join(" "));' "$copy/package.json" "$deps")
if [ -n "$missing" ]; then
echo "NOT CHECKED: $d depends on $missing, which the toolchain does not hold and the check cannot fetch"
continue
fi
mkdir -p "$copy/node_modules"
for p in "$deps"/*; do
case "$(basename "$p")" in typescript | esbuild | @esbuild) continue ;; esac
cp -r "$p" "$copy/node_modules/"
done
sources=$(cd "$copy" && find . -name '*.ts' ! -name '*.d.ts' ! -path './test/*' ! -path './node_modules/*' ! -path './dist/*' | sort)
if [ -n "$sources" ]; then
echo "type-checking $d (TypeScript)"
# shellcheck disable=SC2086 # the module's sources, none with a space in its name
if ! (cd "$copy" && node "$tsc" --noEmit --module NodeNext --moduleResolution NodeNext --target ES2022 \
--types node $sources); then
failed="$failed $m"
continue
fi
else
echo "$d has no TypeScript sources of its own to type-check: its tests read its files"
fi
if ! ls "$copy"/test/*.test.ts >/dev/null 2>&1; then
echo "NOT TESTED: $d is TypeScript and has no tests of its own"
continue
fi
if [ -n "$sources" ]; then
# shellcheck disable=SC2086
(cd "$copy" && node "$tsc" --module NodeNext --moduleResolution NodeNext --target ES2022 --types node \
--rootDir . --outDir dist $sources && node "$tsc" --module NodeNext --moduleResolution NodeNext \
--target ES2022 --types node --rootDir . --outDir . $sources)
fi
echo "testing $d (TypeScript)"
if ! (cd "$copy" && node --test --test-reporter=spec --experimental-strip-types \
--no-warnings=ExperimentalWarning test/*.test.ts); then
failed="$failed $m"
fi
done
if [ -n "$failed" ]; then
echo "TypeScript modules that failed:$failed"
exit 1
fi
+5 -3
View File
@@ -1,5 +1,6 @@
#!/bin/sh
# mesh-check-toolchain: go
# mesh-check-also: typescript merge-check-typescript.sh
#
# The catalogue's own check (novox/hq ADR 0237 as amended): the second layer of a pull request's merge
# check, `mesh/repo-check`, run by the build seat in the mesh's Go toolchain.
@@ -13,8 +14,9 @@
# it shares a rule with refused (MESH_GATE is the controller the mesh runs) — and the count of
# long-running resources without `health` held to the number in health-undeclared, which only goes down;
# 2. the Go tests of every module the change touches that has them, under the race detector when the
# toolchain has a C compiler — and a module whose dependencies cannot be fetched here, or that is
# written in TypeScript, is said as not tested, never passed silently;
# toolchain has a C compiler — and a module whose dependencies cannot be fetched here is said as not
# tested, never passed silently. A module written in TypeScript is type-checked and tested by
# merge-check-typescript.sh, declared above, in the TypeScript toolchain (novox/hq issues 302, 311);
# 3. the words (novox/hq ADR 0244): no word the glossary retired for the tools, as copied in
# retired-words, in any text a module's tools can show an agent — every string in its own code that is
# not a test, and every description in its manifest. Run over every module, not only the touched ones,
@@ -73,7 +75,7 @@ fi
for m in $touched; do
[ -d "modules/$m" ] || continue
if [ ! -f "modules/$m/go.mod" ]; then
[ -f "modules/$m/package.json" ] && echo "NOT TESTED HERE: modules/$m is TypeScript; the gate judges its manifest"
[ -f "modules/$m/package.json" ] && echo "modules/$m is TypeScript: merge-check-typescript.sh type-checks and tests it"
continue
fi
if ! (cd "modules/$m" && GOPRIVATE=git.novox.be go mod download >/dev/null 2>&1); then