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
32 changes: 28 additions & 4 deletions scripts/check-changelog-invariants.sh
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,10 @@ grep -q "🔩 Internal / build plumbing" <<<"$out" || err "28: 'Changelog-Major:
# sentence addressed to whoever tracks the fork graph, shown to someone deciding
# whether their handheld stutters less. Fork/maintainer handles stay in the
# commit body and the technical changelog; they never reach the hero, on either
# tier. Host types and protocols (Artemis, Apollo, Sunshine, Moonlight) are a
# real user-facing choice and must NOT be caught by the same rule.
# tier -- and the handle is REWRITTEN to "upstream" wherever it does appear, so
# the name is nowhere on the release page while the changelog entry survives.
# Host types and protocols (Artemis, Apollo, Sunshine, Moonlight) are a real
# user-facing choice and must NOT be caught by the same rule.
mkrepo
commit "fix(vrr): adopt the upstream active-wait bound" \
"Changelog: Adopt Nonary VRR10 active-wait fix: remove the fixed yield-count limit (4096)"
Expand All @@ -532,10 +534,12 @@ commit "feat: show the host type on each computer card" \
out=$(gen 0.1.0-beta.002)
awk '/^## 🚧/{exit} {print}' <<<"$out" | grep -qi "nonary" \
&& err "29: an upstream fork attribution reached the hero (the 0.5.0 Nonary bullet)"
grep -qi "nonary" <<<"$out" || err "29: the attribution was dropped entirely — the technical changelog must keep it"
grep -qi "nonary" <<<"$out" && err "29: a fork handle survived somewhere on the release page"
grep -q "VRR10 active-wait fix" <<<"$out" \
|| err "29: the entry was DELETED rather than rewritten — the changelog must keep the change, minus the name"
awk '/^## 🚧/{exit} {print}' <<<"$out" | grep -q "Apollo, Sunshine or Artemis" \
|| err "29: the attribution filter is over-broad — host types are a user-facing choice, not fork provenance"
[ "$fail" = "$t0" ] && ok "fork/maintainer attribution stays out of the hero; host types are untouched"
[ "$fail" = "$t0" ] && ok "fork handles are rewritten out of the whole page, entries survive, host types untouched"

# --- 30. the stable BODY shape: hero first and uncollapsed, everything technical --
# plumbing included -- inside one <details> that is properly closed. An unclosed
Expand All @@ -554,6 +558,26 @@ det_line=$(grep -n "Full technical changelog" <<<"$out" | head -1 | cut -d: -f1)
grep -q "🔩 Internal / build plumbing" <<<"$out" || err "30: plumbing vanished instead of moving inside the collapsed block"
[ "$fail" = "$t0" ] && ok "stable body: uncollapsed hero, one closed <details> holding the full changelog and plumbing"

# --- 31. the scrub reaches the COLLAPSED PLUMBING block too, and reads as English on
# both sides of the one wrinkle: a subject that already says "upstream" must not
# come out as "...with upstream VRR10 upstream", and one that doesn't must gain
# the word rather than losing the sense ("Guard VRR features" reads as if we
# guard our own). All four spellings below are real 0.5.0 subjects.
mkrepo
commit "chore(vrr): eliminate all divergences with Nonary VRR10 upstream"
commit "test(vrr): restore Nonary latch tests, ratify trade-off"
commit "build: guard Nonary VRR features behind version checks"
commit "ci: port the gamescope hook from moonlight-qt"
out=$(gen 0.1.0-beta.002)
grep -qi "nonary\|moonlight-qt" <<<"$out" && err "31: a handle survived inside the collapsed plumbing block"
grep -qF -- "- Eliminate all divergences with VRR10 upstream (" <<<"$out" \
|| err "31: a subject that already says 'upstream' was doubled up instead of just dropping the handle"
grep -qF -- "- Restore upstream latch tests, ratify trade-off (" <<<"$out" \
|| err "31: the handle was deleted where the sentence needed the word 'upstream' to still make sense"
grep -qF -- "- Guard upstream VRR features behind version checks (" <<<"$out" || err "31: scrub mangled a plumbing subject"
grep -qF -- "- Port the gamescope hook from upstream (" <<<"$out" || err "31: a hyphenated handle (moonlight-qt) was not scrubbed cleanly"
[ "$fail" = "$t0" ] && ok "handles are scrubbed inside the plumbing block and each rewrite reads as English"

