github-issue-tower-defence-management 1.73.1 → 1.73.3
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/CHANGELOG.md +14 -0
- package/bin/adapter/entry-points/cli/index.js +3 -0
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +8 -8
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +27 -7
- package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
- package/bin/domain/usecases/CheckIssueReviewReadinessUseCase.js +1 -1
- package/bin/domain/usecases/CheckIssueReviewReadinessUseCase.js.map +1 -1
- package/bin/domain/usecases/HandleScheduledEventUseCase.js +1 -0
- package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
- package/bin/domain/usecases/IssueRejectionEvaluator.js +3 -1
- package/bin/domain/usecases/IssueRejectionEvaluator.js.map +1 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +3 -3
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +4 -2
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.js +6 -1
- package/bin/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.test.ts +4 -0
- package/src/adapter/entry-points/cli/index.ts +3 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +114 -1
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +21 -11
- package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +103 -0
- package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +33 -9
- package/src/domain/usecases/CheckIssueReviewReadinessUseCase.ts +5 -1
- package/src/domain/usecases/HandleScheduledEventUseCase.ts +2 -0
- package/src/domain/usecases/IssueRejectionEvaluator.test.ts +51 -0
- package/src/domain/usecases/IssueRejectionEvaluator.ts +12 -5
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +4 -1
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +73 -0
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +10 -2
- package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.test.ts +57 -0
- package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.ts +11 -5
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts +2 -2
- package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
- package/types/domain/usecases/CheckIssueReviewReadinessUseCase.d.ts +1 -0
- package/types/domain/usecases/CheckIssueReviewReadinessUseCase.d.ts.map +1 -1
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
- package/types/domain/usecases/IssueRejectionEvaluator.d.ts +1 -1
- package/types/domain/usecases/IssueRejectionEvaluator.d.ts.map +1 -1
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts +1 -0
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts +1 -0
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.d.ts.map +1 -1
|
@@ -32,7 +32,7 @@ class RevertOrphanedPreparationUseCase {
|
|
|
32
32
|
if (!isOrphaned) {
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
|
-
const { hasRejections, comments } = await this.evaluateHasRejections(issue);
|
|
35
|
+
const { hasRejections, comments } = await this.evaluateHasRejections(issue, params.labelsAsLlmAgentName ?? []);
|
|
36
36
|
if (!hasRejections) {
|
|
37
37
|
if (awaitingQualityCheckStatusOption) {
|
|
38
38
|
await this.issueRepository.updateStatus(project, issue, awaitingQualityCheckStatusOption.id);
|
|
@@ -59,7 +59,7 @@ class RevertOrphanedPreparationUseCase {
|
|
|
59
59
|
await this.issueCommentRepository.createComment(issue, rejectionStatusMessage);
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
|
-
this.evaluateHasRejections = async (issue) => {
|
|
62
|
+
this.evaluateHasRejections = async (issue, labelsAsLlmAgentName) => {
|
|
63
63
|
if (issue.isClosed) {
|
|
64
64
|
return { hasRejections: false, comments: [] };
|
|
65
65
|
}
|
|
@@ -73,7 +73,9 @@ class RevertOrphanedPreparationUseCase {
|
|
|
73
73
|
}
|
|
74
74
|
const categoryLabels = issue.labels.filter((label) => label.startsWith('category:'));
|
|
75
75
|
const hasLlmAgentLabel = issue.labels.some((l) => l === 'llm-agent' || l.startsWith('llm-agent:'));
|
|
76
|
+
const hasLabelAsLlmAgentName = issue.labels.some((label) => labelsAsLlmAgentName.includes(label));
|
|
76
77
|
if (hasLlmAgentLabel ||
|
|
78
|
+
hasLabelAsLlmAgentName ||
|
|
77
79
|
(categoryLabels.length > 0 && !categoryLabels.includes('category:e2e'))) {
|
|
78
80
|
return { hasRejections: false, comments };
|
|
79
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RevertOrphanedPreparationUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/RevertOrphanedPreparationUseCase.ts"],"names":[],"mappings":";;;AASA,+DAKoC;AAEpC,MAAM,qCAAqC,GAAG,sBAAsB,CAAC;AAErE,MAAa,gCAAgC;IAC3C,YACW,iBAGR,EACQ,eAMR,EACQ,sBAGR,EACQ,kBAAsC;QAftC,sBAAiB,GAAjB,iBAAiB,CAGzB;QACQ,oBAAe,GAAf,eAAe,CAMvB;QACQ,2BAAsB,GAAtB,sBAAsB,CAG9B;QACQ,uBAAkB,GAAlB,kBAAkB,CAAoB;QAGjD,QAAG,GAAG,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"RevertOrphanedPreparationUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/RevertOrphanedPreparationUseCase.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,12 @@ class SetDependedIssueUrlForOpenTaskPRsUseCase {
|
|
|
15
15
|
}
|
|
16
16
|
const relatedOpenPRs = await this.issueRepository.findRelatedOpenPRs(issue.url);
|
|
17
17
|
for (const pr of relatedOpenPRs) {
|
|
18
|
-
|
|
18
|
+
try {
|
|
19
|
+
await this.issueRepository.setDependedIssueUrl(pr.url, input.project, issue.url);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.warn(`Failed to set depended issue URL for PR ${pr.url}, skipping and continuing with remaining PRs: ${error instanceof Error ? error.message : String(error)}`);
|
|
23
|
+
}
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SetDependedIssueUrlForOpenTaskPRsUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.ts"],"names":[],"mappings":";;;AAIA,MAAa,wCAAwC;IACnD,YACW,eAGR;QAHQ,oBAAe,GAAf,eAAe,CAGvB;QAGH,QAAG,GAAG,KAAK,EAAE,KAA4C,EAAiB,EAAE;YAC1E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,gCAAgC,EAAE,CAAC;gBACpD,OAAO,CAAC,IAAI,CACV,qHAAqH,CACtH,CAAC;gBACF,OAAO;YACT,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACjC,SAAS;gBACX,CAAC;gBACD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAClE,KAAK,CAAC,GAAG,CACV,CAAC;gBACF,KAAK,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC;oBAChC,MAAM,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAC5C,EAAE,CAAC,GAAG,EACN,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,GAAG,CACV,CAAC;
|
|
1
|
+
{"version":3,"file":"SetDependedIssueUrlForOpenTaskPRsUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.ts"],"names":[],"mappings":";;;AAIA,MAAa,wCAAwC;IACnD,YACW,eAGR;QAHQ,oBAAe,GAAf,eAAe,CAGvB;QAGH,QAAG,GAAG,KAAK,EAAE,KAA4C,EAAiB,EAAE;YAC1E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,gCAAgC,EAAE,CAAC;gBACpD,OAAO,CAAC,IAAI,CACV,qHAAqH,CACtH,CAAC;gBACF,OAAO;YACT,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACjC,SAAS;gBACX,CAAC;gBACD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAClE,KAAK,CAAC,GAAG,CACV,CAAC;gBACF,KAAK,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC;oBAChC,IAAI,CAAC;wBACH,MAAM,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAC5C,EAAE,CAAC,GAAG,EACN,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,GAAG,CACV,CAAC;oBACJ,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,OAAO,CAAC,IAAI,CACV,2CAA2C,EAAE,CAAC,GAAG,iDAAiD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC3J,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC,CAAC;IA9BC,CAAC;CA+BL;AArCD,4FAqCC"}
|
package/package.json
CHANGED
|
@@ -1188,6 +1188,7 @@ mysteryKey: 'value'
|
|
|
1188
1188
|
thresholdForAutoReject: 3,
|
|
1189
1189
|
workflowBlockerResolvedWebhookUrl: null,
|
|
1190
1190
|
allowedIssueAuthors: null,
|
|
1191
|
+
labelsAsLlmAgentName: null,
|
|
1191
1192
|
});
|
|
1192
1193
|
});
|
|
1193
1194
|
|
|
@@ -1222,6 +1223,7 @@ mysteryKey: 'value'
|
|
|
1222
1223
|
thresholdForAutoReject: 3,
|
|
1223
1224
|
workflowBlockerResolvedWebhookUrl: null,
|
|
1224
1225
|
allowedIssueAuthors: null,
|
|
1226
|
+
labelsAsLlmAgentName: null,
|
|
1225
1227
|
});
|
|
1226
1228
|
});
|
|
1227
1229
|
|
|
@@ -1501,6 +1503,7 @@ mysteryKey: 'value'
|
|
|
1501
1503
|
expect(mockRun).toHaveBeenCalledWith({
|
|
1502
1504
|
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
1503
1505
|
issueUrl: 'https://github.com/test/repo/issues/1',
|
|
1506
|
+
labelsAsLlmAgentName: null,
|
|
1504
1507
|
});
|
|
1505
1508
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
1506
1509
|
`${JSON.stringify({ reviewReady: true, rejections: [] })}\n`,
|
|
@@ -1583,6 +1586,7 @@ mysteryKey: 'value'
|
|
|
1583
1586
|
expect(mockRun).toHaveBeenCalledWith({
|
|
1584
1587
|
projectUrl: 'https://github.com/orgs/override/projects/2',
|
|
1585
1588
|
issueUrl: 'https://github.com/test/repo/issues/1',
|
|
1589
|
+
labelsAsLlmAgentName: null,
|
|
1586
1590
|
});
|
|
1587
1591
|
|
|
1588
1592
|
stdoutSpy.mockRestore();
|
|
@@ -268,6 +268,7 @@ program
|
|
|
268
268
|
thresholdForAutoReject: config.thresholdForAutoReject ?? 3,
|
|
269
269
|
awLogDirectoryPath: config.awLogDirectoryPath,
|
|
270
270
|
awLogStaleThresholdMinutes: config.awLogStaleThresholdMinutes,
|
|
271
|
+
labelsAsLlmAgentName: config.labelsAsLlmAgentName ?? null,
|
|
271
272
|
});
|
|
272
273
|
}
|
|
273
274
|
|
|
@@ -448,6 +449,7 @@ program
|
|
|
448
449
|
thresholdForAutoReject,
|
|
449
450
|
workflowBlockerResolvedWebhookUrl,
|
|
450
451
|
allowedIssueAuthors,
|
|
452
|
+
labelsAsLlmAgentName: config.labelsAsLlmAgentName ?? null,
|
|
451
453
|
});
|
|
452
454
|
});
|
|
453
455
|
|
|
@@ -540,6 +542,7 @@ program
|
|
|
540
542
|
const result = await useCase.run({
|
|
541
543
|
projectUrl,
|
|
542
544
|
issueUrl: options.issueUrl,
|
|
545
|
+
labelsAsLlmAgentName: config.labelsAsLlmAgentName ?? null,
|
|
543
546
|
});
|
|
544
547
|
|
|
545
548
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
@@ -2,9 +2,13 @@ import { mock } from 'jest-mock-extended';
|
|
|
2
2
|
import { ApiV3CheerioRestIssueRepository } from './ApiV3CheerioRestIssueRepository';
|
|
3
3
|
import { ApiV3IssueRepository } from './ApiV3IssueRepository';
|
|
4
4
|
import { RestIssueRepository } from './RestIssueRepository';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
GraphqlProjectItemRepository,
|
|
7
|
+
ProjectItem,
|
|
8
|
+
} from './GraphqlProjectItemRepository';
|
|
6
9
|
import { LocalStorageCacheRepository } from '../LocalStorageCacheRepository';
|
|
7
10
|
import { LocalStorageRepository } from '../LocalStorageRepository';
|
|
11
|
+
import { Project } from '../../../domain/entities/Project';
|
|
8
12
|
|
|
9
13
|
describe('ApiV3CheerioRestIssueRepository', () => {
|
|
10
14
|
describe('convertProjectItemToIssue', () => {
|
|
@@ -267,6 +271,115 @@ describe('ApiV3CheerioRestIssueRepository', () => {
|
|
|
267
271
|
});
|
|
268
272
|
});
|
|
269
273
|
|
|
274
|
+
describe('setDependedIssueUrl', () => {
|
|
275
|
+
const projectWithDependedIssueUrlField = {
|
|
276
|
+
...mock<Project>(),
|
|
277
|
+
id: 'test-project-id',
|
|
278
|
+
dependedIssueUrlSeparatedByComma: {
|
|
279
|
+
name: 'Depended Issue URL separated by comma',
|
|
280
|
+
fieldId: 'depended-field-id',
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
const prUrl = 'https://github.com/owner/repo/pull/100';
|
|
284
|
+
const taskIssueUrl = 'https://github.com/owner/repo/issues/1';
|
|
285
|
+
|
|
286
|
+
const makeProjectItem = (
|
|
287
|
+
id: string,
|
|
288
|
+
customFields: { name: string; value: string | null }[],
|
|
289
|
+
): ProjectItem => ({
|
|
290
|
+
...mock<ProjectItem>(),
|
|
291
|
+
id,
|
|
292
|
+
url: prUrl,
|
|
293
|
+
customFields,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('should add the PR to the current project and set the field when the PR has no project item on the current project', async () => {
|
|
297
|
+
const { repository, graphqlProjectItemRepository } =
|
|
298
|
+
createApiV3CheerioRestIssueRepository();
|
|
299
|
+
graphqlProjectItemRepository.fetchProjectItemByUrl.mockResolvedValue(
|
|
300
|
+
null,
|
|
301
|
+
);
|
|
302
|
+
graphqlProjectItemRepository.addIssueToProject.mockResolvedValue(
|
|
303
|
+
'new-project-item-id',
|
|
304
|
+
);
|
|
305
|
+
graphqlProjectItemRepository.updateProjectTextField.mockResolvedValue();
|
|
306
|
+
|
|
307
|
+
await repository.setDependedIssueUrl(
|
|
308
|
+
prUrl,
|
|
309
|
+
projectWithDependedIssueUrlField,
|
|
310
|
+
taskIssueUrl,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
expect(
|
|
314
|
+
graphqlProjectItemRepository.fetchProjectItemByUrl,
|
|
315
|
+
).toHaveBeenCalledWith(prUrl, 'test-project-id');
|
|
316
|
+
expect(
|
|
317
|
+
graphqlProjectItemRepository.addIssueToProject,
|
|
318
|
+
).toHaveBeenCalledWith('test-project-id', prUrl);
|
|
319
|
+
expect(
|
|
320
|
+
graphqlProjectItemRepository.updateProjectTextField,
|
|
321
|
+
).toHaveBeenCalledWith(
|
|
322
|
+
'test-project-id',
|
|
323
|
+
'depended-field-id',
|
|
324
|
+
'new-project-item-id',
|
|
325
|
+
taskIssueUrl,
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('should set the field on the existing project item without adding the PR when it already belongs to the current project', async () => {
|
|
330
|
+
const { repository, graphqlProjectItemRepository } =
|
|
331
|
+
createApiV3CheerioRestIssueRepository();
|
|
332
|
+
graphqlProjectItemRepository.fetchProjectItemByUrl.mockResolvedValue(
|
|
333
|
+
makeProjectItem('existing-project-item-id', []),
|
|
334
|
+
);
|
|
335
|
+
graphqlProjectItemRepository.updateProjectTextField.mockResolvedValue();
|
|
336
|
+
|
|
337
|
+
await repository.setDependedIssueUrl(
|
|
338
|
+
prUrl,
|
|
339
|
+
projectWithDependedIssueUrlField,
|
|
340
|
+
taskIssueUrl,
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
expect(
|
|
344
|
+
graphqlProjectItemRepository.addIssueToProject,
|
|
345
|
+
).not.toHaveBeenCalled();
|
|
346
|
+
expect(
|
|
347
|
+
graphqlProjectItemRepository.updateProjectTextField,
|
|
348
|
+
).toHaveBeenCalledWith(
|
|
349
|
+
'test-project-id',
|
|
350
|
+
'depended-field-id',
|
|
351
|
+
'existing-project-item-id',
|
|
352
|
+
taskIssueUrl,
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should do nothing when the depended-issue-url field value is already set on the existing project item', async () => {
|
|
357
|
+
const { repository, graphqlProjectItemRepository } =
|
|
358
|
+
createApiV3CheerioRestIssueRepository();
|
|
359
|
+
graphqlProjectItemRepository.fetchProjectItemByUrl.mockResolvedValue(
|
|
360
|
+
makeProjectItem('existing-project-item-id', [
|
|
361
|
+
{
|
|
362
|
+
name: 'Depended Issue URL separated by comma',
|
|
363
|
+
value: taskIssueUrl,
|
|
364
|
+
},
|
|
365
|
+
]),
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
await repository.setDependedIssueUrl(
|
|
369
|
+
prUrl,
|
|
370
|
+
projectWithDependedIssueUrlField,
|
|
371
|
+
taskIssueUrl,
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
expect(
|
|
375
|
+
graphqlProjectItemRepository.addIssueToProject,
|
|
376
|
+
).not.toHaveBeenCalled();
|
|
377
|
+
expect(
|
|
378
|
+
graphqlProjectItemRepository.updateProjectTextField,
|
|
379
|
+
).not.toHaveBeenCalled();
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
270
383
|
describe('getPullRequestChangedFilePaths', () => {
|
|
271
384
|
afterEach(() => {
|
|
272
385
|
jest.restoreAllMocks();
|
|
@@ -516,24 +516,31 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
516
516
|
project: Project,
|
|
517
517
|
issueUrl: string,
|
|
518
518
|
): Promise<void> => {
|
|
519
|
-
|
|
519
|
+
const dependedIssueUrlField = project.dependedIssueUrlSeparatedByComma;
|
|
520
|
+
if (!dependedIssueUrlField) {
|
|
520
521
|
return;
|
|
521
522
|
}
|
|
522
|
-
const
|
|
523
|
-
await this.graphqlProjectItemRepository.fetchProjectItemByUrl(
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const existingValue =
|
|
528
|
-
(field) => field.name ===
|
|
523
|
+
const existingProjectItem =
|
|
524
|
+
await this.graphqlProjectItemRepository.fetchProjectItemByUrl(
|
|
525
|
+
prUrl,
|
|
526
|
+
project.id,
|
|
527
|
+
);
|
|
528
|
+
const existingValue = existingProjectItem?.customFields.find(
|
|
529
|
+
(field) => field.name === dependedIssueUrlField.name,
|
|
529
530
|
)?.value;
|
|
530
531
|
if (existingValue) {
|
|
531
532
|
return;
|
|
532
533
|
}
|
|
534
|
+
const projectItemId =
|
|
535
|
+
existingProjectItem?.id ??
|
|
536
|
+
(await this.graphqlProjectItemRepository.addIssueToProject(
|
|
537
|
+
project.id,
|
|
538
|
+
prUrl,
|
|
539
|
+
));
|
|
533
540
|
await this.graphqlProjectItemRepository.updateProjectTextField(
|
|
534
541
|
project.id,
|
|
535
|
-
|
|
536
|
-
|
|
542
|
+
dependedIssueUrlField.fieldId,
|
|
543
|
+
projectItemId,
|
|
537
544
|
issueUrl,
|
|
538
545
|
);
|
|
539
546
|
};
|
|
@@ -547,7 +554,10 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
547
554
|
return;
|
|
548
555
|
}
|
|
549
556
|
const projectItem =
|
|
550
|
-
await this.graphqlProjectItemRepository.fetchProjectItemByUrl(
|
|
557
|
+
await this.graphqlProjectItemRepository.fetchProjectItemByUrl(
|
|
558
|
+
issueUrl,
|
|
559
|
+
project.id,
|
|
560
|
+
);
|
|
551
561
|
if (!projectItem) {
|
|
552
562
|
return;
|
|
553
563
|
}
|
|
@@ -660,5 +660,108 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
660
660
|
|
|
661
661
|
expect(result).toBeNull();
|
|
662
662
|
});
|
|
663
|
+
|
|
664
|
+
const makeMultiProjectPrNode = (
|
|
665
|
+
url: string,
|
|
666
|
+
number: number,
|
|
667
|
+
nodes: {
|
|
668
|
+
id: string;
|
|
669
|
+
projectId: string;
|
|
670
|
+
}[],
|
|
671
|
+
) => ({
|
|
672
|
+
number,
|
|
673
|
+
title: 'PR Title',
|
|
674
|
+
state: 'OPEN',
|
|
675
|
+
url,
|
|
676
|
+
body: 'body text',
|
|
677
|
+
createdAt: '2024-01-01T00:00:00Z',
|
|
678
|
+
author: { login: 'octocat' },
|
|
679
|
+
labels: { nodes: [] },
|
|
680
|
+
assignees: { nodes: [] },
|
|
681
|
+
repository: { nameWithOwner: 'owner/repo' },
|
|
682
|
+
projectItems: {
|
|
683
|
+
nodes: nodes.map((node) => ({
|
|
684
|
+
id: node.id,
|
|
685
|
+
project: { id: node.projectId },
|
|
686
|
+
fieldValues: {
|
|
687
|
+
nodes: [
|
|
688
|
+
{
|
|
689
|
+
__typename: 'ProjectV2ItemFieldSingleSelectValue',
|
|
690
|
+
name: 'Preparation',
|
|
691
|
+
field: { name: 'Status' },
|
|
692
|
+
},
|
|
693
|
+
],
|
|
694
|
+
},
|
|
695
|
+
})),
|
|
696
|
+
},
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it('should select the project item belonging to the given project id when the PR is on multiple projects', async () => {
|
|
700
|
+
const localStorageRepository = new LocalStorageRepository();
|
|
701
|
+
const repository = new GraphqlProjectItemRepository(
|
|
702
|
+
localStorageRepository,
|
|
703
|
+
'dummy-token',
|
|
704
|
+
);
|
|
705
|
+
|
|
706
|
+
mockPost.mockReturnValueOnce(
|
|
707
|
+
mockJsonResponse({
|
|
708
|
+
data: {
|
|
709
|
+
repository: {
|
|
710
|
+
issue: null,
|
|
711
|
+
pullRequest: makeMultiProjectPrNode(
|
|
712
|
+
'https://github.com/owner/repo/pull/11',
|
|
713
|
+
11,
|
|
714
|
+
[
|
|
715
|
+
{ id: 'item-other-project', projectId: 'project-other' },
|
|
716
|
+
{ id: 'item-current-project', projectId: 'project-current' },
|
|
717
|
+
],
|
|
718
|
+
),
|
|
719
|
+
},
|
|
720
|
+
},
|
|
721
|
+
}),
|
|
722
|
+
);
|
|
723
|
+
|
|
724
|
+
const result = await repository.fetchProjectItemByUrl(
|
|
725
|
+
'https://github.com/owner/repo/pull/11',
|
|
726
|
+
'project-current',
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
expect(result).not.toBeNull();
|
|
730
|
+
expect(result?.id).toBe('item-current-project');
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
it('should return null and warn instead of throwing when the PR has no project item on the given project', async () => {
|
|
734
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
735
|
+
const localStorageRepository = new LocalStorageRepository();
|
|
736
|
+
const repository = new GraphqlProjectItemRepository(
|
|
737
|
+
localStorageRepository,
|
|
738
|
+
'dummy-token',
|
|
739
|
+
);
|
|
740
|
+
|
|
741
|
+
mockPost.mockReturnValueOnce(
|
|
742
|
+
mockJsonResponse({
|
|
743
|
+
data: {
|
|
744
|
+
repository: {
|
|
745
|
+
issue: null,
|
|
746
|
+
pullRequest: makeMultiProjectPrNode(
|
|
747
|
+
'https://github.com/owner/repo/pull/12',
|
|
748
|
+
12,
|
|
749
|
+
[{ id: 'item-other-project', projectId: 'project-other' }],
|
|
750
|
+
),
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
}),
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
const result = await repository.fetchProjectItemByUrl(
|
|
757
|
+
'https://github.com/owner/repo/pull/12',
|
|
758
|
+
'project-current',
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
expect(result).toBeNull();
|
|
762
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
763
|
+
|
|
764
|
+
warnSpy.mockRestore();
|
|
765
|
+
});
|
|
663
766
|
});
|
|
664
767
|
});
|
|
@@ -663,6 +663,7 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
663
663
|
};
|
|
664
664
|
fetchProjectItemByUrl = async (
|
|
665
665
|
issueUrl: string,
|
|
666
|
+
projectId?: string,
|
|
666
667
|
): Promise<ProjectItem | null> => {
|
|
667
668
|
const { owner, repo, issueNumber } = this.extractIssueFromUrl(issueUrl);
|
|
668
669
|
const graphql = `query GetIssueOrPullRequest($owner: String!, $repo: String!, $number: Int!) {
|
|
@@ -693,6 +694,9 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
693
694
|
projectItems(first: 10) {
|
|
694
695
|
nodes {
|
|
695
696
|
id
|
|
697
|
+
project {
|
|
698
|
+
id
|
|
699
|
+
}
|
|
696
700
|
fieldValues(first: 10) {
|
|
697
701
|
nodes {
|
|
698
702
|
... on ProjectV2ItemFieldTextValue {
|
|
@@ -767,6 +771,9 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
767
771
|
projectItems(first: 10) {
|
|
768
772
|
nodes {
|
|
769
773
|
id
|
|
774
|
+
project {
|
|
775
|
+
id
|
|
776
|
+
}
|
|
770
777
|
fieldValues(first: 10) {
|
|
771
778
|
nodes {
|
|
772
779
|
... on ProjectV2ItemFieldTextValue {
|
|
@@ -839,6 +846,7 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
839
846
|
projectItems: {
|
|
840
847
|
nodes: {
|
|
841
848
|
id: string;
|
|
849
|
+
project: { id: string } | null;
|
|
842
850
|
fieldValues: {
|
|
843
851
|
nodes: {
|
|
844
852
|
text: string;
|
|
@@ -883,9 +891,16 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
883
891
|
return null;
|
|
884
892
|
}
|
|
885
893
|
const projectItems = content.projectItems.nodes;
|
|
886
|
-
const item =
|
|
894
|
+
const item = projectId
|
|
895
|
+
? projectItems.find((node) => node.project?.id === projectId)
|
|
896
|
+
: projectItems[0];
|
|
887
897
|
if (!item) {
|
|
888
|
-
|
|
898
|
+
console.warn(
|
|
899
|
+
projectId
|
|
900
|
+
? `No project item found for issue ${issueUrl} on project ${projectId}`
|
|
901
|
+
: `No project item found for issue ${issueUrl}`,
|
|
902
|
+
);
|
|
903
|
+
return null;
|
|
889
904
|
}
|
|
890
905
|
return {
|
|
891
906
|
id: item.id,
|
|
@@ -1066,13 +1081,18 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
1066
1081
|
addIssueToProject = async (
|
|
1067
1082
|
projectId: string,
|
|
1068
1083
|
issueUrl: string,
|
|
1069
|
-
): Promise<
|
|
1084
|
+
): Promise<string> => {
|
|
1070
1085
|
const { owner, repo, issueNumber } = this.extractIssueFromUrl(issueUrl);
|
|
1071
1086
|
const nodeIdQuery = {
|
|
1072
|
-
query: `query
|
|
1087
|
+
query: `query GetContentNodeId($owner: String!, $repo: String!, $number: Int!) {
|
|
1073
1088
|
repository(owner: $owner, name: $repo) {
|
|
1074
|
-
|
|
1075
|
-
|
|
1089
|
+
issueOrPullRequest(number: $number) {
|
|
1090
|
+
... on Issue {
|
|
1091
|
+
id
|
|
1092
|
+
}
|
|
1093
|
+
... on PullRequest {
|
|
1094
|
+
id
|
|
1095
|
+
}
|
|
1076
1096
|
}
|
|
1077
1097
|
}
|
|
1078
1098
|
}`,
|
|
@@ -1088,9 +1108,9 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
1088
1108
|
.json<{
|
|
1089
1109
|
data: {
|
|
1090
1110
|
repository: {
|
|
1091
|
-
|
|
1111
|
+
issueOrPullRequest: {
|
|
1092
1112
|
id: string;
|
|
1093
|
-
};
|
|
1113
|
+
} | null;
|
|
1094
1114
|
};
|
|
1095
1115
|
};
|
|
1096
1116
|
errors?: { message: string }[];
|
|
@@ -1098,7 +1118,10 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
1098
1118
|
if (nodeIdRes.errors) {
|
|
1099
1119
|
throw new Error(nodeIdRes.errors.map((e) => e.message).join('\n'));
|
|
1100
1120
|
}
|
|
1101
|
-
const contentId = nodeIdRes.data.repository.
|
|
1121
|
+
const contentId = nodeIdRes.data.repository.issueOrPullRequest?.id;
|
|
1122
|
+
if (!contentId) {
|
|
1123
|
+
throw new Error(`Content not found for url ${issueUrl}`);
|
|
1124
|
+
}
|
|
1102
1125
|
|
|
1103
1126
|
const addQuery = {
|
|
1104
1127
|
query: `mutation AddIssueToProject($projectId: ID!, $contentId: ID!) {
|
|
@@ -1126,5 +1149,6 @@ query GetProjectFields($owner: String!, $repository: String!, $issueNumber: Int!
|
|
|
1126
1149
|
if (addRes.errors) {
|
|
1127
1150
|
throw new Error(addRes.errors.map((e) => e.message).join('\n'));
|
|
1128
1151
|
}
|
|
1152
|
+
return addRes.data.addProjectV2ItemById.item.id;
|
|
1129
1153
|
};
|
|
1130
1154
|
}
|
|
@@ -27,6 +27,7 @@ export class CheckIssueReviewReadinessUseCase {
|
|
|
27
27
|
run = async (params: {
|
|
28
28
|
projectUrl: string;
|
|
29
29
|
issueUrl: string;
|
|
30
|
+
labelsAsLlmAgentName?: string[] | null;
|
|
30
31
|
}): Promise<IssueReviewReadinessResult> => {
|
|
31
32
|
const project = await this.projectRepository.getByUrl(params.projectUrl);
|
|
32
33
|
const issue = await this.issueRepository.get(params.issueUrl, project);
|
|
@@ -35,7 +36,10 @@ export class CheckIssueReviewReadinessUseCase {
|
|
|
35
36
|
throw new IssueNotFoundError(params.issueUrl);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
const { rejections } = await this.issueRejectionEvaluator.evaluate(
|
|
39
|
+
const { rejections } = await this.issueRejectionEvaluator.evaluate(
|
|
40
|
+
issue,
|
|
41
|
+
params.labelsAsLlmAgentName ?? [],
|
|
42
|
+
);
|
|
39
43
|
|
|
40
44
|
return {
|
|
41
45
|
reviewReady: rejections.length === 0,
|
|
@@ -301,6 +301,8 @@ ${JSON.stringify(e)}
|
|
|
301
301
|
input.startPreparation.awLogStaleThresholdMinutes,
|
|
302
302
|
awaitingQualityCheckStatus:
|
|
303
303
|
input.startPreparation.awaitingQualityCheckStatus ?? undefined,
|
|
304
|
+
labelsAsLlmAgentName:
|
|
305
|
+
input.startPreparation.labelsAsLlmAgentName ?? null,
|
|
304
306
|
});
|
|
305
307
|
}
|
|
306
308
|
const preparationResult = await this.startPreparationUseCase.run({
|
|
@@ -149,5 +149,56 @@ describe('IssueRejectionEvaluator', () => {
|
|
|
149
149
|
expect(mockIssueRepository.findRelatedOpenPRs).not.toHaveBeenCalled();
|
|
150
150
|
expect(result.rejections).toHaveLength(0);
|
|
151
151
|
});
|
|
152
|
+
|
|
153
|
+
it('should reject with PULL_REQUEST_NOT_FOUND when a normal issue has no related PR', async () => {
|
|
154
|
+
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
155
|
+
|
|
156
|
+
const result = await evaluator.evaluate({
|
|
157
|
+
url: 'https://github.com/user/repo/issues/1',
|
|
158
|
+
labels: [],
|
|
159
|
+
isPr: false,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(mockIssueRepository.findRelatedOpenPRs).toHaveBeenCalledWith(
|
|
163
|
+
'https://github.com/user/repo/issues/1',
|
|
164
|
+
);
|
|
165
|
+
expect(result.rejections).toHaveLength(1);
|
|
166
|
+
expect(result.rejections[0].type).toBe('PULL_REQUEST_NOT_FOUND');
|
|
167
|
+
expect(result.approvedPrUrl).toBeNull();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should not reject with PULL_REQUEST_NOT_FOUND when issue has a label listed in labelsAsLlmAgentName', async () => {
|
|
171
|
+
const result = await evaluator.evaluate(
|
|
172
|
+
{
|
|
173
|
+
url: 'https://github.com/user/repo/issues/1',
|
|
174
|
+
labels: ['story'],
|
|
175
|
+
isPr: false,
|
|
176
|
+
},
|
|
177
|
+
['story'],
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
expect(mockIssueRepository.findRelatedOpenPRs).not.toHaveBeenCalled();
|
|
181
|
+
expect(result.rejections).toHaveLength(0);
|
|
182
|
+
expect(result.approvedPrUrl).toBeNull();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should still reject with PULL_REQUEST_NOT_FOUND when issue label is not in labelsAsLlmAgentName', async () => {
|
|
186
|
+
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
187
|
+
|
|
188
|
+
const result = await evaluator.evaluate(
|
|
189
|
+
{
|
|
190
|
+
url: 'https://github.com/user/repo/issues/1',
|
|
191
|
+
labels: ['story'],
|
|
192
|
+
isPr: false,
|
|
193
|
+
},
|
|
194
|
+
['bug'],
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(mockIssueRepository.findRelatedOpenPRs).toHaveBeenCalledWith(
|
|
198
|
+
'https://github.com/user/repo/issues/1',
|
|
199
|
+
);
|
|
200
|
+
expect(result.rejections).toHaveLength(1);
|
|
201
|
+
expect(result.rejections[0].type).toBe('PULL_REQUEST_NOT_FOUND');
|
|
202
|
+
});
|
|
152
203
|
});
|
|
153
204
|
});
|
|
@@ -25,11 +25,14 @@ export class IssueRejectionEvaluator {
|
|
|
25
25
|
>,
|
|
26
26
|
) {}
|
|
27
27
|
|
|
28
|
-
evaluate = async (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
evaluate = async (
|
|
29
|
+
issue: {
|
|
30
|
+
url: string;
|
|
31
|
+
labels: string[];
|
|
32
|
+
isPr: boolean;
|
|
33
|
+
},
|
|
34
|
+
labelsAsLlmAgentName: string[] = [],
|
|
35
|
+
): Promise<PrRejectionResult> => {
|
|
33
36
|
const rejections: { type: PrRejectedReasonType; detail: string }[] = [];
|
|
34
37
|
let approvedPrUrl: string | null = null;
|
|
35
38
|
|
|
@@ -39,9 +42,13 @@ export class IssueRejectionEvaluator {
|
|
|
39
42
|
const hasLlmAgentLabel = issue.labels.some(
|
|
40
43
|
(l) => l === 'llm-agent' || l.startsWith('llm-agent:'),
|
|
41
44
|
);
|
|
45
|
+
const hasLabelAsLlmAgentName = issue.labels.some((label) =>
|
|
46
|
+
labelsAsLlmAgentName.includes(label),
|
|
47
|
+
);
|
|
42
48
|
|
|
43
49
|
if (
|
|
44
50
|
!hasLlmAgentLabel &&
|
|
51
|
+
!hasLabelAsLlmAgentName &&
|
|
45
52
|
(categoryLabels.length <= 0 || categoryLabels.includes('category:e2e'))
|
|
46
53
|
) {
|
|
47
54
|
const prsToCheck = issue.isPr
|