Skip to content

Enhance snippet handling#7086

Open
BlackYps wants to merge 5 commits intodevelopfrom
snippets
Open

Enhance snippet handling#7086
BlackYps wants to merge 5 commits intodevelopfrom
snippets

Conversation

@BlackYps
Copy link
Copy Markdown
Contributor

@BlackYps BlackYps commented Apr 10, 2026

Description of the proposed changes

We should reconsider the snippet formats a bit. I think it makes more sense when the PR number comes last. It is not that important that it needs to come first.
Additionally. we can bump the quality of the changelogs on the website a lot by adding some processing. This requires a little additional syntax in the balance snippets, but it's not too complicated.

Yanky first results:
grafik

Testing done on the proposed changes

Additional context

Checklist

Summary by CodeRabbit

  • Documentation

    • Updated changelog snippet documentation with new formatting guidelines for balance and other categories.
    • Clarified unit and blueprint ID requirements in development guides.
  • Chores

    • Enhanced automated PR preparation workflow with snippet template generation.
    • Added documentation build dependencies.
  • Bug Fixes

    • Fixed configuration file reference in local development setup instructions.

@BlackYps BlackYps added the area: changelog management related to (automated) management of changelogs label Apr 10, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

This pull request implements a changelog snippet automation system for Jekyll-based documentation. It introduces GitHub Actions workflow automation to generate changelog snippet files from a template, Jekyll plugins for rendering unit-based changelog entries with change visualization, supporting styles, and updated documentation guidelines for different snippet categories.

Changes

Cohort / File(s) Summary
Workflow Automation
.github/workflows/auto-draft-pr.yaml
Renamed workflow and refactored into two sequential steps: generates changelog snippet file by copying template and substituting PR number, then converts PR to draft status.
Changelog Snippet Infrastructure
changelog/snippets/sections/template-snippet.md, docs/development-changelog.md, docs/development-changelog/balance-snippet.md, docs/development-changelog/other-snippet.md
Added snippet template and updated documentation to specify format requirements for balance and other category snippets, including new {% unit %} block syntax and example snippets.
Jekyll Changelog Rendering
docs/_plugins/balance_change.rb, docs/_plugins/unit_block.rb, docs/_sass/custom/custom.scss
Added Jekyll hook for rendering change tracking (old/new visual distinction) and new unit Liquid block tag for displaying unit-specific changelog entries with icons and formatted headers. Added corresponding styles for layout, typography, and change status indicators.
Configuration & Dependencies
docs/Gemfile, docs/documentation.md
Added csv and base64 gem dependencies; corrected Jekyll config filename typo in local serve command.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

area: documentation

Suggested reviewers

  • lL1l1
  • Basilisk3
  • Rhaelya

Poem

🐰 A changelog flows with snippet care,
Jekyll hooks transform here and there,
Units bright with icons new,
Old and new in balance true—
Automation helps our docs take flight! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Enhance snippet handling' accurately reflects the main changes: improving snippet formats, documentation, and automation in the changelog snippet system.
Description check ✅ Passed The description explains the rationale for reordering snippets and improving changelog quality with visual examples, but lacks specific testing details and incomplete checklist items.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch snippets

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
docs/Gemfile (1)

9-11: Avoid pinning default Ruby gems unless runtime is explicitly pinned.

csv and base64 are default gems tied closely to Ruby runtime versions. Pinning them here without an explicit Ruby version for the docs toolchain can cause Bundler resolution mismatches across local/CI environments. Prefer relying on default gems, or add a strict ruby version and validate lockfile consistency.

#!/bin/bash
# Read-only verification for Ruby/default-gem pinning risk in docs toolchain.

set -euo pipefail

echo "== Find Ruby version constraints =="
rg -n --iglob 'Gemfile*' --iglob '*.gemspec' --iglob '.ruby-version' '\bruby\b|^ruby-version|^RUBY_VERSION'

