Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 24 additions & 13 deletions .github/workflows/pr-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,17 @@ jobs:
contents: read
pull-requests: write
steps:
- name: Check PR standards
- name: Validate PR Title
id: title-check
continue-on-error: true
uses: andreiships/shared-ai-standards/.github/actions/pr-validation@4a7766058a3981a4cd54b440c594aae1d8121c77 # main
with:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behavior change: regex is now stricter.

The old local regex was:

/^(feat|fix|...)\s*(\([a-zA-Z0-9-]+\))?\s*:/

The shared action's default is:

/^(type)(\([\w-]+\))?!?:\s+.+$/

Differences:

  1. Requires a non-empty subject after : (old allowed bare feat:)
  2. Disallows space before colon (old allowed feat :)
  3. Adds ! (breaking-change marker) support
  4. \w in scope also matches digits/underscore, while old was [a-zA-Z0-9-]

This is a good tightening but is an undocumented behavior change for contributors. Consider adding a comment here noting the regex change, or a CHANGELOG entry.

title-types: feat,fix,docs,chore,refactor,test

- name: Manage PR labels and check linked issue
uses: actions/github-script@v7
env:
TITLE_OUTCOME: ${{ steps.title-check.outcome }}
with:
script: |
const pr = context.payload.pull_request;
Expand Down Expand Up @@ -41,6 +50,10 @@ jobs:
}

const title = pr.title;
// 'success' = title valid; 'failure' = title invalid OR infra error (network/action fetch);
// 'skipped'/'cancelled' = step didn't run — treat as unknown (pass to avoid false positives)
const titleOutcome = process.env.TITLE_OUTCOME;
const titlePassed = titleOutcome === 'success' || titleOutcome === 'skipped' || titleOutcome === 'cancelled';

async function addLabel(label) {
await github.rest.issues.addLabels({
Expand Down Expand Up @@ -71,10 +84,10 @@ jobs:
repo: context.repo.repo,
issue_number: pr.number
});
const existing = comments.find(c => c.body.includes(markerText));

const existing = comments.find(c => c.body?.includes(markerText));
if (existing) return;

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
Expand All @@ -83,12 +96,8 @@ jobs:
});
}

// Step 1: Check title format
// Matches: feat:, feat(scope):, feat (scope):, etc.
const titlePattern = /^(feat|fix|docs|chore|refactor|test)\s*(\([a-zA-Z0-9-]+\))?\s*:/;
const hasValidTitle = titlePattern.test(title);

if (!hasValidTitle) {
// Title validation (shared action handles regex, we manage labels/comments)
if (!titlePassed) {
await addLabel('needs:title');
await comment('title', `Hey! Your PR title \`${title}\` doesn't follow conventional commit format.

Expand All @@ -103,16 +112,17 @@ jobs:
Where \`scope\` is the package name (e.g., \`app\`, \`desktop\`, \`opencode\`).

See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#pr-titles) for details.`);
core.setFailed('PR title does not follow conventional commit format');
return;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

core.setFailed() step name is misleading on failure.

When title validation fails, the step that appears failed in the GitHub Actions UI will be Manage PR labels and check linked issue, not something that says PR title invalid. This is confusing for PR authors trying to understand why their PR check failed.

The shared action step name Validate PR Title would have shown the failure clearly if continue-on-error were not set. Consider renaming this step to Validate PR and manage labels or similar so the failure surface is more descriptive.

}

await removeLabel('needs:title');

// Step 2: Check for linked issue (skip for docs/refactor/feat PRs)
const skipIssueCheck = /^(docs|refactor|feat)\s*(\([a-zA-Z0-9-]+\))?\s*:/.test(title);
// Check for linked issue (skip for docs/refactor PRs)
const skipIssueCheck = /^(docs|refactor)\s*(\([a-zA-Z0-9-]+\))?\s*:/.test(title);
if (skipIssueCheck) {
await removeLabel('needs:issue');
console.log('Skipping issue check for docs/refactor/feat PR');
console.log('Skipping issue check for docs/refactor PR');
return;
}
const query = `
Expand Down Expand Up @@ -146,6 +156,7 @@ jobs:
2. Add \`Fixes #<number>\` or \`Closes #<number>\` to this PR description

See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#issue-first-policy) for details.`);
core.setFailed('PR must have a linked issue');
return;
}

Expand Down
Loading