Skip to content

Commit

Permalink
Merge pull request #69 from davidalpert/67-getallglobalgit-mobco-auth…
Browse files Browse the repository at this point in the history
…or-nonzero-exit-code-1-when-soloing

test: reproduce #67 and #68
  • Loading branch information
davidalpert authored Aug 16, 2022
2 parents 5f5b04b + 2f207e5 commit c7b8ae8
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 29 deletions.
28 changes: 28 additions & 0 deletions features/git-mob/bugs/67-getallglobal-errors-when-soloing.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#@announce-gitmob-log
#@announce-stdout @announce-stderr
Feature: 🐛 GetAllGlobal(git-mob.co-author): nonzero exit code: 1: (when soloing)

Background:
Given I have installed git-mob into "local_bin" within the current directory
And I look for executables in "local_bin" within the current directory

Given a file named "~/.gitconfig" with:
"""
[user]
name = Jane Doe
email = jane@example.com
"""
And a simple git repo at "example"
And I successfully run `git solo`

Scenario: #67 git mob print
Given I cd to "example"
And I successfully run `git mob init`
When I run `git mob print`
Then the exit status should be 0

Scenario: #68 git commit
Given I cd to "example"
And I successfully run `git mob init`
When I run `git commit --allow-empty -m "test message"`
Then the exit status should be 0
56 changes: 30 additions & 26 deletions internal/gitConfig/atoms.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,52 +37,56 @@ func GetGlobal(key string) string {

// GetAll gets all values for a multi-valued option key.
func GetAll(key string) ([]string, error) {
o, _, err := shell.SilentRun("git", "config", "--get-all", key)
if err != nil {
return make([]string, 0), err
o, exitCode, err := shell.SilentRun("git", "config", "--get-all", key)
if ExitCode(exitCode) == SectionOrKeyIsInvalid {
return make([]string, 0), nil
} else if err != nil {
return make([]string, 0), ExitCode(exitCode).Errorf(err)
}
return strings.Split(o, "\n"), nil
}

// GetAllGlobal gets all values for a multi-valued option key.
func GetAllGlobal(key string) ([]string, error) {
o, _, err := shell.SilentRun("git", "config", "--global", "--get-all", key)
if err != nil {
return make([]string, 0), err
o, exitCode, err := shell.SilentRun("git", "config", "--global", "--get-all", key)
if ExitCode(exitCode) == SectionOrKeyIsInvalid {
return make([]string, 0), nil
} else if err != nil {
return make([]string, 0), ExitCode(exitCode).Errorf(err)
}
return strings.Split(o, "\n"), nil
}

// Set sets the option, overwriting the existing value if one exists.
func Set(key string, value string) error {
//const { status } = SilentRun(`git config ${key} "${value}"`);
_, _, err := shell.SilentRun("git", "config", key, value)
_, exitCode, err := shell.SilentRun("git", "config", key, value)
if err != nil {
return fmt.Errorf("set(%#v, %#v): %v", key, value, err)
return ExitCode(exitCode).Errorf(fmt.Errorf("set(%#v, %#v): %v", key, value, err))
}
return nil
}

// SetGlobal sets the global option, overwriting the existing value if one exists.
func SetGlobal(key string, value string) error {
//const { status } = SilentRun(`git config ${key} "${value}"`);
_, _, err := shell.SilentRun("git", "config", "--global", key, value)
_, exitCode, err := shell.SilentRun("git", "config", "--global", key, value)
if err != nil {
return fmt.Errorf("option '%s' has multiple values. Cannot overwrite multiple values for option '%s' with a single value", key, key)
return ExitCode(exitCode).Errorf(fmt.Errorf("option '%s' has multiple values. Cannot overwrite multiple values for option '%s' with a single value", key, key))
}
return nil
}

// Add adds a new line to the option without altering any existing values.
func Add(key string, value string) error {
_, _, err := shell.SilentRun("git", "config", "--add", key, value)
return err
_, exitCode, err := shell.SilentRun("git", "config", "--add", key, value)
return ExitCode(exitCode).Errorf(err)
}

// AddGlobal adds a new line to the global option without altering any existing values.
func AddGlobal(key string, value string) error {
_, _, err := shell.SilentRun("git", "config", "--global", "--add", key, value)
return err
_, exitCode, err := shell.SilentRun("git", "config", "--global", "--add", key, value)
return ExitCode(exitCode).Errorf(err)
}

// Has checks if the given option exists in the merged configuration.
Expand All @@ -106,53 +110,53 @@ func HasGlobal(key string) bool {
// RemoveSection removes the given section from the configuration.
func RemoveSection(key string) error {
if Has(key) {
_, _, err := shell.SilentRun("git", "config", "--remove-section", key)
return err
_, exitCode, err := shell.SilentRun("git", "config", "--remove-section", key)
return ExitCode(exitCode).Errorf(err)
}
return nil
}

// RemoveSectionGlobal removes the given section from the global configuration.
func RemoveSectionGlobal(key string) error {
if HasGlobal(key) {
_, _, err := shell.SilentRun("git", "config", "--global", "--remove-section", key)
return err
_, exitCode, err := shell.SilentRun("git", "config", "--global", "--remove-section", key)
return ExitCode(exitCode).Errorf(err)
}
return nil
}

// Remove removes the given key from the configuration.
func Remove(key string) error {
if Has(key) {
_, _, err := shell.SilentRun("git", "config", "--unset", key)
return err
_, exitCode, err := shell.SilentRun("git", "config", "--unset", key)
return ExitCode(exitCode).Errorf(err)
}
return nil
}

// RemoveGlobal removes the given key from the configuration.
func RemoveGlobal(key string) error {
if HasGlobal(key) {
_, _, err := shell.SilentRun("git", "config", "--global", "--unset", key)
return err
_, exitCode, err := shell.SilentRun("git", "config", "--global", "--unset", key)
return ExitCode(exitCode).Errorf(err)
}
return nil
}

// RemoveAll removes all the given keys from the configuration.
func RemoveAll(key string) error {
if Has(key) {
_, _, err := shell.SilentRun("git", "config", "--unset-all", key)
return err
_, exitCode, err := shell.SilentRun("git", "config", "--unset-all", key)
return ExitCode(exitCode).Errorf(err)
}
return nil
}

// RemoveAllGlobal removes all the given keys from the configuration.
func RemoveAllGlobal(key string) error {
if HasGlobal(key) {
_, _, err := shell.SilentRun("git", "config", "--global", "--unset-all", key)
return err
_, exitCode, err := shell.SilentRun("git", "config", "--global", "--unset-all", key)
return ExitCode(exitCode).Errorf(err)
}
return nil
}
61 changes: 61 additions & 0 deletions internal/gitConfig/exit_codes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package gitConfig

import "fmt"

// re: https://git-scm.com/docs/git-config#_description
// [the `git config`] command will fail with non-zero status upon error. Some exit codes are...

// ExitCode of the `git config` command.
type ExitCode int

// ExitCodes
const (
UnknownExitCode ExitCode = iota - 1
CommandSuccess // 0
SectionOrKeyIsInvalid
SectionOrNameNotProvided
FileIsInvalid
FileCannotBeWritten
CannotUnsetOptionWhichDoesNotExist
CannotUnsetSetMultipleLinesMatched
InvalidRegexp
)

var exitCodeNames = [...]string{
CommandSuccess: "On success, the command returns the exit code 0.",
SectionOrKeyIsInvalid: "The section or key is invalid (ret = 1)",
SectionOrNameNotProvided: "no section or name was provided (ret = 2)",
FileIsInvalid: "the config file is invalid (ret = 3)",
FileCannotBeWritten: "the config file cannot be written (ret = 4)",
CannotUnsetOptionWhichDoesNotExist: "you try to unset an option which does not exist (ret = 5)",
CannotUnsetSetMultipleLinesMatched: "you try to unset/set an option for which multiple lines match (ret = 5)",
InvalidRegexp: "you try to use an invalid regexp (ret = 6)",
}

func (c ExitCode) String() string {
if c.IsKnown() {
return exitCodeNames[c]
}

if c == UnknownExitCode {
return "unknown"
}

return "undocumented"
}

func (c ExitCode) IsKnown() bool {
return UnknownExitCode < c && c <= InvalidRegexp
}

// Errorf wraps an error with the known interpretation from git
func (c ExitCode) Errorf(err error) error {
if err == nil {
return nil
}

if c.IsKnown() {
return fmt.Errorf("%s: %v", c, err)
}
return fmt.Errorf("%s: %v", UnknownExitCode, err)
}
30 changes: 30 additions & 0 deletions internal/gitConfig/exit_codes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package gitConfig

import "testing"

func TestExitCode_String(t *testing.T) {
tests := []struct {
have ExitCode
want int
}{
{
have: CommandSuccess,
want: 0,
},
{
have: SectionOrKeyIsInvalid,
want: 1,
},
}
for _, tt := range tests {
t.Run(tt.have.String(), func(t *testing.T) {
if got := int(tt.have); got != tt.want {
t.Errorf("String() = %v, want %v", got, tt.want)
}

if got := ExitCode(tt.want); got != tt.have {
t.Errorf("String() = %v, want %v", got, tt.have)
}
})
}
}
6 changes: 3 additions & 3 deletions internal/shell/silent_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ func SilentRun(name string, arg ...string) (string, int, error) {
"exit.error": exitError.Error(),
"exit.stderr": string(exitError.Stderr),
}).WithError(err).Error("command failed")
return "", 0, fmt.Errorf("nonzero exit code: %d: %s\nexitError.Stderr: %s\ncmd.Stderr: %s\ncmd.Stdout: %s", exitError.ExitCode(), exitError.Error(), string(exitError.Stderr), stdErr.String(), out.String())
return "", exitError.ExitCode(), fmt.Errorf("nonzero exit code: %d: %s", exitError.ExitCode(), exitError.Error())
}
lg.WithError(err).Error("command failed")
return "", 0, fmt.Errorf("%s;%s", stdErr.String(), out.String())
lg.WithError(err).Error("command failed without exitError")
return "", -1, fmt.Errorf("%s;%s", stdErr.String(), out.String())
}

lg.WithFields(log.Fields{
Expand Down

0 comments on commit c7b8ae8

Please sign in to comment.