From c63f49070041a5d072854edc87c1bc984d8f113a Mon Sep 17 00:00:00 2001 From: Daniel Draper Date: Wed, 17 Dec 2025 14:46:58 +0100 Subject: [PATCH 1/3] make it work in a ci-checkout context --- package.json | 4 ++-- src/tools/sync/git.ts | 26 ++++++++++++++++++++++-- tests/tools/sync/git.integration.spec.ts | 10 ++++++++- tests/tools/sync/git.spec.ts | 15 +++++++++++++- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index deb1790..1dddf9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@octomind/octomind", - "version": "3.4.0", + "version": "3.4.1", "description": "a command line client for octomind apis", "main": "./dist/index.js", "packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402", @@ -26,7 +26,7 @@ "lint": "pnpm apigen && npx genversion -des src/version.ts && biome check", "tsc": "tsc --project tsconfig.build.json", "dev:staging": "pnpm apigen && OCTOMIND_CONFIG_FILE=octomind.dev.staging.json OCTOMIND_API_URL=https://preview.octomind.dev/api tsx src/index.ts", - "dev:prod": "pnpm apigen && OCTOMIND_CONFIG_FILE=octomind.dev.staging.json OCTOMIND_API_URL=https://app.octomind.dev/api tsx src/index.ts", + "dev:prod": "pnpm apigen && OCTOMIND_CONFIG_FILE=octomind.dev.prod.json OCTOMIND_API_URL=https://app.octomind.dev/api tsx src/index.ts", "apigen": "openapi-typescript https://preview.octomind.dev/openapi.yaml --output src/api.ts && orval", "build": "pnpm apigen && npx genversion -des src/version.ts && pnpm gendoc && tsc --project tsconfig.build.json", "octomind": "pnpm apigen && tsx src/index.ts", diff --git a/src/tools/sync/git.ts b/src/tools/sync/git.ts index bffbb1f..7e21369 100644 --- a/src/tools/sync/git.ts +++ b/src/tools/sync/git.ts @@ -4,6 +4,10 @@ import { simpleGit } from "simple-git"; import { ExecutionContext } from "./types"; + +const FALLBACK_DEFAULT_BRANCH = "refs/heads/main"; + + export const parseGitRemote = async (): Promise<{ owner?: string; repo?: string; @@ -41,11 +45,29 @@ export type GitContext = ExecutionContext & { defaultBranch?: string; }; -export const getDefaultBranch = async (): Promise => { +export const getDefaultBranch = async (allowedMethod: "symbolicRef+origin" | "origin" = "symbolicRef+origin"): Promise => { const symbolicRef = ( await simpleGit().raw("symbolic-ref", "refs/remotes/origin/HEAD") ).trim(); - return symbolicRef.replace("refs/remotes/origin/", "refs/heads/"); + const symbolicRefBranch = symbolicRef.replace( + "refs/remotes/origin/", + "refs/heads/", + ); + + if (allowedMethod === "symbolicRef+origin" && symbolicRefBranch) { + return symbolicRefBranch; + } + + const origin = await simpleGit().remote(["show", "origin"]); + if (!origin) { + console.warn("could not identify default branch, falling back to 'main'") + return FALLBACK_DEFAULT_BRANCH; + } + + const originDefaultBranch = /HEAD branch:((.*))/.exec(origin) + + return originDefaultBranch?.groups?.["branchName"] ? `refs/heads/${originDefaultBranch?.groups?.["branchName"]}` : FALLBACK_DEFAULT_BRANCH; + }; export const getGitContext = async (): Promise => { diff --git a/tests/tools/sync/git.integration.spec.ts b/tests/tools/sync/git.integration.spec.ts index 7ccf156..70b3c89 100644 --- a/tests/tools/sync/git.integration.spec.ts +++ b/tests/tools/sync/git.integration.spec.ts @@ -1,4 +1,4 @@ -import {parseGitRemote} from "../../../src/tools/sync/git"; +import {getDefaultBranch, parseGitRemote} from "../../../src/tools/sync/git"; describe('git', () => { it("should return the actual owner and repo", async () => { @@ -7,4 +7,12 @@ describe('git', () => { expect(remote.owner).toEqual("OctoMind-dev"); expect(remote.repo).toEqual("cli"); }) + + it.each(["symbolicRef+origin", "origin"] as const)("should return the actual default branch for method '%s'", async (allowedMethod) => { + const defaultBranch = await getDefaultBranch(allowedMethod); + + expect(defaultBranch).toEqual("refs/heads/main"); + }); + + }) \ No newline at end of file diff --git a/tests/tools/sync/git.spec.ts b/tests/tools/sync/git.spec.ts index 4ba0010..0b07136 100644 --- a/tests/tools/sync/git.spec.ts +++ b/tests/tools/sync/git.spec.ts @@ -1,5 +1,5 @@ import {simpleGit} from "simple-git"; -import {parseGitRemote, getGitContext} from "../../../src/tools/sync/git"; +import {parseGitRemote, getGitContext, getDefaultBranch} from "../../../src/tools/sync/git"; import {mock} from "jest-mock-extended"; jest.mock("simple-git"); @@ -14,6 +14,19 @@ describe("git", () => { console.error = jest.fn(); }) + describe("getDefaultBranch", () => { + it("should fallback if both origin and symbolic ref fail", async () => { + console.warn = jest.fn(); + mockGit.raw.mockResolvedValue("") + mockGit.remote.mockResolvedValue("") + + const defaultBranch = await getDefaultBranch(); + + expect(defaultBranch).toEqual("refs/heads/main") + expect(console.warn).toHaveBeenCalled() + }) + }) + describe("parseGitRemote", () => { it("parses SSH git remote format (git@github.com:owner/repo.git)", async () => { mockGit.remote.mockResolvedValue("git@github.com:my-org/my-repo.git"); From 1c74d79b2fb85e975e33a0b0291467974a8daf98 Mon Sep 17 00:00:00 2001 From: Daniel Draper Date: Wed, 17 Dec 2025 14:49:35 +0100 Subject: [PATCH 2/3] dont call symbolic-ref if not supported --- src/tools/sync/git.ts | 22 ++++++++++++---------- tests/tools/sync/git.integration.spec.ts | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/tools/sync/git.ts b/src/tools/sync/git.ts index 7e21369..3883733 100644 --- a/src/tools/sync/git.ts +++ b/src/tools/sync/git.ts @@ -46,16 +46,18 @@ export type GitContext = ExecutionContext & { }; export const getDefaultBranch = async (allowedMethod: "symbolicRef+origin" | "origin" = "symbolicRef+origin"): Promise => { - const symbolicRef = ( - await simpleGit().raw("symbolic-ref", "refs/remotes/origin/HEAD") - ).trim(); - const symbolicRefBranch = symbolicRef.replace( - "refs/remotes/origin/", - "refs/heads/", - ); - - if (allowedMethod === "symbolicRef+origin" && symbolicRefBranch) { - return symbolicRefBranch; + if (allowedMethod === "symbolicRef+origin") { + const symbolicRef = ( + await simpleGit().raw("symbolic-ref", "refs/remotes/origin/HEAD") + ).trim(); + const symbolicRefBranch = symbolicRef.replace( + "refs/remotes/origin/", + "refs/heads/", + ); + + if (symbolicRefBranch) { + return symbolicRefBranch; + } } const origin = await simpleGit().remote(["show", "origin"]); diff --git a/tests/tools/sync/git.integration.spec.ts b/tests/tools/sync/git.integration.spec.ts index 70b3c89..84e5738 100644 --- a/tests/tools/sync/git.integration.spec.ts +++ b/tests/tools/sync/git.integration.spec.ts @@ -8,7 +8,7 @@ describe('git', () => { expect(remote.repo).toEqual("cli"); }) - it.each(["symbolicRef+origin", "origin"] as const)("should return the actual default branch for method '%s'", async (allowedMethod) => { + it.each(["origin"] as const)("should return the actual default branch for method '%s'", async (allowedMethod) => { const defaultBranch = await getDefaultBranch(allowedMethod); expect(defaultBranch).toEqual("refs/heads/main"); From b91a82c1a6dd8bceaa36ee5613ff1c7f38aa301f Mon Sep 17 00:00:00 2001 From: Daniel Draper Date: Wed, 17 Dec 2025 14:50:11 +0100 Subject: [PATCH 3/3] biome --- biome.json | 2 +- src/tools/sync/git.ts | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/biome.json b/biome.json index 10ed704..74a58e7 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", + "$schema": "https://biomejs.dev/schemas/2.3.9/schema.json", "vcs": { "enabled": false, "clientKind": "git", diff --git a/src/tools/sync/git.ts b/src/tools/sync/git.ts index 3883733..3e07f65 100644 --- a/src/tools/sync/git.ts +++ b/src/tools/sync/git.ts @@ -4,10 +4,8 @@ import { simpleGit } from "simple-git"; import { ExecutionContext } from "./types"; - const FALLBACK_DEFAULT_BRANCH = "refs/heads/main"; - export const parseGitRemote = async (): Promise<{ owner?: string; repo?: string; @@ -45,14 +43,16 @@ export type GitContext = ExecutionContext & { defaultBranch?: string; }; -export const getDefaultBranch = async (allowedMethod: "symbolicRef+origin" | "origin" = "symbolicRef+origin"): Promise => { +export const getDefaultBranch = async ( + allowedMethod: "symbolicRef+origin" | "origin" = "symbolicRef+origin", +): Promise => { if (allowedMethod === "symbolicRef+origin") { const symbolicRef = ( - await simpleGit().raw("symbolic-ref", "refs/remotes/origin/HEAD") + await simpleGit().raw("symbolic-ref", "refs/remotes/origin/HEAD") ).trim(); const symbolicRefBranch = symbolicRef.replace( - "refs/remotes/origin/", - "refs/heads/", + "refs/remotes/origin/", + "refs/heads/", ); if (symbolicRefBranch) { @@ -60,16 +60,17 @@ export const getDefaultBranch = async (allowedMethod: "symbolicRef+origin" | "or } } - const origin = await simpleGit().remote(["show", "origin"]); - if (!origin) { - console.warn("could not identify default branch, falling back to 'main'") - return FALLBACK_DEFAULT_BRANCH; - } - - const originDefaultBranch = /HEAD branch:((.*))/.exec(origin) + const origin = await simpleGit().remote(["show", "origin"]); + if (!origin) { + console.warn("could not identify default branch, falling back to 'main'"); + return FALLBACK_DEFAULT_BRANCH; + } - return originDefaultBranch?.groups?.["branchName"] ? `refs/heads/${originDefaultBranch?.groups?.["branchName"]}` : FALLBACK_DEFAULT_BRANCH; + const originDefaultBranch = /HEAD branch:((.*))/.exec(origin); + return originDefaultBranch?.groups?.["branchName"] + ? `refs/heads/${originDefaultBranch?.groups?.["branchName"]}` + : FALLBACK_DEFAULT_BRANCH; }; export const getGitContext = async (): Promise => {