-
Notifications
You must be signed in to change notification settings - Fork 1
add: workflows #3
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
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 @@ | ||
| ## What does this PR change? | ||
| <!-- Briefly describe your changes --> | ||
|
|
||
| ## Why is this needed? | ||
| <!-- Explain the motivation or issue --> | ||
|
|
||
| ## Checklist | ||
| - [ ] Code runs locally | ||
| - [ ] Tests pass | ||
| - [ ] No hardcoded secrets | ||
| - [ ] Documentation updated | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,114 @@ | ||||||||||||||||||||||||||||||||||
| name: Peer Review Reminder | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||
| pull_request_target: | ||||||||||||||||||||||||||||||||||
| types: [opened, reopened, synchronize, ready_for_review] | ||||||||||||||||||||||||||||||||||
| pull_request_review: | ||||||||||||||||||||||||||||||||||
| types: [submitted, dismissed] | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||
| pull-requests: write | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||
| check-peer-review: | ||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||
| name: Check PR has a peer review | ||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||
| - name: Check for approved peer review and comment if missing | ||||||||||||||||||||||||||||||||||
| uses: actions/github-script@v7 | ||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||||||||||||||||||||||||||||||||||
| script: | | ||||||||||||||||||||||||||||||||||
| const repo = context.repo; | ||||||||||||||||||||||||||||||||||
| const pr = context.payload.pull_request; | ||||||||||||||||||||||||||||||||||
| const prNumber = pr.number; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Skip draft PRs | ||||||||||||||||||||||||||||||||||
| if (pr.draft) { | ||||||||||||||||||||||||||||||||||
| console.log(`PR #${prNumber} is a draft, skipping peer review check`); | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Check for exempt users (bots and maintainers) | ||||||||||||||||||||||||||||||||||
| const exemptUsers = ['A1L13N', 'copilot-swe-agent[bot]', 'copilot[bot]', 'github-copilot[bot]', 'dependabot[bot]']; | ||||||||||||||||||||||||||||||||||
| if (exemptUsers.includes(pr.user.login)) { | ||||||||||||||||||||||||||||||||||
| console.log(`PR author ${pr.user.login} is exempt from peer review requirement`); | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Check if PR branch is a copilot branch | ||||||||||||||||||||||||||||||||||
| if (pr.head.ref.startsWith('copilot/')) { | ||||||||||||||||||||||||||||||||||
| console.log(`PR branch ${pr.head.ref} starts with 'copilot/', exempt from peer review requirement`); | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Fetch all reviews for this PR | ||||||||||||||||||||||||||||||||||
| const { data: reviews } = await github.rest.pulls.listReviews({ | ||||||||||||||||||||||||||||||||||
| owner: repo.owner, | ||||||||||||||||||||||||||||||||||
| repo: repo.repo, | ||||||||||||||||||||||||||||||||||
| pull_number: prNumber, | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+50
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: cat -n .github/workflows/peer-review-reminder.ymlRepository: alphaonelabs/scholarai Length of output: 5167 🌐 Web query:
💡 Result: For the REST endpoint “List reviews for a pull request” (
Example: GET /repos/OWNER/REPO/pulls/123/reviews?per_page=100&page=2Sources: [1] [2] 🌐 Web query:
💡 Result: Yes. In Example: - uses: actions/github-script@v7
with:
script: |
const issues = await github.paginate(
github.rest.issues.listForRepo,
{ owner: context.repo.owner, repo: context.repo.repo, state: "open", per_page: 100 }
)
core.info(`Fetched ${issues.length} issues`)Citations: Paginate reviews and comments to avoid false reminders on busy PRs. The GitHub API defaults to 30 items per page for both Replace the destructured calls with 💡 Suggested patch- const { data: reviews } = await github.rest.pulls.listReviews({
- owner: repo.owner,
- repo: repo.repo,
- pull_number: prNumber,
- });
+ const reviews = await github.paginate(github.rest.pulls.listReviews, {
+ owner: repo.owner,
+ repo: repo.repo,
+ pull_number: prNumber,
+ per_page: 100,
+ });
...
- const { data: comments } = await github.rest.issues.listComments({
- owner: repo.owner,
- repo: repo.repo,
- issue_number: prNumber,
- });
+ const comments = await github.paginate(github.rest.issues.listComments, {
+ owner: repo.owner,
+ repo: repo.repo,
+ issue_number: prNumber,
+ per_page: 100,
+ });🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Check if there is at least one APPROVED review from someone other than the PR author | ||||||||||||||||||||||||||||||||||
| const approvedReview = reviews.find( | ||||||||||||||||||||||||||||||||||
| review => review.state === 'APPROVED' && review.user.login !== pr.user.login | ||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+53
to
+55
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: cat -n .github/workflows/peer-review-reminder.ymlRepository: alphaonelabs/scholarai Length of output: 5167 🌐 Web query:
💡 Result:
If you need “latest review per user”, you have to post-process the list (group by Sources: [1] GitHub REST docs – Pull request reviews (“List reviews… returns in chronological order”) ; [2] GitHub Community discussion with GraphQL approach for “latest review by user”. Track each reviewer's latest state instead of matching the first approval. The GitHub API's Build a Map of the latest review state per reviewer by processing reviews sorted by 💡 Suggested patch- const approvedReview = reviews.find(
- review => review.state === 'APPROVED' && review.user.login !== pr.user.login
- );
+ const latestReviewStateByUser = new Map();
+ for (const review of reviews.sort(
+ (a, b) => new Date(a.submitted_at) - new Date(b.submitted_at)
+ )) {
+ const reviewer = review.user?.login;
+ if (!reviewer || reviewer === pr.user.login) continue;
+ latestReviewStateByUser.set(reviewer, review.state);
+ }
+ const approvedReviewer = [...latestReviewStateByUser.entries()]
+ .find(([, state]) => state === 'APPROVED')?.[0];
- if (approvedReview) {
- console.log(`PR #${prNumber} has an approved review from ${approvedReview.user.login}`);
+ if (approvedReviewer) {
+ console.log(`PR #${prNumber} has an approved review from ${approvedReviewer}`);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const botUserName = 'github-actions[bot]'; | ||||||||||||||||||||||||||||||||||
| const commentMarker = '<!-- peer-review-reminder -->'; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Fetch existing comments | ||||||||||||||||||||||||||||||||||
| const { data: comments } = await github.rest.issues.listComments({ | ||||||||||||||||||||||||||||||||||
| owner: repo.owner, | ||||||||||||||||||||||||||||||||||
| repo: repo.repo, | ||||||||||||||||||||||||||||||||||
| issue_number: prNumber, | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const previousComment = comments.find( | ||||||||||||||||||||||||||||||||||
| comment => comment.user.login === botUserName && comment.body.includes(commentMarker) | ||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (approvedReview) { | ||||||||||||||||||||||||||||||||||
| console.log(`PR #${prNumber} has an approved review from ${approvedReview.user.login}`); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Remove the reminder comment if it exists and PR now has approval | ||||||||||||||||||||||||||||||||||
| if (previousComment) { | ||||||||||||||||||||||||||||||||||
| await github.rest.issues.deleteComment({ | ||||||||||||||||||||||||||||||||||
| owner: repo.owner, | ||||||||||||||||||||||||||||||||||
| repo: repo.repo, | ||||||||||||||||||||||||||||||||||
| comment_id: previousComment.id, | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
| console.log(`Removed peer review reminder comment from PR #${prNumber}`); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // No approved review found — post a reminder if not already posted | ||||||||||||||||||||||||||||||||||
| if (previousComment) { | ||||||||||||||||||||||||||||||||||
| console.log(`Already commented about missing peer review on PR #${prNumber}, skipping duplicate`); | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const message = [ | ||||||||||||||||||||||||||||||||||
| commentMarker, | ||||||||||||||||||||||||||||||||||
| '## 👀 Peer Review Required', | ||||||||||||||||||||||||||||||||||
| '', | ||||||||||||||||||||||||||||||||||
| `Hi @${pr.user.login}! This pull request does not yet have a **peer review**.`, | ||||||||||||||||||||||||||||||||||
| '', | ||||||||||||||||||||||||||||||||||
| 'Before this PR can be merged, please request a review from one of your peers:', | ||||||||||||||||||||||||||||||||||
| '', | ||||||||||||||||||||||||||||||||||
| '- Go to the PR page and click **"Reviewers"** on the right sidebar.', | ||||||||||||||||||||||||||||||||||
| '- Select a team member or contributor to review your changes.', | ||||||||||||||||||||||||||||||||||
| '- Once they approve, this reminder will be automatically removed.', | ||||||||||||||||||||||||||||||||||
| '', | ||||||||||||||||||||||||||||||||||
| 'Thank you for contributing! 🎉', | ||||||||||||||||||||||||||||||||||
| ].join('\n'); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| await github.rest.issues.createComment({ | ||||||||||||||||||||||||||||||||||
| owner: repo.owner, | ||||||||||||||||||||||||||||||||||
| repo: repo.repo, | ||||||||||||||||||||||||||||||||||
| issue_number: prNumber, | ||||||||||||||||||||||||||||||||||
| body: message, | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| console.log(`Posted peer review reminder on PR #${prNumber}`); | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| name: PR Title Format Check | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, edited, reopened] | ||
|
|
||
| jobs: | ||
| check-title: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/github-script@v7 | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const prTitle = context.payload.pull_request.title; | ||
| const valid = /^(fix|feat|docs|add|update):/i.test(prTitle); | ||
| if (!valid) { | ||
| throw new Error('PR title must start with fix:, feat:, docs:, add:, or update:'); | ||
| } |
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.
Fix markdownlint violations in the PR template.
Line 1 should be a top-level heading, Line 7 should have a blank line after it, and the file should end with a single trailing newline.
💡 Suggested patch
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 1-1: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
[warning] 7-7: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 11-11: Files should end with a single newline character
(MD047, single-trailing-newline)
🤖 Prompt for AI Agents