validate-registry.yml 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. name: Validate Registry on PR
  2. # This workflow validates the registry.json and prompt library structure on all PRs.
  3. #
  4. # For bot-created PRs (like automated version bumps), the workflow won't trigger automatically
  5. # due to GitHub's security restrictions. In those cases, you can manually trigger this workflow:
  6. #
  7. # Option 1 - Run Validation:
  8. # 1. Go to Actions > Validate Registry on PR > Run workflow
  9. # 2. Enter the PR number (e.g., 106)
  10. # 3. Leave "skip_validation" unchecked
  11. # 4. Click "Run workflow"
  12. #
  13. # Option 2 - Admin Bypass (for trusted bot PRs):
  14. # 1. Go to Actions > Validate Registry on PR > Run workflow
  15. # 2. Enter the PR number (e.g., 106)
  16. # 3. Check "skip_validation" checkbox
  17. # 4. Click "Run workflow"
  18. # 5. The check will pass immediately without running validation
  19. on:
  20. # Use pull_request_target to allow running on bot-created PRs
  21. # This also allows the workflow to write to the PR branch
  22. pull_request_target:
  23. branches:
  24. - main
  25. - dev
  26. # Removed paths filter - this check is required by repository ruleset
  27. # so it must run on ALL PRs to prevent blocking merges
  28. workflow_dispatch:
  29. inputs:
  30. pr_number:
  31. description: 'PR number to validate (for manual runs on bot-created PRs)'
  32. required: false
  33. type: number
  34. skip_validation:
  35. description: 'Skip validation checks (maintainer override)'
  36. required: false
  37. type: boolean
  38. default: false
  39. permissions:
  40. contents: write
  41. pull-requests: write
  42. jobs:
  43. validate-and-update:
  44. runs-on: ubuntu-latest
  45. steps:
  46. - name: Admin bypass check
  47. if: github.event_name == 'workflow_dispatch' && github.event.inputs.skip_validation == 'true'
  48. run: |
  49. echo "## ✅ Validation Bypassed (Admin Override)" >> $GITHUB_STEP_SUMMARY
  50. echo "" >> $GITHUB_STEP_SUMMARY
  51. echo "Validation checks skipped by maintainer." >> $GITHUB_STEP_SUMMARY
  52. echo "PR: #${{ github.event.inputs.pr_number }}" >> $GITHUB_STEP_SUMMARY
  53. echo "" >> $GITHUB_STEP_SUMMARY
  54. echo "The workflow will complete successfully without running validation steps." >> $GITHUB_STEP_SUMMARY
  55. - name: Checkout repository (for manual runs)
  56. if: github.event_name == 'workflow_dispatch' && github.event.inputs.skip_validation != 'true'
  57. uses: actions/checkout@v4
  58. with:
  59. fetch-depth: 0
  60. token: ${{ secrets.GITHUB_TOKEN }}
  61. - name: Get PR details (for manual runs)
  62. if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_number != '' && github.event.inputs.skip_validation != 'true'
  63. id: get_pr
  64. run: |
  65. PR_DATA=$(gh pr view ${{ github.event.inputs.pr_number }} --json headRefName,headRepository,headRepositoryOwner)
  66. echo "head_ref=$(echo $PR_DATA | jq -r '.headRefName')" >> $GITHUB_OUTPUT
  67. echo "head_repo=$(echo $PR_DATA | jq -r '.headRepositoryOwner.login + "/" + .headRepository.name')" >> $GITHUB_OUTPUT
  68. env:
  69. GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  70. - name: Checkout PR branch
  71. if: github.event.inputs.skip_validation != 'true'
  72. uses: actions/checkout@v4
  73. with:
  74. # For manual runs: use PR details from get_pr step
  75. # For PR events: use event data
  76. repository: ${{ github.event_name == 'workflow_dispatch' && steps.get_pr.outputs.head_repo || github.event.pull_request.head.repo.full_name }}
  77. ref: ${{ github.event_name == 'workflow_dispatch' && steps.get_pr.outputs.head_ref || github.event.pull_request.head.ref }}
  78. fetch-depth: 0
  79. token: ${{ secrets.GITHUB_TOKEN }}
  80. - name: Detect fork PR
  81. if: github.event.inputs.skip_validation != 'true'
  82. id: fork_check
  83. run: |
  84. # For manual runs, use the fetched PR data
  85. if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
  86. HEAD_REPO="${{ steps.get_pr.outputs.head_repo }}"
  87. else
  88. HEAD_REPO="${{ github.event.pull_request.head.repo.full_name }}"
  89. fi
  90. if [ "$HEAD_REPO" != "${{ github.repository }}" ]; then
  91. echo "is_fork=true" >> $GITHUB_OUTPUT
  92. echo "🔀 Fork PR detected from: $HEAD_REPO"
  93. else
  94. echo "is_fork=false" >> $GITHUB_OUTPUT
  95. echo "📝 Internal PR detected"
  96. fi
  97. - name: Install dependencies
  98. if: github.event.inputs.skip_validation != 'true'
  99. run: |
  100. sudo apt-get update
  101. sudo apt-get install -y jq
  102. - name: Install Bun
  103. if: github.event.inputs.skip_validation != 'true'
  104. uses: oven-sh/setup-bun@v2
  105. with:
  106. bun-version: latest
  107. - name: Install dependencies
  108. if: github.event.inputs.skip_validation != 'true'
  109. run: |
  110. # Install root dependencies (glob package needed for validation script)
  111. bun install --frozen-lockfile
  112. - name: Make scripts executable
  113. if: github.event.inputs.skip_validation != 'true'
  114. run: |
  115. chmod +x scripts/registry/validate-registry.sh
  116. chmod +x scripts/registry/auto-detect-components.sh
  117. chmod +x scripts/registry/register-component.sh
  118. chmod +x scripts/prompts/validate-pr.sh
  119. - name: Auto-detect new components
  120. if: github.event.inputs.skip_validation != 'true'
  121. id: auto_detect
  122. run: |
  123. echo "## 🔍 Auto-Detection Results" >> $GITHUB_STEP_SUMMARY
  124. echo "" >> $GITHUB_STEP_SUMMARY
  125. # Run auto-detect in dry-run mode first to see what would be added
  126. if ./scripts/registry/auto-detect-components.sh --dry-run > /tmp/detect-output.txt 2>&1; then
  127. cat /tmp/detect-output.txt >> $GITHUB_STEP_SUMMARY
  128. # Check if new components were found
  129. if grep -q "Found.*new component" /tmp/detect-output.txt; then
  130. echo "new_components=true" >> $GITHUB_OUTPUT
  131. echo "" >> $GITHUB_STEP_SUMMARY
  132. echo "⚠️ New components detected - will auto-add to registry" >> $GITHUB_STEP_SUMMARY
  133. else
  134. echo "new_components=false" >> $GITHUB_OUTPUT
  135. echo "✅ No new components found" >> $GITHUB_STEP_SUMMARY
  136. fi
  137. else
  138. echo "new_components=false" >> $GITHUB_OUTPUT
  139. echo "❌ Auto-detection failed" >> $GITHUB_STEP_SUMMARY
  140. fi
  141. - name: Add new components to registry
  142. if: steps.auto_detect.outputs.new_components == 'true' && github.event.inputs.skip_validation != 'true'
  143. run: |
  144. echo "## 📝 Adding New Components" >> $GITHUB_STEP_SUMMARY
  145. echo "" >> $GITHUB_STEP_SUMMARY
  146. ./scripts/registry/auto-detect-components.sh --auto-add | tee -a $GITHUB_STEP_SUMMARY
  147. - name: Validate prompt library structure
  148. if: github.event.inputs.skip_validation != 'true'
  149. id: validate_prompts
  150. run: |
  151. echo "## 🔍 Prompt Library Validation" >> $GITHUB_STEP_SUMMARY
  152. echo "" >> $GITHUB_STEP_SUMMARY
  153. if ./scripts/prompts/validate-pr.sh > /tmp/prompt-validation.txt 2>&1; then
  154. echo "prompt_validation=passed" >> $GITHUB_OUTPUT
  155. echo "✅ Prompt library structure is valid!" >> $GITHUB_STEP_SUMMARY
  156. echo "" >> $GITHUB_STEP_SUMMARY
  157. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  158. cat /tmp/prompt-validation.txt >> $GITHUB_STEP_SUMMARY
  159. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  160. else
  161. echo "prompt_validation=failed" >> $GITHUB_OUTPUT
  162. echo "❌ Prompt library validation failed!" >> $GITHUB_STEP_SUMMARY
  163. echo "" >> $GITHUB_STEP_SUMMARY
  164. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  165. cat /tmp/prompt-validation.txt >> $GITHUB_STEP_SUMMARY
  166. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  167. echo "" >> $GITHUB_STEP_SUMMARY
  168. echo "**Architecture:**" >> $GITHUB_STEP_SUMMARY
  169. echo "- Agent files (.opencode/agent/**/*.md) = Canonical defaults" >> $GITHUB_STEP_SUMMARY
  170. echo "- Prompt variants (.opencode/prompts/<agent>/<model>.md) = Model-specific" >> $GITHUB_STEP_SUMMARY
  171. echo "- default.md files should NOT exist" >> $GITHUB_STEP_SUMMARY
  172. echo "- Agents organized in category subdirectories (core/, development/, content/, etc.)" >> $GITHUB_STEP_SUMMARY
  173. echo "" >> $GITHUB_STEP_SUMMARY
  174. echo "See [CONTRIBUTING.md](docs/contributing/CONTRIBUTING.md) for details" >> $GITHUB_STEP_SUMMARY
  175. exit 1
  176. fi
  177. - name: Validate markdown context links
  178. if: github.event.inputs.skip_validation != 'true'
  179. id: validate_context_links
  180. run: |
  181. echo "## 🔗 Context Link Validation" >> $GITHUB_STEP_SUMMARY
  182. echo "" >> $GITHUB_STEP_SUMMARY
  183. if bun run scripts/validation/validate-markdown-links.ts > /tmp/context-link-validation.txt 2>&1; then
  184. echo "context_links=passed" >> $GITHUB_OUTPUT
  185. echo "✅ Context markdown links are valid!" >> $GITHUB_STEP_SUMMARY
  186. echo "" >> $GITHUB_STEP_SUMMARY
  187. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  188. cat /tmp/context-link-validation.txt >> $GITHUB_STEP_SUMMARY
  189. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  190. else
  191. echo "context_links=failed" >> $GITHUB_OUTPUT
  192. echo "❌ Context markdown link validation failed!" >> $GITHUB_STEP_SUMMARY
  193. echo "" >> $GITHUB_STEP_SUMMARY
  194. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  195. cat /tmp/context-link-validation.txt >> $GITHUB_STEP_SUMMARY
  196. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  197. exit 1
  198. fi
  199. - name: Validate registry
  200. if: github.event.inputs.skip_validation != 'true'
  201. id: validate
  202. run: |
  203. echo "## ✅ Registry Validation" >> $GITHUB_STEP_SUMMARY
  204. echo "" >> $GITHUB_STEP_SUMMARY
  205. # Use TypeScript validator (fast and reliable)
  206. # Run validation and capture output (show in logs AND save to file)
  207. if bun run scripts/registry/validate-registry.ts 2>&1 | tee /tmp/validation-output.txt; then
  208. echo "validation=passed" >> $GITHUB_OUTPUT
  209. echo "✅ All registry paths are valid!" >> $GITHUB_STEP_SUMMARY
  210. echo "" >> $GITHUB_STEP_SUMMARY
  211. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  212. cat /tmp/validation-output.txt >> $GITHUB_STEP_SUMMARY
  213. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  214. else
  215. echo "validation=failed" >> $GITHUB_OUTPUT
  216. echo "❌ Registry validation failed!" >> $GITHUB_STEP_SUMMARY
  217. echo "" >> $GITHUB_STEP_SUMMARY
  218. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  219. cat /tmp/validation-output.txt >> $GITHUB_STEP_SUMMARY
  220. echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
  221. echo "" >> $GITHUB_STEP_SUMMARY
  222. echo "**Check the logs above for detailed error output**" >> $GITHUB_STEP_SUMMARY
  223. exit 1
  224. fi
  225. - name: Commit registry updates (Internal PRs only)
  226. if: |
  227. github.event.inputs.skip_validation != 'true' &&
  228. steps.fork_check.outputs.is_fork == 'false' &&
  229. steps.auto_detect.outputs.new_components == 'true' &&
  230. steps.validate_prompts.outputs.prompt_validation == 'passed' &&
  231. steps.validate.outputs.validation == 'passed'
  232. run: |
  233. git config --local user.email "github-actions[bot]@users.noreply.github.com"
  234. git config --local user.name "github-actions[bot]"
  235. if ! git diff --quiet registry.json; then
  236. git add registry.json
  237. git commit -m "chore: auto-update registry with new components [skip ci]"
  238. # For manual runs, use the fetched branch name
  239. BRANCH_NAME="${{ github.event_name == 'workflow_dispatch' && steps.get_pr.outputs.head_ref || github.event.pull_request.head.ref }}"
  240. git push origin "$BRANCH_NAME"
  241. echo "## 🚀 Registry Updated" >> $GITHUB_STEP_SUMMARY
  242. echo "" >> $GITHUB_STEP_SUMMARY
  243. echo "Registry has been automatically updated with new components." >> $GITHUB_STEP_SUMMARY
  244. echo "Changes have been pushed to this PR branch." >> $GITHUB_STEP_SUMMARY
  245. else
  246. echo "## ℹ️ No Changes to Commit" >> $GITHUB_STEP_SUMMARY
  247. echo "" >> $GITHUB_STEP_SUMMARY
  248. echo "Registry is already up to date." >> $GITHUB_STEP_SUMMARY
  249. fi
  250. - name: Fork PR notice
  251. if: |
  252. github.event.inputs.skip_validation != 'true' &&
  253. steps.fork_check.outputs.is_fork == 'true' &&
  254. steps.auto_detect.outputs.new_components == 'true' &&
  255. steps.validate_prompts.outputs.prompt_validation == 'passed' &&
  256. steps.validate.outputs.validation == 'passed'
  257. uses: actions/github-script@v7
  258. with:
  259. script: |
  260. github.rest.issues.createComment({
  261. issue_number: context.issue.number,
  262. owner: context.repo.owner,
  263. repo: context.repo.repo,
  264. body: `## 📝 Registry Update Needed
  265. Hi @${{ github.event.pull_request.user.login }}! 👋
  266. New components were detected in your PR. Since this is a fork PR, I can't auto-commit the registry updates for security reasons.
  267. **Please run these commands locally:**
  268. \`\`\`bash
  269. ./scripts/registry/auto-detect-components.sh --auto-add
  270. git add registry.json
  271. git commit -m "chore: update registry"
  272. git push
  273. \`\`\`
  274. Once you push the updated registry, the checks will pass! ✅`
  275. });
  276. - name: Fork PR summary
  277. if: steps.fork_check.outputs.is_fork == 'true' && github.event.inputs.skip_validation != 'true'
  278. run: |
  279. echo "## 🔀 Fork PR Detected" >> $GITHUB_STEP_SUMMARY
  280. echo "" >> $GITHUB_STEP_SUMMARY
  281. echo "This is an external contribution - thank you! 🎉" >> $GITHUB_STEP_SUMMARY
  282. echo "" >> $GITHUB_STEP_SUMMARY
  283. if [ "${{ steps.auto_detect.outputs.new_components }}" == "true" ]; then
  284. echo "⚠️ **Action Required:** New components detected" >> $GITHUB_STEP_SUMMARY
  285. echo "" >> $GITHUB_STEP_SUMMARY
  286. echo "A comment has been posted with instructions to update the registry." >> $GITHUB_STEP_SUMMARY
  287. else
  288. echo "✅ No registry updates needed" >> $GITHUB_STEP_SUMMARY
  289. fi
  290. - name: Post validation summary
  291. if: always()
  292. run: |
  293. echo "" >> $GITHUB_STEP_SUMMARY
  294. echo "---" >> $GITHUB_STEP_SUMMARY
  295. echo "" >> $GITHUB_STEP_SUMMARY
  296. PROMPT_VALID="${{ steps.validate_prompts.outputs.prompt_validation }}"
  297. CONTEXT_LINKS_VALID="${{ steps.validate_context_links.outputs.context_links }}"
  298. REGISTRY_VALID="${{ steps.validate.outputs.validation }}"
  299. if [ "$PROMPT_VALID" = "passed" ] && [ "$CONTEXT_LINKS_VALID" = "passed" ] && [ "$REGISTRY_VALID" = "passed" ]; then
  300. echo "### ✅ All Validations Passed" >> $GITHUB_STEP_SUMMARY
  301. echo "" >> $GITHUB_STEP_SUMMARY
  302. echo "- ✅ Prompt library structure is valid" >> $GITHUB_STEP_SUMMARY
  303. echo "- ✅ Context markdown links are valid" >> $GITHUB_STEP_SUMMARY
  304. echo "- ✅ Registry paths are valid" >> $GITHUB_STEP_SUMMARY
  305. echo "" >> $GITHUB_STEP_SUMMARY
  306. echo "This PR is ready for review!" >> $GITHUB_STEP_SUMMARY
  307. else
  308. echo "### ❌ Validation Failed" >> $GITHUB_STEP_SUMMARY
  309. echo "" >> $GITHUB_STEP_SUMMARY
  310. if [ "$PROMPT_VALID" != "passed" ]; then
  311. echo "- ❌ Prompt library validation failed" >> $GITHUB_STEP_SUMMARY
  312. else
  313. echo "- ✅ Prompt library validation passed" >> $GITHUB_STEP_SUMMARY
  314. fi
  315. if [ "$CONTEXT_LINKS_VALID" != "passed" ]; then
  316. echo "- ❌ Context markdown link validation failed" >> $GITHUB_STEP_SUMMARY
  317. else
  318. echo "- ✅ Context markdown link validation passed" >> $GITHUB_STEP_SUMMARY
  319. fi
  320. if [ "$REGISTRY_VALID" != "passed" ]; then
  321. echo "- ❌ Registry validation failed" >> $GITHUB_STEP_SUMMARY
  322. else
  323. echo "- ✅ Registry validation passed" >> $GITHUB_STEP_SUMMARY
  324. fi
  325. echo "" >> $GITHUB_STEP_SUMMARY
  326. echo "Please fix the issues above before merging." >> $GITHUB_STEP_SUMMARY
  327. fi
  328. - name: Fail if validation failed
  329. if: |
  330. (steps.validate_prompts.outputs.prompt_validation == 'failed' || steps.validate_context_links.outputs.context_links == 'failed' || steps.validate.outputs.validation == 'failed') &&
  331. github.event.inputs.skip_validation != 'true'
  332. run: |
  333. echo "❌ Validation failed - blocking PR merge"
  334. echo "Maintainer can override by running workflow manually with 'skip_validation' enabled"
  335. exit 1