Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions apps/server/src/git/GitManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2757,6 +2757,65 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
}),
);

it.effect("preserves both branch materialization failures when the fallback also fails", () =>
Effect.gen(function* () {
const repoDir = yield* makeTempDir("t3code-git-manager-");
yield* initRepo(repoDir);
const originDir = yield* createBareRemote();
yield* runGit(repoDir, ["remote", "add", "origin", originDir]);
yield* runGit(repoDir, ["push", "-u", "origin", "main"]);

const missingForkDir = NodePath.join(repoDir, "missing-fork.git");
const { manager } = yield* makeManager({
ghScenario: {
pullRequest: {
number: 93,
title: "Missing fork branch",
url: "https://github.com/pingdotgg/codething-mvp/pull/93",
baseRefName: "main",
headRefName: "feature/missing-fork-branch",
state: "open",
isCrossRepository: true,
headRepositoryNameWithOwner: "octocat/codething-mvp",
headRepositoryOwnerLogin: "octocat",
},
repositoryCloneUrls: {
"octocat/codething-mvp": {
url: missingForkDir,
sshUrl: missingForkDir,
},
},
},
});

const error = yield* preparePullRequestThread(manager, {
cwd: repoDir,
reference: "93",
mode: "worktree",
}).pipe(Effect.flip);

if (error._tag !== "GitPullRequestMaterializationError") {
return yield* Effect.die(error);
}
expect(error).toMatchObject({
cwd: repoDir,
pullRequestNumber: 93,
headRepository: "octocat/codething-mvp",
headBranch: "feature/missing-fork-branch",
localBranch: "t3code/pr-93/feature/missing-fork-branch",
});
if (!(error.cause instanceof AggregateError)) {
return yield* Effect.die(error.cause);
}
expect(error.cause.errors).toHaveLength(2);
expect(error.cause.errors).toEqual([
expect.objectContaining({ _tag: "GitCommandError" }),
expect.objectContaining({ _tag: "GitCommandError" }),
]);
expect(error.cause.cause).toBe(error.cause.errors[0]);
}),
);

it.effect("launches setup only when creating a new PR worktree", () =>
Effect.gen(function* () {
const repoDir = yield* makeTempDir("t3code-git-manager-");
Expand Down
49 changes: 36 additions & 13 deletions apps/server/src/git/GitManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {
type ChangeRequestTerminology,
} from "@t3tools/shared/sourceControl";

import { GitManagerError } from "@t3tools/contracts";
import { GitManagerError, GitPullRequestMaterializationError } from "@t3tools/contracts";
import * as TextGeneration from "../textGeneration/TextGeneration.ts";
import * as ProjectSetupScriptRunner from "../project/ProjectSetupScriptRunner.ts";
import { extractBranchNameFromRemoteRef } from "./remoteRefs.ts";
Expand Down Expand Up @@ -631,9 +631,12 @@ export const make = Effect.gen(function* () {
) =>
configurePullRequestHeadUpstreamBase(cwd, pullRequest, localBranch).pipe(
Effect.catch((error) =>
Effect.logWarning(
`GitManager.configurePullRequestHeadUpstream: failed to configure upstream for ${localBranch} -> ${pullRequest.headBranch} in ${cwd}: ${error.message}`,
).pipe(Effect.asVoid),
Effect.logWarning("GitManager.configurePullRequestHeadUpstream failed", {
cwd,
localBranch,
headBranch: pullRequest.headBranch,
cause: error,
}).pipe(Effect.asVoid),
),
);

Expand Down Expand Up @@ -691,12 +694,30 @@ export const make = Effect.gen(function* () {
localBranch = pullRequest.headBranch,
) =>
materializePullRequestHeadBranchBase(cwd, pullRequest, localBranch).pipe(
Effect.catch(() =>
gitCore.fetchPullRequestBranch({
cwd,
prNumber: pullRequest.number,
branch: localBranch,
}),
Effect.catch((primaryCause) =>
gitCore
.fetchPullRequestBranch({
cwd,
prNumber: pullRequest.number,
branch: localBranch,
})
.pipe(
Effect.mapError(
(fallbackCause) =>
new GitPullRequestMaterializationError({
cwd,
pullRequestNumber: pullRequest.number,
headRepository: resolveHeadRepositoryNameWithOwner(pullRequest),
headBranch: pullRequest.headBranch,
localBranch,
cause: new AggregateError(
[primaryCause, fallbackCause],
`Repository-head and pull-request-ref fetches both failed for pull request #${pullRequest.number}.`,
{ cause: primaryCause },
),
}),
),
),
),
);
const fileSystem = yield* FileSystem.FileSystem;
Expand Down Expand Up @@ -1452,9 +1473,11 @@ export const make = Effect.gen(function* () {
})
.pipe(
Effect.catch((error) =>
Effect.logWarning(
`GitManager.preparePullRequestThread: failed to launch worktree setup script for thread ${input.threadId} in ${worktreePath}: ${error.message}`,
).pipe(Effect.asVoid),
Effect.logWarning("GitManager.preparePullRequestThread setup script failed", {
threadId: input.threadId,
worktreePath,
cause: error,
}).pipe(Effect.asVoid),
),
);
};
Expand Down
17 changes: 17 additions & 0 deletions packages/contracts/src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,25 @@ export class GitManagerError extends Schema.TaggedErrorClass<GitManagerError>()(
}
}

export class GitPullRequestMaterializationError extends Schema.TaggedErrorClass<GitPullRequestMaterializationError>()(
"GitPullRequestMaterializationError",
{
cwd: TrimmedNonEmptyStringSchema,
pullRequestNumber: PositiveInt,
headRepository: Schema.NullOr(TrimmedNonEmptyStringSchema),
headBranch: TrimmedNonEmptyStringSchema,
localBranch: TrimmedNonEmptyStringSchema,
cause: Schema.Defect(),
},
) {
override get message(): string {
return `Failed to materialize pull request #${this.pullRequestNumber} branch ${this.headBranch} as ${this.localBranch}.`;
}
}

export const GitManagerServiceError = Schema.Union([
GitManagerError,
GitPullRequestMaterializationError,
GitCommandError,
SourceControlProviderError,
TextGenerationError,
Expand Down
Loading