History

Use structural Git status semantics

Julius Marminge committed 4 months ago

Commit c4945aa1 parent cebbe6f2 files · +34 −31Browse files

Unified · Split

2 files
  1. apps/server/src/git/GitManager.test.ts +32 −5
  2. apps/server/src/git/GitManager.ts +2 −26
apps/server/src/git/GitManager.test.ts+32−5
@@ -638,6 +638,7 @@ function makeManager(input?: { · 637 unmodified lines
638 ghScenario?: FakeGhScenario;638 ghScenario?: FakeGhScenario;
639 textGeneration?: Partial<FakeGitTextGeneration>;639 textGeneration?: Partial<FakeGitTextGeneration>;
640 setupScriptRunner?: ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"];640 setupScriptRunner?: ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"];
641 vcsDriverLayer?: Layer.Layer<GitVcsDriver.GitVcsDriver>;
641}) {642}) {
642 const { service: gitHubCli, ghCalls } = createGitHubCliWithFakeGh(input?.ghScenario);643 const { service: gitHubCli, ghCalls } = createGitHubCliWithFakeGh(input?.ghScenario);
643 const textGeneration = createTextGeneration(input?.textGeneration);644 const textGeneration = createTextGeneration(input?.textGeneration);
@@ -647,11 +648,13 @@ function makeManager(input?: { · 3 unmodified lines
647 648
648 const serverSettingsLayer = ServerSettings.ServerSettingsService.layerTest();649 const serverSettingsLayer = ServerSettings.ServerSettingsService.layerTest();
649 650
650 const vcsDriverLayer = GitVcsDriver.layer.pipe(651 const vcsDriverLayer =
651 Layer.provideMerge(VcsProcess.layer),652 input?.vcsDriverLayer ??
652 Layer.provideMerge(NodeServices.layer),653 GitVcsDriver.layer.pipe(
653 Layer.provideMerge(serverConfigLayer),654 Layer.provideMerge(VcsProcess.layer),
654 );655 Layer.provideMerge(NodeServices.layer),
656 Layer.provideMerge(serverConfigLayer),
657 );
655 const sourceControlRegistryLayer = Layer.effect(658 const sourceControlRegistryLayer = Layer.effect(
656 SourceControlProviderRegistry.SourceControlProviderRegistry,659 SourceControlProviderRegistry.SourceControlProviderRegistry,
657 GitHubSourceControlProvider.make.pipe(660 GitHubSourceControlProvider.make.pipe(
@@ -934,6 +937,30 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { · 276 unmodified lines
934 }),937 }),
935 );938 );
936 939
940 it.effect("does not classify Git command failures from message text", () =>
941 Effect.gen(function* () {
942 const cwd = "/workspace";
943 const cause = new Error("spawn failed");
944 const commandError = new GitCommandError({
945 operation: "GitVcsDriver.statusDetails.status",
946 command: "git",
947 cwd,
948 detail: "not a git repository",
949 cause,
950 });
951 const { manager } = yield* makeManager({
952 vcsDriverLayer: Layer.mock(GitVcsDriver.GitVcsDriver)({
953 statusDetailsLocal: () => Effect.fail(commandError),
954 }),
955 });
956
957 const error = yield* manager.localStatus({ cwd }).pipe(Effect.flip);
958
959 expect(error).toBe(commandError);
960 expect(error.cause).toBe(cause);
961 }),
962 );
963
937 it.effect("status briefly caches repeated lookups for the same cwd", () =>964 it.effect("status briefly caches repeated lookups for the same cwd", () =>
938 Effect.gen(function* () {965 Effect.gen(function* () {
939 const repoDir = yield* makeTempDir("t3code-git-manager-");966 const repoDir = yield* makeTempDir("t3code-git-manager-");
apps/server/src/git/GitManager.ts+2−26
@@ -15,7 +15,6 @@ import * as Ref from "effect/Ref"; · 14 unmodified lines
15import {15import {
16 GitActionProgressEvent,16 GitActionProgressEvent,
17 GitActionProgressPhase,17 GitActionProgressPhase,
18 GitCommandError,
19 GitPreparePullRequestThreadInput,18 GitPreparePullRequestThreadInput,
20 GitPreparePullRequestThreadResult,19 GitPreparePullRequestThreadResult,
21 GitPullRequestRefInput,20 GitPullRequestRefInput,
@@ -99,10 +98,6 @@ type StripProgressContext<T> = T extends any ? Omit<T, "actionId" | "cwd" | "act · 77 unmodified lines
99type GitActionProgressPayload = StripProgressContext<GitActionProgressEvent>;98type GitActionProgressPayload = StripProgressContext<GitActionProgressEvent>;
100type GitActionProgressEmitter = (event: GitActionProgressPayload) => Effect.Effect<void, never>;99type GitActionProgressEmitter = (event: GitActionProgressPayload) => Effect.Effect<void, never>;
101 100
102function isNotGitRepositoryError(error: GitCommandError): boolean {
103 return error.message.toLowerCase().includes("not a git repository");
104}
105
106interface OpenPrInfo {101interface OpenPrInfo {
107 number: number;102 number: number;
108 title: string;103 title: string;
@@ -727,25 +722,8 @@ export const make = Effect.gen(function* () { · 618 unmodified lines
727 const canonicalizeExistingPath = (value: string) =>722 const canonicalizeExistingPath = (value: string) =>
728 fileSystem.realPath(value).pipe(Effect.orElseSucceed(() => value));723 fileSystem.realPath(value).pipe(Effect.orElseSucceed(() => value));
729 const normalizeStatusCacheKey = canonicalizeExistingPath;724 const normalizeStatusCacheKey = canonicalizeExistingPath;
730 const nonRepositoryStatusDetails = {
731 isRepo: false,
732 hasOriginRemote: false,
733 isDefaultBranch: false,
734 branch: null,
735 upstreamRef: null,
736 hasWorkingTreeChanges: false,
737 workingTree: { files: [], insertions: 0, deletions: 0 },
738 hasUpstream: false,
739 aheadCount: 0,
740 behindCount: 0,
741 aheadOfDefaultCount: 0,
742 } satisfies GitVcsDriver.GitStatusDetails;
743 const readLocalStatus = Effect.fn("readLocalStatus")(function* (cwd: string) {725 const readLocalStatus = Effect.fn("readLocalStatus")(function* (cwd: string) {
744 const details = yield* gitCore726 const details = yield* gitCore.statusDetailsLocal(cwd);
745 .statusDetailsLocal(cwd)
746 .pipe(
747 Effect.catchIf(isNotGitRepositoryError, () => Effect.succeed(nonRepositoryStatusDetails)),
748 );
749 const hostingProvider = details.isRepo727 const hostingProvider = details.isRepo
750 ? yield* resolveHostingProvider(cwd, details.branch)728 ? yield* resolveHostingProvider(cwd, details.branch)
751 : null;729 : null;
@@ -772,9 +750,7 @@ export const make = Effect.gen(function* () { · 20 unmodified lines
772 cwd: string,750 cwd: string,
773 options?: GitVcsDriver.GitRemoteStatusOptions,751 options?: GitVcsDriver.GitRemoteStatusOptions,
774 ) {752 ) {
775 const details = yield* gitCore753 const details = yield* gitCore.statusDetailsRemote(cwd, options);
776 .statusDetailsRemote(cwd, options)
777 .pipe(Effect.catchIf(isNotGitRepositoryError, () => Effect.succeed(null)));
778 if (details === null || !details.isRepo) {754 if (details === null || !details.isRepo) {
779 return null;755 return null;
780 }756 }