Repository: grafana/tanka
Pull request #849 (2023-04-26 10:37:41)

publish-page: Give write permissions explicitly
Dependabot PRs are failing this check, giving a red X in the status that makes it hard to see whether they passed CI or not. Also, we want this to work for updates to the yarn stuff.
    
Then because we do that, we lose the default permissions for other types too. Specifically, the pr-preview-action needs to comment which is  `pull-requests: write`. So set that too.

iainlane: [Upstream says](https://github.com/JamesIves/github-pages-deploy-action#getting-started-airplane) this permission is required, makes sense as it's pushing to a branch in the repo.

Not sure why it works for our PRs without this, maybe people with write access get greater permissions by default? Couldn't find docs describing this either way.

Think this is safe as it just grants access to the current repo, i.e. a fork will just push to itself. Will check that before undrafting.
github-actions[bot]: [PR Preview Action](https://github.com/rossjrw/pr-preview-action) da5b7b4a85f155769de510c8ddc083b2f5d5aece
:---:
:rocket: Deployed preview to https://grafana.github.io/tanka/pr-preview/pr-849/
on branch [`gh-pages`](https://github.com/grafana/tanka/tree/gh-pages) at 2023-04-26 10:59 UTC
<!-- Sticky Pull Request Commentpr-preview -->
julienduchesne: I think we can also not run this pipeline if docs/ isn't changed. That should fix the issue with dependabot
iainlane: > I think we can also not run this pipeline if `docs/` isn't changed. That should fix the issue with dependabot

not as it is, we have npm updates configured

We could remove those too, but seems like a good idea to update them
iainlane: alright it works here, see [this run](https://github.com/grafana/tanka/actions/runs/4808595195/jobs/8558770078). Going to remove the change to `docs` and un-draft.

Does _not_ work for forks, seems like the run there doesn't have enough permission to push to itself, maybe that's by design, not sure. See #850. Suspect that is not worse than what we currently have?

Patch:
diff --git a/.drone/drone.jsonnet b/.drone/drone.jsonnet
index e736a777..736be1fd 100644
--- a/.drone/drone.jsonnet
+++ b/.drone/drone.jsonnet
@@ -1,6 +1,6 @@
 local vault = import 'vault.libsonnet';
 
-local golang = 'golang:1.19';
+local golang = 'golang:1.20';
 
 local volumes = [{ name: 'gopath', temp: {} }];
 local mounts = [{ name: 'gopath', path: '/go' }];
@@ -76,6 +76,23 @@ local docker(arch, depends_on=[]) =
     ],
   } + constraints.pullRequest + constraints.mainPush,
 
+  pipeline('benchmark against main') {
+    node: {
+      // To ensure that benchmarks are accurate, run this build on a node that doesn't do parallel builds.
+      type: 'no-parallel',
+    },
+    steps: [
+      go('benchmark', [
+        'go test -bench=. -benchmem -count=6 -run=^$ ./... | tee bench-pr.txt',
+        'git fetch origin main',
+        'git checkout main',
+        'go test -bench=. -benchmem -count=6 -run=^$ ./... | tee bench-main.txt',
+        'go install golang.org/x/perf/cmd/...@latest',
+        'benchstat bench-main.txt bench-pr.txt',
+      ]),
+    ],
+  } + constraints.pullRequest,
+
   pipeline('release') {
     steps: [
       go('fetch-tags', ['git fetch origin --tags']),
@@ -105,7 +122,7 @@ local docker(arch, depends_on=[]) =
       ]),
       {
         name: 'manifest',
-        image: 'plugins/manifest',
+        image: 'plugins/manifest:1.4.0',
         settings: {
           ignore_missing: true,
           spec: '.drone/docker-manifest.tmpl',
@@ -124,7 +141,7 @@ local docker(arch, depends_on=[]) =
   pipeline('manifest') {
     steps: [{
       name: 'manifest',
-      image: 'plugins/manifest',
+      image: 'plugins/manifest:1.4.0',
       settings: {
         auto_tag: true,
         ignore_missing: true,
diff --git a/.drone/drone.yml b/.drone/drone.yml
index bb0b0e73..c086a325 100644
--- a/.drone/drone.yml
+++ b/.drone/drone.yml
@@ -4,7 +4,7 @@ name: check
 steps:
 - commands:
   - go mod download
-  image: golang:1.19
+  image: golang:1.20
   name: download
   volumes:
   - name: gopath
@@ -17,7 +17,7 @@ steps:
   - fi
   - cp linux-amd64/helm /usr/local/bin/helm
   - make lint
-  image: golang:1.19
+  image: golang:1.20
   name: lint
   volumes:
   - name: gopath
@@ -30,7 +30,7 @@ steps:
   - fi
   - cp linux-amd64/helm /usr/local/bin/helm
   - make test
-  image: golang:1.19
+  image: golang:1.20
   name: test
   volumes:
   - name: gopath
@@ -43,7 +43,7 @@ steps:
   - fi
   - cp linux-amd64/helm /usr/local/bin/helm
   - make cross
-  image: golang:1.19
+  image: golang:1.20
   name: build
   volumes:
   - name: gopath
@@ -56,6 +56,30 @@ volumes:
 - name: gopath
   temp: {}
 ---
+kind: pipeline
+name: benchmark against main
+node:
+  type: no-parallel
+steps:
+- commands:
+  - go test -bench=. -benchmem -count=6 -run=^$ ./... | tee bench-pr.txt
+  - git fetch origin main
+  - git checkout main
+  - go test -bench=. -benchmem -count=6 -run=^$ ./... | tee bench-main.txt
+  - go install golang.org/x/perf/cmd/...@latest
+  - benchstat bench-main.txt bench-pr.txt
+  image: golang:1.20
+  name: benchmark
+  volumes:
+  - name: gopath
+    path: /go
+trigger:
+  ref:
+  - refs/pull/*/head
+volumes:
+- name: gopath
+  temp: {}
+---
 depends_on:
 - check
 kind: pipeline
@@ -63,7 +87,7 @@ name: release
 steps:
 - commands:
   - git fetch origin --tags
-  image: golang:1.19
+  image: golang:1.20
   name: fetch-tags
   volumes:
   - name: gopath
@@ -76,7 +100,7 @@ steps:
   - fi
   - cp linux-amd64/helm /usr/local/bin/helm
   - make cross
-  image: golang:1.19
+  image: golang:1.20
   name: cross
   volumes:
   - name: gopath
@@ -127,7 +151,7 @@ platform:
 steps:
 - commands:
   - git fetch origin --tags
-  image: golang:1.19
+  image: golang:1.20
   name: fetch-tags
   volumes:
   - name: gopath
@@ -140,7 +164,7 @@ steps:
   - fi
   - cp linux-amd64/helm /usr/local/bin/helm
   - make static
-  image: golang:1.19
+  image: golang:1.20
   name: static
   volumes:
   - name: gopath
@@ -173,7 +197,7 @@ platform:
 steps:
 - commands:
   - git fetch origin --tags
-  image: golang:1.19
+  image: golang:1.20
   name: fetch-tags
   volumes:
   - name: gopath
@@ -186,7 +210,7 @@ steps:
   - fi
   - cp linux-amd64/helm /usr/local/bin/helm
   - make static
-  image: golang:1.19
+  image: golang:1.20
   name: static
   volumes:
   - name: gopath
@@ -218,12 +242,12 @@ steps:
 - commands:
   - git fetch origin --tags
   - echo "main-$(git describe --tags)" > .tags
-  image: golang:1.19
+  image: golang:1.20
   name: fetch-tags
   volumes:
   - name: gopath
     path: /go
-- image: plugins/manifest
+- image: plugins/manifest:1.4.0
   name: manifest
   settings:
     ignore_missing: true
@@ -245,7 +269,7 @@ depends_on:
 kind: pipeline
 name: manifest
 steps:
-- image: plugins/manifest
+- image: plugins/manifest:1.4.0
   name: manifest
   settings:
     auto_tag: true
@@ -282,6 +306,6 @@ kind: secret
 name: dockerhub_password
 ---
 kind: signature
-hmac: adbd6c28d92efbf2968766102c5abe0bbad5a4af1c76e84ada59b2c5cf9c958d
+hmac: 060bcec4c9fbea042e7b70478a9ea0255de0f1e245f3dd031b02d7ca83ef5067
 
 ...
diff --git a/.github/workflows/publish-page.yml b/.github/workflows/publish-page.yml
index a17a9c32..5bf0fea2 100644
--- a/.github/workflows/publish-page.yml
+++ b/.github/workflows/publish-page.yml
@@ -12,6 +12,10 @@ on:
       - synchronize
       - closed
 
+permissions:
+  contents: write
+  pull-requests: write
+
 concurrency: ci-${{ github.ref }}
 
 jobs:
@@ -46,4 +50,5 @@ jobs:
         if: github.event_name == 'pull_request'
         uses: rossjrw/pr-preview-action@v1
         with:
+          deploy-repository: ${{ github.event.pull_request.head.repo.full_name }}
           source-dir: ./docs/public/
diff --git a/Dockerfile b/Dockerfile
index 63138b4a..bfa7c250 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # download kubectl
-FROM golang:1.20.3-alpine as kubectl
+FROM golang:1.20.4-alpine as kubectl
 RUN apk add --no-cache curl
 RUN export VERSION=$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt) &&\
     export OS=$(go env GOOS) && \
@@ -8,7 +8,7 @@ RUN export VERSION=$(curl -s https://storage.googleapis.com/kubernetes-release/r
     chmod +x /usr/local/bin/kubectl
 
 # build jsonnet-bundler
-FROM golang:1.20.3-alpine as jb
+FROM golang:1.20.4-alpine as jb
 WORKDIR /tmp
 RUN apk add --no-cache git make bash &&\
     git clone https://github.com/jsonnet-bundler/jsonnet-bundler &&\
@@ -17,7 +17,7 @@ RUN apk add --no-cache git make bash &&\
     make static &&\
     mv _output/jb /usr/local/bin/jb
 
-FROM golang:1.20.3-alpine as helm
+FROM golang:1.20.4-alpine as helm
 WORKDIR /tmp/helm
 RUN apk add --no-cache jq curl
 RUN export TAG=$(curl --silent "https://api.github.com/repos/helm/helm/releases/latest" | jq -r .tag_name) &&\
@@ -26,7 +26,7 @@ RUN export TAG=$(curl --silent "https://api.github.com/repos/helm/helm/releases/
     curl -SL "https://get.helm.sh/helm-${TAG}-${OS}-${ARCH}.tar.gz" > helm.tgz && \
     tar -xvf helm.tgz --strip-components=1
 
-FROM golang:1.20.3-alpine as kustomize
+FROM golang:1.20.4-alpine as kustomize
 WORKDIR /tmp/kustomize
 RUN apk add --no-cache jq curl
 # Get the latest version of kustomize
@@ -40,7 +40,7 @@ RUN export TAG=$(curl --silent "https://api.github.com/repos/kubernetes-sigs/kus
     tar -xvf kustomize.tgz
 
 # assemble final container
-FROM alpine:3.17
+FROM alpine:3.18
 RUN apk add --no-cache coreutils diffutils less git openssh-client
 COPY tk /usr/local/bin/tk
 COPY --from=kubectl /usr/local/bin/kubectl /usr/local/bin/kubectl
diff --git a/Makefile b/Makefile
index 09888112..4dc47767 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ GOX := $(BIN_DIR)/gox
 GOLINTER := $(GOPATH)/bin/golangci-lint
 
 $(GOLINTER):
-	go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.49.0
+	go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2
 
 lint: $(GOLINTER)
 	$(GOLINTER) run
diff --git a/cmd/tk/env.go b/cmd/tk/env.go
index 8264a153..dba9aba9 100644
--- a/cmd/tk/env.go
+++ b/cmd/tk/env.go
@@ -114,11 +114,7 @@ func envSetCmd() *cli.Command {
 			return err
 		}
 
-		if err := writeJSON(cfg, filepath.Join(path, "spec.json")); err != nil {
-			return err
-		}
-
-		return nil
+		return writeJSON(cfg, filepath.Join(path, "spec.json"))
 	}
 	return cmd
 }
@@ -149,11 +145,7 @@ func envAddCmd() *cli.Command {
 			}
 		}
 
-		if err := addEnv(args[0], cfg, *inline); err != nil {
-			return err
-		}
-
-		return nil
+		return addEnv(args[0], cfg, *inline)
 	}
 	return cmd
 }
diff --git a/cmd/tk/jsonnet.go b/cmd/tk/jsonnet.go
index 5079416d..e4d2d4fb 100644
--- a/cmd/tk/jsonnet.go
+++ b/cmd/tk/jsonnet.go
@@ -38,11 +38,7 @@ func evalCmd() *cli.Command {
 			return err
 		}
 
-		if err := pageln(string(out)); err != nil {
-			return err
-		}
-
-		return nil
+		return pageln(string(out))
 	}
 
 	return cmd
diff --git a/cmd/tk/util.go b/cmd/tk/util.go
index 53cb1451..d56f919b 100644
--- a/cmd/tk/util.go
+++ b/cmd/tk/util.go
@@ -33,9 +33,7 @@ func fPageln(r io.Reader) error {
 		cmd.Stdout = os.Stdout
 		cmd.Stderr = os.Stderr
 
-		if err := cmd.Run(); err != nil {
-			// Fallthrough on failure so that the contents of the reader are copied to stdout.
-		} else {
+		if err := cmd.Run(); err == nil {
 			return nil
 		}
 	}
diff --git a/docs/docs/helm.mdx b/docs/docs/helm.mdx
index 20546564..1881de3a 100644
--- a/docs/docs/helm.mdx
+++ b/docs/docs/helm.mdx
@@ -79,8 +79,6 @@ The following options control how the command is invoked:
     values: {
       persistence: { enabled: true }
     },
-    // Equivalent to: --include-crds; only relevant for Helm v3+.
-    includeCrds: true,
     // Equivalent to: --api-versions v1 --api-versions apps/v1
     apiVersions: ['v1', 'apps/v1']
     // Equivalent to: --kube-version v1.20.0
@@ -90,6 +88,17 @@ The following options control how the command is invoked:
 }
 ```
 
+Tanka will install Custom Resource Definitions (CRDs) automatically, if the
+Helm Chart requires them and ships them in `crds/`. This is equivalent to `helm
+template --include-crds`. This can be disabled using `includeCrds: false`:
+
+```jsonnet
+{
+  grafana: helm.template("grafana", "./charts/grafana", {
+    includeCrds: false
+  })
+}
+```
 
 ## Vendoring Helm Charts
 
diff --git a/go.mod b/go.mod
index df3592f4..fdfbf3a7 100644
--- a/go.mod
+++ b/go.mod
@@ -4,23 +4,23 @@ go 1.20
 
 require (
 	github.com/Masterminds/semver v1.5.0
-	github.com/Masterminds/sprig/v3 v3.2.2
-	github.com/fatih/color v1.13.0
+	github.com/Masterminds/sprig/v3 v3.2.3
+	github.com/fatih/color v1.15.0
 	github.com/fatih/structs v1.1.0
 	github.com/go-clix/cli v0.2.0
 	github.com/gobwas/glob v0.2.3
-	github.com/google/go-cmp v0.5.8
+	github.com/google/go-cmp v0.5.9
 	github.com/google/go-jsonnet v0.20.0
-	github.com/karrick/godirwalk v1.16.1
+	github.com/karrick/godirwalk v1.17.0
 	github.com/pkg/errors v0.9.1
 	github.com/posener/complete v1.2.3
-	github.com/rs/zerolog v1.28.0
+	github.com/rs/zerolog v1.29.1
 	github.com/spf13/pflag v1.0.5
 	github.com/stretchr/objx v0.3.0
 	github.com/stretchr/testify v1.7.1
-	github.com/thoas/go-funk v0.9.2
-	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
-	golang.org/x/text v0.3.7
+	github.com/thoas/go-funk v0.9.3
+	golang.org/x/term v0.8.0
+	golang.org/x/text v0.9.0
 	gopkg.in/yaml.v2 v2.4.0
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
 	k8s.io/apimachinery v0.23.6
@@ -30,22 +30,22 @@ require (
 
 require (
 	github.com/Masterminds/goutils v1.1.1 // indirect
-	github.com/Masterminds/semver/v3 v3.1.1 // indirect
+	github.com/Masterminds/semver/v3 v3.2.0 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/go-logr/logr v1.2.2 // indirect
 	github.com/google/uuid v1.3.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/go-multierror v1.1.1 // indirect
-	github.com/huandu/xstrings v1.3.2 // indirect
+	github.com/huandu/xstrings v1.3.3 // indirect
 	github.com/imdario/mergo v0.3.12 // indirect
-	github.com/mattn/go-colorable v0.1.12 // indirect
-	github.com/mattn/go-isatty v0.0.14 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.17 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
 	github.com/spf13/cast v1.4.1 // indirect
-	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
-	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/crypto v0.3.0 // indirect
+	golang.org/x/sys v0.8.0 // indirect
 	k8s.io/klog/v2 v2.40.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 406627be..9f71ddb5 100644
--- a/go.sum
+++ b/go.sum
@@ -4,17 +4,17 @@ github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJ
 github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
 github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
-github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
-github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
+github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
+github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
+github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
+github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 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=
@@ -25,8 +25,8 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
-github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
+github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
 github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
 github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -66,8 +66,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g=
 github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -87,16 +87,15 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh
 github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
-github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
+github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
-github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
+github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI=
+github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
 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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -107,12 +106,13 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
 github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
+github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@@ -145,8 +145,8 @@ github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXq
 github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
-github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
+github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
+github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
@@ -168,17 +168,18 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/thoas/go-funk v0.9.2 h1:oKlNYv0AY5nyf9g+/GhMgS/UO2ces0QRdPKwkhY3VCk=
-github.com/thoas/go-funk v0.9.2/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
+github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
+github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
-golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
+golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -186,6 +187,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -197,8 +199,11 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -206,6 +211,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -213,8 +219,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -222,21 +226,30 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -247,6 +260,7 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/pkg/helm/charts.go b/pkg/helm/charts.go
index 18453cb1..da8f015a 100644
--- a/pkg/helm/charts.go
+++ b/pkg/helm/charts.go
@@ -140,11 +140,11 @@ func (c Charts) Vendor(prune bool) error {
 			if chartYAML.Version.String() == r.Version.String() {
 				log.Info().Msgf("%s exists", r)
 				continue
-			} else {
-				log.Info().Msgf("Removing %s", r)
-				if err := os.RemoveAll(chartPath); err != nil {
-					return err
-				}
+			}
+
+			log.Info().Msgf("Removing %s", r)
+			if err := os.RemoveAll(chartPath); err != nil {
+				return err
 			}
 		} else if chartDirExists {
 			// If the chart dir exists but the manifest doesn't, we'll clear it out and re-download the chart
diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go
index b68f8a38..edaa7631 100644
--- a/pkg/helm/helm.go
+++ b/pkg/helm/helm.go
@@ -21,6 +21,9 @@ type Helm interface {
 
 	// Template returns the individual resources of a Helm Chart
 	Template(name, chart string, opts TemplateOpts) (manifest.List, error)
+
+	// ChartExists checks if a chart exists in the provided calledFromPath
+	ChartExists(chart string, opts *JsonnetOpts) (string, error)
 }
 
 // PullOpts are additional, non-required options for Helm.Pull
@@ -103,6 +106,17 @@ func (e ExecHelm) RepoUpdate(opts Opts) error {
 	return nil
 }
 
+func (e ExecHelm) ChartExists(chart string, opts *JsonnetOpts) (string, error) {
+	// resolve the Chart relative to the caller
+	callerDir := filepath.Dir(opts.CalledFrom)
+	chart = filepath.Join(callerDir, chart)
+	if _, err := os.Stat(chart); err != nil {
+		return "", fmt.Errorf("helmTemplate: Failed to find a chart at '%s': %s. See https://tanka.dev/helm#failed-to-find-chart", chart, err)
+	}
+
+	return chart, nil
+}
+
 // cmd returns a prepared exec.Cmd to use the `helm` binary
 func (e ExecHelm) cmd(action string, args ...string) *exec.Cmd {
 	argv := []string{action}
diff --git a/pkg/helm/jsonnet.go b/pkg/helm/jsonnet.go
index e0d7111e..f7cfe156 100644
--- a/pkg/helm/jsonnet.go
+++ b/pkg/helm/jsonnet.go
@@ -3,8 +3,6 @@ package helm
 import (
 	"encoding/json"
 	"fmt"
-	"os"
-	"path/filepath"
 
 	"github.com/google/go-jsonnet"
 	"github.com/google/go-jsonnet/ast"
@@ -54,10 +52,8 @@ func NativeFunc(h Helm) *jsonnet.NativeFunction {
 				return "", err
 			}
 
-			// resolve the Chart relative to the caller
-			callerDir := filepath.Dir(opts.CalledFrom)
-			chart := filepath.Join(callerDir, chartpath)
-			if _, err := os.Stat(chart); err != nil {
+			chart, err := h.ChartExists(chartpath, opts)
+			if err != nil {
 				return nil, fmt.Errorf("helmTemplate: Failed to find a chart at '%s': %s. See https://tanka.dev/helm#failed-to-find-chart", chart, err)
 			}
 
@@ -83,7 +79,16 @@ func parseOpts(data interface{}) (*JsonnetOpts, error) {
 	if err != nil {
 		return nil, err
 	}
-	var opts JsonnetOpts
+
+	// default IncludeCRDs to true, as this is the default in the `helm install`
+	// command. Needs to be specified here because the zero value of bool is
+	// false.
+	opts := JsonnetOpts{
+		TemplateOpts: TemplateOpts{
+			IncludeCRDs: true,
+		},
+	}
+
 	if err := json.Unmarshal(c, &opts); err != nil {
 		return nil, err
 	}
diff --git a/pkg/helm/template.go b/pkg/helm/template.go
index 7aa70439..f395da0e 100644
--- a/pkg/helm/template.go
+++ b/pkg/helm/template.go
@@ -10,13 +10,18 @@ import (
 	yaml "gopkg.in/yaml.v3"
 )
 
-// Template expands a Helm Chart into a regular manifest.List using the `helm
-// template` command
-func (e ExecHelm) Template(name, chart string, opts TemplateOpts) (manifest.List, error) {
+func (e ExecHelm) templateCommandArgs(name, chart string, opts TemplateOpts) []string {
 	args := []string{name, chart,
 		"--values", "-", // values from stdin
 	}
 	args = append(args, opts.Flags()...)
+	return args
+}
+
+// Template expands a Helm Chart into a regular manifest.List using the `helm
+// template` command
+func (e ExecHelm) Template(name, chart string, opts TemplateOpts) (manifest.List, error) {
+	args := e.templateCommandArgs(name, chart, opts)
 
 	cmd := e.cmd("template", args...)
 	var buf bytes.Buffer
diff --git a/pkg/jsonnet/find_importers.go b/pkg/jsonnet/find_importers.go
index 50c5d8fc..ac8bba9f 100644
--- a/pkg/jsonnet/find_importers.go
+++ b/pkg/jsonnet/find_importers.go
@@ -220,8 +220,27 @@ func findImporters(root string, searchForFile string, chain map[string]struct{})
 		}
 	}
 
-	importersCache[key] = importers
-	return importers, nil
+	// If we've found a vendored file, check that it's not overridden by a vendored file in the environment root
+	// In that case, we only want to keep the environment vendored file
+	var filteredImporters []string
+	rootVendor := filepath.Join(root, "vendor")
+	if strings.HasPrefix(searchForFile, rootVendor) {
+		for _, importer := range importers {
+			relativePath, err := filepath.Rel(rootVendor, searchForFile)
+			if err != nil {
+				return nil, err
+			}
+			vendoredFileInEnvironment := filepath.Join(filepath.Dir(importer), "vendor", relativePath)
+			if _, ok := jsonnetFilesCache[root][vendoredFileInEnvironment]; !ok {
+				filteredImporters = append(filteredImporters, importer)
+			}
+		}
+	} else {
+		filteredImporters = importers
+	}
+
+	importersCache[key] = filteredImporters
+	return filteredImporters, nil
 }
 
 func createJsonnetFileCache(root string) (map[string]*cachedJsonnetFile, error) {
diff --git a/pkg/jsonnet/importer.go b/pkg/jsonnet/importer.go
index 54e370ec..c8b45113 100644
--- a/pkg/jsonnet/importer.go
+++ b/pkg/jsonnet/importer.go
@@ -68,7 +68,7 @@ func (i *ExtendedImporter) Import(importedFrom, importedPath string) (contents j
 }
 
 // tkLoader provides `tk.libsonnet` from memory (builtin)
-func tkLoader(importedFrom, importedPath string) (contents *jsonnet.Contents, foundAt string, err error) {
+func tkLoader(_, importedPath string) (contents *jsonnet.Contents, foundAt string, err error) {
 	if importedPath != "tk" {
 		return nil, "", nil
 	}
diff --git a/pkg/kubernetes/client/apply.go b/pkg/kubernetes/client/apply.go
index 40825efd..c36d46d2 100644
--- a/pkg/kubernetes/client/apply.go
+++ b/pkg/kubernetes/client/apply.go
@@ -12,7 +12,7 @@ import (
 )
 
 // Test-ability: isolate applyCtl to build and return exec.Cmd from ApplyOpts
-func (k Kubectl) applyCtl(data manifest.List, opts ApplyOpts) *exec.Cmd {
+func (k Kubectl) applyCtl(_ manifest.List, opts ApplyOpts) *exec.Cmd {
 	argv := []string{"-f", "-"}
 	serverSide := (opts.ApplyStrategy == "server")
 	if serverSide {
diff --git a/pkg/process/filter.go b/pkg/process/filter.go
index 8d359592..eaa5f1ee 100644
--- a/pkg/process/filter.go
+++ b/pkg/process/filter.go
@@ -116,7 +116,7 @@ type NegMatcher struct {
 	exp Matcher
 }
 
-func (n NegMatcher) MatchString(s string) bool {
+func (n NegMatcher) MatchString(_ string) bool {
 	return true
 }
 
diff --git a/pkg/tanka/inline.go b/pkg/tanka/inline.go
index e7f6a97b..71f5182a 100644
--- a/pkg/tanka/inline.go
+++ b/pkg/tanka/inline.go
@@ -37,12 +37,12 @@ func (i *InlineLoader) Load(path string, opts LoaderOpts) (*v1alpha1.Environment
 		names := make([]string, 0, len(envs))
 		for _, e := range envs {
 			// If there's a full match on the given name, use this environment
-			if name := e.Metadata().Name(); name == opts.Name {
+			name := e.Metadata().Name()
+			if name == opts.Name {
 				envs = manifest.List{e}
 				break
-			} else {
-				names = append(names, name)
 			}
+			names = append(names, name)
 		}
 		if len(envs) > 1 {
 			sort.Strings(names)
diff --git a/pkg/tanka/prune.go b/pkg/tanka/prune.go
index 5cf18f6a..a377f0a8 100644
--- a/pkg/tanka/prune.go
+++ b/pkg/tanka/prune.go
@@ -66,10 +66,10 @@ func Prune(baseDir string, opts PruneOpts) error {
 	}
 
 	// prompt for confirm
-	if opts.AutoApprove == AutoApproveAlways {
-		// Skip approval
-	} else if err := confirmPrompt("Pruning from", p.Env.Spec.Namespace, kube.Info()); err != nil {
-		return err
+	if opts.AutoApprove != AutoApproveAlways {
+		if err := confirmPrompt("Pruning from", p.Env.Spec.Namespace, kube.Info()); err != nil {
+			return err
+		}
 	}
 
 	// delete resources
diff --git a/pkg/tanka/static.go b/pkg/tanka/static.go
index f076f943..68c677e9 100644
--- a/pkg/tanka/static.go
+++ b/pkg/tanka/static.go
@@ -27,7 +27,7 @@ func (s StaticLoader) Load(path string, opts LoaderOpts) (*v1alpha1.Environment,
 	return config, nil
 }
 
-func (s StaticLoader) Peek(path string, opts LoaderOpts) (*v1alpha1.Environment, error) {
+func (s StaticLoader) Peek(path string, _ LoaderOpts) (*v1alpha1.Environment, error) {
 	config, err := parseStaticSpec(path)
 	if err != nil {
 		return nil, err
diff --git a/pkg/tanka/workflow.go b/pkg/tanka/workflow.go
index adbc8c33..b965e2b8 100644
--- a/pkg/tanka/workflow.go
+++ b/pkg/tanka/workflow.go
@@ -127,10 +127,10 @@ func Apply(baseDir string, opts ApplyOpts) error {
 	}
 
 	// prompt for confirmation
-	if opts.AutoApprove == AutoApproveAlways || (noChanges && opts.AutoApprove == AutoApproveNoChanges) || opts.DryRun != "" {
-		// Skip approval
-	} else if err := confirmPrompt("Applying to", l.Env.Spec.Namespace, kube.Info()); err != nil {
-		return err
+	if opts.AutoApprove != AutoApproveAlways && !(noChanges && opts.AutoApprove == AutoApproveNoChanges) && opts.DryRun == "" {
+		if err := confirmPrompt("Applying to", l.Env.Spec.Namespace, kube.Info()); err != nil {
+			return err
+		}
 	}
 
 	return kube.Apply(l.Resources, kubernetes.ApplyOpts{
@@ -230,10 +230,10 @@ func Delete(baseDir string, opts DeleteOpts) error {
 	}
 
 	// prompt for confirmation
-	if opts.AutoApprove == AutoApproveAlways || opts.DryRun != "" {
-		// Skip approval
-	} else if err := confirmPrompt("Deleting from", l.Env.Spec.Namespace, kube.Info()); err != nil {
-		return err
+	if opts.AutoApprove != AutoApproveAlways && opts.DryRun == "" {
+		if err := confirmPrompt("Deleting from", l.Env.Spec.Namespace, kube.Info()); err != nil {
+			return err
+		}
 	}
 
 	return kube.Delete(l.Resources, kubernetes.DeleteOpts{


Test patch:
diff --git a/pkg/helm/jsonnet_test.go b/pkg/helm/jsonnet_test.go
new file mode 100644
index 00000000..7607e836
--- /dev/null
+++ b/pkg/helm/jsonnet_test.go
@@ -0,0 +1,142 @@
+package helm
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
+
+	"github.com/grafana/tanka/pkg/kubernetes/manifest"
+)
+
+const calledFrom = "/my/path/here"
+
+type MockHelm struct {
+	mock.Mock
+}
+
+// fulfill the Helm interface
+func (m *MockHelm) Pull(chart, version string, opts PullOpts) error {
+	args := m.Called(chart, version, opts)
+	return args.Error(0)
+}
+
+func (m *MockHelm) RepoUpdate(opts Opts) error {
+	args := m.Called(opts)
+	return args.Error(0)
+}
+
+func (m *MockHelm) Template(name, chart string, opts TemplateOpts) (manifest.List, error) {
+	args := m.Called(name, chart, opts)
+
+	// figure out what arguments `helm template` would be called with and save
+	// them
+	execHelm := &ExecHelm{}
+	cmdArgs := execHelm.templateCommandArgs(name, chart, opts)
+	m.TestData().Set("templateCommandArgs", cmdArgs)
+
+	return args.Get(0).(manifest.List), args.Error(1)
+}
+
+func (m *MockHelm) ChartExists(chart string, opts *JsonnetOpts) (string, error) {
+	args := m.Called(chart, opts)
+	return args.String(0), args.Error(1)
+}
+
+func callNativeFunction(t *testing.T, expectedHelmTemplateOptions TemplateOpts, inputOptionsFromJsonnet map[string]interface{}) []string {
+	t.Helper()
+
+	helmMock := &MockHelm{}
+
+	helmMock.On(
+		"ChartExists",
+		"exampleChartPath",
+		mock.AnythingOfType("*helm.JsonnetOpts")).
+		Return("/full/chart/path", nil).
+		Once()
+
+	// this verifies that the helmMock.Template() method is called with the
+	// correct arguments, i.e. includeCrds: true is set by default
+	helmMock.On("Template", "exampleChartName", "/full/chart/path", expectedHelmTemplateOptions).
+		Return(manifest.List{}, nil).
+		Once()
+
+	nf := NativeFunc(helmMock)
+	require.NotNil(t, nf)
+
+	// the mandatory parameters to helm.template() in Jsonnet
+	params := []string{
+		"exampleChartName",
+		"exampleChartPath",
+	}
+
+	// mandatory parameters + the k-v pairs from the Jsonnet input
+	paramsInterface := make([]interface{}, 3)
+	paramsInterface[0] = params[0]
+	paramsInterface[1] = params[1]
+	paramsInterface[2] = inputOptionsFromJsonnet
+
+	_, err := nf.Func(paramsInterface)
+
+	require.NoError(t, err)
+
+	helmMock.AssertExpectations(t)
+
+	return helmMock.TestData().Get("templateCommandArgs").StringSlice()
+}
+
+// TestDefaultCommandineFlagsIncludeCrds tests that the includeCrds flag is set
+// to true by default
+func TestDefaultCommandLineFlagsIncludeCrds(t *testing.T) {
+	kubeVersion := "1.18.0"
+
+	// we will check that the template function is called with these options,
+	// i.e. that includeCrds got set to true. This is not us passing an input,
+	// we are asserting here that the template function is called with these
+	// options.
+	expectedHelmTemplateOptions := TemplateOpts{
+		KubeVersion: kubeVersion,
+		IncludeCRDs: true,
+	}
+
+	// the options to helm.template(), which are a Jsonnet object, turned into a
+	// go map[string]interface{}. we do not set includeCrds here, so it should
+	// be true by default
+	inputOptionsFromJsonnet := make(map[string]interface{})
+	inputOptionsFromJsonnet["calledFrom"] = calledFrom
+	inputOptionsFromJsonnet["kubeVersion"] = kubeVersion
+
+	args := callNativeFunction(t, expectedHelmTemplateOptions, inputOptionsFromJsonnet)
+
+	// finally check that the actual command line arguments we will pass to
+	// `helm template` contain the --include-crds flag
+	require.Contains(t, args, "--include-crds")
+}
+
+// TestIncludeCrdsFalse tests that the includeCrds flag is can be set to false,
+// and this makes it to the helm.Template() method call
+func TestIncludeCrdsFalse(t *testing.T) {
+	kubeVersion := "1.18.0"
+
+	// we will check that the template function is called with these options,
+	// i.e. that includeCrds got set to false. This is not us passing an input,
+	// we are asserting here that the template function is called with these
+	// options.
+	expectedHelmTemplateOptions := TemplateOpts{
+		KubeVersion: kubeVersion,
+		IncludeCRDs: false,
+	}
+
+	// the options to helm.template(), which are a Jsonnet object, turned into a
+	// go map[string]interface{}. we explicitly set includeCrds to false here
+	inputOptionsFromJsonnet := make(map[string]interface{})
+	inputOptionsFromJsonnet["calledFrom"] = calledFrom
+	inputOptionsFromJsonnet["kubeVersion"] = kubeVersion
+	inputOptionsFromJsonnet["includeCrds"] = false
+
+	args := callNativeFunction(t, expectedHelmTemplateOptions, inputOptionsFromJsonnet)
+
+	// finally check that the actual command line arguments we will pass to
+	// `helm template` don't contain the --include-crds flag
+	require.NotContains(t, args, "--include-crds")
+}
diff --git a/pkg/jsonnet/find_importers_test.go b/pkg/jsonnet/find_importers_test.go
index d295f821..c3d03722 100644
--- a/pkg/jsonnet/find_importers_test.go
+++ b/pkg/jsonnet/find_importers_test.go
@@ -5,30 +5,36 @@ import (
 	"path/filepath"
 	"testing"
 
+	"github.com/grafana/tanka/pkg/jsonnet/jpath"
 	"github.com/stretchr/testify/require"
 )
 
-func TestFindImportersForFiles(t *testing.T) {
-	// Make sure the main files all eval correctly
-	// We want to make sure that the importers command works correctly,
-	// but there's no point in testing on invalid jsonnet files
-	files, err := FindFiles("testdata", nil)
-	require.NoError(t, err)
-	require.NotEmpty(t, files)
-	for _, file := range files {
-		_, err := EvaluateFile(file, Opts{})
-		require.NoError(t, err, "failed to eval %s", file)
+type findImportersTestCase struct {
+	name              string
+	files             []string
+	expectedImporters []string
+	expectedErr       error
+}
+
+func (tc findImportersTestCase) run(t testing.TB) {
+	importers, err := FindImporterForFiles("testdata/findImporters", tc.files)
+
+	if tc.expectedErr != nil {
+		require.EqualError(t, err, tc.expectedErr.Error())
+	} else {
+		require.NoError(t, err)
+		require.Equal(t, tc.expectedImporters, importers)
 	}
+}
 
-	cases := []struct {
-		name              string
-		files             []string
-		expectedImporters []string
-		expectedErr       error
-	}{
+func findImportersTestCases(t testing.TB) []findImportersTestCase {
+	t.Helper()
+
+	return []findImportersTestCase{
 		{
-			name:  "no files",
-			files: []string{},
+			name:              "no files",
+			files:             []string{},
+			expectedImporters: nil,
 		},
 		{
 			name:        "invalid file",
@@ -114,18 +120,44 @@ func TestFindImportersForFiles(t *testing.T) {
 				absPath(t, "testdata/findImporters/environments/relative-import/main.jsonnet"),
 			},
 		},
+		{
+			name: "vendor override in env: override vendor used",
+			files: []string{
+				"testdata/findImporters/environments/vendor-override-in-env/vendor/vendor-override-in-env/main.libsonnet",
+			},
+			expectedImporters: []string{
+				absPath(t, "testdata/findImporters/environments/vendor-override-in-env/main.jsonnet"),
+			},
+		},
+		{
+			name: "vendor override in env: global vendor unused",
+			files: []string{
+				"testdata/findImporters/vendor/vendor-override-in-env/main.libsonnet",
+			},
+			expectedImporters: nil,
+		},
 	}
+}
 
-	for _, c := range cases {
-		t.Run(c.name, func(t *testing.T) {
-			importers, err := FindImporterForFiles("testdata/findImporters", c.files)
+func TestFindImportersForFiles(t *testing.T) {
+	// Make sure the main files all eval correctly
+	// We want to make sure that the importers command works correctly,
+	// but there's no point in testing on invalid jsonnet files
+	files, err := FindFiles("testdata", nil)
+	require.NoError(t, err)
+	require.NotEmpty(t, files)
+	for _, file := range files {
+		// Skip non-main files
+		if filepath.Base(file) != jpath.DefaultEntrypoint {
+			continue
+		}
+		_, err := EvaluateFile(file, Opts{})
+		require.NoError(t, err, "failed to eval %s", file)
+	}
 
-			if c.expectedErr != nil {
-				require.EqualError(t, err, c.expectedErr.Error())
-			} else {
-				require.NoError(t, err)
-				require.Equal(t, c.expectedImporters, importers)
-			}
+	for _, c := range findImportersTestCases(t) {
+		t.Run(c.name, func(t *testing.T) {
+			c.run(t)
 		})
 	}
 }
@@ -149,7 +181,17 @@ func BenchmarkFindImporters(b *testing.B) {
 	}
 }
 
-func absPath(t *testing.T, path string) string {
+func BenchmarkFindImporters_StaticCases(b *testing.B) {
+	for _, c := range findImportersTestCases(b) {
+		b.Run(c.name, func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				c.run(b)
+			}
+		})
+	}
+}
+
+func absPath(t testing.TB, path string) string {
 	t.Helper()
 
 	abs, err := filepath.Abs(path)
diff --git a/pkg/jsonnet/testdata/findImporters/environments/vendor-override-in-env/main.jsonnet b/pkg/jsonnet/testdata/findImporters/environments/vendor-override-in-env/main.jsonnet
new file mode 100644
index 00000000..b1ae741f
--- /dev/null
+++ b/pkg/jsonnet/testdata/findImporters/environments/vendor-override-in-env/main.jsonnet
@@ -0,0 +1,4 @@
+{
+  assert self.imported.test == 'env-vendor',
+  imported: (import 'vendor-override-in-env/main.libsonnet'),
+}
diff --git a/pkg/jsonnet/testdata/findImporters/environments/vendor-override-in-env/vendor/vendor-override-in-env/main.libsonnet b/pkg/jsonnet/testdata/findImporters/environments/vendor-override-in-env/vendor/vendor-override-in-env/main.libsonnet
new file mode 100644
index 00000000..3bb0e285
--- /dev/null
+++ b/pkg/jsonnet/testdata/findImporters/environments/vendor-override-in-env/vendor/vendor-override-in-env/main.libsonnet
@@ -0,0 +1,3 @@
+{
+  test: 'env-vendor',
+}
diff --git a/pkg/jsonnet/testdata/findImporters/vendor/vendor-override-in-env/main.libsonnet b/pkg/jsonnet/testdata/findImporters/vendor/vendor-override-in-env/main.libsonnet
new file mode 100644
index 00000000..54fd76a4
--- /dev/null
+++ b/pkg/jsonnet/testdata/findImporters/vendor/vendor-override-in-env/main.libsonnet
@@ -0,0 +1,3 @@
+{
+  test: 'global-vendor',
+}