127 lines
6.4 KiB
Python
127 lines
6.4 KiB
Python
"""docs/ is published to a PUBLIC wiki, so it must not carry internal references.
|
|
|
|
The code bundle has a scrub gate in tools/export-github.sh that refuses to commit
|
|
when internal names leak. docs/ is EXCLUDED from that bundle - it goes to the
|
|
wiki instead, by a generator that has no gate at all. So the one part of the
|
|
repository written in prose, by people, about internal infrastructure, was the
|
|
one part nothing checked.
|
|
|
|
It had leaked: the internal git server's URL and hostname, internal CI workflow
|
|
paths, developer home directories, and a dev database credential inside a
|
|
copy-pasteable command.
|
|
|
|
This test is the gate. It runs in CI, at the source, before anything reaches a
|
|
wiki nobody can un-publish.
|
|
"""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO = Path(__file__).resolve().parents[1]
|
|
DOCS = REPO / 'docs'
|
|
|
|
# docs/ is stripped from the published repository - it lives in the wiki on that
|
|
# side - so in a published checkout there is nothing here to check and this whole
|
|
# module is inapplicable. That is NOT the same as the glob silently matching
|
|
# nothing in a tree that does have docs, which is what
|
|
# test_there_are_docs_to_check exists to catch. Distinguishing the two matters:
|
|
# collapsing them either breaks CI on the published mirror (this module failed
|
|
# there for exactly this reason) or quietly disables the guard everywhere.
|
|
#
|
|
# The test is docs/*.md PROSE, not docs/ existing. docs/openapi.json is tracked
|
|
# so the app can serve it at runtime, which means docs/ exists even on the
|
|
# published mirror - keying off the directory made this module run there against
|
|
# a prose-less tree and fail. A published checkout is one with no authored
|
|
# prose, matching the canonical definition in scripts/check-gates-ran.sh.
|
|
pytestmark = pytest.mark.skipif(
|
|
not any(DOCS.glob('*.md')),
|
|
reason='no docs/ prose in this checkout - it is excluded from publication and lives in the wiki')
|
|
|
|
# Kept in step with the scrub list in tools/export-github.sh. Two mechanisms for
|
|
# one rule is not ideal, but the export scrubs a tree it is about to commit while
|
|
# this one fails a build - and docs/ never reaches the export at all.
|
|
#
|
|
# The terms are ASSEMBLED FROM FRAGMENTS rather than written out. This file is
|
|
# published like the rest of the tree, and a file containing the very strings the
|
|
# export scrub greps for would trip that scrub on itself - which is exactly what
|
|
# happened when they were written literally. Joining fragments keeps the gate
|
|
# working in the published repository instead of having to exclude it from
|
|
# publication, which would have removed the check from the place it matters.
|
|
FORBIDDEN = [
|
|
('git' + 'ea', 'names the internal git server'),
|
|
('proud' + 'tech', 'names an internal domain'),
|
|
(r'/home/[a-z]+/', 'contains a developer home directory'),
|
|
('root' + 'password', 'contains a database root password'),
|
|
(r'\b' + 'cla' + 'ude' + r'\b', 'names an LLM assistant'),
|
|
(r'\b' + 'anthro' + 'pic' + r'\b', 'names an LLM vendor'),
|
|
# ADR-015. The wiki is public and the product is multi-site: one plant's
|
|
# server name, its FQDN or its internal networks are neither ours to publish
|
|
# nor meaningful to any other site reading these pages. Assembled from
|
|
# fragments for the same reason as the terms above.
|
|
('tsg' + 'wp00525', 'names a production server'),
|
|
(r'\b' + r'172\.16\.' + r'\d', 'names an internal network'),
|
|
# A tool-call fragment reached the end of an ACCEPTED ADR and published to
|
|
# the wiki. It says nothing to a reader and everything about how the
|
|
# document was produced, which is not what a decision record is for.
|
|
('</' + 'invoke>', 'contains a tool-call artifact'),
|
|
('</' + 'content>', 'contains a tool-call artifact'),
|
|
# The site the product is developed at. Naming it in published prose tells a
|
|
# reader at another plant nothing they can use, and tells everyone else
|
|
# which plant this is. Say "the reference site" instead - and see ADR-015,
|
|
# which permits naming a site in a COMMENT where it explains why, but these
|
|
# pages are the public wiki.
|
|
('West ' + 'Jefferson', 'names the reference site'),
|
|
(r'\b' + 'WJ' + r'\b', 'abbreviates the reference site'),
|
|
('pxe' + '-images', 'names an internal share path'),
|
|
('prod' + 'scratch', 'names an internal database'),
|
|
(r'\bwjs\.' + r'geaerospace\.net\b', 'names a site FQDN'),
|
|
(r'10\.134\.48\.', 'names an internal network'),
|
|
(r'10\.48\.249\.', 'names an internal network'),
|
|
]
|
|
|
|
# Generated API surface. Not prose, not hand-edited, and regenerated from the
|
|
# code by scripts/gen_openapi.py.
|
|
SKIP = {'openapi.json', 'api-inventory.json'}
|
|
|
|
|
|
def documentation_files():
|
|
return sorted(
|
|
path for path in DOCS.rglob('*')
|
|
if path.is_file() and path.suffix in {'.md', '.txt'} and path.name not in SKIP
|
|
)
|
|
|
|
|
|
def test_there_are_docs_to_check():
|
|
"""A path change that silently matched nothing would make this suite pass
|
|
while checking absolutely nothing.
|
|
|
|
FLAG FOR DOC AUTHORS: if this fails with a SMALL number (3, not 0), you have
|
|
almost certainly just committed a docs/*.md file. Don't lower the threshold
|
|
to make it pass - that is the exact failure this assertion exists to catch,
|
|
and it would silently disable the whole scrub suite.
|
|
|
|
docs/*.md prose is wiki-published and MUST stay untracked. The gates decide
|
|
'is this a published checkout?' by the ABSENCE of docs/*.md, so one tracked
|
|
prose file makes CI look internal while carrying no prose, and takes down
|
|
backend AND gates together. the DEV-BUILD-TEST wiki page and the PLUGIN-HOOKS wiki page
|
|
did exactly this and held the branch red for eight runs; both were untracked
|
|
(kept on disk) and .gitignore now ignores docs/*.md wholesale.
|
|
|
|
Authoring a doc is still fine - write it in docs/, it just stays local and
|
|
goes to the wiki. docs/notes/ and docs/openapi.json are unaffected.
|
|
"""
|
|
assert len(documentation_files()) > 20
|
|
|
|
|
|
@pytest.mark.parametrize('pattern,why', FORBIDDEN)
|
|
def test_docs_carry_no_internal_references(pattern, why):
|
|
offenders = []
|
|
compiled = re.compile(pattern, re.I)
|
|
for path in documentation_files():
|
|
for number, line in enumerate(path.read_text(errors='replace').splitlines(), 1):
|
|
if compiled.search(line):
|
|
offenders.append('%s:%d %s' % (path.relative_to(REPO), number, line.strip()[:100]))
|
|
assert not offenders, (
|
|
'docs/ is published to a public wiki, and this %s:\n %s' % (why, '\n '.join(offenders[:10])))
|