Skip to content

Commit

Permalink
feat: return error exit codes and add windows fixes (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikesmithgh authored Mar 23, 2024
1 parent 9f577f0 commit d6193d5
Show file tree
Hide file tree
Showing 19 changed files with 496 additions and 89 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ module github.com/mikesmithgh/git-prompt-string
go 1.22.0

require github.com/pelletier/go-toml/v2 v2.1.1

require github.com/buildkite/shellwords v0.0.0-20180315110454-59467a9b8e10
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/buildkite/shellwords v0.0.0-20180315110454-59467a9b8e10 h1:XwHQ5xDtYPdtBbVPyRO6UZoWZe8/mbKUb076f8x7RvI=
github.com/buildkite/shellwords v0.0.0-20180315110454-59467a9b8e10/go.mod h1:gv0DYOzHEsKgo31lTCDGauIg4DTTGn41Bzp+t3wSOlk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
103 changes: 50 additions & 53 deletions integration/gps_test.go
Original file line number Diff line number Diff line change
@@ -1,86 +1,81 @@
package integration

import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
)

func TestGPS(t *testing.T) {
var notFoundMsg string
if runtime.GOOS == "windows" {
notFoundMsg = "The system cannot find the path specified."
} else {
notFoundMsg = "no such file or directory"
}
tests := []struct {
dir string
input []string
expected string
environ []string
err error
}{
{"bare", []string{"--config=NONE"}, "\x1b[90m \ue0a0 BARE:main\x1b[0m", nil},
{"no_upstream", []string{"--config=NONE"}, "\x1b[90m \ue0a0 main\x1b[0m", nil},
{"no_upstream_remote", []string{"--config=NONE"}, "\x1b[90m \ue0a0 main → mikesmithgh/test/main\x1b[0m", nil},
{"git_dir", []string{"--config=NONE"}, "\x1b[90m \ue0a0 GIT_DIR!\x1b[0m", nil},
{"clean", []string{"--config=NONE"}, "\x1b[32m \ue0a0 main\x1b[0m", nil},
{"tag", []string{"--config=NONE"}, "\x1b[90m \ue0a0 (v1.0.0)\x1b[0m", nil},
{"commit", []string{"--config=NONE"}, "\x1b[90m \ue0a0 (24afc95)\x1b[0m", nil},
{"dirty", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main *\x1b[0m", nil},
{"dirty_staged", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main *\x1b[0m", nil},
{"conflict_ahead", []string{"--config=NONE"}, "\x1b[33m \ue0a0 main ↑[1]\x1b[0m", nil},
{"conflict_behind", []string{"--config=NONE"}, "\x1b[33m \ue0a0 main ↓[1]\x1b[0m", nil},
{"conflict_diverged", []string{"--config=NONE"}, "\x1b[33m \ue0a0 main ↕ ↑[1] ↓[1]\x1b[0m", nil},
{"untracked", []string{"--config=NONE"}, "\x1b[35m \ue0a0 main *\x1b[0m", nil},
{"sparse", []string{"--config=NONE"}, "\x1b[32m \ue0a0 main|SPARSE\x1b[0m", nil},
{"sparse_merge_conflict", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|SPARSE|MERGING|CONFLICT *↕ ↑[1] ↓[1]\x1b[0m", nil},
{"bare", []string{"--config=NONE"}, "\x1b[90m \ue0a0 BARE:main\x1b[0m", nil, nil},
{"no_upstream", []string{"--config=NONE"}, "\x1b[90m \ue0a0 main\x1b[0m", nil, nil},
{"no_upstream_remote", []string{"--config=NONE"}, "\x1b[90m \ue0a0 main → mikesmithgh/test/main\x1b[0m", nil, nil},
{"git_dir", []string{"--config=NONE"}, "\x1b[90m \ue0a0 GIT_DIR!\x1b[0m", nil, nil},
{"clean", []string{"--config=NONE"}, "\x1b[32m \ue0a0 main\x1b[0m", nil, nil},
{"tag", []string{"--config=NONE"}, "\x1b[90m \ue0a0 (v1.0.0)\x1b[0m", nil, nil},
{"commit", []string{"--config=NONE"}, "\x1b[90m \ue0a0 (24afc95)\x1b[0m", nil, nil},
{"dirty", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main *\x1b[0m", nil, nil},
{"dirty_staged", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main *\x1b[0m", nil, nil},
{"conflict_ahead", []string{"--config=NONE"}, "\x1b[33m \ue0a0 main ↑[1]\x1b[0m", nil, nil},
{"conflict_behind", []string{"--config=NONE"}, "\x1b[33m \ue0a0 main ↓[1]\x1b[0m", nil, nil},
{"conflict_diverged", []string{"--config=NONE"}, "\x1b[33m \ue0a0 main ↕ ↑[1] ↓[1]\x1b[0m", nil, nil},
{"untracked", []string{"--config=NONE"}, "\x1b[35m \ue0a0 main *\x1b[0m", nil, nil},
{"sparse", []string{"--config=NONE"}, "\x1b[32m \ue0a0 main|SPARSE\x1b[0m", nil, nil},
{"sparse_merge_conflict", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|SPARSE|MERGING|CONFLICT *↕ ↑[1] ↓[1]\x1b[0m", nil, nil},

// rebase merge
{"rebase_i", []string{"--config=NONE"}, "\x1b[34m \ue0a0 main|REBASE-i 1/1\x1b[0m", nil},
{"rebase_m", []string{"--config=NONE"}, "\x1b[34m \ue0a0 main|REBASE-m 1/1\x1b[0m", nil},
{"rebase_i", []string{"--config=NONE"}, "\x1b[34m \ue0a0 main|REBASE-i 1/1\x1b[0m", nil, nil},
{"rebase_m", []string{"--config=NONE"}, "\x1b[34m \ue0a0 main|REBASE-m 1/1\x1b[0m", nil, nil},
// rebase apply
{"am_rebase", []string{"--config=NONE"}, "\x1b[34m \ue0a0 (b69e688)|AM/REBASE 1/1\x1b[0m", nil},
{"am", []string{"--config=NONE"}, "\x1b[34m \ue0a0 (b69e688)|AM 1/1\x1b[0m", nil},
{"rebase", []string{"--config=NONE"}, "\x1b[34m \ue0a0 main|REBASE 1/1\x1b[0m", nil},
{"am_rebase", []string{"--config=NONE"}, "\x1b[34m \ue0a0 (b69e688)|AM/REBASE 1/1\x1b[0m", nil, nil},
{"am", []string{"--config=NONE"}, "\x1b[34m \ue0a0 (b69e688)|AM 1/1\x1b[0m", nil, nil},
{"rebase", []string{"--config=NONE"}, "\x1b[34m \ue0a0 main|REBASE 1/1\x1b[0m", nil, nil},
// merge
{"merge_conflict", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|MERGING|CONFLICT *↕ ↑[1] ↓[1]\x1b[0m", nil},
{"merge", []string{"--config=NONE"}, "\x1b[35m \ue0a0 main|MERGING *↕ ↑[1] ↓[1]\x1b[0m", nil},
{"merge_conflict", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|MERGING|CONFLICT *↕ ↑[1] ↓[1]\x1b[0m", nil, nil},
{"merge", []string{"--config=NONE"}, "\x1b[35m \ue0a0 main|MERGING *↕ ↑[1] ↓[1]\x1b[0m", nil, nil},
// cherry pick
{"cherry_pick_conflict", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|CHERRY-PICKING|CONFLICT *↕ ↑[1] ↓[1]\x1b[0m", nil},
{"cherry_pick", []string{"--config=NONE"}, "\x1b[35m \ue0a0 main|CHERRY-PICKING *↕ ↑[1] ↓[1]\x1b[0m", nil},
{"cherry_pick_conflict", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|CHERRY-PICKING|CONFLICT *↕ ↑[1] ↓[1]\x1b[0m", nil, nil},
{"cherry_pick", []string{"--config=NONE"}, "\x1b[35m \ue0a0 main|CHERRY-PICKING *↕ ↑[1] ↓[1]\x1b[0m", nil, nil},
// revert
{"revert_conflict", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|REVERTING|CONFLICT *↕ ↑[2] ↓[1]\x1b[0m", nil},
{"revert", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|REVERTING *↕ ↑[2] ↓[1]\x1b[0m", nil},
{"revert_conflict", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|REVERTING|CONFLICT *↕ ↑[2] ↓[1]\x1b[0m", nil, nil},
{"revert", []string{"--config=NONE"}, "\x1b[31m \ue0a0 main|REVERTING *↕ ↑[2] ↓[1]\x1b[0m", nil, nil},
// bisect
{"bisect", []string{"--config=NONE"}, "\x1b[34m \ue0a0 main|BISECTING ↓[1]\x1b[0m", nil},
{"bisect", []string{"--config=NONE"}, "\x1b[34m \ue0a0 main|BISECTING ↓[1]\x1b[0m", nil, nil},

// formatting
{"clean", []string{"--config=NONE", "--color-disabled"}, " \ue0a0 main", nil},
{"clean", []string{"--config=NONE", "--color-disabled", "--prompt-prefix= start "}, " start main", nil},
{"clean", []string{"--config=NONE", "--color-disabled", "--prompt-suffix= stop"}, " \ue0a0 main stop", nil},
{"conflict_ahead", []string{"--config=NONE", "--color-disabled", "--ahead-format=ahead by %d"}, " \ue0a0 main ahead by 1", nil},
{"conflict_behind", []string{"--config=NONE", "--color-disabled", "--behind-format=behind by %d"}, " \ue0a0 main behind by 1", nil},
{"conflict_diverged", []string{"--config=NONE", "--color-disabled", "--diverged-format=ahead by %d behind by %d"}, " \ue0a0 main ahead by 1 behind by 1", nil},
{"no_upstream_remote", []string{"--config=NONE", "--color-disabled", "--no-upstream-remote-format= upstream=[repo: %s branch: %s]"}, " \ue0a0 main upstream=[repo: mikesmithgh/test branch: main]", nil},
{"clean", []string{"--config=NONE", "--color-disabled"}, " \ue0a0 main", nil, nil},
{"clean", []string{"--config=NONE", "--color-disabled", "--prompt-prefix= start "}, " start main", nil, nil},
{"clean", []string{"--config=NONE", "--color-disabled", "--prompt-suffix= stop"}, " \ue0a0 main stop", nil, nil},
{"conflict_ahead", []string{"--config=NONE", "--color-disabled", "--ahead-format=ahead by %d"}, " \ue0a0 main ahead by 1", nil, nil},
{"conflict_behind", []string{"--config=NONE", "--color-disabled", "--behind-format=behind by %d"}, " \ue0a0 main behind by 1", nil, nil},
{"conflict_diverged", []string{"--config=NONE", "--color-disabled", "--diverged-format=ahead by %d behind by %d"}, " \ue0a0 main ahead by 1 behind by 1", nil, nil},
{"no_upstream_remote", []string{"--config=NONE", "--color-disabled", "--no-upstream-remote-format= upstream=[repo: %s branch: %s]"}, " \ue0a0 main upstream=[repo: mikesmithgh/test branch: main]", nil, nil},

// color overrides
{"clean", []string{"--config=../configs/color_overrides.toml"}, "\x1b[38;2;230;238;4m \ue0a0 main\x1b[0m", nil},
{"no_upstream", []string{"--config=../configs/color_overrides.toml"}, "\x1b[30m\x1b[47m \ue0a0 main\x1b[0m", nil},
{"dirty", []string{"--config=../configs/color_overrides.toml"}, "\x1b[48;2;179;5;89m \ue0a0 main *\x1b[0m", nil},
{"conflict_ahead", []string{"--config=../configs/color_overrides.toml"}, "\x1b[38;2;252;183;40m \ue0a0 main ↑[1]\x1b[0m", nil},
{"untracked", []string{"--config=../configs/color_overrides.toml"}, "\x1b[38;2;255;0;0m\x1b[48;2;22;242;170m \ue0a0 main *\x1b[0m", nil},
{"bisect", []string{}, "\x1b[48;2;204;204;255m\x1b[35m \ue0a0 main|BISECTING ↓[1]\x1b[0m", []string{"GIT_PROMPT_STRING_CONFIG=../configs/color_overrides.toml"}},
{"clean", []string{"--config=../configs/color_overrides.toml"}, "\x1b[38;2;230;238;4m \ue0a0 main\x1b[0m", nil, nil},
{"no_upstream", []string{"--config=../configs/color_overrides.toml"}, "\x1b[30m\x1b[47m \ue0a0 main\x1b[0m", nil, nil},
{"dirty", []string{"--config=../configs/color_overrides.toml"}, "\x1b[48;2;179;5;89m \ue0a0 main *\x1b[0m", nil, nil},
{"conflict_ahead", []string{"--config=../configs/color_overrides.toml"}, "\x1b[38;2;252;183;40m \ue0a0 main ↑[1]\x1b[0m", nil, nil},
{"untracked", []string{"--config=../configs/color_overrides.toml"}, "\x1b[38;2;255;0;0m\x1b[48;2;22;242;170m \ue0a0 main *\x1b[0m", nil, nil},
{"bisect", []string{}, "\x1b[48;2;204;204;255m\x1b[35m \ue0a0 main|BISECTING ↓[1]\x1b[0m", []string{"GIT_PROMPT_STRING_CONFIG=../configs/color_overrides.toml"}, nil},

// config errors
{"clean", []string{"--config=/fromparam/does/not/exist"}, fmt.Sprintf("\x1b[31m git-prompt-string error(read config): open /fromparam/does/not/exist: %s\x1b[0m", notFoundMsg), nil},
{"configs", []string{}, fmt.Sprintf("\x1b[31m git-prompt-string error(read config): open /fromenvvar/does/not/exist: %s\x1b[0m", notFoundMsg), []string{"GIT_PROMPT_STRING_CONFIG=/fromenvvar/does/not/exist"}},
{"configs", []string{"--config=invalid_syntax.toml"}, "\x1b[31m git-prompt-string error(unmarshal config): toml: expected character =\x1b[0m", nil},
{"configs", []string{}, "\x1b[31m git-prompt-string error(unmarshal config): toml: expected character =\x1b[0m", []string{"GIT_PROMPT_STRING_CONFIG=invalid_syntax.toml"}},
{"clean", []string{"--config=/fromparam/does/not/exist"}, fmt.Sprintf("\x1b[31m git-prompt-string error(read config): \"open /fromparam/does/not/exist: %s\"\x1b[0m", notFoundMsg), nil, errors.New("exit status 1")},
{"configs", []string{}, fmt.Sprintf("\x1b[31m git-prompt-string error(read config): \"open /fromenvvar/does/not/exist: %s\"\x1b[0m", notFoundMsg), []string{"GIT_PROMPT_STRING_CONFIG=/fromenvvar/does/not/exist"}, errors.New("exit status 1")},
{"configs", []string{"--config=invalid_syntax.toml"}, fmt.Sprintf("\x1b[31m git-prompt-string error(unmarshal config): \"toml: expected character %s\"\x1b[0m", escapedEqualSign), nil, errors.New("exit status 1")},
{"configs", []string{}, fmt.Sprintf("\x1b[31m git-prompt-string error(unmarshal config): \"toml: expected character %s\"\x1b[0m", escapedEqualSign), []string{"GIT_PROMPT_STRING_CONFIG=invalid_syntax.toml"}, errors.New("exit status 1")},

{"norepo", []string{"--config=NONE"}, "", nil},
{"norepo", []string{"--config=NONE"}, "", nil, nil},
}

for _, test := range tests {
Expand All @@ -91,8 +86,10 @@ func TestGPS(t *testing.T) {
cmd.Env = append(cmd.Env, test.environ...)
}
result, err := cmd.CombinedOutput()
if err != nil {
if test.err == nil && err != nil {
t.Errorf("Unexpected error: %s", err)
} else if test.err != nil && test.err.Error() != err.Error() {
t.Errorf("Expected error: %s, got: %s", test.err, err)
}
actual := string(result)
if actual != test.expected {
Expand Down
10 changes: 3 additions & 7 deletions integration/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
)

Expand All @@ -22,19 +21,16 @@ func TestMain(m *testing.M) {
}
defer os.RemoveAll(tmpDir)

gps := "git-prompt-string"
if runtime.GOOS == "windows" {
gps += ".exe"
}
builtBinaryPath = filepath.Join(tmpDir, gps)
builtBinaryPath = filepath.Join(tmpDir, git_prompt_string_bin)

cmd := exec.Command("go", "build", "-o", builtBinaryPath, "..")
output, err := cmd.CombinedOutput()
if err != nil {
panic(fmt.Sprintf("failed to build gps: %s, %s", output, err))
}

cmd = exec.Command("cp", "-r", "../testdata", tmpDir)
copyCommand, copyArgs := copyTestDataCmd("..", tmpDir)
cmd = exec.Command(copyCommand, copyArgs...)
err = cmd.Run()
if err != nil {
panic(fmt.Sprintf("failed to copy test data: %s", err))
Expand Down
15 changes: 15 additions & 0 deletions integration/init_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build !windows

package integration

import "path/filepath"

var (
notFoundMsg string = "no such file or directory"
git_prompt_string_bin string = "git-prompt-string"
escapedEqualSign string = "\\="
)

func copyTestDataCmd(src string, dest string) (string, []string) {
return "cp", []string{"-r", filepath.Join(src, "testdata"), dest}
}
13 changes: 13 additions & 0 deletions integration/init_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package integration

import "path/filepath"

var (
notFoundMsg string = "The system cannot find the path specified."
git_prompt_string_bin string = "git-prompt-string.exe"
escapedEqualSign string = "^="
)

func copyTestDataCmd(src string, dest string) (string, []string) {
return "xcopy", []string{"/S", "/E", "/I", filepath.Join(src, "testdata"), filepath.Join(dest, "testdata")}
}
33 changes: 17 additions & 16 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"flag"
"fmt"
"os"
"os/exec"
"path"
"runtime"
"strconv"
"strings"

Expand Down Expand Up @@ -38,6 +38,7 @@ var (
)

func main() {
// TODO: check if git is installed?
cfg := config.GPSConfig{
PromptPrefix: *promptPrefix,
PromptSuffix: *promptSuffix,
Expand Down Expand Up @@ -68,30 +69,26 @@ func main() {
if xdgConfigHome == "" {
home, err := os.UserHomeDir()
if err != nil {
util.ErrMsg("user home", err, 0)
}
if runtime.GOOS == "windows" {
xdgConfigHome = path.Join(home, "AppData", "Local")
} else {
xdgConfigHome = path.Join(home, ".config")
util.ErrMsg("user home", err)
}
xdgConfigHome = path.Join(home, util.XDGConfigPath)
}
gpsConfig = path.Join(xdgConfigHome, "git-prompt-string", "config.toml")
}

if gpsConfig != "NONE" {
gpsConfigRaw, err := os.ReadFile(gpsConfig)
if err != nil && !os.IsNotExist(err) {
util.ErrMsg("read config exists", err, 0)
util.ErrMsg("read config exists", err)
}

if err != nil && (*configPath != "" || gpsConfigEnv != "") {
util.ErrMsg("read config", err, 0)
util.ErrMsg("read config", err)
}

err = toml.Unmarshal(gpsConfigRaw, &cfg)
if err != nil {
util.ErrMsg("unmarshal config", err, 0)
util.ErrMsg("unmarshal config", err)
}
}

Expand All @@ -112,7 +109,7 @@ func main() {
case "color-disabled":
colorDisabled, err := strconv.ParseBool(f.Value.String())
if err != nil {
util.ErrMsg("parse color disabled", err, 0)
util.ErrMsg("parse color disabled", err)
}
cfg.ColorDisabled = colorDisabled
case "color-clean":
Expand Down Expand Up @@ -147,24 +144,28 @@ func main() {

clearColor, err := color.Color("none")
if err != nil {
util.ErrMsg("color none", err, 0)
util.ErrMsg("color none", err)
}

gitRepo, stderr, err := git.RevParse()
if err != nil {
if strings.Contains(string(stderr), "not a git repository") {
switch {
case strings.Contains(err.Error(), exec.ErrNotFound.Error()):
util.ErrMsg("rev parse", err)
case strings.Contains(string(stderr), "not a git repository"):
os.Exit(0)
default:
// allow other errors to pass through, the git repo may not have upstream
}
// allow other errors to pass through, the git repo may not have upstream
}

branchInfo, err := gitRepo.BranchInfo(cfg)
if err != nil {
util.ErrMsg("branch info", err, 0)
util.ErrMsg("branch info", err)
}
branchStatus, promptColor, err := gitRepo.BranchStatus(cfg)
if err != nil {
util.ErrMsg("branch status", err, 0)
util.ErrMsg("branch status", err)
}

fmt.Printf("%s%s%s%s%s%s", promptColor, cfg.PromptPrefix, branchInfo, branchStatus, cfg.PromptSuffix, clearColor)
Expand Down
Loading

0 comments on commit d6193d5

Please sign in to comment.