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
54 changes: 52 additions & 2 deletions cmd/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,52 @@ func init() {
rootCmd.AddCommand(commitCmd)
}

// getGPGSignStatus는 현재 GPG 서명 상태를 문자열로 반환합니다
func getGPGSignStatus(cfg *config.Config, msg i18n.Messages) string {
if noGPGSign {
if cfg.UILanguage == "ko" {
return "비활성화 (--no-gpg-sign)"
}
return "disabled (--no-gpg-sign)"
} else if gpgSign {
if cfg.UILanguage == "ko" {
return "활성화 (-S)"
}
return "enabled (-S)"
} else if cfg.GPGSign {
if cfg.UILanguage == "ko" {
return "활성화 (설정)"
}
return "enabled (config)"
}
if cfg.UILanguage == "ko" {
return "비활성화"
}
return "disabled"
}

// getCommitOptions는 설정값과 플래그를 조합하여 CommitOptions를 반환합니다
// 우선순위: 플래그 > 설정 파일
func getCommitOptions(cfg *config.Config) git.CommitOptions {
opts := git.CommitOptions{
NoVerify: noVerify,
}

// GPG 서명 처리: 플래그가 명시적으로 지정되면 플래그 우선
if noGPGSign {
// --no-gpg-sign 플래그가 지정되면 서명 비활성화
opts.NoGPGSign = true
} else if gpgSign {
// -S / --gpg-sign 플래그가 지정되면 서명 활성화
opts.GPGSign = true
} else if cfg.GPGSign {
// 플래그 미지정 시 설정 파일 값 사용
opts.GPGSign = true
}

return opts
}

