Skip to content

Commit

Permalink
feat: use a unique exit code to indicate that no lockfiles could be f…
Browse files Browse the repository at this point in the history
…ound in the given args (#138)

* feat: use a unique exit code to indicate that no lockfiles could be found in the given args

* fix: only return different exit code if all args are directories & are empty

* test: restructure cases for checking "no lockfiles in directory" exit code
  • Loading branch information
G-Rath committed Jul 9, 2022
1 parent df1963c commit 2009ec2
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 10 deletions.
30 changes: 23 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func loadDatabases(
const parseAsCsvFile = "csv-file"
const parseAsCsvRow = "csv-row"

func findLockfiles(r *reporter.Reporter, pathToLockOrDirectory string, parseAs string) []string {
func findLockfiles(r *reporter.Reporter, pathToLockOrDirectory string, parseAs string) ([]string, bool) {
lockfiles := make([]string, 0, 1)
file, err := os.Open(pathToLockOrDirectory)

Expand Down Expand Up @@ -280,23 +280,31 @@ func findLockfiles(r *reporter.Reporter, pathToLockOrDirectory string, parseAs s
return lockfiles[i] < lockfiles[j]
})

return lockfiles
return lockfiles, err != nil
}

func findAllLockfiles(r *reporter.Reporter, pathsToCheck []string, parseAs string) []string {
func findAllLockfiles(r *reporter.Reporter, pathsToCheck []string, parseAs string) ([]string, bool) {
var paths []string

if parseAs == parseAsCsvRow {
return []string{"-"}
return []string{"-"}, false
}

errored := false

for _, pathToLockOrDirectory := range pathsToCheck {
for _, p := range findLockfiles(r, pathToLockOrDirectory, parseAs) {
lps, erred := findLockfiles(r, pathToLockOrDirectory, parseAs)

if erred {
errored = true
}

for _, p := range lps {
paths = append(paths, path.Clean(p))
}
}

return paths
return paths, errored
}

func parseLockfile(pathToLock string, parseAs string, args []string) (lockfile.Lockfile, error) {
Expand Down Expand Up @@ -529,13 +537,21 @@ This flag can be passed multiple times to ignore different vulnerabilities`)
}
}

pathsToLocks := findAllLockfiles(r, cli.Args(), *parseAs)
pathsToLocks, errored := findAllLockfiles(r, cli.Args(), *parseAs)

if len(pathsToLocks) == 0 {
r.PrintError(
"You must provide at least one path to either a lockfile or a directory containing at least one lockfile (see --help for usage and flags)\n",
)

// being provided with at least one path and not hitting an error on any of those
// paths means everything was valid, we just didn't find any parsable lockfiles
// in any of the directories
if len(cli.Args()) > 0 && !errored {
// so we want to use a specific exit code to represent this state
return 128
}

return 127
}

Expand Down
91 changes: 88 additions & 3 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,88 @@ func TestRun(t *testing.T) {
csv-row
`,
},
// only the files in the given directories are checked (no recursion)
{
name: "",
args: []string{"./fixtures/locks-none"},
args: []string{"./fixtures/"},
wantExitCode: 128,
wantStdout: "",
wantStderr: `
You must provide at least one path to either a lockfile or a directory containing at least one lockfile (see --help for usage and flags)
`,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

testCli(t, tt)
})
}
}

func TestRun_EmptyDirExitCode(t *testing.T) {
t.Parallel()

tests := []cliTestCase{
// no paths should return standard error exit code
{
name: "",
args: []string{},
wantExitCode: 127,
wantStdout: "",
wantStderr: `
You must provide at least one path to either a lockfile or a directory containing at least one lockfile (see --help for usage and flags)
`,
},
// one directory without any lockfiles should result in "no lockfiles in directories" exit code
{
name: "",
args: []string{"./fixtures/locks-none"},
wantExitCode: 128,
wantStdout: "",
wantStderr: `
You must provide at least one path to either a lockfile or a directory containing at least one lockfile (see --help for usage and flags)
`,
},
// two directories without any lockfiles should return "no lockfiles in directories" exit code
{
name: "",
args: []string{"./fixtures/locks-none", "./fixtures/"},
wantExitCode: 128,
wantStdout: "",
wantStderr: `
You must provide at least one path to either a lockfile or a directory containing at least one lockfile (see --help for usage and flags)
`,
},
// a path to an unknown lockfile should return standard error exit code
{
name: "",
args: []string{"./fixtures/locks-none/my-file.txt"},
wantExitCode: 127,
wantStdout: `
Loaded the following OSV databases:
`,
wantStderr: `
Error, could not determine parser for fixtures/locks-none/my-file.txt
`,
},
// mix and match of directory without any lockfiles and a path to an unknown lockfile should return standard exit code
{
name: "",
args: []string{"./fixtures/locks-none/my-file.txt", "./fixtures/"},
wantExitCode: 127,
wantStdout: `
Loaded the following OSV databases:
`,
wantStderr: `
Error, could not determine parser for fixtures/locks-none/my-file.txt
`,
},
// when the directory does not exist, the exit code should not be for "no lockfiles in directories"
{
name: "",
args: []string{"./fixtures/does/not/exist"},
Expand All @@ -156,12 +229,24 @@ func TestRun(t *testing.T) {
You must provide at least one path to either a lockfile or a directory containing at least one lockfile (see --help for usage and flags)
`,
},
// only the files in the given directories are checked (no recursion)
// an empty directory + a directory that does not exist should return standard exit code
{
name: "",
args: []string{"./fixtures/"},
args: []string{"./fixtures/does/not/exist", "./fixtures/locks-none"},
wantExitCode: 127,
wantStdout: "",
// "file not found" message is different on Windows vs other OSs
wantStderr: `
Error reading ./fixtures/does/not/exist: open ./fixtures/does/not/exist: %%
You must provide at least one path to either a lockfile or a directory containing at least one lockfile (see --help for usage and flags)
`,
},
// when there are no parsable lockfiles in the directory + --json should give sensible json
{
name: "",
args: []string{"--json", "./fixtures/locks-none"},
wantExitCode: 128,
wantStdout: `{"results":[]}`,
wantStderr: `
You must provide at least one path to either a lockfile or a directory containing at least one lockfile (see --help for usage and flags)
`,
Expand Down

0 comments on commit 2009ec2

Please sign in to comment.