Build Modules API #65
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: Build Modules API | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'modules/**/manifest.json' | |
| schedule: | |
| # Refresh reaction counts daily at 06:00 UTC | |
| - cron: '0 6 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| discussions: read | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build api/modules.json from manifests | |
| run: | | |
| python3 - <<'EOF' | |
| import json, os, glob | |
| from datetime import datetime | |
| manifests = sorted(glob.glob("modules/**/manifest.json", recursive=True)) | |
| items = [] | |
| for path in manifests: | |
| with open(path) as f: | |
| data = json.load(f) | |
| module_id = data.get("moduleId") or os.path.basename(os.path.dirname(path)) | |
| data["moduleId"] = module_id | |
| if not data.get("downloadUrl"): | |
| data["downloadUrl"] = f"https://raw.githubusercontent.com/AI-Hydro/Modules/main/modules/{module_id}/module.html" | |
| if not data.get("githubUrl"): | |
| data["githubUrl"] = f"https://github.com/AI-Hydro/Modules/tree/main/modules/{module_id}" | |
| if not data.get("createdAt"): | |
| data["createdAt"] = datetime.now().strftime("%Y-%m-%d") | |
| data["updatedAt"] = datetime.now().strftime("%Y-%m-%d") | |
| # Preserve existing reaction/download counts if already in api/modules.json | |
| existing_path = "api/modules.json" | |
| if os.path.exists(existing_path): | |
| with open(existing_path) as ef: | |
| existing = {e["moduleId"]: e for e in json.load(ef)} | |
| prev = existing.get(module_id, {}) | |
| data.setdefault("downloadCount", prev.get("downloadCount", 0)) | |
| data.setdefault("githubReactions", prev.get("githubReactions", 0)) | |
| data.setdefault("discussionUrl", prev.get("discussionUrl", "")) | |
| else: | |
| data.setdefault("downloadCount", 0) | |
| data.setdefault("githubReactions", 0) | |
| data.setdefault("discussionUrl", "") | |
| data.setdefault("isFeatured", False) | |
| items.append(data) | |
| os.makedirs("api", exist_ok=True) | |
| with open("api/modules.json", "w") as f: | |
| json.dump(items, f, indent=2) | |
| print(f"Built api/modules.json with {len(items)} module(s):") | |
| for item in items: | |
| print(f" - {item['moduleId']}: {item['title']}") | |
| EOF | |
| - name: Scrape GitHub Discussion reactions | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| python3 - <<'EOF' | |
| import json, subprocess, os | |
| api_path = "api/modules.json" | |
| if not os.path.exists(api_path): | |
| print("No api/modules.json found, skipping reaction scrape") | |
| exit(0) | |
| with open(api_path) as f: | |
| items = json.load(f) | |
| repo = "AI-Hydro/Modules" | |
| updated = False | |
| for item in items: | |
| module_id = item["moduleId"] | |
| title = item.get("title", module_id) | |
| discussion_url = item.get("discussionUrl", "") | |
| # Find or create a discussion for this module | |
| search_result = subprocess.run( | |
| ["gh", "api", f"repos/{repo}/discussions", | |
| "--jq", f'[.[] | select(.title == "Module: {title}")] | first'], | |
| capture_output=True, text=True | |
| ) | |
| discussion = None | |
| if search_result.returncode == 0 and search_result.stdout.strip() not in ("", "null"): | |
| try: | |
| discussion = json.loads(search_result.stdout.strip()) | |
| except Exception: | |
| pass | |
| if discussion: | |
| reactions_result = subprocess.run( | |
| ["gh", "api", f"repos/{repo}/discussions/{discussion['number']}/reactions", | |
| "--jq", "length"], | |
| capture_output=True, text=True | |
| ) | |
| if reactions_result.returncode == 0: | |
| try: | |
| count = int(reactions_result.stdout.strip()) | |
| if item.get("githubReactions") != count: | |
| item["githubReactions"] = count | |
| updated = True | |
| except ValueError: | |
| pass | |
| new_url = discussion.get("html_url", "") | |
| if new_url and item.get("discussionUrl") != new_url: | |
| item["discussionUrl"] = new_url | |
| updated = True | |
| if updated: | |
| with open(api_path, "w") as f: | |
| json.dump(items, f, indent=2) | |
| print("Updated reaction counts in api/modules.json") | |
| else: | |
| print("No reaction count changes") | |
| EOF | |
| - name: Commit updated API | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add api/modules.json | |
| if git diff --staged --quiet; then | |
| echo "No changes to api/modules.json" | |
| else | |
| git commit -m "ci: rebuild api/modules.json [skip ci]" | |
| git push | |
| fi |