cd "$ROOT"
if [ "$fail" = 0 ]; then
echo "changelog invariants: OK"
Expand Down
42 changes: 34 additions & 8 deletions scripts/gen-changelog.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,33 @@ is_stable_cut() { # $1 = version -> 0 if this cut is a production release
# 0.5.0's hero opened its second bullet with "Adopt Nonary VRR10 active-wait fix: remove
# the fixed yield-count limit (4096)..." -- a sentence addressed to whoever tracks the
# fork graph, published to people who wanted to know if their handheld stutters less.
# The attribution belongs in the commit body and the technical changelog, both of which
# keep it; the hero is the one place it does not belong. Deliberately narrow: only fork
# and maintainer handles. "Artemis", "Apollo", "Sunshine" and "Moonlight" are host types
# and protocols a user genuinely picks between, so they are NOT listed here.
HERO_ATTRIBUTION_RE='(^|[^[:alnum:]])(nonary|wjbeckett|cgutman|moonlight-qt)([^[:alnum:]]|$)'
#
# Deliberately narrow: fork and maintainer HANDLES only. "Artemis", "Apollo", "Sunshine"
# and "Moonlight" are host types and protocols a user genuinely picks between, so they
# are NOT listed here and must never be.
ATTRIBUTION_HANDLES='nonary|wjbeckett|cgutman|moonlight-qt'
HERO_ATTRIBUTION_RE="(^|[^[:alnum:]])(${ATTRIBUTION_HANDLES})([^[:alnum:]]|\$)"

# A handle is REWRITTEN, not deleted, and everywhere -- hero, technical sections and the
# collapsed plumbing block alike. Deleting the bullet would lose a real changelog entry;
# leaving the handle in the technical section still puts a fork maintainer's name on the
# release page, which is what was asked to stop. "Upstream" says the same thing about
# where the change came from without naming anyone.
#
# The one wrinkle worth the code: a subject that ALREADY says "upstream" ("Eliminate all
# divergences with Nonary VRR10 upstream") would come out as "...with upstream VRR10
# upstream". So substitute the word in only when the sentence does not already carry it,
# and simply drop the handle when it does. Runs BEFORE sentence_case, so a handle at the
# start of a subject still yields a capitalized bullet.
scrub_attribution() {
local s="$1" repl='upstream '
if printf '%s' "$s" | grep -qiE '(^|[^[:alnum:]])upstream([^[:alnum:]]|$)'; then
repl=''
fi
printf '%s' "$s" \
| sed -E "s/(^|[^[:alnum:]-])(${ATTRIBUTION_HANDLES})([^[:alnum:]-]|\$)/\1${repl}\3/gI" \
| sed -E 's/ +/ /g; s/ +([,.;:])/\1/g; s/^[[:space:]]+//; s/[[:space:]]+$//'
}

# Only a real version tag may anchor a range. tier_rank() calls anything without a
# pre-release infix "stable" (rank 4), which is right for `0.4.3` and catastrophically
Expand Down Expand Up @@ -323,11 +345,11 @@ while IFS='|' read -r hash subject; do
if [ "$force_internal" = true ] \
|| { [ "$force_user_facing" != true ] \
&& { printf '%s' "$subject" | grep -qiE "$INTERNAL_SUBJECT_RE" || ! ships "$hash"; }; }; then
INTERNAL="${INTERNAL}- $(md_escape "$(sentence_case "$(strip_md_structure "$clean")")") (\`${hash}\`)"$'\n'
INTERNAL="${INTERNAL}- $(md_escape "$(sentence_case "$(scrub_attribution "$(strip_md_structure "$clean")")")") (\`${hash}\`)"$'\n'
continue
fi

entry=$(md_escape "$(sentence_case "$(strip_md_structure "${note:-$clean}")")")
entry=$(md_escape "$(sentence_case "$(scrub_attribution "$(strip_md_structure "${note:-$clean}")")")")

# Classified ONCE, and read by both the hero gate and the technical routing below.
# These used to be one if/elif chain at the bottom; the stable hero needs to know
Expand All @@ -341,8 +363,12 @@ while IFS='|' read -r hash subject; do

# ── Does this note earn a place in the hero? ─────────────────────────────────
hero=true
# Tested against the RAW note, not $entry: $entry has already been through
# scrub_attribution, so the handle is gone and this would never match. A note that
# NEEDED scrubbing is provenance-flavoured writing, which is the signal being read
# here -- the scrub neutralises the name, this keeps the sentence out of the hero.
if printf '%s' "$note" | grep -qiE "$HERO_ATTRIBUTION_RE"; then
# Fork provenance, not user-facing news. Kept in the technical changelog.
# Fork provenance, not user-facing news. Kept, rewritten, in the technical changelog.
hero=false
echo "Note: hero bullet from ${hash} names an upstream fork/maintainer; technical section only." >&2
elif [ "$STABLE_CUT" = true ] && [ "$kind" != feature ] && [ "$is_major" != true ]; then
Expand Down