github-issue-tower-defence-management 1.53.0 → 1.54.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/CHANGELOG.md +7 -0
- package/README.md +2 -2
- package/bin/domain/entities/WorkflowStatus.js +5 -7
- package/bin/domain/entities/WorkflowStatus.js.map +1 -1
- package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js +19 -3
- package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/domain/entities/WorkflowStatus.ts +5 -6
- package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +177 -9
- package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +34 -2
- package/types/domain/entities/WorkflowStatus.d.ts +4 -2
- package/types/domain/entities/WorkflowStatus.d.ts.map +1 -1
- package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts +2 -0
- package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [1.54.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.53.0...v1.54.0) (2026-05-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **core:** change status ([b027776](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/b0277767b231cca3f5910998e63b1b2fc1166e0e))
|
|
7
|
+
|
|
1
8
|
# [1.53.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.52.1...v1.53.0) (2026-05-23)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ npx github-issue-tower-defence-management notifyFinishedIssuePreparation --confi
|
|
|
73
73
|
|
|
74
74
|
The `config.yaml` for the `schedule` command must match the input type of `HandleScheduledEventUseCase.run()`. Below is the structure:
|
|
75
75
|
|
|
76
|
-
Workflow status names (`Unread`, `Awaiting Task Breakdown`, `Awaiting Workspace`, `Preparation`, `Failed Preparation`, `Awaiting Quality Check`, `Todo
|
|
76
|
+
Workflow status names (`Unread`, `Awaiting Task Breakdown`, `Awaiting Workspace`, `Preparation`, `Failed Preparation`, `Awaiting Quality Check`, `Todo by human`, `In Tmux by human`, `Done`, `Icebox`) are fixed code constants and cannot be overridden from CLI options, config files, or project README. The `schedule` command automatically creates any missing required statuses on the target project on each run via `SetupTowerDefenceProjectUseCase`. Projects with the legacy `Todo` and `In Tmux` status names are automatically migrated to `Todo by human` and `In Tmux by human` respectively by reusing the existing option IDs so that task associations are preserved. The legacy `PC Todo` status is removed from the required list and excluded from the project status list on the next setup run.
|
|
77
77
|
|
|
78
78
|
```yaml
|
|
79
79
|
disabled: boolean # When true, skip all processing and return null
|
|
@@ -168,7 +168,7 @@ awLogDirectoryPath?: string # Optional: Directory path where aw log files named
|
|
|
168
168
|
awLogStaleThresholdMinutes?: number # Optional: Minutes since last aw log mtime after which a Preparation issue is considered orphaned even when pgrep still returns 0. Requires awLogDirectoryPath
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
Workflow status names (`Unread`, `Awaiting Task Breakdown`, `Awaiting Workspace`, `Preparation`, `Failed Preparation`, `Awaiting Quality Check`, `Todo
|
|
171
|
+
Workflow status names (`Unread`, `Awaiting Task Breakdown`, `Awaiting Workspace`, `Preparation`, `Failed Preparation`, `Awaiting Quality Check`, `Todo by human`, `In Tmux by human`, `Done`, `Icebox`) are hardcoded constants and are not accepted via this config, the CLI, or the project README. To ensure they exist on the target project, run the `schedule` command — it invokes `SetupTowerDefenceProjectUseCase` automatically. Projects with the legacy `Todo` and `In Tmux` status names are automatically migrated; `PC Todo` is removed on the next setup run.
|
|
172
172
|
|
|
173
173
|
Example:
|
|
174
174
|
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.REQUIRED_WORKFLOW_STATUSES = exports.ICEBOX_STATUS_NAME = exports.DONE_STATUS_NAME = exports.IN_TMUX_STATUS_NAME = exports.PC_TODO_STATUS_NAME = exports.TODO_STATUS_NAME = exports.AWAITING_QUALITY_CHECK_STATUS_NAME = exports.FAILED_PREPARATION_STATUS_NAME = exports.PREPARATION_STATUS_NAME = exports.AWAITING_WORKSPACE_STATUS_NAME = exports.AWAITING_TASK_BREAKDOWN_STATUS_NAME = exports.DEFAULT_STATUS_NAME = void 0;
|
|
3
|
+
exports.REQUIRED_WORKFLOW_STATUSES = exports.LEGACY_IN_TMUX_STATUS_NAME = exports.LEGACY_TODO_STATUS_NAME = exports.ICEBOX_STATUS_NAME = exports.DONE_STATUS_NAME = exports.IN_TMUX_STATUS_NAME = exports.PC_TODO_STATUS_NAME = exports.TODO_STATUS_NAME = exports.AWAITING_QUALITY_CHECK_STATUS_NAME = exports.FAILED_PREPARATION_STATUS_NAME = exports.PREPARATION_STATUS_NAME = exports.AWAITING_WORKSPACE_STATUS_NAME = exports.AWAITING_TASK_BREAKDOWN_STATUS_NAME = exports.DEFAULT_STATUS_NAME = void 0;
|
|
4
4
|
exports.DEFAULT_STATUS_NAME = 'Unread';
|
|
5
5
|
exports.AWAITING_TASK_BREAKDOWN_STATUS_NAME = 'Awaiting Task Breakdown';
|
|
6
6
|
exports.AWAITING_WORKSPACE_STATUS_NAME = 'Awaiting Workspace';
|
|
7
7
|
exports.PREPARATION_STATUS_NAME = 'Preparation';
|
|
8
8
|
exports.FAILED_PREPARATION_STATUS_NAME = 'Failed Preparation';
|
|
9
9
|
exports.AWAITING_QUALITY_CHECK_STATUS_NAME = 'Awaiting Quality Check';
|
|
10
|
-
exports.TODO_STATUS_NAME = 'Todo';
|
|
10
|
+
exports.TODO_STATUS_NAME = 'Todo by human';
|
|
11
11
|
exports.PC_TODO_STATUS_NAME = 'PC Todo';
|
|
12
|
-
exports.IN_TMUX_STATUS_NAME = 'In Tmux';
|
|
12
|
+
exports.IN_TMUX_STATUS_NAME = 'In Tmux by human';
|
|
13
13
|
exports.DONE_STATUS_NAME = 'Done';
|
|
14
14
|
exports.ICEBOX_STATUS_NAME = 'Icebox';
|
|
15
|
+
exports.LEGACY_TODO_STATUS_NAME = 'Todo';
|
|
16
|
+
exports.LEGACY_IN_TMUX_STATUS_NAME = 'In Tmux';
|
|
15
17
|
exports.REQUIRED_WORKFLOW_STATUSES = [
|
|
16
18
|
{
|
|
17
19
|
name: exports.DEFAULT_STATUS_NAME,
|
|
@@ -41,10 +43,6 @@ exports.REQUIRED_WORKFLOW_STATUSES = [
|
|
|
41
43
|
name: exports.TODO_STATUS_NAME,
|
|
42
44
|
color: 'PINK',
|
|
43
45
|
},
|
|
44
|
-
{
|
|
45
|
-
name: exports.PC_TODO_STATUS_NAME,
|
|
46
|
-
color: 'PINK',
|
|
47
|
-
},
|
|
48
46
|
{
|
|
49
47
|
name: exports.IN_TMUX_STATUS_NAME,
|
|
50
48
|
color: 'RED',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkflowStatus.js","sourceRoot":"","sources":["../../../src/domain/entities/WorkflowStatus.ts"],"names":[],"mappings":";;;AAEa,QAAA,mBAAmB,GAAG,QAAQ,CAAC;AAC/B,QAAA,mCAAmC,GAAG,yBAAyB,CAAC;AAChE,QAAA,8BAA8B,GAAG,oBAAoB,CAAC;AACtD,QAAA,uBAAuB,GAAG,aAAa,CAAC;AACxC,QAAA,8BAA8B,GAAG,oBAAoB,CAAC;AACtD,QAAA,kCAAkC,GAAG,wBAAwB,CAAC;AAC9D,QAAA,gBAAgB,GAAG,
|
|
1
|
+
{"version":3,"file":"WorkflowStatus.js","sourceRoot":"","sources":["../../../src/domain/entities/WorkflowStatus.ts"],"names":[],"mappings":";;;AAEa,QAAA,mBAAmB,GAAG,QAAQ,CAAC;AAC/B,QAAA,mCAAmC,GAAG,yBAAyB,CAAC;AAChE,QAAA,8BAA8B,GAAG,oBAAoB,CAAC;AACtD,QAAA,uBAAuB,GAAG,aAAa,CAAC;AACxC,QAAA,8BAA8B,GAAG,oBAAoB,CAAC;AACtD,QAAA,kCAAkC,GAAG,wBAAwB,CAAC;AAC9D,QAAA,gBAAgB,GAAG,eAAe,CAAC;AACnC,QAAA,mBAAmB,GAAG,SAAS,CAAC;AAChC,QAAA,mBAAmB,GAAG,kBAAkB,CAAC;AACzC,QAAA,gBAAgB,GAAG,MAAM,CAAC;AAC1B,QAAA,kBAAkB,GAAG,QAAQ,CAAC;AAE9B,QAAA,uBAAuB,GAAG,MAAM,CAAC;AACjC,QAAA,0BAA0B,GAAG,SAAS,CAAC;AAOvC,QAAA,0BAA0B,GAA+B;IACpE;QACE,IAAI,EAAE,2BAAmB;QACzB,KAAK,EAAE,QAAQ;KAChB;IACD;QACE,IAAI,EAAE,2CAAmC;QACzC,KAAK,EAAE,QAAQ;KAChB;IACD;QACE,IAAI,EAAE,sCAA8B;QACpC,KAAK,EAAE,MAAM;KACd;IACD;QACE,IAAI,EAAE,+BAAuB;QAC7B,KAAK,EAAE,QAAQ;KAChB;IACD;QACE,IAAI,EAAE,sCAA8B;QACpC,KAAK,EAAE,KAAK;KACb;IACD;QACE,IAAI,EAAE,0CAAkC;QACxC,KAAK,EAAE,OAAO;KACf;IACD;QACE,IAAI,EAAE,wBAAgB;QACtB,KAAK,EAAE,MAAM;KACd;IACD;QACE,IAAI,EAAE,2BAAmB;QACzB,KAAK,EAAE,KAAK;KACb;IACD;QACE,IAAI,EAAE,wBAAgB;QACtB,KAAK,EAAE,QAAQ;KAChB;IACD;QACE,IAAI,EAAE,0BAAkB;QACxB,KAAK,EAAE,MAAM;KACd;CACF,CAAC"}
|
|
@@ -8,14 +8,21 @@ class SetupTowerDefenceProjectUseCase {
|
|
|
8
8
|
this.run = async (params) => {
|
|
9
9
|
const project = await this.projectRepository.getByUrl(params.projectUrl);
|
|
10
10
|
const existing = project.status.statuses;
|
|
11
|
-
|
|
11
|
+
const hasMigratedFromName = existing.some((s) => SetupTowerDefenceProjectUseCase.MIGRATED_FROM_NAMES.has(s.name));
|
|
12
|
+
if (!hasMigratedFromName &&
|
|
13
|
+
SetupTowerDefenceProjectUseCase.hasRequiredStatusesInCanonicalOrder(existing)) {
|
|
12
14
|
return;
|
|
13
15
|
}
|
|
14
16
|
const requiredNames = new Set(WorkflowStatus_1.REQUIRED_WORKFLOW_STATUSES.map((s) => s.name));
|
|
15
|
-
const others = existing.filter((status) => !requiredNames.has(status.name)
|
|
17
|
+
const others = existing.filter((status) => !requiredNames.has(status.name) &&
|
|
18
|
+
!SetupTowerDefenceProjectUseCase.MIGRATED_FROM_NAMES.has(status.name));
|
|
16
19
|
const newStatusList = [
|
|
17
20
|
...WorkflowStatus_1.REQUIRED_WORKFLOW_STATUSES.map((required) => {
|
|
18
|
-
const
|
|
21
|
+
const legacyName = SetupTowerDefenceProjectUseCase.LEGACY_STATUS_NAMES[required.name];
|
|
22
|
+
const found = existing.find((status) => status.name === required.name) ??
|
|
23
|
+
(legacyName !== undefined
|
|
24
|
+
? existing.find((status) => status.name === legacyName)
|
|
25
|
+
: undefined);
|
|
19
26
|
return {
|
|
20
27
|
id: found ? found.id : null,
|
|
21
28
|
name: required.name,
|
|
@@ -35,6 +42,15 @@ class SetupTowerDefenceProjectUseCase {
|
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
44
|
exports.SetupTowerDefenceProjectUseCase = SetupTowerDefenceProjectUseCase;
|
|
45
|
+
SetupTowerDefenceProjectUseCase.LEGACY_STATUS_NAMES = {
|
|
46
|
+
[WorkflowStatus_1.TODO_STATUS_NAME]: WorkflowStatus_1.LEGACY_TODO_STATUS_NAME,
|
|
47
|
+
[WorkflowStatus_1.IN_TMUX_STATUS_NAME]: WorkflowStatus_1.LEGACY_IN_TMUX_STATUS_NAME,
|
|
48
|
+
};
|
|
49
|
+
SetupTowerDefenceProjectUseCase.MIGRATED_FROM_NAMES = new Set([
|
|
50
|
+
WorkflowStatus_1.LEGACY_TODO_STATUS_NAME,
|
|
51
|
+
WorkflowStatus_1.LEGACY_IN_TMUX_STATUS_NAME,
|
|
52
|
+
WorkflowStatus_1.PC_TODO_STATUS_NAME,
|
|
53
|
+
]);
|
|
38
54
|
SetupTowerDefenceProjectUseCase.hasRequiredStatusesInCanonicalOrder = (existing) => {
|
|
39
55
|
if (existing.length < WorkflowStatus_1.REQUIRED_WORKFLOW_STATUSES.length) {
|
|
40
56
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SetupTowerDefenceProjectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/SetupTowerDefenceProjectUseCase.ts"],"names":[],"mappings":";;;AAEA,+
|
|
1
|
+
{"version":3,"file":"SetupTowerDefenceProjectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/SetupTowerDefenceProjectUseCase.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -6,12 +6,15 @@ export const AWAITING_WORKSPACE_STATUS_NAME = 'Awaiting Workspace';
|
|
|
6
6
|
export const PREPARATION_STATUS_NAME = 'Preparation';
|
|
7
7
|
export const FAILED_PREPARATION_STATUS_NAME = 'Failed Preparation';
|
|
8
8
|
export const AWAITING_QUALITY_CHECK_STATUS_NAME = 'Awaiting Quality Check';
|
|
9
|
-
export const TODO_STATUS_NAME = 'Todo';
|
|
9
|
+
export const TODO_STATUS_NAME = 'Todo by human';
|
|
10
10
|
export const PC_TODO_STATUS_NAME = 'PC Todo';
|
|
11
|
-
export const IN_TMUX_STATUS_NAME = 'In Tmux';
|
|
11
|
+
export const IN_TMUX_STATUS_NAME = 'In Tmux by human';
|
|
12
12
|
export const DONE_STATUS_NAME = 'Done';
|
|
13
13
|
export const ICEBOX_STATUS_NAME = 'Icebox';
|
|
14
14
|
|
|
15
|
+
export const LEGACY_TODO_STATUS_NAME = 'Todo';
|
|
16
|
+
export const LEGACY_IN_TMUX_STATUS_NAME = 'In Tmux';
|
|
17
|
+
|
|
15
18
|
export type WorkflowStatusDefinition = {
|
|
16
19
|
name: string;
|
|
17
20
|
color: FieldOption['color'];
|
|
@@ -46,10 +49,6 @@ export const REQUIRED_WORKFLOW_STATUSES: WorkflowStatusDefinition[] = [
|
|
|
46
49
|
name: TODO_STATUS_NAME,
|
|
47
50
|
color: 'PINK',
|
|
48
51
|
},
|
|
49
|
-
{
|
|
50
|
-
name: PC_TODO_STATUS_NAME,
|
|
51
|
-
color: 'PINK',
|
|
52
|
-
},
|
|
53
52
|
{
|
|
54
53
|
name: IN_TMUX_STATUS_NAME,
|
|
55
54
|
color: 'RED',
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
FAILED_PREPARATION_STATUS_NAME,
|
|
12
12
|
ICEBOX_STATUS_NAME,
|
|
13
13
|
IN_TMUX_STATUS_NAME,
|
|
14
|
+
LEGACY_IN_TMUX_STATUS_NAME,
|
|
15
|
+
LEGACY_TODO_STATUS_NAME,
|
|
14
16
|
PC_TODO_STATUS_NAME,
|
|
15
17
|
PREPARATION_STATUS_NAME,
|
|
16
18
|
REQUIRED_WORKFLOW_STATUSES,
|
|
@@ -44,7 +46,7 @@ const buildCanonicalStatuses = (): FieldOption[] =>
|
|
|
44
46
|
}));
|
|
45
47
|
|
|
46
48
|
describe('SetupTowerDefenceProjectUseCase', () => {
|
|
47
|
-
it('should define exactly the
|
|
49
|
+
it('should define exactly the 10 required statuses in the documented order with the documented colors and no descriptions', () => {
|
|
48
50
|
expect(REQUIRED_WORKFLOW_STATUSES).toEqual([
|
|
49
51
|
{ name: DEFAULT_STATUS_NAME, color: 'ORANGE' },
|
|
50
52
|
{ name: AWAITING_TASK_BREAKDOWN_STATUS_NAME, color: 'ORANGE' },
|
|
@@ -53,7 +55,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
53
55
|
{ name: FAILED_PREPARATION_STATUS_NAME, color: 'RED' },
|
|
54
56
|
{ name: AWAITING_QUALITY_CHECK_STATUS_NAME, color: 'GREEN' },
|
|
55
57
|
{ name: TODO_STATUS_NAME, color: 'PINK' },
|
|
56
|
-
{ name: PC_TODO_STATUS_NAME, color: 'PINK' },
|
|
57
58
|
{ name: IN_TMUX_STATUS_NAME, color: 'RED' },
|
|
58
59
|
{ name: DONE_STATUS_NAME, color: 'PURPLE' },
|
|
59
60
|
{ name: ICEBOX_STATUS_NAME, color: 'GRAY' },
|
|
@@ -187,12 +188,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
187
188
|
color: 'PINK',
|
|
188
189
|
description: '',
|
|
189
190
|
},
|
|
190
|
-
{
|
|
191
|
-
id: null,
|
|
192
|
-
name: PC_TODO_STATUS_NAME,
|
|
193
|
-
color: 'PINK',
|
|
194
|
-
description: '',
|
|
195
|
-
},
|
|
196
191
|
{
|
|
197
192
|
id: null,
|
|
198
193
|
name: IN_TMUX_STATUS_NAME,
|
|
@@ -249,7 +244,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
249
244
|
FAILED_PREPARATION_STATUS_NAME,
|
|
250
245
|
AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
251
246
|
TODO_STATUS_NAME,
|
|
252
|
-
PC_TODO_STATUS_NAME,
|
|
253
247
|
IN_TMUX_STATUS_NAME,
|
|
254
248
|
DONE_STATUS_NAME,
|
|
255
249
|
ICEBOX_STATUS_NAME,
|
|
@@ -272,4 +266,178 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
272
266
|
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
273
267
|
expect(payload[2].color).toBe(REQUIRED_WORKFLOW_STATUSES[2].color);
|
|
274
268
|
});
|
|
269
|
+
|
|
270
|
+
it('should rename legacy "Todo" to "Todo by human" by reusing the existing option ID', async () => {
|
|
271
|
+
const mockProjectRepository =
|
|
272
|
+
mock<Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>>();
|
|
273
|
+
const statuses: FieldOption[] = REQUIRED_WORKFLOW_STATUSES.map(
|
|
274
|
+
(required, index) => ({
|
|
275
|
+
id: `id-${index}`,
|
|
276
|
+
name:
|
|
277
|
+
required.name === TODO_STATUS_NAME
|
|
278
|
+
? LEGACY_TODO_STATUS_NAME
|
|
279
|
+
: required.name,
|
|
280
|
+
color: required.color,
|
|
281
|
+
description: '',
|
|
282
|
+
}),
|
|
283
|
+
);
|
|
284
|
+
const project = buildProject(statuses);
|
|
285
|
+
mockProjectRepository.getByUrl.mockResolvedValue(project);
|
|
286
|
+
mockProjectRepository.updateStatusList.mockResolvedValue([]);
|
|
287
|
+
|
|
288
|
+
const useCase = new SetupTowerDefenceProjectUseCase(mockProjectRepository);
|
|
289
|
+
await useCase.run({ projectUrl: project.url });
|
|
290
|
+
|
|
291
|
+
expect(mockProjectRepository.updateStatusList).toHaveBeenCalledTimes(1);
|
|
292
|
+
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
293
|
+
const todoEntry = payload.find((s) => s.name === TODO_STATUS_NAME);
|
|
294
|
+
expect(todoEntry).toBeDefined();
|
|
295
|
+
expect(todoEntry?.id).toBe('id-6');
|
|
296
|
+
expect(payload.some((s) => s.name === LEGACY_TODO_STATUS_NAME)).toBe(false);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should rename legacy "In Tmux" to "In Tmux by human" by reusing the existing option ID', async () => {
|
|
300
|
+
const mockProjectRepository =
|
|
301
|
+
mock<Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>>();
|
|
302
|
+
const statuses: FieldOption[] = REQUIRED_WORKFLOW_STATUSES.map(
|
|
303
|
+
(required, index) => ({
|
|
304
|
+
id: `id-${index}`,
|
|
305
|
+
name:
|
|
306
|
+
required.name === IN_TMUX_STATUS_NAME
|
|
307
|
+
? LEGACY_IN_TMUX_STATUS_NAME
|
|
308
|
+
: required.name,
|
|
309
|
+
color: required.color,
|
|
310
|
+
description: '',
|
|
311
|
+
}),
|
|
312
|
+
);
|
|
313
|
+
const project = buildProject(statuses);
|
|
314
|
+
mockProjectRepository.getByUrl.mockResolvedValue(project);
|
|
315
|
+
mockProjectRepository.updateStatusList.mockResolvedValue([]);
|
|
316
|
+
|
|
317
|
+
const useCase = new SetupTowerDefenceProjectUseCase(mockProjectRepository);
|
|
318
|
+
await useCase.run({ projectUrl: project.url });
|
|
319
|
+
|
|
320
|
+
expect(mockProjectRepository.updateStatusList).toHaveBeenCalledTimes(1);
|
|
321
|
+
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
322
|
+
const inTmuxEntry = payload.find((s) => s.name === IN_TMUX_STATUS_NAME);
|
|
323
|
+
expect(inTmuxEntry).toBeDefined();
|
|
324
|
+
expect(inTmuxEntry?.id).toBe('id-7');
|
|
325
|
+
expect(payload.some((s) => s.name === LEGACY_IN_TMUX_STATUS_NAME)).toBe(
|
|
326
|
+
false,
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should remove "PC Todo" from the status list and not include it in others', async () => {
|
|
331
|
+
const mockProjectRepository =
|
|
332
|
+
mock<Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>>();
|
|
333
|
+
const statuses: FieldOption[] = [
|
|
334
|
+
...buildCanonicalStatuses(),
|
|
335
|
+
{
|
|
336
|
+
id: 'pc-todo-id',
|
|
337
|
+
name: PC_TODO_STATUS_NAME,
|
|
338
|
+
color: 'PINK',
|
|
339
|
+
description: '',
|
|
340
|
+
},
|
|
341
|
+
];
|
|
342
|
+
const project = buildProject(statuses);
|
|
343
|
+
mockProjectRepository.getByUrl.mockResolvedValue(project);
|
|
344
|
+
mockProjectRepository.updateStatusList.mockResolvedValue([]);
|
|
345
|
+
|
|
346
|
+
const useCase = new SetupTowerDefenceProjectUseCase(mockProjectRepository);
|
|
347
|
+
await useCase.run({ projectUrl: project.url });
|
|
348
|
+
|
|
349
|
+
expect(mockProjectRepository.updateStatusList).toHaveBeenCalledTimes(1);
|
|
350
|
+
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
351
|
+
expect(payload.some((s) => s.name === PC_TODO_STATUS_NAME)).toBe(false);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should migrate a project with legacy statuses: rename Todo and In Tmux by ID, remove PC Todo', async () => {
|
|
355
|
+
const mockProjectRepository =
|
|
356
|
+
mock<Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>>();
|
|
357
|
+
const statuses: FieldOption[] = [
|
|
358
|
+
{
|
|
359
|
+
id: 'id-0',
|
|
360
|
+
name: DEFAULT_STATUS_NAME,
|
|
361
|
+
color: 'ORANGE',
|
|
362
|
+
description: '',
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
id: 'id-1',
|
|
366
|
+
name: AWAITING_TASK_BREAKDOWN_STATUS_NAME,
|
|
367
|
+
color: 'ORANGE',
|
|
368
|
+
description: '',
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
id: 'id-2',
|
|
372
|
+
name: AWAITING_WORKSPACE_STATUS_NAME,
|
|
373
|
+
color: 'BLUE',
|
|
374
|
+
description: '',
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
id: 'id-3',
|
|
378
|
+
name: PREPARATION_STATUS_NAME,
|
|
379
|
+
color: 'YELLOW',
|
|
380
|
+
description: '',
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
id: 'id-4',
|
|
384
|
+
name: FAILED_PREPARATION_STATUS_NAME,
|
|
385
|
+
color: 'RED',
|
|
386
|
+
description: '',
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
id: 'id-5',
|
|
390
|
+
name: AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
391
|
+
color: 'GREEN',
|
|
392
|
+
description: '',
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: 'id-6',
|
|
396
|
+
name: LEGACY_TODO_STATUS_NAME,
|
|
397
|
+
color: 'PINK',
|
|
398
|
+
description: '',
|
|
399
|
+
},
|
|
400
|
+
{ id: 'id-7', name: PC_TODO_STATUS_NAME, color: 'PINK', description: '' },
|
|
401
|
+
{
|
|
402
|
+
id: 'id-8',
|
|
403
|
+
name: LEGACY_IN_TMUX_STATUS_NAME,
|
|
404
|
+
color: 'RED',
|
|
405
|
+
description: '',
|
|
406
|
+
},
|
|
407
|
+
{ id: 'id-9', name: DONE_STATUS_NAME, color: 'PURPLE', description: '' },
|
|
408
|
+
{ id: 'id-10', name: ICEBOX_STATUS_NAME, color: 'GRAY', description: '' },
|
|
409
|
+
];
|
|
410
|
+
const project = buildProject(statuses);
|
|
411
|
+
mockProjectRepository.getByUrl.mockResolvedValue(project);
|
|
412
|
+
mockProjectRepository.updateStatusList.mockResolvedValue([]);
|
|
413
|
+
|
|
414
|
+
const useCase = new SetupTowerDefenceProjectUseCase(mockProjectRepository);
|
|
415
|
+
await useCase.run({ projectUrl: project.url });
|
|
416
|
+
|
|
417
|
+
expect(mockProjectRepository.updateStatusList).toHaveBeenCalledTimes(1);
|
|
418
|
+
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
419
|
+
|
|
420
|
+
expect(payload.map((s) => s.name)).toEqual([
|
|
421
|
+
DEFAULT_STATUS_NAME,
|
|
422
|
+
AWAITING_TASK_BREAKDOWN_STATUS_NAME,
|
|
423
|
+
AWAITING_WORKSPACE_STATUS_NAME,
|
|
424
|
+
PREPARATION_STATUS_NAME,
|
|
425
|
+
FAILED_PREPARATION_STATUS_NAME,
|
|
426
|
+
AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
427
|
+
TODO_STATUS_NAME,
|
|
428
|
+
IN_TMUX_STATUS_NAME,
|
|
429
|
+
DONE_STATUS_NAME,
|
|
430
|
+
ICEBOX_STATUS_NAME,
|
|
431
|
+
]);
|
|
432
|
+
|
|
433
|
+
expect(payload.find((s) => s.name === TODO_STATUS_NAME)?.id).toBe('id-6');
|
|
434
|
+
expect(payload.find((s) => s.name === IN_TMUX_STATUS_NAME)?.id).toBe(
|
|
435
|
+
'id-8',
|
|
436
|
+
);
|
|
437
|
+
expect(payload.some((s) => s.name === PC_TODO_STATUS_NAME)).toBe(false);
|
|
438
|
+
expect(payload.some((s) => s.name === LEGACY_TODO_STATUS_NAME)).toBe(false);
|
|
439
|
+
expect(payload.some((s) => s.name === LEGACY_IN_TMUX_STATUS_NAME)).toBe(
|
|
440
|
+
false,
|
|
441
|
+
);
|
|
442
|
+
});
|
|
275
443
|
});
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { FieldOption } from '../entities/Project';
|
|
2
2
|
import { ProjectRepository } from './adapter-interfaces/ProjectRepository';
|
|
3
3
|
import {
|
|
4
|
+
IN_TMUX_STATUS_NAME,
|
|
5
|
+
LEGACY_IN_TMUX_STATUS_NAME,
|
|
6
|
+
LEGACY_TODO_STATUS_NAME,
|
|
7
|
+
PC_TODO_STATUS_NAME,
|
|
4
8
|
REQUIRED_WORKFLOW_STATUSES,
|
|
9
|
+
TODO_STATUS_NAME,
|
|
5
10
|
WorkflowStatusDefinition,
|
|
6
11
|
} from '../entities/WorkflowStatus';
|
|
7
12
|
|
|
@@ -13,11 +18,28 @@ export class SetupTowerDefenceProjectUseCase {
|
|
|
13
18
|
>,
|
|
14
19
|
) {}
|
|
15
20
|
|
|
21
|
+
private static readonly LEGACY_STATUS_NAMES: Readonly<
|
|
22
|
+
Record<string, string>
|
|
23
|
+
> = {
|
|
24
|
+
[TODO_STATUS_NAME]: LEGACY_TODO_STATUS_NAME,
|
|
25
|
+
[IN_TMUX_STATUS_NAME]: LEGACY_IN_TMUX_STATUS_NAME,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
private static readonly MIGRATED_FROM_NAMES: ReadonlySet<string> = new Set([
|
|
29
|
+
LEGACY_TODO_STATUS_NAME,
|
|
30
|
+
LEGACY_IN_TMUX_STATUS_NAME,
|
|
31
|
+
PC_TODO_STATUS_NAME,
|
|
32
|
+
]);
|
|
33
|
+
|
|
16
34
|
run = async (params: { projectUrl: string }): Promise<void> => {
|
|
17
35
|
const project = await this.projectRepository.getByUrl(params.projectUrl);
|
|
18
36
|
const existing = project.status.statuses;
|
|
19
37
|
|
|
38
|
+
const hasMigratedFromName = existing.some((s) =>
|
|
39
|
+
SetupTowerDefenceProjectUseCase.MIGRATED_FROM_NAMES.has(s.name),
|
|
40
|
+
);
|
|
20
41
|
if (
|
|
42
|
+
!hasMigratedFromName &&
|
|
21
43
|
SetupTowerDefenceProjectUseCase.hasRequiredStatusesInCanonicalOrder(
|
|
22
44
|
existing,
|
|
23
45
|
)
|
|
@@ -28,13 +50,23 @@ export class SetupTowerDefenceProjectUseCase {
|
|
|
28
50
|
const requiredNames = new Set(
|
|
29
51
|
REQUIRED_WORKFLOW_STATUSES.map((s) => s.name),
|
|
30
52
|
);
|
|
31
|
-
const others = existing.filter(
|
|
53
|
+
const others = existing.filter(
|
|
54
|
+
(status) =>
|
|
55
|
+
!requiredNames.has(status.name) &&
|
|
56
|
+
!SetupTowerDefenceProjectUseCase.MIGRATED_FROM_NAMES.has(status.name),
|
|
57
|
+
);
|
|
32
58
|
|
|
33
59
|
const newStatusList: (Omit<FieldOption, 'id'> & {
|
|
34
60
|
id: FieldOption['id'] | null;
|
|
35
61
|
})[] = [
|
|
36
62
|
...REQUIRED_WORKFLOW_STATUSES.map((required) => {
|
|
37
|
-
const
|
|
63
|
+
const legacyName =
|
|
64
|
+
SetupTowerDefenceProjectUseCase.LEGACY_STATUS_NAMES[required.name];
|
|
65
|
+
const found =
|
|
66
|
+
existing.find((status) => status.name === required.name) ??
|
|
67
|
+
(legacyName !== undefined
|
|
68
|
+
? existing.find((status) => status.name === legacyName)
|
|
69
|
+
: undefined);
|
|
38
70
|
return {
|
|
39
71
|
id: found ? found.id : null,
|
|
40
72
|
name: required.name,
|
|
@@ -5,11 +5,13 @@ export declare const AWAITING_WORKSPACE_STATUS_NAME = "Awaiting Workspace";
|
|
|
5
5
|
export declare const PREPARATION_STATUS_NAME = "Preparation";
|
|
6
6
|
export declare const FAILED_PREPARATION_STATUS_NAME = "Failed Preparation";
|
|
7
7
|
export declare const AWAITING_QUALITY_CHECK_STATUS_NAME = "Awaiting Quality Check";
|
|
8
|
-
export declare const TODO_STATUS_NAME = "Todo";
|
|
8
|
+
export declare const TODO_STATUS_NAME = "Todo by human";
|
|
9
9
|
export declare const PC_TODO_STATUS_NAME = "PC Todo";
|
|
10
|
-
export declare const IN_TMUX_STATUS_NAME = "In Tmux";
|
|
10
|
+
export declare const IN_TMUX_STATUS_NAME = "In Tmux by human";
|
|
11
11
|
export declare const DONE_STATUS_NAME = "Done";
|
|
12
12
|
export declare const ICEBOX_STATUS_NAME = "Icebox";
|
|
13
|
+
export declare const LEGACY_TODO_STATUS_NAME = "Todo";
|
|
14
|
+
export declare const LEGACY_IN_TMUX_STATUS_NAME = "In Tmux";
|
|
13
15
|
export type WorkflowStatusDefinition = {
|
|
14
16
|
name: string;
|
|
15
17
|
color: FieldOption['color'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkflowStatus.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/WorkflowStatus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAC5C,eAAO,MAAM,mCAAmC,4BAA4B,CAAC;AAC7E,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AACrD,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,kCAAkC,2BAA2B,CAAC;AAC3E,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"WorkflowStatus.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/WorkflowStatus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAC5C,eAAO,MAAM,mCAAmC,4BAA4B,CAAC;AAC7E,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AACrD,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,kCAAkC,2BAA2B,CAAC;AAC3E,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAC7C,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AACtD,eAAO,MAAM,gBAAgB,SAAS,CAAC;AACvC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,eAAO,MAAM,uBAAuB,SAAS,CAAC;AAC9C,eAAO,MAAM,0BAA0B,YAAY,CAAC;AAEpD,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,wBAAwB,EAyChE,CAAC"}
|
|
@@ -2,6 +2,8 @@ import { ProjectRepository } from './adapter-interfaces/ProjectRepository';
|
|
|
2
2
|
export declare class SetupTowerDefenceProjectUseCase {
|
|
3
3
|
private readonly projectRepository;
|
|
4
4
|
constructor(projectRepository: Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>);
|
|
5
|
+
private static readonly LEGACY_STATUS_NAMES;
|
|
6
|
+
private static readonly MIGRATED_FROM_NAMES;
|
|
5
7
|
run: (params: {
|
|
6
8
|
projectUrl: string;
|
|
7
9
|
}) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SetupTowerDefenceProjectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/SetupTowerDefenceProjectUseCase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"SetupTowerDefenceProjectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/SetupTowerDefenceProjectUseCase.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAW3E,qBAAa,+BAA+B;IAExC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAAjB,iBAAiB,EAAE,IAAI,CACtC,iBAAiB,EACjB,UAAU,GAAG,kBAAkB,CAChC;IAGH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAKzC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAIxC;IAEH,GAAG,GAAU,QAAQ;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,IAAI,CAAC,CAoDzD;IAEF,OAAO,CAAC,MAAM,CAAC,mCAAmC,CAgBhD;CACH"}
|