Skip to content
Open
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
22 changes: 22 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# EditorConfig helps maintain consistent coding styles
# https://editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.md]
# Trailing whitespace is significant in Markdown (line breaks)
trim_trailing_whitespace = false

[*.{yaml,yml}]
indent_size = 2

[Makefile]
indent_style = tab
43 changes: 43 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## Summary

<!-- One line: what does this PR fix or add? -->

## Changes

<!-- List files changed and what was fixed/added in each -->

## Languages affected

<!-- List all language versions checked. If you changed English content, did you check if translations exist? -->
- [ ] English
- [ ] Brazilian Portuguese (`content/brazilian-portuguese/`)
- [ ] Spanish (`content/espanol/`)
- [ ] Korean (`content/korean/`)
- [ ] Other: ___

## Checklist

<!-- Check all that apply. PRs that skip required items may be sent back for changes. -->

### Required
- [ ] Hugo builds without errors (`hugo --quiet`)
- [ ] All image references use **lowercase** extensions (`.png`, `.jpg`, `.gif` — not `.PNG`)
- [ ] All `<img>` tags have quoted attributes and `alt` text
- [ ] YAML frontmatter has no duplicate keys
- [ ] If I changed English content, I checked for matching translations and either fixed them too or noted which ones need updating

### If adding images
- [ ] Images are under 500 KB (compress large screenshots)
- [ ] Image filenames are descriptive (not `image1.png`)
- [ ] Images have `width` attributes to control sizing

### If adding a new workshop
- [ ] Workshop follows the standard folder structure (see CONTRIBUTING.md)
- [ ] `_index.md` has correct frontmatter (title, description, prereq, difficulty, icon, draft)
- [ ] Table of contents uses `<details>/<summary>` pattern
- [ ] All activities are numbered with correct `weight` values

### If translating
- [ ] Translated ALL user-visible text (titles, descriptions, button labels)
- [ ] Kept image paths, code blocks, and shortcodes unchanged
- [ ] YAML frontmatter keys are in English (only values are translated)
192 changes: 192 additions & 0 deletions .github/workflows/content-quality.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
name: Content Quality Checks

on:
pull_request:
paths:
- 'content/**'
- 'layouts/**'
- 'static/**'
- 'themes/**'
- 'config.toml'

jobs:
hugo-build:
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:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment on lines +14 to +30
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:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment on lines +31 to +146
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

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment on lines +147 to +192
Loading
Loading