🔧 Refactor HTML and Create JSON-Based Card System #36
Workflow file for this run
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: Payload-Based Content PR Handler | |
| on: | |
| pull_request: | |
| types: [opened, edited, synchronize] | |
| paths: | |
| - '.github/content-payloads/**' | |
| - 'base.json' | |
| pull_request_review: | |
| types: [submitted] | |
| jobs: | |
| validate-payload-pr: | |
| runs-on: ubuntu-latest | |
| if: github.event.pull_request.draft == false | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| persist-credentials: true | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| - name: Install dependencies | |
| run: | | |
| npm install -g marked js-yaml | |
| - name: Validate payload files | |
| run: | | |
| echo "🔍 Validating payload files..." | |
| python3 << 'EOF' | |
| import json | |
| import os | |
| import sys | |
| payloads_dir = '.github/content-payloads' | |
| if not os.path.exists(payloads_dir): | |
| print("❌ No payloads directory found") | |
| sys.exit(1) | |
| payload_files = [f for f in os.listdir(payloads_dir) if f.endswith('.json')] | |
| if not payload_files: | |
| print("❌ No payload files found") | |
| sys.exit(1) | |
| print(f"📋 Found {len(payload_files)} payload file(s)") | |
| for payload_file in payload_files: | |
| try: | |
| with open(os.path.join(payloads_dir, payload_file), 'r') as f: | |
| payload = json.load(f) | |
| # Validate required fields | |
| required_fields = ['version', 'type', 'metadata', 'content'] | |
| for field in required_fields: | |
| if field not in payload: | |
| print(f"❌ Missing required field '{field}' in {payload_file}") | |
| sys.exit(1) | |
| # Validate metadata | |
| required_metadata = ['title', 'category', 'subtopic'] | |
| for field in required_metadata: | |
| if field not in payload['metadata']: | |
| print(f"❌ Missing required metadata field '{field}' in {payload_file}") | |
| sys.exit(1) | |
| # Validate content | |
| required_content = ['id', 'title', 'description'] | |
| for field in required_content: | |
| if field not in payload['content']: | |
| print(f"❌ Missing required content field '{field}' in {payload_file}") | |
| sys.exit(1) | |
| print(f"✅ Validated payload: {payload_file}") | |
| except Exception as e: | |
| print(f"❌ Error validating {payload_file}: {e}") | |
| sys.exit(1) | |
| print("✅ All payload files are valid") | |
| EOF | |
| - name: Validate base configuration | |
| run: | | |
| echo "🔍 Validating base configuration..." | |
| if [ -f "base.json" ]; then | |
| python3 -m json.tool base.json > /dev/null | |
| echo "✅ Base configuration is valid" | |
| else | |
| echo "❌ Base configuration not found" | |
| exit 1 | |
| fi | |
| - name: Test content merge engine | |
| run: | | |
| echo "🔄 Testing content merge engine..." | |
| node scripts/merge-content.js | |
| if [ -f "content-config.json" ]; then | |
| echo "✅ Content merge engine generated configuration successfully" | |
| else | |
| echo "❌ Content merge engine failed to generate configuration" | |
| exit 1 | |
| fi | |
| - name: Validate GitHub repository access | |
| run: | | |
| echo "🔍 Validating GitHub repository access..." | |
| python3 << 'EOF' | |
| import json | |
| import requests | |
| import sys | |
| import os | |
| payloads_dir = '.github/content-payloads' | |
| for payload_file in os.listdir(payloads_dir): | |
| if payload_file.endswith('.json'): | |
| with open(os.path.join(payloads_dir, payload_file), 'r') as f: | |
| payload = json.load(f) | |
| if 'repo' in payload['content'] and 'path' in payload['content']: | |
| repo = payload['content']['repo'] | |
| path = payload['content']['path'] | |
| # Test GitHub API access | |
| url = f"https://api.github.com/repos/{repo}" | |
| response = requests.get(url) | |
| if response.status_code == 200: | |
| print(f"✅ Repository accessible: {repo}") | |
| else: | |
| print(f"❌ Repository not accessible: {repo} (Status: {response.status_code})") | |
| sys.exit(1) | |
| # Test file access | |
| raw_url = f"https://raw.githubusercontent.com/{repo}/main/{path}" | |
| file_response = requests.head(raw_url) | |
| if file_response.status_code == 200: | |
| print(f"✅ File accessible: {repo}/{path}") | |
| else: | |
| print(f"❌ File not accessible: {repo}/{path} (Status: {file_response.status_code})") | |
| sys.exit(1) | |
| EOF | |
| - name: Generate site preview | |
| run: | | |
| echo "🏗️ Generating site preview with merged content..." | |
| node scripts/update-navigation.js | |
| echo "✅ Site preview generated successfully" | |
| # Verify the HTML files were updated with new content | |
| echo "🔍 Verifying updated HTML files..." | |
| node -e " | |
| const fs = require('fs'); | |
| const config = JSON.parse(fs.readFileSync('content-config.json', 'utf8')); | |
| // Get all new content titles from the merged config | |
| const newContentTitles = []; | |
| for (const category of Object.values(config.categories)) { | |
| for (const subtopic of Object.values(category.subtopics)) { | |
| for (const content of subtopic.content) { | |
| if (content.status === 'active') { | |
| newContentTitles.push(content.title); | |
| } | |
| } | |
| } | |
| } | |
| // Check if any HTML files contain the new content | |
| const htmlFiles = ['index.html', 'kubernetes.html', 'algorithms.html', 'networking.html', 'databases.html', 'microservices.html', 'system-design.html']; | |
| let foundContent = 0; | |
| for (const htmlFile of htmlFiles) { | |
| if (fs.existsSync(htmlFile)) { | |
| const content = fs.readFileSync(htmlFile, 'utf8'); | |
| for (const title of newContentTitles) { | |
| if (content.includes(title)) { | |
| foundContent++; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if (foundContent > 0) { | |
| console.log('✅ HTML files updated with new content'); | |
| } else { | |
| console.log('❌ HTML files not updated properly'); | |
| process.exit(1); | |
| } | |
| " | |
| - name: Commit generated files | |
| run: | | |
| echo "📝 Committing generated HTML files to PR..." | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| # Check if there are changes to commit | |
| if git diff --quiet && git diff --cached --quiet; then | |
| echo "No changes to commit" | |
| else | |
| git add *.html content-config.json | |
| git commit -m "chore: Auto-generate HTML files with new content | |
| - Generated by payload workflow | |
| - Includes updated navigation and content | |
| - Auto-commit for Vercel preview deployment" | |
| echo "🔄 Pushing generated files to PR branch..." | |
| git push origin HEAD:${{ github.head_ref }} | |
| fi | |
| - name: Upload preview artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: site-preview | |
| path: | | |
| index.html | |
| kubernetes.html | |
| algorithms.html | |
| networking.html | |
| databases.html | |
| microservices.html | |
| system-design.html | |
| content-config.json | |
| retention-days: 7 | |
| - name: Comment on PR | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| try { | |
| // Read the generated content config | |
| const config = JSON.parse(fs.readFileSync('content-config.json', 'utf8')); | |
| // Count payloads | |
| const payloadsDir = '.github/content-payloads'; | |
| let payloadCount = 0; | |
| if (fs.existsSync(payloadsDir)) { | |
| const files = fs.readdirSync(payloadsDir); | |
| payloadCount = files.filter(f => f.endsWith('.json')).length; | |
| } | |
| // Generate statistics | |
| let totalContent = 0; | |
| let totalVisualizations = 0; | |
| let totalGuides = 0; | |
| let categoriesWithContent = 0; | |
| for (const category of Object.values(config.categories)) { | |
| if (Object.keys(category.subtopics).length > 0) { | |
| categoriesWithContent++; | |
| for (const subtopic of Object.values(category.subtopics)) { | |
| for (const contentItem of subtopic.content) { | |
| if (contentItem.status === 'active') { | |
| totalContent++; | |
| if (contentItem.type === 'visualization') { | |
| totalVisualizations++; | |
| } else { | |
| totalGuides++; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| const comment = `## 🎉 Payload-Based Content Validation Successful! | |
| Your content payload(s) have been validated and merged successfully! | |
| ### 📊 Impact Summary | |
| - **Payload files processed**: ${payloadCount} | |
| - **Total content items**: ${totalContent} | |
| - **Visualizations**: ${totalVisualizations} | |
| - **Guides**: ${totalGuides} | |
| - **Active categories**: ${categoriesWithContent} | |
| - **Configuration version**: ${config.version} | |
| ### ✅ Validation Results | |
| - Payload file structure is valid | |
| - Base configuration is valid | |
| - Content merge engine executed successfully | |
| - GitHub repository access confirmed | |
| - File paths are accessible | |
| - Site preview generated successfully | |
| ### 🏗️ Site Preview Generated | |
| The workflow has generated a complete site preview with your changes: | |
| - Updated navigation structure | |
| - Modified category pages | |
| - Updated statistics | |
| - Preview artifacts uploaded for review | |
| ### 🚀 Next Steps | |
| Once this PR is merged, the content will be automatically: | |
| 1. Merged into the base configuration | |
| 2. Integrated into the site navigation | |
| 3. Made accessible through the template renderer | |
| 4. Included in site statistics | |
| 5. Deployed live immediately | |
| **Review the generated preview artifacts to see exactly how the site will look!** 🎯`; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); | |
| } catch (error) { | |
| console.error('Error creating comment:', error); | |
| } | |
| auto-merge-ready: | |
| runs-on: ubuntu-latest | |
| needs: validate-payload-pr | |
| if: github.event.pull_request.draft == false && github.event.review.state == 'approved' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Auto-merge PR | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const { owner, repo, number } = context.issue; | |
| // Check if PR is ready for merge | |
| const pr = await github.rest.pulls.get({ | |
| owner, | |
| repo, | |
| pull_number: number | |
| }); | |
| if (pr.data.mergeable && pr.data.mergeable_state === 'clean') { | |
| await github.rest.pulls.merge({ | |
| owner, | |
| repo, | |
| pull_number: number, | |
| merge_method: 'squash' | |
| }); | |
| console.log('✅ PR merged successfully!'); | |
| } else { | |
| console.log('❌ PR is not ready for merge'); | |
| } |