github-issue-tower-defence-management 1.73.2 → 1.74.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +3 -0
  3. package/bin/adapter/entry-points/cli/index.js +3 -0
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/cli/projectConfig.js +6 -0
  6. package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
  7. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +8 -2
  8. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  9. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +8 -8
  10. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  11. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +27 -7
  12. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
  13. package/bin/domain/usecases/HandleScheduledEventUseCase.js +1 -0
  14. package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
  15. package/bin/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.js +6 -1
  16. package/bin/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.js.map +1 -1
  17. package/bin/domain/usecases/StartPreparationUseCase.js +21 -5
  18. package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/adapter/entry-points/cli/index.test.ts +2 -0
  21. package/src/adapter/entry-points/cli/index.ts +7 -0
  22. package/src/adapter/entry-points/cli/projectConfig.ts +8 -0
  23. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +3 -0
  24. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +114 -1
  25. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +21 -11
  26. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +103 -0
  27. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +33 -9
  28. package/src/domain/usecases/HandleScheduledEventUseCase.ts +3 -0
  29. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.test.ts +57 -0
  30. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.ts +11 -5
  31. package/src/domain/usecases/StartPreparationUseCase.test.ts +409 -0
  32. package/src/domain/usecases/StartPreparationUseCase.ts +32 -4
  33. package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
  34. package/types/adapter/entry-points/cli/projectConfig.d.ts +1 -0
  35. package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
  36. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  37. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  38. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts +2 -2
  39. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
  40. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +1 -0
  41. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
  42. package/types/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.d.ts.map +1 -1
  43. package/types/domain/usecases/StartPreparationUseCase.d.ts +2 -0
  44. package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
@@ -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 = projectItems[0];
894
+ const item = projectId
895
+ ? projectItems.find((node) => node.project?.id === projectId)
896
+ : projectItems[0];
887
897
  if (!item) {
888
- throw new Error(`No project item found for issue ${issueUrl}`);
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<void> => {
1084
+ ): Promise<string> => {
1070
1085
  const { owner, repo, issueNumber } = this.extractIssueFromUrl(issueUrl);
1071
1086
  const nodeIdQuery = {
1072
- query: `query GetIssueNodeId($owner: String!, $repo: String!, $number: Int!) {
1087
+ query: `query GetContentNodeId($owner: String!, $repo: String!, $number: Int!) {
1073
1088
  repository(owner: $owner, name: $repo) {
1074
- issue(number: $number) {
1075
- id
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
- issue: {
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.issue.id;
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
  }
@@ -81,6 +81,7 @@ export class HandleScheduledEventUseCase {
81
81
  startPreparation?: {
82
82
  defaultAgentName: string;
83
83
  defaultLlmModelName?: string | null;
84
+ fallbackLlmModelName?: string | null;
84
85
  defaultLlmAgentName?: string | null;
85
86
  configFilePath: string;
86
87
  maximumPreparingIssuesCount: number | null;
@@ -309,6 +310,8 @@ ${JSON.stringify(e)}
309
310
  projectUrl: input.projectUrl,
310
311
  defaultAgentName: input.startPreparation.defaultAgentName,
311
312
  defaultLlmModelName: input.startPreparation.defaultLlmModelName ?? null,
313
+ fallbackLlmModelName:
314
+ input.startPreparation.fallbackLlmModelName ?? null,
312
315
  defaultLlmAgentName: input.startPreparation.defaultLlmAgentName ?? null,
313
316
  configFilePath: input.startPreparation.configFilePath,
314
317
  maximumPreparingIssuesCount:
@@ -211,4 +211,61 @@ describe('SetDependedIssueUrlForOpenTaskPRsUseCase', () => {
211
211
  secondOpenTaskIssue.url,
212
212
  );
213
213
  });
214
+
215
+ it('should isolate a single PR failure, log it, and still process the remaining PRs without aborting the cycle', async () => {
216
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
217
+ const failingPrUrl = 'https://github.com/owner/repo/pull/100';
218
+ const succeedingPrUrl = 'https://github.com/owner/repo/pull/200';
219
+
220
+ mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([
221
+ {
222
+ url: failingPrUrl,
223
+ branchName: null,
224
+ createdAt: new Date(0),
225
+ isDraft: false,
226
+ isConflicted: false,
227
+ isPassedAllCiJob: true,
228
+ isCiStateSuccess: true,
229
+ isResolvedAllReviewComments: true,
230
+ isBranchOutOfDate: false,
231
+ missingRequiredCheckNames: [],
232
+ },
233
+ {
234
+ url: succeedingPrUrl,
235
+ branchName: null,
236
+ createdAt: new Date(0),
237
+ isDraft: false,
238
+ isConflicted: false,
239
+ isPassedAllCiJob: true,
240
+ isCiStateSuccess: true,
241
+ isResolvedAllReviewComments: true,
242
+ isBranchOutOfDate: false,
243
+ missingRequiredCheckNames: [],
244
+ },
245
+ ]);
246
+ mockIssueRepository.setDependedIssueUrl.mockImplementation(
247
+ async (prUrl) => {
248
+ if (prUrl === failingPrUrl) {
249
+ throw new Error('boom');
250
+ }
251
+ },
252
+ );
253
+
254
+ await expect(
255
+ useCase.run({
256
+ project: projectWithField,
257
+ issues: [openTaskIssue],
258
+ }),
259
+ ).resolves.toBeUndefined();
260
+
261
+ expect(mockIssueRepository.setDependedIssueUrl).toHaveBeenCalledTimes(2);
262
+ expect(mockIssueRepository.setDependedIssueUrl).toHaveBeenCalledWith(
263
+ succeedingPrUrl,
264
+ projectWithField,
265
+ openTaskIssue.url,
266
+ );
267
+ expect(warnSpy).toHaveBeenCalled();
268
+
269
+ warnSpy.mockRestore();
270
+ });
214
271
  });
@@ -25,11 +25,17 @@ export class SetDependedIssueUrlForOpenTaskPRsUseCase {
25
25
  issue.url,
26
26
  );
27
27
  for (const pr of relatedOpenPRs) {
28
- await this.issueRepository.setDependedIssueUrl(
29
- pr.url,
30
- input.project,
31
- issue.url,
32
- );
28
+ try {
29
+ await this.issueRepository.setDependedIssueUrl(
30
+ pr.url,
31
+ input.project,
32
+ issue.url,
33
+ );
34
+ } catch (error) {
35
+ console.warn(
36
+ `Failed to set depended issue URL for PR ${pr.url}, skipping and continuing with remaining PRs: ${error instanceof Error ? error.message : String(error)}`,
37
+ );
38
+ }
33
39
  }
34
40
  }
35
41
  };