pr-checks.yml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. name: PR Checks
  2. on:
  3. pull_request:
  4. branches: [main, dev]
  5. types: [opened, edited, synchronize, reopened]
  6. jobs:
  7. pr-title-check:
  8. name: Validate PR Title
  9. runs-on: ubuntu-latest
  10. outputs:
  11. title-valid: ${{ steps.validate.outputs.valid }}
  12. steps:
  13. - name: Check PR title format
  14. id: validate
  15. uses: actions/github-script@v7
  16. with:
  17. script: |
  18. const prTitle = context.payload.pull_request.title;
  19. // Conventional commit patterns
  20. const patterns = {
  21. feat: /^feat(\(.+\))?!?:\s.+/,
  22. fix: /^fix(\(.+\))?!?:\s.+/,
  23. docs: /^docs(\(.+\))?:\s.+/,
  24. style: /^style(\(.+\))?:\s.+/,
  25. refactor: /^refactor(\(.+\))?:\s.+/,
  26. perf: /^perf(\(.+\))?:\s.+/,
  27. test: /^test(\(.+\))?:\s.+/,
  28. chore: /^chore(\(.+\))?:\s.+/,
  29. ci: /^ci(\(.+\))?:\s.+/,
  30. build: /^build(\(.+\))?:\s.+/,
  31. revert: /^revert(\(.+\))?:\s.+/,
  32. alpha: /^\[alpha\]\s.+/,
  33. beta: /^\[beta\]\s.+/,
  34. rc: /^\[rc\]\s.+/
  35. };
  36. // Check if title matches any pattern
  37. const matchedType = Object.entries(patterns).find(([type, pattern]) =>
  38. pattern.test(prTitle)
  39. );
  40. if (!matchedType) {
  41. const validExamples = [
  42. '✅ feat(evals): add new evaluator',
  43. '✅ fix(agents): correct delegation logic',
  44. '✅ docs(readme): update installation guide',
  45. '✅ test(evals): add execution-balance tests',
  46. '✅ chore(deps): update dependencies',
  47. '✅ feat!: breaking API change',
  48. '✅ [alpha] experimental feature'
  49. ];
  50. const message = `
  51. ## ❌ Invalid PR Title Format
  52. **Current title:** \`${prTitle}\`
  53. ### Required Format
  54. PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/) format:
  55. \`\`\`
  56. <type>(<scope>): <description>
  57. \`\`\`
  58. ### Valid Types
  59. - **feat** - New feature (triggers minor version bump: 0.3.0 → 0.4.0)
  60. - **fix** - Bug fix (triggers patch version bump: 0.3.0 → 0.3.1)
  61. - **docs** - Documentation changes (triggers patch bump)
  62. - **test** - Test additions/changes (triggers patch bump)
  63. - **refactor** - Code refactoring (triggers patch bump)
  64. - **chore** - Maintenance tasks (triggers patch bump)
  65. - **ci** - CI/CD changes (triggers patch bump)
  66. - **perf** - Performance improvements (triggers patch bump)
  67. - **style** - Code style changes (triggers patch bump)
  68. - **build** - Build system changes (triggers patch bump)
  69. - **revert** - Revert previous commit (triggers patch bump)
  70. ### Breaking Changes
  71. - **feat!:** or **fix!:** - Breaking change (triggers major version bump: 0.3.0 → 1.0.0)
  72. - **BREAKING CHANGE:** in description
  73. ### Pre-release Tags
  74. - **[alpha]** - Alpha release (0.3.0 → 0.3.1-alpha.1)
  75. - **[beta]** - Beta release (0.3.0 → 0.3.1-beta.1)
  76. - **[rc]** - Release candidate (0.3.0 → 0.3.1-rc.1)
  77. ### Valid Examples
  78. ${validExamples.map(ex => `- ${ex}`).join('\n')}
  79. ### Why This Matters
  80. - ✅ Enables automatic semantic versioning
  81. - ✅ Generates meaningful changelogs
  82. - ✅ Makes commit history searchable
  83. - ✅ Clarifies the impact of changes
  84. ### How to Fix
  85. Edit your PR title to match the format above.
  86. `;
  87. core.setFailed(message);
  88. // Also post as a comment
  89. await github.rest.issues.createComment({
  90. owner: context.repo.owner,
  91. repo: context.repo.repo,
  92. issue_number: context.payload.pull_request.number,
  93. body: message
  94. });
  95. } else {
  96. const [type] = matchedType;
  97. let versionBump = 'patch (0.3.0 → 0.3.1)';
  98. if (type === 'feat' && prTitle.includes('!')) {
  99. versionBump = 'major (0.3.0 → 1.0.0) - BREAKING CHANGE';
  100. } else if (type === 'feat') {
  101. versionBump = 'minor (0.3.0 → 0.4.0)';
  102. } else if (type === 'fix' && prTitle.includes('!')) {
  103. versionBump = 'major (0.3.0 → 1.0.0) - BREAKING CHANGE';
  104. } else if (type === 'alpha') {
  105. versionBump = 'alpha (0.3.0 → 0.3.1-alpha.1)';
  106. } else if (type === 'beta') {
  107. versionBump = 'beta (0.3.0 → 0.3.1-beta.1)';
  108. } else if (type === 'rc') {
  109. versionBump = 'rc (0.3.0 → 0.3.1-rc.1)';
  110. }
  111. const message = `
  112. ## ✅ PR Title Valid
  113. **Title:** \`${prTitle}\`
  114. **Type:** \`${type}\`
  115. **Version bump:** ${versionBump}
  116. When this PR is merged using **"Squash and Merge"**, the version will be automatically bumped.
  117. `;
  118. core.info(message);
  119. // Set output for summary
  120. core.summary
  121. .addHeading('✅ PR Title Validation Passed', 2)
  122. .addRaw(`**Title:** \`${prTitle}\``)
  123. .addBreak()
  124. .addRaw(`**Type:** \`${type}\``)
  125. .addBreak()
  126. .addRaw(`**Version bump:** ${versionBump}`)
  127. .write();
  128. // Set output for dependent jobs
  129. core.setOutput('valid', 'true');
  130. }
  131. check-changes:
  132. name: Detect Changed Files
  133. runs-on: ubuntu-latest
  134. needs: pr-title-check
  135. if: needs.pr-title-check.outputs.title-valid == 'true'
  136. outputs:
  137. has-evals: ${{ steps.filter.outputs.evals }}
  138. has-docs: ${{ steps.filter.outputs.docs }}
  139. has-workflows: ${{ steps.filter.outputs.workflows }}
  140. steps:
  141. - name: Checkout code
  142. uses: actions/checkout@v4
  143. with:
  144. fetch-depth: 0
  145. - name: Check changed files
  146. id: filter
  147. run: |
  148. # Get list of changed files
  149. git fetch origin ${{ github.base_ref }}
  150. CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
  151. echo "Changed files:"
  152. echo "$CHANGED_FILES"
  153. # Check for evals changes
  154. if echo "$CHANGED_FILES" | grep -q "^evals/"; then
  155. echo "has-evals=true" >> $GITHUB_OUTPUT
  156. echo "✅ Evals changes detected"
  157. else
  158. echo "has-evals=false" >> $GITHUB_OUTPUT
  159. echo "ℹ️ No evals changes"
  160. fi
  161. # Check for docs changes
  162. if echo "$CHANGED_FILES" | grep -q "^docs/"; then
  163. echo "has-docs=true" >> $GITHUB_OUTPUT
  164. echo "✅ Docs changes detected"
  165. else
  166. echo "has-docs=false" >> $GITHUB_OUTPUT
  167. echo "ℹ️ No docs changes"
  168. fi
  169. # Check for workflow changes
  170. if echo "$CHANGED_FILES" | grep -q "^.github/workflows/"; then
  171. echo "has-workflows=true" >> $GITHUB_OUTPUT
  172. echo "✅ Workflow changes detected"
  173. else
  174. echo "has-workflows=false" >> $GITHUB_OUTPUT
  175. echo "ℹ️ No workflow changes"
  176. fi
  177. build-check:
  178. name: Build & Validate
  179. runs-on: ubuntu-latest
  180. timeout-minutes: 5
  181. needs: [pr-title-check, check-changes]
  182. if: |
  183. needs.pr-title-check.outputs.title-valid == 'true' &&
  184. needs.check-changes.outputs.has-evals == 'true'
  185. steps:
  186. - name: Checkout code
  187. uses: actions/checkout@v4
  188. - name: Setup Node.js
  189. uses: actions/setup-node@v4
  190. with:
  191. node-version: '20'
  192. cache: 'npm'
  193. cache-dependency-path: 'evals/framework/package-lock.json'
  194. - name: Install dependencies
  195. working-directory: evals/framework
  196. run: npm ci
  197. - name: Build framework
  198. working-directory: evals/framework
  199. run: npm run build
  200. - name: Validate test suites
  201. working-directory: evals/framework
  202. run: npm run validate:suites:all
  203. - name: Summary
  204. if: success()
  205. run: |
  206. echo "## ✅ Build Check Passed" >> $GITHUB_STEP_SUMMARY
  207. echo "" >> $GITHUB_STEP_SUMMARY
  208. echo "- ✅ TypeScript compilation successful" >> $GITHUB_STEP_SUMMARY
  209. echo "- ✅ Test suite validation passed" >> $GITHUB_STEP_SUMMARY
  210. echo "" >> $GITHUB_STEP_SUMMARY
  211. echo "**Note:** Full agent tests are not run on PRs to save time and costs." >> $GITHUB_STEP_SUMMARY
  212. echo "Maintainers can run \`npm run test:ci\` locally if needed." >> $GITHUB_STEP_SUMMARY
  213. - name: Failure summary
  214. if: failure()
  215. run: |
  216. echo "## ❌ Build Check Failed" >> $GITHUB_STEP_SUMMARY
  217. echo "" >> $GITHUB_STEP_SUMMARY
  218. echo "Please check the logs above for details." >> $GITHUB_STEP_SUMMARY
  219. echo "" >> $GITHUB_STEP_SUMMARY
  220. echo "**Common fixes:**" >> $GITHUB_STEP_SUMMARY
  221. echo "- TypeScript errors: Fix type issues in \`evals/framework/src/\`" >> $GITHUB_STEP_SUMMARY
  222. echo "- YAML validation: Check test files in \`evals/agents/*/tests/\`" >> $GITHUB_STEP_SUMMARY
  223. summary:
  224. name: PR Checks Summary
  225. runs-on: ubuntu-latest
  226. needs: [pr-title-check, check-changes, build-check]
  227. if: always()
  228. steps:
  229. - name: Generate summary
  230. run: |
  231. echo "## 📊 PR Checks Summary" >> $GITHUB_STEP_SUMMARY
  232. echo "" >> $GITHUB_STEP_SUMMARY
  233. # PR Title Check
  234. if [ "${{ needs.pr-title-check.result }}" == "success" ]; then
  235. echo "✅ **PR Title:** Valid conventional commit format" >> $GITHUB_STEP_SUMMARY
  236. else
  237. echo "❌ **PR Title:** Invalid format - please fix" >> $GITHUB_STEP_SUMMARY
  238. fi
  239. # Changed Files Detection
  240. if [ "${{ needs.check-changes.result }}" == "success" ]; then
  241. echo "✅ **Changed Files:** Detected successfully" >> $GITHUB_STEP_SUMMARY
  242. if [ "${{ needs.check-changes.outputs.has-evals }}" == "true" ]; then
  243. echo " - 📦 Evals changes detected" >> $GITHUB_STEP_SUMMARY
  244. fi
  245. if [ "${{ needs.check-changes.outputs.has-docs }}" == "true" ]; then
  246. echo " - 📚 Docs changes detected" >> $GITHUB_STEP_SUMMARY
  247. fi
  248. if [ "${{ needs.check-changes.outputs.has-workflows }}" == "true" ]; then
  249. echo " - ⚙️ Workflow changes detected" >> $GITHUB_STEP_SUMMARY
  250. fi
  251. else
  252. echo "⏭️ **Changed Files:** Skipped (title validation failed)" >> $GITHUB_STEP_SUMMARY
  253. fi
  254. # Build Check
  255. if [ "${{ needs.build-check.result }}" == "success" ]; then
  256. echo "✅ **Build & Validate:** Passed" >> $GITHUB_STEP_SUMMARY
  257. elif [ "${{ needs.build-check.result }}" == "skipped" ]; then
  258. echo "⏭️ **Build & Validate:** Skipped (no evals changes)" >> $GITHUB_STEP_SUMMARY
  259. elif [ "${{ needs.build-check.result }}" == "failure" ]; then
  260. echo "❌ **Build & Validate:** Failed - check logs" >> $GITHUB_STEP_SUMMARY
  261. fi
  262. echo "" >> $GITHUB_STEP_SUMMARY
  263. # Overall status
  264. if [ "${{ needs.pr-title-check.result }}" == "success" ] && \
  265. ([ "${{ needs.build-check.result }}" == "success" ] || [ "${{ needs.build-check.result }}" == "skipped" ]); then
  266. echo "### ✅ All Required Checks Passed!" >> $GITHUB_STEP_SUMMARY
  267. echo "" >> $GITHUB_STEP_SUMMARY
  268. echo "This PR is ready for review." >> $GITHUB_STEP_SUMMARY
  269. else
  270. echo "### ❌ Some Checks Failed" >> $GITHUB_STEP_SUMMARY
  271. echo "" >> $GITHUB_STEP_SUMMARY
  272. echo "Please fix the failing checks before merging." >> $GITHUB_STEP_SUMMARY
  273. fi