diff --git a/internal/command/command.go b/internal/command/command.go index 65a3781..14d0c9e 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -1,32 +1,47 @@ +/* +© Copyright IBM Corporation 2017 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package command contains code to run external commands package command import ( "os/exec" "runtime" - - "golang.org/x/sys/unix" + "syscall" ) // Run runs an OS command. On Linux it waits for the command to // complete and returns the exit status (return code). +// Do not use this function to run shell built-ins (like "cd"), because +// the error handling works differently func Run(name string, arg ...string) (string, int, error) { cmd := exec.Command(name, arg...) // Run the command and wait for completion out, err := cmd.CombinedOutput() if err != nil { - var rc int - // Only works on Linux - if runtime.GOOS == "linux" { - var ws unix.WaitStatus - unix.Wait4(cmd.Process.Pid, &ws, 0, nil) - rc = ws.ExitStatus() - } else { - rc = -1 + // Assert that this is an ExitError + exiterr, ok := err.(*exec.ExitError) + // If the type assertion was correct, and we're on Linux + if ok && runtime.GOOS == "linux" { + status, ok := exiterr.Sys().(syscall.WaitStatus) + if ok { + return string(out), status.ExitStatus(), err + } } - if rc == 0 { - return string(out), rc, nil - } - return string(out), rc, err + return string(out), -1, err } return string(out), 0, nil } diff --git a/internal/command/command_test.go b/internal/command/command_test.go new file mode 100644 index 0000000..ccd5c57 --- /dev/null +++ b/internal/command/command_test.go @@ -0,0 +1,47 @@ +/* +© Copyright IBM Corporation 2017 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package command + +import ( + "runtime" + "testing" +) + +var commandTests = []struct { + name string + arg []string + rc int +}{ + {"ls", []string{}, 0}, + {"ls", []string{"madeup"}, 2}, + {"bash", []string{"-c", "exit 99"}, 99}, +} + +func TestRun(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("Skipping tests for package which only works on Linux") + } + for _, table := range commandTests { + arg := table.arg + _, rc, err := Run(table.name, arg...) + if rc != table.rc { + t.Errorf("Run(%v,%v) - expected %v, got %v", table.name, table.arg, table.rc, rc) + } + if rc != 0 && err == nil { + t.Errorf("Run(%v,%v) - expected error for non-zero return code (rc=%v)", table.name, table.arg, rc) + } + } +}