Skip to content

docs: add team usage guide for scope and language conventions #102

docs: add team usage guide for scope and language conventions

docs: add team usage guide for scope and language conventions #102

Workflow file for this run

name: PR Validation
on:
pull_request:
types: [opened, edited, labeled, unlabeled, synchronize]
jobs:
check-issue-reference:
name: Check Issue Reference
runs-on: ubuntu-latest
steps:
- name: Verify PR links an issue
uses: actions/github-script@v7
with:
script: |
const body = context.payload.pull_request.body || '';
const issuePattern = /(?:closes|fixes|resolves)\s+#(\d+)/gi;
const matches = [...body.matchAll(issuePattern)];
if (matches.length === 0) {
core.setFailed(
'❌ PR must reference an approved issue.\n' +
'Add "Closes #N", "Fixes #N", or "Resolves #N" to the PR body.\n' +
'See CONTRIBUTING.md for the full contribution workflow.'
);
} else {
const issueNumbers = matches.map(m => m[1]).join(', ');
core.info(`✅ Found issue reference(s): #${issueNumbers}`);
}
check-issue-approved:
name: Check Issue Has status:approved
runs-on: ubuntu-latest
needs: check-issue-reference
steps:
- name: Verify linked issue is approved
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const body = context.payload.pull_request.body || '';
const issuePattern = /(?:closes|fixes|resolves)\s+#(\d+)/gi;
const matches = [...body.matchAll(issuePattern)];
if (matches.length === 0) {
core.setFailed('❌ No issue reference found in PR body.');
return;
}
const failures = [];
for (const match of matches) {
const issueNumber = parseInt(match[1], 10);
core.info(`Checking issue #${issueNumber} for status:approved label...`);
let issue;
try {
const response = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
});
issue = response.data;
} catch (err) {
failures.push(`Issue #${issueNumber}: could not be fetched (${err.message})`);
continue;
}
const labels = issue.labels.map(l => l.name);
core.info(`Issue #${issueNumber} labels: ${labels.join(', ') || '(none)'}`);
if (!labels.includes('status:approved')) {
failures.push(
`Issue #${issueNumber} ("${issue.title}") does not have the \`status:approved\` label. ` +
`A maintainer must approve the issue before a PR can be merged.`
);
} else {
core.info(`✅ Issue #${issueNumber} has status:approved`);
}
}
if (failures.length > 0) {
core.setFailed('❌ Linked issue(s) not approved:\n' + failures.join('\n'));
}
check-type-label:
name: Check PR Has type:* Label
runs-on: ubuntu-latest
steps:
- name: Verify PR has a type label
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = context.payload.pull_request.number;
const { data: pullRequest } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
});
const labels = pullRequest.labels.map(l => l.name);
core.info(`PR #${prNumber} labels: ${labels.join(', ') || '(none)'}`);
const typeLabels = labels.filter(l => l.startsWith('type:'));
if (typeLabels.length === 0) {
core.setFailed(
'❌ PR must have exactly one type:* label.\n' +
'Valid types: type:bug, type:feature, type:docs, type:refactor, type:chore, type:breaking-change\n' +
'Add the label to the PR after opening it.'
);
} else if (typeLabels.length > 1) {
core.setFailed(
`❌ PR has multiple type:* labels: ${typeLabels.join(', ')}.\n` +
'Please keep exactly one type label.'
);
} else {
core.info(`✅ PR has type label: ${typeLabels[0]}`);
}