Skip to content
Merged
19 changes: 6 additions & 13 deletions repo-config/configure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,15 @@ main_ruleset="$script_dir/main.json"
settings_file="$script_dir/settings.json"

# ----- Resolve the declared description (optional, shared by apply and check) -----
# Per GOVERNANCE.md "Repository Details", once a repo declares registry/repos.json's `description` field, that field becomes the About panel's source rather than the README.
# The audit's description_findings() (spec/audit.py) measures the README, About, and Docker Hub mirror set against that same field.
# A repo with no declared field is left untouched here, so the README stays its source of truth.
# Undeclared means unchanged: the About panel keeps following the README, as before this field existed.
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Outdated
description=""
if [ -f "$registry" ]; then
# Trimmed defensively even though spec/validate.py already rejects an untrimmed value.
# A registry edited ahead of its next validate.py run still resolves to the same canonical value spec/audit.py compares against.
# Trims only space/tab, so an edge newline survives to trip the guard below rather than being silently dropped.
if ! description="$(jq -r --arg n "$name" \
'(.repos[] | select(.name==$n) | .description) // "" | gsub("^\\s+|\\s+$"; "")' "$registry")"; then
'(.repos[] | select(.name==$n) | .description) // "" | gsub("^[ \\t]+|[ \\t]+$"; "")' "$registry")"; then
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
echo "Failed to read description from $registry (invalid JSON?)." >&2
Comment thread
ptr727 marked this conversation as resolved.
Outdated
exit 1
fi
# The trim above only strips leading/trailing whitespace, so an embedded newline or carriage return survives it.
# Caught here rather than left to reach `gh api` as a multi-line value.
case "$description" in
*$'\n'* | *$'\r'*)
Expand Down Expand Up @@ -181,8 +177,7 @@ cmd_apply() {
payload="$(jq --argjson d "$disc" '. + {has_discussions: $d}' "$settings_file")"
echo "Warning: $repo has no 'main' branch. Leaving default_branch unchanged." >&2
fi
# The About description, only once a repo declares registry/repos.json's `description` (see the resolution above).
# Left untouched otherwise, so a repo that has not adopted the field yet keeps its hand-set (or README-derived) description.
# Applies only once a repo declares the field (see the resolution above).
if [ -n "$description" ]; then
payload="$(jq --arg desc "$description" '. + {description: $desc}' <<<"$payload")"
fi
Expand Down Expand Up @@ -303,13 +298,11 @@ check_settings() {
if gh api "repos/$repo/branches/main" --jq '.name' >/dev/null 2>&1; then
assert "default_branch = main" test "$(jq -r '.default_branch' <<<"$live")" = main
fi
# The About description, only where the registry declares one (see the resolution above).
# A repo that has not adopted the field is a manual-verify note, exactly as secrets are: nothing declared here to check against.
# The two reasons `$description` can be empty are told apart, since "no registry" and "no field for this repo" call for different follow-up.
# $description is empty for two different reasons, told apart below since each needs different follow-up.
if [ -n "$description" ]; then
assert "description = '$description'" test "$(jq -r '.description' <<<"$live")" = "$description"
elif [ ! -f "$registry" ]; then
note "description: no $registry to read (pass a plain repo argument or run from a hub checkout) - verify manually"
note "description: no $registry to read, resolved relative to this script rather than from the repo argument - run from a hub checkout for it to exist - verify manually"
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Outdated
else
note "description: no registry/repos.json description declared for $name - verify manually (falls back to the README tagline, see GOVERNANCE.md 'Repository Details')"
fi
Expand Down
14 changes: 14 additions & 0 deletions scripts/tests/test_spec_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,20 @@ def test_whitespace_only_is_rejected(self) -> None:
["Fixture: description must be a non-empty string"],
)

def test_an_explicit_null_is_rejected_rather_than_read_as_absent(self) -> None:
# The per-repo loop tests "description" in repo rather than is not None.
# An explicit "description": null therefore reaches here as `None` instead of being skipped as absent.
self.assertEqual(
validate.description_errors("Fixture", None),
["Fixture: description must be a non-empty string"],
)

def test_a_non_string_is_rejected(self) -> None:
self.assertEqual(
validate.description_errors("Fixture", 42),
["Fixture: description must be a non-empty string"],
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

def test_an_inline_markdown_link_is_rejected(self) -> None:
self.assertEqual(
validate.description_errors("Fixture", "See [docs](https://example.test) for more."),
Expand Down
18 changes: 17 additions & 1 deletion spec/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1303,7 +1303,16 @@ def description_findings(doc_texts, entry, live, slug):
declared value is canonical on its own and does not need the README to establish it.
"""
findings = []
declared = (entry.get("description") or "").strip() or None
raw_declared = entry.get("description")
if raw_declared is not None and not isinstance(raw_declared, str):
findings.append(
(
"DEFECT",
f"registry: description is a {type(raw_declared).__name__}, not a string (spec/validate.py rejects this once run) - treating it as undeclared",
)
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Outdated
)
raw_declared = None
declared = (raw_declared or "").strip() or None
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
readme_want = None
if "README.md" in doc_texts:
title, intro = title_and_intro(doc_texts["README.md"])
Expand Down Expand Up @@ -4214,6 +4223,13 @@ def _selftest():
{"description": "Anything."},
0,
),
(
"a non-string declared field is reported rather than crashing",
desc_readme,
{"description": 42},
{"description": "A short tagline."},
1,
),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
]
for label, doc_texts_fx, entry_fx, live_fx, wantn in desc_cases:
got = description_findings(doc_texts_fx, entry_fx, live_fx, "owner/Fixture")
Expand Down
6 changes: 3 additions & 3 deletions spec/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,9 +503,9 @@ def check_secret_set(label, entry, need_kind):
if effective_model == "operational" and eol is None:
errors.append(f"{name}: operational repo must declare lineEndings (lf or crlf)")
# Optional per GOVERNANCE.md "Repository Details": a repo that has not adopted the field yet is unaffected, since spec/audit.py's description_findings() falls back to the README tagline for it.
desc = repo.get("description")
if desc is not None:
errors.extend(description_errors(name, desc))
# Presence is the test, not `is not None`, so an explicit `"description": null` reaches description_errors() as invalid rather than reading as absent.
if "description" in repo:
errors.extend(description_errors(name, repo["description"]))
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Outdated

status = repo.get("status")
if status is None:
Expand Down