Errors and return values¶
Two exported sentinel errors, a set of wrapped errors carrying context, and three cases
where an empty result is returned with no error at all. All errors are built with
cockroachdb/errors, so errors.Is matches
through the wrapping.
The two sentinel errors¶
ErrSinceTagNotFound¶
Returned by GenerateFromRepo and CommitsFromRepo when the value given to
WithSinceTag matches no semver tag and is not the exact name of any tag in the
repository. The output string is empty; nothing is generated.
md, err := changelog.GenerateFromRepo(".", changelog.WithSinceTag("v9.9.9"))
if errors.Is(err, changelog.ErrSinceTagNotFound) {
// the tag is wrong, not the repository
}
The message wraps the offending value: "v9.9.9": since tag not found in repository.
A tag that exists but is not valid semver is taken by its exact name, so
WithSinceTag("lint/v0.6.0") bounds the walk at that tag rather than reporting "not
found"; see A tag that is not a version.
Failing here is deliberate. Silently ignoring an unmatched since tag would emit the entire history for a typo, which looks like a working changelog and is not one.
ErrNoReleases¶
Returned by Parse when the input is non-empty and no release could be recognised — and
propagated by ParseFromArchive when the changelog it extracted is in that state.
cl, err := changelog.Parse(raw)
if errors.Is(err, changelog.ErrNoReleases) {
// present the raw notes instead of a parsed summary
}
The returned *Changelog is non-nil and empty, so a caller that ignores the error gets an
empty model rather than a nil dereference.
ErrNoReleases means "nothing was recognised at all", not "no release heading". A
document with bullets but no headings parses into one anonymous release whose Version is
the empty string, and returns a nil error. Check cl.Releases[i].Version != "" if an
unversioned release would be a problem for you.
Wrapped errors from generation¶
None of these have sentinels; match on them only by string if you must, and prefer to surface them.
| Message prefix | Cause |
|---|---|
resolving repository path |
The path could not be made absolute |
opening git repository |
No repository at the path or above it — e.g. opening git repository: repository does not exist |
listing tags / iterating tags |
The tag references could not be read |
resolving HEAD |
HEAD exists but could not be resolved |
resolving commit for range walk / iterating commits |
A commit object could not be read — typically a corrupt or partial clone |
Generating from a shallow clone fails¶
A shallow clone is the usual real-world cause, and it fails with a message that does not mention shallowness:
The history walk needs the commits the clone did not fetch. Generate from a full clone —
in GitLab CI that means GIT_DEPTH: "0", which is what this project's own pipeline sets.
Wrapped errors from archive parsing¶
| Message prefix | Cause |
|---|---|
failed to open gzip reader |
The stream is not gzip — a plain tar or a zip fails here |
failed to read tar entry |
The tar stream is truncated or corrupt |
failed to read changelog from archive |
The changelog entry could not be read to the end |
Empty results that are not errors¶
Three cases return successfully with nothing in them. All three are deliberate, and all three are easy to mistake for a failure:
| Call | Result | When |
|---|---|---|
GenerateFromRepo |
"", nil |
Repository has no commits, or every commit was dropped by classification |
Parse |
empty *Changelog, nil |
Input is empty or whitespace only |
ParseFromArchive |
nil, nil |
The archive contains no CHANGELOG.md |
ParseFromArchive returns nil, nil when no changelog is found¶
This is the one that bites. A release archive with no changelog in it is not an error —
the design lets a caller fall back to fetching release notes from an API — but the
returned *Changelog is nil, not an empty struct, so cl.HasBreakingChanges() on the
result panics.
cl, err := changelog.ParseFromArchive(resp.Body)
if err != nil {
return err
}
if cl == nil {
// no changelog in the archive; fall back
return fetchNotesFromAPI(ctx, version)
}
Note the asymmetry with Parse, which never returns a nil *Changelog.
FormatSummary never fails¶
FormatSummary returns a string and no error. A nil changelog and a changelog with no
releases both format to the empty string, so an unchecked call cannot panic and cannot
produce a stray heading.