Preserve evidence when local tag inspection fails
This commit is contained in:
@@ -9,7 +9,8 @@ import (
|
||||
)
|
||||
|
||||
// CreateAnnotatedTag records the captured note as an unsigned annotated tag
|
||||
// on the captured commit and returns the tag object's ID.
|
||||
// on the captured commit. Once its type and ID are established, the ID is
|
||||
// returned even if a later verification fails.
|
||||
func CreateAnnotatedTag(ctx context.Context, repositoryDir, tag, commit string, note []byte) (string, error) {
|
||||
if err := CheckTagRef(ctx, repositoryDir, tag); err != nil {
|
||||
return "", err
|
||||
@@ -35,35 +36,43 @@ func CreateAnnotatedTag(ctx context.Context, repositoryDir, tag, commit string,
|
||||
}
|
||||
|
||||
ref := "refs/tags/" + tag
|
||||
object, err := inspectCreatedTag(ctx, repositoryDir, ref, commit, note)
|
||||
if err != nil {
|
||||
return object, fmt.Errorf("verify created tag %s; local ref may remain: %w", ref, err)
|
||||
}
|
||||
return object, nil
|
||||
}
|
||||
|
||||
func inspectCreatedTag(ctx context.Context, repositoryDir, ref, commit string, note []byte) (string, error) {
|
||||
tagType, err := run(ctx, repositoryDir, nil, "cat-file", "-t", ref)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("inspect created tag %s: %w", tag, err)
|
||||
return "", fmt.Errorf("inspect created tag %s: %w", ref, err)
|
||||
}
|
||||
if strings.TrimSpace(tagType) != "tag" {
|
||||
return "", fmt.Errorf("created tag %s has object type %q, want tag", tag, strings.TrimSpace(tagType))
|
||||
return "", fmt.Errorf("created tag %s has object type %q, want tag", ref, strings.TrimSpace(tagType))
|
||||
}
|
||||
tagObject, err := run(ctx, repositoryDir, nil, "rev-parse", "--verify", ref)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read created tag object %s: %w", tag, err)
|
||||
return "", fmt.Errorf("read created tag object %s: %w", ref, err)
|
||||
}
|
||||
tagObject = strings.TrimSpace(tagObject)
|
||||
if !validObjectID(tagObject) {
|
||||
return "", fmt.Errorf("created tag %s has invalid object ID %q", tag, tagObject)
|
||||
return "", fmt.Errorf("created tag %s has invalid object ID %q", ref, tagObject)
|
||||
}
|
||||
peeled, err := run(ctx, repositoryDir, nil, "rev-parse", "--verify", ref+"^{}")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read peeled commit for tag %s: %w", tag, err)
|
||||
return tagObject, fmt.Errorf("read peeled commit for tag %s: %w", ref, err)
|
||||
}
|
||||
if strings.TrimSpace(peeled) != commit {
|
||||
return "", fmt.Errorf("created tag %s peels to %s, want candidate %s", tag, strings.TrimSpace(peeled), commit)
|
||||
return tagObject, fmt.Errorf("created tag %s peels to %s, want candidate %s", ref, strings.TrimSpace(peeled), commit)
|
||||
}
|
||||
tagContents, err := run(ctx, repositoryDir, nil, "cat-file", "tag", ref)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read annotated tag %s: %w", tag, err)
|
||||
return tagObject, fmt.Errorf("read annotated tag %s: %w", ref, err)
|
||||
}
|
||||
separator := bytes.Index([]byte(tagContents), []byte("\n\n"))
|
||||
if separator < 0 || !bytes.Equal([]byte(tagContents[separator+2:]), note) {
|
||||
return "", fmt.Errorf("annotated tag %s message does not match the captured release note", tag)
|
||||
return tagObject, fmt.Errorf("annotated tag %s message does not match the captured release note", ref)
|
||||
}
|
||||
return tagObject, nil
|
||||
}
|
||||
|
||||
@@ -147,10 +147,10 @@ func Publish(ctx context.Context, request Request) (Result, error) {
|
||||
result.Phase = TagCreation
|
||||
result.State = NotPublished
|
||||
tagObject, err := git.CreateAnnotatedTag(ctx, captured.RepositoryDir, captured.Tag, captured.Commit, []byte(captured.Note))
|
||||
result.TagObject = tagObject
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.TagObject = tagObject
|
||||
if err := candidate.Recheck(ctx, captured, tagObject); err != nil {
|
||||
return result, fmt.Errorf("pre-push local guard: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user