@aaronshaf/ger 3.0.1 → 4.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/docs/prd/commands.md +59 -1
- package/package.json +1 -1
- package/src/api/gerrit-types.ts +121 -0
- package/src/api/gerrit.ts +68 -102
- package/src/cli/commands/analyze.ts +284 -0
- package/src/cli/commands/cherry.ts +268 -0
- package/src/cli/commands/failures.ts +74 -0
- package/src/cli/commands/files.ts +86 -0
- package/src/cli/commands/list.ts +239 -0
- package/src/cli/commands/rebase.ts +5 -1
- package/src/cli/commands/retrigger.ts +91 -0
- package/src/cli/commands/reviewers.ts +95 -0
- package/src/cli/commands/setup.ts +19 -6
- package/src/cli/commands/tree-cleanup.ts +139 -0
- package/src/cli/commands/tree-rebase.ts +168 -0
- package/src/cli/commands/tree-setup.ts +202 -0
- package/src/cli/commands/trees.ts +107 -0
- package/src/cli/commands/update.ts +73 -0
- package/src/cli/register-analytics-commands.ts +90 -0
- package/src/cli/register-commands.ts +96 -40
- package/src/cli/register-list-commands.ts +105 -0
- package/src/cli/register-tree-commands.ts +128 -0
- package/src/schemas/config.ts +3 -0
- package/src/schemas/gerrit.ts +2 -0
- package/src/schemas/reviewer.ts +16 -0
- package/src/services/config.ts +15 -0
- package/tests/analyze.test.ts +197 -0
- package/tests/cherry.test.ts +208 -0
- package/tests/failures.test.ts +212 -0
- package/tests/files.test.ts +223 -0
- package/tests/helpers/config-mock.ts +4 -0
- package/tests/list.test.ts +220 -0
- package/tests/retrigger.test.ts +159 -0
- package/tests/reviewers.test.ts +259 -0
- package/tests/tree.test.ts +517 -0
- package/tests/update.test.ts +86 -0
package/docs/prd/commands.md
CHANGED
|
@@ -28,7 +28,65 @@ ger show # Auto-detect from HEAD
|
|
|
28
28
|
- Full diff
|
|
29
29
|
- All comments with context
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
### files
|
|
32
|
+
|
|
33
|
+
List files changed in a change.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ger files [change-id]
|
|
37
|
+
ger files 12345
|
|
38
|
+
ger files # Auto-detect from HEAD
|
|
39
|
+
ger files 12345 --json
|
|
40
|
+
ger files 12345 --xml
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Option | Description |
|
|
44
|
+
|--------|-------------|
|
|
45
|
+
| `--json` | Output as JSON |
|
|
46
|
+
| `--xml` | Output as XML for LLM consumption |
|
|
47
|
+
|
|
48
|
+
**Output:** One file per line with status prefix (`M` modified, `A` added, `D` deleted, `R` renamed). Magic files (`/COMMIT_MSG`, `/MERGE_LIST`, `/PATCHSET_LEVEL`) are filtered out.
|
|
49
|
+
|
|
50
|
+
**JSON output:**
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"status": "success",
|
|
54
|
+
"change_id": "12345",
|
|
55
|
+
"files": [
|
|
56
|
+
{ "path": "src/foo.ts", "status": "M", "lines_inserted": 10, "lines_deleted": 2 }
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### reviewers
|
|
62
|
+
|
|
63
|
+
List reviewers on a change.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ger reviewers [change-id]
|
|
67
|
+
ger reviewers 12345
|
|
68
|
+
ger reviewers # Auto-detect from HEAD
|
|
69
|
+
ger reviewers 12345 --json
|
|
70
|
+
ger reviewers 12345 --xml
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Option | Description |
|
|
74
|
+
|--------|-------------|
|
|
75
|
+
| `--json` | Output as JSON |
|
|
76
|
+
| `--xml` | Output as XML for LLM consumption |
|
|
77
|
+
|
|
78
|
+
**Output:** One reviewer per line in `Name <email>` format (or email alone for email-only accounts).
|
|
79
|
+
|
|
80
|
+
**JSON output:**
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"status": "success",
|
|
84
|
+
"change_id": "12345",
|
|
85
|
+
"reviewers": [
|
|
86
|
+
{ "account_id": 1001, "name": "Alice Smith", "email": "alice@example.com", "username": "alice" }
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
```
|
|
32
90
|
|
|
33
91
|
### diff
|
|
34
92
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Schema } from '@effect/schema'
|
|
2
|
+
import type { Effect } from 'effect'
|
|
3
|
+
import type {
|
|
4
|
+
ChangeInfo,
|
|
5
|
+
CommentInfo,
|
|
6
|
+
MessageInfo,
|
|
7
|
+
DiffOptions,
|
|
8
|
+
FileDiffContent,
|
|
9
|
+
FileInfo,
|
|
10
|
+
ProjectInfo,
|
|
11
|
+
ReviewInput,
|
|
12
|
+
ReviewerInput,
|
|
13
|
+
ReviewerResult,
|
|
14
|
+
RevisionInfo,
|
|
15
|
+
SubmitInfo,
|
|
16
|
+
GroupInfo,
|
|
17
|
+
GroupDetailInfo,
|
|
18
|
+
AccountInfo,
|
|
19
|
+
} from '@/schemas/gerrit'
|
|
20
|
+
import type { ReviewerListItem } from '@/schemas/reviewer'
|
|
21
|
+
|
|
22
|
+
export interface ApiErrorFields {
|
|
23
|
+
readonly message: string
|
|
24
|
+
readonly status?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ApiErrorSchema = Schema.TaggedError<ApiErrorFields>()('ApiError', {
|
|
28
|
+
message: Schema.String,
|
|
29
|
+
status: Schema.optional(Schema.Number),
|
|
30
|
+
} as const) as unknown
|
|
31
|
+
|
|
32
|
+
export class ApiError
|
|
33
|
+
extends (ApiErrorSchema as new (
|
|
34
|
+
args: ApiErrorFields,
|
|
35
|
+
) => ApiErrorFields & Error & { readonly _tag: 'ApiError' })
|
|
36
|
+
implements Error
|
|
37
|
+
{
|
|
38
|
+
readonly name = 'ApiError'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface GerritApiServiceImpl {
|
|
42
|
+
readonly getChange: (changeId: string) => Effect.Effect<ChangeInfo, ApiError>
|
|
43
|
+
readonly listChanges: (query?: string) => Effect.Effect<readonly ChangeInfo[], ApiError>
|
|
44
|
+
readonly listProjects: (options?: {
|
|
45
|
+
pattern?: string
|
|
46
|
+
}) => Effect.Effect<readonly ProjectInfo[], ApiError>
|
|
47
|
+
readonly postReview: (changeId: string, review: ReviewInput) => Effect.Effect<void, ApiError>
|
|
48
|
+
readonly abandonChange: (changeId: string, message?: string) => Effect.Effect<void, ApiError>
|
|
49
|
+
readonly restoreChange: (
|
|
50
|
+
changeId: string,
|
|
51
|
+
message?: string,
|
|
52
|
+
) => Effect.Effect<ChangeInfo, ApiError>
|
|
53
|
+
readonly rebaseChange: (
|
|
54
|
+
changeId: string,
|
|
55
|
+
options?: { base?: string; allowConflicts?: boolean },
|
|
56
|
+
) => Effect.Effect<ChangeInfo, ApiError>
|
|
57
|
+
readonly submitChange: (changeId: string) => Effect.Effect<SubmitInfo, ApiError>
|
|
58
|
+
readonly testConnection: Effect.Effect<boolean, ApiError>
|
|
59
|
+
readonly getRevision: (
|
|
60
|
+
changeId: string,
|
|
61
|
+
revisionId?: string,
|
|
62
|
+
) => Effect.Effect<RevisionInfo, ApiError>
|
|
63
|
+
readonly getFiles: (
|
|
64
|
+
changeId: string,
|
|
65
|
+
revisionId?: string,
|
|
66
|
+
) => Effect.Effect<Record<string, FileInfo>, ApiError>
|
|
67
|
+
readonly getFileDiff: (
|
|
68
|
+
changeId: string,
|
|
69
|
+
filePath: string,
|
|
70
|
+
revisionId?: string,
|
|
71
|
+
base?: string,
|
|
72
|
+
) => Effect.Effect<FileDiffContent, ApiError>
|
|
73
|
+
readonly getFileContent: (
|
|
74
|
+
changeId: string,
|
|
75
|
+
filePath: string,
|
|
76
|
+
revisionId?: string,
|
|
77
|
+
) => Effect.Effect<string, ApiError>
|
|
78
|
+
readonly getPatch: (changeId: string, revisionId?: string) => Effect.Effect<string, ApiError>
|
|
79
|
+
readonly getDiff: (
|
|
80
|
+
changeId: string,
|
|
81
|
+
options?: DiffOptions,
|
|
82
|
+
) => Effect.Effect<string | string[] | Record<string, unknown> | FileDiffContent, ApiError>
|
|
83
|
+
readonly getComments: (
|
|
84
|
+
changeId: string,
|
|
85
|
+
revisionId?: string,
|
|
86
|
+
) => Effect.Effect<Record<string, readonly CommentInfo[]>, ApiError>
|
|
87
|
+
readonly getMessages: (changeId: string) => Effect.Effect<readonly MessageInfo[], ApiError>
|
|
88
|
+
readonly addReviewer: (
|
|
89
|
+
changeId: string,
|
|
90
|
+
reviewer: string,
|
|
91
|
+
options?: { state?: 'REVIEWER' | 'CC'; notify?: 'NONE' | 'OWNER' | 'OWNER_REVIEWERS' | 'ALL' },
|
|
92
|
+
) => Effect.Effect<ReviewerResult, ApiError>
|
|
93
|
+
readonly listGroups: (options?: {
|
|
94
|
+
owned?: boolean
|
|
95
|
+
project?: string
|
|
96
|
+
user?: string
|
|
97
|
+
pattern?: string
|
|
98
|
+
limit?: number
|
|
99
|
+
skip?: number
|
|
100
|
+
}) => Effect.Effect<readonly GroupInfo[], ApiError>
|
|
101
|
+
readonly getGroup: (groupId: string) => Effect.Effect<GroupInfo, ApiError>
|
|
102
|
+
readonly getGroupDetail: (groupId: string) => Effect.Effect<GroupDetailInfo, ApiError>
|
|
103
|
+
readonly getGroupMembers: (groupId: string) => Effect.Effect<readonly AccountInfo[], ApiError>
|
|
104
|
+
readonly getReviewers: (changeId: string) => Effect.Effect<readonly ReviewerListItem[], ApiError>
|
|
105
|
+
readonly removeReviewer: (
|
|
106
|
+
changeId: string,
|
|
107
|
+
accountId: string,
|
|
108
|
+
options?: { notify?: 'NONE' | 'OWNER' | 'OWNER_REVIEWERS' | 'ALL' },
|
|
109
|
+
) => Effect.Effect<void, ApiError>
|
|
110
|
+
readonly getTopic: (changeId: string) => Effect.Effect<string | null, ApiError>
|
|
111
|
+
readonly setTopic: (changeId: string, topic: string) => Effect.Effect<string, ApiError>
|
|
112
|
+
readonly deleteTopic: (changeId: string) => Effect.Effect<void, ApiError>
|
|
113
|
+
readonly setReady: (changeId: string, message?: string) => Effect.Effect<void, ApiError>
|
|
114
|
+
readonly setWip: (changeId: string, message?: string) => Effect.Effect<void, ApiError>
|
|
115
|
+
readonly fetchMergedChanges: (options: {
|
|
116
|
+
after: string
|
|
117
|
+
before?: string
|
|
118
|
+
repo?: string
|
|
119
|
+
maxResults?: number
|
|
120
|
+
}) => Effect.Effect<readonly ChangeInfo[], ApiError>
|
|
121
|
+
}
|
package/src/api/gerrit.ts
CHANGED
|
@@ -18,109 +18,20 @@ import {
|
|
|
18
18
|
GroupDetailInfo,
|
|
19
19
|
AccountInfo,
|
|
20
20
|
} from '@/schemas/gerrit'
|
|
21
|
+
import { ReviewerListItem } from '@/schemas/reviewer'
|
|
21
22
|
import { filterMeaningfulMessages } from '@/utils/message-filters'
|
|
22
23
|
import { convertToUnifiedDiff } from '@/utils/diff-formatters'
|
|
23
24
|
import { ConfigService } from '@/services/config'
|
|
24
25
|
import { normalizeChangeIdentifier } from '@/utils/change-id'
|
|
25
26
|
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
readonly listProjects: (options?: {
|
|
30
|
-
pattern?: string
|
|
31
|
-
}) => Effect.Effect<readonly ProjectInfo[], ApiError>
|
|
32
|
-
readonly postReview: (changeId: string, review: ReviewInput) => Effect.Effect<void, ApiError>
|
|
33
|
-
readonly abandonChange: (changeId: string, message?: string) => Effect.Effect<void, ApiError>
|
|
34
|
-
readonly restoreChange: (
|
|
35
|
-
changeId: string,
|
|
36
|
-
message?: string,
|
|
37
|
-
) => Effect.Effect<ChangeInfo, ApiError>
|
|
38
|
-
readonly rebaseChange: (
|
|
39
|
-
changeId: string,
|
|
40
|
-
options?: { base?: string },
|
|
41
|
-
) => Effect.Effect<ChangeInfo, ApiError>
|
|
42
|
-
readonly submitChange: (changeId: string) => Effect.Effect<SubmitInfo, ApiError>
|
|
43
|
-
readonly testConnection: Effect.Effect<boolean, ApiError>
|
|
44
|
-
readonly getRevision: (
|
|
45
|
-
changeId: string,
|
|
46
|
-
revisionId?: string,
|
|
47
|
-
) => Effect.Effect<RevisionInfo, ApiError>
|
|
48
|
-
readonly getFiles: (
|
|
49
|
-
changeId: string,
|
|
50
|
-
revisionId?: string,
|
|
51
|
-
) => Effect.Effect<Record<string, FileInfo>, ApiError>
|
|
52
|
-
readonly getFileDiff: (
|
|
53
|
-
changeId: string,
|
|
54
|
-
filePath: string,
|
|
55
|
-
revisionId?: string,
|
|
56
|
-
base?: string,
|
|
57
|
-
) => Effect.Effect<FileDiffContent, ApiError>
|
|
58
|
-
readonly getFileContent: (
|
|
59
|
-
changeId: string,
|
|
60
|
-
filePath: string,
|
|
61
|
-
revisionId?: string,
|
|
62
|
-
) => Effect.Effect<string, ApiError>
|
|
63
|
-
readonly getPatch: (changeId: string, revisionId?: string) => Effect.Effect<string, ApiError>
|
|
64
|
-
readonly getDiff: (
|
|
65
|
-
changeId: string,
|
|
66
|
-
options?: DiffOptions,
|
|
67
|
-
) => Effect.Effect<string | string[] | Record<string, unknown> | FileDiffContent, ApiError>
|
|
68
|
-
readonly getComments: (
|
|
69
|
-
changeId: string,
|
|
70
|
-
revisionId?: string,
|
|
71
|
-
) => Effect.Effect<Record<string, readonly CommentInfo[]>, ApiError>
|
|
72
|
-
readonly getMessages: (changeId: string) => Effect.Effect<readonly MessageInfo[], ApiError>
|
|
73
|
-
readonly addReviewer: (
|
|
74
|
-
changeId: string,
|
|
75
|
-
reviewer: string,
|
|
76
|
-
options?: { state?: 'REVIEWER' | 'CC'; notify?: 'NONE' | 'OWNER' | 'OWNER_REVIEWERS' | 'ALL' },
|
|
77
|
-
) => Effect.Effect<ReviewerResult, ApiError>
|
|
78
|
-
readonly listGroups: (options?: {
|
|
79
|
-
owned?: boolean
|
|
80
|
-
project?: string
|
|
81
|
-
user?: string
|
|
82
|
-
pattern?: string
|
|
83
|
-
limit?: number
|
|
84
|
-
skip?: number
|
|
85
|
-
}) => Effect.Effect<readonly GroupInfo[], ApiError>
|
|
86
|
-
readonly getGroup: (groupId: string) => Effect.Effect<GroupInfo, ApiError>
|
|
87
|
-
readonly getGroupDetail: (groupId: string) => Effect.Effect<GroupDetailInfo, ApiError>
|
|
88
|
-
readonly getGroupMembers: (groupId: string) => Effect.Effect<readonly AccountInfo[], ApiError>
|
|
89
|
-
readonly removeReviewer: (
|
|
90
|
-
changeId: string,
|
|
91
|
-
accountId: string,
|
|
92
|
-
options?: { notify?: 'NONE' | 'OWNER' | 'OWNER_REVIEWERS' | 'ALL' },
|
|
93
|
-
) => Effect.Effect<void, ApiError>
|
|
94
|
-
readonly getTopic: (changeId: string) => Effect.Effect<string | null, ApiError>
|
|
95
|
-
readonly setTopic: (changeId: string, topic: string) => Effect.Effect<string, ApiError>
|
|
96
|
-
readonly deleteTopic: (changeId: string) => Effect.Effect<void, ApiError>
|
|
97
|
-
readonly setReady: (changeId: string, message?: string) => Effect.Effect<void, ApiError>
|
|
98
|
-
readonly setWip: (changeId: string, message?: string) => Effect.Effect<void, ApiError>
|
|
99
|
-
}
|
|
27
|
+
export type { GerritApiServiceImpl, ApiErrorFields } from './gerrit-types'
|
|
28
|
+
export { ApiError } from './gerrit-types'
|
|
29
|
+
import { ApiError, type GerritApiServiceImpl } from './gerrit-types'
|
|
100
30
|
|
|
101
31
|
export const GerritApiService: Context.Tag<GerritApiServiceImpl, GerritApiServiceImpl> =
|
|
102
32
|
Context.GenericTag<GerritApiServiceImpl>('GerritApiService')
|
|
103
33
|
export type GerritApiService = Context.Tag.Identifier<typeof GerritApiService>
|
|
104
34
|
|
|
105
|
-
export interface ApiErrorFields {
|
|
106
|
-
readonly message: string
|
|
107
|
-
readonly status?: number
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const ApiErrorSchema = Schema.TaggedError<ApiErrorFields>()('ApiError', {
|
|
111
|
-
message: Schema.String,
|
|
112
|
-
status: Schema.optional(Schema.Number),
|
|
113
|
-
} as const) as unknown
|
|
114
|
-
|
|
115
|
-
export class ApiError
|
|
116
|
-
extends (ApiErrorSchema as new (
|
|
117
|
-
args: ApiErrorFields,
|
|
118
|
-
) => ApiErrorFields & Error & { readonly _tag: 'ApiError' })
|
|
119
|
-
implements Error
|
|
120
|
-
{
|
|
121
|
-
readonly name = 'ApiError'
|
|
122
|
-
}
|
|
123
|
-
|
|
124
35
|
const createAuthHeader = (credentials: GerritCredentials): string => {
|
|
125
36
|
const auth = btoa(`${credentials.username}:${credentials.password}`)
|
|
126
37
|
return `Basic ${auth}`
|
|
@@ -227,13 +138,8 @@ export const GerritApiServiceLive: Layer.Layer<GerritApiService, never, ConfigSe
|
|
|
227
138
|
const { credentials, authHeader } = yield* getCredentialsAndAuth
|
|
228
139
|
let url = `${credentials.host}/a/projects/`
|
|
229
140
|
if (options?.pattern) url += `?p=${encodeURIComponent(options.pattern)}`
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
authHeader,
|
|
233
|
-
'GET',
|
|
234
|
-
undefined,
|
|
235
|
-
Schema.Record({ key: Schema.String, value: ProjectInfo }),
|
|
236
|
-
)
|
|
141
|
+
const schema = Schema.Record({ key: Schema.String, value: ProjectInfo })
|
|
142
|
+
const projectsRecord = yield* makeRequest(url, authHeader, 'GET', undefined, schema)
|
|
237
143
|
return Object.values(projectsRecord).sort((a, b) => a.name.localeCompare(b.name))
|
|
238
144
|
})
|
|
239
145
|
|
|
@@ -263,12 +169,17 @@ export const GerritApiServiceLive: Layer.Layer<GerritApiService, never, ConfigSe
|
|
|
263
169
|
return yield* makeRequest(url, authHeader, 'POST', body, ChangeInfo)
|
|
264
170
|
})
|
|
265
171
|
|
|
266
|
-
const rebaseChange = (
|
|
172
|
+
const rebaseChange = (
|
|
173
|
+
changeId: string,
|
|
174
|
+
options?: { base?: string; allowConflicts?: boolean },
|
|
175
|
+
) =>
|
|
267
176
|
Effect.gen(function* () {
|
|
268
177
|
const { credentials, authHeader } = yield* getCredentialsAndAuth
|
|
269
178
|
const normalized = yield* normalizeAndValidate(changeId)
|
|
270
179
|
const url = `${credentials.host}/a/changes/${encodeURIComponent(normalized)}/revisions/current/rebase`
|
|
271
|
-
const body
|
|
180
|
+
const body: Record<string, string | boolean> = {}
|
|
181
|
+
if (options?.base) body['base'] = options.base
|
|
182
|
+
if (options?.allowConflicts) body['allow_conflicts'] = true
|
|
272
183
|
return yield* makeRequest(url, authHeader, 'POST', body, ChangeInfo)
|
|
273
184
|
})
|
|
274
185
|
|
|
@@ -581,6 +492,15 @@ export const GerritApiServiceLive: Layer.Layer<GerritApiService, never, ConfigSe
|
|
|
581
492
|
return yield* makeRequest(url, authHeader, 'GET', undefined, Schema.Array(AccountInfo))
|
|
582
493
|
})
|
|
583
494
|
|
|
495
|
+
const getReviewers = (changeId: string) =>
|
|
496
|
+
Effect.gen(function* () {
|
|
497
|
+
const { credentials, authHeader } = yield* getCredentialsAndAuth
|
|
498
|
+
const normalized = yield* normalizeAndValidate(changeId)
|
|
499
|
+
const url = `${credentials.host}/a/changes/${encodeURIComponent(normalized)}/reviewers`
|
|
500
|
+
const schema = Schema.Array(ReviewerListItem)
|
|
501
|
+
return yield* makeRequest(url, authHeader, 'GET', undefined, schema)
|
|
502
|
+
})
|
|
503
|
+
|
|
584
504
|
const removeReviewer = (
|
|
585
505
|
changeId: string,
|
|
586
506
|
accountId: string,
|
|
@@ -655,6 +575,50 @@ export const GerritApiServiceLive: Layer.Layer<GerritApiService, never, ConfigSe
|
|
|
655
575
|
yield* makeRequest(url, authHeader, 'POST', body)
|
|
656
576
|
})
|
|
657
577
|
|
|
578
|
+
const fetchMergedChanges = (options: {
|
|
579
|
+
after: string
|
|
580
|
+
before?: string
|
|
581
|
+
repo?: string
|
|
582
|
+
maxResults?: number
|
|
583
|
+
}) =>
|
|
584
|
+
Effect.gen(function* () {
|
|
585
|
+
const { credentials, authHeader } = yield* getCredentialsAndAuth
|
|
586
|
+
const limit = options.maxResults ?? 500
|
|
587
|
+
const pageSize = Math.min(limit, 500)
|
|
588
|
+
const allChanges: ChangeInfo[] = []
|
|
589
|
+
let start = 0
|
|
590
|
+
let hasMore = true
|
|
591
|
+
|
|
592
|
+
while (hasMore) {
|
|
593
|
+
let q = `status:merged after:${options.after}`
|
|
594
|
+
if (options.before) q += ` before:${options.before}`
|
|
595
|
+
if (options.repo) q += ` project:${options.repo}`
|
|
596
|
+
const url = `${credentials.host}/a/changes/?q=${encodeURIComponent(q)}&o=DETAILED_ACCOUNTS&n=${pageSize}&S=${start}`
|
|
597
|
+
const page = yield* makeRequest(
|
|
598
|
+
url,
|
|
599
|
+
authHeader,
|
|
600
|
+
'GET',
|
|
601
|
+
undefined,
|
|
602
|
+
Schema.Array(ChangeInfo),
|
|
603
|
+
)
|
|
604
|
+
allChanges.push(...page)
|
|
605
|
+
const remaining = limit - allChanges.length
|
|
606
|
+
if (page.length < pageSize || remaining <= 0) {
|
|
607
|
+
hasMore = false
|
|
608
|
+
} else {
|
|
609
|
+
start += pageSize
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (allChanges.length >= limit) {
|
|
614
|
+
console.warn(
|
|
615
|
+
`Warning: results capped at ${limit}. Use --start-date to narrow the date range.`,
|
|
616
|
+
)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
return allChanges as readonly ChangeInfo[]
|
|
620
|
+
})
|
|
621
|
+
|
|
658
622
|
return {
|
|
659
623
|
getChange,
|
|
660
624
|
listChanges,
|
|
@@ -674,6 +638,7 @@ export const GerritApiServiceLive: Layer.Layer<GerritApiService, never, ConfigSe
|
|
|
674
638
|
getComments,
|
|
675
639
|
getMessages,
|
|
676
640
|
addReviewer,
|
|
641
|
+
getReviewers,
|
|
677
642
|
listGroups,
|
|
678
643
|
getGroup,
|
|
679
644
|
getGroupDetail,
|
|
@@ -684,6 +649,7 @@ export const GerritApiServiceLive: Layer.Layer<GerritApiService, never, ConfigSe
|
|
|
684
649
|
deleteTopic,
|
|
685
650
|
setReady,
|
|
686
651
|
setWip,
|
|
652
|
+
fetchMergedChanges,
|
|
687
653
|
}
|
|
688
654
|
}),
|
|
689
655
|
)
|