lalph 0.2.17 → 0.2.19
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/dist/cli.mjs +287 -253
- package/package.json +3 -3
- package/src/Github/Cli.ts +71 -39
- package/src/Settings.ts +10 -7
- package/src/commands/projects/rm.ts +11 -1
- package/src/domain/GithubComment.ts +30 -30
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lalph",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.19",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"@changesets/changelog-github": "^0.5.2",
|
|
24
24
|
"@changesets/cli": "^2.29.8",
|
|
25
25
|
"@effect/language-service": "^0.72.0",
|
|
26
|
-
"@effect/platform-node": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@
|
|
26
|
+
"@effect/platform-node": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@a5fa8be",
|
|
27
27
|
"@linear/sdk": "^72.0.0",
|
|
28
28
|
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
|
|
29
29
|
"@octokit/types": "^16.0.0",
|
|
30
|
-
"effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@
|
|
30
|
+
"effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@a5fa8be",
|
|
31
31
|
"husky": "^9.1.7",
|
|
32
32
|
"lint-staged": "^16.2.7",
|
|
33
33
|
"octokit": "^5.0.5",
|
package/src/Github/Cli.ts
CHANGED
|
@@ -10,9 +10,9 @@ import {
|
|
|
10
10
|
} from "effect"
|
|
11
11
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
|
12
12
|
import {
|
|
13
|
-
CommentsData,
|
|
14
|
-
ReviewComment,
|
|
15
13
|
Comment,
|
|
14
|
+
GithubPullRequestData,
|
|
15
|
+
ReviewComment,
|
|
16
16
|
} from "../domain/GithubComment.ts"
|
|
17
17
|
|
|
18
18
|
export class GithubCli extends ServiceMap.Service<GithubCli>()(
|
|
@@ -41,17 +41,19 @@ export class GithubCli extends ServiceMap.Service<GithubCli>()(
|
|
|
41
41
|
const reviewComments = (pr: number) =>
|
|
42
42
|
ChildProcess.make`gh api graphql -f owner=${owner} -f repo=${repo} -F pr=${pr} -f query=${githubReviewCommentsQuery}`.pipe(
|
|
43
43
|
ChildProcess.string,
|
|
44
|
-
Effect.flatMap(Schema.decodeEffect(
|
|
44
|
+
Effect.flatMap(Schema.decodeEffect(PullRequestDataFromJson)),
|
|
45
45
|
Effect.map((data) => {
|
|
46
46
|
const comments =
|
|
47
|
-
data.data.repository.pullRequest.comments.
|
|
48
|
-
(
|
|
47
|
+
data.data.repository.pullRequest.comments.nodes.filter(
|
|
48
|
+
(c) => !c.author.login.startsWith("github"),
|
|
49
49
|
)
|
|
50
|
-
const
|
|
51
|
-
data.data.repository.pullRequest.
|
|
52
|
-
(
|
|
50
|
+
const reviews =
|
|
51
|
+
data.data.repository.pullRequest.reviews.nodes.filter(
|
|
52
|
+
(r) => r.body.trim().length > 0,
|
|
53
53
|
)
|
|
54
|
-
|
|
54
|
+
const reviewThreads =
|
|
55
|
+
data.data.repository.pullRequest.reviewThreads.nodes
|
|
56
|
+
return { comments, reviews, reviewThreads } as const
|
|
55
57
|
}),
|
|
56
58
|
Effect.provideService(
|
|
57
59
|
ChildProcessSpawner.ChildProcessSpawner,
|
|
@@ -61,12 +63,16 @@ export class GithubCli extends ServiceMap.Service<GithubCli>()(
|
|
|
61
63
|
|
|
62
64
|
const prFeedbackMd = (pr: number) =>
|
|
63
65
|
reviewComments(pr).pipe(
|
|
64
|
-
Effect.map(({ comments, reviewThreads }) => {
|
|
66
|
+
Effect.map(({ comments, reviewThreads, reviews }) => {
|
|
65
67
|
const eligibleReviewThreads = reviewThreads.filter(
|
|
66
68
|
(thread) => thread.shouldDisplayThread,
|
|
67
69
|
)
|
|
68
70
|
|
|
69
|
-
if (
|
|
71
|
+
if (
|
|
72
|
+
comments.length === 0 &&
|
|
73
|
+
eligibleReviewThreads.length === 0 &&
|
|
74
|
+
reviews.length === 0
|
|
75
|
+
) {
|
|
70
76
|
return `No review comments found.`
|
|
71
77
|
}
|
|
72
78
|
|
|
@@ -90,6 +96,23 @@ Comments are rendered in XML format.`
|
|
|
90
96
|
${reviewCommentsMd}`
|
|
91
97
|
}
|
|
92
98
|
|
|
99
|
+
if (reviews.length > 0) {
|
|
100
|
+
const reviewsXml = reviews
|
|
101
|
+
.map(
|
|
102
|
+
(review) => `<review author="${review.author.login}">
|
|
103
|
+
<body><![CDATA[${review.body}]]></body>
|
|
104
|
+
</review>`,
|
|
105
|
+
)
|
|
106
|
+
.join("\n")
|
|
107
|
+
content += `
|
|
108
|
+
|
|
109
|
+
## Reviews
|
|
110
|
+
|
|
111
|
+
<reviews>
|
|
112
|
+
${reviewsXml}
|
|
113
|
+
</reviews>`
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
if (comments.length > 0) {
|
|
94
117
|
const generalCommentsXml = comments
|
|
95
118
|
.map((comment) => renderGeneralComment(comment))
|
|
@@ -155,45 +178,54 @@ const renderGeneralComment = (
|
|
|
155
178
|
|
|
156
179
|
// Schema definitions and GraphQL query
|
|
157
180
|
|
|
158
|
-
const
|
|
181
|
+
const PullRequestDataFromJson = Schema.fromJsonString(GithubPullRequestData)
|
|
159
182
|
|
|
160
183
|
const githubReviewCommentsQuery = `
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
isResolved
|
|
172
|
-
comments(first: 100) {
|
|
173
|
-
nodes {
|
|
174
|
-
id
|
|
175
|
-
author { login }
|
|
176
|
-
body
|
|
177
|
-
path
|
|
178
|
-
originalLine
|
|
179
|
-
diffHunk
|
|
180
|
-
createdAt
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
+
query FetchPRComments($owner: String!, $repo: String!, $pr: Int!) {
|
|
185
|
+
repository(owner: $owner, name: $repo) {
|
|
186
|
+
pullRequest(number: $pr) {
|
|
187
|
+
url
|
|
188
|
+
reviewDecision
|
|
189
|
+
reviews(first: 100) {
|
|
190
|
+
nodes {
|
|
191
|
+
id
|
|
192
|
+
author {
|
|
193
|
+
login
|
|
184
194
|
}
|
|
195
|
+
body
|
|
185
196
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
}
|
|
198
|
+
reviewThreads(first: 100) {
|
|
199
|
+
nodes {
|
|
200
|
+
isCollapsed
|
|
201
|
+
isOutdated
|
|
202
|
+
isResolved
|
|
203
|
+
comments(first: 100) {
|
|
204
|
+
nodes {
|
|
189
205
|
id
|
|
206
|
+
author {
|
|
207
|
+
login
|
|
208
|
+
}
|
|
190
209
|
body
|
|
191
|
-
|
|
210
|
+
path
|
|
211
|
+
originalLine
|
|
212
|
+
diffHunk
|
|
192
213
|
createdAt
|
|
193
214
|
}
|
|
194
215
|
}
|
|
195
216
|
}
|
|
196
217
|
}
|
|
218
|
+
comments(first: 100) {
|
|
219
|
+
nodes {
|
|
220
|
+
id
|
|
221
|
+
body
|
|
222
|
+
author {
|
|
223
|
+
login
|
|
224
|
+
}
|
|
225
|
+
createdAt
|
|
226
|
+
}
|
|
227
|
+
}
|
|
197
228
|
}
|
|
198
229
|
}
|
|
230
|
+
}
|
|
199
231
|
`
|
package/src/Settings.ts
CHANGED
|
@@ -20,9 +20,10 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
|
|
|
20
20
|
make: Effect.gen(function* () {
|
|
21
21
|
const kvs = yield* KeyValueStore.KeyValueStore
|
|
22
22
|
const projectKvs = yield* ProjectsKvs
|
|
23
|
-
const store = KeyValueStore.prefix(kvs, "settings.")
|
|
24
23
|
const reactivity = yield* Reactivity.Reactivity
|
|
25
24
|
|
|
25
|
+
const store = KeyValueStore.prefix(kvs, "settings.")
|
|
26
|
+
|
|
26
27
|
const cache = yield* Cache.make({
|
|
27
28
|
lookup(setting: Setting<string, Schema.Codec<any, any>>) {
|
|
28
29
|
const s = KeyValueStore.toSchemaStore(store, setting.schema)
|
|
@@ -63,7 +64,7 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
|
|
|
63
64
|
onSome: (v) => Effect.orDie(s.set(setting.name, v)),
|
|
64
65
|
})
|
|
65
66
|
return reactivity.mutation(
|
|
66
|
-
[`settings
|
|
67
|
+
[`settings:${setting.name}`],
|
|
67
68
|
Effect.andThen(update, setCache),
|
|
68
69
|
)
|
|
69
70
|
}
|
|
@@ -109,7 +110,7 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
|
|
|
109
110
|
onSome: (v) => Effect.orDie(s.set(setting.name, v)),
|
|
110
111
|
})
|
|
111
112
|
yield* reactivity.mutation(
|
|
112
|
-
[`settings.${projectId}
|
|
113
|
+
[`settings.${projectId}:${setting.name}`],
|
|
113
114
|
Effect.andThen(update, setCache),
|
|
114
115
|
)
|
|
115
116
|
},
|
|
@@ -162,7 +163,9 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
|
|
|
162
163
|
> {
|
|
163
164
|
const read = pipe(
|
|
164
165
|
Settings.runtime.atom(Settings.get(setting)),
|
|
165
|
-
atomRuntime.withReactivity(
|
|
166
|
+
atomRuntime.withReactivity({
|
|
167
|
+
settings: [setting.name],
|
|
168
|
+
}),
|
|
166
169
|
)
|
|
167
170
|
const set = Settings.runtime.fn<Option.Option<S["Type"]>>()((value) =>
|
|
168
171
|
Settings.set(setting, value),
|
|
@@ -198,9 +201,9 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
|
|
|
198
201
|
Effect.provideService(CurrentProjectId, options.projectId),
|
|
199
202
|
),
|
|
200
203
|
),
|
|
201
|
-
atomRuntime.withReactivity(
|
|
202
|
-
`settings.${options.projectId}
|
|
203
|
-
|
|
204
|
+
atomRuntime.withReactivity({
|
|
205
|
+
[`settings.${options.projectId}`]: [options.setting.name],
|
|
206
|
+
}),
|
|
204
207
|
)
|
|
205
208
|
const set = Settings.runtime.fn<Option.Option<S["Type"]>>()((value) =>
|
|
206
209
|
Settings.setProject(options.setting, value).pipe(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, Option } from "effect"
|
|
1
|
+
import { Effect, FileSystem, Option, Path } from "effect"
|
|
2
2
|
import { Command } from "effect/unstable/cli"
|
|
3
3
|
import { allProjects, getAllProjects, selectProject } from "../../Projects.ts"
|
|
4
4
|
import { Settings } from "../../Settings.ts"
|
|
@@ -8,6 +8,8 @@ export const commandProjectsRm = Command.make("rm").pipe(
|
|
|
8
8
|
Command.withDescription("Remove a project"),
|
|
9
9
|
Command.withHandler(
|
|
10
10
|
Effect.fnUntraced(function* () {
|
|
11
|
+
const fs = yield* FileSystem.FileSystem
|
|
12
|
+
const pathService = yield* Path.Path
|
|
11
13
|
const projects = yield* getAllProjects
|
|
12
14
|
if (projects.length === 0) {
|
|
13
15
|
return yield* Effect.log("There are no projects to remove.")
|
|
@@ -15,6 +17,14 @@ export const commandProjectsRm = Command.make("rm").pipe(
|
|
|
15
17
|
const project = yield* selectProject
|
|
16
18
|
const newProjects = projects.filter((p) => p.id !== project.id)
|
|
17
19
|
yield* Settings.set(allProjects, Option.some(newProjects))
|
|
20
|
+
const kvsPath = pathService.join(
|
|
21
|
+
".lalph",
|
|
22
|
+
"projects",
|
|
23
|
+
encodeURIComponent(project.id),
|
|
24
|
+
)
|
|
25
|
+
if (yield* fs.exists(kvsPath)) {
|
|
26
|
+
yield* fs.remove(kvsPath)
|
|
27
|
+
}
|
|
18
28
|
}),
|
|
19
29
|
),
|
|
20
30
|
Command.provide(Settings.layer),
|
|
@@ -1,62 +1,70 @@
|
|
|
1
1
|
import * as S from "effect/Schema"
|
|
2
2
|
|
|
3
|
-
export class Author extends S.Class<Author>("
|
|
3
|
+
export class Author extends S.Class<Author>("Author")({
|
|
4
4
|
login: S.String,
|
|
5
5
|
}) {}
|
|
6
6
|
|
|
7
|
-
export class Comment extends S.Class<Comment>("
|
|
7
|
+
export class Comment extends S.Class<Comment>("Comment")({
|
|
8
8
|
id: S.String,
|
|
9
9
|
body: S.String,
|
|
10
10
|
author: Author,
|
|
11
|
-
|
|
12
|
-
}) {}
|
|
13
|
-
|
|
14
|
-
export class CommentsEdge extends S.Class<CommentsEdge>("github/CommentsEdge")({
|
|
15
|
-
node: Comment,
|
|
11
|
+
createdAt: S.String,
|
|
16
12
|
}) {}
|
|
17
13
|
|
|
18
14
|
export class PullRequestComments extends S.Class<PullRequestComments>(
|
|
19
15
|
"PullRequestComments",
|
|
20
16
|
)({
|
|
21
|
-
|
|
17
|
+
nodes: S.Array(Comment),
|
|
22
18
|
}) {}
|
|
23
19
|
|
|
24
|
-
export class PullRequest extends S.Class<PullRequest>("
|
|
20
|
+
export class PullRequest extends S.Class<PullRequest>("PullRequest")({
|
|
25
21
|
url: S.String,
|
|
22
|
+
reviewDecision: S.Null,
|
|
23
|
+
reviews: S.suspend(() => Reviews),
|
|
26
24
|
reviewThreads: S.suspend(() => ReviewThreads),
|
|
27
25
|
comments: PullRequestComments,
|
|
28
26
|
}) {}
|
|
29
27
|
|
|
30
|
-
export class Repository extends S.Class<Repository>("
|
|
28
|
+
export class Repository extends S.Class<Repository>("Repository")({
|
|
31
29
|
pullRequest: PullRequest,
|
|
32
30
|
}) {}
|
|
33
31
|
|
|
34
|
-
export class Data extends S.Class<Data>("
|
|
32
|
+
export class Data extends S.Class<Data>("Data")({
|
|
35
33
|
repository: Repository,
|
|
36
34
|
}) {}
|
|
37
35
|
|
|
38
|
-
export class
|
|
36
|
+
export class GithubPullRequestData extends S.Class<GithubPullRequestData>(
|
|
37
|
+
"GithubPullRequestData",
|
|
38
|
+
)({
|
|
39
39
|
data: Data,
|
|
40
40
|
}) {}
|
|
41
41
|
|
|
42
|
-
export class
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
export class Review extends S.Class<Review>("Review")({
|
|
43
|
+
id: S.String,
|
|
44
|
+
author: Author,
|
|
45
|
+
body: S.String,
|
|
46
|
+
}) {}
|
|
47
|
+
|
|
48
|
+
export class Reviews extends S.Class<Reviews>("Reviews")({
|
|
49
|
+
nodes: S.Array(Review),
|
|
50
|
+
}) {}
|
|
51
|
+
|
|
52
|
+
export class ReviewComment extends S.Class<ReviewComment>("ReviewComment")({
|
|
45
53
|
id: S.String,
|
|
46
54
|
author: Author,
|
|
47
55
|
body: S.String,
|
|
48
56
|
path: S.String,
|
|
49
|
-
originalLine: S.
|
|
57
|
+
originalLine: S.Number,
|
|
50
58
|
diffHunk: S.String,
|
|
51
|
-
|
|
59
|
+
createdAt: S.String,
|
|
52
60
|
}) {}
|
|
53
61
|
|
|
54
|
-
export class NodeComments extends S.Class<NodeComments>("
|
|
62
|
+
export class NodeComments extends S.Class<NodeComments>("NodeComments")({
|
|
55
63
|
nodes: S.Array(ReviewComment),
|
|
56
64
|
}) {}
|
|
57
65
|
|
|
58
|
-
export class
|
|
59
|
-
"
|
|
66
|
+
export class ReviewThreadsNode extends S.Class<ReviewThreadsNode>(
|
|
67
|
+
"ReviewThreadsNode",
|
|
60
68
|
)({
|
|
61
69
|
isCollapsed: S.Boolean,
|
|
62
70
|
isOutdated: S.Boolean,
|
|
@@ -67,14 +75,6 @@ export class ReviewThreadNode extends S.Class<ReviewThreadNode>(
|
|
|
67
75
|
readonly shouldDisplayThread = !this.isCollapsed && !this.isOutdated
|
|
68
76
|
}
|
|
69
77
|
|
|
70
|
-
export class
|
|
71
|
-
|
|
72
|
-
)({
|
|
73
|
-
node: ReviewThreadNode,
|
|
74
|
-
}) {}
|
|
75
|
-
|
|
76
|
-
export class ReviewThreads extends S.Class<ReviewThreads>(
|
|
77
|
-
"github/ReviewThreads",
|
|
78
|
-
)({
|
|
79
|
-
edges: S.Array(ReviewThreadsEdge),
|
|
78
|
+
export class ReviewThreads extends S.Class<ReviewThreads>("ReviewThreads")({
|
|
79
|
+
nodes: S.Array(ReviewThreadsNode),
|
|
80
80
|
}) {}
|