echo
echo "== Inspect docs Gemfile/Gemfile.lock entries for csv/base64 =="
fd -i 'Gemfile.lock|Gemfile' docs . | xargs -r rg -n '^\s{0,4}(csv|base64)\b|gem\s+"(csv|base64)"'

echo
echo "== Locate actual requires/usages in docs-related Ruby code =="
rg -n --type ruby -C2 'require\s+["'\''](csv|base64)["'\'']|CSV\b|Base64\b' docs
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/Gemfile` around lines 9 - 11, The Gemfile currently pins default Ruby
stdlib gems with lines gem "csv", "~> 3.3" and gem "base64", "~> 0.3.0", which
can cause Bundler resolution mismatches; either remove these gem entries so the
toolchain uses the runtime-provided stdlib versions, or instead add an explicit
ruby version constraint (ruby "x.y.z") and update/commit a matching Gemfile.lock
so the pinned versions are intentional and reproducible—change the Gemfile by
deleting the gem "csv" and gem "base64" lines or add a strict ruby declaration
and regenerate the lockfile, referencing the existing Gemfile entries (gem
"csv"/gem "base64") to locate the edits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/auto-draft-pr.yaml:
- Around line 34-47: The workflow step that creates and commits a snippet is
missing a repository checkout, a push of the new commit, and uses an
unrecognized filename pattern; to fix it add an initial "actions/checkout@vX"
step before changing into fa/changelog/snippets, update FILE from category.${{
github.event.pull_request.number }}.md to one of the accepted patterns (e.g.
other.${{ github.event.pull_request.number }}.md or features.${{
github.event.pull_request.number }}.md) so the bundler will pick it up, and
after git commit run git push to push the new branch/commit; ensure the existing
commands that reference FILE, cp sections/template-snippet.md, sed -i
"s/XYZW/${{ github.event.pull_request.number }}/g" $FILE, git add ., and git
commit -m "Add snippet template" remain but run only after checkout and before
the git push.

In `@docs/_plugins/balance_change.rb`:
- Around line 8-10: The current regex in doc.output.gsub! (the
/([^<\s]+)\s*--&gt;\s*([^<\s]+)/) only captures single tokens (no spaces or
parentheses), so multi-token values like "60 (225) --> 45 (225)" are missed;
update the pattern to allow any non-HTML content around the arrow (e.g. use lazy
captures like /([^<]+?)\s*--&gt;\s*([^<]+?)/ or similar) and trim captured
groups before inserting them into the replacement span so leading/trailing
whitespace isn't preserved; keep the replacement logic in the same gsub! call
but switch to the broader capture groups (referencing doc.output.gsub! and the
regex literal) and ensure you still escape HTML-sensitive chars if needed.

In `@docs/_plugins/unit_block.rb`:
- Around line 13-16: The template injects raw `@unit_id` and name into HTML;
update the UnitBlock rendering to validate `@unit_id` against a strict
whitelist/regex (e.g., only [A-Za-z0-9_-]) and reject or normalize unexpected
values, and escape any user-provided text using an HTML-escaping helper (e.g.,
CGI.escapeHTML or Jekyll::Utils.escape_html) when outputting name and when
composing the img src attribute; locate the renderer method that outputs the div
with class "unit-header" (the code using `@unit_id` and name) and apply these
validations/escapes so the src="/assets/icons/#{`@unit_id`}.png" and the span
content use safe, escaped values.

In `@docs/development-changelog/balance-snippet.md`:
- Line 19: Add a fenced language specifier to the code block starting at the
opening ``` on line 19 by changing it to ```md, and fix the heading level by
replacing the "### Example snippets" heading with "## Example snippets" (update
the heading text referenced as "### Example snippets" to "## Example snippets")
so markdownlint no longer reports a missing fence language and a skipped heading
level.
- Around line 9-14: Remove the draft/internal notes lines in the balance-snippet
content (the brainstorming lines about "Unit or explanation first?", "The unitID
is also used...", etc.) so the page reads as polished guidance; also ensure the
md->lua converter strips the inline marker "{: .unit-change}" and removes the
trailing "(unitID)" tokens (including cases like "enhancements/faction/name")
from rendered output so no draft markers or raw unit IDs remain in published
docs.

In `@docs/development-changelog/other-snippet.md`:
- Around line 12-13: The markdown violates lint rules by skipping a heading
level and omitting a fence language: change the "### Example snippet" heading to
"## Example snippet" and add the language tag "md" to the opening code fence for
the example (the fenced block that contains the LOD formula line) so it reads as
a proper fenced code block with a declared language.

---

Nitpick comments:
In `@docs/Gemfile`:
- Around line 9-11: The Gemfile currently pins default Ruby stdlib gems with
lines gem "csv", "~> 3.3" and gem "base64", "~> 0.3.0", which can cause Bundler
resolution mismatches; either remove these gem entries so the toolchain uses the
runtime-provided stdlib versions, or instead add an explicit ruby version
constraint (ruby "x.y.z") and update/commit a matching Gemfile.lock so the
pinned versions are intentional and reproducible—change the Gemfile by deleting
the gem "csv" and gem "base64" lines or add a strict ruby declaration and
regenerate the lockfile, referencing the existing Gemfile entries (gem "csv"/gem
"base64") to locate the edits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 700c6765-d17f-4090-9a14-9a88974a38aa

📥 Commits

Reviewing files that changed from the base of the PR and between 1d1530b and 4c0d80f.

⛔ Files ignored due to path filters (2)
  • docs/Gemfile.lock is excluded by !**/*.lock
  • docs/assets/icons/XES0102.png is excluded by !**/*.png
📒 Files selected for processing (10)
  • .github/workflows/auto-draft-pr.yaml
  • changelog/snippets/sections/template-snippet.md
  • docs/Gemfile
  • docs/_plugins/balance_change.rb
  • docs/_plugins/unit_block.rb
  • docs/_sass/custom/custom.scss
  • docs/development-changelog.md
  • docs/development-changelog/balance-snippet.md
  • docs/development-changelog/other-snippet.md
  • docs/documentation.md

Comment on lines +34 to +47
- name: Add snippet
working-directory: fa/changelog/snippets
run: |
# Configure git
git config user.email "github@faforever.com"
git config user.name "FAForever Machine User"

FILE=category.${{ github.event.pull_request.number }}.md
cp sections/template-snippet.md $FILE
sed -i "s/XYZW/${{ github.event.pull_request.number }}/g" $FILE

git add .
git commit -m "Add snippet template"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow signals:"
rg -n 'actions/checkout|working-directory|FILE=|git commit|git push' .github/workflows/auto-draft-pr.yaml

echo
echo "Bundler accepted snippet categories:"
rg -n 'process_snippets "' .github/workflows/scripts/bash/changelog-combine.sh

Repository: FAForever/fa

Length of output: 693


🏁 Script executed:

cat -n .github/workflows/auto-draft-pr.yaml | head -50

Repository: FAForever/fa

Length of output: 2288


Workflow will fail: missing checkout, no push, and invalid filename pattern.

The job is missing a checkout step (line 34 tries to access fa/changelog/snippets without cloning the repo first), never pushes the commit (line 46), and uses category.<PR>.md which the bundler doesn't recognize—it only processes balance.*.md, features.*.md, fix.*.md, graphics.*.md, ai.*.md, performance.*.md, or other.*.md.

Suggested fix
+      - name: Checkout PR branch
+        uses: actions/checkout@v4
+        with:
+          ref: ${{ github.event.pull_request.head.ref }}

       - name: Add snippet
         working-directory: fa/changelog/snippets
         run: |
           # Configure git
           git config user.email "github@faforever.com"
           git config user.name "FAForever Machine User"
           
-          FILE=category.${{ github.event.pull_request.number }}.md
+          FILE=other.${{ github.event.pull_request.number }}.md
           cp sections/template-snippet.md $FILE
           sed -i "s/XYZW/${{ github.event.pull_request.number }}/g" $FILE
           
-          git add .
-          git commit -m "Add snippet template"
+          git add "$FILE"
+          git commit -m "Add snippet template" || true
+          git push
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/auto-draft-pr.yaml around lines 34 - 47, The workflow step
that creates and commits a snippet is missing a repository checkout, a push of
the new commit, and uses an unrecognized filename pattern; to fix it add an
initial "actions/checkout@vX" step before changing into fa/changelog/snippets,
update FILE from category.${{ github.event.pull_request.number }}.md to one of
the accepted patterns (e.g. other.${{ github.event.pull_request.number }}.md or
features.${{ github.event.pull_request.number }}.md) so the bundler will pick it
up, and after git commit run git push to push the new branch/commit; ensure the
existing commands that reference FILE, cp sections/template-snippet.md, sed -i
"s/XYZW/${{ github.event.pull_request.number }}/g" $FILE, git add ., and git
commit -m "Add snippet template" remain but run only after checkout and before
the git push.

Comment on lines +8 to +10
doc.output.gsub!(
/([^<\s]+)\s*--&gt;\s*([^<\s]+)/,
'<span class="old">\1</span> → <span class="new">\2</span>'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Regex misses common value formats with spaces/parentheses.

This pattern only matches single tokens, so examples like 60 (225) --> 45 (225) won’t be transformed.

🔧 Suggested patch
-    /([^<\s]+)\s*--&gt;\s*([^<\s]+)/,
+    /([^<]+?)\s*--&gt;\s*([^<]+?)(?=\s*(?:<|$))/,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
doc.output.gsub!(
/([^<\s]+)\s*--&gt;\s*([^<\s]+)/,
'<span class="old">\1</span> → <span class="new">\2</span>'
doc.output.gsub!(
/([^<]+?)\s*--&gt;\s*([^<]+?)(?=\s*(?:<|$))/,
'<span class="old">\1</span> → <span class="new">\2</span>'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/_plugins/balance_change.rb` around lines 8 - 10, The current regex in
doc.output.gsub! (the /([^<\s]+)\s*--&gt;\s*([^<\s]+)/) only captures single
tokens (no spaces or parentheses), so multi-token values like "60 (225) --> 45
(225)" are missed; update the pattern to allow any non-HTML content around the
arrow (e.g. use lazy captures like /([^<]+?)\s*--&gt;\s*([^<]+?)/ or similar)
and trim captured groups before inserting them into the replacement span so
leading/trailing whitespace isn't preserved; keep the replacement logic in the
same gsub! call but switch to the broader capture groups (referencing
doc.output.gsub! and the regex literal) and ensure you still escape
HTML-sensitive chars if needed.

Comment on lines +13 to +16
<div class="unit-header" data-unit="#{@unit_id}">
<img class="unit-icon"
src="/assets/icons/#{@unit_id}.png">
<span class="unit-name">#{name}</span>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Escape and validate interpolated HTML values.

@unit_id and name are injected raw into HTML. This should be escaped (and unit_id validated) before rendering.

🛡️ Suggested patch
+require "cgi"
+
 module Jekyll
   class UnitBlock < Liquid::Block
@@
     def render(context)
-      name = super.strip
+      name = CGI.escapeHTML(super.strip)
+      unit_id = `@unit_id.strip`
+      raise ArgumentError, "Invalid unit id" unless unit_id.match?(/\A[a-zA-Z0-9_\/-]+\z/)
+      safe_unit_id = CGI.escapeHTML(unit_id)

       <<~HTML
-      <div class="unit-header" data-unit="#{`@unit_id`}">
+      <div class="unit-header" data-unit="#{safe_unit_id}">
         <img class="unit-icon"
-             src="/assets/icons/#{`@unit_id`}.png">
+             src="/assets/icons/#{safe_unit_id}.png"
+             alt="">
         <span class="unit-name">#{name}</span>
       </div>
       HTML
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/_plugins/unit_block.rb` around lines 13 - 16, The template injects raw
`@unit_id` and name into HTML; update the UnitBlock rendering to validate `@unit_id`
against a strict whitelist/regex (e.g., only [A-Za-z0-9_-]) and reject or
normalize unexpected values, and escape any user-provided text using an
HTML-escaping helper (e.g., CGI.escapeHTML or Jekyll::Utils.escape_html) when
outputting name and when composing the img src attribute; locate the renderer
method that outputs the div with class "unit-header" (the code using `@unit_id`
and name) and apply these validations/escapes so the
src="/assets/icons/#{`@unit_id`}.png" and the span content use safe, escaped
values.

Comment on lines +9 to +14
The game just renders the text as is, so we need to keep that in mind.
Unit or explanation first?
The unitID is also used to infer if the snippet is supposed to go into the land, navy, air, structures or enhancements category.
if the change is an enhancement, the unitID is enhancements/faction/name
The md to lua converter should remove the {: .unit-change} and the (unitID)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove draft notes before publishing this doc page.

These lines read like internal brainstorming/TODO notes and conflict with the otherwise polished guidance.

🧾 Suggested cleanup
-The game just renders the text as is, so we need to keep that in mind.
-Unit or explanation first?
-The unitID is also used to infer if the snippet is supposed to go into the land, navy, air, structures or enhancements category.
-if the change is an enhancement, the unitID is enhancements/faction/name
-The md to lua converter should remove the {: .unit-change} and the (unitID)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The game just renders the text as is, so we need to keep that in mind.
Unit or explanation first?
The unitID is also used to infer if the snippet is supposed to go into the land, navy, air, structures or enhancements category.
if the change is an enhancement, the unitID is enhancements/faction/name
The md to lua converter should remove the {: .unit-change} and the (unitID)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/development-changelog/balance-snippet.md` around lines 9 - 14, Remove
the draft/internal notes lines in the balance-snippet content (the brainstorming
lines about "Unit or explanation first?", "The unitID is also used...", etc.) so
the page reads as polished guidance; also ensure the md->lua converter strips
the inline marker "{: .unit-change}" and removes the trailing "(unitID)" tokens
(including cases like "enhancements/faction/name") from rendered output so no
draft markers or raw unit IDs remain in published docs.


The snippet file should be named `balance.<PR Number>.md`.

```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdownlint issues in examples section.

Line 19 should specify a fenced language, and Line 38 should not jump heading levels.

🧹 Suggested patch
-```
+```md
@@
-### Example snippets
+## Example snippets

Also applies to: 38-38

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 19-19: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/development-changelog/balance-snippet.md` at line 19, Add a fenced
language specifier to the code block starting at the opening ``` on line 19 by
changing it to ```md, and fix the heading level by replacing the "### Example
snippets" heading with "## Example snippets" (update the heading text referenced
as "### Example snippets" to "## Example snippets") so markdownlint no longer
reports a missing fence language and a skipped heading level.

Comment on lines +12 to +13
### Example snippet
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdownlint violations in the example section.

Line 12 should not skip heading levels, and Line 13 should declare a fence language.

🧹 Suggested patch
-### Example snippet
-```
+## Example snippet
+```md
 - Tweak the formula used to calculate the level of detail (LOD) for props.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Example snippet
```
## Example snippet
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 12-12: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)


[warning] 13-13: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/development-changelog/other-snippet.md` around lines 12 - 13, The
markdown violates lint rules by skipping a heading level and omitting a fence
language: change the "### Example snippet" heading to "## Example snippet" and
add the language tag "md" to the opening code fence for the example (the fenced
block that contains the LOD formula line) so it reads as a proper fenced code
block with a declared language.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: changelog management related to (automated) management of changelogs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant