From ead44199d7d350d3deb01789d729c65c567c7c05 Mon Sep 17 00:00:00 2001 From: Ben Rogerson Date: Wed, 13 May 2026 23:07:57 +0930 Subject: [PATCH 1/2] fix: parse @ref suffix from git URLs in classifySpecifier The templates README documents tag/branch pinning via `url@v2.0.0` syntax, but classifySpecifier was treating the suffix as part of the URL. Now the resolver extracts @ref into a separate field and passes it to cloneToTemp, so git clone receives the correct reference without the unparsed suffix.\n\nAdds tests for HTTPS and SSH-style git URLs with @ref suffixes. --- packages/core/src/cli/resolver.test.ts | 26 ++++++++++++++++++++++++++ packages/core/src/cli/resolver.ts | 12 +++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/core/src/cli/resolver.test.ts b/packages/core/src/cli/resolver.test.ts index 20bed50..2efe934 100644 --- a/packages/core/src/cli/resolver.test.ts +++ b/packages/core/src/cli/resolver.test.ts @@ -26,6 +26,32 @@ describe("classifySpecifier", () => { strictEqual(classifySpecifier("https://github.com/ben/repo.git").kind, "git-url"); strictEqual(classifySpecifier("git@github.com:ben/repo.git").kind, "git-url"); }); + + it("extracts @ref suffix from git URLs", () => { + const c = classifySpecifier("https://github.com/you/template.git@v2"); + strictEqual(c.kind, "git-url"); + if (c.kind === "git-url") { + strictEqual(c.url, "https://github.com/you/template.git"); + strictEqual(c.ref, "v2"); + } + }); + + it("leaves ref undefined for git URLs without @ref suffix", () => { + const c = classifySpecifier("https://github.com/ben/repo.git"); + strictEqual(c.kind, "git-url"); + if (c.kind === "git-url") { + strictEqual(c.ref, undefined); + } + }); + + it("extracts @ref from SSH-style git URLs", () => { + const c = classifySpecifier("git@github.com:you/template.git@v2"); + strictEqual(c.kind, "git-url"); + if (c.kind === "git-url") { + strictEqual(c.url, "git@github.com:you/template.git"); + strictEqual(c.ref, "v2"); + } + }); }); describe("resolveTemplate (local)", () => { diff --git a/packages/core/src/cli/resolver.ts b/packages/core/src/cli/resolver.ts index 2fe2371..11bb96d 100644 --- a/packages/core/src/cli/resolver.ts +++ b/packages/core/src/cli/resolver.ts @@ -11,7 +11,7 @@ const exec = promisify(execFile); export type Specifier = | { kind: "catalog"; alias: string } | { kind: "github-short"; url: string } - | { kind: "git-url"; url: string } + | { kind: "git-url"; url: string; ref?: string } | { kind: "local"; path: string }; export interface ResolvedTemplate { @@ -39,8 +39,13 @@ export function classifySpecifier(spec: string): Specifier { spec.startsWith("git@") || spec.startsWith("https://") || spec.startsWith("git+") || - spec.endsWith(".git") + spec.endsWith(".git") || + /\.git@[^@]+$/.test(spec) ) { + const parts = spec.split(/\.git@(.+)$/); + if (parts.length >= 2 && parts[1]) { + return { kind: "git-url", url: `${parts[0]}.git`, ref: parts[1] }; + } return { kind: "git-url", url: spec }; } if (isAbsolute(spec) || spec.startsWith("./") || spec.startsWith("../")) { @@ -76,7 +81,8 @@ export async function resolveTemplate(spec: string): Promise { } if (classified.kind === "github-short" || classified.kind === "git-url") { - const cloned = await cloneToTemp(classified.url); + const ref = classified.kind === "git-url" ? classified.ref : undefined; + const cloned = await cloneToTemp(classified.url, ref); assertHasManifest(cloned.dir, classified.url, cloned.cleanup); return cloned; } From 6eb002e736d968aa447b60c2c1635faf3bf1ef40 Mon Sep 17 00:00:00 2001 From: Ben Rogerson Date: Wed, 13 May 2026 23:07:58 +0930 Subject: [PATCH 2/2] chore: add changeset --- .changeset/git-url-tag-suffix-not-parsed.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/git-url-tag-suffix-not-parsed.md diff --git a/.changeset/git-url-tag-suffix-not-parsed.md b/.changeset/git-url-tag-suffix-not-parsed.md new file mode 100644 index 0000000..353ea01 --- /dev/null +++ b/.changeset/git-url-tag-suffix-not-parsed.md @@ -0,0 +1,5 @@ +--- +"@generata/core": patch +--- + +fix: parse @ref suffix from git URLs in classifySpecifier