vulncheck: use a Go test to run a bash script

This allows trybots to be our CI system.

See the similar setup in x/vuln and x/vulndb.

Fixes golang/go#50467.

Change-Id: I3d339101f999846979aac580fe93555f906609be
Reviewed-on: https://go-review.googlesource.com/c/exp/+/386199
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/vulncheck/all_test.go b/vulncheck/all_test.go
new file mode 100644
index 0000000..30a18b3
--- /dev/null
+++ b/vulncheck/all_test.go
@@ -0,0 +1,28 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.17 && !windows
+// +build go1.17,!windows
+
+package vulncheck
+
+import (
+	"os"
+	"os/exec"
+	"testing"
+)
+
+func TestChecksBash(t *testing.T) {
+	bash, err := exec.LookPath("bash")
+	if err != nil {
+		t.Skipf("skipping: %v", err)
+	}
+
+	cmd := exec.Command(bash, "./checks.bash")
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err != nil {
+		t.Fatal(err)
+	}
+}
diff --git a/vulncheck/checks.bash b/vulncheck/checks.bash
new file mode 100755
index 0000000..8df0e07
--- /dev/null
+++ b/vulncheck/checks.bash
@@ -0,0 +1,133 @@
+#!/usr/bin/env bash
+# Copyright 2021 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# This file will be run by `go test`.
+# See all_test.go in this directory.
+
+go version
+
+# Ensure that installed go binaries are on the path.
+# This bash expression follows the algorithm described at the top of
+# `go install help`: first try $GOBIN, then $GOPATH/bin, then $HOME/go/bin.
+go_install_dir=${GOBIN:-${GOPATH:-$HOME/go}/bin}
+PATH=$PATH:$go_install_dir
+
+source lib.sh
+
+# ensure_go_binary verifies that a binary exists in $PATH corresponding to the
+# given go-gettable URI. If no such binary exists, it is fetched via `go get`.
+ensure_go_binary() {
+  local binary=$(basename $1)
+  if ! [ -x "$(command -v $binary)" ]; then
+    info "Installing: $1"
+    # Install the binary in a way that doesn't affect our go.mod file.
+    go install $1
+  fi
+}
+
+# verify_header checks that all given files contain the standard header for Go
+# projects.
+verify_header() {
+  if [[ "$@" != "" ]]; then
+    for FILE in $@
+    do
+        line="$(head -4 $FILE)"
+        if [[ ! $line == *"The Go Authors. All rights reserved."* ]] &&
+         [[ ! $line == "// DO NOT EDIT. This file was copied from" ]]; then
+              err "missing license header: $FILE"
+        fi
+    done
+  fi
+}
+
+# Support ** in globs for finding files throughout the tree.
+shopt -s globstar
+
+# check_headers checks that all source files that have been staged in this
+# commit, and all other non-third-party files in the repo, have a license
+# header.
+check_headers() {
+  if [[ $# -gt 0 ]]; then
+    info "Checking listed files for license header"
+    verify_header $*
+  else
+    # Check code files that have been modified or added.
+    info "Checking go and sh files for license header"
+    verify_header $(find **/*.go) $(find **/*.sh)
+  fi
+}
+
+# check_unparam runs unparam on source files.
+check_unparam() {
+  ensure_go_binary mvdan.cc/unparam
+  runcmd unparam ./...
+}
+
+# check_vet runs go vet on source files.
+check_vet() {
+  runcmd go vet -all ./...
+}
+
+# check_staticcheck runs staticcheck on source files.
+check_staticcheck() {
+  if [[ $(go version) = *go1.17* ]]; then
+    ensure_go_binary honnef.co/go/tools/cmd/staticcheck
+    runcmd staticcheck ./...
+  fi
+}
+
+# check_misspell runs misspell on source files.
+check_misspell() {
+  ensure_go_binary github.com/client9/misspell/cmd/misspell
+  # reports/GO-2020-0041.yaml is about module "github.com/unknwon/cae",
+  # and the second element is a common misspelling of unknown.
+  runcmd misspell -i "unknwon"  -error .
+}
+
+go_linters() {
+  check_vet
+  check_staticcheck
+  check_misspell
+  check_unparam
+}
+
+go_modtidy() {
+  runcmd go mod tidy
+}
+
+runchecks() {
+  check_headers
+  go_linters
+  go_modtidy
+}
+
+usage() {
+  cat <<EOUSAGE
+Usage: $0 [subcommand]
+Available subcommands:
+  help           - display this help message
+EOUSAGE
+}
+
+main() {
+  case "$1" in
+    "-h" | "--help" | "help")
+      usage
+      exit 0
+      ;;
+    "")
+      runchecks
+      ;;
+    *)
+      usage
+      exit 1
+  esac
+  if [[ $EXIT_CODE != 0 ]]; then
+    err "FAILED; see errors above"
+  fi
+  exit $EXIT_CODE
+}
+
+main $@
diff --git a/vulncheck/go.mod b/vulncheck/go.mod
index 013f4c4..056fc37 100644
--- a/vulncheck/go.mod
+++ b/vulncheck/go.mod
@@ -3,13 +3,17 @@
 go 1.17
 
 require (
-	golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023
+	github.com/client9/misspell v0.3.4
+	golang.org/x/tools v0.1.8
 	golang.org/x/vuln v0.0.0-20211207171702-7209860d2c63
+	honnef.co/go/tools v0.1.3
+	mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5
 )
 
 require (
 	cloud.google.com/go v0.97.0 // indirect
 	cloud.google.com/go/errorreporting v0.1.0 // indirect
+	github.com/BurntSushi/toml v0.3.1 // indirect
 	github.com/census-instrumentation/opencensus-proto v0.2.1 // indirect
 	github.com/cespare/xxhash v1.1.0 // indirect
 	github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 // indirect
@@ -25,7 +29,7 @@
 	golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
 	golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
-	golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
+	golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/api v0.60.0 // indirect
diff --git a/vulncheck/go.sum b/vulncheck/go.sum
index bc6c801..bf70cca 100644
--- a/vulncheck/go.sum
+++ b/vulncheck/go.sum
@@ -49,6 +49,7 @@
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -93,6 +94,7 @@
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -348,6 +350,8 @@
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -374,6 +378,8 @@
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
+github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -638,8 +644,9 @@
 golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 h1:A0Qkn7Z/n8zC1xd9LTw17AiKlBRK64tw3ejWQiEqca0=
+golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -713,8 +720,9 @@
 golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI=
 golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
+golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
+golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
 golang.org/x/vuln v0.0.0-20211207171702-7209860d2c63 h1:pKwhHArAWkz04s3lSHKpF34o0tDEbNSTyFDFuWZ0Jzw=
 golang.org/x/vuln v0.0.0-20211207171702-7209860d2c63/go.mod h1:zIQqHjf9sHpn0TOli6Vdy9mrmuePs9lmnGBRAzECxz0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -878,6 +886,7 @@
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
@@ -900,7 +909,10 @@
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o=
 honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
+mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio=
+mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5/go.mod h1:b8RRCBm0eeiWR8cfN88xeq2G5SG3VKGO+5UPWi5FSOY=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/vulncheck/lib.sh b/vulncheck/lib.sh
new file mode 100644
index 0000000..1304d3a
--- /dev/null
+++ b/vulncheck/lib.sh
@@ -0,0 +1,54 @@
+# Copyright 2021 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Library of useful bash functions and variables.
+
+RED=; GREEN=; YELLOW=; NORMAL=;
+MAXWIDTH=0
+
+if tput setaf 1 >& /dev/null; then
+  RED=`tput setaf 1`
+  GREEN=`tput setaf 2`
+  YELLOW=`tput setaf 3`
+  NORMAL=`tput sgr0`
+  MAXWIDTH=$(( $(tput cols) - 2 ))
+fi
+
+EXIT_CODE=0
+
+info() { echo -e "${GREEN}$@${NORMAL}" 1>&2; }
+warn() { echo -e "${YELLOW}$@${NORMAL}" 1>&2; }
+err() { echo -e "${RED}$@${NORMAL}" 1>&2; EXIT_CODE=1; }
+
+die() {
+  err $@
+  exit 1
+}
+
+dryrun=false
+
+# runcmd prints an info log describing the command that is about to be run, and
+# then runs it. It sets EXIT_CODE to non-zero if the command fails, but does not exit
+# the script.
+runcmd() {
+  msg="$@"
+  if $dryrun; then
+    echo -e "${YELLOW}dryrun${GREEN}\$ $msg${NORMAL}"
+    return 0
+  fi
+  # Truncate command logging for narrow terminals.
+  # Account for the 2 characters of '$ '.
+  if [[ $MAXWIDTH -gt 0 && ${#msg} -gt $MAXWIDTH ]]; then
+    msg="${msg::$(( MAXWIDTH - 3 ))}..."
+  fi
+
+  echo -e "$@\n" 1>&2;
+  $@ || err "command failed"
+}
+
+# tfvar NAME returns the value of NAME in the terraform.tfvars file.
+tfvar() {
+  local name=$1
+  awk '$1 == "'$name'" { print substr($3, 2, length($3)-2) }' terraform/terraform.tfvars
+}
diff --git a/vulncheck/tools_test.go b/vulncheck/tools_test.go
new file mode 100644
index 0000000..3c9b1d6
--- /dev/null
+++ b/vulncheck/tools_test.go
@@ -0,0 +1,14 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build tools
+// +build tools
+
+package main
+
+import (
+	_ "github.com/client9/misspell/cmd/misspell"
+	_ "honnef.co/go/tools/cmd/staticcheck"
+	_ "mvdan.cc/unparam"
+)