-
Notifications
You must be signed in to change notification settings - Fork 15
Add contributor templates, issue forms, and PR triage automation #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3a13947
cb2da8b
f7946dd
750fa1f
0bcdedb
fabbf6c
2014f07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Ownership for CI, release, and security-sensitive infrastructure. | ||
| # A PR that edits these paths requires review from a code owner once | ||
| # branch protection's "Require review from Code Owners" is enabled. | ||
|
|
||
| /.github/ @beardthelion | ||
| /Dockerfile @beardthelion | ||
| /Dockerfile.bins @beardthelion | ||
| /docker-compose.yml @beardthelion | ||
| /release-please-config.json @beardthelion | ||
| /.release-please-manifest.json @beardthelion | ||
| /SECURITY.md @beardthelion |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| name: Bug report | ||
| description: Report a defect in a Gitlawb crate or node behavior | ||
| labels: ["bug"] | ||
| body: | ||
| - type: markdown | ||
| attributes: | ||
| value: | | ||
| Thanks for the report. Please fill in the fields below so we can reproduce the issue. | ||
| For security vulnerabilities, do NOT open an issue — follow SECURITY.md instead. | ||
| - type: textarea | ||
| id: what-happened | ||
| attributes: | ||
| label: What happened? | ||
| description: A clear description of the bug. | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: expected | ||
| attributes: | ||
| label: Expected behavior | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: repro | ||
| attributes: | ||
| label: Steps to reproduce | ||
| placeholder: | | ||
| 1. ... | ||
| 2. ... | ||
| 3. ... | ||
| validations: | ||
| required: true | ||
| - type: dropdown | ||
| id: crate | ||
| attributes: | ||
| label: Affected crate | ||
| options: | ||
| - gitlawb-node | ||
| - gl | ||
| - git-remote-gitlawb | ||
| - gitlawb-core | ||
| - not sure / other | ||
| validations: | ||
| required: true | ||
| - type: input | ||
| id: version | ||
| attributes: | ||
| label: Version or commit | ||
| description: Output of the binary's `--version`, or the git commit you built. | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: environment | ||
| attributes: | ||
| label: Environment | ||
| description: OS, Rust version (`rustc --version`), and anything else relevant. | ||
| validations: | ||
| required: false | ||
| - type: textarea | ||
| id: logs | ||
| attributes: | ||
| label: Relevant logs | ||
| description: Paste logs or errors. This will be rendered as code, no need for backticks. | ||
| render: shell | ||
| validations: | ||
| required: false |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| blank_issues_enabled: false | ||
| contact_links: | ||
| - name: Security vulnerability | ||
| url: https://github.com/Gitlawb/node/blob/main/SECURITY.md | ||
| about: Please report security issues privately per our security policy, not as a public issue. | ||
| - name: Questions & discussion | ||
| url: https://github.com/Gitlawb/node/discussions | ||
| about: For usage questions and open-ended discussion. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| name: Feature request | ||
| description: Suggest an improvement or new capability | ||
| labels: ["enhancement"] | ||
| body: | ||
| - type: textarea | ||
| id: problem | ||
| attributes: | ||
| label: Problem / use case | ||
| description: What are you trying to do that's hard or impossible today? | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: proposal | ||
| attributes: | ||
| label: Proposed solution | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: alternatives | ||
| attributes: | ||
| label: Alternatives considered | ||
| validations: | ||
| required: false | ||
| - type: dropdown | ||
| id: protocol | ||
| attributes: | ||
| label: Does this touch the protocol? | ||
| description: Identity, signatures, UCAN, ref certs, or P2P wire formats. Protocol changes need an issue discussion before any PR. | ||
| options: | ||
| - "No" | ||
| - "Yes" | ||
| - "Not sure" | ||
| validations: | ||
| required: true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| <!-- | ||
| Thanks for contributing to Gitlawb. Keep PRs focused on one change. | ||
| Protocol-level changes (identity, signatures, wire format) should start | ||
| as an issue before code — see CONTRIBUTING.md. | ||
| --> | ||
|
|
||
| ## Summary | ||
|
|
||
| <!-- One or two sentences: what changes and why it matters. --> | ||
|
|
||
|
|
||
|
|
||
| ## Motivation & context | ||
|
|
||
| <!-- What problem does this solve? Link prior discussion if any. --> | ||
| Closes # | ||
|
|
||
| ## Kind of change | ||
|
|
||
| - [ ] Bug fix | ||
| - [ ] Feature | ||
| - [ ] Security fix | ||
| - [ ] Docs | ||
| - [ ] Tests / CI | ||
| - [ ] Refactor (no behavior change) | ||
| - [ ] Breaking or protocol change (issue required first) | ||
|
|
||
| ## What changed | ||
|
|
||
| <!-- Bullet the concrete changes. Note the crate(s) touched: | ||
| gitlawb-core / gitlawb-node / gl / git-remote-gitlawb --> | ||
|
|
||
| - | ||
|
|
||
| ## How a reviewer can verify | ||
|
|
||
| <!-- Commands to run, or repro steps for a bug plus proof it's fixed. --> | ||
|
|
||
| ```sh | ||
|
|
||
| ``` | ||
|
|
||
| ## Before you request review | ||
|
|
||
| - [ ] Scope is one logical change; no unrelated churn | ||
| - [ ] `cargo test --workspace` passes locally | ||
| - [ ] New behavior is covered by tests (required for fixes) | ||
| - [ ] `cargo fmt --all` and `cargo clippy --workspace --all-targets -- -D warnings` are clean | ||
| - [ ] Commit titles use Conventional Commits (`feat(...)`, `fix(...)`, `docs(...)`) | ||
| - [ ] Docs / `.env.example` updated if behavior or config changed (or N/A) | ||
| - [ ] Checked existing PRs so this isn't a duplicate | ||
|
|
||
| ## Protocol & signing impact | ||
|
|
||
| <!-- Delete this block if your change doesn't touch the protocol. --> | ||
|
|
||
| - [ ] Touches DID / `did:key`, Ed25519 / RFC 9421 signatures, UCAN, ref certs, or P2P wire formats | ||
| - [ ] Discussed in an issue before implementation | ||
| - [ ] Backward-compatible with existing nodes and previously signed history | ||
|
|
||
| ## Notes for reviewers | ||
|
|
||
| <!-- Anything out of scope, follow-ups, or known limitations. Optional. --> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| name: PR Triage | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, edited, synchronize, reopened] | ||
|
|
||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| issues: write | ||
|
|
||
| concurrency: | ||
| group: pr-triage-${{ github.event.pull_request.number }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| triage: | ||
| name: Quality-signal triage | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - name: Label and guide | ||
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | ||
| with: | ||
| script: | | ||
| const pr = context.payload.pull_request; | ||
| const { owner, repo } = context.repo; | ||
| const prNumber = pr.number; | ||
|
|
||
| // Trusted authors bypass triage entirely. | ||
| const trusted = ["OWNER", "MEMBER", "COLLABORATOR"]; | ||
| if (trusted.includes(pr.author_association)) { | ||
| core.info(`Author association ${pr.author_association} is trusted; skipping triage.`); | ||
| return; | ||
| } | ||
|
|
||
| // Labels this workflow owns. Anything here may be added or removed each run. | ||
| const MANAGED = ["needs-issue", "needs-description", "needs-tests", "workflow-change"]; | ||
| const want = new Set(); | ||
|
|
||
| const body = pr.body || ""; | ||
|
|
||
| // Linked issue: look for "closes/fixes/resolves #N" or any bare #N reference. | ||
| const hasLinkedIssue = | ||
| /\b(close[sd]?|fix(e[sd])?|resolve[sd]?)\b[^\n]*#\d+/i.test(body) || | ||
| /#\d+/.test(body); | ||
| if (!hasLinkedIssue) want.add("needs-issue"); | ||
|
|
||
| // Description substance: strip HTML comments and markdown headings, then measure. | ||
| const stripped = body | ||
| .replace(/<!--[\s\S]*?-->/g, "") | ||
| .replace(/^#+.*$/gm, "") | ||
| .trim(); | ||
| if (stripped.length < 50) want.add("needs-description"); | ||
|
|
||
| // File-based signals. | ||
| const files = await github.paginate(github.rest.pulls.listFiles, { | ||
| owner, repo, pull_number: prNumber, per_page: 100, | ||
| }); | ||
| const names = files.map(f => f.filename); | ||
|
|
||
| const isFork = pr.head.repo && pr.head.repo.full_name !== pr.base.repo.full_name; | ||
| const touchesWorkflows = names.some(n => n.startsWith(".github/workflows/")); | ||
| if (isFork && touchesWorkflows) want.add("workflow-change"); | ||
|
|
||
| const changedRust = names.some(n => n.startsWith("crates/") && n.endsWith(".rs")); | ||
| // A test can live in a tests/ dir, a *_test.rs file, or — the common Rust | ||
| // pattern — an inline #[test] / #[cfg(test)] block added inside the source file. | ||
| const addsInlineTest = files.some(f => | ||
| f.filename.endsWith(".rs") && f.patch && | ||
| /^\+.*#\[(test\]|cfg\(test\))/m.test(f.patch)); | ||
| const touchedTests = | ||
| names.some(n => n.includes("/tests/") || n.endsWith("_test.rs")) || addsInlineTest; | ||
| if (changedRust && !touchedTests) want.add("needs-tests"); | ||
|
Comment on lines
+72
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scope the test signal to Rust test paths only. Line 72 currently treats any Suggested fix- const touchedTests =
- names.some(n => n.includes("/tests/") || n.endsWith("_test.rs")) || addsInlineTest;
- if (changedRust && !touchedTests) want.add("needs-tests");
+ const touchedRustTests =
+ names.some(n =>
+ n.startsWith("crates/") &&
+ n.endsWith(".rs") &&
+ (n.includes("/tests/") || n.endsWith("_test.rs") || n.endsWith("_tests.rs"))
+ ) || addsInlineTest;
+ if (changedRust && !touchedRustTests) want.add("needs-tests");🤖 Prompt for AI Agents |
||
|
|
||
| // Reconcile labels: add wanted managed labels, remove managed labels no longer wanted. | ||
| const current = (await github.rest.issues.listLabelsOnIssue({ | ||
| owner, repo, issue_number: prNumber, | ||
| })).data.map(l => l.name); | ||
|
|
||
| const toAdd = [...want].filter(l => !current.includes(l)); | ||
| const toRemove = MANAGED.filter(l => current.includes(l) && !want.has(l)); | ||
|
|
||
| if (toAdd.length) { | ||
| await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: toAdd }); | ||
| } | ||
| for (const name of toRemove) { | ||
| await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name }) | ||
| .catch(() => {}); // ignore if already gone | ||
| } | ||
|
|
||
| // Guidance comment: only for advisory signals, and only once. | ||
| const advisory = ["needs-issue", "needs-description", "needs-tests"].filter(l => want.has(l)); | ||
| if (!advisory.length) return; | ||
|
|
||
| const MARKER = "<!-- gitlawb-triage -->"; | ||
| const comments = await github.paginate(github.rest.issues.listComments, { | ||
| owner, repo, issue_number: prNumber, per_page: 100, | ||
| }); | ||
| if (comments.some(c => c.body && c.body.includes(MARKER))) { | ||
| core.info("Guidance comment already present; not reposting."); | ||
| return; | ||
| } | ||
|
|
||
| const reasons = { | ||
| "needs-issue": "Link the issue this addresses (`Closes #123`). For protocol changes, open an issue first.", | ||
| "needs-description": "Add a short summary of what changes and why — the template's Summary and Motivation sections.", | ||
| "needs-tests": "This changes Rust source but no tests changed. Tests are required for fixes and strongly encouraged for features.", | ||
| }; | ||
| const lines = advisory.map(l => `- ${reasons[l]}`).join("\n"); | ||
| const comment = [ | ||
| MARKER, | ||
| "Thanks for the contribution. A couple of things will help us review this faster:", | ||
| "", | ||
| lines, | ||
| "", | ||
| "See [CONTRIBUTING.md](https://github.com/Gitlawb/node/blob/main/CONTRIBUTING.md). Update the PR and these notes will clear automatically.", | ||
| ].join("\n"); | ||
|
|
||
| await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: comment }); | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,39 @@ | ||||||
| name: Stale PRs | ||||||
|
|
||||||
| on: | ||||||
| schedule: | ||||||
| - cron: "30 1 * * *" # daily at 01:30 UTC | ||||||
| workflow_dispatch: | ||||||
|
|
||||||
| permissions: | ||||||
| contents: read | ||||||
| pull-requests: write | ||||||
| issues: write | ||||||
|
|
||||||
| jobs: | ||||||
| stale: | ||||||
| name: Flag and close ignored PRs | ||||||
| runs-on: ubuntu-latest | ||||||
| timeout-minutes: 10 | ||||||
| steps: | ||||||
| - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9 | ||||||
| with: | ||||||
| days-before-stale: 14 | ||||||
| days-before-close: 7 | ||||||
| # Act on PRs only; never on issues. | ||||||
| days-before-issue-stale: -1 | ||||||
| days-before-issue-close: -1 | ||||||
| # Only PRs the triage step flagged as needing an author response. | ||||||
| only-pr-labels: "needs-issue,needs-description" | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify actions/stale input semantics directly from upstream action metadata/docs.
set -euo pipefail
curl -fsSL https://raw.githubusercontent.com/actions/stale/main/README.md | rg -n "only-pr-labels|any-of-pr-labels|contain all|contain one"Repository: Gitlawb/node Length of output: 2219 Change The Suggested fix- only-pr-labels: "needs-issue,needs-description"
+ any-of-pr-labels: "needs-issue,needs-description"📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: MCP tools |
||||||
| stale-pr-label: "stale" | ||||||
| exempt-pr-labels: "workflow-change,security,pinned" | ||||||
| remove-stale-when-updated: true | ||||||
| stale-pr-message: > | ||||||
| This PR has been inactive for 14 days and is still missing a linked issue | ||||||
| or a description. It will be closed in 7 days if there's no update. Push a | ||||||
| change or leave a comment to keep it open — no hard feelings, you can reopen | ||||||
| anytime. | ||||||
| close-pr-message: > | ||||||
| Closing due to inactivity. Reopen whenever you're ready to update it — see | ||||||
| CONTRIBUTING.md for what helps us review quickly. | ||||||
| ascending: true | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
For GitHub Actions as of 2026: when a workflow is triggered bypull_requestfrom a fork, canGITHUB_TOKENwithissues: write/pull-requests: writeactually add labels and post PR comments, or is it still read-only?💡 Result:
As of 2026, when a workflow is triggered by the
pull_requestevent from a forked repository, the GITHUB_TOKEN is automatically restricted to read-only permissions, regardless of anypermissionsconfiguration defined in your workflow [1][2][3]. This is a security feature designed to prevent unauthorized code in a fork from accessing secrets or performing write operations in the target repository [4][5]. Consequently, attempts to add labels or post PR comments using this token will fail with a "Resource not accessible by integration" (403) error [1][6][7]. To perform these actions for pull requests from forks, you must use thepull_request_targetevent instead ofpull_request[1][6][5]. Thepull_request_targetevent triggers the workflow in the context of the base repository (the target branch) rather than the fork's branch, which allows the GITHUB_TOKEN to retain the write permissions granted in your workflow'spermissionsblock [1][7][5]. When usingpull_request_target, it is critical to observe security best practices, as the workflow has access to the target repository's secrets and write permissions [1][4]. Specifically: 1. Never check out or execute code directly from the forked PR branch if you usepull_request_target, as this could allow malicious code to access your repository's tokens and secrets [1][7]. 2. If you need to process PR data (like the title, body, or issue number), use the data available in thegithub.eventcontext rather than checking out the untrusted code [7]. 3. Ensure your workflow only performs the intended automation (such as applying labels or posting a standard comment) and does not inadvertently expose the token to untrusted user input [7].Citations:
🏁 Script executed:
Repository: Gitlawb/node
Length of output: 284
🏁 Script executed:
Repository: Gitlawb/node
Length of output: 92
🏁 Script executed:
Repository: Gitlawb/node
Length of output: 148
🏁 Script executed:
Repository: Gitlawb/node
Length of output: 2522
Switch to
pull_request_targetevent to enable fork PR write operations.The current
pull_requesttrigger automatically restricts GITHUB_TOKEN to read-only for forked PRs, causing the label and comment writes at lines 85, 88, and 120 to fail with a 403 error. Usepull_request_targetinstead, which runs in the base repository context and preserves write permissions. Note: when usingpull_request_target, avoid checking out or executing untrusted code from the PR branch; usegithub.eventcontext data instead.🤖 Prompt for AI Agents
Source: MCP tools