diff --git a/.commit.json b/.commit.json index 00b0502..fb84559 100644 --- a/.commit.json +++ b/.commit.json @@ -1,3 +1,3 @@ { - "issueRegex": "#[0-9]+" -} \ No newline at end of file + "issueRegex": "[0-9]+" +} diff --git a/.gitignore b/.gitignore index 7a6548b..0152cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.so *.dylib commit +!cmd/commit # Test binary, built with `go test -c` *.test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..abda6cc --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +.PHONY: build +build: + @go build -o bin/ ./cmd/commit + +.PHONY: clean +clean: + @if [ -f bin/commit ]; then \ + rm bin/commit ; \ + fi; + @go clean + +.PHONY: run +run: + @go run ./cmd/commit "$(filter-out $@,$(MAKECMDGOALS))" + +.PHONY: test +test: + @go test -v ./tests/ + +%: + @: diff --git a/main.go b/cmd/commit/main.go similarity index 76% rename from main.go rename to cmd/commit/main.go index ff9903d..2511081 100644 --- a/main.go +++ b/cmd/commit/main.go @@ -7,6 +7,10 @@ import ( "strings" "time" + "github.com/artem-y/commit/internal/config" + "github.com/artem-y/commit/internal/helpers" + "github.com/artem-y/commit/internal/user" + "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" @@ -15,9 +19,9 @@ import ( func main() { commitMessage := getCommitMessage() - issueRegex := default_issue_regex + issueRegex := helpers.DEFAULT_ISSUE_REGEX - commitCfg := readCommitConfig() + commitCfg := config.ReadCommitConfig() if commitCfg.IssueRegex != "" { issueRegex = commitCfg.IssueRegex } @@ -51,7 +55,7 @@ func main() { func getCommitMessage() string { args := os.Args[1:] if len(args) < 1 { - fmt.Fprintln(os.Stderr, red("Commit message cannot be empty")) + fmt.Fprintln(os.Stderr, helpers.Red("Commit message cannot be empty")) os.Exit(1) } @@ -62,7 +66,7 @@ func getCommitMessage() string { func openRepo() *git.Repository { repo, err := git.PlainOpen(".") if err != nil { - fmt.Fprintf(os.Stderr, red("Failed to open repository: %v\n"), err) + fmt.Fprintf(os.Stderr, helpers.Red("Failed to open repository: %v\n"), err) os.Exit(1) } return repo @@ -72,7 +76,7 @@ func openRepo() *git.Repository { func getCurrentHead(repo *git.Repository) *plumbing.Reference { headRef, err := repo.Head() if err != nil { - fmt.Fprintf(os.Stderr, red("Failed to read current HEAD: %v\n"), err) + fmt.Fprintf(os.Stderr, helpers.Red("Failed to read current HEAD: %v\n"), err) os.Exit(1) } return headRef @@ -82,17 +86,12 @@ func getCurrentHead(repo *git.Repository) *plumbing.Reference { func openWorktree(repo *git.Repository) *git.Worktree { worktree, err := repo.Worktree() if err != nil { - fmt.Fprintf(os.Stderr, red("Failed to open worktree: %v\n"), err) + fmt.Fprintf(os.Stderr, helpers.Red("Failed to open worktree: %v\n"), err) os.Exit(1) } return worktree } -// Wraps the message string in red color -func red(msg string) string { - return fmt.Sprintf("\033[31m%s\033[0m", msg) -} - // Searches the branch name for issue numbers matching the given regex func findIssueMatchesInBranch(rgxRaw string, branchName string) []string { rgx := regexp.MustCompile(rgxRaw) @@ -108,11 +107,11 @@ func findIssueMatchesInBranch(rgxRaw string, branchName string) []string { } // Creates commit options with the author information -func makeCommitOptions(usr user) git.CommitOptions { +func makeCommitOptions(usr user.User) git.CommitOptions { return git.CommitOptions{ Author: &object.Signature{ - Name: usr.name, - Email: usr.email, + Name: usr.Name, + Email: usr.Email, When: time.Now(), }, AllowEmptyCommits: false, @@ -123,12 +122,12 @@ func makeCommitOptions(usr user) git.CommitOptions { // Commits changes with provided message func commitChanges(repo *git.Repository, commitMessage string) { worktree := openWorktree(repo) - usr := getUser(*repo) + usr := user.GetUser(*repo) commitOptions := makeCommitOptions(usr) _, err := worktree.Commit(commitMessage, &commitOptions) if err != nil { - fmt.Fprintf(os.Stderr, red("Failed to commit: %v\n"), err) + fmt.Fprintf(os.Stderr, helpers.Red("Failed to commit: %v\n"), err) os.Exit(1) } } diff --git a/constants.go b/constants.go deleted file mode 100644 index 29f035b..0000000 --- a/constants.go +++ /dev/null @@ -1,6 +0,0 @@ -package main - -const ( - default_config_file_path = ".commit.json" - default_issue_regex = "#[0-9]+" -) diff --git a/config.go b/internal/config/config.go similarity index 58% rename from config.go rename to internal/config/config.go index 42ca433..4a1fa9c 100644 --- a/config.go +++ b/internal/config/config.go @@ -1,9 +1,11 @@ -package main +package config import ( "encoding/json" "fmt" "os" + + "github.com/artem-y/commit/internal/helpers" ) type commitConfig struct { @@ -11,9 +13,9 @@ type commitConfig struct { } // Reads .commit.json file from current directory and unmarshals it into commitConfig struct -func readCommitConfig() commitConfig { +func ReadCommitConfig() commitConfig { - configFilePath := default_config_file_path + configFilePath := helpers.DEFAULT_CONFIG_FILE_PATH _, err := os.Stat(configFilePath) if os.IsNotExist(err) { @@ -22,14 +24,14 @@ func readCommitConfig() commitConfig { file, err := os.ReadFile(configFilePath) if err != nil { - fmt.Fprintf(os.Stderr, red("Error reading %s file: %v\n"), err, configFilePath) + fmt.Fprintf(os.Stderr, helpers.Red("Error reading %s file: %v\n"), err, configFilePath) os.Exit(1) } var cfg commitConfig err = json.Unmarshal(file, &cfg) if err != nil { - fmt.Fprintf(os.Stderr, red("Error unmarshalling %s file: %v\n"), err, configFilePath) + fmt.Fprintf(os.Stderr, helpers.Red("Error unmarshalling %s file: %v\n"), err, configFilePath) os.Exit(1) } diff --git a/internal/helpers/constants.go b/internal/helpers/constants.go new file mode 100644 index 0000000..ef60cd2 --- /dev/null +++ b/internal/helpers/constants.go @@ -0,0 +1,6 @@ +package helpers + +const ( + DEFAULT_CONFIG_FILE_PATH = ".commit.json" + DEFAULT_ISSUE_REGEX = "#[0-9]+" +) diff --git a/internal/helpers/helpers.go b/internal/helpers/helpers.go new file mode 100644 index 0000000..85582c7 --- /dev/null +++ b/internal/helpers/helpers.go @@ -0,0 +1,9 @@ +package helpers + +import "fmt" + +// Wraps the message string in red color +func Red(msg string) string { + return fmt.Sprintf("\033[31m%s\033[0m", msg) +} + diff --git a/internal/user/user.go b/internal/user/user.go new file mode 100644 index 0000000..4e144ac --- /dev/null +++ b/internal/user/user.go @@ -0,0 +1,47 @@ +package user + +import ( + "fmt" + "os" + + "github.com/artem-y/commit/internal/helpers" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" +) + +// Simple representation of a git user +type User struct { + Name string + Email string +} + +// Returns the user name and email from the local repository or the global git config +func GetUser(repo git.Repository) User { + cfg, err := repo.Config() + if err != nil { + fmt.Fprintf(os.Stderr, helpers.Red("Error loading local config: %v\n"), err) + os.Exit(1) + } + + usr := User{ + Name: cfg.User.Name, + Email: cfg.User.Email, + } + + globalCfg, err := config.LoadConfig(config.GlobalScope) + if err != nil { + fmt.Fprintf(os.Stderr, helpers.Red("Error loading global config: %v\n"), err) + os.Exit(1) + } + + if usr.Email == "" { + usr.Email = globalCfg.User.Email + } + + if usr.Name == "" { + usr.Name = globalCfg.User.Name + } + + return usr +} diff --git a/tests/helpers_test.go b/tests/helpers_test.go new file mode 100644 index 0000000..1d3daa3 --- /dev/null +++ b/tests/helpers_test.go @@ -0,0 +1,18 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/artem-y/commit/internal/helpers" +) + +func Test_Red_WrapsCommitMessage_InRedColor(t *testing.T) { + errorMessage := "Error: Something went wrong" + actualOutput := helpers.Red(errorMessage) + expectedOutput := fmt.Sprintf("\033[31m%s\033[0m", errorMessage) + + if actualOutput != expectedOutput { + t.Errorf("got %q, want %q", actualOutput, expectedOutput) + } +} diff --git a/user.go b/user.go deleted file mode 100644 index 2a0ce11..0000000 --- a/user.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" -) - -// Simple representation of a git user -type user struct { - name string - email string -} - -// Returns the user name and email from the local repository or the global git config -func getUser(repo git.Repository) user { - cfg, err := repo.Config() - if err != nil { - fmt.Fprintf(os.Stderr, red("Error loading local config: %v\n"), err) - os.Exit(1) - } - - usr := user{ - name: cfg.User.Name, - email: cfg.User.Email, - } - - globalCfg, err := config.LoadConfig(config.GlobalScope) - if err != nil { - fmt.Fprintf(os.Stderr, red("Error loading global config: %v\n"), err) - os.Exit(1) - } - - if usr.email == "" { - usr.email = globalCfg.User.Email - } - - if usr.name == "" { - usr.name = globalCfg.User.Name - } - - return usr -}