Skip to content

Commit 61dddcf

Browse files
committed
Extract shared vcsErrorText helper and add force support to Azure DevOps checkout
- Extract duplicated errorText function from AzureDevOpsCli.ts and GitHubCli.ts into a shared vcsErrorText utility in vcs/vcsErrorText.ts - Add force parameter to AzureDevOpsCliShape.checkoutPullRequest to match SourceControlProviderShape.checkoutChangeRequest interface - When force is true, run git checkout --force before az repos pr checkout to discard local modifications (matching gh pr checkout --force behavior)
1 parent 18d781b commit 61dddcf

3 files changed

Lines changed: 49 additions & 37 deletions

File tree

apps/server/src/sourceControl/AzureDevOpsCli.ts

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Context, Effect, FileSystem, Layer, Result, Schema, SchemaIssue } from
22
import { TrimmedNonEmptyString, type VcsError } from "@t3tools/contracts";
33

44
import { VcsProcess, type VcsProcessOutput } from "../vcs/VcsProcess.ts";
5+
import { vcsErrorText } from "../vcs/vcsErrorText.ts";
56
import {
67
decodeAzureDevOpsPullRequestJson,
78
decodeAzureDevOpsPullRequestListJson,
@@ -69,29 +70,19 @@ export interface AzureDevOpsCliShape {
6970
readonly checkoutPullRequest: (input: {
7071
readonly cwd: string;
7172
readonly reference: string;
73+
readonly force?: boolean;
7274
}) => Effect.Effect<void, AzureDevOpsCliError>;
7375
}
7476

7577
export class AzureDevOpsCli extends Context.Service<AzureDevOpsCli, AzureDevOpsCliShape>()(
7678
"t3/source-control/AzureDevOpsCli",
7779
) {}
7880

79-
function errorText(error: VcsError | unknown): string {
80-
if (typeof error === "object" && error !== null) {
81-
const tag = "_tag" in error && typeof error._tag === "string" ? error._tag : "";
82-
const detail = "detail" in error && typeof error.detail === "string" ? error.detail : "";
83-
const message = "message" in error && typeof error.message === "string" ? error.message : "";
84-
return [tag, detail, message].filter(Boolean).join("\n");
85-
}
86-
87-
return String(error);
88-
}
89-
9081
function normalizeAzureDevOpsCliError(
9182
operation: "execute" | "readBodyFile",
9283
error: VcsError | unknown,
9384
): AzureDevOpsCliError {
94-
const text = errorText(error);
85+
const text = vcsErrorText(error);
9586
const lower = text.toLowerCase();
9687

9788
if (lower.includes("command not found: az") || lower.includes("enoent")) {
@@ -363,19 +354,38 @@ export const make = Effect.fn("makeAzureDevOpsCli")(function* () {
363354
Effect.map((repo) => normalizeDefaultBranch(repo.defaultBranch)),
364355
),
365356
checkoutPullRequest: (input) =>
366-
execute({
367-
cwd: input.cwd,
368-
args: [
369-
"repos",
370-
"pr",
371-
"checkout",
372-
"--only-show-errors",
373-
"--id",
374-
normalizeChangeRequestId(input.reference),
375-
"--remote-name",
376-
"origin",
377-
],
378-
}).pipe(Effect.asVoid),
357+
(input.force
358+
? process
359+
.run({
360+
operation: "AzureDevOpsCli.checkoutPullRequest",
361+
command: "git",
362+
args: ["checkout", "--force"],
363+
cwd: input.cwd,
364+
timeoutMs: DEFAULT_TIMEOUT_MS,
365+
})
366+
.pipe(
367+
Effect.mapError((error) => normalizeAzureDevOpsCliError("execute", error)),
368+
Effect.asVoid,
369+
)
370+
: Effect.void
371+
).pipe(
372+
Effect.andThen(
373+
execute({
374+
cwd: input.cwd,
375+
args: [
376+
"repos",
377+
"pr",
378+
"checkout",
379+
"--only-show-errors",
380+
"--id",
381+
normalizeChangeRequestId(input.reference),
382+
"--remote-name",
383+
"origin",
384+
],
385+
}),
386+
),
387+
Effect.asVoid,
388+
),
379389
});
380390
});
381391

apps/server/src/sourceControl/GitHubCli.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Context, Effect, Layer, Result, Schema, SchemaIssue } from "effect";
33
import { GitHubCliError, TrimmedNonEmptyString, type VcsError } from "@t3tools/contracts";
44

55
import { VcsProcess, type VcsProcessOutput } from "../vcs/VcsProcess.ts";
6+
import { vcsErrorText } from "../vcs/vcsErrorText.ts";
67
import {
78
decodeGitHubPullRequestJson,
89
decodeGitHubPullRequestListJson,
@@ -75,22 +76,11 @@ export class GitHubCli extends Context.Service<GitHubCli, GitHubCliShape>()(
7576
"t3/source-control/GitHubCli",
7677
) {}
7778

78-
function errorText(error: VcsError | unknown): string {
79-
if (typeof error === "object" && error !== null) {
80-
const tag = "_tag" in error && typeof error._tag === "string" ? error._tag : "";
81-
const detail = "detail" in error && typeof error.detail === "string" ? error.detail : "";
82-
const message = "message" in error && typeof error.message === "string" ? error.message : "";
83-
return [tag, detail, message].filter(Boolean).join("\n");
84-
}
85-
86-
return String(error);
87-
}
88-
8979
function normalizeGitHubCliError(
9080
operation: "execute" | "stdout",
9181
error: VcsError | unknown,
9282
): GitHubCliError {
93-
const text = errorText(error);
83+
const text = vcsErrorText(error);
9484
const lower = text.toLowerCase();
9585

9686
if (lower.includes("command not found: gh") || lower.includes("enoent")) {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { VcsError } from "@t3tools/contracts";
2+
3+
export function vcsErrorText(error: VcsError | unknown): string {
4+
if (typeof error === "object" && error !== null) {
5+
const tag = "_tag" in error && typeof error._tag === "string" ? error._tag : "";
6+
const detail = "detail" in error && typeof error.detail === "string" ? error.detail : "";
7+
const message = "message" in error && typeof error.message === "string" ? error.message : "";
8+
return [tag, detail, message].filter(Boolean).join("\n");
9+
}
10+
11+
return String(error);
12+
}

0 commit comments

Comments
 (0)