diff --git a/README.md b/README.md index 2b86d91..a6b293e 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ func main() { | `flagrun.Version(version string)` | バージョン表示に使用する文字列を指定します。 | | `flagrun.Commit(commit string)` | コミットハッシュなどを指定します(デフォルト: `dev`)。 | | `flagrun.ArgsRequired()` | コマンドライン引数を必須にします。引数がない場合は UNKNOWN で終了します。 | +| `flagrun.AlwaysStdout()` | `Run` の戻り値を、終了コードに関係なく標準出力へ出力します。 | ## 終了コード diff --git a/flagrun.go b/flagrun.go index d9c9aba..d52704f 100644 --- a/flagrun.go +++ b/flagrun.go @@ -6,6 +6,7 @@ import ( "os" "reflect" "runtime" + "runtime/debug" "strings" "github.com/jessevdk/go-flags" @@ -27,6 +28,7 @@ type Flagrun struct { ArgsRequired bool Version string Commit string + AlwaysStdout bool } type FlagrunOptions func(*Flagrun) @@ -53,6 +55,12 @@ func ArgsRequired() FlagrunOptions { } } +func AlwaysStdout() FlagrunOptions { + return func(f *Flagrun) { + f.AlwaysStdout = true + } +} + func printLine(w io.Writer, s string) error { if w == nil { return nil @@ -65,9 +73,9 @@ func printLine(w io.Writer, s string) error { } func Go(opt Runner, options ...FlagrunOptions) int { - msg, code := internalGo(os.Args[1:], os.Stdout, os.Stderr, opt, options...) + f, msg, code := internalGo(os.Args[1:], os.Stdout, os.Stderr, opt, options...) if msg != "" { - if code == OK { + if code == OK || f.AlwaysStdout { _ = printLine(os.Stdout, msg) } else { _ = printLine(os.Stderr, msg) @@ -105,15 +113,36 @@ func hasBooleanVersionField(opt Runner) bool { return ok && field.Type.Kind() == reflect.Bool && v.FieldByName("Version").Bool() } +func buildCommitHash() string { + commit := "dev" + dirty := false + if info, ok := debug.ReadBuildInfo(); ok { + for _, setting := range info.Settings { + if setting.Key == "vcs.revision" && setting.Value != "" { + commit = setting.Value + } else if setting.Key == "vcs.modified" && setting.Value == "true" { + dirty = true + } + } + } + if len(commit) > 7 { + commit = commit[:7] + } + if dirty { + commit += "-dirty" + } + return commit +} + func internalGo( argv []string, stdout io.Writer, stderr io.Writer, opt Runner, options ...FlagrunOptions, -) (string, int) { +) (*Flagrun, string, int) { f := &Flagrun{ - Commit: "dev", + Commit: buildCommitHash(), Version: "unknown", } for _, option := range options { @@ -136,18 +165,18 @@ func internalGo( runtime.GOARCH, runtime.Version(), f.Commit) - return "", OK + return f, "", OK } else if flags.WroteHelp(err) { fmt.Fprintf(stdout, "%v\n", err) - return "", OK + return f, "", OK } else if err != nil { fmt.Fprintf(stderr, "%v\n", err) - return "", UNKNOWN + return f, "", UNKNOWN } else if f.ArgsRequired && len(args) == 0 { fmt.Fprintf(stderr, "command is required\n") psr.WriteHelp(stderr) - return "", UNKNOWN + return f, "", UNKNOWN } - - return opt.Run(args) + msg, code := opt.Run(args) + return f, msg, code } diff --git a/flagrun_test.go b/flagrun_test.go index 253d196..c0a2001 100644 --- a/flagrun_test.go +++ b/flagrun_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type testRunner struct { @@ -84,10 +85,10 @@ func TestInternalGo(t *testing.T) { } var stdout bytes.Buffer var stderr bytes.Buffer - msg, code := internalGo(tt.args, &stdout, &stderr, o, options...) + f, msg, code := internalGo(tt.args, &stdout, &stderr, o, options...) stdoutStr := stdout.String() stderrStr := stderr.String() - + require.NotNil(t, f, "%s Flagrun instance should not be nil", tt.name) assert.Equal(t, tt.wantMsg, msg, "%s msg", tt.name) assert.Contains(t, stdoutStr, tt.wantStdout, "%s stdout", tt.name) assert.Contains(t, stderrStr, tt.wantStderr, "%s stderr", tt.name) @@ -112,10 +113,11 @@ func TestInternalGoWithRequiredParameters(t *testing.T) { o := &requiredRunner{} var stdout bytes.Buffer var stderr bytes.Buffer - msg, code := internalGo([]string{}, &stdout, &stderr, o) + f, msg, code := internalGo([]string{}, &stdout, &stderr, o) stdoutStr := stdout.String() stderrStr := stderr.String() + assert.NotNil(t, f, "Flagrun instance should not be nil") assert.Equal(t, "", msg) assert.Contains(t, stdoutStr, "") assert.Contains(t, stderrStr, "the required flag `-r, --required")