diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index b7008354c..35e26f55f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -42,6 +42,12 @@ body: 3. ... validations: required: true + - type: textarea + id: reproduction + attributes: + label: Minimal reproduction + description: A link to a small public repository, or the smallest files/snippet that trigger the problem. Optional, but it's the fastest way to get a fix. + placeholder: https://github.com/you/repro-project - type: textarea id: expected attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index bc9ad7844..ac1c90377 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -27,6 +27,16 @@ body: description: How you'd like it to work. Mockups or examples from other tools are welcome. validations: required: true + - type: textarea + id: acceptance + attributes: + label: Acceptance criteria + description: How we'll know it's done. One observable behavior per line. + placeholder: | + - [ ] Right-clicking a test class shows "Run with Coverage" + - [ ] Coverage results open in the Coverage tool window + validations: + required: true - type: textarea id: alternatives attributes: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..836b8710b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ +Closes # + +## What and why + + + +## How it was verified + + + +## Review focus + + diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml new file mode 100644 index 000000000..d1a74b7ee --- /dev/null +++ b/.github/workflows/pr-check.yml @@ -0,0 +1,69 @@ +name: PR check +on: + # pull_request_target so the check can label PRs from forks. It never checks out + # or runs PR code: it only reads the PR title and description. + pull_request_target: + types: [opened, edited, reopened, ready_for_review] + +permissions: + issues: write + pull-requests: write + +concurrency: + group: pr-check-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + check: + if: github.event.pull_request.user.type != 'Bot' && !github.event.pull_request.draft + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const LABEL = 'needs-info'; + const MARKER = ''; + // Docs, CI and pure refactors may skip the linked issue. + const NO_ISSUE_TYPES = /^(docs|ci|build|chore|refactor|style|test)(\(.+\))?!?:/; + + const body = (pr.body || '').replace(//g, ''); + const problems = []; + + const linksIssue = + /\b(close[sd]?|fix(e[sd])?|resolve[sd]?)\s*:?\s+(#\d+|https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/issues\/\d+)/i.test(body); + if (!linksIssue && !NO_ISSUE_TYPES.test(pr.title)) { + problems.push('Link the issue this PR resolves, e.g. `Closes #123`.'); + } + + const verified = body.match(/^##+\s*How it was verified\s*$([\s\S]*?)(?=^##+\s|(?![\s\S]))/im); + if (!verified || !verified[1].trim()) { + problems.push('Fill in **How it was verified**: the commands you ran and their results. UI changes need before/after screenshots.'); + } + + const { owner, repo } = context.repo; + const issue_number = pr.number; + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }); + const existing = comments.find(c => c.user.type === 'Bot' && c.body.includes(MARKER)); + const labeled = pr.labels.some(l => l.name === LABEL); + + if (problems.length) { + const text = `${MARKER}\nThanks for the PR. Before review, please update the description (see [CONTRIBUTING.md](https://github.com/${owner}/${repo}/blob/main/CONTRIBUTING.md)):\n\n${problems.map(p => `- ${p}`).join('\n')}\n\nThis check reruns when you edit the description.`; + if (existing) { + await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body: text }); + } else { + await github.rest.issues.createComment({ owner, repo, issue_number, body: text }); + } + if (!labeled) { + await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [LABEL] }); + } + return; + } + + // Only undo what this check did: the label is cleared only when our comment exists. + if (existing) { + await github.rest.issues.deleteComment({ owner, repo, comment_id: existing.id }); + if (labeled) { + await github.rest.issues.removeLabel({ owner, repo, issue_number, name: LABEL }).catch(() => {}); + } + } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..547527892 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Contributing to OpenIDEA + +These rules apply to everyone, and to any coding agent working on their behalf. + +## Issues + +- One problem or request per issue. Use the issue forms. +- Bugs: steps that reproduce the problem from a fresh start. A minimal sample + project is the fastest way to get a fix. +- Features: state the problem and the acceptance criteria. A feature is ready to + build once a maintainer labels it `planned`. +- Labels track status: `needs-info` waits on the reporter, `needs-decision` waits + on a maintainer, `planned` is accepted, `needs-acceptance` has a fix waiting to be + checked. + +## Pull requests + +- One issue per PR, linked with `Closes #123`. Feature PRs need a `planned` issue. + Docs, CI and pure refactors may skip the issue. +- Reproduce the problem first, or write a test that fails before the fix. +- Fix the cause, and check whether the same cause breaks anything nearby. +- Keep the diff focused. Unrelated refactors go in their own PR. +- Title: [Conventional Commits](https://www.conventionalcommits.org/), e.g. + `fix(editor): keep the caret visible after folding`. +- Fill in the template. Keep the description short and in plain words. If it + doesn't fit in a few sentences, the PR is probably too big. +- **How it was verified** is required: the commands you ran and their results, + and what else the change could affect. UI changes need before/after screenshots; + multi-step interactions need a recording. +- If a change needs a product decision you can't make, ask in the issue and stop + rather than guessing. + +A check on each PR adds `needs-info` when the issue link or the verification +section is missing, and removes it once they're filled in.