Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions cmd/tag/new_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ package tag

import (
"bytes"
"errors"
"fmt"
"os/exec"
"regexp"
Expand All @@ -53,13 +54,16 @@ import (
"github.com/tonbiattack/git-plus/internal/ui"
)

const initialVersionTag = "v0.0.0"

var (
tagMessage string // タグメッセージ(アノテーテッドタグ用)
tagPush bool // 作成後に自動的にリモートへプッシュするフラグ
tagDryRun bool // 実際には作成せず、次のバージョンだけを表示するフラグ
tagRelease bool // プッシュ後に自動的にGitHubリリースを作成するフラグ
tagReleaseDraft bool // リリースをドラフトとして作成するフラグ
tagReleasePrerelease bool // リリースをプレリリースとして作成するフラグ
errNoGitTags = errors.New("git repository has no tags")
)

// newTagCmd は new-tag コマンドの定義です。
Expand All @@ -85,9 +89,13 @@ var newTagCmd = &cobra.Command{
// 最新タグを取得
currentTag, err := getLatestTag()
if err != nil {
fmt.Println("エラー: 最新タグの取得に失敗しました")
fmt.Println("タグが存在しない可能性があります。最初のタグを手動で作成してください。")
return err
if errors.Is(err, errNoGitTags) {
fmt.Printf("既存のタグが見つからないため、初期タグ %s から新しいタグを計算します。\n", initialVersionTag)
currentTag = initialVersionTag
} else {
fmt.Println("エラー: 最新タグの取得に失敗しました")
return err
}
}

// バージョンを解析
Expand Down Expand Up @@ -197,11 +205,34 @@ func getLatestTag() (string, error) {
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
output, err := cmd.Output()
if err != nil {
if isNoTagsDescribeError(err) {
return "", errNoGitTags
}
return "", err
}
return strings.TrimSpace(string(output)), nil
}

// isNoTagsDescribeError は git describe の結果が「タグが存在しない」場合を判定します。
//
// パラメータ:
// - err: git describe 実行時に発生したエラー
//
// 戻り値:
// - bool: エラーがタグ未作成によるものなら true
func isNoTagsDescribeError(err error) bool {
if err == nil {
return false
}

var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
stderr := string(exitErr.Stderr)
return strings.Contains(stderr, "No names found, cannot describe anything.")
}
return false
}

// extractVersion はタグからバージョン番号を抽出します。
//
// パラメータ:
Expand Down
44 changes: 44 additions & 0 deletions cmd/tag/new_tag_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package tag

import (
"errors"
"os/exec"
"testing"

"github.com/tonbiattack/git-plus/cmd"
Expand Down Expand Up @@ -346,3 +348,45 @@ func TestVersionTypeAliases(t *testing.T) {
}
}
}

// TestIsNoTagsDescribeError は git describe でタグが存在しない場合のエラー判定をテストします
func TestIsNoTagsDescribeError(t *testing.T) {
tests := []struct {
name string
err error
want bool
}{
{
name: "detects no tags error",
err: &exec.ExitError{
Stderr: []byte("fatal: No names found, cannot describe anything.\n"),
},
want: true,
},
{
name: "ignores other exit errors",
err: &exec.ExitError{
Stderr: []byte("fatal: not a git repository (or any of the parent directories): .git"),
},
want: false,
},
{
name: "ignores generic errors",
err: errors.New("some other error"),
want: false,
},
{
name: "nil error",
err: nil,
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isNoTagsDescribeError(tt.err); got != tt.want {
t.Fatalf("isNoTagsDescribeError() = %v, want %v", got, tt.want)
}
})
}
}
Loading