From 76f193a0920a286e7536e359b09d7da9fd18bb0c Mon Sep 17 00:00:00 2001 From: Matthew F Leader Date: Tue, 9 Jan 2024 12:39:36 -0500 Subject: [PATCH 1/5] append stderr function --- exex.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/exex.go b/exex.go index 2cd3682..1f2c511 100644 --- a/exex.go +++ b/exex.go @@ -26,6 +26,7 @@ import ( "bytes" "context" "errors" + "fmt" "io" "os/exec" ) @@ -152,6 +153,20 @@ func RunContext(ctx context.Context, cmd string, args ...string) error { return CommandContext(ctx, cmd, args...).Run() } +func AppendStderr(err error, errMsg string) error { + var exErr *ExitError + if err != nil { + if !errors.As(err, &exErr) { + return fmt.Errorf("error converting error to exex.ExitError") + } + if exErr.Stderr == nil { + return fmt.Errorf("%s (%w)", errMsg, err) + } + return fmt.Errorf("%s (%w)\n%s", errMsg, err, exErr.Stderr) + } + return nil +} + // Error is a type alias for exec.Error type Error = exec.Error From eb3fe84d56d2f945ebb40241c92212a0f0e5560f Mon Sep 17 00:00:00 2001 From: Matthew F Leader Date: Tue, 9 Jan 2024 13:23:18 -0500 Subject: [PATCH 2/5] refactor append stderr name --- exex.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exex.go b/exex.go index 1f2c511..69dfcbc 100644 --- a/exex.go +++ b/exex.go @@ -153,7 +153,7 @@ func RunContext(ctx context.Context, cmd string, args ...string) error { return CommandContext(ctx, cmd, args...).Run() } -func AppendStderr(err error, errMsg string) error { +func CommandError(err error, errMsg string) error { var exErr *ExitError if err != nil { if !errors.As(err, &exErr) { From 077feb10dbbbb220238df0965fb29c8c57672e58 Mon Sep 17 00:00:00 2001 From: Matthew F Leader Date: Tue, 9 Jan 2024 13:29:10 -0500 Subject: [PATCH 3/5] add func description --- exex.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exex.go b/exex.go index 69dfcbc..b757119 100644 --- a/exex.go +++ b/exex.go @@ -153,6 +153,8 @@ func RunContext(ctx context.Context, cmd string, args ...string) error { return CommandContext(ctx, cmd, args...).Run() } +// CommandError returns the error with the stderr log appended, +// if the error is a command exit error, and the stderr log exists. func CommandError(err error, errMsg string) error { var exErr *ExitError if err != nil { From b7ded23082b6f07850e306594e599c4a12b42057 Mon Sep 17 00:00:00 2001 From: Dustin Black Date: Fri, 17 Apr 2026 11:52:15 +0000 Subject: [PATCH 4/5] Add .gitignore and fix doc comments - Add standard Go .gitignore - Fix package doc comment to use 'Package exex' convention - Add trailing periods to doc comments for godot compliance Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 16 ++++++++++++++++ exex.go | 12 ++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9592b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +.idea/ diff --git a/exex.go b/exex.go index b757119..5cced8d 100644 --- a/exex.go +++ b/exex.go @@ -1,6 +1,6 @@ -// exex provides a custom Cmd type that wraps exec.Cmd in a way that -// it will always capture standard error stream if execution fails -// with an exec.ExitError. +// Package exex provides a custom Cmd type that wraps exec.Cmd in a +// way that it will always capture standard error stream if execution +// fails with an exec.ExitError. // // The standard library exec package contains a very useful API to // execute commands, however, the exec.Cmd.Run and exec.Cmd.Output @@ -133,7 +133,7 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, error) { return (*exec.Cmd)(c).StdinP // standard output when the command starts. func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { return (*exec.Cmd)(c).StdoutPipe() } -// String returns a human-readable description of c +// String returns a human-readable description of c. func (c *Cmd) String() string { return (*exec.Cmd)(c).String() } // RunCommand wraps an *exec.Cmd into a Cmd and returns the result of @@ -169,10 +169,10 @@ func CommandError(err error, errMsg string) error { return nil } -// Error is a type alias for exec.Error +// Error is a type alias for exec.Error. type Error = exec.Error -// ExitError is a type alias for exec.ExitError +// ExitError is a type alias for exec.ExitError. type ExitError = exec.ExitError // ErrNotFound is an alias for exec.ErrNotFound, the error resulting From 717d64ebe3a8b606a4c5831f8855cf9a4143e7c2 Mon Sep 17 00:00:00 2001 From: Dustin Black Date: Fri, 17 Apr 2026 12:02:28 +0000 Subject: [PATCH 5/5] Upgrade to Go 1.25, add golangci-lint config and gosec annotations - Update Go version from 1.17 to 1.25 - Add .golangci.yaml with comprehensive linter configuration (v2 format) - Add //nolint:gosec annotations on Command and CommandContext for intentional variable argument execution Co-Authored-By: Claude Opus 4.6 (1M context) --- .golangci.yaml | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ exex.go | 4 +-- go.mod | 2 +- 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 .golangci.yaml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..68f594d --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,76 @@ +version: "2" +run: + timeout: 5m +linters: + enable: + # region General + + # Prevent improper directives in go.mod. + - gomoddirectives + # Prevent improper nolint directives. + - nolintlint + + # endregion + + + # region Code Quality and Comments + + # Inspect source code for potential security problems. This check has a fairly high false positive rate, + # comment with // nolint:gosec where not relevant. + - gosec + # Complain about deeply nested if cases. + - nestif + # Prevent naked returns in long functions. + - nakedret + # Make Go code more readable. + - gocritic + # Check if comments end in a period. This helps prevent incomplete comment lines, such as half-written sentences. + - godot + # Complain about comments as these indicate incomplete code. + - godox + # Keep the cyclomatic complexity of functions to a reasonable level. + - gocyclo + # Complain about cognitive complexity of functions. + - gocognit + # Find repeated strings that could be converted into constants. + - goconst + # Complain about unnecessary type conversions. + - unconvert + # Complain about unused parameters. These should be replaced with underscores. + - unparam + # Check for non-ASCII identifiers. + - asciicheck + # Check for HTTP response body being closed. Sometimes, you may need to disable this using // nolint:bodyclose. + - bodyclose + # Check for duplicate code. You may want to disable this with // nolint:dupl if the source code is the same, but + # legitimately exists for different reasons. + - dupl + # Prevent dogsledding (mass-ignoring return values). This typically indicates missing error handling. + - dogsled + # Enforce consistent import aliases across all files. + - importas + # Prevent faulty error checks. + - nilerr + # Prevent direct error checks that won't work with wrapped errors. + - errorlint + # Find slice usage that could potentially be preallocated. + - prealloc + # Check for improper duration handling. + - durationcheck + # Enforce tests being in the _test package. + - testpackage + # Enforce line length limits. + - lll + + # endregion + settings: + govet: + enable-all: true + disable: + # We don't care about variable shadowing. + - shadow + - fieldalignment +formatters: + enable: + # Make code properly formatted. + - gofmt diff --git a/exex.go b/exex.go index 5cced8d..355af2b 100644 --- a/exex.go +++ b/exex.go @@ -46,7 +46,7 @@ type Cmd exec.Cmd // // Refer to the exec.Command documentation for additional information. func Command(name string, args ...string) *Cmd { - return (*Cmd)(exec.Command(name, args...)) + return (*Cmd)(exec.Command(name, args...)) //nolint:gosec // Variable args intentional. } // CommandContext is like Command but the Cmd is associated with a @@ -54,7 +54,7 @@ func Command(name string, args ...string) *Cmd { // // Refer to the exec.Command documentation for additional information. func CommandContext(ctx context.Context, name string, args ...string) *Cmd { - return (*Cmd)(exec.CommandContext(ctx, name, args...)) + return (*Cmd)(exec.CommandContext(ctx, name, args...)) //nolint:gosec // Variable args intentional. } // Run starts the command and waits for it to end. diff --git a/go.mod b/go.mod index 9b0351c..eb091f7 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/inkel/exex -go 1.17 +go 1.25