Skip to content

Commit 8c65234

Browse files
authored
Merge branch 'main' into test/svg-constants-integrity
2 parents 1c46ce0 + 0783fae commit 8c65234

298 files changed

Lines changed: 42193 additions & 3674 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.local.example

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
# Generate new token (classic)
2121
# Required scope: read:user
2222
# ------------------------------------------------------------
23-
GITHUB_TOKEN=ghp_your_personal_access_token_here
24-
23+
GITHUB_TOKEN=
2524

2625
# ------------------------------------------------------------
2726
# REQUIRED — Public site URL
@@ -48,3 +47,19 @@ NEXT_PUBLIC_SITE_URL=http://localhost:3000
4847
# 4. Replace <password> with your DB user's password
4948
# ------------------------------------------------------------
5049
# MONGODB_URI=mongodb+srv://<username>:<password>@cluster.mongodb.net/commitpulse?retryWrites=true&w=majority
50+
51+
# ------------------------------------------------------------
52+
# OPTIONAL — Upstash Redis / Vercel KV (Distributed Rate Limiting)
53+
# ------------------------------------------------------------
54+
# Enables distributed rate limiting across all serverless instances.
55+
# Without these, rate limiting is per-instance (each Vercel function
56+
# gets its own counter), which allows N× the intended limit under load.
57+
#
58+
# How to get one (free):
59+
# 1. Create a free Upstash Redis database at https://console.upstash.com
60+
# 2. Copy the REST API URL and token
61+
# 3. If using Vercel KV, link it to your project in the Vercel dashboard
62+
# — it sets KV_REST_API_URL and KV_REST_API_TOKEN automatically.
63+
# ------------------------------------------------------------
64+
# KV_REST_API_URL=https://<your-db>.upstash.io
65+
# KV_REST_API_TOKEN=<your-token>

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto eol=lf

.github/scripts/issue-management/assign-handler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ async function findExistingAssignments(github, owner, repo, username, currentIss
1010
return issues.filter((issue) => !issue.pull_request && issue.number !== currentIssueNumber);
1111
}
1212

13-
const MAX_ASSIGNED_ISSUES = 3;
13+
const MAX_ASSIGNED_ISSUES = 5;
1414

