| 1 | name: PR Issue Check |
| 2 | |
| 3 | on: |
| 4 | pull_request: |
| 5 | types: [opened, synchronize, reopened, edited] |
| 6 | |
| 7 | permissions: |
| 8 | contents: read |
| 9 | pull-requests: read |
| 10 | issues: read |
| 11 | |
| 12 | jobs: |
| 13 | check-issue-linked: |
| 14 | name: Check Issue Linked |
| 15 | runs-on: ubuntu-latest |
| 16 | steps: |
| 17 | - name: Checkout |
| 18 | uses: actions/checkout@v4 |
| 19 | |
| 20 | - name: Check if PR is linked to an issue |
| 21 | uses: actions/github-script@v7 |
| 22 | with: |
| 23 | github-token: ${{ secrets.GITHUB_TOKEN }} |
| 24 | script: | |
| 25 | const prNumber = context.payload.pull_request.number; |
| 26 | let prBody = context.payload.pull_request.body || ''; |
| 27 | const prTitle = context.payload.pull_request.title || ''; |
| 28 | |
| 29 | const conventionalCommitsPattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|build)(\(.+\))?: .+$/; |
| 30 | const validTypes = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build']; |
| 31 | |
| 32 | if (!conventionalCommitsPattern.test(prTitle)) { |
| 33 | const errorMessage = |
| 34 | '❌ PR title does not follow Conventional Commits format.\n\n' + |
| 35 | 'Required format: <type>(<scope>): <description>\n\n' + |
| 36 | 'Valid types:\n' + |
| 37 | validTypes.map(t => ` - ${t}`).join('\n') + '\n\n' + |
| 38 | 'Examples:\n' + |
| 39 | ' ✅ feat: add user authentication\n' + |
| 40 | ' ✅ fix(web): resolve login button issue\n' + |
| 41 | ' ✅ docs: update API documentation\n' + |
| 42 | ' ✅ refactor(backend): improve error handling\n' + |
| 43 | ' ✅ chore: update dependencies\n\n' + |
| 44 | 'Common mistakes:\n' + |
| 45 | ' ❌ "Add new feature" (missing type and colon)\n' + |
| 46 | ' ❌ "feat add feature" (missing colon)\n' + |
| 47 | ' ❌ "feat: " (missing description)\n' + |
| 48 | ' ❌ "feature: add something" (invalid type)\n\n' + |
| 49 | `Current title: "${prTitle}"\n\n` + |
| 50 | 'Please update your PR title to follow the Conventional Commits specification.'; |
| 51 | |
| 52 | core.setFailed(errorMessage); |
| 53 | return; |
| 54 | } |