Skip to content

Add quality infrastructure: PR template, CI checks, contributing guide#626

Open
beagandica wants to merge 1 commit into
NuevoFoundation:masterfrom
beagandica:infra/quality-guardrails
Open

Add quality infrastructure: PR template, CI checks, contributing guide#626
beagandica wants to merge 1 commit into
NuevoFoundation:masterfrom
beagandica:infra/quality-guardrails

Conversation

@beagandica
Copy link
Copy Markdown
Member

Summary

Adds Tier 1 quality guardrails to prevent recurring content issues and help contributors submit clean PRs.

Changes

PR Template (.github/PULL_REQUEST_TEMPLATE.md)

Every new PR automatically gets a checklist covering:

  • Hugo build verification
  • Image case-sensitivity (no .PNG/.JPG)
  • HTML attribute quoting + alt text
  • YAML frontmatter validation
  • Multi-language parity check
  • Special sections for images, new workshops, and translations

Content Quality CI (.github/workflows/content-quality.yaml)

Runs automatically on every PR that touches \content/, \layouts/, \static/, or \ hemes/:

Check What it catches Severity
Hugo Build Broken site builds ❌ Fails PR
Uppercase Extensions .PNG, .JPG (break on Linux) ❌ Fails PR
Unquoted HTML \src= file.png\ (malformed HTML) ❌ Fails PR
Duplicate YAML Keys Duplicate frontmatter (silent Hugo bugs) ❌ Fails PR
Missing Alt Text Images without alt text ⚠️ Warning only
Translation Parity English changes with untouched translations ℹ️ Info only

Contributing Guide (\CONTRIBUTING.md)

Complete guide for contributors covering:

  • Workshop folder structure and frontmatter templates
  • Image naming, sizing, and alt text standards
  • TOC collapsible pattern (<details>/)
  • Translation do's and don'ts
  • Local testing instructions
  • PR guidelines

EditorConfig (.editorconfig)

Standardizes: UTF-8, LF line endings, final newline, 2-space indent.

Why

These guardrails prevent the exact issues we've been fixing in PRs #609, #622, #623, #624, and #625:

  • .PNG\ case-sensitivity → caught by CI
  • Unquoted HTML attributes → caught by CI
  • Duplicate YAML keys → caught by CI
  • Missing alt text → flagged by CI
  • Single-language fixes → translation parity check

Testing

  • Hugo builds successfully with all new files
  • CI workflow syntax validated
  • No impact on existing content or build process

Comment on lines +14 to +30
name: Hugo Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: 'latest'
extended: true

- name: Build site
run: hugo --quiet --minify

content-lint:
Comment on lines +31 to +146
name: Content Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Get changed files
id: changed
uses: tj-actions/changed-files@v44
with:
files: |
content/**/*.md

- name: Check uppercase image extensions
if: steps.changed.outputs.any_changed == 'true'
run: |
echo "🔍 Checking for uppercase image extensions (.PNG, .JPG, .GIF)..."
ERRORS=0
for file in ${{ steps.changed.outputs.all_changed_files }}; do
if [ -f "$file" ]; then
MATCHES=$(grep -n '\.\(PNG\|JPG\|GIF\|JPEG\|BMP\)' "$file" || true)
if [ -n "$MATCHES" ]; then
echo "❌ $file:"
echo "$MATCHES"
ERRORS=$((ERRORS + 1))
fi
fi
done
if [ $ERRORS -gt 0 ]; then
echo ""
echo "⚠️ Found $ERRORS file(s) with uppercase image extensions."
echo " Linux servers are case-sensitive — use .png, .jpg, .gif instead."
exit 1
fi
echo "✅ No uppercase image extensions found."

- name: Check unquoted HTML attributes
if: steps.changed.outputs.any_changed == 'true'
run: |
echo "🔍 Checking for unquoted HTML attributes..."
ERRORS=0
for file in ${{ steps.changed.outputs.all_changed_files }}; do
if [ -f "$file" ]; then
# Match src= or href= followed by a space then non-quote char (unquoted attr)
MATCHES=$(grep -n 'src= [^"]' "$file" || true)
if [ -n "$MATCHES" ]; then
echo "❌ $file:"
echo "$MATCHES"
ERRORS=$((ERRORS + 1))
fi
fi
done
if [ $ERRORS -gt 0 ]; then
echo ""
echo "⚠️ Found $ERRORS file(s) with unquoted HTML attributes."
echo " Always quote attributes: src=\"file.png\" not src= file.png"
exit 1
fi
echo "✅ No unquoted HTML attributes found."

