summaryrefslogtreecommitdiffhomepage
path: root/.github/workflows
diff options
context:
space:
mode:
authorRyan Vogel <[email protected]>2026-02-18 17:20:23 -0500
committerGitHub <[email protected]>2026-02-18 17:20:23 -0500
commit639d1dd8fea6d77c648df4eabf8ea9d6973c27bb (patch)
tree5d68718cf3b2f9d51f0bcfa58905cb5cdd2a5d5d /.github/workflows
parent7033b4d0a856982a326d48dd8d86f717e28ed379 (diff)
downloadopencode-639d1dd8fea6d77c648df4eabf8ea9d6973c27bb.tar.gz
opencode-639d1dd8fea6d77c648df4eabf8ea9d6973c27bb.zip
chore: add compliance checks for issues and PRs with recheck on edit (#14170)
Diffstat (limited to '.github/workflows')
-rw-r--r--.github/workflows/duplicate-issues.yml64
-rw-r--r--.github/workflows/pr-management.yml27
-rw-r--r--.github/workflows/pr-standards.yml218
3 files changed, 285 insertions, 24 deletions
diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml
index 87e655fe4..6c1943fe7 100644
--- a/.github/workflows/duplicate-issues.yml
+++ b/.github/workflows/duplicate-issues.yml
@@ -2,10 +2,11 @@ name: duplicate-issues
on:
issues:
- types: [opened]
+ types: [opened, edited]
jobs:
check-duplicates:
+ if: github.event.action == 'opened'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
@@ -34,7 +35,7 @@ jobs:
"webfetch": "deny"
}
run: |
- opencode run -m opencode/claude-haiku-4-5 "A new issue has been created:
+ opencode run -m opencode/claude-sonnet-4-6 "A new issue has been created:
Issue number: ${{ github.event.issue.number }}
@@ -115,3 +116,62 @@ jobs:
If you believe this was flagged incorrectly, please let a maintainer know.
Remember: post at most ONE comment combining all findings. If everything is fine, post nothing."
+
+ recheck-compliance:
+ if: github.event.action == 'edited' && contains(github.event.issue.labels.*.name, 'needs:compliance')
+ runs-on: blacksmith-4vcpu-ubuntu-2404
+ permissions:
+ contents: read
+ issues: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - uses: ./.github/actions/setup-bun
+
+ - name: Install opencode
+ run: curl -fsSL https://opencode.ai/install | bash
+
+ - name: Recheck compliance
+ env:
+ OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ OPENCODE_PERMISSION: |
+ {
+ "bash": {
+ "*": "deny",
+ "gh issue*": "allow"
+ },
+ "webfetch": "deny"
+ }
+ run: |
+ opencode run -m opencode/claude-sonnet-4-6 "Issue #${{ github.event.issue.number }} was previously flagged as non-compliant and has been edited.
+
+ Lookup this issue with gh issue view ${{ github.event.issue.number }}.
+
+ Re-check whether the issue now follows our contributing guidelines and issue templates.
+
+ This project has three issue templates that every issue MUST use one of:
+
+ 1. Bug Report - requires a Description field with real content
+ 2. Feature Request - requires a verification checkbox and description, title should start with [FEATURE]:
+ 3. Question - requires the Question field with real content
+
+ Additionally check:
+ - No AI-generated walls of text (long, AI-generated descriptions are not acceptable)
+ - The issue has real content, not just template placeholder text left unchanged
+ - Bug reports should include some context about how to reproduce
+ - Feature requests should explain the problem or need
+ - We want to push for having the user provide system description & information
+
+ Do NOT be nitpicky about optional fields. Only flag real problems like: no template used, required fields empty or placeholder text only, obviously AI-generated walls of text, or completely empty/nonsensical content.
+
+ If the issue is NOW compliant:
+ 1. Remove the needs:compliance label: gh issue edit ${{ github.event.issue.number }} --remove-label needs:compliance
+ 2. Find and delete the previous compliance comment (the one containing <!-- issue-compliance -->) using: gh api repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments --jq '.[] | select(.body | contains(\"<!-- issue-compliance -->\")) | .id' then delete it with: gh api -X DELETE repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments/{id}
+ 3. Post a short comment thanking them for updating the issue.
+
+ If the issue is STILL not compliant:
+ Post a comment explaining what still needs to be fixed. Keep the needs:compliance label."
diff --git a/.github/workflows/pr-management.yml b/.github/workflows/pr-management.yml
index 008272415..35bd7ae36 100644
--- a/.github/workflows/pr-management.yml
+++ b/.github/workflows/pr-management.yml
@@ -6,17 +6,6 @@ on:
jobs:
check-duplicates:
- if: |
- github.event.pull_request.user.login != 'actions-user' &&
- github.event.pull_request.user.login != 'opencode' &&
- github.event.pull_request.user.login != 'rekram1-node' &&
- github.event.pull_request.user.login != 'thdxr' &&
- github.event.pull_request.user.login != 'kommander' &&
- github.event.pull_request.user.login != 'jayair' &&
- github.event.pull_request.user.login != 'fwang' &&
- github.event.pull_request.user.login != 'adamdotdevin' &&
- github.event.pull_request.user.login != 'iamdavidhill' &&
- github.event.pull_request.user.login != 'opencode-agent[bot]'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
@@ -27,16 +16,31 @@ jobs:
with:
fetch-depth: 1
+ - name: Check team membership
+ id: team-check
+ run: |
+ LOGIN="${{ github.event.pull_request.user.login }}"
+ if [ "$LOGIN" = "opencode-agent[bot]" ] || grep -qxF "$LOGIN" .github/TEAM_MEMBERS; then
+ echo "is_team=true" >> "$GITHUB_OUTPUT"
+ echo "Skipping: $LOGIN is a team member or bot"
+ else
+ echo "is_team=false" >> "$GITHUB_OUTPUT"
+ fi
+
- name: Setup Bun
+ if: steps.team-check.outputs.is_team != 'true'
uses: ./.github/actions/setup-bun
- name: Install dependencies
+ if: steps.team-check.outputs.is_team != 'true'
run: bun install
- name: Install opencode
+ if: steps.team-check.outputs.is_team != 'true'
run: curl -fsSL https://opencode.ai/install | bash
- name: Build prompt
+ if: steps.team-check.outputs.is_team != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
@@ -53,6 +57,7 @@ jobs:
} > pr_info.txt
- name: Check for duplicate PRs
+ if: steps.team-check.outputs.is_team != 'true'
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/pr-standards.yml b/.github/workflows/pr-standards.yml
index 397f794a1..a7e9eed3a 100644
--- a/.github/workflows/pr-standards.yml
+++ b/.github/workflows/pr-standards.yml
@@ -6,19 +6,9 @@ on:
jobs:
check-standards:
- if: |
- github.event.pull_request.user.login != 'actions-user' &&
- github.event.pull_request.user.login != 'opencode' &&
- github.event.pull_request.user.login != 'rekram1-node' &&
- github.event.pull_request.user.login != 'thdxr' &&
- github.event.pull_request.user.login != 'kommander' &&
- github.event.pull_request.user.login != 'jayair' &&
- github.event.pull_request.user.login != 'fwang' &&
- github.event.pull_request.user.login != 'adamdotdevin' &&
- github.event.pull_request.user.login != 'iamdavidhill' &&
- github.event.pull_request.user.login != 'opencode-agent[bot]'
runs-on: ubuntu-latest
permissions:
+ contents: read
pull-requests: write
steps:
- name: Check PR standards
@@ -26,6 +16,22 @@ jobs:
with:
script: |
const pr = context.payload.pull_request;
+ const login = pr.user.login;
+
+ // Check if author is a team member or bot
+ if (login === 'opencode-agent[bot]') return;
+ const { data: file } = await github.rest.repos.getContent({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ path: '.github/TEAM_MEMBERS',
+ ref: 'dev'
+ });
+ const members = Buffer.from(file.content, 'base64').toString().split('\n').map(l => l.trim()).filter(Boolean);
+ if (members.includes(login)) {
+ console.log(`Skipping: ${login} is a team member`);
+ return;
+ }
+
const title = pr.title;
async function addLabel(label) {
@@ -137,3 +143,193 @@ jobs:
await removeLabel('needs:issue');
console.log('PR meets all standards');
+
+ check-compliance:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ steps:
+ - name: Check PR template compliance
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const pr = context.payload.pull_request;
+ const login = pr.user.login;
+
+ // Check if author is a team member or bot
+ if (login === 'opencode-agent[bot]') return;
+ const { data: file } = await github.rest.repos.getContent({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ path: '.github/TEAM_MEMBERS',
+ ref: 'dev'
+ });
+ const members = Buffer.from(file.content, 'base64').toString().split('\n').map(l => l.trim()).filter(Boolean);
+ if (members.includes(login)) {
+ console.log(`Skipping: ${login} is a team member`);
+ return;
+ }
+
+ const body = pr.body || '';
+ const title = pr.title;
+ const isDocsOrRefactor = /^(docs|refactor)\s*(\([a-zA-Z0-9-]+\))?\s*:/.test(title);
+
+ const issues = [];
+
+ // Check: template sections exist
+ const hasWhatSection = /### What does this PR do\?/.test(body);
+ const hasTypeSection = /### Type of change/.test(body);
+ const hasVerifySection = /### How did you verify your code works\?/.test(body);
+ const hasChecklistSection = /### Checklist/.test(body);
+ const hasIssueSection = /### Issue for this PR/.test(body);
+
+ if (!hasWhatSection || !hasTypeSection || !hasVerifySection || !hasChecklistSection || !hasIssueSection) {
+ issues.push('PR description is missing required template sections. Please use the [PR template](../blob/dev/.github/pull_request_template.md).');
+ }
+
+ // Check: "What does this PR do?" has real content (not just placeholder text)
+ if (hasWhatSection) {
+ const whatMatch = body.match(/### What does this PR do\?\s*\n([\s\S]*?)(?=###|$)/);
+ const whatContent = whatMatch ? whatMatch[1].trim() : '';
+ const placeholder = 'Please provide a description of the issue';
+ const onlyPlaceholder = whatContent.includes(placeholder) && whatContent.replace(placeholder, '').replace(/[*\s]/g, '').length < 20;
+ if (!whatContent || onlyPlaceholder) {
+ issues.push('"What does this PR do?" section is empty or only contains placeholder text. Please describe your changes.');
+ }
+ }
+
+ // Check: at least one "Type of change" checkbox is checked
+ if (hasTypeSection) {
+ const typeMatch = body.match(/### Type of change\s*\n([\s\S]*?)(?=###|$)/);
+ const typeContent = typeMatch ? typeMatch[1] : '';
+ const hasCheckedBox = /- \[x\]/i.test(typeContent);
+ if (!hasCheckedBox) {
+ issues.push('No "Type of change" checkbox is checked. Please select at least one.');
+ }
+ }
+
+ // Check: issue reference (skip for docs/refactor)
+ if (!isDocsOrRefactor && hasIssueSection) {
+ const issueMatch = body.match(/### Issue for this PR\s*\n([\s\S]*?)(?=###|$)/);
+ const issueContent = issueMatch ? issueMatch[1].trim() : '';
+ const hasIssueRef = /(closes|fixes|resolves)\s+#\d+/i.test(issueContent) || /#\d+/.test(issueContent);
+ if (!hasIssueRef) {
+ issues.push('No issue referenced. Please add `Closes #<number>` linking to the relevant issue.');
+ }
+ }
+
+ // Check: "How did you verify" has content
+ if (hasVerifySection) {
+ const verifyMatch = body.match(/### How did you verify your code works\?\s*\n([\s\S]*?)(?=###|$)/);
+ const verifyContent = verifyMatch ? verifyMatch[1].trim() : '';
+ if (!verifyContent) {
+ issues.push('"How did you verify your code works?" section is empty. Please explain how you tested.');
+ }
+ }
+
+ // Check: checklist boxes are checked
+ if (hasChecklistSection) {
+ const checklistMatch = body.match(/### Checklist\s*\n([\s\S]*?)(?=###|$)/);
+ const checklistContent = checklistMatch ? checklistMatch[1] : '';
+ const unchecked = (checklistContent.match(/- \[ \]/g) || []).length;
+ const checked = (checklistContent.match(/- \[x\]/gi) || []).length;
+ if (checked < 2) {
+ issues.push('Not all checklist items are checked. Please confirm you have tested locally and have not included unrelated changes.');
+ }
+ }
+
+ // Helper functions
+ async function addLabel(label) {
+ await github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number,
+ labels: [label]
+ });
+ }
+
+ async function removeLabel(label) {
+ try {
+ await github.rest.issues.removeLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number,
+ name: label
+ });
+ } catch (e) {}
+ }
+
+ const hasComplianceLabel = pr.labels.some(l => l.name === 'needs:compliance');
+
+ if (issues.length > 0) {
+ // Non-compliant
+ if (!hasComplianceLabel) {
+ await addLabel('needs:compliance');
+ }
+
+ const marker = '<!-- issue-compliance -->';
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number
+ });
+ const existing = comments.find(c => c.body.includes(marker));
+
+ const body_text = `${marker}
+ This PR doesn't fully meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) and [PR template](../blob/dev/.github/pull_request_template.md).
+
+ **What needs to be fixed:**
+ ${issues.map(i => `- ${i}`).join('\n')}
+
+ Please edit this PR description to address the above within **2 hours**, or it will be automatically closed.
+
+ If you believe this was flagged incorrectly, please let a maintainer know.`;
+
+ if (existing) {
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: existing.id,
+ body: body_text
+ });
+ } else {
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number,
+ body: body_text
+ });
+ }
+
+ console.log(`PR #${pr.number} is non-compliant: ${issues.join(', ')}`);
+ } else if (hasComplianceLabel) {
+ // Was non-compliant, now fixed
+ await removeLabel('needs:compliance');
+
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number
+ });
+ const marker = '<!-- issue-compliance -->';
+ const existing = comments.find(c => c.body.includes(marker));
+ if (existing) {
+ await github.rest.issues.deleteComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: existing.id
+ });
+ }
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number,
+ body: 'Thanks for updating your PR! It now meets our contributing guidelines. :+1:'
+ });
+
+ console.log(`PR #${pr.number} is now compliant, label removed`);
+ } else {
+ console.log(`PR #${pr.number} is compliant`);
+ }