Skip to content
This repository was archived by the owner on Apr 23, 2026. It is now read-only.
Merged
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
33 changes: 33 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# EditorConfig — consistent formatting across editors and contributors
# https://editorconfig.org

root = true

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

[*.md]
trim_trailing_whitespace = false

[*.{html,css}]
indent_size = 2

[*.js]
indent_size = 2

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

[*.py]
indent_size = 4

[*.{r,R}]
indent_size = 2

[Makefile]
indent_style = tab
51 changes: 51 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Auto-detect text files and normalise line endings to LF
* text=auto eol=lf

# Explicitly declare text files
*.html text eol=lf
*.css text eol=lf
*.js text eol=lf
*.ts text eol=lf
*.json text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.toml text eol=lf
*.xml text eol=lf
*.svg text eol=lf
*.txt text eol=lf
*.sh text eol=lf
*.py text eol=lf
*.r text eol=lf
*.R text eol=lf

# Windows-specific files keep CRLF
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf

# Binary files — no diff, no merge, no line-ending conversion
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.webp binary
*.avif binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
*.otf binary
*.pdf binary
*.zip binary
*.gz binary
*.tar binary
*.mp4 binary
*.webm binary
*.mp3 binary
*.ogg binary

# Lock files — exact merge to prevent conflicts
package-lock.json merge=ours
yarn.lock merge=ours
42 changes: 42 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Commit message hook: enforce prefix convention from CONTRIBUTING.md
set -euo pipefail

MSG_FILE="$1"
MSG=$(head -1 "$MSG_FILE")

RED='\033[0;31m'
NC='\033[0m'

# Skip merge commits and fixup/squash commits
if echo "$MSG" | grep -qE '^(Merge|Revert|fixup!|squash!)'; then
exit 0
fi

# Allowed prefixes (from CONTRIBUTING.md)
PREFIXES="Add|Fix|Update|Translate|Docs|Refactor|Test|CI|Chore|Merge"

if ! echo "$MSG" | grep -qE "^(${PREFIXES}):"; then
echo -e "${RED}Invalid commit message format.${NC}"
echo ""
echo "Commit message must start with one of these prefixes:"
echo " Add: — New feature, course, or tool"
echo " Fix: — Bug fix or broken link"
echo " Update: — Improvement to existing content or code"
echo " Translate: — Translation work"
echo " Docs: — Documentation changes"
echo " Refactor: — Code restructuring (no behaviour change)"
echo " Test: — Adding or updating tests"
echo " CI: — CI/CD pipeline changes"
echo " Chore: — Maintenance (deps, configs, tooling)"
echo ""
echo "Example: Add: interactive Theory of Change lab"
echo ""
echo "Your message was: $MSG"
exit 1
fi

# Warn if subject line is too long
if [ ${#MSG} -gt 72 ]; then
echo -e "\033[0;33mWARNING:\033[0m Commit subject is ${#MSG} chars (recommended max: 72)"
fi
65 changes: 65 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# Pre-commit hook: catch common mistakes before they're committed
set -euo pipefail

RED='\033[0;31m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

echo "Running pre-commit checks..."

# 1. Block commits of sensitive files
SENSITIVE_PATTERNS=('.env' '.env.local' '.env.production' 'credentials.json' '*.pem' '*.key')
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)

for pattern in "${SENSITIVE_PATTERNS[@]}"; do
while IFS= read -r file; do
if [[ -n "$file" ]]; then
echo -e "${RED}BLOCKED:${NC} Refusing to commit sensitive file: $file"
echo "If this is intentional, use: git commit --no-verify"
exit 1
fi
done < <(echo "$STAGED_FILES" | grep -E "^${pattern//\*/.*}$" 2>/dev/null || true)
done

# 2. Check for debug/console statements in staged JS/TS/Python files
CODE_FILES=$(echo "$STAGED_FILES" | grep -E '\.(js|ts|py)$' || true)
if [ -n "$CODE_FILES" ]; then
ISSUES=0
while IFS= read -r file; do
if git diff --cached "$file" | grep -E '^\+.*console\.(log|debug|warn)\(' | grep -v '// keep' > /dev/null 2>&1; then
echo -e "${YELLOW}WARNING:${NC} console.log/debug/warn found in $file"
ISSUES=$((ISSUES + 1))
fi
if git diff --cached "$file" | grep -E '^\+.*(debugger|breakpoint\(\))' > /dev/null 2>&1; then
echo -e "${RED}BLOCKED:${NC} debugger statement found in $file"
exit 1
fi
done <<< "$CODE_FILES"
if [ $ISSUES -gt 0 ]; then
echo -e "${YELLOW}Found $ISSUES file(s) with console statements. Consider removing them.${NC}"
fi
fi

# 3. Check for merge conflict markers
if [ -n "$STAGED_FILES" ]; then
while IFS= read -r file; do
if [ -f "$file" ] && grep -rn '<<<<<<<\|=======\|>>>>>>>' "$file" > /dev/null 2>&1; then
echo -e "${RED}BLOCKED:${NC} Merge conflict markers found in $file"
exit 1
fi
done <<< "$STAGED_FILES"
fi

# 4. Warn on large files (> 500KB)
while IFS= read -r file; do
if [ -f "$file" ]; then
SIZE=$(wc -c < "$file")
if [ "$SIZE" -gt 512000 ]; then
SIZE_KB=$((SIZE / 1024))
echo -e "${YELLOW}WARNING:${NC} Large file (${SIZE_KB}KB): $file"
fi
fi
done <<< "$STAGED_FILES"

echo "Pre-commit checks passed."
67 changes: 67 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Environment files (contain secrets)
.env
.env.*
!.env.example

# Dependencies
node_modules/
jspm_packages/
bower_components/

# Python
__pycache__/
*.py[cod]
*$py.class
*.egg-info/
.eggs/
venv/
.venv/
env/

# Build output
build/
dist/
out/
coverage/
*.lcov
.nyc_output/

# OS files
.DS_Store
Thumbs.db
Desktop.ini

# Editor files
.vscode/
.idea/
*.swp
*.swo
*~
*.sublime-project
*.sublime-workspace

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
*.pid
*.pid.lock
*.seed

# Package caches
.npm
.eslintcache
.stylelintcache
*.tsbuildinfo
*.tgz

# Misc
.cache/
.parcel-cache/
.next/
.nuxt/
.serverless/
.firebase/
19 changes: 19 additions & 0 deletions .gitmessage
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# <prefix>: <short summary in imperative mood, max 72 chars>
#
# Prefixes:
# Add: — New feature, course, or tool
# Fix: — Bug fix or broken link
# Update: — Improvement to existing content or code
# Translate: — Translation work
# Docs: — Documentation changes
# Refactor: — Code restructuring (no behaviour change)
# Test: — Adding or updating tests
# CI: — CI/CD pipeline changes
# Chore: — Maintenance (deps, configs, tooling)
#
# Example:
# Add: interactive Theory of Change lab
# Fix: broken nav dropdown on mobile Safari
#
# Body (optional): explain *why*, not *what* — the diff shows what changed.
# Wrap at 72 characters.
Loading