- name: Check duplicate YAML frontmatter keys
if: steps.changed.outputs.any_changed == 'true'
run: |
echo "🔍 Checking for duplicate YAML frontmatter keys..."
ERRORS=0
for file in ${{ steps.changed.outputs.all_changed_files }}; do
if [ -f "$file" ]; then
# Extract frontmatter (between --- markers), get keys, find duplicates
DUPES=$(sed -n '/^---$/,/^---$/p' "$file" | grep -E '^\w+:' | sed 's/:.*//' | sort | uniq -d)
if [ -n "$DUPES" ]; then
echo "❌ $file has duplicate YAML keys:"
echo "$DUPES"
ERRORS=$((ERRORS + 1))
fi
fi
done
if [ $ERRORS -gt 0 ]; then
echo ""
echo "⚠️ Found $ERRORS file(s) with duplicate YAML frontmatter keys."
echo " Hugo silently uses the last value — remove duplicates."
exit 1
fi
echo "✅ No duplicate YAML keys found."

- name: Check images have alt text
if: steps.changed.outputs.any_changed == 'true'
run: |
echo "🔍 Checking for images without alt text..."
WARNINGS=0
for file in ${{ steps.changed.outputs.all_changed_files }}; do
if [ -f "$file" ]; then
# Check for ![]( (empty alt text in markdown images)
MATCHES=$(grep -n '!\[\](' "$file" || true)
if [ -n "$MATCHES" ]; then
echo "⚠️ $file has images without alt text:"
echo "$MATCHES"
WARNINGS=$((WARNINGS + 1))
fi
# Check for <img> tags without alt attribute
IMG_NO_ALT=$(grep -n '<img ' "$file" | grep -v 'alt=' || true)
if [ -n "$IMG_NO_ALT" ]; then
echo "⚠️ $file has <img> tags without alt attribute:"
echo "$IMG_NO_ALT"
WARNINGS=$((WARNINGS + 1))
fi
fi
done
if [ $WARNINGS -gt 0 ]; then
echo ""
echo "⚠️ Found $WARNINGS file(s) with missing alt text."
echo " All images should have descriptive alt text for accessibility."
echo " Example: ![Student typing code in editor](media/screenshot.png)"
# Warning only — don't fail the build for alt text (too many existing files)
fi
echo "✅ Alt text check complete."

translation-parity:
Comment on lines +147 to +192
name: Translation Parity Check
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4

- name: Get changed files
id: changed
uses: tj-actions/changed-files@v44
with:
files: |
content/**/*.md

- name: Check if translations exist for changed workshops
if: steps.changed.outputs.any_changed == 'true'
run: |
echo "🌍 Checking translation parity for changed workshops..."
LANGUAGES=("brazilian-portuguese" "espanol" "korean" "francais" "german" "simplified-chinese" "traditional-chinese" "kyrgyz")
NOTICES=0

# Get unique workshop paths from changed English files
for file in ${{ steps.changed.outputs.all_changed_files }}; do
if [[ "$file" == content/english/* ]]; then
# Extract workshop name (first directory after english/)
WORKSHOP=$(echo "$file" | sed 's|content/english/||' | cut -d'/' -f1)

for lang in "${LANGUAGES[@]}"; do
LANG_DIR="content/$lang/$WORKSHOP"
if [ -d "$LANG_DIR" ]; then
echo "📋 Workshop '$WORKSHOP' also exists in $lang — please verify changes apply there too."
NOTICES=$((NOTICES + 1))
break # Only report once per workshop
fi
done
fi
done

if [ $NOTICES -gt 0 ]; then
echo ""
echo "ℹ️ $NOTICES workshop(s) have translations that may need matching updates."
echo " If this is a structural change (TOC, layout, images), update all languages."
echo " If this is English-only content, you can skip this."
else
echo "✅ No translation parity issues detected."
fi
# Info only — don't fail the build
Tier 1 quality guardrails for NuevoFoundation workshops:

- PR template: checklist for Hugo build, images, alt text, YAML, translations
- Content quality CI: checks for uppercase extensions, unquoted HTML attrs,
  duplicate YAML keys, missing alt text on all content PRs
- Translation parity CI: flags when English changes have untouched translations
- CONTRIBUTING.md: complete contributor guide covering folder structure,
  frontmatter templates, image guidelines, translation rules, testing steps
- .editorconfig: standardize UTF-8, LF endings, final newline, 2-space indent

Prevents recurrence of issues fixed in PRs NuevoFoundation#609, NuevoFoundation#622, NuevoFoundation#623, NuevoFoundation#624, NuevoFoundation#625.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@beagandica beagandica force-pushed the infra/quality-guardrails branch from c25ff24 to bd21132 Compare April 29, 2026 02:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants