@buoy-design/plugin-github 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/formatter.test.d.ts +2 -0
- package/dist/__tests__/formatter.test.d.ts.map +1 -0
- package/dist/__tests__/formatter.test.js +237 -0
- package/dist/__tests__/formatter.test.js.map +1 -0
- package/dist/__tests__/github.test.d.ts +2 -0
- package/dist/__tests__/github.test.d.ts.map +1 -0
- package/dist/__tests__/github.test.js +301 -0
- package/dist/__tests__/github.test.js.map +1 -0
- package/dist/formatter.d.ts +4 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +70 -0
- package/dist/formatter.js.map +1 -0
- package/dist/github.d.ts +19 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +66 -0
- package/dist/github.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/formatter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// packages/plugin-github/src/__tests__/formatter.test.ts
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { formatPRComment, COMMENT_MARKER } from '../formatter.js';
|
|
4
|
+
describe('GitHub formatter', () => {
|
|
5
|
+
describe('formatPRComment', () => {
|
|
6
|
+
describe('empty results', () => {
|
|
7
|
+
it('formats empty results as success message', () => {
|
|
8
|
+
const result = formatPRComment(createEmptyResult());
|
|
9
|
+
expect(result).toContain('No design drift detected');
|
|
10
|
+
expect(result).toContain('aligned with the design system');
|
|
11
|
+
});
|
|
12
|
+
it('shows green icon for no issues', () => {
|
|
13
|
+
const result = formatPRComment(createEmptyResult());
|
|
14
|
+
expect(result).toContain('🟢');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('buoy marker', () => {
|
|
18
|
+
it('includes buoy marker for comment updates', () => {
|
|
19
|
+
const result = formatPRComment(createEmptyResult());
|
|
20
|
+
expect(result).toContain(COMMENT_MARKER);
|
|
21
|
+
});
|
|
22
|
+
it('places marker at the beginning of the comment', () => {
|
|
23
|
+
const result = formatPRComment(createEmptyResult());
|
|
24
|
+
expect(result.startsWith(COMMENT_MARKER)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('severity grouping', () => {
|
|
28
|
+
it('groups critical issues under Critical section', () => {
|
|
29
|
+
const driftResult = createResultWithSignals([
|
|
30
|
+
createSignal('critical', 'Missing color token', 'Button'),
|
|
31
|
+
]);
|
|
32
|
+
const result = formatPRComment(driftResult);
|
|
33
|
+
expect(result).toContain('### Critical');
|
|
34
|
+
expect(result).toContain('Missing color token');
|
|
35
|
+
});
|
|
36
|
+
it('groups warning issues under Warnings section', () => {
|
|
37
|
+
const driftResult = createResultWithSignals([
|
|
38
|
+
createSignal('warning', 'Consider using design token', 'Card'),
|
|
39
|
+
]);
|
|
40
|
+
const result = formatPRComment(driftResult);
|
|
41
|
+
expect(result).toContain('### Warnings');
|
|
42
|
+
expect(result).toContain('Consider using design token');
|
|
43
|
+
});
|
|
44
|
+
it('groups info issues in collapsible details section', () => {
|
|
45
|
+
const driftResult = createResultWithSignals([
|
|
46
|
+
createSignal('info', 'Minor style suggestion', 'Icon'),
|
|
47
|
+
]);
|
|
48
|
+
const result = formatPRComment(driftResult);
|
|
49
|
+
expect(result).toContain('<details>');
|
|
50
|
+
expect(result).toContain('1 info-level issue');
|
|
51
|
+
expect(result).toContain('Minor style suggestion');
|
|
52
|
+
expect(result).toContain('</details>');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('severity icons', () => {
|
|
56
|
+
it('shows red icon when critical issues exist', () => {
|
|
57
|
+
const driftResult = createResultWithSignals([
|
|
58
|
+
createSignal('critical', 'Critical issue'),
|
|
59
|
+
]);
|
|
60
|
+
const result = formatPRComment(driftResult);
|
|
61
|
+
expect(result).toContain('🔴');
|
|
62
|
+
});
|
|
63
|
+
it('shows yellow icon for warnings when no critical issues', () => {
|
|
64
|
+
const driftResult = createResultWithSignals([
|
|
65
|
+
createSignal('warning', 'Warning issue'),
|
|
66
|
+
]);
|
|
67
|
+
const result = formatPRComment(driftResult);
|
|
68
|
+
expect(result).toContain('🟡');
|
|
69
|
+
expect(result).not.toContain('🔴');
|
|
70
|
+
});
|
|
71
|
+
it('shows green icon for info-only issues', () => {
|
|
72
|
+
const driftResult = createResultWithSignals([
|
|
73
|
+
createSignal('info', 'Info issue'),
|
|
74
|
+
]);
|
|
75
|
+
const result = formatPRComment(driftResult);
|
|
76
|
+
expect(result).toContain('🟢');
|
|
77
|
+
});
|
|
78
|
+
it('prioritizes critical icon over warning', () => {
|
|
79
|
+
const driftResult = createResultWithSignals([
|
|
80
|
+
createSignal('critical', 'Critical issue'),
|
|
81
|
+
createSignal('warning', 'Warning issue'),
|
|
82
|
+
]);
|
|
83
|
+
const result = formatPRComment(driftResult);
|
|
84
|
+
expect(result).toContain('🔴');
|
|
85
|
+
expect(result).not.toContain('🟡');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('markdown formatting', () => {
|
|
89
|
+
it('formats output as valid markdown with header', () => {
|
|
90
|
+
const result = formatPRComment(createEmptyResult());
|
|
91
|
+
expect(result).toContain('## ');
|
|
92
|
+
expect(result).toContain('Buoy Drift Report');
|
|
93
|
+
});
|
|
94
|
+
it('includes markdown tables for critical issues', () => {
|
|
95
|
+
const driftResult = createResultWithSignals([
|
|
96
|
+
createSignal('critical', 'Test issue', 'TestComponent', 'src/Test.tsx', 42),
|
|
97
|
+
]);
|
|
98
|
+
const result = formatPRComment(driftResult);
|
|
99
|
+
expect(result).toContain('| Component | Issue | File |');
|
|
100
|
+
expect(result).toContain('|-----------|-------|------|');
|
|
101
|
+
expect(result).toContain('`TestComponent`');
|
|
102
|
+
expect(result).toContain('`src/Test.tsx:42`');
|
|
103
|
+
});
|
|
104
|
+
it('includes footer with Buoy link', () => {
|
|
105
|
+
const result = formatPRComment(createEmptyResult());
|
|
106
|
+
expect(result).toContain('🔱');
|
|
107
|
+
expect(result).toContain('href="https://github.com/dylantarre/buoy"');
|
|
108
|
+
expect(result).toContain('Buoy');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('summary counts', () => {
|
|
112
|
+
it('includes total count in summary', () => {
|
|
113
|
+
const driftResult = createResultWithSignals([
|
|
114
|
+
createSignal('critical', 'Issue 1'),
|
|
115
|
+
createSignal('warning', 'Issue 2'),
|
|
116
|
+
createSignal('info', 'Issue 3'),
|
|
117
|
+
]);
|
|
118
|
+
const result = formatPRComment(driftResult);
|
|
119
|
+
expect(result).toContain('3 issues found');
|
|
120
|
+
});
|
|
121
|
+
it('includes breakdown by severity', () => {
|
|
122
|
+
const driftResult = createResultWithSignals([
|
|
123
|
+
createSignal('critical', 'Crit 1'),
|
|
124
|
+
createSignal('critical', 'Crit 2'),
|
|
125
|
+
createSignal('warning', 'Warn 1'),
|
|
126
|
+
createSignal('info', 'Info 1'),
|
|
127
|
+
]);
|
|
128
|
+
const result = formatPRComment(driftResult);
|
|
129
|
+
expect(result).toContain('2 critical');
|
|
130
|
+
expect(result).toContain('1 warning');
|
|
131
|
+
expect(result).toContain('1 info');
|
|
132
|
+
});
|
|
133
|
+
it('uses singular form for single issue', () => {
|
|
134
|
+
const driftResult = createResultWithSignals([
|
|
135
|
+
createSignal('warning', 'Single issue'),
|
|
136
|
+
]);
|
|
137
|
+
const result = formatPRComment(driftResult);
|
|
138
|
+
expect(result).toContain('1 issue found');
|
|
139
|
+
});
|
|
140
|
+
it('uses plural form for multiple issues', () => {
|
|
141
|
+
const driftResult = createResultWithSignals([
|
|
142
|
+
createSignal('warning', 'Issue 1'),
|
|
143
|
+
createSignal('warning', 'Issue 2'),
|
|
144
|
+
]);
|
|
145
|
+
const result = formatPRComment(driftResult);
|
|
146
|
+
expect(result).toContain('2 issues found');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('warning truncation', () => {
|
|
150
|
+
it('truncates warnings list when more than 10 items', () => {
|
|
151
|
+
const warnings = Array.from({ length: 15 }, (_, i) => createSignal('warning', `Warning ${i + 1}`, `Component${i}`));
|
|
152
|
+
const driftResult = createResultWithSignals(warnings);
|
|
153
|
+
const result = formatPRComment(driftResult);
|
|
154
|
+
// Should show first 10 and truncation message
|
|
155
|
+
expect(result).toContain('Warning 1');
|
|
156
|
+
expect(result).toContain('Warning 10');
|
|
157
|
+
expect(result).toContain('5 more warnings');
|
|
158
|
+
});
|
|
159
|
+
it('shows all warnings when 10 or fewer', () => {
|
|
160
|
+
const warnings = Array.from({ length: 10 }, (_, i) => createSignal('warning', `Warning ${i + 1}`));
|
|
161
|
+
const driftResult = createResultWithSignals(warnings);
|
|
162
|
+
const result = formatPRComment(driftResult);
|
|
163
|
+
expect(result).not.toContain('more warnings');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
describe('edge cases', () => {
|
|
167
|
+
it('handles missing component name', () => {
|
|
168
|
+
const driftResult = createResultWithSignals([
|
|
169
|
+
{ type: 'hardcoded-value', severity: 'critical', message: 'Issue without component' },
|
|
170
|
+
]);
|
|
171
|
+
const result = formatPRComment(driftResult);
|
|
172
|
+
expect(result).toContain('`-`');
|
|
173
|
+
});
|
|
174
|
+
it('handles missing file location', () => {
|
|
175
|
+
const driftResult = createResultWithSignals([
|
|
176
|
+
createSignal('critical', 'Issue without file', 'Component'),
|
|
177
|
+
]);
|
|
178
|
+
const result = formatPRComment(driftResult);
|
|
179
|
+
expect(result).toContain('| - |');
|
|
180
|
+
});
|
|
181
|
+
it('handles file without line number', () => {
|
|
182
|
+
const driftResult = createResultWithSignals([
|
|
183
|
+
{ type: 'test', severity: 'critical', message: 'Test', component: 'Comp', file: 'test.tsx' },
|
|
184
|
+
]);
|
|
185
|
+
const result = formatPRComment(driftResult);
|
|
186
|
+
expect(result).toContain('`test.tsx`');
|
|
187
|
+
});
|
|
188
|
+
it('formats info issues with component and location', () => {
|
|
189
|
+
const driftResult = createResultWithSignals([
|
|
190
|
+
createSignal('info', 'Info message', 'InfoComponent', 'src/info.tsx', 10),
|
|
191
|
+
]);
|
|
192
|
+
const result = formatPRComment(driftResult);
|
|
193
|
+
expect(result).toContain('`InfoComponent`');
|
|
194
|
+
expect(result).toContain('src/info.tsx:10');
|
|
195
|
+
});
|
|
196
|
+
it('handles unknown component in info section', () => {
|
|
197
|
+
const driftResult = createResultWithSignals([
|
|
198
|
+
{ type: 'test', severity: 'info', message: 'Info without component' },
|
|
199
|
+
]);
|
|
200
|
+
const result = formatPRComment(driftResult);
|
|
201
|
+
expect(result).toContain('`Unknown`');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
// Helper functions
|
|
207
|
+
function createEmptyResult() {
|
|
208
|
+
return {
|
|
209
|
+
signals: [],
|
|
210
|
+
summary: { total: 0, critical: 0, warning: 0, info: 0 },
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function createSignal(severity, message, component, file, line) {
|
|
214
|
+
return {
|
|
215
|
+
type: 'test-drift',
|
|
216
|
+
severity,
|
|
217
|
+
message,
|
|
218
|
+
component,
|
|
219
|
+
file,
|
|
220
|
+
line,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function createResultWithSignals(signals) {
|
|
224
|
+
const critical = signals.filter(s => s.severity === 'critical').length;
|
|
225
|
+
const warning = signals.filter(s => s.severity === 'warning').length;
|
|
226
|
+
const info = signals.filter(s => s.severity === 'info').length;
|
|
227
|
+
return {
|
|
228
|
+
signals,
|
|
229
|
+
summary: {
|
|
230
|
+
total: signals.length,
|
|
231
|
+
critical,
|
|
232
|
+
warning,
|
|
233
|
+
info,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=formatter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.test.js","sourceRoot":"","sources":["../../src/__tests__/formatter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/github.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// packages/plugin-github/src/__tests__/github.test.ts
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import { COMMENT_MARKER } from '../formatter.js';
|
|
4
|
+
// Mock Octokit before importing the plugin
|
|
5
|
+
vi.mock('@octokit/rest', () => ({
|
|
6
|
+
Octokit: vi.fn().mockImplementation(() => ({
|
|
7
|
+
issues: {
|
|
8
|
+
listComments: vi.fn(),
|
|
9
|
+
createComment: vi.fn(),
|
|
10
|
+
updateComment: vi.fn(),
|
|
11
|
+
deleteComment: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
})),
|
|
14
|
+
}));
|
|
15
|
+
// Import after mocking
|
|
16
|
+
import { Octokit } from '@octokit/rest';
|
|
17
|
+
import { GitHubClient, parseRepoString } from '../github.js';
|
|
18
|
+
describe('GitHubClient', () => {
|
|
19
|
+
let mockOctokit;
|
|
20
|
+
let client;
|
|
21
|
+
const testContext = {
|
|
22
|
+
token: 'test-token',
|
|
23
|
+
owner: 'test-owner',
|
|
24
|
+
repo: 'test-repo',
|
|
25
|
+
prNumber: 123,
|
|
26
|
+
};
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
vi.clearAllMocks();
|
|
29
|
+
client = new GitHubClient(testContext);
|
|
30
|
+
// Get the mock instance that was created
|
|
31
|
+
mockOctokit = Octokit.mock.results[0]?.value;
|
|
32
|
+
});
|
|
33
|
+
describe('constructor', () => {
|
|
34
|
+
it('creates an Octokit instance with the provided token', () => {
|
|
35
|
+
expect(Octokit).toHaveBeenCalledWith({
|
|
36
|
+
auth: 'test-token',
|
|
37
|
+
request: {
|
|
38
|
+
timeout: 30000,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('findExistingComment', () => {
|
|
44
|
+
it('returns null when no comments exist', async () => {
|
|
45
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
46
|
+
data: [],
|
|
47
|
+
});
|
|
48
|
+
const result = await client.findExistingComment();
|
|
49
|
+
expect(result).toBeNull();
|
|
50
|
+
expect(mockOctokit.issues.listComments).toHaveBeenCalledWith({
|
|
51
|
+
owner: 'test-owner',
|
|
52
|
+
repo: 'test-repo',
|
|
53
|
+
issue_number: 123,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
it('returns null when no buoy comment exists', async () => {
|
|
57
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
58
|
+
data: [
|
|
59
|
+
{ id: 1, body: 'Regular comment' },
|
|
60
|
+
{ id: 2, body: 'Another comment' },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
const result = await client.findExistingComment();
|
|
64
|
+
expect(result).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
it('returns comment id when buoy marker found', async () => {
|
|
67
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
68
|
+
data: [
|
|
69
|
+
{ id: 456, body: `${COMMENT_MARKER}\nOld report content` },
|
|
70
|
+
{ id: 789, body: 'Some other comment' },
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
const result = await client.findExistingComment();
|
|
74
|
+
expect(result).toBe(456);
|
|
75
|
+
});
|
|
76
|
+
it('returns first matching comment when multiple buoy comments exist', async () => {
|
|
77
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
78
|
+
data: [
|
|
79
|
+
{ id: 100, body: 'Unrelated comment' },
|
|
80
|
+
{ id: 200, body: `${COMMENT_MARKER}\nFirst buoy comment` },
|
|
81
|
+
{ id: 300, body: `${COMMENT_MARKER}\nSecond buoy comment` },
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
const result = await client.findExistingComment();
|
|
85
|
+
expect(result).toBe(200);
|
|
86
|
+
});
|
|
87
|
+
it('handles comments with null body', async () => {
|
|
88
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
89
|
+
data: [
|
|
90
|
+
{ id: 1, body: null },
|
|
91
|
+
{ id: 2, body: undefined },
|
|
92
|
+
{ id: 3, body: `${COMMENT_MARKER}\nValid comment` },
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
const result = await client.findExistingComment();
|
|
96
|
+
expect(result).toBe(3);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('createOrUpdateComment', () => {
|
|
100
|
+
const testBody = `${COMMENT_MARKER}\n## Test Report`;
|
|
101
|
+
it('creates new comment when none exists', async () => {
|
|
102
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
103
|
+
data: [],
|
|
104
|
+
});
|
|
105
|
+
mockOctokit.issues.createComment.mockResolvedValue({
|
|
106
|
+
data: { id: 123, html_url: 'https://github.com/test-owner/test-repo/pull/123#issuecomment-123' },
|
|
107
|
+
});
|
|
108
|
+
await client.createOrUpdateComment(testBody);
|
|
109
|
+
expect(mockOctokit.issues.createComment).toHaveBeenCalledWith({
|
|
110
|
+
owner: 'test-owner',
|
|
111
|
+
repo: 'test-repo',
|
|
112
|
+
issue_number: 123,
|
|
113
|
+
body: testBody,
|
|
114
|
+
});
|
|
115
|
+
expect(mockOctokit.issues.updateComment).not.toHaveBeenCalled();
|
|
116
|
+
});
|
|
117
|
+
it('updates existing comment when buoy marker found', async () => {
|
|
118
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
119
|
+
data: [
|
|
120
|
+
{ id: 456, body: `${COMMENT_MARKER}\nOld report content` },
|
|
121
|
+
{ id: 789, body: 'Some other comment' },
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
mockOctokit.issues.updateComment.mockResolvedValue({
|
|
125
|
+
data: { id: 456 },
|
|
126
|
+
});
|
|
127
|
+
await client.createOrUpdateComment(testBody);
|
|
128
|
+
expect(mockOctokit.issues.updateComment).toHaveBeenCalledWith({
|
|
129
|
+
owner: 'test-owner',
|
|
130
|
+
repo: 'test-repo',
|
|
131
|
+
comment_id: 456,
|
|
132
|
+
body: testBody,
|
|
133
|
+
});
|
|
134
|
+
expect(mockOctokit.issues.createComment).not.toHaveBeenCalled();
|
|
135
|
+
});
|
|
136
|
+
it('handles API rate limit errors', async () => {
|
|
137
|
+
mockOctokit.issues.listComments.mockRejectedValue(new Error('API rate limit exceeded'));
|
|
138
|
+
await expect(client.createOrUpdateComment(testBody)).rejects.toThrow('API rate limit exceeded');
|
|
139
|
+
});
|
|
140
|
+
it('handles network errors gracefully', async () => {
|
|
141
|
+
mockOctokit.issues.listComments.mockRejectedValue(new Error('Network error'));
|
|
142
|
+
await expect(client.createOrUpdateComment(testBody)).rejects.toThrow('Network error');
|
|
143
|
+
});
|
|
144
|
+
it('handles createComment API errors', async () => {
|
|
145
|
+
mockOctokit.issues.listComments.mockResolvedValue({ data: [] });
|
|
146
|
+
mockOctokit.issues.createComment.mockRejectedValue(new Error('Resource not accessible by integration'));
|
|
147
|
+
await expect(client.createOrUpdateComment(testBody)).rejects.toThrow('Resource not accessible by integration');
|
|
148
|
+
});
|
|
149
|
+
it('handles updateComment API errors', async () => {
|
|
150
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
151
|
+
data: [{ id: 456, body: `${COMMENT_MARKER}\nOld content` }],
|
|
152
|
+
});
|
|
153
|
+
mockOctokit.issues.updateComment.mockRejectedValue(new Error('Comment not found'));
|
|
154
|
+
await expect(client.createOrUpdateComment(testBody)).rejects.toThrow('Comment not found');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
describe('deleteComment', () => {
|
|
158
|
+
it('deletes existing buoy comment', async () => {
|
|
159
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
160
|
+
data: [{ id: 456, body: `${COMMENT_MARKER}\nReport content` }],
|
|
161
|
+
});
|
|
162
|
+
mockOctokit.issues.deleteComment.mockResolvedValue({});
|
|
163
|
+
await client.deleteComment();
|
|
164
|
+
expect(mockOctokit.issues.deleteComment).toHaveBeenCalledWith({
|
|
165
|
+
owner: 'test-owner',
|
|
166
|
+
repo: 'test-repo',
|
|
167
|
+
comment_id: 456,
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
it('does nothing when no buoy comment exists', async () => {
|
|
171
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
172
|
+
data: [{ id: 789, body: 'Some other comment' }],
|
|
173
|
+
});
|
|
174
|
+
await client.deleteComment();
|
|
175
|
+
expect(mockOctokit.issues.deleteComment).not.toHaveBeenCalled();
|
|
176
|
+
});
|
|
177
|
+
it('handles delete errors', async () => {
|
|
178
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
179
|
+
data: [{ id: 456, body: `${COMMENT_MARKER}\nReport` }],
|
|
180
|
+
});
|
|
181
|
+
mockOctokit.issues.deleteComment.mockRejectedValue(new Error('Cannot delete comment'));
|
|
182
|
+
await expect(client.deleteComment()).rejects.toThrow('Cannot delete comment');
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('parseRepoString', () => {
|
|
187
|
+
it('parses valid owner/repo format', () => {
|
|
188
|
+
const result = parseRepoString('test-owner/test-repo');
|
|
189
|
+
expect(result).toEqual({
|
|
190
|
+
owner: 'test-owner',
|
|
191
|
+
repo: 'test-repo',
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
it('handles repo names with hyphens', () => {
|
|
195
|
+
const result = parseRepoString('my-org/my-cool-repo');
|
|
196
|
+
expect(result).toEqual({
|
|
197
|
+
owner: 'my-org',
|
|
198
|
+
repo: 'my-cool-repo',
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
it('handles repo names with underscores', () => {
|
|
202
|
+
const result = parseRepoString('org_name/repo_name');
|
|
203
|
+
expect(result).toEqual({
|
|
204
|
+
owner: 'org_name',
|
|
205
|
+
repo: 'repo_name',
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
it('handles repo names with dots', () => {
|
|
209
|
+
const result = parseRepoString('owner/repo.js');
|
|
210
|
+
expect(result).toEqual({
|
|
211
|
+
owner: 'owner',
|
|
212
|
+
repo: 'repo.js',
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
it('throws error for invalid format without slash', () => {
|
|
216
|
+
expect(() => parseRepoString('invalid-format')).toThrow('Invalid repo format: "invalid-format". Expected "owner/repo".');
|
|
217
|
+
});
|
|
218
|
+
it('throws error for format with multiple slashes', () => {
|
|
219
|
+
expect(() => parseRepoString('owner/repo/extra')).toThrow('Invalid repo format: "owner/repo/extra". Expected "owner/repo".');
|
|
220
|
+
});
|
|
221
|
+
it('throws error for empty string', () => {
|
|
222
|
+
expect(() => parseRepoString('')).toThrow('Invalid repo format: "". Expected "owner/repo".');
|
|
223
|
+
});
|
|
224
|
+
it('parses slash into empty owner and repo', () => {
|
|
225
|
+
// Edge case: "/" splits into ["", ""] which has length 2
|
|
226
|
+
// So the function returns empty strings rather than throwing
|
|
227
|
+
const result = parseRepoString('/');
|
|
228
|
+
expect(result).toEqual({ owner: '', repo: '' });
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
describe('GitHubClient integration scenarios', () => {
|
|
232
|
+
let mockOctokit;
|
|
233
|
+
let client;
|
|
234
|
+
beforeEach(() => {
|
|
235
|
+
vi.clearAllMocks();
|
|
236
|
+
client = new GitHubClient({
|
|
237
|
+
token: 'ghp_test123',
|
|
238
|
+
owner: 'acme',
|
|
239
|
+
repo: 'design-system',
|
|
240
|
+
prNumber: 42,
|
|
241
|
+
});
|
|
242
|
+
mockOctokit = Octokit.mock.results[0]?.value;
|
|
243
|
+
});
|
|
244
|
+
it('handles first report on a new PR', async () => {
|
|
245
|
+
mockOctokit.issues.listComments.mockResolvedValue({ data: [] });
|
|
246
|
+
mockOctokit.issues.createComment.mockResolvedValue({
|
|
247
|
+
data: { id: 1001, html_url: 'https://github.com/acme/design-system/pull/42#issuecomment-1001' },
|
|
248
|
+
});
|
|
249
|
+
const reportBody = `${COMMENT_MARKER}\n## Drift Report\nAll good!`;
|
|
250
|
+
await client.createOrUpdateComment(reportBody);
|
|
251
|
+
expect(mockOctokit.issues.createComment).toHaveBeenCalledTimes(1);
|
|
252
|
+
expect(mockOctokit.issues.updateComment).not.toHaveBeenCalled();
|
|
253
|
+
});
|
|
254
|
+
it('handles subsequent reports updating existing comment', async () => {
|
|
255
|
+
const existingCommentId = 5001;
|
|
256
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
257
|
+
data: [
|
|
258
|
+
{ id: existingCommentId, body: `${COMMENT_MARKER}\n## Old Report` },
|
|
259
|
+
],
|
|
260
|
+
});
|
|
261
|
+
mockOctokit.issues.updateComment.mockResolvedValue({
|
|
262
|
+
data: { id: existingCommentId },
|
|
263
|
+
});
|
|
264
|
+
const newReportBody = `${COMMENT_MARKER}\n## Updated Report\n3 issues found`;
|
|
265
|
+
await client.createOrUpdateComment(newReportBody);
|
|
266
|
+
expect(mockOctokit.issues.updateComment).toHaveBeenCalledTimes(1);
|
|
267
|
+
expect(mockOctokit.issues.createComment).not.toHaveBeenCalled();
|
|
268
|
+
expect(mockOctokit.issues.updateComment).toHaveBeenCalledWith({
|
|
269
|
+
owner: 'acme',
|
|
270
|
+
repo: 'design-system',
|
|
271
|
+
comment_id: existingCommentId,
|
|
272
|
+
body: newReportBody,
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
it('preserves other comments when updating buoy comment', async () => {
|
|
276
|
+
mockOctokit.issues.listComments.mockResolvedValue({
|
|
277
|
+
data: [
|
|
278
|
+
{ id: 1, body: 'LGTM!' },
|
|
279
|
+
{ id: 2, body: `${COMMENT_MARKER}\n## Buoy Report` },
|
|
280
|
+
{ id: 3, body: 'Can you fix the linting?' },
|
|
281
|
+
],
|
|
282
|
+
});
|
|
283
|
+
mockOctokit.issues.updateComment.mockResolvedValue({ data: { id: 2 } });
|
|
284
|
+
await client.createOrUpdateComment(`${COMMENT_MARKER}\n## New Report`);
|
|
285
|
+
// Should only update comment id 2, not touch others
|
|
286
|
+
expect(mockOctokit.issues.updateComment).toHaveBeenCalledWith(expect.objectContaining({ comment_id: 2 }));
|
|
287
|
+
expect(mockOctokit.issues.deleteComment).not.toHaveBeenCalled();
|
|
288
|
+
});
|
|
289
|
+
it('handles PR with many comments efficiently', async () => {
|
|
290
|
+
// Simulate a PR with many comments where buoy comment is near the end
|
|
291
|
+
const manyComments = Array.from({ length: 50 }, (_, i) => ({
|
|
292
|
+
id: i + 1,
|
|
293
|
+
body: i === 45 ? `${COMMENT_MARKER}\n## Report` : `Comment ${i}`,
|
|
294
|
+
}));
|
|
295
|
+
mockOctokit.issues.listComments.mockResolvedValue({ data: manyComments });
|
|
296
|
+
mockOctokit.issues.updateComment.mockResolvedValue({ data: { id: 46 } });
|
|
297
|
+
const existingId = await client.findExistingComment();
|
|
298
|
+
expect(existingId).toBe(46); // 45 + 1 due to 0-indexing
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
//# sourceMappingURL=github.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.test.js","sourceRoot":"","sources":["../../src/__tests__/github.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,cAAc,+BAA+B,CAAC;AAE3D,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CA2E5D"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Hidden marker to identify Buoy comments for updates
|
|
2
|
+
export const COMMENT_MARKER = '<!-- buoy-drift-report -->';
|
|
3
|
+
export function formatPRComment(results) {
|
|
4
|
+
const lines = [COMMENT_MARKER];
|
|
5
|
+
// Header
|
|
6
|
+
const icon = results.summary.critical > 0 ? '🔴' :
|
|
7
|
+
results.summary.warning > 0 ? '🟡' : '🟢';
|
|
8
|
+
lines.push(`## ${icon} Buoy Drift Report`);
|
|
9
|
+
lines.push('');
|
|
10
|
+
// Summary
|
|
11
|
+
const { total, critical, warning, info } = results.summary;
|
|
12
|
+
if (total === 0) {
|
|
13
|
+
lines.push('No design drift detected. Your code is aligned with the design system!');
|
|
14
|
+
lines.push('');
|
|
15
|
+
lines.push('---');
|
|
16
|
+
lines.push('<sub>🔱 <a href="https://github.com/dylantarre/buoy">Buoy</a></sub>');
|
|
17
|
+
return lines.join('\n');
|
|
18
|
+
}
|
|
19
|
+
lines.push(`**${total} issue${total === 1 ? '' : 's'} found** (${critical} critical, ${warning} warning${warning === 1 ? '' : 's'}, ${info} info)`);
|
|
20
|
+
lines.push('');
|
|
21
|
+
// Group by severity
|
|
22
|
+
const criticals = results.signals.filter(s => s.severity === 'critical');
|
|
23
|
+
const warnings = results.signals.filter(s => s.severity === 'warning');
|
|
24
|
+
const infos = results.signals.filter(s => s.severity === 'info');
|
|
25
|
+
// Critical issues table
|
|
26
|
+
if (criticals.length > 0) {
|
|
27
|
+
lines.push('### Critical');
|
|
28
|
+
lines.push('');
|
|
29
|
+
lines.push('| Component | Issue | File |');
|
|
30
|
+
lines.push('|-----------|-------|------|');
|
|
31
|
+
for (const signal of criticals) {
|
|
32
|
+
const file = signal.file ? `\`${signal.file}${signal.line ? `:${signal.line}` : ''}\`` : '-';
|
|
33
|
+
lines.push(`| \`${signal.component || '-'}\` | ${signal.message} | ${file} |`);
|
|
34
|
+
}
|
|
35
|
+
lines.push('');
|
|
36
|
+
}
|
|
37
|
+
// Warning issues table
|
|
38
|
+
if (warnings.length > 0) {
|
|
39
|
+
lines.push('### Warnings');
|
|
40
|
+
lines.push('');
|
|
41
|
+
lines.push('| Component | Issue | File |');
|
|
42
|
+
lines.push('|-----------|-------|------|');
|
|
43
|
+
for (const signal of warnings.slice(0, 10)) {
|
|
44
|
+
const file = signal.file ? `\`${signal.file}${signal.line ? `:${signal.line}` : ''}\`` : '-';
|
|
45
|
+
lines.push(`| \`${signal.component || '-'}\` | ${signal.message} | ${file} |`);
|
|
46
|
+
}
|
|
47
|
+
if (warnings.length > 10) {
|
|
48
|
+
lines.push(`| ... | *${warnings.length - 10} more warnings* | |`);
|
|
49
|
+
}
|
|
50
|
+
lines.push('');
|
|
51
|
+
}
|
|
52
|
+
// Info issues collapsed
|
|
53
|
+
if (infos.length > 0) {
|
|
54
|
+
lines.push('<details>');
|
|
55
|
+
lines.push(`<summary>${infos.length} info-level issue${infos.length === 1 ? '' : 's'}</summary>`);
|
|
56
|
+
lines.push('');
|
|
57
|
+
for (const signal of infos) {
|
|
58
|
+
const loc = signal.file ? ` (${signal.file}${signal.line ? `:${signal.line}` : ''})` : '';
|
|
59
|
+
lines.push(`- \`${signal.component || 'Unknown'}\`: ${signal.message}${loc}`);
|
|
60
|
+
}
|
|
61
|
+
lines.push('');
|
|
62
|
+
lines.push('</details>');
|
|
63
|
+
lines.push('');
|
|
64
|
+
}
|
|
65
|
+
// Footer
|
|
66
|
+
lines.push('---');
|
|
67
|
+
lines.push('<sub>🔱 <a href="https://github.com/dylantarre/buoy">Buoy</a></sub>');
|
|
68
|
+
return lines.join('\n');
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
|
package/dist/github.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface GitHubContext {
|
|
2
|
+
token: string;
|
|
3
|
+
owner: string;
|
|
4
|
+
repo: string;
|
|
5
|
+
prNumber: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class GitHubClient {
|
|
8
|
+
private octokit;
|
|
9
|
+
private context;
|
|
10
|
+
constructor(context: GitHubContext);
|
|
11
|
+
findExistingComment(): Promise<number | null>;
|
|
12
|
+
createOrUpdateComment(body: string): Promise<void>;
|
|
13
|
+
deleteComment(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseRepoString(repoString: string): {
|
|
16
|
+
owner: string;
|
|
17
|
+
repo: string;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,OAAO,CAAgB;gBAEnB,OAAO,EAAE,aAAa;IAU5B,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgB7C,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBlD,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAYrC;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMnF"}
|
package/dist/github.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// packages/plugin-github/src/github.ts
|
|
2
|
+
import { Octokit } from '@octokit/rest';
|
|
3
|
+
import { COMMENT_MARKER } from './formatter.js';
|
|
4
|
+
const REQUEST_TIMEOUT_MS = 30000; // 30 seconds
|
|
5
|
+
export class GitHubClient {
|
|
6
|
+
octokit;
|
|
7
|
+
context;
|
|
8
|
+
constructor(context) {
|
|
9
|
+
this.context = context;
|
|
10
|
+
this.octokit = new Octokit({
|
|
11
|
+
auth: context.token,
|
|
12
|
+
request: {
|
|
13
|
+
timeout: REQUEST_TIMEOUT_MS,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async findExistingComment() {
|
|
18
|
+
const { owner, repo, prNumber } = this.context;
|
|
19
|
+
const comments = await this.octokit.issues.listComments({
|
|
20
|
+
owner,
|
|
21
|
+
repo,
|
|
22
|
+
issue_number: prNumber,
|
|
23
|
+
});
|
|
24
|
+
const existing = comments.data.find((comment) => comment.body?.includes(COMMENT_MARKER));
|
|
25
|
+
return existing?.id ?? null;
|
|
26
|
+
}
|
|
27
|
+
async createOrUpdateComment(body) {
|
|
28
|
+
const { owner, repo, prNumber } = this.context;
|
|
29
|
+
const existingId = await this.findExistingComment();
|
|
30
|
+
if (existingId) {
|
|
31
|
+
await this.octokit.issues.updateComment({
|
|
32
|
+
owner,
|
|
33
|
+
repo,
|
|
34
|
+
comment_id: existingId,
|
|
35
|
+
body,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
await this.octokit.issues.createComment({
|
|
40
|
+
owner,
|
|
41
|
+
repo,
|
|
42
|
+
issue_number: prNumber,
|
|
43
|
+
body,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async deleteComment() {
|
|
48
|
+
const { owner, repo } = this.context;
|
|
49
|
+
const existingId = await this.findExistingComment();
|
|
50
|
+
if (existingId) {
|
|
51
|
+
await this.octokit.issues.deleteComment({
|
|
52
|
+
owner,
|
|
53
|
+
repo,
|
|
54
|
+
comment_id: existingId,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function parseRepoString(repoString) {
|
|
60
|
+
const parts = repoString.split('/');
|
|
61
|
+
if (parts.length !== 2) {
|
|
62
|
+
throw new Error(`Invalid repo format: "${repoString}". Expected "owner/repo".`);
|
|
63
|
+
}
|
|
64
|
+
return { owner: parts[0], repo: parts[1] };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,kBAAkB,GAAG,KAAK,CAAC,CAAC,aAAa;AAS/C,MAAM,OAAO,YAAY;IACf,OAAO,CAAU;IACjB,OAAO,CAAgB;IAE/B,YAAY,OAAsB;QAChC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC;YACzB,IAAI,EAAE,OAAO,CAAC,KAAK;YACnB,OAAO,EAAE;gBACP,OAAO,EAAE,kBAAkB;aAC5B;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAE/C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;YACtD,KAAK;YACL,IAAI;YACJ,YAAY,EAAE,QAAQ;SACvB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CACjC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,cAAc,CAAC,CACpD,CAAC;QAEF,OAAO,QAAQ,EAAE,EAAE,IAAI,IAAI,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,IAAY;QACtC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAC/C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAEpD,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;gBACtC,KAAK;gBACL,IAAI;gBACJ,UAAU,EAAE,UAAU;gBACtB,IAAI;aACL,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;gBACtC,KAAK;gBACL,IAAI;gBACJ,YAAY,EAAE,QAAQ;gBACtB,IAAI;aACL,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QACrC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAEpD,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;gBACtC,KAAK;gBACL,IAAI;gBACJ,UAAU,EAAE,UAAU;aACvB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yBAAyB,UAAU,2BAA2B,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC;AAC/C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BuoyPlugin } from '@buoy-design/core';
|
|
2
|
+
declare const plugin: BuoyPlugin;
|
|
3
|
+
declare const _default: () => BuoyPlugin;
|
|
4
|
+
export default _default;
|
|
5
|
+
export { plugin };
|
|
6
|
+
export { formatPRComment } from './formatter.js';
|
|
7
|
+
export { GitHubClient, parseRepoString } from './github.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAA8B,MAAM,mBAAmB,CAAC;AAIhF,QAAA,MAAM,MAAM,EAAE,UAkCb,CAAC;;AAEF,wBAA4B;AAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { formatPRComment } from './formatter.js';
|
|
2
|
+
import { GitHubClient, parseRepoString } from './github.js';
|
|
3
|
+
const plugin = {
|
|
4
|
+
metadata: {
|
|
5
|
+
name: '@buoy-design/plugin-github',
|
|
6
|
+
version: '0.0.1',
|
|
7
|
+
description: 'GitHub PR comment integration for Buoy',
|
|
8
|
+
},
|
|
9
|
+
async report(results, context) {
|
|
10
|
+
if (!context.github) {
|
|
11
|
+
throw new Error('GitHub context is required. Provide token, repo, and pr number.');
|
|
12
|
+
}
|
|
13
|
+
const { token, repo, pr } = context.github;
|
|
14
|
+
const { owner, repo: repoName } = parseRepoString(repo);
|
|
15
|
+
const client = new GitHubClient({
|
|
16
|
+
token,
|
|
17
|
+
owner,
|
|
18
|
+
repo: repoName,
|
|
19
|
+
prNumber: pr,
|
|
20
|
+
});
|
|
21
|
+
// If no issues, optionally delete existing comment or post success
|
|
22
|
+
if (results.summary.total === 0) {
|
|
23
|
+
// Post a success comment
|
|
24
|
+
const body = formatPRComment(results);
|
|
25
|
+
await client.createOrUpdateComment(body);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Post or update comment with drift results
|
|
29
|
+
const body = formatPRComment(results);
|
|
30
|
+
await client.createOrUpdateComment(body);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default () => plugin;
|
|
34
|
+
export { plugin };
|
|
35
|
+
export { formatPRComment } from './formatter.js';
|
|
36
|
+
export { GitHubClient, parseRepoString } from './github.js';
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,MAAM,GAAe;IACzB,QAAQ,EAAE;QACR,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,OAAO;QAChB,WAAW,EAAE,wCAAwC;KACtD;IAED,KAAK,CAAC,MAAM,CAAC,OAAoB,EAAE,OAAsB;QACvD,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;QACrF,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAC3C,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QAExD,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC;YAC9B,KAAK;YACL,KAAK;YACL,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC;QAEH,mEAAmE;QACnE,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YAChC,yBAAyB;YACzB,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;YACtC,MAAM,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;YACzC,OAAO;QACT,CAAC;QAED,4CAA4C;QAC5C,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;CACF,CAAC;AAEF,eAAe,GAAG,EAAE,CAAC,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@buoy-design/plugin-github",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Buoy plugin for GitHub PR comments and CI integration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Buoy <hello@buoy.design>",
|
|
8
|
+
"homepage": "https://buoy.design",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/dylantarre/buoy-design.git",
|
|
12
|
+
"directory": "packages/plugin-github"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/dylantarre/buoy-design/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": ["buoy", "plugin", "github", "pr", "comments", "ci", "design-system"],
|
|
18
|
+
"files": ["dist", "README.md"],
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"dev": "tsc --watch",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"clean": "rm -rf dist",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@buoy-design/core": "workspace:*"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@octokit/rest": "^20.0.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@buoy-design/core": "workspace:*",
|
|
43
|
+
"typescript": "^5.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|