@markjaquith/agency 2.56.1 → 2.58.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/README.md +35 -9
- package/package.json +1 -1
- package/src/cli-parser.test.ts +8 -0
- package/src/cli-parser.ts +7 -1
- package/src/commands/archive.test.ts +39 -1
- package/src/commands/archive.ts +25 -2
- package/src/commands/pr.test.ts +40 -1
- package/src/commands/pr.ts +6 -0
- package/src/commands/restore.test.ts +8 -0
- package/src/services/ArchiveBulkService.test.ts +485 -0
- package/src/services/ArchiveService.test.ts +164 -1
- package/src/services/ArchiveService.ts +447 -29
- package/src/services/ContextService.ts +27 -10
- package/src/services/DoctorService.ts +6 -11
- package/src/services/GraphService.ts +60 -102
- package/src/services/PullRequestService.test.ts +45 -1
- package/src/services/PullRequestService.ts +18 -1
- package/src/services/PushService.test.ts +10 -13
- package/src/services/RepositoryService.test.ts +6 -1
- package/src/services/RepositoryService.ts +57 -114
- package/src/services/ReviewService.test.ts +2 -2
- package/src/services/ReviewService.ts +47 -28
- package/src/services/SyncService.test.ts +17 -2
- package/src/services/SyncService.ts +41 -18
- package/src/services/TaskService.ts +18 -2
- package/src/services/VcsMigrationService.test.ts +58 -1
- package/src/services/VcsMigrationService.ts +9 -0
- package/src/services/VersionControlService.test.ts +38 -0
- package/src/services/VersionControlService.ts +211 -2
- package/src/services/WorktreeService.test.ts +4 -0
- package/src/services/WorktreeService.ts +1 -5
- package/src/vcs-status-fast.ts +3 -8
- package/src/workbase/delivery-command.test.ts +11 -2
- package/src/workbase/delivery-command.ts +5 -1
|
@@ -12,11 +12,35 @@ export interface RegisteredWorkspace {
|
|
|
12
12
|
readonly dirty?: boolean
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
interface PullRequestDefaults {
|
|
16
|
+
readonly title: string
|
|
17
|
+
readonly body: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface RepositoryInspection {
|
|
21
|
+
readonly kind: "bare" | "repository"
|
|
22
|
+
readonly remote: string | null
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
export interface VersionControlBackend {
|
|
16
26
|
readonly kind: VersionControlKind
|
|
27
|
+
readonly cloneRepository: (
|
|
28
|
+
source: string,
|
|
29
|
+
destination: string,
|
|
30
|
+
) => Effect.Effect<void, unknown, any>
|
|
17
31
|
readonly initializeRepository: (
|
|
18
32
|
path: string,
|
|
19
33
|
) => Effect.Effect<void, unknown, any>
|
|
34
|
+
readonly inspectRepository: (
|
|
35
|
+
path: string,
|
|
36
|
+
) => Effect.Effect<RepositoryInspection | null, unknown, any>
|
|
37
|
+
readonly gitEnvironment: (
|
|
38
|
+
path: string,
|
|
39
|
+
) => Effect.Effect<Record<string, string>, unknown, any>
|
|
40
|
+
readonly pullRequestDefaults: (
|
|
41
|
+
workspacePath: string,
|
|
42
|
+
base: string,
|
|
43
|
+
) => Effect.Effect<PullRequestDefaults | null, unknown, any>
|
|
20
44
|
readonly listWorkspaces: (
|
|
21
45
|
repositoryPath: string,
|
|
22
46
|
) => Effect.Effect<readonly RegisteredWorkspace[], unknown, any>
|
|
@@ -59,6 +83,11 @@ export interface VersionControlBackend {
|
|
|
59
83
|
repositoryPath: string,
|
|
60
84
|
remote: string,
|
|
61
85
|
) => Effect.Effect<string | null, unknown, any>
|
|
86
|
+
readonly setRemoteUrl: (
|
|
87
|
+
repositoryPath: string,
|
|
88
|
+
remote: string,
|
|
89
|
+
url: string | null,
|
|
90
|
+
) => Effect.Effect<void, unknown, any>
|
|
62
91
|
}
|
|
63
92
|
|
|
64
93
|
class VersionControlError extends Data.TaggedError("VersionControlError")<{
|
|
@@ -122,7 +151,43 @@ export class GitVersionControlService extends Effect.Service<GitVersionControlSe
|
|
|
122
151
|
sync: () =>
|
|
123
152
|
({
|
|
124
153
|
kind: "git",
|
|
154
|
+
cloneRepository: (source, destination) =>
|
|
155
|
+
Effect.gen(function* () {
|
|
156
|
+
const fs = yield* FileSystemService
|
|
157
|
+
yield* requireSuccess(
|
|
158
|
+
"Failed to clone Git repository",
|
|
159
|
+
fs.runCommand(
|
|
160
|
+
["git", "clone", "--bare", "--", source, destination],
|
|
161
|
+
{
|
|
162
|
+
captureOutput: true,
|
|
163
|
+
},
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
}),
|
|
125
167
|
initializeRepository: () => Effect.void,
|
|
168
|
+
inspectRepository: (path) =>
|
|
169
|
+
Effect.gen(function* () {
|
|
170
|
+
const fs = yield* FileSystemService
|
|
171
|
+
const valid = yield* fs.runCommand(
|
|
172
|
+
["git", "-C", path, "rev-parse", "--git-dir"],
|
|
173
|
+
{ captureOutput: true },
|
|
174
|
+
)
|
|
175
|
+
if (valid.exitCode !== 0) return null
|
|
176
|
+
const bare = yield* fs.runCommand(
|
|
177
|
+
["git", "-C", path, "rev-parse", "--is-bare-repository"],
|
|
178
|
+
{ captureOutput: true },
|
|
179
|
+
)
|
|
180
|
+
const remote = yield* fs.runCommand(
|
|
181
|
+
["git", "-C", path, "remote", "get-url", "origin"],
|
|
182
|
+
{ captureOutput: true },
|
|
183
|
+
)
|
|
184
|
+
return {
|
|
185
|
+
kind: bare.stdout.trim() === "true" ? "bare" : "repository",
|
|
186
|
+
remote: remote.exitCode === 0 ? remote.stdout.trim() : null,
|
|
187
|
+
} as const
|
|
188
|
+
}),
|
|
189
|
+
gitEnvironment: () => Effect.succeed({}),
|
|
190
|
+
pullRequestDefaults: () => Effect.succeed(null),
|
|
126
191
|
listWorkspaces: (repositoryPath) =>
|
|
127
192
|
Effect.gen(function* () {
|
|
128
193
|
const fs = yield* FileSystemService
|
|
@@ -272,6 +337,30 @@ export class GitVersionControlService extends Effect.Service<GitVersionControlSe
|
|
|
272
337
|
)
|
|
273
338
|
return result.exitCode === 0 ? result.stdout.trim() : null
|
|
274
339
|
}),
|
|
340
|
+
setRemoteUrl: (repositoryPath, remote, url) =>
|
|
341
|
+
Effect.gen(function* () {
|
|
342
|
+
const fs = yield* FileSystemService
|
|
343
|
+
const previous = yield* fs.runCommand(
|
|
344
|
+
["git", "-C", repositoryPath, "remote", "get-url", remote],
|
|
345
|
+
{ captureOutput: true },
|
|
346
|
+
)
|
|
347
|
+
const command =
|
|
348
|
+
url === null
|
|
349
|
+
? ["git", "-C", repositoryPath, "remote", "remove", remote]
|
|
350
|
+
: [
|
|
351
|
+
"git",
|
|
352
|
+
"-C",
|
|
353
|
+
repositoryPath,
|
|
354
|
+
"remote",
|
|
355
|
+
previous.exitCode === 0 ? "set-url" : "add",
|
|
356
|
+
remote,
|
|
357
|
+
url,
|
|
358
|
+
]
|
|
359
|
+
yield* requireSuccess(
|
|
360
|
+
`Failed to update Git remote '${remote}'`,
|
|
361
|
+
fs.runCommand(command, { captureOutput: true }),
|
|
362
|
+
)
|
|
363
|
+
}),
|
|
275
364
|
}) satisfies VersionControlBackend,
|
|
276
365
|
},
|
|
277
366
|
) {}
|
|
@@ -290,6 +379,25 @@ export class JjVersionControlService extends Effect.Service<JjVersionControlServ
|
|
|
290
379
|
sync: () =>
|
|
291
380
|
({
|
|
292
381
|
kind: "jj",
|
|
382
|
+
cloneRepository: (source, destination) =>
|
|
383
|
+
Effect.gen(function* () {
|
|
384
|
+
const fs = yield* FileSystemService
|
|
385
|
+
yield* requireSuccess(
|
|
386
|
+
"Failed to clone jj repository",
|
|
387
|
+
fs.runCommand(
|
|
388
|
+
[
|
|
389
|
+
"jj",
|
|
390
|
+
"git",
|
|
391
|
+
"clone",
|
|
392
|
+
"--no-colocate",
|
|
393
|
+
"--",
|
|
394
|
+
source,
|
|
395
|
+
destination,
|
|
396
|
+
],
|
|
397
|
+
{ captureOutput: true },
|
|
398
|
+
),
|
|
399
|
+
)
|
|
400
|
+
}),
|
|
293
401
|
initializeRepository: (path) =>
|
|
294
402
|
Effect.gen(function* () {
|
|
295
403
|
const fs = yield* FileSystemService
|
|
@@ -301,6 +409,72 @@ export class JjVersionControlService extends Effect.Service<JjVersionControlServ
|
|
|
301
409
|
}),
|
|
302
410
|
)
|
|
303
411
|
}),
|
|
412
|
+
inspectRepository: (path) =>
|
|
413
|
+
Effect.gen(function* () {
|
|
414
|
+
const fs = yield* FileSystemService
|
|
415
|
+
const root = yield* fs.runCommand(jjCommand(path, ["root"]), {
|
|
416
|
+
captureOutput: true,
|
|
417
|
+
})
|
|
418
|
+
if (root.exitCode !== 0) return null
|
|
419
|
+
const remote = yield* fs.runCommand(
|
|
420
|
+
jjCommand(path, ["git", "remote", "list"]),
|
|
421
|
+
{ captureOutput: true },
|
|
422
|
+
)
|
|
423
|
+
return {
|
|
424
|
+
kind: "repository",
|
|
425
|
+
remote:
|
|
426
|
+
remote.exitCode === 0
|
|
427
|
+
? (remote.stdout
|
|
428
|
+
.split("\n")
|
|
429
|
+
.find((line) => line.startsWith("origin "))
|
|
430
|
+
?.slice("origin ".length)
|
|
431
|
+
.trim() ?? null)
|
|
432
|
+
: null,
|
|
433
|
+
} as const
|
|
434
|
+
}),
|
|
435
|
+
gitEnvironment: (path) =>
|
|
436
|
+
Effect.gen(function* () {
|
|
437
|
+
const fs = yield* FileSystemService
|
|
438
|
+
const result = yield* requireSuccess(
|
|
439
|
+
"Failed to locate jj backing Git repository",
|
|
440
|
+
fs.runCommand(jjCommand(path, ["git", "root"]), {
|
|
441
|
+
captureOutput: true,
|
|
442
|
+
}),
|
|
443
|
+
)
|
|
444
|
+
return { GIT_DIR: result.stdout.trim() }
|
|
445
|
+
}),
|
|
446
|
+
pullRequestDefaults: (workspacePath, base) =>
|
|
447
|
+
Effect.gen(function* () {
|
|
448
|
+
const fs = yield* FileSystemService
|
|
449
|
+
const result = yield* fs.runCommand(
|
|
450
|
+
jjCommand(workspacePath, [
|
|
451
|
+
"log",
|
|
452
|
+
"--ignore-working-copy",
|
|
453
|
+
"--no-graph",
|
|
454
|
+
"-r",
|
|
455
|
+
`${base}..@-`,
|
|
456
|
+
"-T",
|
|
457
|
+
'description.first_line() ++ "\\n"',
|
|
458
|
+
]),
|
|
459
|
+
{ captureOutput: true },
|
|
460
|
+
)
|
|
461
|
+
if (result.exitCode !== 0) return null
|
|
462
|
+
const commits = result.stdout
|
|
463
|
+
.split("\n")
|
|
464
|
+
.map((line) => line.trim())
|
|
465
|
+
.filter(Boolean)
|
|
466
|
+
if (commits.length === 0) return null
|
|
467
|
+
return {
|
|
468
|
+
title: commits.at(-1)!,
|
|
469
|
+
body:
|
|
470
|
+
commits.length === 1
|
|
471
|
+
? ""
|
|
472
|
+
: `Commits:\n${commits
|
|
473
|
+
.toReversed()
|
|
474
|
+
.map((commit) => `- ${commit}`)
|
|
475
|
+
.join("\n")}`,
|
|
476
|
+
} satisfies PullRequestDefaults
|
|
477
|
+
}),
|
|
304
478
|
listWorkspaces: (repositoryPath) =>
|
|
305
479
|
Effect.gen(function* () {
|
|
306
480
|
const fs = yield* FileSystemService
|
|
@@ -456,6 +630,20 @@ export class JjVersionControlService extends Effect.Service<JjVersionControlServ
|
|
|
456
630
|
push: (workspacePath, remote, branch) =>
|
|
457
631
|
Effect.gen(function* () {
|
|
458
632
|
const fs = yield* FileSystemService
|
|
633
|
+
yield* requireSuccess(
|
|
634
|
+
"Failed to set jj delivery bookmark",
|
|
635
|
+
fs.runCommand(
|
|
636
|
+
jjCommand(workspacePath, [
|
|
637
|
+
"bookmark",
|
|
638
|
+
"set",
|
|
639
|
+
"--allow-backwards",
|
|
640
|
+
branch,
|
|
641
|
+
"-r",
|
|
642
|
+
"@-",
|
|
643
|
+
]),
|
|
644
|
+
{ captureOutput: true },
|
|
645
|
+
),
|
|
646
|
+
)
|
|
459
647
|
yield* requireSuccess(
|
|
460
648
|
"Failed to push branch",
|
|
461
649
|
fs.runCommand(
|
|
@@ -464,8 +652,8 @@ export class JjVersionControlService extends Effect.Service<JjVersionControlServ
|
|
|
464
652
|
"push",
|
|
465
653
|
"--remote",
|
|
466
654
|
remote,
|
|
467
|
-
"--
|
|
468
|
-
|
|
655
|
+
"--bookmark",
|
|
656
|
+
branch,
|
|
469
657
|
]),
|
|
470
658
|
{ captureOutput: true },
|
|
471
659
|
),
|
|
@@ -487,6 +675,27 @@ export class JjVersionControlService extends Effect.Service<JjVersionControlServ
|
|
|
487
675
|
.trim() ?? null
|
|
488
676
|
)
|
|
489
677
|
}),
|
|
678
|
+
setRemoteUrl: (repositoryPath, remote, url) =>
|
|
679
|
+
Effect.gen(function* () {
|
|
680
|
+
const fs = yield* FileSystemService
|
|
681
|
+
const previous = yield* fs.runCommand(
|
|
682
|
+
jjCommand(repositoryPath, ["git", "remote", "list"]),
|
|
683
|
+
{ captureOutput: true },
|
|
684
|
+
)
|
|
685
|
+
const exists = previous.stdout
|
|
686
|
+
.split("\n")
|
|
687
|
+
.some((line) => line.startsWith(`${remote} `))
|
|
688
|
+
const args =
|
|
689
|
+
url === null
|
|
690
|
+
? ["git", "remote", "remove", remote]
|
|
691
|
+
: ["git", "remote", exists ? "set-url" : "add", remote, url]
|
|
692
|
+
yield* requireSuccess(
|
|
693
|
+
`Failed to update jj Git remote '${remote}'`,
|
|
694
|
+
fs.runCommand(jjCommand(repositoryPath, args), {
|
|
695
|
+
captureOutput: true,
|
|
696
|
+
}),
|
|
697
|
+
)
|
|
698
|
+
}),
|
|
490
699
|
}) satisfies VersionControlBackend,
|
|
491
700
|
},
|
|
492
701
|
) {}
|
|
@@ -276,6 +276,10 @@ describe("WorktreeService", () => {
|
|
|
276
276
|
expect(
|
|
277
277
|
await Bun.file(join(workspace.writablePath!, "post-checkout")).text(),
|
|
278
278
|
).toBe("jj:writable")
|
|
279
|
+
await jj(
|
|
280
|
+
["commit", "-m", "record post-checkout output"],
|
|
281
|
+
workspace.writablePath!,
|
|
282
|
+
)
|
|
279
283
|
const inspection = await runTestEffect(
|
|
280
284
|
WorktreeService.pipe(
|
|
281
285
|
Effect.flatMap((service) =>
|
|
@@ -449,11 +449,7 @@ const inspectExecution = (
|
|
|
449
449
|
: yield* backend.workspaceHead(checkoutPath)
|
|
450
450
|
: null
|
|
451
451
|
const dirty =
|
|
452
|
-
exists && atPath
|
|
453
|
-
? atPath.dirty !== undefined
|
|
454
|
-
? atPath.dirty
|
|
455
|
-
: yield* backend.workspaceDirty(checkoutPath)
|
|
456
|
-
: null
|
|
452
|
+
exists && atPath ? yield* backend.workspaceDirty(checkoutPath) : null
|
|
457
453
|
const expectedCommit =
|
|
458
454
|
"branch" in checkout && context?.skipWritableRevisionResolution
|
|
459
455
|
? actualCommit
|
package/src/vcs-status-fast.ts
CHANGED
|
@@ -132,19 +132,14 @@ const inspectRepository = async (root: string, alias: string) => {
|
|
|
132
132
|
return null
|
|
133
133
|
}
|
|
134
134
|
if (!stats.isDirectory() && !stats.isSymbolicLink()) return null
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
run(["git", "-C", path, "rev-parse", "--is-bare-repository"]),
|
|
138
|
-
])
|
|
139
|
-
if (git.exitCode !== 0 || bare.exitCode !== 0) return null
|
|
135
|
+
const jj = await run(["jj", "-R", path, "root"])
|
|
136
|
+
if (jj.exitCode !== 0) return null
|
|
140
137
|
return {
|
|
141
138
|
alias,
|
|
142
139
|
path,
|
|
143
140
|
kind: stats.isSymbolicLink()
|
|
144
141
|
? ("symlink" as const)
|
|
145
|
-
:
|
|
146
|
-
? ("bare" as const)
|
|
147
|
-
: ("repository" as const),
|
|
142
|
+
: ("repository" as const),
|
|
148
143
|
initialized: await directoryExists(join(path, ".jj")),
|
|
149
144
|
}
|
|
150
145
|
}
|
|
@@ -39,12 +39,21 @@ describe("delivery commands", () => {
|
|
|
39
39
|
repository: "example/agency",
|
|
40
40
|
draft: false,
|
|
41
41
|
} as const
|
|
42
|
-
expect(
|
|
42
|
+
expect(
|
|
43
|
+
resolveGitHubCreateCommand({
|
|
44
|
+
...input,
|
|
45
|
+
vcs: "jj",
|
|
46
|
+
defaults: { title: "Add example", body: "Details" },
|
|
47
|
+
}),
|
|
48
|
+
).toEqual({
|
|
43
49
|
argv: [
|
|
44
50
|
"gh",
|
|
45
51
|
"pr",
|
|
46
52
|
"create",
|
|
47
|
-
"--
|
|
53
|
+
"--title",
|
|
54
|
+
"Add example",
|
|
55
|
+
"--body",
|
|
56
|
+
"Details",
|
|
48
57
|
"--base",
|
|
49
58
|
"main",
|
|
50
59
|
"--head",
|
|
@@ -24,18 +24,22 @@ export const resolveGitHubCreateCommand = ({
|
|
|
24
24
|
repository,
|
|
25
25
|
draft,
|
|
26
26
|
vcs,
|
|
27
|
+
defaults,
|
|
27
28
|
}: {
|
|
28
29
|
readonly base: string
|
|
29
30
|
readonly branch: string
|
|
30
31
|
readonly repository: string
|
|
31
32
|
readonly draft: boolean
|
|
32
33
|
readonly vcs: "git" | "jj"
|
|
34
|
+
readonly defaults?: { readonly title: string; readonly body: string }
|
|
33
35
|
}) => ({
|
|
34
36
|
argv: [
|
|
35
37
|
"gh",
|
|
36
38
|
"pr",
|
|
37
39
|
"create",
|
|
38
|
-
|
|
40
|
+
...(defaults
|
|
41
|
+
? ["--title", defaults.title, "--body", defaults.body]
|
|
42
|
+
: ["--fill"]),
|
|
39
43
|
"--base",
|
|
40
44
|
base,
|
|
41
45
|
...(vcs === "jj" ? ["--head", branch, "--repo", repository] : []),
|