Skip to content

Commit

Permalink
Explain why test deps may not be removed using go mod tidy. (#1378)
Browse files Browse the repository at this point in the history
* Explain why test deps may not be removed using go mod tidy.

* Apply suggestions from code review

Co-authored-by: Jessica Black <[email protected]>

* Add `rg` as an option.

* Add a script to try to find out if a go dep is likely a test only dep.

* Mention vendor code in test-only deps explainer.

---------

Co-authored-by: Jessica Black <[email protected]>
  • Loading branch information
csasarak and jssblck authored Feb 15, 2024
1 parent 66aa638 commit 3f99e7c
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 0 deletions.
28 changes: 28 additions & 0 deletions docs/references/strategies/languages/golang/gomodules.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,31 @@ not using backoff!

As a concrete step towards resolving this sort of discrepancy, we recommend running `go mod tidy` on projects regularly;
this command should synchronize the `go.mod` file with the actual state of the project.

#### Test Dependencies

Sometimes the above procedure may uncover a dependency that is not reported by FOSSA but that is also not removed by `go mod tidy`.
The other reason that a dependency may appear in `go.mod` but not in FOSSA is that it is a test dependency.
`go.mod` itself does not label dependencies as being used only in tests, but FOSSA's Go module strategy identifies and excludes test-only dependencies.
To verify that a direct dependency is not a test-only dependency, we recommend searching the project source code for where a package from the module is imported.
Generally, for a declaration in `go.mod` like:

```
github.com/prometheus/client_golang v1.12.2
```

You can search for its imports using a command like:

```sh
$ find <project_directory> -name \*.go -not -path '*vendor*' -exec grep -Hn "github.com/prometheus/client_golang" {} \;
```
If `find`/`grep` are not available on your system, you can install a tool like [ripgrep](https://github.com/BurntSushi/ripgrep?tab=readme-ov-file#ripgrep-rg) to perform the same operation:
```sh
rg 'github.com/prometheus/client_golang' --glob=\!\*vendor\* -F -l
```

If the import only appears in source files that end in `_test.go`, it is a test-only dependency.
You can read more about how tests are defined in Go [here](https://go.dev/doc/tutorial/add-a-test).

The above process will make vendored test-only dependencies seem like regular dependencies.
Doing the above search should be considered a heuristic, not definitive proof that a package is test-only or not.
43 changes: 43 additions & 0 deletions scripts/is_test_only_go_dep.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/sh

# This script returns 0 if it thinks the dep is a test-only dep, 1 otherwise.

USAGE="usage: ./go_test_only_dep.sh <project directory> <Go Package>"

if [ $# -lt 2 ]; then
echo "$USAGE"
exit 1
fi

PROJ_DIR=$1
PACKAGE=$2

# Here we exclude any directories that look like vendored code.
# If they have vendored code in other directories, this could mean there's a false positive.
IMPORTS=$(find "$PROJ_DIR" -not -path '*vendor*' -name \*.go -exec grep -Hn "$PACKAGE" {} \;)

if [ "$IMPORTS" = '' ]; then
echo "Found no references to package \"$PACKAGE\" in $PROJ_DIR"
exit 1
fi

# -v inverts the match, printing any lines that DON'T match and returning 0 on matches found, 1 otherwise.
# Any line match for the package in a file that DOES NOT have a '_test.go' string in the name is printed.
# It's a convoluted way to figure out if any of the files found referencing the package are not _test.go files.
# SH is the pits.
TEST_LINES=$(echo "$IMPORTS" | grep -v "_test.go")
HAS_TEST_LINES="$?"

# Edge case to be aware of:
# This doesn't actually parse Go source files, so even a commented reference to a package counts as a "hit".
if [ "$HAS_TEST_LINES" = "0" ] ; then
echo "$TEST_LINES" | while read -r i; do
FILE=$(echo "$i" | awk 'BEGIN { FS=":" }; {print $1}')
echo "Found package \"$PACKAGE\" in non-test file: $FILE"
exit 1
done
else
echo "Package \"$PACKAGE\" not found in any non-*_test.go file."
echo "It is likely a test-only package."
exit 0
fi

0 comments on commit 3f99e7c

Please sign in to comment.