feat(blog): publish hybrid-project-bots #16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| checks: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check GTM snippet on all HTML pages | |
| run: | | |
| GTM_ID="G-MN16CB3V3T" | |
| MISSING=() | |
| while IFS= read -r file; do | |
| # Skip backup directory | |
| [[ "$file" == */backup/* ]] && continue | |
| if ! grep -q "$GTM_ID" "$file"; then | |
| MISSING+=("$file") | |
| fi | |
| done < <(find . -name "*.html" -type f) | |
| if [ ${#MISSING[@]} -gt 0 ]; then | |
| echo "::error::Google Tag Manager ($GTM_ID) missing from the following pages:" | |
| for f in "${MISSING[@]}"; do | |
| echo " ❌ $f" | |
| done | |
| echo "" | |
| echo "Add this snippet to <head> after <meta charset=\"UTF-8\">:" | |
| echo "" | |
| echo ' <!-- Google tag (gtag.js) -->' | |
| echo " <script async src=\"https://www.googletagmanager.com/gtag/js?id=$GTM_ID\"></script>" | |
| echo ' <script>' | |
| echo ' window.dataLayer = window.dataLayer || [];' | |
| echo ' function gtag(){dataLayer.push(arguments);}' | |
| echo " gtag('js', new Date());" | |
| echo " gtag('config', '$GTM_ID');" | |
| echo ' </script>' | |
| exit 1 | |
| fi | |
| echo "✅ All HTML pages have GTM ($GTM_ID)" | |
| - name: Validate HTML structure | |
| run: | | |
| ERRORS=() | |
| while IFS= read -r file; do | |
| [[ "$file" == */backup/* ]] && continue | |
| # Check for required meta tags | |
| if ! grep -q '<meta charset' "$file"; then | |
| ERRORS+=("$file: missing <meta charset>") | |
| fi | |
| if ! grep -q '<title>' "$file"; then | |
| ERRORS+=("$file: missing <title>") | |
| fi | |
| if ! grep -q '<meta name="description"' "$file"; then | |
| ERRORS+=("$file: missing <meta name=\"description\">") | |
| fi | |
| done < <(find . -name "*.html" -type f) | |
| if [ ${#ERRORS[@]} -gt 0 ]; then | |
| echo "::warning::HTML structure issues found:" | |
| for e in "${ERRORS[@]}"; do | |
| echo " ⚠️ $e" | |
| done | |
| # Warning only, don't fail | |
| else | |
| echo "✅ All HTML pages have required meta tags" | |
| fi | |
| - name: Validate blog posts.json | |
| run: | | |
| POSTS_JSON="./blog/posts.json" | |
| if [ ! -f "$POSTS_JSON" ]; then | |
| echo "::error::blog/posts.json not found!" | |
| exit 1 | |
| fi | |
| # Validate JSON syntax | |
| if ! python3 -m json.tool "$POSTS_JSON" > /dev/null 2>&1; then | |
| echo "::error::blog/posts.json has invalid JSON syntax" | |
| exit 1 | |
| fi | |
| ERRORS=() | |
| # Check every post in posts.json has a matching HTML file | |
| for url in $(python3 -c "import json; [print(p['url']) for p in json.load(open('$POSTS_JSON'))]"); do | |
| filepath=".${url}" | |
| if [ ! -f "$filepath" ]; then | |
| ERRORS+=("Post in posts.json references missing file: $filepath") | |
| fi | |
| done | |
| # Check every HTML file in blog/posts/ is listed in posts.json | |
| LISTED_URLS=$(python3 -c "import json; [print(p['url']) for p in json.load(open('$POSTS_JSON'))]") | |
| while IFS= read -r file; do | |
| rel_path="/${file#./}" | |
| if ! echo "$LISTED_URLS" | grep -qF "$rel_path"; then | |
| ERRORS+=("Blog post not in posts.json: $rel_path") | |
| fi | |
| done < <(find ./blog/posts -name "*.html" -type f) | |
| if [ ${#ERRORS[@]} -gt 0 ]; then | |
| echo "::error::Blog manifest issues found:" | |
| for e in "${ERRORS[@]}"; do | |
| echo " ❌ $e" | |
| done | |
| echo "" | |
| echo "Update blog/posts.json to match the actual blog post files." | |
| exit 1 | |
| fi | |
| POST_COUNT=$(python3 -c "import json; print(len(json.load(open('$POSTS_JSON'))))") | |
| echo "✅ blog/posts.json is valid ($POST_COUNT posts, all files exist)" |