tag.go

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5package main
 6
 7import (
 8	"fmt"
 9	"strings"
10
11	"github.com/spf13/cobra"
12)
13
14var (
15	tagMessage string
16	tagBody    string
17	tagForce   bool
18)
19
20var tagCmd = &cobra.Command{
21	Use:   "git-formatted-tag NAME",
22	Short: "Create formatted annotated Git tags",
23	Long: `git-formatted-tag helps you create well-formatted annotated Git tags with
24proper subject length validation and body wrapping.`,
25	Example: `
26# Simple tag with just a message
27git formatted-tag v1.0.0 -m "Initial stable release"
28
29# Tag with body
30git formatted-tag v1.2.0 -m "Add user authentication" -b "$(cat <<'EOF'
31✨ Features
32- [auth]: implement JWT-based authentication
33- [api]: add login and logout endpoints
34
35🐛 Bug Fixes
36- [login]: correct password validation error
37EOF
38)"
39
40# Force overwrite an existing tag
41git formatted-tag v1.0.0 -m "Re-release with hotfix" -f
42`,
43	Args: cobra.ExactArgs(1),
44	RunE: func(cmd *cobra.Command, args []string) error {
45		tagName := args[0]
46
47		if tagMessage == "" {
48			return fmt.Errorf("formatted-tag requires at least a subject (-m)\nFor lightweight tags, use: git tag %s", tagName)
49		}
50
51		if err := validateSubjectLength(tagMessage); err != nil {
52			return err
53		}
54
55		var tagContent strings.Builder
56		tagContent.WriteString(tagMessage)
57
58		if tagBody != "" {
59			formattedBody, err := formatBody(tagBody)
60			if err != nil {
61				return fmt.Errorf("failed to format body: %w", err)
62			}
63			tagContent.WriteString("\n\n")
64			tagContent.WriteString(formattedBody)
65		}
66
67		gitArgs := []string{"tag", "-a", tagName}
68		if tagForce {
69			gitArgs = append(gitArgs, "-f")
70		}
71		gitArgs = append(gitArgs, "-F", "-")
72
73		return runGitWithStdin(gitArgs, tagContent.String())
74	},
75}
76
77func init() {
78	tagCmd.Flags().StringVarP(&tagMessage, "message", "m", "", "tag subject (required, max 50 characters)")
79	tagCmd.Flags().StringVarP(&tagBody, "body", "b", "", "tag body (optional)")
80	tagCmd.Flags().BoolVarP(&tagForce, "force", "f", false, "replace existing tag")
81}