func runCommit() error {
// 3. 설정 로드
cfg, err := config.Load()
Expand All @@ -65,6 +111,10 @@ func runCommit() error {
return fmt.Errorf("%s: %w", msg.ErrorNoStagedChanges, err)
}

// GPG 서명 상태 표시
gpgStatus := getGPGSignStatus(cfg, msg)
color.Cyan(msg.GPGSignStatus, gpgStatus)

if diff == "" {
return errors.New(msg.NoChanges)
}
Expand Down Expand Up @@ -146,7 +196,7 @@ func runCommit() error {
case msg.PromptYes:
// 8. 커밋 실행
color.Cyan(msg.Committing)
if err := git.Commit(commitMessage, noVerify); err != nil {
if err := git.Commit(commitMessage, getCommitOptions(cfg)); err != nil {
return fmt.Errorf("%s: %w", msg.ErrorCommitFailed, err)
}
color.Green(msg.CommitSuccess)
Expand Down Expand Up @@ -206,7 +256,7 @@ func runCommit() error {
case msg.EditActionUseMessage:
// 현재 메시지로 커밋
color.Cyan(msg.Committing)
if err := git.Commit(commitMessage, noVerify); err != nil {
if err := git.Commit(commitMessage, getCommitOptions(cfg)); err != nil {
return fmt.Errorf("%s: %w", msg.ErrorCommitFailed, err)
}
color.Green(msg.CommitSuccess)
Expand Down
35 changes: 35 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ var showCmd = &cobra.Command{
color.White(msg.ConfigCommitLanguage, cfg.CommitLanguage)
color.White(msg.ConfigUILanguage, cfg.UILanguage)
color.White(msg.ConfigTemplate, cfg.Template)
color.White(msg.ConfigGPGSign, cfg.GPGSign)
fmt.Println()

color.Yellow(msg.OpenAISettings)
Expand Down Expand Up @@ -251,12 +252,46 @@ func maskAPIKey(key string) string {
return key[:4] + "..." + key[len(key)-4:]
}

var setGPGSignCmd = &cobra.Command{
Use: "set-gpg-sign [true|false]",
Short: "GPG 서명 사용 여부를 설정합니다",
Long: `커밋 시 GPG 서명을 기본으로 사용할지 설정합니다.`,
Example: ` commitmate config set-gpg-sign true
commitmate config set-gpg-sign false`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
value := args[0]

cfg, err := config.Load()
if err != nil {
cfg = config.Default()
}

msg := i18n.GetMessages(cfg.UILanguage)

if value != "true" && value != "false" {
color.Red("❌ " + msg.ErrorInvalidBoolValue)
os.Exit(1)
}

cfg.GPGSign = value == "true"

if err := config.Save(cfg); err != nil {
color.Red("❌ "+msg.ErrorSaveConfig, err)
os.Exit(1)
}

color.Green(msg.GPGSignSet, cfg.GPGSign)
},
}

func init() {
rootCmd.AddCommand(configCmd)
configCmd.AddCommand(setKeyCmd)
configCmd.AddCommand(setProviderCmd)
configCmd.AddCommand(setModelCmd)
configCmd.AddCommand(setCommitLanguageCmd)
configCmd.AddCommand(setUILanguageCmd)
configCmd.AddCommand(setGPGSignCmd)
configCmd.AddCommand(showCmd)
}
12 changes: 8 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
)

var (
cfgFile string
provider string
dryRun bool
noVerify bool
cfgFile string
provider string
dryRun bool
noVerify bool
gpgSign bool
noGPGSign bool
)

// rootCmd는 인자 없이 호출될 때의 기본 명령어입니다
Expand Down Expand Up @@ -48,6 +50,8 @@ func init() {
// 로컬 플래그
rootCmd.Flags().BoolVar(&dryRun, "dry-run", false, "커밋 메시지만 생성하고 커밋하지 않음")
rootCmd.Flags().BoolVar(&noVerify, "no-verify", false, "git commit hooks 무시")
rootCmd.Flags().BoolVarP(&gpgSign, "gpg-sign", "S", false, "GPG 서명으로 커밋")
rootCmd.Flags().BoolVar(&noGPGSign, "no-gpg-sign", false, "GPG 서명 비활성화")
}

// initConfig는 설정 파일과 환경변수를 읽습니다
Expand Down
12 changes: 9 additions & 3 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import (
type Config struct {
Provider string `mapstructure:"provider"`
CommitLanguage string `mapstructure:"commit_language"` // AI가 생성하는 커밋 메시지 언어
UILanguage string `mapstructure:"ui_language"` // CLI UI 메시지 언어
UILanguage string `mapstructure:"ui_language"` // CLI UI 메시지 언어
Template string `mapstructure:"template"`
GPGSign bool `mapstructure:"gpg_sign"` // GPG 서명 사용 여부
OpenAI OpenAIConfig `mapstructure:"openai"`
Claude ClaudeConfig `mapstructure:"claude"`
}
Expand All @@ -36,9 +37,10 @@ type ClaudeConfig struct {
func Default() *Config {
return &Config{
Provider: "openai",
CommitLanguage: "en", // 커밋 메시지는 영어가 기본
UILanguage: "ko", // UI는 한글이 기본
CommitLanguage: "en", // 커밋 메시지는 영어가 기본
UILanguage: "ko", // UI는 한글이 기본
Template: "conventional",
GPGSign: false, // GPG 서명은 기본적으로 비활성화
OpenAI: OpenAIConfig{
APIKey: "",
Model: "gpt-4o",
Expand Down Expand Up @@ -94,6 +96,9 @@ func Load() (*Config, error) {
if uiLang := os.Getenv("COMMITMATE_UI_LANGUAGE"); uiLang != "" {
cfg.UILanguage = uiLang
}
if gpgSign := os.Getenv("COMMITMATE_GPG_SIGN"); gpgSign != "" {
cfg.GPGSign = gpgSign == "true" || gpgSign == "1"
}

return cfg, nil
}
Expand All @@ -117,6 +122,7 @@ func Save(cfg *Config) error {
viper.Set("commit_language", cfg.CommitLanguage)
viper.Set("ui_language", cfg.UILanguage)
viper.Set("template", cfg.Template)
viper.Set("gpg_sign", cfg.GPGSign)
viper.Set("openai.api_key", cfg.OpenAI.APIKey)
viper.Set("openai.model", cfg.OpenAI.Model)
viper.Set("openai.max_tokens", cfg.OpenAI.MaxTokens)
Expand Down
52 changes: 45 additions & 7 deletions internal/git/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,65 @@ package git

import (
"bytes"
"os"
"os/exec"
)

// CommitOptions는 커밋 옵션을 담는 구조체입니다
type CommitOptions struct {
NoVerify bool
GPGSign bool // true면 -S 플래그 추가
NoGPGSign bool // true면 --no-gpg-sign 플래그 추가
}

// Commit은 주어진 메시지로 커밋을 생성합니다
func Commit(message string, noVerify bool) error {
func Commit(message string, opts CommitOptions) error {
args := []string{"commit", "-m", message}

if noVerify {
if opts.NoVerify {
args = append(args, "--no-verify")
}

// GPG 서명 옵션 처리
if opts.GPGSign {
args = append(args, "-S")
} else if opts.NoGPGSign {
args = append(args, "--no-gpg-sign")
}

cmd := exec.Command("git", args...)

var stderr bytes.Buffer
cmd.Stderr = &stderr
// TTY 연결로 GPG pinentry 지원
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

if err := cmd.Run(); err != nil {
return err
return cmd.Run()
}

// CommitWithFile은 파일에서 커밋 메시지를 읽어 커밋합니다 (멀티라인 메시지용)
func CommitWithFile(messageFile string, opts CommitOptions) error {
args := []string{"commit", "-F", messageFile}

if opts.NoVerify {
args = append(args, "--no-verify")
}

// GPG 서명 옵션 처리
if opts.GPGSign {
args = append(args, "-S")
} else if opts.NoGPGSign {
args = append(args, "--no-gpg-sign")
}

return nil
cmd := exec.Command("git", args...)

// TTY 연결로 GPG pinentry 지원
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

return cmd.Run()
}

// GetLastCommitMessage는 마지막 커밋 메시지를 반환합니다
Expand Down
7 changes: 5 additions & 2 deletions internal/i18n/en.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ func getEnglishMessages() Messages {
// Git operations
CheckingRepository: "🔍 Checking repository...",
AnalyzingStagedChanges: "📝 Analyzing staged changes...",
GPGSignStatus: "🔐 GPG signing: %s",
LargeChangesWarning: "⚠️ Large changes detected (%d bytes). Only summarized information will be sent.",
NoChanges: "No changes to commit. Please run 'git add' first",

Expand Down Expand Up @@ -35,13 +36,14 @@ func getEnglishMessages() Messages {
EditActionBack: "↩️ Back - go back",

// Config
ConfigTitle: "📋 Current configuration:",
ConfigTitle: "📋 Current configuration:",
ConfigProvider: "Default provider: %s",
ConfigCommitLanguage: "Commit message language: %s",
ConfigUILanguage: "UI language: %s",
ConfigTemplate: "Template: %s",
ConfigGPGSign: "GPG sign: %v",
OpenAISettings: "OpenAI settings:",
ClaudeSettings: "Claude settings:",
ClaudeSettings: "Claude settings:",
APIKeyLabel: " API key: %s",
APIKeyNotSet: " API key: (not set)",
ModelLabel: " Model: %s",
Expand All @@ -53,6 +55,7 @@ func getEnglishMessages() Messages {
ModelSet: "✓ %s model set to %s",
CommitLanguageSet: "✓ Commit message language set to %s",
UILanguageSet: "✓ UI language set to %s",
GPGSignSet: "✓ GPG sign set to %v",

// Error messages
ErrorNotGitRepo: "Not a git repository",
Expand Down
3 changes: 3 additions & 0 deletions internal/i18n/i18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type Messages struct {
// Git 작업
CheckingRepository string
AnalyzingStagedChanges string
GPGSignStatus string
LargeChangesWarning string
NoChanges string

Expand Down Expand Up @@ -40,6 +41,7 @@ type Messages struct {
ConfigCommitLanguage string
ConfigUILanguage string
ConfigTemplate string
ConfigGPGSign string
OpenAISettings string
ClaudeSettings string
APIKeyLabel string
Expand All @@ -53,6 +55,7 @@ type Messages struct {
ModelSet string
CommitLanguageSet string
UILanguageSet string
GPGSignSet string

// 에러 메시지
ErrorNotGitRepo string
Expand Down
3 changes: 3 additions & 0 deletions internal/i18n/ko.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ func getKoreanMessages() Messages {
// Git 작업
CheckingRepository: "🔍 Git 저장소 확인 중...",
AnalyzingStagedChanges: "📝 Staged 변경사항 분석 중...",
GPGSignStatus: "🔐 GPG 서명: %s",
LargeChangesWarning: "⚠️ 변경사항이 큽니다 (%d 바이트). 요약된 정보만 전달됩니다.",
NoChanges: "커밋할 변경사항이 없습니다. 'git add' 명령어를 먼저 실행하세요",

Expand Down Expand Up @@ -40,6 +41,7 @@ func getKoreanMessages() Messages {
ConfigCommitLanguage: "커밋 메시지 언어: %s",
ConfigUILanguage: "UI 언어: %s",
ConfigTemplate: "템플릿: %s",
ConfigGPGSign: "GPG 서명: %v",
OpenAISettings: "OpenAI 설정:",
ClaudeSettings: "Claude 설정:",
APIKeyLabel: " API 키: %s",
Expand All @@ -53,6 +55,7 @@ func getKoreanMessages() Messages {
ModelSet: "✓ %s 모델이 %s로 설정되었습니다",
CommitLanguageSet: "✓ 커밋 메시지 언어가 %s로 설정되었습니다",
UILanguageSet: "✓ UI 언어가 %s로 설정되었습니다",
GPGSignSet: "✓ GPG 서명이 %v로 설정되었습니다",

// 에러 메시지
ErrorNotGitRepo: "Git 저장소가 아닙니다",
Expand Down
Loading