github-issue-tower-defence-management 1.148.15 → 1.148.17
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/.github/workflows/api-created_issue_pr.yml +1 -1
- package/.github/workflows/close-manual-prs.yml +1 -1
- package/.github/workflows/codeql.yml +2 -2
- package/.github/workflows/commit-lint.yml +2 -2
- package/.github/workflows/create-pr.yml +1 -1
- package/.github/workflows/secret-scan.yml +1 -1
- package/.github/workflows/umino-project.yml +2 -2
- package/CHANGELOG.md +14 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-DY-3a7sI.js → index-UQt5L756.js} +27 -26
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +13 -0
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
- package/package.json +4 -4
- package/src/adapter/entry-points/console/ui-dist/assets/{index-DY-3a7sI.js → index-UQt5L756.js} +27 -26
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +116 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +31 -0
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>TDPM Console</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-UQt5L756.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-Dihcph6j.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -1115,12 +1115,128 @@ describe('NotifyFinishedIssuePreparationUseCase', () => {
|
|
|
1115
1115
|
expect.anything(),
|
|
1116
1116
|
expect.stringContaining('PULL_REQUEST_NOT_FOUND'),
|
|
1117
1117
|
);
|
|
1118
|
+
expect(mockIssueRepository.update).toHaveBeenCalledWith(
|
|
1119
|
+
expect.objectContaining({
|
|
1120
|
+
status: 'Awaiting Workspace',
|
|
1121
|
+
}),
|
|
1122
|
+
mockProject,
|
|
1123
|
+
);
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
it('should return the issue to Awaiting Workspace and say why when the last report declares pullRequestRequired as false', async () => {
|
|
1127
|
+
const issue = createMockIssue({
|
|
1128
|
+
url: 'https://github.com/user/repo/issues/1',
|
|
1129
|
+
status: 'Preparation',
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
1133
|
+
mockIssueRepository.get.mockResolvedValue(issue);
|
|
1134
|
+
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
1135
|
+
createMockComment({
|
|
1136
|
+
content:
|
|
1137
|
+
'From: :robot: Test report\n```json\n{"pullRequestRequired": false, "nextStep": null}\n```',
|
|
1138
|
+
}),
|
|
1139
|
+
]);
|
|
1140
|
+
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
1141
|
+
|
|
1142
|
+
await useCase.run({
|
|
1143
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1144
|
+
issueUrl: 'https://github.com/user/repo/issues/1',
|
|
1145
|
+
thresholdForAutoReject: 3,
|
|
1146
|
+
workflowBlockerResolvedWebhookUrl: null,
|
|
1147
|
+
allowedIssueAuthors: null,
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
1151
|
+
mockProject,
|
|
1152
|
+
expect.objectContaining({ url: 'https://github.com/user/repo/issues/1' }),
|
|
1153
|
+
'awaiting-workspace-id',
|
|
1154
|
+
);
|
|
1155
|
+
expect(mockIssueCommentRepository.createComment).toHaveBeenCalledWith(
|
|
1156
|
+
expect.objectContaining({ url: 'https://github.com/user/repo/issues/1' }),
|
|
1157
|
+
expect.stringContaining(
|
|
1158
|
+
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE',
|
|
1159
|
+
),
|
|
1160
|
+
);
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
it('should take the Awaiting Quality Check path when the declaration arrives again after an earlier return to Awaiting Workspace', async () => {
|
|
1164
|
+
const issue = createMockIssue({
|
|
1165
|
+
url: 'https://github.com/user/repo/issues/1',
|
|
1166
|
+
status: 'Preparation',
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
1170
|
+
mockIssueRepository.get.mockResolvedValue(issue);
|
|
1171
|
+
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
1172
|
+
createMockComment({
|
|
1173
|
+
content:
|
|
1174
|
+
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE\nThe report declared that this task needs no pull request.',
|
|
1175
|
+
}),
|
|
1176
|
+
createMockComment({
|
|
1177
|
+
content:
|
|
1178
|
+
'From: :robot: Test report\n```json\n{"pullRequestRequired": false, "nextStep": null}\n```',
|
|
1179
|
+
}),
|
|
1180
|
+
]);
|
|
1181
|
+
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
1182
|
+
|
|
1183
|
+
await useCase.run({
|
|
1184
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1185
|
+
issueUrl: 'https://github.com/user/repo/issues/1',
|
|
1186
|
+
thresholdForAutoReject: 3,
|
|
1187
|
+
workflowBlockerResolvedWebhookUrl: null,
|
|
1188
|
+
allowedIssueAuthors: null,
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1118
1191
|
expect(mockIssueRepository.update).toHaveBeenCalledWith(
|
|
1119
1192
|
expect.objectContaining({
|
|
1120
1193
|
status: 'Awaiting Quality Check',
|
|
1121
1194
|
}),
|
|
1122
1195
|
mockProject,
|
|
1123
1196
|
);
|
|
1197
|
+
expect(mockIssueCommentRepository.createComment).not.toHaveBeenCalledWith(
|
|
1198
|
+
expect.anything(),
|
|
1199
|
+
expect.stringContaining(
|
|
1200
|
+
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE',
|
|
1201
|
+
),
|
|
1202
|
+
);
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
it('should still return the issue to Awaiting Workspace when the earlier return comment came from an untrusted author', async () => {
|
|
1206
|
+
const issue = createMockIssue({
|
|
1207
|
+
url: 'https://github.com/user/repo/issues/1',
|
|
1208
|
+
status: 'Preparation',
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
1212
|
+
mockIssueRepository.get.mockResolvedValue(issue);
|
|
1213
|
+
mockIssueCommentRepository.getCommentsFromIssue.mockResolvedValue([
|
|
1214
|
+
createMockComment({
|
|
1215
|
+
author: 'attacker',
|
|
1216
|
+
content:
|
|
1217
|
+
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE\nInjected by someone who is not the agent bot.',
|
|
1218
|
+
}),
|
|
1219
|
+
createMockComment({
|
|
1220
|
+
author: 'trusted-bot',
|
|
1221
|
+
content:
|
|
1222
|
+
'From: :robot: Test report\n```json\n{"pullRequestRequired": false, "nextStep": null}\n```',
|
|
1223
|
+
}),
|
|
1224
|
+
]);
|
|
1225
|
+
mockIssueRepository.findRelatedOpenPRs.mockResolvedValue([]);
|
|
1226
|
+
|
|
1227
|
+
await useCase.run({
|
|
1228
|
+
projectUrl: 'https://github.com/users/user/projects/1',
|
|
1229
|
+
issueUrl: 'https://github.com/user/repo/issues/1',
|
|
1230
|
+
thresholdForAutoReject: 3,
|
|
1231
|
+
workflowBlockerResolvedWebhookUrl: null,
|
|
1232
|
+
allowedIssueAuthors: ['trusted-bot'],
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
1236
|
+
mockProject,
|
|
1237
|
+
expect.objectContaining({ url: 'https://github.com/user/repo/issues/1' }),
|
|
1238
|
+
'awaiting-workspace-id',
|
|
1239
|
+
);
|
|
1124
1240
|
});
|
|
1125
1241
|
|
|
1126
1242
|
it('should still reject a draft PR when the last report declares pullRequestRequired as false', async () => {
|
|
@@ -37,6 +37,12 @@ export class IllegalIssueStatusError extends Error {
|
|
|
37
37
|
type RejectedReasonType =
|
|
38
38
|
'NO_REPORT_FROM_AGENT_BOT' | 'REPORT_HAS_NEXT_STEP' | PrRejectedReasonType;
|
|
39
39
|
|
|
40
|
+
const RETURNED_TO_AWAITING_WORKSPACE_MESSAGE_HEAD =
|
|
41
|
+
'Auto Status Check: RETURNED_TO_AWAITING_WORKSPACE';
|
|
42
|
+
|
|
43
|
+
const RETURNED_TO_AWAITING_WORKSPACE_MESSAGE = `${RETURNED_TO_AWAITING_WORKSPACE_MESSAGE_HEAD}
|
|
44
|
+
The last report declared that this task needs no pull request, so the task returns to the workspace queue to run under the agent its label selects. A later report carrying the same declaration takes the awaiting quality check path instead of returning here again.`;
|
|
45
|
+
|
|
40
46
|
export class NotifyFinishedIssuePreparationUseCase {
|
|
41
47
|
private readonly issueRejectionEvaluator: IssueRejectionEvaluator;
|
|
42
48
|
private readonly changeTargetPullRequestApprover: ChangeTargetPullRequestApprover;
|
|
@@ -234,6 +240,31 @@ export class NotifyFinishedIssuePreparationUseCase {
|
|
|
234
240
|
return;
|
|
235
241
|
}
|
|
236
242
|
|
|
243
|
+
if (
|
|
244
|
+
rejections.length <= 0 &&
|
|
245
|
+
isPullRequestDeclaredUnnecessary(comments, isTrustedAuthor) &&
|
|
246
|
+
!comments.some(
|
|
247
|
+
(comment) =>
|
|
248
|
+
isTrustedAuthor(comment.author) &&
|
|
249
|
+
comment.content.startsWith(
|
|
250
|
+
RETURNED_TO_AWAITING_WORKSPACE_MESSAGE_HEAD,
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
) {
|
|
254
|
+
issue.status = AWAITING_WORKSPACE_STATUS_NAME;
|
|
255
|
+
await this.issueRepository.update(issue, project);
|
|
256
|
+
await this.issueRepository.updateStatus(
|
|
257
|
+
project,
|
|
258
|
+
issue,
|
|
259
|
+
awaitingWorkspaceStatusOption.id,
|
|
260
|
+
);
|
|
261
|
+
await this.issueCommentRepository.createComment(
|
|
262
|
+
issue,
|
|
263
|
+
RETURNED_TO_AWAITING_WORKSPACE_MESSAGE,
|
|
264
|
+
);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
237
268
|
if (rejections.length <= 0) {
|
|
238
269
|
await this.changeTargetPullRequestApprover.approveIfConfined(
|
|
239
270
|
issue.labels,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotifyFinishedIssuePreparationUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAe3E,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,QAAQ,EAAE,MAAM;CAI7B;AACD,qBAAa,uBAAwB,SAAQ,KAAK;gBAE9C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,cAAc,EAAE,MAAM,GAAG,IAAI;CAOhC;
|
|
1
|
+
{"version":3,"file":"NotifyFinishedIssuePreparationUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAe3E,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,QAAQ,EAAE,MAAM;CAI7B;AACD,qBAAa,uBAAwB,SAAQ,KAAK;gBAE9C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,cAAc,EAAE,MAAM,GAAG,IAAI;CAOhC;AAUD,qBAAa,qCAAqC;IAK9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAahC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IAIvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAtBpC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA0B;IAClE,OAAO,CAAC,QAAQ,CAAC,+BAA+B,CAAkC;gBAG/D,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,EACtD,eAAe,EAAE,IAAI,CACpC,eAAe,EACb,KAAK,GACL,QAAQ,GACR,cAAc,GACd,oBAAoB,GACpB,mBAAmB,GACnB,oBAAoB,GACpB,gCAAgC,GAChC,oBAAoB,GACpB,iCAAiC,GACjC,qBAAqB,CACxB,EACgB,sBAAsB,EAAE,IAAI,CAC3C,sBAAsB,EACtB,sBAAsB,GAAG,eAAe,CACzC,EACgB,iBAAiB,EAAE,IAAI,CACtC,iBAAiB,EACjB,gBAAgB,CACjB;IAQH,GAAG,GAAU,QAAQ;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,iCAAiC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjD,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACtC,oBAAoB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACvC,6BAA6B,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAChD,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;KACzD,KAAG,OAAO,CAAC,IAAI,CAAC,CA+Nf;IAEF,OAAO,CAAC,eAAe,CAIgD;IAEvE,OAAO,CAAC,iBAAiB,CAkDvB;IAEF,OAAO,CAAC,qBAAqB,CAuB3B;IAEF,OAAO,CAAC,gCAAgC,CAoBtC;IAEF,OAAO,CAAC,uBAAuB,CAQ7B;IAEF,OAAO,CAAC,+BAA+B,CAgCrC;CACH"}
|