Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
128 changes: 128 additions & 0 deletions .github/scripts/check-stale-docs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/env bash
#
# Fail if any documentation still tells the reader to use something this
# repository deleted.
#
# Every string below was, at some point, the documented way to do something
# here, and every one of them outlived the thing it named. The lineage demo's
# own readme told visitors for months that the demo "cannot be built" and gave
# them `mvn -f pom_dlineage.xml package` -- a file removed on 2026-07-28 -- and
# `java -jar gudusoft.dlineage.jar`, a jar this build has never produced. A
# first-time evaluator who opened that folder concluded the product was broken.
# Nothing went red, because no check reads prose.
#
# So this one does. It is deliberately dumb: fixed strings, no cleverness about
# context. A file whose job is to record that these things died is listed in
# ALLOW; anything else naming them is treated as an instruction to the reader.
#
# Run --self-test to prove the check still catches what it claims to. A grep
# that silently matches nothing would pass this repository forever.

set -euo pipefail

cd "$(dirname "$0")/../.."

# Documents whose subject IS the removal. They have to name what was removed.
ALLOW=(
"README.md"
"docs/maintenance-notes.md"
)

DEAD=(
"pom_dlineage.xml"
"gudusoft.dlineage.jar"
"src/main/java/demos/"
'src\main\java\demos\'
)

WHY=(
"merged into pom.xml on 2026-07-28; build with 'mvn package -DskipTests'"
"the standalone jar is target/gsp_demo_java-1.0-SNAPSHOT-dlineage.jar"
"second package root removed 2026-07-27; demos are under src/main/java/gudusoft/gsqlparser/demos/"
"same, in Windows path form"
)

# scan <file>... -- prints every hit, returns 1 if there was any
scan() {
local failed=0 f i hits line
for f in "$@"; do
for i in "${!DEAD[@]}"; do
if hits=$(grep -Fn -- "${DEAD[$i]}" "$f" 2>/dev/null); then
while IFS= read -r line; do
printf ' %s:%s\n' "$f" "$line"
done <<<"$hits"
printf ' ^ "%s" is gone -- %s\n\n' "${DEAD[$i]}" "${WHY[$i]}"
failed=1
fi
done
done
return "$failed"
}

self_test() {
local tmp rc pass=0 fail=0 i
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' RETURN

printf 'Build it with `mvn package -DskipTests`.\n' >"$tmp/clean.md"
if scan "$tmp/clean.md" >/dev/null 2>&1; then
echo " ok a clean document passes"
pass=$((pass + 1))
else
echo " FAIL a clean document was reported as stale"
fail=$((fail + 1))
fi

for i in "${!DEAD[@]}"; do
printf 'run %s to build it\n' "${DEAD[$i]}" >"$tmp/stale.md"
rc=0
scan "$tmp/stale.md" >/dev/null 2>&1 || rc=$?
if [ "$rc" -eq 1 ]; then
echo " ok \"${DEAD[$i]}\" is caught"
pass=$((pass + 1))
else
echo " FAIL \"${DEAD[$i]}\" slipped through"
fail=$((fail + 1))
fi
done

echo
if [ "$fail" -gt 0 ]; then
echo "self-test: $fail of $((pass + fail)) cases FAILED"
return 1
fi
echo "self-test: all $pass cases pass"
}

if [ "${1:-}" = "--self-test" ]; then
echo "proving check-stale-docs.sh catches what it claims to"
echo
self_test
exit $?
fi

files=()
while IFS= read -r f; do
skip=0
for a in "${ALLOW[@]}"; do
if [ "$f" = "$a" ]; then skip=1; fi
done
if [ "$skip" -eq 0 ]; then files+=("$f"); fi
done < <(git ls-files '*.md')

if [ "${#files[@]}" -eq 0 ]; then
echo "::error::no markdown files found to check; is this a git checkout?"
exit 1
fi

echo "checking ${#files[@]} markdown files for references to deleted things"
echo

if scan "${files[@]}"; then
echo "ok: no documentation points at anything that has been removed"
exit 0
fi

echo "::error::documentation above still instructs the reader to use something that no longer exists"
echo "If a file's purpose is to record the removal, add it to ALLOW in $0."
exit 1
17 changes: 17 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ jobs:
if: matrix.java == '21'
run: .github/scripts/test-pre-commit-hook.sh

# Prose does not compile, so a readme can name a file that was deleted two
# releases ago and every build stays green. The lineage demo's own readme
# did exactly that: it told visitors the demo "cannot be built", handed
# them `mvn -f pom_dlineage.xml package` for a POM removed on 2026-07-28
# and `java -jar gudusoft.dlineage.jar` for a jar this build has never
# produced. Someone evaluating GSP for the first time reasonably concluded
# the product was broken.
#
# --self-test runs first and on purpose: a fixed-string check that matches
# nothing looks identical to a clean repository.
- name: Documentation does not point at deleted things
if: matrix.java == '21'
run: |
.github/scripts/check-stale-docs.sh --self-test
echo
.github/scripts/check-stale-docs.sh

- name: Build
run: mvn -B package -DskipTests

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ than only building them.
|---|---|
| Parser version consistency | `set-parser-version.sh --check` across all four POMs |
| The pre-commit hook | `test-pre-commit-hook.sh`: a drifting bump is refused in a throwaway clone |
| Documentation | `check-stale-docs.sh`: no readme names `pom_dlineage.xml`, `gudusoft.dlineage.jar` or the old `demos` package root; `--self-test` first, so a check that matches nothing cannot pass as a clean repo |
| Build and test | JDK 8 and 21; 156 tests, and a run that skipped everything fails |
| Demo smoke test | `checksyntax` against known SQL |
| Standalone lineage jar | `smoke-dlineage-jar.sh` on JDK 8 and 21 — asserts on **output**, in JSON *and* XML |
Expand Down
Loading
Loading