Skip to content

fix: return no version instead of an error when no tag matches - #40

Open
nathanklick wants to merge 2 commits into
PandasWhoCode:masterfrom
nathanklick:fix/no-error-when-no-matching-tags
Open

nathanklick wants to merge 2 commits into
PandasWhoCode:masterfrom
nathanklick:fix/no-error-when-no-matching-tags

Conversation

@nathanklick

@nathanklick nathanklick commented Sep 1, 2026 •

Copy link
Copy Markdown

Problem

findLatestVersionTag turns "no matching tags" into a fatal error, which makes latest and next unusable on two ordinary repositories:

  • a repository which has not been released yet (no tags at all), and
  • a repository whose only tags are pre-releases — the release lookup filters pre-releases out, so it finds nothing.

next is affected in both cases even when a pre-release is requested, because Next always resolves the latest release tag (FindLatestVersion(repo, filter, false), next.go:37) before it ever looks at --pre-release-tag:

$ git-semver latest
level=fatal msg="no matching semantic version tags found"

$ git-semver next --stable=false --pre-release-tag=alpha --pre-release-counter
level=fatal msg="Error while trying to find latest release version tag: no matching semantic version tags found"

This bit a real release pipeline: a repository whose only tags were v0.1.0-alpha.1..4 could not compute any next version, stable or alpha, and the release workflow failed with the second error above.

Fix

Return nil, nil instead of an error when no tag matches. Both callers already treat a nil tag as "no previous version" and fall back to the empty version (0.0.0) — version_log.go even documents it ("fromVersionTag may be null if there is no latest version") — so this is the contract they were written against, and it matches upstream PSanetra/git-semver. The reachability assertion this fork intentionally removed stays removed.

Tests

Seven new Go tests over real on-disk repositories, all of which fail on master and pass here:

  • latest: no tags → no version; only pre-release tags → no version; only pre-release tags with --include-pre-releases → the latest pre-release; pre-releases ignored when a release tag exists.
  • next: first version on a repo without tags; next stable version when only pre-release tags exist; pre-release counter increments when only pre-release tags exist.

Three integration tests were @Disabled("Skipping as it requires a specific version from fork source") for exactly this regression — LatestCmdTests.shouldReturnEmptyVersionOnRepoWithoutTags, LatestCmdIncludingPreReleasesTests.shouldReturnEmptyVersionOnRepoWithoutTags and NextCmdTests.shouldReturnFirstVersionOnRepoWithoutTags — and are re-enabled. The two that remain disabled cover the reachability assertion this fork deliberately dropped; their @Disabled reason now says so instead of implying a version problem.

Verification against the affected repository

Built from this branch and run against the repo that was stuck (only v0.1.0-alpha.1..4, no stable tag):

command master this branch upstream PSanetra
latest fatal 0.0.0 0.0.0
latest --include-pre-releases 0.1.0-alpha.4 0.1.0-alpha.4 0.1.0-alpha.4
next --stable=false fatal 0.1.0 0.1.0
next --stable=false --pre-release-tag=alpha --pre-release-counter fatal 0.1.0-alpha.5 0.1.0-alpha.5

Run from a branch where the newest tag is not reachable from HEAD, this branch still returns 0.1.0-alpha.5 while upstream fatals — the fork's intended relaxation is preserved.

Second commit: the git rev-list exec

Adding the tests above surfaced a second, independent failure in the same function. findLatestVersionTag shelled out to git rev-list --tags --max-count=1 and threw the result away — its own comment reads "We no longer need to capture the output of the command" — but still propagated the exec error. The tags it considers come from repo.Tags(), so the call changed nothing except to make the lookup fail wherever the git binary is absent:

exec: "git": executable file not found in $PATH

That is exactly what happens in this repository's own Dockerfile, whose build stage is golang:1.24-alpine — no git — so RUN go test -v -vet=off ./... fails there for any test touching this path, and with it every integration test, since they build that image. The dead exec is removed; the lookup now relies on go-git alone.

Verified in the shipping image (docker build ., then git semver inside the container):

scenario result
empty repo, latest 0.0.0
empty repo, latest --include-pre-releases 0.0.0
one commit, no tags, next 1.0.0
only v0.1.0-alpha.1..4, next --stable=false --pre-release-tag=alpha --pre-release-counter 0.1.0-alpha.5
v1.2.3 + a feat commit, latest / next 1.2.3 / 1.3.0 (unchanged)

nathanklick and others added 2 commits September 1, 2026 12:49
`findLatestVersionTag` turned "no matching tags" into a fatal error, which
made `latest` and `next` unusable on two ordinary repositories:

  * a repository which has not been released yet (no tags at all), and
  * a repository whose only tags are pre-releases, because the release
    lookup filters pre-releases out.

`next` is affected in both cases even when a pre-release is requested,
since `Next` always resolves the latest *release* tag before it looks at
`--pre-release-tag`:

    $ git-semver next --stable=false --pre-release-tag=alpha --pre-release-counter
    level=fatal msg="Error while trying to find latest release version tag: no matching semantic version tags found"

Both callers already treat a nil tag as "no previous version" and fall
back to the empty version (0.0.0) — `version_log` even documents it — so
return nil instead of an error, restoring the upstream behavior.

The three integration tests covering repositories without tags were
disabled for this regression; re-enable them. The two remaining disabled
tests cover the reachability assertion this fork intentionally removed,
so they stay disabled with an accurate reason.

Signed-off-by: Nathan Klick <nathan@swirldslabs.com>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kVCwjXgKkUCtQz7n1Yitd
`findLatestVersionTag` shelled out to `git rev-list --tags --max-count=1`
and discarded the result — its own comment says "We no longer need to
capture the output of the command". The call had no effect on the tags
that are considered, since those come from `repo.Tags()`, but it did make
the lookup fail outright wherever the git binary is missing or the
repository has no worktree:

    exec: "git": executable file not found in $PATH

That is not hypothetical: the build stage of this repository's own
Dockerfile runs on `golang:1.24-alpine`, which does not ship git, so any
test touching this code path fails during `docker build`.

Drop the dead exec so the lookup relies on go-git alone.

Signed-off-by: Nathan Klick <nathan@swirldslabs.com>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kVCwjXgKkUCtQz7n1Yitd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant