Skip to content

Commit 6e2342e

Browse files
committed
fix: resolve new merge conflicts in lib/github.ts and package-lock.json
2 parents 6955e81 + 19cae3e commit 6e2342e

72 files changed

Lines changed: 3586 additions & 411 deletions

Some content is hidden

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ async function handleClaim({ github, context }) {
7474
owner,
7575
repo,
7676
issue_number: issueNumber,
77-
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}`,
7878
});
7979
return;
8080
}

.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/conflict-notifier.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
branches:
88
- main
99
pull_request_target:
10-
types: [opened, synchronize, reopened, edited]
10+
types: [synchronize, reopened, edited]
1111
workflow_dispatch:
1212

1313
permissions:

.github/workflows/issue-management.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ jobs:
2222
contains(github.event.comment.body, '/assign') ||
2323
contains(github.event.comment.body, '/unassign') ||
2424
contains(github.event.comment.body, '/addlabel') ||
25-
contains(github.event.comment.body, '/claim')
25+
contains(github.event.comment.body, '/claim') ||
26+
contains(github.event.comment.body, '/unclaim')
2627
)
2728
steps:
2829
- name: Checkout Repository

CONTRIBUTING.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ Our automation runs entirely through issue comments. Here is how you interact wi
338338
| Command | Who Can Use It? | What It Does |
339339
| ----------------------------- | ------------------------------------------------------- | --------------------------------------------------------- |
340340
| `/claim` | **Issue Author (or Anyone if authored by jhasourav07)** | Self-assigns the issue to you. |
341+
| `/unclaim` | **Assigned Contributor** | Removes the assignment from yourself (opens it back up). |
341342
| `/addlabel <label1> <label2>` | **Anyone** | Adds labels to the issue (e.g. `/addlabel frontend bug`). |
342343
| `/unassign @username` | **Maintainers Only** | Removes the assignee from an issue. |
343344
| `/assign @username` | **Maintainers Only** | Manually assigns someone to an issue. |
@@ -363,7 +364,7 @@ To keep the project moving, assignments are not permanent.
363364
If the bot rejects your command, check these common scenarios:
364365

365366
- **"Commands cannot be used on closed issues"**: You cannot claim, assign, or unassign on closed issues. Find an open one!
366-
- **"You already have X/3 active assigned issues"**: You have reached the maximum of 3 concurrent assignments. Finish one of your current tasks before claiming a new issue. If you're stuck, ask a maintainer to `/unassign` you from one.
367+
- **"You already have X/3 active assigned issues"**: You have reached the maximum of 3 concurrent assignments. Finish one of your current tasks before claiming a new issue. If you're stuck, use the `/unclaim` command to unassign yourself from an issue, or ask a maintainer to `/unassign` you.
367368
- **"This issue is already assigned to @username"**: Be faster next time! Look for issues without assignees.
368369
- **"Only the author of this issue can claim it"**: You tried to `/claim` an issue you did not create. You can only claim issues that you authored (unless the issue was authored by `jhasourav07`, which anyone can claim).
369370
- **"The following label(s) do not exist"**: You can only add existing repo labels. The bot will reply with a list of valid labels you can use.

app/(root)/dashboard/[username]/page.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ describe('DashboardPage', () => {
9191
insights: [],
9292
achievements: [],
9393
commitClock: [],
94+
graphData: { nodes: [], links: [] },
9495
};
9596

9697
beforeEach(() => {

app/(root)/dashboard/error.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default function DashboardError({
3333
: 'Something went wrong'}
3434
</h1>
3535

36-
<p className="text-gray-600 dark:text-gray-400 mb-8 leading-relaxed">
36+
<p className="text-gray-600 dark:text-white/70 mb-8 leading-relaxed">
3737
{isNotFound
3838
? "We couldn't find a GitHub user with that username. Please check the spelling and try again."
3939
: isRateLimit

app/api/github/route.test.ts

Lines changed: 60 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,67 +18,82 @@ function makeRequest(params: Record<string, string> = {}): Request {
1818
describe('GET /api/github', () => {
1919
beforeEach(() => {
2020
vi.clearAllMocks();
21+
vi.mocked(getFullDashboardData).mockResolvedValue({} as never);
2122
});
2223

23-
// Test 1 — missing username → 400
24-
it('returns 400 when username is missing', async () => {
25-
const response = await GET(makeRequest());
26-
const body = await response.json();
24+
describe('cache bypass via ?refresh=true', () => {
25+
it('calls getFullDashboardData with { bypassCache: true } when ?refresh=true', async () => {
26+
await GET(makeRequest({ username: 'octocat', refresh: 'true' }));
2727

28-
expect(response.status).toBe(400);
29-
expect(body.error).toContain('Invalid parameters');
30-
});
28+
expect(getFullDashboardData).toHaveBeenCalledWith('octocat', { bypassCache: true });
29+
});
3130

32-
it('returns 400 and skips GitHub when username format is invalid', async () => {
33-
const response = await GET(makeRequest({ username: 'bad user' }));
34-
const body = await response.json();
31+
it('calls getFullDashboardData with { bypassCache: false } when refresh is omitted', async () => {
32+
await GET(makeRequest({ username: 'octocat' }));
3533

36-
expect(response.status).toBe(400);
37-
expect(body.error).toContain('Invalid parameters');
38-
expect(getFullDashboardData).not.toHaveBeenCalled();
39-
});
34+
expect(getFullDashboardData).toHaveBeenCalledWith('octocat', { bypassCache: false });
35+
});
4036

41-
// Test 2 — valid username → 200
42-
it('returns 200 with JSON body for a valid username', async () => {
43-
vi.mocked(getFullDashboardData).mockResolvedValue({ profile: 'octocat' } as never);
37+
// Test 1 — missing username → 400
38+
it('returns 400 when username is missing', async () => {
39+
const response = await GET(makeRequest());
40+
const body = await response.json();
4441

45-
const response = await GET(makeRequest({ username: 'octocat' }));
46-
const body = await response.json();
42+
expect(response.status).toBe(400);
43+
expect(body.error).toContain('Invalid parameters');
44+
});
4745

48-
expect(response.status).toBe(200);
49-
expect(body).toEqual({ profile: 'octocat' });
50-
});
46+
it('returns 400 and skips GitHub when username format is invalid', async () => {
47+
const response = await GET(makeRequest({ username: 'bad user' }));
48+
const body = await response.json();
5149

52-
// Test 3 — throws 'User not found' → 404
53-
it('returns 404 when getFullDashboardData throws User not found', async () => {
54-
vi.mocked(getFullDashboardData).mockRejectedValue(new Error('User not found'));
50+
expect(response.status).toBe(400);
51+
expect(body.error).toContain('Invalid parameters');
52+
expect(getFullDashboardData).not.toHaveBeenCalled();
53+
});
5554

56-
const response = await GET(makeRequest({ username: 'octocat' }));
57-
const body = await response.json();
55+
// Test 2 — valid username → 200
56+
it('returns 200 with JSON body for a valid username', async () => {
57+
vi.mocked(getFullDashboardData).mockResolvedValue({ profile: 'octocat' } as never);
5858

59-
expect(response.status).toBe(404);
60-
expect(body.error).toContain('User not found');
61-
});
59+
const response = await GET(makeRequest({ username: 'octocat' }));
60+
const body = await response.json();
6261

63-
// Test 4 — throws 'API limit reached' → 403
64-
it('returns 403 when getFullDashboardData throws API limit reached', async () => {
65-
vi.mocked(getFullDashboardData).mockRejectedValue(new Error('API limit reached'));
62+
expect(response.status).toBe(200);
63+
expect(body).toEqual({ profile: 'octocat' });
64+
});
6665

67-
const response = await GET(makeRequest({ username: 'octocat' }));
68-
const body = await response.json();
66+
// Test 3 — throws 'User not found' → 404
67+
it('returns 404 when getFullDashboardData throws User not found', async () => {
68+
vi.mocked(getFullDashboardData).mockRejectedValue(new Error('User not found'));
6969

70-
expect(response.status).toBe(403);
71-
expect(body.error).toContain('rate limit');
72-
});
70+
const response = await GET(makeRequest({ username: 'octocat' }));
71+
const body = await response.json();
72+
73+
expect(response.status).toBe(404);
74+
expect(body.error).toContain('User not found');
75+
});
76+
77+
// Test 4 — throws 'API limit reached' → 403
78+
it('returns 403 when getFullDashboardData throws API limit reached', async () => {
79+
vi.mocked(getFullDashboardData).mockRejectedValue(new Error('API limit reached'));
80+
81+
const response = await GET(makeRequest({ username: 'octocat' }));
82+
const body = await response.json();
83+
84+
expect(response.status).toBe(403);
85+
expect(body.error).toContain('rate limit');
86+
});
7387

74-
// Test 5 — throws generic error → 500
75-
it('returns 500 for a generic unexpected error', async () => {
76-
vi.mocked(getFullDashboardData).mockRejectedValue(new Error('Something went wrong'));
88+
// Test 5 — throws generic error → 500
89+
it('returns 500 for a generic unexpected error', async () => {
90+
vi.mocked(getFullDashboardData).mockRejectedValue(new Error('Something went wrong'));
7791

78-
const response = await GET(makeRequest({ username: 'octocat' }));
79-
const body = await response.json();
92+
const response = await GET(makeRequest({ username: 'octocat' }));
93+
const body = await response.json();
8094

81-
expect(response.status).toBe(500);
82-
expect(body.error).toContain('Something went wrong');
95+
expect(response.status).toBe(500);
96+
expect(body.error).toContain('Something went wrong');
97+
});
8398
});
8499
});

0 commit comments

Comments
 (0)