1515
async function handleAssign({ github, context, username, hasWriteAccess }) {
1616
const { owner, repo } = context.repo;

.github/scripts/issue-management/claim-handler.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ async function findExistingAssignments(github, owner, repo, username, currentIss
1010
return issues.filter((issue) => !issue.pull_request && issue.number !== currentIssueNumber);
1111
}
1212

13-
const MAX_ASSIGNED_ISSUES = 3;
13+
const MAX_ASSIGNED_ISSUES = 5;
1414

1515
async function handleClaim({ github, context }) {
1616
const { owner, repo } = context.repo;
@@ -30,9 +30,10 @@ async function handleClaim({ github, context }) {
3030

3131
const issueAuthor = context.payload.issue.user.login;
3232

33-
const isAuthorJhasourav07 = issueAuthor.toLowerCase() === 'jhasourav07';
33+
const MAINTAINERS = ['jhasourav07', 'aamod007', 'souravjhahind'];
34+
const isOpenedByMaintainer = MAINTAINERS.includes(issueAuthor.toLowerCase());
3435

35-
if (!isAuthorJhasourav07 && commenter.toLowerCase() !== issueAuthor.toLowerCase()) {
36+
if (!isOpenedByMaintainer && commenter.toLowerCase() !== issueAuthor.toLowerCase()) {
3637
await github.rest.issues.createComment({
3738
owner,
3839
repo,
@@ -73,7 +74,7 @@ async function handleClaim({ github, context }) {
7374
owner,
7475
repo,
7576
issue_number: issueNumber,
76-
body: `❌ You already have **${existingIssues.length}/${MAX_ASSIGNED_ISSUES}** active assigned issues (the maximum allowed).\nPlease complete or unassign one of your current issues before claiming another.\n\n${issueList}`,
77+
body: `❌ You already have **${existingIssues.length}/${MAX_ASSIGNED_ISSUES}** active assigned issues (the maximum allowed).\nPlease complete or \`/unclaim\` one of your current issues before claiming another.\n\n${issueList}`,
7778
});
7879
return;
7980
}

.github/scripts/issue-management/main.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const { handleAssign } = require('./assign-handler');
44
const { handleUnassign } = require('./unassign-handler');
55
const { handleAddLabel } = require('./addlabel-handler');
66
const { handleClaim } = require('./claim-handler');
7+
const { handleUnclaim } = require('./unclaim-handler');
78

89
module.exports = async ({ github, context, core }) => {
910
const commentBody = context.payload.comment?.body;
@@ -48,6 +49,9 @@ module.exports = async ({ github, context, core }) => {
4849
case 'claim':
4950
await handleClaim({ github, context });
5051
break;
52+
case 'unclaim':
53+
await handleUnclaim({ github, context });
54+
break;
5155
case 'ping':
5256
await github.rest.issues.createComment({
5357
owner,

.github/scripts/issue-management/parse-command.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ function parseCommand(commentBody) {
1515
const claimMatch = line.match(/^\/claim\s*$/i);
1616
if (claimMatch) return { command: 'claim' };
1717

18+
const unclaimMatch = line.match(/^\/unclaim\s*$/i);
19+
if (unclaimMatch) return { command: 'unclaim' };
20+
1821
const pingMatch = line.match(/^\/ping\s*$/i);
1922
if (pingMatch) return { command: 'ping' };
2023

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
async function handleUnclaim({ github, context }) {
2+
const { owner, repo } = context.repo;
3+
const issueNumber = context.payload.issue.number;
4+
const commenter = context.payload.comment.user.login;
5+
const issueState = context.payload.issue.state;
6+
7+
if (issueState === 'closed') {
8+
await github.rest.issues.createComment({
9+
owner,
10+
repo,
11+
issue_number: issueNumber,
12+
body: `❌ Commands cannot be used on closed issues.`,
13+
});
14+
return;
15+
}
16+
17+
const currentAssignees = context.payload.issue.assignees.map((a) => a.login.toLowerCase());
18+
19+
if (!currentAssignees.includes(commenter.toLowerCase())) {
20+
await github.rest.issues.createComment({
21+
owner,
22+
repo,
23+
issue_number: issueNumber,
24+
body: `ℹ️ @${commenter}, you are not currently assigned to this issue, so there's nothing to unclaim.`,
25+
});
26+
return;
27+
}
28+
29+
await github.rest.issues.removeAssignees({
30+
owner,
31+
repo,
32+
issue_number: issueNumber,
33+
assignees: [commenter],
34+
});
35+
36+
await github.rest.issues.createComment({
37+
owner,
38+
repo,
39+
issue_number: issueNumber,
40+
body: `✅ Successfully unclaimed this issue for @${commenter}.\n\n> 🔓 The issue is now open for others to claim.`,
41+
});
42+
}
43+
44+
module.exports = { handleUnclaim };

.github/workflows/assign-request-reminder.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
contains(github.event.comment.body, 'can i be assigned') ||
4343
contains(github.event.comment.body, 'assign me to this') ||
4444
contains(github.event.comment.body, 'assign me this') ||
45-
contains(github.event.comment.body, 'i will work on this') ||
45+
contains(github.event.comment.body, 'work on this') ||
4646
contains(github.event.comment.body, 'i''ll work on this') ||
4747
contains(github.event.comment.body, 'ill work on this') ||
4848
contains(github.event.comment.body, 'i can work on this') ||
@@ -93,7 +93,7 @@ jobs:
9393
``,
9494
`## 📋 A Few Things to Know`,
9595
``,
96-
`- You can hold a **maximum of 3 open issues** at a time.`,
96+
`- You can hold a **maximum of 5 open issues** at a time.`,
9797
`- If there's **no activity for 3 days**, the assignment will automatically expire so others can pick it up.`,
9898
`- Make sure to read our **[CONTRIBUTING.md](https://github.com/${owner}/${repo}/blob/main/CONTRIBUTING.md)** before you start — it covers code style, commit conventions, and the PR checklist.`,
9999
``,

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
branches: [main, dev]
66
pull_request:
77
branches: [main, dev]
8+
merge_group: # Run CI on the merged commit before it lands on main
89

910
jobs:
1011
quality:

.github/workflows/conflict-notifier.yml

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ name: Merge Conflict Notifier
22

33
on:
44
schedule:
5-
- cron: '*/5 * * * *'
5+
- cron: '*/30 * * * *' # Hourly/half-hourly fallback
6+
push:
7+
branches:
8+
- main
9+
pull_request_target:
10+
types: [synchronize, reopened, edited]
611
workflow_dispatch:
712

813
permissions:
@@ -74,35 +79,8 @@ jobs:
7479
issue_number: pr.number,
7580
labels: [label],
7681
});
77-
}
78-
79-
// Check existing comments to enforce a 2-hour notification gap
80-
const { data: comments } = await github.rest.issues.listComments({
81-
owner: context.repo.owner,
82-
repo: context.repo.repo,
83-
issue_number: pr.number,
84-
per_page: 100,
85-
});
86-
87-
const conflictComments = comments.filter(c =>
88-
c.user?.login === 'github-actions[bot]' &&
89-
c.body?.includes('merge conflicts with the main branch')
90-
);
91-
92-
let shouldComment = false;
93-
if (conflictComments.length === 0) {
94-
shouldComment = true;
95-
} else {
96-
const lastComment = conflictComments[conflictComments.length - 1];
97-
const lastCommentTime = new Date(lastComment.created_at).getTime();
98-
const nowTime = new Date().getTime();
99-
const TWO_HOURS_MS = 2 * 60 * 60 * 1000;
100-
if (nowTime - lastCommentTime >= TWO_HOURS_MS) {
101-
shouldComment = true;
102-
}
103-
}
10482
105-
if (shouldComment) {
83+
// Post notification comment ONLY ONCE (when the label is first applied)
10684
await github.rest.issues.createComment({
10785
owner: context.repo.owner,
10886
repo: context.repo.repo,

0 commit comments

Comments
 (0)