diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cbb12095..366ae693 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -37,6 +37,15 @@ "go.lintFlags": [ "--fast" ], + "go.coverOnSave": true, + "go.coverageDecorator": { + "type": "gutter", + "coveredHighlightColor": "rgba(64,128,128,0.5)", + "uncoveredHighlightColor": "rgba(128,64,64,0.25)", + "coveredGutterStyle": "blockgreen", + "uncoveredGutterStyle": "blockred" + }, + "go.coverOnSingleTest": true, "editor.formatOnSave": true, "files.insertFinalNewline": true, "files.trimFinalNewlines": true, diff --git a/.devcontainer/scripts/docker-debian.sh b/.devcontainer/scripts/docker-debian.sh index c720394f..77a94363 100755 --- a/.devcontainer/scripts/docker-debian.sh +++ b/.devcontainer/scripts/docker-debian.sh @@ -7,13 +7,17 @@ # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md # Maintainer: The VS Code and Codespaces Teams # -# Syntax: ./docker-debian.sh [enable non-root docker socket access flag] [source socket] [target socket] [non-root user] [use moby] +# Syntax: ./docker-debian.sh [enable non-root docker socket access flag] [source socket] [target socket] [non-root user] [use moby] [CLI version] +# shellcheck disable=SC2068,SC2086,SC2155,SC2012,SC2236,SC1091 ENABLE_NONROOT_DOCKER=${1:-"true"} SOURCE_SOCKET=${2:-"/var/run/docker-host.sock"} TARGET_SOCKET=${3:-"/var/run/docker.sock"} USERNAME=${4:-"automatic"} USE_MOBY=${5:-"true"} +DOCKER_VERSION=${6:-"latest"} +MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" +DOCKER_DASH_COMPOSE_VERSION="1" set -e @@ -39,8 +43,23 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then USERNAME=root fi +# Get central common setting +get_common_setting() { + if [ "${common_settings_file_loaded}" != "true" ]; then + curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping." + common_settings_file_loaded=true + fi + if [ -f "/tmp/vsdc-settings.env" ]; then + local multi_line="" + if [ "$2" = "true" ]; then multi_line="-z"; fi + local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')" + if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi + fi + echo "$1=${!1}" +} + # Function to run apt-get if needed -apt-get-update-if-needed() +apt_get_update_if_needed() { if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then echo "Running apt-get update..." @@ -50,33 +69,114 @@ apt-get-update-if-needed() fi } +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install --no-install-recommends "$@" + fi +} + +# Figure out correct version of a three part version number is not passed +find_version_from_git_tags() { + local variable_name=$1 + local requested_version=${!variable_name} + if [ "${requested_version}" = "none" ]; then return; fi + local repository=$2 + local prefix=${3:-"tags/v"} + local separator=${4:-"."} + local last_part_optional=${5:-"false"} + if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then + local escaped_separator=${separator//./\\.} + local last_part + if [ "${last_part_optional}" = "true" ]; then + last_part="(${escaped_separator}[0-9]+)?" + else + last_part="${escaped_separator}[0-9]+" + fi + local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$" + local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" + if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then + declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" + else + set +e + declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" + set -e + fi + fi + if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then + echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 + exit 1 + fi + echo "${variable_name}=${!variable_name}" +} + # Ensure apt is in non-interactive to avoid prompts export DEBIAN_FRONTEND=noninteractive -# Install apt-transport-https, curl, gpg if missing -if ! dpkg -s apt-transport-https curl ca-certificates > /dev/null 2>&1 || ! type gpg > /dev/null 2>&1; then - apt-get-update-if-needed - apt-get -y install --no-install-recommends apt-transport-https curl ca-certificates gnupg2 +# Install dependencies +check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr +if ! type git > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install git +fi + +# Source /etc/os-release to get OS info +. /etc/os-release +# Fetch host/container arch. +architecture="$(dpkg --print-architecture)" + +# Set up the necessary apt repos (either Microsoft's or Docker's) +if [ "${USE_MOBY}" = "true" ]; then + + cli_package_name="moby-cli" + + # Import key safely and import Microsoft apt repo + get_common_setting MICROSOFT_GPG_KEYS_URI + curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg + echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list +else + # Name of proprietary engine package + cli_package_name="docker-ce-cli" + + # Import key safely and import Docker apt repo + curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list +fi + +# Refresh apt lists +apt-get update + +# Soft version matching for CLI +if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then + # Empty, meaning grab whatever "latest" is in apt repo + cli_version_suffix="" +else + # Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...) + docker_version_dot_escaped="${DOCKER_VERSION//./\\.}" + docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}" + # Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/ + docker_version_regex="^(.+:)?${docker_version_dot_plus_escaped}([\\.\\+ ~:-]|$)" + set +e # Don't exit if finding version fails - will handle gracefully + cli_version_suffix="=$(apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")" + set -e + if [ -z "${cli_version_suffix}" ] || [ "${cli_version_suffix}" = "=" ]; then + echo "(!) No full or partial Docker / Moby version match found for \"${DOCKER_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:" + apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+' + exit 1 + fi + echo "cli_version_suffix ${cli_version_suffix}" fi # Install Docker / Moby CLI if not already installed if type docker > /dev/null 2>&1; then echo "Docker / Moby CLI already installed." else - # Source /etc/os-release to get OS info - . /etc/os-release if [ "${USE_MOBY}" = "true" ]; then - # Import key safely (new 'signed-by' method rather than deprecated apt-key approach) and install - curl -sSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list - apt-get update - apt-get -y install --no-install-recommends moby-cli moby-buildx + apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx + apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." else - # Import key safely (new 'signed-by' method rather than deprecated apt-key approach) and install - curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list - apt-get update - apt-get -y install --no-install-recommends docker-ce-cli + apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix} fi fi @@ -90,19 +190,26 @@ else fi if [ "${TARGET_COMPOSE_ARCH}" != "x86_64" ]; then # Use pip to get a version that runns on this architecture - if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv pipx > /dev/null 2>&1; then - apt-get-update-if-needed - apt-get -y install python3-minimal python3-pip libffi-dev python3-venv pipx + if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install python3-minimal python3-pip libffi-dev python3-venv fi export PIPX_HOME=/usr/local/pipx mkdir -p ${PIPX_HOME} export PIPX_BIN_DIR=/usr/local/bin + export PYTHONUSERBASE=/tmp/pip-tmp export PIP_CACHE_DIR=/tmp/pip-tmp/cache - pipx install --system-site-packages --pip-args '--no-cache-dir --force-reinstall' docker-compose + pipx_bin=pipx + if ! type pipx > /dev/null 2>&1; then + pip3 install --disable-pip-version-check --no-warn-script-location --no-cache-dir --user pipx + pipx_bin=/tmp/pip-tmp/bin/pipx + fi + ${pipx_bin} install --system-site-packages --pip-args '--no-cache-dir --force-reinstall' docker-compose rm -rf /tmp/pip-tmp else - LATEST_COMPOSE_VERSION=$(basename "$(curl -fsSL -o /dev/null -w "%{url_effective}" https://github.com/docker/compose/releases/latest)") - curl -fsSL "https://github.com/docker/compose/releases/download/${LATEST_COMPOSE_VERSION}/docker-compose-$(uname -s)-${TARGET_COMPOSE_ARCH}" -o /usr/local/bin/docker-compose + find_version_from_git_tags DOCKER_DASH_COMPOSE_VERSION "https://github.com/docker/compose" "tags/" + echo "(*) Installing docker-compose ${DOCKER_DASH_COMPOSE_VERSION}..." + curl -fsSL "https://github.com/docker/compose/releases/download/${DOCKER_DASH_COMPOSE_VERSION}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose fi fi @@ -111,6 +218,7 @@ fi if [ -f "/usr/local/share/docker-init.sh" ]; then exit 0 fi +echo "docker-init doesnt exist, adding..." # By default, make the source and target sockets the same if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then @@ -128,7 +236,7 @@ fi # If enabling non-root access and specified user is found, setup socat and add script chown -h "${USERNAME}":root "${TARGET_SOCKET}" if ! dpkg -s socat > /dev/null 2>&1; then - apt-get-update-if-needed + apt_get_update_if_needed apt-get -y install socat fi tee /usr/local/share/docker-init.sh > /dev/null \ diff --git a/cmd/root.go b/cmd/root.go index 3db49a71..e4822d03 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -101,9 +101,19 @@ func rootRunE(cmd *cobra.Command, args []string) error { var ignorer *ignore.Ignore if !noIgnore { - ignorer = ignore.NewIgnore(cfg.IgnoreFiles) + cwd, err := os.Getwd() + if err != nil { + return err + } + fs, err := ignore.GetRootGitDir(cwd) + if err != nil { + return err + } + ignorer, err = ignore.NewIgnore(fs, cfg.IgnoreFiles) + if err != nil { + return err + } } - p := parser.NewParser(cfg.Rules, ignorer) print, err := printer.NewPrinter(outputName, output.Stdout) diff --git a/cmd/root_test.go b/cmd/root_test.go index 577e4992..87071e7e 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -111,6 +111,9 @@ func TestRunE(t *testing.T) { t.Run("findings w error", func(t *testing.T) { exitOneOnFailure = true + // don't ignore testdata folder + noIgnore = true + t.Cleanup(func() { exitOneOnFailure = false }) diff --git a/docs/ignore.md b/docs/ignore.md index 8cd66056..6d9fe7c4 100644 --- a/docs/ignore.md +++ b/docs/ignore.md @@ -74,3 +74,27 @@ func main() { fmt.Println("and here is the blacklist") } ``` + +## Nested Ignore Files + +`woke` will apply ignore rules from nested ignore files to any child files/folders, similar to a nested `.gitignore` file. Nested ignore files work for any ignore file type listed above. + +```txt +project +│ README.md +│ .wokeignore (applies to whole project) +│ +└───folder1 +│ │ file011.txt +│ │ file012.txt +│ │ .wokeignore (applies to file011.txt, file012.txt, and subfolder1) +│ │ +│ └───subfolder1 +│ │ file111.txt +│ │ file112.txt +│ │ ... +│ +└───folder2 + │ file021.txt + │ file022.txt +``` diff --git a/docs/snippets/woke.md b/docs/snippets/woke.md index 90b669fe..22b570fa 100644 --- a/docs/snippets/woke.md +++ b/docs/snippets/woke.md @@ -31,4 +31,4 @@ woke [globs ...] [flags] --stdin Read from stdin ``` -###### Auto generated by spf13/cobra on 22-Nov-2021 +###### Auto generated by spf13/cobra on 6-Dec-2021 diff --git a/go.mod b/go.mod index d4998776..d9d1117d 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,8 @@ require ( github.com/caitlinelfring/go-env-default v1.0.0 github.com/fatih/color v1.13.0 github.com/get-woke/fastwalk v1.0.0 - github.com/get-woke/go-gitignore v1.1.2 + github.com/go-git/go-billy/v5 v5.3.1 + github.com/go-git/go-git/v5 v5.4.2 github.com/mattn/go-colorable v0.1.12 github.com/mitchellh/go-homedir v1.1.0 github.com/rs/zerolog v1.26.0 @@ -17,11 +18,14 @@ require ( ) require ( + github.com/acomagu/bufpipe v1.0.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/go-git/gcfg v1.5.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/mapstructure v1.4.2 // indirect @@ -34,8 +38,12 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect + golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect golang.org/x/text v0.3.6 // indirect gopkg.in/ini.v1 v1.63.2 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) + +replace github.com/go-git/go-git/v5 => github.com/inclusive-dev-tools/go-git/v5 v5.4.4 diff --git a/go.sum b/go.sum index c8fbaa30..352277c4 100644 --- a/go.sum +++ b/go.sum @@ -45,12 +45,19 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/caitlinelfring/go-env-default v1.0.0 h1:DY8qY3OKb9PrGe+sjop7dw7tOKh6kV8cdZONlESSbZc= @@ -69,9 +76,11 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -84,14 +93,20 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/get-woke/fastwalk v1.0.0 h1:Ti8z9nf231TUhLzBuJQV/rh/b5qiuyPunYun4FpEshQ= github.com/get-woke/fastwalk v1.0.0/go.mod h1:WTDsePalrkZGMzdzn5YabupFqFmU/MjxGM2CSSc9sOo= -github.com/get-woke/go-gitignore v1.1.2 h1:FgPanYFAZjhXtJM1d19OX/IpXZw7D0Wkj6a//ryOE3M= -github.com/get-woke/go-gitignore v1.1.2/go.mod h1:qbLKpA7cfqI6TqvUDGqE4hsYke8nuiKvmepVBFBwVh4= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -199,23 +214,35 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inclusive-dev-tools/go-git/v5 v5.4.4 h1:P4Wo7sL0oPZjbo08Co00hIfHHfmkhbqa1r6O2Kbuumc= +github.com/inclusive-dev-tools/go-git/v5 v5.4.4/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -247,11 +274,13 @@ github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -269,8 +298,10 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -289,6 +320,7 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -298,6 +330,7 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -319,6 +352,7 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -326,6 +360,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -398,8 +434,10 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -430,6 +468,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -440,6 +479,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -473,9 +513,11 @@ golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -690,15 +732,21 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/ignore/ignore.go b/pkg/ignore/ignore.go index c0ca3cd8..22858070 100644 --- a/pkg/ignore/ignore.go +++ b/pkg/ignore/ignore.go @@ -1,19 +1,22 @@ package ignore import ( - "errors" - "io/ioutil" "os" + "path/filepath" "strings" "time" - gitignore "github.com/get-woke/go-gitignore" + "github.com/go-git/go-billy/v5" + "github.com/go-git/go-billy/v5/osfs" + "github.com/go-git/go-git/v5/plumbing/format/gitignore" "github.com/rs/zerolog/log" + + "github.com/get-woke/woke/pkg/util" ) // Ignore is a gitignore-style object to ignore files/directories type Ignore struct { - matcher *gitignore.GitIgnore + matcher gitignore.Matcher } var defaultIgnoreFiles = []string{ @@ -23,9 +26,47 @@ var defaultIgnoreFiles = []string{ ".git/info/exclude", } +// Given a workingDir (example: /root/proj/subDir/curDir) +// and given a gitRoot (example: /root/proj) +// it will return []string{"subDir", "curDir"}. +// This is the domain (path from git root) that ignore rules will apply to. +func getDomainFromWorkingDir(workingDir, gitRoot string) []string { + // if working directory does not end with a slash, add it + if !strings.HasSuffix(gitRoot, string(os.PathSeparator)) { + gitRoot += string(os.PathSeparator) + } + + res := strings.SplitN(workingDir, gitRoot, 2) + if len(res) > 1 { + x := util.FilterEmptyStrings(strings.Split(res[1], string(os.PathSeparator))) + return x + } + return []string{} +} + +func GetRootGitDir(workingDir string) (filesystem billy.Filesystem, err error) { + dir, err := filepath.Abs(workingDir) + if err != nil { + return + } + + for { + if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil { + return osfs.New(dir), nil + } + + parent := filepath.Dir(dir) + if parent == dir { + log.Debug().Msg("Could Not Find Root Git Folder") + return osfs.New(workingDir), nil + } + dir = parent + } +} + // NewIgnore produces an Ignore object, with compiled lines from defaultIgnoreFiles // which you can match files against -func NewIgnore(lines []string) *Ignore { +func NewIgnore(filesystem billy.Filesystem, lines []string) (ignore *Ignore, err error) { start := time.Now() defer func() { log.Debug(). @@ -33,34 +74,32 @@ func NewIgnore(lines []string) *Ignore { Msg("finished compiling ignores") }() - for _, filename := range defaultIgnoreFiles { - lines = append(lines, readIgnoreFile(filename)...) + var cwd string + if cwd, err = os.Getwd(); err != nil { + return } - ignorer := Ignore{ - matcher: gitignore.CompileIgnoreLines(lines...), + var ps []gitignore.Pattern + if ps, err = gitignore.ReadPatterns(filesystem, nil, defaultIgnoreFiles); err != nil { + return } - return &ignorer -} - -// Match returns true if the provided file matches any of the defined ignores -func (i *Ignore) Match(f string) bool { - return i.matcher.MatchesPath(f) -} + // get domain for git ignore rules supplied from the lines argument + domain := getDomainFromWorkingDir(cwd, filesystem.Root()) + for _, line := range lines { + pattern := gitignore.ParsePattern(line, domain) + ps = append(ps, pattern) + } -func readIgnoreFile(file string) []string { - buffer, err := ioutil.ReadFile(file) - if err != nil { - _event := log.Warn() - if errors.Is(err, os.ErrNotExist) { - _event = log.Debug() - } - _event.Err(err).Str("file", file).Msg("skipping ignorefile") - return []string{} + ignore = &Ignore{ + matcher: gitignore.NewMatcher(ps), } - log.Debug().Str("file", file).Msg("adding ignorefile") + return +} - return strings.Split(strings.TrimSpace(string(buffer)), "\n") +// Match returns true if the provided file matches any of the defined ignores +func (i *Ignore) Match(f string, isDir bool) bool { + parts := util.FilterEmptyStrings(strings.Split(f, string(os.PathSeparator))) + return i.matcher.Match(parts, isDir) } diff --git a/pkg/ignore/ignore_test.go b/pkg/ignore/ignore_test.go index 5df40443..e7be706f 100644 --- a/pkg/ignore/ignore_test.go +++ b/pkg/ignore/ignore_test.go @@ -1,59 +1,169 @@ package ignore import ( + "fmt" "os" "path/filepath" "testing" + "github.com/go-git/go-billy/v5" + "github.com/go-git/go-billy/v5/osfs" + "github.com/go-git/go-billy/v5/util" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" ) func init() { zerolog.SetGlobalLevel(zerolog.NoLevel) } -func TestIgnore_Match(t *testing.T) { - i := NewIgnore([]string{"my/files/*"}) - assert.NotNil(t, i) +type IgnoreTestSuite struct { + suite.Suite + GFS billy.Filesystem // git repository root +} + +func TempFileSystem() (fs billy.Filesystem, clean func()) { + fs = osfs.New(os.TempDir()) + path, err := util.TempDir(fs, "", "") + if err != nil { + panic(err) + } + + fs, err = fs.Chroot(path) + if err != nil { + panic(err) + } + + return fs, func() { + _ = util.RemoveAll(fs, path) + } +} + +func (suite *IgnoreTestSuite) SetupTest() { + // setup generic git repository root + fs, clean := TempFileSystem() + defer clean() + f, err := fs.Create(".gitignore") + suite.NoError(err) + _, err = f.Write([]byte("*.DS_Store\n")) + suite.NoError(err) + err = f.Close() + suite.NoError(err) + + err = fs.MkdirAll(".git", os.ModePerm) + suite.NoError(err) - // Test if rules with backslashes match on windows - assert.False(t, i.Match("not/foo")) - assert.True(t, i.Match("my/files/file1")) - assert.False(t, i.Match("my/files")) + f, err = fs.Create(".ignore") + suite.NoError(err) + _, err = f.Write([]byte("*.IGNORE\n")) + suite.NoError(err) + err = f.Close() + suite.NoError(err) + + f, err = fs.Create(".notignored") + suite.NoError(err) + _, err = f.Write([]byte("*.NOTIGNORED\n")) + suite.NoError(err) + err = f.Close() + suite.NoError(err) + + f, err = fs.Create(".wokeignore") + suite.NoError(err) + _, err = f.Write([]byte("*.WOKEIGNORE\ntestFolder\n")) + suite.NoError(err) + err = f.Close() + suite.NoError(err) + + suite.GFS = fs +} + +func BenchmarkIgnore(b *testing.B) { + zerolog.SetGlobalLevel(zerolog.NoLevel) + fs, clean := TempFileSystem() + defer clean() + for i := 0; i < 10; i++ { + for j := 0; j < 10; j++ { + err := fs.MkdirAll(fs.Join(fmt.Sprintf("%d", i), fmt.Sprintf("%d", j)), os.ModePerm) + assert.NoError(b, err) + f, err := fs.Create(".wokeignore") + assert.NoError(b, err) + _, err = f.Write([]byte("testFolder")) + assert.NoError(b, err) + err = f.Close() + assert.NoError(b, err) + for k := 0; k < 100; k++ { + f, err := fs.Create(fmt.Sprintf("%d.txt", k)) + assert.NoError(b, err) + err = f.Close() + assert.NoError(b, err) + } + } + } - assert.False(t, i.Match(filepath.Join("not", "foo"))) - assert.True(t, i.Match(filepath.Join("my", "files", "file1"))) - assert.False(t, i.Match(filepath.Join("my", "files"))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + ignorer, err := NewIgnore(fs, []string{}) + assert.NoError(b, err) + ignorer.Match(filepath.Join("not", "foo"), false) + } +} + +func (suite *IgnoreTestSuite) TestGetDomainFromWorkingDir() { + suite.Equal([]string{}, getDomainFromWorkingDir(filepath.FromSlash("a/b/c/d"), filepath.FromSlash("b/c/d"))) + suite.Equal([]string{}, getDomainFromWorkingDir(filepath.FromSlash("a/b/c/d"), filepath.FromSlash("a/b/c/d"))) + suite.Equal([]string{"d"}, getDomainFromWorkingDir(filepath.FromSlash("a/b/c/d"), "c")) + suite.Equal([]string{"d"}, getDomainFromWorkingDir(filepath.FromSlash("a/b/c/d"), filepath.FromSlash("b/c"))) + suite.Equal([]string{"b", "c", "d"}, getDomainFromWorkingDir(filepath.FromSlash("a/b/c/d"), "a")) + suite.Equal([]string{"c", "d"}, getDomainFromWorkingDir(filepath.FromSlash("a/b/c/d"), filepath.FromSlash("b/"))) + suite.Equal([]string{"b", "c", "d"}, getDomainFromWorkingDir(filepath.FromSlash("b/b/c/d"), filepath.FromSlash("b/"))) +} + +func (suite *IgnoreTestSuite) TestGetRootGitDir() { + cwd, err := os.Getwd() + suite.NoError(err) + + rootFs, err := GetRootGitDir(cwd) + suite.NoError(err) + suite.Equal(filepath.Dir(filepath.Dir(cwd)), rootFs.Root()) +} + +func (suite *IgnoreTestSuite) TestGetRootGitDirNotExist() { + fs, clean := TempFileSystem() + defer clean() + rootFs, err := GetRootGitDir(fs.Root()) + suite.NoError(err) + suite.Equal(fs.Root(), rootFs.Root()) +} +func (suite *IgnoreTestSuite) TestIgnore_Match() { + i, err := NewIgnore(suite.GFS, []string{"my/files/*"}) + suite.NoError(err) + suite.NotNil(i) + + suite.False(i.Match(filepath.Join("not", "foo"), false)) + suite.True(i.Match(filepath.Join("my", "files", "file1"), false)) + suite.False(i.Match(filepath.Join("my", "files"), false)) } // Test all default ignore files, except for .git/info/exclude, since // that uses a .git directory that we cannot check in. -func TestIgnoreDefaultIgoreFiles_Match(t *testing.T) { - // Temporarily change into testdata directojry for this test - // since paths are relative - err := os.Chdir("testdata") - assert.NoError(t, err) - t.Cleanup(func() { - err = os.Chdir("..") - assert.NoError(t, err) - }) - - i := NewIgnore([]string{"*.FROMARGUMENT"}) - assert.NotNil(t, i) - - assert.False(t, i.Match("notfoo")) - assert.True(t, i.Match("test.FROMARGUMENT")) // From .gitignore - assert.True(t, i.Match("test.DS_Store")) // From .gitignore - assert.True(t, i.Match("test.IGNORE")) // From .ignore - assert.True(t, i.Match("test.WOKEIGNORE")) // From .wokeignore - assert.False(t, i.Match("test.NOTIGNORED")) // From .notincluded - making sure only default are included -} - -func TestReadIgnoreFile(t *testing.T) { - ignoreLines := readIgnoreFile("testdata/.gitignore") - assert.Equal(t, []string{"*.DS_Store"}, ignoreLines) - - noIgnoreLines := readIgnoreFile(".gitignore") - assert.Equal(t, []string{}, noIgnoreLines) +func (suite *IgnoreTestSuite) TestIgnoreDefaultIgoreFiles_Match() { + i, err := NewIgnore(suite.GFS, []string{"*.FROMARGUMENT"}) + suite.NoError(err) + suite.NotNil(i) + + suite.False(i.Match(filepath.Join("testdata", "notfoo"), false)) + suite.True(i.Match(filepath.Join("testdata", "test.FROMARGUMENT"), false)) // From .gitignore + suite.True(i.Match(filepath.Join("testdata", "test.DS_Store"), false)) // From .gitignore + suite.True(i.Match(filepath.Join("testdata", "test.IGNORE"), false)) // From .ignore + suite.True(i.Match(filepath.Join("testdata", "test.WOKEIGNORE"), false)) // From .wokeignore + suite.True(i.Match(filepath.Join("testdata", "testFolder"), true)) // From .wokeignore + suite.False(i.Match(filepath.Join("testdata", "notTestFolder"), true)) // From .wokeignore + suite.False(i.Match(filepath.Join("testdata", "test.NOTIGNORED"), false)) // From .notincluded - making sure only default are included +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestIgnoreTestSuite(t *testing.T) { + suite.Run(t, new(IgnoreTestSuite)) } diff --git a/pkg/ignore/testdata/.gitignore b/pkg/ignore/testdata/.gitignore deleted file mode 100644 index 5509140f..00000000 --- a/pkg/ignore/testdata/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.DS_Store diff --git a/pkg/ignore/testdata/.ignore b/pkg/ignore/testdata/.ignore deleted file mode 100644 index 1caa4714..00000000 --- a/pkg/ignore/testdata/.ignore +++ /dev/null @@ -1 +0,0 @@ -*.IGNORE diff --git a/pkg/ignore/testdata/.notignored b/pkg/ignore/testdata/.notignored deleted file mode 100644 index aa39ea77..00000000 --- a/pkg/ignore/testdata/.notignored +++ /dev/null @@ -1 +0,0 @@ -*.NOTIGNORED diff --git a/pkg/ignore/testdata/.wokeignore b/pkg/ignore/testdata/.wokeignore deleted file mode 100644 index f0ab2a94..00000000 --- a/pkg/ignore/testdata/.wokeignore +++ /dev/null @@ -1 +0,0 @@ -*.WOKEIGNORE diff --git a/pkg/parser/findings_test.go b/pkg/parser/findings_test.go index 348f62d3..9722b09e 100644 --- a/pkg/parser/findings_test.go +++ b/pkg/parser/findings_test.go @@ -30,7 +30,8 @@ func TestGenerateFileFindings(t *testing.T) { t.Run(tc.desc, func(t *testing.T) { f, err := newFile(t, tc.content) assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) res, err := p.generateFileFindingsFromFilename(f.Name()) assert.NoError(t, err) @@ -60,8 +61,9 @@ func TestGenerateFileFindings(t *testing.T) { }) } t.Run("missing file", func(t *testing.T) { - p := testParser() - _, err := p.generateFileFindingsFromFilename("missing.file") + p, err := testParser() + assert.NoError(t, err) + _, err = p.generateFileFindingsFromFilename("missing.file") assert.Error(t, err) }) @@ -69,7 +71,8 @@ func TestGenerateFileFindings(t *testing.T) { f, err := newFileWithPrefix(t, "whitelist-", "content") assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) res, err := p.generateFileFindingsFromFilename(f.Name()) assert.NoError(t, err) assert.Len(t, res.Results, 1) @@ -80,7 +83,8 @@ func TestGenerateFileFindings(t *testing.T) { f, err := newFileWithPrefix(t, "empty-whitelist-", "") assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) res, err := p.generateFileFindingsFromFilename(f.Name()) assert.NoError(t, err) assert.Len(t, res.Results, 1) @@ -127,7 +131,8 @@ func TestGenerateFileFindingsOverlappingRules(t *testing.T) { f, err := newFile(t, tc.content) assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) res, err := p.generateFileFindingsFromFilename(f.Name()) assert.NoError(t, err) assert.Len(t, res.Results, tc.matches) @@ -152,7 +157,8 @@ func TestGenerateFileFindingsNewLineIgnores(t *testing.T) { f, err := newFile(t, tc.content) assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) res, err := p.generateFileFindingsFromFilename(f.Name()) assert.NoError(t, err) assert.Len(t, res.Results, tc.matches) diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 1a9ea844..a542b7f3 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -131,8 +131,8 @@ func (p *Parser) walkDir(dirname string, done chan bool) <-chan string { go func() { defer close(paths) - _ = walker.Walk(dirname, func(path string, _ os.FileMode) error { - if p.Ignorer != nil && p.Ignorer.Match(path) { + _ = walker.Walk(dirname, func(path string, info os.FileMode) error { + if p.Ignorer != nil && p.Ignorer.Match(path, info.IsDir()) { log.Debug().Str("file", path).Str("reason", "ignored file").Msg("skipping") return nil } diff --git a/pkg/parser/parser_test.go b/pkg/parser/parser_test.go index d29a691b..639395e7 100644 --- a/pkg/parser/parser_test.go +++ b/pkg/parser/parser_test.go @@ -7,12 +7,13 @@ import ( "path/filepath" "testing" + "github.com/go-git/go-billy/v5/osfs" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/get-woke/woke/pkg/ignore" "github.com/get-woke/woke/pkg/result" "github.com/get-woke/woke/pkg/rule" - - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" ) type testPrinter struct { @@ -35,9 +36,19 @@ func (p *testPrinter) PrintSuccessExitMessage() bool { return true } -func testParser() *Parser { +func testParser() (parser *Parser, err error) { r := rule.TestRule - return NewParser([]*rule.Rule{&r}, ignore.NewIgnore([]string{})) + cwd, err := os.Getwd() + if err != nil { + return + } + fs := osfs.New(cwd) + ignorer, err := ignore.NewIgnore(fs, []string{}) + if err != nil { + return + } + parser = NewParser([]*rule.Rule{&r}, ignorer) + return } func parsePathTests(t *testing.T) { @@ -46,7 +57,8 @@ func parsePathTests(t *testing.T) { assert.NoError(t, err) pr := new(testPrinter) - p := testParser() + p, err := testParser() + assert.NoError(t, err) findings := p.ParsePaths(pr, f.Name()) assert.Len(t, pr.results, 1) assert.Equal(t, len(pr.results), findings) @@ -81,7 +93,8 @@ func parsePathTests(t *testing.T) { f, err := newFile(t, "i have a no findings\n") assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) pr := new(testPrinter) findings := p.ParsePaths(pr, f.Name()) assert.Len(t, pr.results, 0) @@ -92,7 +105,8 @@ func parsePathTests(t *testing.T) { f, err := newFileWithPrefix(t, "whitelist", "") assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) pr := new(testPrinter) findings := p.ParsePaths(pr, f.Name()) assert.Len(t, pr.results, 1) @@ -103,7 +117,8 @@ func parsePathTests(t *testing.T) { f, err := newFile(t, "") assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) pr := new(testPrinter) findings := p.ParsePaths(pr, f.Name()) assert.Len(t, pr.results, 0) @@ -117,7 +132,8 @@ func parsePathTests(t *testing.T) { assert.NoError(t, err) // Test with multiple paths supplied - p := testParser() + p, err := testParser() + assert.NoError(t, err) pr := new(testPrinter) findings := p.ParsePaths(pr, f1.Name(), f2.Name()) assert.Len(t, pr.results, 1) @@ -128,8 +144,14 @@ func parsePathTests(t *testing.T) { f, err := newFile(t, "i have a whitelist finding, but am ignored\n") assert.NoError(t, err) - p := testParser() - p.Ignorer = ignore.NewIgnore([]string{filepath.ToSlash(f.Name())}) + p, err := testParser() + assert.NoError(t, err) + cwd, err := os.Getwd() + assert.NoError(t, err) + fs := osfs.New(cwd) + ignorer, err := ignore.NewIgnore(fs, []string{filepath.ToSlash(f.Name())}) + assert.NoError(t, err) + p.Ignorer = ignorer pr := new(testPrinter) findings := p.ParsePaths(pr, f.Name()) @@ -141,7 +163,8 @@ func parsePathTests(t *testing.T) { f, err := newFile(t, "i have a whitelist finding, but am ignored # wokeignore:rule=whitelist\n") assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) pr := new(testPrinter) findings := p.ParsePaths(pr, f.Name()) @@ -153,7 +176,8 @@ func parsePathTests(t *testing.T) { f, err := newFile(t, "i have a whitelist finding, but am ignored # wokeignore:rule=whitelist\n") assert.NoError(t, err) - p := testParser() + p, err := testParser() + assert.NoError(t, err) p.Ignorer = nil pr := new(testPrinter) @@ -164,8 +188,14 @@ func parsePathTests(t *testing.T) { t.Run("default path", func(t *testing.T) { // Test default path (which would run tests against the parser package) - p := testParser() - p.Ignorer = ignore.NewIgnore([]string{"*_test.go"}) + p, err := testParser() + assert.NoError(t, err) + cwd, err := os.Getwd() + assert.NoError(t, err) + fs := osfs.New(cwd) + ignorer, err := ignore.NewIgnore(fs, []string{"*_test.go"}) + assert.NoError(t, err) + p.Ignorer = ignorer pr := new(testPrinter) findings := p.ParsePaths(pr) @@ -175,7 +205,8 @@ func parsePathTests(t *testing.T) { t.Run("stdin", func(t *testing.T) { err := writeToStdin(t, "i have a whitelist here\n", func() { - p := testParser() + p, err := testParser() + assert.NoError(t, err) pr := new(testPrinter) findings := p.ParsePaths(pr, os.Stdin.Name()) assert.Len(t, pr.results, 1) @@ -213,7 +244,8 @@ func parsePathTests(t *testing.T) { f, err := newFile(t, "i have a whitelist") assert.NoError(t, err) const TestNote = "TEST NOTE" - p := testParser() + p, err := testParser() + assert.NoError(t, err) p.Rules[0].Note = TestNote p.Rules[0].Options.IncludeNote = nil pr := new(testPrinter) @@ -227,7 +259,8 @@ func parsePathTests(t *testing.T) { assert.NoError(t, err) const TestNote = "TEST NOTE" includeNote := true - p := testParser() + p, err := testParser() + assert.NoError(t, err) p.Rules[0].Note = TestNote p.Rules[0].Options.IncludeNote = &includeNote // Test IncludeNote flag doesn't get overridden with SetIncludeNote method @@ -287,7 +320,8 @@ func BenchmarkParsePaths(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - p := testParser() + p, err := testParser() + assert.NoError(b, err) pr := new(testPrinter) findings := p.ParsePaths(pr, tmpFile.Name()) assert.Equal(b, 1, findings) @@ -299,7 +333,8 @@ func BenchmarkParsePathsRoot(b *testing.B) { for i := 0; i < b.N; i++ { assert.NotPanics(b, func() { - p := testParser() + p, err := testParser() + assert.NoError(b, err) pr := new(testPrinter) // Unknown how many findings this will return since it's parsing the whole repo // there's no way to know for sure at any given time, so just check that it doesn't panic diff --git a/pkg/util/string.go b/pkg/util/string.go index add134e5..36f450ca 100644 --- a/pkg/util/string.go +++ b/pkg/util/string.go @@ -23,6 +23,16 @@ func InSlice(s string, slice []string) bool { return false } +func FilterEmptyStrings(s []string) []string { + var r []string + for _, str := range s { + if str != "" { + r = append(r, str) + } + } + return r +} + // ContainsAlphanumeric returns true if alphanumeric chars are found in the string func ContainsAlphanumeric(s string) bool { if len(s) == 0 { diff --git a/pkg/util/string_test.go b/pkg/util/string_test.go index e55294ee..031939a7 100644 --- a/pkg/util/string_test.go +++ b/pkg/util/string_test.go @@ -50,3 +50,19 @@ func TestContainsAlphanumeric(t *testing.T) { }) } } + +func TestFilterEmptyStrings(t *testing.T) { + tests := []struct { + s []string + actual []string + }{ + {[]string{"foo", "bar"}, []string{"foo", "bar"}}, + {[]string{"foo", "", "bar"}, []string{"foo", "bar"}}, + {[]string{"", "bar"}, []string{"bar"}}, + } + for i, tt := range tests { + t.Run(fmt.Sprintf("%s-%d", tt.s, i), func(t *testing.T) { + assert.ElementsMatch(t, FilterEmptyStrings(tt.s), tt.actual) + }) + } +}