npm-cli-gh-issue-preparator 1.0.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/.env.example +0 -0
- package/.eslintrc.cjs +65 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/workflows/commit-lint.yml +52 -0
- package/.github/workflows/configs/commitlint.config.js +27 -0
- package/.github/workflows/create-pr.yml +66 -0
- package/.github/workflows/empty-format-test-job.yml +28 -0
- package/.github/workflows/format.yml +25 -0
- package/.github/workflows/publish.yml +47 -0
- package/.github/workflows/test.yml +38 -0
- package/.github/workflows/umino-project.yml +191 -0
- package/.prettierignore +22 -0
- package/.prettierrc +5 -0
- package/CHANGELOG.md +27 -0
- package/CONTRIBUTING.md +107 -0
- package/README.md +49 -0
- package/bin/adapter/entry-points/cli/index.js +72 -0
- package/bin/adapter/entry-points/cli/index.js.map +1 -0
- package/bin/adapter/repositories/GitHubIssueRepository.js +340 -0
- package/bin/adapter/repositories/GitHubIssueRepository.js.map +1 -0
- package/bin/adapter/repositories/GitHubProjectRepository.js +123 -0
- package/bin/adapter/repositories/GitHubProjectRepository.js.map +1 -0
- package/bin/adapter/repositories/NodeLocalCommandRunner.js +34 -0
- package/bin/adapter/repositories/NodeLocalCommandRunner.js.map +1 -0
- package/bin/domain/entities/Issue.js +3 -0
- package/bin/domain/entities/Issue.js.map +1 -0
- package/bin/domain/entities/Project.js +3 -0
- package/bin/domain/entities/Project.js.map +1 -0
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +37 -0
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -0
- package/bin/domain/usecases/StartPreparationUseCase.js +31 -0
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/IssueRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/IssueRepository.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/LocalCommandRunner.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/LocalCommandRunner.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/ProjectRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/ProjectRepository.js.map +1 -0
- package/bin/index.js +6 -0
- package/bin/index.js.map +1 -0
- package/commitlint.config.js +6 -0
- package/jest.config.js +33 -0
- package/package.json +75 -0
- package/renovate.json +37 -0
- package/src/adapter/entry-points/cli/index.integration.test.ts +143 -0
- package/src/adapter/entry-points/cli/index.test.ts +165 -0
- package/src/adapter/entry-points/cli/index.ts +110 -0
- package/src/adapter/repositories/GitHubIssueRepository.integration.test.ts +50 -0
- package/src/adapter/repositories/GitHubIssueRepository.test.ts +996 -0
- package/src/adapter/repositories/GitHubIssueRepository.ts +470 -0
- package/src/adapter/repositories/GitHubProjectRepository.test.ts +252 -0
- package/src/adapter/repositories/GitHubProjectRepository.ts +162 -0
- package/src/adapter/repositories/NodeLocalCommandRunner.test.ts +80 -0
- package/src/adapter/repositories/NodeLocalCommandRunner.ts +37 -0
- package/src/domain/entities/Issue.ts +7 -0
- package/src/domain/entities/Project.ts +7 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +109 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +48 -0
- package/src/domain/usecases/StartPreparationUseCase.test.ts +150 -0
- package/src/domain/usecases/StartPreparationUseCase.ts +48 -0
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +8 -0
- package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +7 -0
- package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +5 -0
- package/src/index.test.ts +7 -0
- package/src/index.ts +3 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +16 -0
- package/types/adapter/entry-points/cli/index.d.ts +5 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -0
- package/types/adapter/repositories/GitHubIssueRepository.d.ts +14 -0
- package/types/adapter/repositories/GitHubIssueRepository.d.ts.map +1 -0
- package/types/adapter/repositories/GitHubProjectRepository.d.ts +9 -0
- package/types/adapter/repositories/GitHubProjectRepository.d.ts.map +1 -0
- package/types/adapter/repositories/NodeLocalCommandRunner.d.ts +9 -0
- package/types/adapter/repositories/NodeLocalCommandRunner.d.ts.map +1 -0
- package/types/domain/entities/Issue.d.ts +8 -0
- package/types/domain/entities/Issue.d.ts.map +1 -0
- package/types/domain/entities/Project.d.ts +8 -0
- package/types/domain/entities/Project.d.ts.map +1 -0
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts +20 -0
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -0
- package/types/domain/usecases/StartPreparationUseCase.d.ts +17 -0
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +8 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts +8 -0
- package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts +5 -0
- package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts.map +1 -0
- package/types/index.d.ts +3 -0
- package/types/index.d.ts.map +1 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { program } from './index';
|
|
2
|
+
import { StartPreparationUseCase } from '../../../domain/usecases/StartPreparationUseCase';
|
|
3
|
+
import { NotifyFinishedIssuePreparationUseCase } from '../../../domain/usecases/NotifyFinishedIssuePreparationUseCase';
|
|
4
|
+
|
|
5
|
+
jest.mock('../../../domain/usecases/StartPreparationUseCase');
|
|
6
|
+
jest.mock('../../../domain/usecases/NotifyFinishedIssuePreparationUseCase');
|
|
7
|
+
jest.mock('../../repositories/GitHubProjectRepository');
|
|
8
|
+
jest.mock('../../repositories/GitHubIssueRepository');
|
|
9
|
+
jest.mock('../../repositories/NodeLocalCommandRunner');
|
|
10
|
+
|
|
11
|
+
describe('CLI', () => {
|
|
12
|
+
const originalEnv = process.env;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
process.env = { ...originalEnv, GH_TOKEN: 'test-token' };
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
process.env = originalEnv;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should export program', () => {
|
|
24
|
+
expect(program).toBeDefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should pass correct parameters to StartPreparationUseCase', async () => {
|
|
28
|
+
const mockRun = jest.fn().mockResolvedValue(undefined);
|
|
29
|
+
const MockedStartPreparationUseCase = jest.mocked(StartPreparationUseCase);
|
|
30
|
+
|
|
31
|
+
MockedStartPreparationUseCase.mockImplementation(function (
|
|
32
|
+
this: StartPreparationUseCase,
|
|
33
|
+
) {
|
|
34
|
+
this.run = mockRun;
|
|
35
|
+
this.maximumPreparingIssuesCount = 6;
|
|
36
|
+
return this;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await program.parseAsync([
|
|
40
|
+
'node',
|
|
41
|
+
'test',
|
|
42
|
+
'startDaemon',
|
|
43
|
+
'--projectUrl',
|
|
44
|
+
'https://github.com/test/project',
|
|
45
|
+
'--awaitingWorkspaceStatus',
|
|
46
|
+
'Awaiting',
|
|
47
|
+
'--preparationStatus',
|
|
48
|
+
'Preparing',
|
|
49
|
+
'--defaultAgentName',
|
|
50
|
+
'agent1',
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
expect(mockRun).toHaveBeenCalledTimes(1);
|
|
54
|
+
expect(mockRun).toHaveBeenCalledWith({
|
|
55
|
+
projectUrl: 'https://github.com/test/project',
|
|
56
|
+
awaitingWorkspaceStatus: 'Awaiting',
|
|
57
|
+
preparationStatus: 'Preparing',
|
|
58
|
+
defaultAgentName: 'agent1',
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should pass correct parameters to NotifyFinishedIssuePreparationUseCase', async () => {
|
|
63
|
+
const mockRun = jest.fn().mockResolvedValue(undefined);
|
|
64
|
+
const MockedNotifyFinishedUseCase = jest.mocked(
|
|
65
|
+
NotifyFinishedIssuePreparationUseCase,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
MockedNotifyFinishedUseCase.mockImplementation(function (
|
|
69
|
+
this: NotifyFinishedIssuePreparationUseCase,
|
|
70
|
+
) {
|
|
71
|
+
this.run = mockRun;
|
|
72
|
+
return this;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await program.parseAsync([
|
|
76
|
+
'node',
|
|
77
|
+
'test',
|
|
78
|
+
'notifyFinishedIssuePreparation',
|
|
79
|
+
'--projectUrl',
|
|
80
|
+
'https://github.com/test/project',
|
|
81
|
+
'--issueUrl',
|
|
82
|
+
'https://github.com/test/issue/1',
|
|
83
|
+
'--preparationStatus',
|
|
84
|
+
'Preparing',
|
|
85
|
+
'--awaitingQualityCheckStatus',
|
|
86
|
+
'Awaiting QC',
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
expect(mockRun).toHaveBeenCalledTimes(1);
|
|
90
|
+
expect(mockRun).toHaveBeenCalledWith({
|
|
91
|
+
projectUrl: 'https://github.com/test/project',
|
|
92
|
+
issueUrl: 'https://github.com/test/issue/1',
|
|
93
|
+
preparationStatus: 'Preparing',
|
|
94
|
+
awaitingQualityCheckStatus: 'Awaiting QC',
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should exit with error when GH_TOKEN is missing for startDaemon', async () => {
|
|
99
|
+
delete process.env.GH_TOKEN;
|
|
100
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
101
|
+
const processExitSpy = jest
|
|
102
|
+
.spyOn(process, 'exit')
|
|
103
|
+
.mockImplementation(() => {
|
|
104
|
+
throw new Error('process.exit called');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await expect(
|
|
108
|
+
program.parseAsync([
|
|
109
|
+
'node',
|
|
110
|
+
'test',
|
|
111
|
+
'startDaemon',
|
|
112
|
+
'--projectUrl',
|
|
113
|
+
'https://github.com/test/project',
|
|
114
|
+
'--awaitingWorkspaceStatus',
|
|
115
|
+
'Awaiting',
|
|
116
|
+
'--preparationStatus',
|
|
117
|
+
'Preparing',
|
|
118
|
+
'--defaultAgentName',
|
|
119
|
+
'agent1',
|
|
120
|
+
]),
|
|
121
|
+
).rejects.toThrow('process.exit called');
|
|
122
|
+
|
|
123
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
124
|
+
'GH_TOKEN environment variable is required',
|
|
125
|
+
);
|
|
126
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
127
|
+
|
|
128
|
+
consoleErrorSpy.mockRestore();
|
|
129
|
+
processExitSpy.mockRestore();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should exit with error when GH_TOKEN is missing for notifyFinishedIssuePreparation', async () => {
|
|
133
|
+
delete process.env.GH_TOKEN;
|
|
134
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
135
|
+
const processExitSpy = jest
|
|
136
|
+
.spyOn(process, 'exit')
|
|
137
|
+
.mockImplementation(() => {
|
|
138
|
+
throw new Error('process.exit called');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await expect(
|
|
142
|
+
program.parseAsync([
|
|
143
|
+
'node',
|
|
144
|
+
'test',
|
|
145
|
+
'notifyFinishedIssuePreparation',
|
|
146
|
+
'--projectUrl',
|
|
147
|
+
'https://github.com/test/project',
|
|
148
|
+
'--issueUrl',
|
|
149
|
+
'https://github.com/test/issue/1',
|
|
150
|
+
'--preparationStatus',
|
|
151
|
+
'Preparing',
|
|
152
|
+
'--awaitingQualityCheckStatus',
|
|
153
|
+
'Awaiting QC',
|
|
154
|
+
]),
|
|
155
|
+
).rejects.toThrow('process.exit called');
|
|
156
|
+
|
|
157
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
158
|
+
'GH_TOKEN environment variable is required',
|
|
159
|
+
);
|
|
160
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
161
|
+
|
|
162
|
+
consoleErrorSpy.mockRestore();
|
|
163
|
+
processExitSpy.mockRestore();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { StartPreparationUseCase } from '../../../domain/usecases/StartPreparationUseCase';
|
|
7
|
+
import { NotifyFinishedIssuePreparationUseCase } from '../../../domain/usecases/NotifyFinishedIssuePreparationUseCase';
|
|
8
|
+
import { GitHubProjectRepository } from '../../repositories/GitHubProjectRepository';
|
|
9
|
+
import { GitHubIssueRepository } from '../../repositories/GitHubIssueRepository';
|
|
10
|
+
import { NodeLocalCommandRunner } from '../../repositories/NodeLocalCommandRunner';
|
|
11
|
+
|
|
12
|
+
type StartDaemonOptions = {
|
|
13
|
+
projectUrl: string;
|
|
14
|
+
awaitingWorkspaceStatus: string;
|
|
15
|
+
preparationStatus: string;
|
|
16
|
+
defaultAgentName: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type NotifyFinishedOptions = {
|
|
20
|
+
projectUrl: string;
|
|
21
|
+
issueUrl: string;
|
|
22
|
+
preparationStatus: string;
|
|
23
|
+
awaitingQualityCheckStatus: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const program = new Command();
|
|
27
|
+
program
|
|
28
|
+
.name('npm-cli-gh-issue-preparator')
|
|
29
|
+
.description('CLI tool to prepare GitHub issues');
|
|
30
|
+
|
|
31
|
+
program
|
|
32
|
+
.command('startDaemon')
|
|
33
|
+
.description('Start daemon to prepare GitHub issues')
|
|
34
|
+
.requiredOption('--projectUrl <url>', 'GitHub project URL')
|
|
35
|
+
.requiredOption(
|
|
36
|
+
'--awaitingWorkspaceStatus <status>',
|
|
37
|
+
'Status for issues awaiting workspace',
|
|
38
|
+
)
|
|
39
|
+
.requiredOption(
|
|
40
|
+
'--preparationStatus <status>',
|
|
41
|
+
'Status for issues in preparation',
|
|
42
|
+
)
|
|
43
|
+
.requiredOption('--defaultAgentName <name>', 'Default agent name')
|
|
44
|
+
.action(async (options: StartDaemonOptions) => {
|
|
45
|
+
const token = process.env.GH_TOKEN;
|
|
46
|
+
if (!token) {
|
|
47
|
+
console.error('GH_TOKEN environment variable is required');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const projectRepository = new GitHubProjectRepository(token);
|
|
52
|
+
const issueRepository = new GitHubIssueRepository(token);
|
|
53
|
+
const localCommandRunner = new NodeLocalCommandRunner();
|
|
54
|
+
|
|
55
|
+
const useCase = new StartPreparationUseCase(
|
|
56
|
+
projectRepository,
|
|
57
|
+
issueRepository,
|
|
58
|
+
localCommandRunner,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
await useCase.run({
|
|
62
|
+
projectUrl: options.projectUrl,
|
|
63
|
+
awaitingWorkspaceStatus: options.awaitingWorkspaceStatus,
|
|
64
|
+
preparationStatus: options.preparationStatus,
|
|
65
|
+
defaultAgentName: options.defaultAgentName,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
program
|
|
70
|
+
.command('notifyFinishedIssuePreparation')
|
|
71
|
+
.description('Notify that issue preparation is finished')
|
|
72
|
+
.requiredOption('--projectUrl <url>', 'GitHub project URL')
|
|
73
|
+
.requiredOption('--issueUrl <url>', 'GitHub issue URL')
|
|
74
|
+
.requiredOption(
|
|
75
|
+
'--preparationStatus <status>',
|
|
76
|
+
'Status for issues in preparation',
|
|
77
|
+
)
|
|
78
|
+
.requiredOption(
|
|
79
|
+
'--awaitingQualityCheckStatus <status>',
|
|
80
|
+
'Status for issues awaiting quality check',
|
|
81
|
+
)
|
|
82
|
+
.action(async (options: NotifyFinishedOptions) => {
|
|
83
|
+
const token = process.env.GH_TOKEN;
|
|
84
|
+
if (!token) {
|
|
85
|
+
console.error('GH_TOKEN environment variable is required');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const projectRepository = new GitHubProjectRepository(token);
|
|
90
|
+
const issueRepository = new GitHubIssueRepository(token);
|
|
91
|
+
|
|
92
|
+
const useCase = new NotifyFinishedIssuePreparationUseCase(
|
|
93
|
+
projectRepository,
|
|
94
|
+
issueRepository,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
await useCase.run({
|
|
98
|
+
projectUrl: options.projectUrl,
|
|
99
|
+
issueUrl: options.issueUrl,
|
|
100
|
+
preparationStatus: options.preparationStatus,
|
|
101
|
+
awaitingQualityCheckStatus: options.awaitingQualityCheckStatus,
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/* istanbul ignore next */
|
|
106
|
+
if (process.argv && require.main === module) {
|
|
107
|
+
program.parse(process.argv);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export { program };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
|
|
4
|
+
import { GitHubIssueRepository } from './GitHubIssueRepository';
|
|
5
|
+
import { GitHubProjectRepository } from './GitHubProjectRepository';
|
|
6
|
+
|
|
7
|
+
describe('GitHubIssueRepository Integration Test', () => {
|
|
8
|
+
const token = process.env.GH_TOKEN;
|
|
9
|
+
const projectUrl = 'https://github.com/users/HiromiShikata/projects/49';
|
|
10
|
+
const issueUrl =
|
|
11
|
+
'https://github.com/HiromiShikata/test-repository/issues/1552';
|
|
12
|
+
|
|
13
|
+
it('should update issue status from Awaiting workspace to Preparation and verify the change', async () => {
|
|
14
|
+
if (!token) {
|
|
15
|
+
throw new Error('GH_TOKEN environment variable is required');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const repository = new GitHubIssueRepository(token);
|
|
19
|
+
const projectRepository = new GitHubProjectRepository(token);
|
|
20
|
+
const project = await projectRepository.getByUrl(projectUrl);
|
|
21
|
+
|
|
22
|
+
const initialIssue = await repository.get(issueUrl, project);
|
|
23
|
+
expect(initialIssue).not.toBeNull();
|
|
24
|
+
if (!initialIssue) {
|
|
25
|
+
throw new Error('Failed to get initial issue');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
initialIssue.status = 'Awaiting workspace';
|
|
29
|
+
await repository.update(initialIssue, project);
|
|
30
|
+
|
|
31
|
+
const reloadedIssue = await repository.get(issueUrl, project);
|
|
32
|
+
if (!reloadedIssue) {
|
|
33
|
+
throw new Error('Failed to get reloaded issue');
|
|
34
|
+
}
|
|
35
|
+
expect(reloadedIssue.status).toBe('Awaiting workspace');
|
|
36
|
+
|
|
37
|
+
reloadedIssue.status = 'Preparation';
|
|
38
|
+
await repository.update(reloadedIssue, project);
|
|
39
|
+
|
|
40
|
+
const verifyIssue = await repository.get(issueUrl, project);
|
|
41
|
+
expect(verifyIssue).not.toBeNull();
|
|
42
|
+
if (!verifyIssue) {
|
|
43
|
+
throw new Error('Failed to get verify issue');
|
|
44
|
+
}
|
|
45
|
+
expect(verifyIssue.status).toBe('Preparation');
|
|
46
|
+
|
|
47
|
+
verifyIssue.status = 'Awaiting workspace';
|
|
48
|
+
await repository.update(verifyIssue, project);
|
|
49
|
+
}, 60000);
|
|
50
|
+
});
|