From 6da707f1e6c269fe877cc30fbea1a040c826b958 Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Wed, 4 Feb 2026 08:06:16 -0800 Subject: [PATCH] debug: log to terminal --- src/services/gitService.ts | 55 +++++++++++++++++++++++++------- src/utils/logger.ts | 13 ++++++-- test/services/gitService.spec.ts | 38 ++++++++++++++++++++++ 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/src/services/gitService.ts b/src/services/gitService.ts index c3813c3..b0d8c8b 100644 --- a/src/services/gitService.ts +++ b/src/services/gitService.ts @@ -237,32 +237,59 @@ const deleteStaleLocalBranch = async( } }; +/** + * Fetches and checks out an existing remote branch. + * @param logger - The logger instance. + * @param repoPath - The repository path. + * @param branchName - The branch name to fetch and checkout. + * @returns True if successful, false if the branch no longer exists on remote. + */ +const fetchAndCheckoutRemoteBranch = async( + logger: Logger, + repoPath: string, + branchName: string, +): Promise => { + try { + await runGitCommand( + logger, + repoPath, + `git fetch origin ${branchName}:refs/remotes/origin/${branchName}`, + ); + await runGitCommand( + logger, + repoPath, + `git checkout -b ${branchName} origin/${branchName}`, + ); + return true; + } catch { + // Branch may have been deleted between check and fetch + return false; + } +}; + /** * Handles updating an existing branch with a newer version. * @param options - The branch update options. - * @returns The update result. + * @returns The update result, or null if branch no longer exists. */ const handleExistingBranch = async( options: BranchUpdateOptions, -): Promise => { +): Promise => { const { branchName, clonedRepo, logger, packageName, targetVersion } = options; const { path: repoPath } = clonedRepo; await deleteStaleLocalBranch(logger, repoPath, branchName); - // Fetch the specific branch to ensure we have the latest refs - await runGitCommand( + const checkedOut = await fetchAndCheckoutRemoteBranch( logger, repoPath, - `git fetch origin ${branchName}:refs/remotes/origin/${branchName}`, - ); - - await runGitCommand( - logger, - repoPath, - `git checkout -b ${branchName} origin/${branchName}`, + branchName, ); + if (!checkedOut) { + // Branch was deleted on remote, fall back to creating new branch + return null; + } const currentVersion = await getCurrentVersionOnBranch(repoPath, packageName); @@ -355,7 +382,11 @@ const createOrUpdateBranch = async( const branchExists = remoteBranches.includes(`origin/${branchName}`); if (branchExists) { - return await handleExistingBranch(options); + const result = await handleExistingBranch(options); + // If null, branch was deleted on remote between check and fetch + if (result !== null) { + return result; + } } return await handleNewBranch(options); diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 491e338..6a4ec52 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -4,8 +4,17 @@ * @author Naomi Carrigan */ -import { Logger } from "@nhcarrigan/logger"; +// import { Logger } from "@nhcarrigan/logger"; -const logger = new Logger("Minori", process.env.LOG_TOKEN ?? ""); +// const logger = new Logger("Minori", process.env.LOG_TOKEN ?? ""); + +const logger = { + error: (message: string, error: Error) => { + console.error(message, error); + }, + log: (level: string, message: string) => { + console.log(level, message); + }, +}; export { logger }; diff --git a/test/services/gitService.spec.ts b/test/services/gitService.spec.ts index 4ef22bd..6246ea8 100644 --- a/test/services/gitService.spec.ts +++ b/test/services/gitService.spec.ts @@ -402,6 +402,44 @@ describe("gitService", () => { expect(writtenContent.devDependencies["test-package"]).toBe("2.0.0"); }); + it("should fall back to new branch when remote branch fetch fails", async() => { + expect.assertions(2); + mockExecAsync.mockImplementation((command: string) => { + if (command.includes("git branch -r")) { + // Report branch as existing + return Promise.resolve({ + stderr: "", + stdout: " origin/dependencies/update-test-package\n", + }); + } + if (command.includes("git fetch origin dependencies/update-test-package")) { + // But fail when trying to fetch it (deleted between check and fetch) + const error = new Error("Git command failed") as Error & { stderr: string }; + error.stderr = "fatal: couldn't find remote ref dependencies/update-test-package"; + return Promise.reject(error); + } + return Promise.resolve({ stderr: "", stdout: "" }); + }); + vi.mocked(readFile).mockResolvedValue(JSON.stringify({ + dependencies: { "test-package": "1.0.0" }, + })); + vi.mocked(writeFile).mockResolvedValue(undefined); + const { createOrUpdateBranch } = await import("../../src/services/gitService.js"); + const mockClonedRepo = createMockClonedRepo(); + const result = await createOrUpdateBranch({ + branchName: "dependencies/update-test-package", + clonedRepo: mockClonedRepo, + logger: mockLogger, + packageName: "test-package", + targetVersion: "2.0.0", + }); + // Should fall back to creating a new branch + expect(result.status).toBe("created"); + if (result.status === "created") { + expect(result.branchName).toBe("dependencies/update-test-package"); + } + }); + it("should log pnpm error details when install fails", async() => { expect.assertions(2); mockExecAsync.mockImplementation((command: string) => {