Validate derived candidate tags before checks

This commit is contained in:
2026-11-01 21:13:54 +00:00
parent abbab3c8ce
commit b91a0d1914
8 changed files with 54 additions and 14 deletions

View File

@@ -105,6 +105,9 @@ func Capture(ctx context.Context, request Request) (Candidate, error) {
if err := validateNote(note, request.Product, request.Version); err != nil {
return captured, fmt.Errorf("release note %q: %w", identity.NotePath, err)
}
if err := git.CheckTagRef(ctx, repositoryDir, identity.Tag); err != nil {
return captured, fmt.Errorf("validate local candidate tag: %w", err)
}
if exists, err := hasLocalTag(ctx, repositoryDir, identity.Tag); err != nil {
return captured, fmt.Errorf("inspect local tag %q: %w", identity.Tag, err)
} else if exists {

View File

@@ -9,7 +9,7 @@ import (
// PrepareTaggedCheckout fetches one explicit tag ref, checks its peeled commit
// against expectedCommit, and checks out that commit detached.
func PrepareTaggedCheckout(ctx context.Context, repositoryDir, tag, remote, expectedCommit string) error {
if err := checkTagRef(ctx, repositoryDir, tag); err != nil {
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
return err
}
if !validObjectID(expectedCommit) {
@@ -35,7 +35,7 @@ func PrepareTaggedCheckout(ctx context.Context, repositoryDir, tag, remote, expe
// VerifyTaggedCheckout confirms that the existing local tag, HEAD, and clean
// worktree all identify the expected commit. It does not fetch or check out.
func VerifyTaggedCheckout(ctx context.Context, repositoryDir, tag, expectedCommit string) error {
if err := checkTagRef(ctx, repositoryDir, tag); err != nil {
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
return err
}
if !validObjectID(expectedCommit) {

View File

@@ -100,7 +100,7 @@ func (endpoint Endpoint) Recheck(ctx context.Context, repositoryDir string) erro
// Query reads exact main and tag refs. A command failure is unreadable,
// never evidence that a ref is absent.
func (endpoint Endpoint) Query(ctx context.Context, repositoryDir, tag string) (Snapshot, error) {
if err := checkTagRef(ctx, repositoryDir, tag); err != nil {
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
return Snapshot{}, err
}
mainRef := "refs/heads/main"
@@ -301,7 +301,8 @@ func finalizeRef(ref RemoteRef) RemoteRef {
return ref
}
func checkTagRef(ctx context.Context, repositoryDir, tag string) error {
// CheckTagRef validates a complete tag name using Git ref rules.
func CheckTagRef(ctx context.Context, repositoryDir, tag string) error {
if tag == "" {
return fmt.Errorf("invalid Git tag name %q", tag)
}

View File

@@ -151,7 +151,7 @@ func CaptureHead(ctx context.Context, repositoryDir string) (string, error) {
// InspectTaggedSource reads the local HEAD, worktree state, and selected tag.
// Both lightweight historical tags and annotated tags are supported.
func InspectTaggedSource(ctx context.Context, repositoryDir, tag string) (TaggedSource, error) {
if err := checkTagRef(ctx, repositoryDir, tag); err != nil {
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
return TaggedSource{}, err
}
var source TaggedSource

View File

@@ -35,8 +35,8 @@ func TestInspectTaggedSourceSupportsLightweightAndAnnotatedTags(t *testing.T) {
func TestTagOperationsAcceptFullSlashContainingRefs(t *testing.T) {
fixture := newRemoteFixture(t)
tag := "integrations/check/v2.3.4"
if err := checkTagRef(context.Background(), fixture.work, tag); err != nil {
t.Fatalf("checkTagRef(%q): %v", tag, err)
if err := CheckTagRef(context.Background(), fixture.work, tag); err != nil {
t.Fatalf("CheckTagRef(%q): %v", tag, err)
}
object, err := CreateAnnotatedTag(context.Background(), fixture.work, tag, fixture.commit, []byte("release note\n"))
if err != nil {
@@ -57,8 +57,8 @@ func TestTagOperationsAcceptFullSlashContainingRefs(t *testing.T) {
func TestTagReferenceValidationUsesGitRules(t *testing.T) {
fixture := newRemoteFixture(t)
for _, tag := range []string{"", "v1..2.3", "v1.2.3.lock", "v1.2.3/", "v1.2.3\\bad"} {
if err := checkTagRef(context.Background(), fixture.work, tag); err == nil {
t.Errorf("checkTagRef(%q) succeeded", tag)
if err := CheckTagRef(context.Background(), fixture.work, tag); err == nil {
t.Errorf("CheckTagRef(%q) succeeded", tag)
}
}
}

View File

@@ -11,7 +11,7 @@ import (
// CreateAnnotatedTag records the captured note as an unsigned annotated tag
// on the captured commit and returns the tag object's ID.
func CreateAnnotatedTag(ctx context.Context, repositoryDir, tag, commit string, note []byte) (string, error) {
if err := checkTagRef(ctx, repositoryDir, tag); err != nil {
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
return "", err
}
if !validObjectID(commit) {
@@ -71,7 +71,7 @@ func CreateAnnotatedTag(ctx context.Context, repositoryDir, tag, commit string,
// InspectLocalTag confirms the selected tag still points to the exact
// annotated tag object captured before publication.
func InspectLocalTag(ctx context.Context, repositoryDir, tag, expectedObject string) error {
if err := checkTagRef(ctx, repositoryDir, tag); err != nil {
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
return err
}
if !validObjectID(expectedObject) {
@@ -99,7 +99,7 @@ func InspectLocalTag(ctx context.Context, repositoryDir, tag, expectedObject str
// one exact authoritative version tag and its peeled commit. Failed
// verification or reads are unreadable and never interpreted as absence.
func (endpoint Endpoint) QueryTag(ctx context.Context, repositoryDir, tag string) (RemoteRef, error) {
if err := checkTagRef(ctx, repositoryDir, tag); err != nil {
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
return RemoteRef{}, err
}
if err := endpoint.verifyScopedURLs(ctx, repositoryDir); err != nil {
@@ -129,7 +129,7 @@ func (endpoint Endpoint) QueryTag(ctx context.Context, repositoryDir, tag string
// PushTag publishes only the captured tag object to its exact tag ref.
func (endpoint Endpoint) PushTag(ctx context.Context, repositoryDir, tag, tagObject string) error {
if err := checkTagRef(ctx, repositoryDir, tag); err != nil {
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
return err
}
if !validObjectID(tagObject) {