From e7d4b9b8884ec6abfe8c8d12faa5e082eba41f67 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 15 Jun 2026 15:34:28 +0000 Subject: [PATCH] fix(registry): skip publish when git tag not pushed yet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The publish-all job discovers versions from registry APIs (npm/pip) but builds by cloning the matching git tag. When a registry publishes a version before its git tag lands (e.g. vitest 4.1.9 on npm with no v4.1.9 tag yet), the clone fails and the whole job exits 1. Detect missing-ref git errors via a new isMissingRefError helper and treat them as a skip in publish-all — the next run picks the version up once the tag is pushed. https://claude.ai/code/session_01N5iVzHsBqYBZ5oA78kGNsW --- .changeset/quiet-pandas-jump.md | 5 +++++ packages/context/src/git.test.ts | 22 ++++++++++++++++++++++ packages/context/src/git.ts | 19 +++++++++++++++++++ packages/context/src/index.ts | 1 + packages/registry/src/cli.ts | 8 ++++++++ 5 files changed, 55 insertions(+) create mode 100644 .changeset/quiet-pandas-jump.md diff --git a/.changeset/quiet-pandas-jump.md b/.changeset/quiet-pandas-jump.md new file mode 100644 index 0000000..655a3c9 --- /dev/null +++ b/.changeset/quiet-pandas-jump.md @@ -0,0 +1,5 @@ +--- +"@neuledge/context": minor +--- + +Add `isMissingRefError` helper to detect git "ref not found" errors (e.g. when a registry publishes a version before its git tag is pushed) diff --git a/packages/context/src/git.test.ts b/packages/context/src/git.test.ts index 59baeda..925b4fd 100644 --- a/packages/context/src/git.test.ts +++ b/packages/context/src/git.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; import { findLatestStableVersion, + isMissingRefError, isTransientGitError, parseMonorepoTag, } from "./git.js"; @@ -39,6 +40,27 @@ describe("isTransientGitError", () => { }); }); +describe("isMissingRefError", () => { + it("detects a tag not pushed yet (npm ahead of git)", () => { + expect( + isMissingRefError( + "Git clone failed: Cloning into '/tmp/context-git-9itQYk'...\nfatal: Remote branch v4.1.9 not found in upstream origin", + ), + ).toBe(true); + }); + + it("detects a missing pathspec/reference on checkout", () => { + expect( + isMissingRefError("error: pathspec 'v4.1.9' did not match any file(s)"), + ).toBe(true); + }); + + it("rejects transient and unrelated errors", () => { + expect(isMissingRefError("Could not resolve host: github.com")).toBe(false); + expect(isMissingRefError("remote: Repository not found.")).toBe(false); + }); +}); + describe("parseMonorepoTag", () => { it("parses plain version tags", () => { expect(parseMonorepoTag("1.2.3")).toEqual({ diff --git a/packages/context/src/git.ts b/packages/context/src/git.ts index edc2f5c..fa764d1 100755 --- a/packages/context/src/git.ts +++ b/packages/context/src/git.ts @@ -231,6 +231,25 @@ export function isTransientGitError(message: string): boolean { return TRANSIENT_GIT_ERROR_PATTERNS.some((re) => re.test(message)); } +/** + * Git error patterns indicating the requested ref (tag/branch) doesn't exist + * in the remote. Happens when a registry (e.g. npm) publishes a version before + * the matching git tag is pushed — a transient state that self-heals once the + * tag lands, so callers can skip rather than hard-fail. + */ +const MISSING_REF_GIT_ERROR_PATTERNS = [ + /remote branch .* not found in upstream/i, + /could not find remote (branch|ref)/i, + /(pathspec|reference) .* did not match/i, +]; + +/** + * Check if a git error message indicates the requested ref doesn't exist. + */ +export function isMissingRefError(message: string): boolean { + return MISSING_REF_GIT_ERROR_PATTERNS.some((re) => re.test(message)); +} + /** * Synchronous sleep (cloneRepository is sync, so no await available). */ diff --git a/packages/context/src/index.ts b/packages/context/src/index.ts index bd30f21..d2aa3a9 100644 --- a/packages/context/src/index.ts +++ b/packages/context/src/index.ts @@ -39,6 +39,7 @@ export { extractVersion, type GitCloneResult, isGitUrl, + isMissingRefError, type LocalDocsResult, parseGitUrl, readLocalDocsFiles, diff --git a/packages/registry/src/cli.ts b/packages/registry/src/cli.ts index 905bb7e..1a9a077 100644 --- a/packages/registry/src/cli.ts +++ b/packages/registry/src/cli.ts @@ -7,6 +7,7 @@ import { mkdirSync, rmSync } from "node:fs"; import { resolve } from "node:path"; +import { isMissingRefError } from "@neuledge/context"; import { Command } from "commander"; import { buildFromDefinition, @@ -291,6 +292,13 @@ program succeeded++; } catch (err) { const message = err instanceof Error ? err.message : String(err); + // A registry can publish a version before its git tag is pushed. + // Skip (don't fail) — the next run picks it up once the tag lands. + if (isMissingRefError(message)) { + console.log(` Skipping ${id} (git tag not published yet)`); + skipped++; + continue; + } console.error(` FAILED ${id}: ${message}`); failures.push({ id, error: message }); }