@markjaquith/agency 2.56.0 → 2.57.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 +14 -3
- package/package.json +2 -1
- package/src/commands/pr.test.ts +40 -1
- package/src/commands/pr.ts +6 -0
- package/src/commands/sync.test.ts +27 -0
- package/src/commands/sync.ts +40 -5
- 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 +132 -2
- package/src/services/SyncService.ts +230 -75
- 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/vcs-status-fast.ts +3 -8
- package/src/workbase/delivery-command.test.ts +11 -2
- package/src/workbase/delivery-command.ts +5 -1
|
@@ -74,21 +74,16 @@ describe("PushService", () => {
|
|
|
74
74
|
await requireCommand(["git", "commit", "-m", "Initial commit"], seed)
|
|
75
75
|
await requireCommand(["git", "remote", "add", "origin", remote], seed)
|
|
76
76
|
await requireCommand(["git", "push", "-u", "origin", "main"], seed)
|
|
77
|
-
await requireCommand(["git", "clone", "--bare", remote, repository])
|
|
78
77
|
if (vcs === "jj") {
|
|
79
|
-
await requireCommand([
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
["jj", "git", "fetch", "--remote", "origin"],
|
|
86
|
-
repository,
|
|
87
|
-
)
|
|
88
|
-
await requireCommand(
|
|
89
|
-
["jj", "bookmark", "create", "main", "-r", "main@origin"],
|
|
78
|
+
await requireCommand([
|
|
79
|
+
"jj",
|
|
80
|
+
"git",
|
|
81
|
+
"clone",
|
|
82
|
+
"--no-colocate",
|
|
83
|
+
remote,
|
|
90
84
|
repository,
|
|
91
|
-
)
|
|
85
|
+
])
|
|
86
|
+
expect(await Bun.file(join(repository, ".git")).exists()).toBe(false)
|
|
92
87
|
await requireCommand(
|
|
93
88
|
["jj", "config", "set", "--repo", "user.name", "Agency Test"],
|
|
94
89
|
repository,
|
|
@@ -97,6 +92,8 @@ describe("PushService", () => {
|
|
|
97
92
|
["jj", "config", "set", "--repo", "user.email", "agency@example.com"],
|
|
98
93
|
repository,
|
|
99
94
|
)
|
|
95
|
+
} else {
|
|
96
|
+
await requireCommand(["git", "clone", "--bare", remote, repository])
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
await runTestEffect(
|
|
@@ -119,13 +119,18 @@ describe("RepositoryService", () => {
|
|
|
119
119
|
),
|
|
120
120
|
)
|
|
121
121
|
expect((await stat(join(destination, ".jj"))).isDirectory()).toBe(true)
|
|
122
|
+
expect(await Bun.file(join(destination, ".git")).exists()).toBe(false)
|
|
122
123
|
expect(
|
|
123
124
|
await runTestEffect(
|
|
124
125
|
RepositoryService.pipe(
|
|
125
126
|
Effect.flatMap((service) => service.show("agency", root)),
|
|
126
127
|
),
|
|
127
128
|
),
|
|
128
|
-
).toMatchObject({
|
|
129
|
+
).toMatchObject({
|
|
130
|
+
kind: "repository",
|
|
131
|
+
remote: portableRemote("jj-agency"),
|
|
132
|
+
states: ["declared", "materialized"],
|
|
133
|
+
})
|
|
129
134
|
})
|
|
130
135
|
|
|
131
136
|
test("links an existing repository", async () => {
|
|
@@ -171,9 +171,12 @@ const inspectRemote = (path: string) =>
|
|
|
171
171
|
return result.exitCode === 0 ? result.stdout.trim() : null
|
|
172
172
|
})
|
|
173
173
|
|
|
174
|
-
const portableRemote = (path: string) =>
|
|
174
|
+
const portableRemote = (path: string, backend?: VersionControlBackend) =>
|
|
175
175
|
Effect.gen(function* () {
|
|
176
|
-
const
|
|
176
|
+
const backendRemote = backend
|
|
177
|
+
? yield* backend.remoteUrl(path, "origin")
|
|
178
|
+
: null
|
|
179
|
+
const remote = backendRemote ?? (yield* inspectRemote(path))
|
|
177
180
|
if (!remote) {
|
|
178
181
|
return yield* new RepositoryError({
|
|
179
182
|
message: `Repository '${path}' has no portable origin remote`,
|
|
@@ -219,7 +222,7 @@ const removalBlockers = (
|
|
|
219
222
|
Effect.gen(function* () {
|
|
220
223
|
const fs = yield* FileSystemService
|
|
221
224
|
const graph = yield* GraphService
|
|
222
|
-
const report = yield* graph.get({ cwd: startPath })
|
|
225
|
+
const report = yield* graph.get({ cwd: startPath, backend })
|
|
223
226
|
const repositoryId = `repository:${repository.alias}`
|
|
224
227
|
const references = report.edges
|
|
225
228
|
.filter(
|
|
@@ -373,50 +376,30 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
373
376
|
: resolve(startPath, remote)
|
|
374
377
|
const declaredRemote = inputIsPortable
|
|
375
378
|
? yield* validateRemote(remote)
|
|
376
|
-
: yield* portableRemote(cloneSource)
|
|
379
|
+
: yield* portableRemote(cloneSource, backend)
|
|
377
380
|
const staging = join(
|
|
378
381
|
state.root,
|
|
379
382
|
"repos",
|
|
380
383
|
`.agency-clone-${validAlias}-${process.pid}-${Date.now()}`,
|
|
381
384
|
)
|
|
382
385
|
yield* fs.createDirectory(join(state.root, "repos"))
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
386
|
+
yield* backend.cloneRepository(cloneSource, staging).pipe(
|
|
387
|
+
Effect.catchAll((cause) =>
|
|
388
|
+
fs.deleteDirectory(staging).pipe(
|
|
389
|
+
Effect.ignore,
|
|
390
|
+
Effect.zipRight(
|
|
391
|
+
Effect.fail(
|
|
392
|
+
new RepositoryError({
|
|
393
|
+
message: `Failed to clone repository '${remote}': ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
394
|
+
cause,
|
|
395
|
+
}),
|
|
396
|
+
),
|
|
397
|
+
),
|
|
398
|
+
),
|
|
399
|
+
),
|
|
393
400
|
)
|
|
394
|
-
if (cloned.exitCode !== 0) {
|
|
395
|
-
yield* fs.deleteDirectory(staging).pipe(Effect.ignore)
|
|
396
|
-
return yield* new RepositoryError({
|
|
397
|
-
message: `Failed to clone repository '${remote}': ${cloned.stderr.trim()}`,
|
|
398
|
-
})
|
|
399
|
-
}
|
|
400
|
-
yield* backend.initializeRepository(staging)
|
|
401
401
|
if (declaredRemote !== remote) {
|
|
402
|
-
|
|
403
|
-
[
|
|
404
|
-
"git",
|
|
405
|
-
"-C",
|
|
406
|
-
staging,
|
|
407
|
-
"remote",
|
|
408
|
-
"set-url",
|
|
409
|
-
"origin",
|
|
410
|
-
declaredRemote,
|
|
411
|
-
],
|
|
412
|
-
{ captureOutput: true },
|
|
413
|
-
)
|
|
414
|
-
if (setRemote.exitCode !== 0) {
|
|
415
|
-
yield* fs.deleteDirectory(staging).pipe(Effect.ignore)
|
|
416
|
-
return yield* new RepositoryError({
|
|
417
|
-
message: `Failed to record portable remote for repository '${validAlias}': ${setRemote.stderr.trim()}`,
|
|
418
|
-
})
|
|
419
|
-
}
|
|
402
|
+
yield* backend.setRemoteUrl(staging, "origin", declaredRemote)
|
|
420
403
|
}
|
|
421
404
|
const config = withDeclarations(state.config, {
|
|
422
405
|
...(state.config.repositories ?? {}),
|
|
@@ -465,19 +448,21 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
465
448
|
message: `Repository path does not exist: ${resolvedTarget}`,
|
|
466
449
|
})
|
|
467
450
|
}
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
{ captureOutput: true },
|
|
471
|
-
)
|
|
472
|
-
if (git.exitCode !== 0) {
|
|
451
|
+
const inspection = yield* backend.inspectRepository(resolvedTarget)
|
|
452
|
+
if (!inspection && backend.kind === "git") {
|
|
473
453
|
return yield* new RepositoryError({
|
|
474
|
-
message: `Path is not a
|
|
454
|
+
message: `Path is not a ${backend.kind} repository: ${resolvedTarget}`,
|
|
475
455
|
})
|
|
476
456
|
}
|
|
477
457
|
yield* backend.initializeRepository(resolvedTarget)
|
|
458
|
+
if (!(yield* backend.inspectRepository(resolvedTarget))) {
|
|
459
|
+
return yield* new RepositoryError({
|
|
460
|
+
message: `Path is not a ${backend.kind} repository: ${resolvedTarget}`,
|
|
461
|
+
})
|
|
462
|
+
}
|
|
478
463
|
const declaredRemote =
|
|
479
464
|
state.config.repositories?.[validAlias]?.remote ??
|
|
480
|
-
(yield* portableRemote(resolvedTarget))
|
|
465
|
+
(yield* portableRemote(resolvedTarget, backend))
|
|
481
466
|
const staging = join(
|
|
482
467
|
state.root,
|
|
483
468
|
"repos",
|
|
@@ -522,6 +507,8 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
522
507
|
const { root, config } = yield* WorkbaseService.pipe(
|
|
523
508
|
Effect.flatMap((service) => service.loadConfig(startPath)),
|
|
524
509
|
)
|
|
510
|
+
const versionControl = yield* VersionControlService
|
|
511
|
+
const backend = yield* versionControl.forWorkbase(root)
|
|
525
512
|
const reposPath = join(root, "repos")
|
|
526
513
|
const entries = (yield* fs.isDirectory(reposPath))
|
|
527
514
|
? (yield* fs.readDirectory(reposPath)).filter(
|
|
@@ -569,20 +556,12 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
569
556
|
const target = entry.isSymlink
|
|
570
557
|
? yield* fs.readSymlinkTarget(path)
|
|
571
558
|
: null
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
{ captureOutput: true },
|
|
575
|
-
)
|
|
576
|
-
const bare = yield* fs.runCommand(
|
|
577
|
-
["git", "-C", path, "rev-parse", "--is-bare-repository"],
|
|
578
|
-
{ captureOutput: true },
|
|
579
|
-
)
|
|
580
|
-
const remote =
|
|
581
|
-
git.exitCode === 0 ? yield* inspectRemote(path) : null
|
|
559
|
+
const inspection = yield* backend.inspectRepository(path)
|
|
560
|
+
const remote = inspection?.remote ?? null
|
|
582
561
|
const states: RepositoryState[] = []
|
|
583
562
|
if (declaredRemote) states.push("declared")
|
|
584
563
|
states.push(entry.isSymlink ? "linked" : "materialized")
|
|
585
|
-
if (
|
|
564
|
+
if (!inspection) states.push("invalid")
|
|
586
565
|
if (declaredRemote && remote !== declaredRemote)
|
|
587
566
|
states.push("remote-drifted")
|
|
588
567
|
repositories.push({
|
|
@@ -590,9 +569,7 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
590
569
|
path,
|
|
591
570
|
kind: entry.isSymlink
|
|
592
571
|
? "symlink"
|
|
593
|
-
:
|
|
594
|
-
? "bare"
|
|
595
|
-
: "repository",
|
|
572
|
+
: (inspection?.kind ?? "repository"),
|
|
596
573
|
remote,
|
|
597
574
|
declaredRemote,
|
|
598
575
|
target,
|
|
@@ -760,10 +737,12 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
760
737
|
) =>
|
|
761
738
|
Effect.gen(function* () {
|
|
762
739
|
const fs = yield* FileSystemService
|
|
740
|
+
const versionControl = yield* VersionControlService
|
|
763
741
|
const repository = yield* find(alias, startPath)
|
|
764
742
|
if (remote === undefined) return repository
|
|
765
743
|
const portable = yield* validateRemote(remote)
|
|
766
744
|
const state = yield* configState(startPath)
|
|
745
|
+
const backend = yield* versionControl.forWorkbase(state.root)
|
|
767
746
|
const config = withDeclarations(state.config, {
|
|
768
747
|
...(state.config.repositories ?? {}),
|
|
769
748
|
[repository.alias]: { remote: portable },
|
|
@@ -777,30 +756,12 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
777
756
|
const previous = repository.remote
|
|
778
757
|
const update = (value: string | null) =>
|
|
779
758
|
Effect.runPromise(
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
"remote",
|
|
787
|
-
"remove",
|
|
788
|
-
"origin",
|
|
789
|
-
]
|
|
790
|
-
: [
|
|
791
|
-
"git",
|
|
792
|
-
"-C",
|
|
793
|
-
repository.path,
|
|
794
|
-
"remote",
|
|
795
|
-
previous === null ? "add" : "set-url",
|
|
796
|
-
"origin",
|
|
797
|
-
value,
|
|
798
|
-
],
|
|
799
|
-
{ captureOutput: true },
|
|
800
|
-
),
|
|
801
|
-
).then((result) => {
|
|
802
|
-
if (result.exitCode !== 0) throw new Error(result.stderr.trim())
|
|
803
|
-
})
|
|
759
|
+
backend
|
|
760
|
+
.setRemoteUrl(repository.path, "origin", value)
|
|
761
|
+
.pipe(
|
|
762
|
+
Effect.provideService(FileSystemService, fs),
|
|
763
|
+
) as Effect.Effect<void, unknown, never>,
|
|
764
|
+
)
|
|
804
765
|
steps.push({
|
|
805
766
|
label: `update origin for repos/${repository.alias}`,
|
|
806
767
|
preflight: async () => {
|
|
@@ -810,21 +771,11 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
810
771
|
`Repository alias '${repository.alias}' changed to a linked checkout; retry the remote update`,
|
|
811
772
|
)
|
|
812
773
|
}
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
"-C",
|
|
818
|
-
repository.path,
|
|
819
|
-
"remote",
|
|
820
|
-
"get-url",
|
|
821
|
-
"origin",
|
|
822
|
-
],
|
|
823
|
-
{ captureOutput: true },
|
|
824
|
-
),
|
|
774
|
+
const currentRemote = await Effect.runPromise(
|
|
775
|
+
backend
|
|
776
|
+
.remoteUrl(repository.path, "origin")
|
|
777
|
+
.pipe(Effect.provideService(FileSystemService, fs)),
|
|
825
778
|
)
|
|
826
|
-
const currentRemote =
|
|
827
|
-
current.exitCode === 0 ? current.stdout.trim() : null
|
|
828
779
|
if (currentRemote !== previous) {
|
|
829
780
|
throw new Error(
|
|
830
781
|
`Origin for repository '${repository.alias}' changed; retry the remote update`,
|
|
@@ -847,7 +798,7 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
847
798
|
if (repository.states.includes("missing"))
|
|
848
799
|
issues.push("Local materialization is missing")
|
|
849
800
|
if (repository.states.includes("invalid"))
|
|
850
|
-
issues.push("Path is not a
|
|
801
|
+
issues.push("Path is not a valid repository")
|
|
851
802
|
if (!repository.declaredRemote)
|
|
852
803
|
issues.push("Portable remote is not declared")
|
|
853
804
|
if (repository.states.includes("remote-drifted"))
|
|
@@ -877,7 +828,7 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
877
828
|
unresolved.push({
|
|
878
829
|
alias: repository.alias,
|
|
879
830
|
state: "invalid",
|
|
880
|
-
message: `Local path for '${repository.alias}' is not a valid
|
|
831
|
+
message: `Local path for '${repository.alias}' is not a valid repository`,
|
|
881
832
|
action: `Repair the path or run 'agency repo remove ${repository.alias}' before setup`,
|
|
882
833
|
})
|
|
883
834
|
continue
|
|
@@ -936,25 +887,17 @@ export class RepositoryService extends Effect.Service<RepositoryService>()(
|
|
|
936
887
|
`.agency-setup-${action.alias}-${process.pid}-${Date.now()}`,
|
|
937
888
|
)
|
|
938
889
|
yield* fs.createDirectory(join(state.root, "repos"))
|
|
939
|
-
const cloned = yield*
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
...(backend.kind === "git" ? ["--bare"] : []),
|
|
944
|
-
"--",
|
|
945
|
-
action.remote,
|
|
946
|
-
from,
|
|
947
|
-
],
|
|
948
|
-
{ captureOutput: true },
|
|
949
|
-
)
|
|
950
|
-
if (cloned.exitCode !== 0) {
|
|
890
|
+
const cloned = yield* backend
|
|
891
|
+
.cloneRepository(action.remote, from)
|
|
892
|
+
.pipe(Effect.either)
|
|
893
|
+
if (Either.isLeft(cloned)) {
|
|
951
894
|
for (const item of staging)
|
|
952
895
|
yield* fs.deleteDirectory(item.from).pipe(Effect.ignore)
|
|
953
896
|
return yield* new RepositoryError({
|
|
954
|
-
message: `Failed to materialize repository '${action.alias}': ${cloned.
|
|
897
|
+
message: `Failed to materialize repository '${action.alias}': ${cloned.left instanceof Error ? cloned.left.message : String(cloned.left)}`,
|
|
898
|
+
cause: cloned.left,
|
|
955
899
|
})
|
|
956
900
|
}
|
|
957
|
-
yield* backend.initializeRepository(from)
|
|
958
901
|
staging.push({
|
|
959
902
|
alias: action.alias,
|
|
960
903
|
from,
|
|
@@ -166,11 +166,11 @@ describe("ReviewService", () => {
|
|
|
166
166
|
if (!Bun.which("jj")) return
|
|
167
167
|
const repository = join(root, "repos/agency")
|
|
168
168
|
await rm(repository, { recursive: true, force: true })
|
|
169
|
-
await git(["clone", source, repository])
|
|
170
169
|
await git(["switch", "-c", "jj-review"], source)
|
|
171
170
|
await Bun.write(join(source, "README.md"), "jj review one\n")
|
|
172
171
|
await git(["commit", "-am", "jj review one"], source)
|
|
173
|
-
await jj(["git", "
|
|
172
|
+
await jj(["git", "clone", "--no-colocate", source, repository])
|
|
173
|
+
expect(await Bun.file(join(repository, ".git")).exists()).toBe(false)
|
|
174
174
|
await Bun.write(
|
|
175
175
|
join(root, "agency.json"),
|
|
176
176
|
JSON.stringify({ version: 2, vcs: "jj" }),
|
|
@@ -45,8 +45,15 @@ const githubRepository = (remote: string) => {
|
|
|
45
45
|
const pinRef = (taskId: string) =>
|
|
46
46
|
`refs/agency/reviews/${Buffer.from(taskId).toString("hex")}`
|
|
47
47
|
|
|
48
|
-
const runGit = async (
|
|
49
|
-
|
|
48
|
+
const runGit = async (
|
|
49
|
+
args: readonly string[],
|
|
50
|
+
environment: Record<string, string> = {},
|
|
51
|
+
) => {
|
|
52
|
+
const child = Bun.spawn([...args], {
|
|
53
|
+
stdout: "pipe",
|
|
54
|
+
stderr: "pipe",
|
|
55
|
+
env: { ...process.env, ...environment },
|
|
56
|
+
})
|
|
50
57
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
51
58
|
child.exited,
|
|
52
59
|
new Response(child.stdout).text(),
|
|
@@ -121,7 +128,7 @@ const restoreSnapshots = async (
|
|
|
121
128
|
}
|
|
122
129
|
}
|
|
123
130
|
|
|
124
|
-
const normalizeBranch = (input: string
|
|
131
|
+
const normalizeBranch = (input: string) =>
|
|
125
132
|
Effect.gen(function* () {
|
|
126
133
|
const fs = yield* FileSystemService
|
|
127
134
|
if (
|
|
@@ -150,7 +157,7 @@ const normalizeBranch = (input: string, repositoryPath: string) =>
|
|
|
150
157
|
})
|
|
151
158
|
}
|
|
152
159
|
const checked = yield* fs.runCommand(
|
|
153
|
-
["git", "
|
|
160
|
+
["git", "check-ref-format", "--branch", name],
|
|
154
161
|
{ captureOutput: true },
|
|
155
162
|
)
|
|
156
163
|
if (checked.exitCode !== 0) {
|
|
@@ -168,6 +175,7 @@ const fetchCommit = (
|
|
|
168
175
|
) =>
|
|
169
176
|
Effect.gen(function* () {
|
|
170
177
|
const fs = yield* FileSystemService
|
|
178
|
+
const environment = yield* backend.gitEnvironment(repoPath)
|
|
171
179
|
const temporaryRef = `refs/agency/review-fetch/${process.pid}-${randomUUID()}`
|
|
172
180
|
const fetched = yield* fs.runCommand(
|
|
173
181
|
[
|
|
@@ -179,12 +187,12 @@ const fetchCommit = (
|
|
|
179
187
|
"origin",
|
|
180
188
|
`+${sourceRef}:${temporaryRef}`,
|
|
181
189
|
],
|
|
182
|
-
{ captureOutput: true },
|
|
190
|
+
{ captureOutput: true, env: environment },
|
|
183
191
|
)
|
|
184
192
|
if (fetched.exitCode !== 0) {
|
|
185
193
|
const cleanup = yield* fs.runCommand(
|
|
186
194
|
["git", "-C", repoPath, "update-ref", "-d", temporaryRef],
|
|
187
|
-
{ captureOutput: true },
|
|
195
|
+
{ captureOutput: true, env: environment },
|
|
188
196
|
)
|
|
189
197
|
return yield* new ReviewError({
|
|
190
198
|
message: `Review source '${sourceRef}' could not be fetched: ${fetched.stderr.trim()}${cleanup.exitCode === 0 ? "" : `; temporary ref cleanup failed: ${cleanup.stderr.trim()}`}`,
|
|
@@ -199,13 +207,13 @@ const fetchCommit = (
|
|
|
199
207
|
"--verify",
|
|
200
208
|
`${temporaryRef}^{commit}`,
|
|
201
209
|
],
|
|
202
|
-
{ captureOutput: true },
|
|
210
|
+
{ captureOutput: true, env: environment },
|
|
203
211
|
)
|
|
204
212
|
const commit = resolved.stdout.trim()
|
|
205
213
|
if (resolved.exitCode !== 0 || !/^[a-f0-9]{40}$/.test(commit)) {
|
|
206
214
|
yield* fs.runCommand(
|
|
207
215
|
["git", "-C", repoPath, "update-ref", "-d", temporaryRef],
|
|
208
|
-
{ captureOutput: true },
|
|
216
|
+
{ captureOutput: true, env: environment },
|
|
209
217
|
)
|
|
210
218
|
return yield* new ReviewError({
|
|
211
219
|
message: `Review source '${sourceRef}' did not resolve to a commit`,
|
|
@@ -214,7 +222,7 @@ const fetchCommit = (
|
|
|
214
222
|
const imported = yield* Effect.either(backend.importGitRefs(repoPath))
|
|
215
223
|
const cleanup = yield* fs.runCommand(
|
|
216
224
|
["git", "-C", repoPath, "update-ref", "-d", temporaryRef],
|
|
217
|
-
{ captureOutput: true },
|
|
225
|
+
{ captureOutput: true, env: environment },
|
|
218
226
|
)
|
|
219
227
|
if (cleanup.exitCode !== 0) {
|
|
220
228
|
return yield* new ReviewError({
|
|
@@ -284,7 +292,7 @@ export class ReviewService extends Effect.Service<ReviewService>()(
|
|
|
284
292
|
fetchRef: sourceRef,
|
|
285
293
|
}
|
|
286
294
|
} else if (input.ref) {
|
|
287
|
-
sourceRef = yield* normalizeBranch(input.ref
|
|
295
|
+
sourceRef = yield* normalizeBranch(input.ref)
|
|
288
296
|
source = { kind: "branch", ref: sourceRef }
|
|
289
297
|
} else {
|
|
290
298
|
return yield* new ReviewError({
|
|
@@ -366,6 +374,11 @@ export class ReviewService extends Effect.Service<ReviewService>()(
|
|
|
366
374
|
task.data.review.repo,
|
|
367
375
|
root,
|
|
368
376
|
)
|
|
377
|
+
const versionControl = yield* VersionControlService
|
|
378
|
+
const backend = yield* versionControl.forWorkbase(root)
|
|
379
|
+
const gitEnvironment = yield* backend.gitEnvironment(
|
|
380
|
+
repository.path,
|
|
381
|
+
)
|
|
369
382
|
const hadCheckout = inspection.checkouts.some(
|
|
370
383
|
(checkout) => checkout.exists || checkout.registered,
|
|
371
384
|
)
|
|
@@ -391,25 +404,31 @@ export class ReviewService extends Effect.Service<ReviewService>()(
|
|
|
391
404
|
steps.push({
|
|
392
405
|
label: `advance review pin for ${taskId}`,
|
|
393
406
|
apply: () =>
|
|
394
|
-
runGit(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
407
|
+
runGit(
|
|
408
|
+
[
|
|
409
|
+
"git",
|
|
410
|
+
"-C",
|
|
411
|
+
repository.path,
|
|
412
|
+
"update-ref",
|
|
413
|
+
pinRef(taskId),
|
|
414
|
+
latest.commit,
|
|
415
|
+
previousReview.commit,
|
|
416
|
+
],
|
|
417
|
+
gitEnvironment,
|
|
418
|
+
).then(() => undefined),
|
|
403
419
|
rollback: () =>
|
|
404
|
-
runGit(
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
420
|
+
runGit(
|
|
421
|
+
[
|
|
422
|
+
"git",
|
|
423
|
+
"-C",
|
|
424
|
+
repository.path,
|
|
425
|
+
"update-ref",
|
|
426
|
+
pinRef(taskId),
|
|
427
|
+
previousReview.commit,
|
|
428
|
+
latest.commit,
|
|
429
|
+
],
|
|
430
|
+
gitEnvironment,
|
|
431
|
+
).then(() => undefined),
|
|
413
432
|
manualRecovery: `Reset ${pinRef(taskId)} to ${previousReview.commit}`,
|
|
414
433
|
})
|
|
415
434
|
if (hadCheckout) {
|