Repository: linkerd/linkerd2
Pull request #14236 (2025-07-13 17:20:59)

build(deps): bump query-string from 9.2.1 to 9.2.2 in /web/app
Bumps [query-string](https://github.com/sindresorhus/query-string) from 9.2.1 to 9.2.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/sindresorhus/query-string/releases">query-string's releases</a>.</em></p>
<blockquote>
<h2>v9.2.2</h2>
<ul>
<li>Accept valueless parameters as <code>true</code> when using a boolean with <code>types</code> option (<a href="https://redirect.github.com/sindresorhus/query-string/issues/410">#410</a>)  557a550</li>
</ul>
<hr />
<p><a href="https://github.com/sindresorhus/query-string/compare/v9.2.1...v9.2.2">https://github.com/sindresorhus/query-string/compare/v9.2.1...v9.2.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/sindresorhus/query-string/commit/dc13d74d1350f8a6504b50193b8d3b60078dffaf"><code>dc13d74</code></a> 9.2.2</li>
<li><a href="https://github.com/sindresorhus/query-string/commit/557a550191b416b4a0ef945435f93d67bd406e62"><code>557a550</code></a> Accept valueless parameters as <code>true</code> when using a boolean with <code>types</code> optio...</li>
<li>See full diff in <a href="https://github.com/sindresorhus/query-string/compare/v9.2.1...v9.2.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=query-string&package-manager=npm_and_yarn&previous-version=9.2.1&new-version=9.2.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.gitignore b/.gitignore
index 55ceac7c1..31b2b6d21 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,6 @@ web/app/js/locales/_build
 web/app/js/locales/**/*.js
 web/app/yarn-error.log
 vendor
-**/*.gogen*
 **/*.swp
 **/charts/**/charts
 package-lock.json
diff --git a/bin/build-cli-bin b/bin/build-cli-bin
index 892dc6648..44d9500ad 100755
--- a/bin/build-cli-bin
+++ b/bin/build-cli-bin
@@ -17,12 +17,7 @@ rootdir=$( cd "$bindir"/.. && pwd )
     cd "$rootdir"
     cd "$(pwd -P)"
     target=target/cli/$(os)/linkerd
-     # TODO: `go generate` does not honor -mod=readonly
-    GO111MODULE=on go generate -mod=readonly ./pkg/charts/static
-    GO111MODULE=on go generate -mod=readonly ./jaeger/static
-    GO111MODULE=on go generate -mod=readonly ./multicluster/static
-    GO111MODULE=on go generate -mod=readonly ./viz/static
-    
+
     root_tag=$("$bindir"/root-tag)
     GO111MODULE=on CGO_ENABLED=0 go build -o "$target" -tags prod -mod=readonly -ldflags "-s -w -X github.com/linkerd/linkerd2/pkg/version.Version=$root_tag" ./cli
     echo "$target"
diff --git a/charts/templates.go b/charts/templates.go
new file mode 100644
index 000000000..3e5761bbc
--- /dev/null
+++ b/charts/templates.go
@@ -0,0 +1,8 @@
+package charts
+
+import (
+	"embed"
+)
+
+//go:embed linkerd-control-plane linkerd-crds linkerd2-cni all:partials patch
+var Templates embed.FS
diff --git a/cli/Dockerfile b/cli/Dockerfile
index c81073702..e2077eed2 100644
--- a/cli/Dockerfile
+++ b/cli/Dockerfile
@@ -22,13 +22,6 @@ COPY controller/api controller/api
 COPY controller/gen controller/gen
 COPY pkg pkg
 
-# Generate static templates
-# TODO: `go generate` does not honor -mod=readonly
-RUN go generate -mod=readonly ./pkg/charts/static
-RUN go generate -mod=readonly ./jaeger/static
-RUN go generate -mod=readonly ./multicluster/static
-RUN go generate -mod=readonly ./viz/static
-
 RUN mkdir -p /out
 
 FROM go-gen AS linux-amd64-full
diff --git a/cli/cmd/install-cni-plugin.go b/cli/cmd/install-cni-plugin.go
index 1b1906e60..eb8e19698 100644
--- a/cli/cmd/install-cni-plugin.go
+++ b/cli/cmd/install-cni-plugin.go
@@ -6,9 +6,9 @@ import (
 	"os"
 	"strings"
 
-	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	cnicharts "github.com/linkerd/linkerd2/pkg/charts/cni"
-	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/cmd"
 	"github.com/linkerd/linkerd2/pkg/flags"
 	"github.com/linkerd/linkerd2/pkg/version"
@@ -249,13 +249,13 @@ func renderCNIPlugin(w io.Writer, valOpts values.Options, config *cniPluginOptio
 		{Name: "templates/cni-plugin.yaml"},
 	}
 
-	ch := &charts.Chart{
+	ch := &chartspkg.Chart{
 		Name:      helmCNIDefaultChartName,
 		Dir:       helmCNIDefaultChartDir,
 		Namespace: defaultCNINamespace,
 		Values:    mergedValues,
 		Files:     files,
-		Fs:        static.Templates,
+		Fs:        charts.Templates,
 	}
 
 	buf, err := ch.RenderCNI()
diff --git a/cli/cmd/install.go b/cli/cmd/install.go
index 7060b954d..1889368d5 100644
--- a/cli/cmd/install.go
+++ b/cli/cmd/install.go
@@ -13,10 +13,10 @@ import (
 	"text/template"
 	"time"
 
+	"github.com/linkerd/linkerd2/charts"
 	"github.com/linkerd/linkerd2/cli/flag"
-	"github.com/linkerd/linkerd2/pkg/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
-	"github.com/linkerd/linkerd2/pkg/charts/static"
 	pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
 	flagspkg "github.com/linkerd/linkerd2/pkg/flags"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
@@ -331,14 +331,11 @@ func isRunAsRoot(values map[string]interface{}) bool {
 // them into a buffer. The coalesced values are also returned so that they may be rendered via
 // `renderOverrides` if appropriate.
 func renderChartToBuffer(files []*loader.BufferedFile, values map[string]interface{}, valuesOverrides map[string]interface{}) (*bytes.Buffer, chartutil.Values, error) {
-	// Load the partials in addition to the main chart.
-	var partials []*loader.BufferedFile
-	for _, template := range charts.L5dPartials {
-		partials = append(partials, &loader.BufferedFile{Name: template})
-	}
-	if err := charts.FilesReader(static.Templates, "", partials); err != nil {
+	partials, err := chartspkg.LoadPartials()
+	if err != nil {
 		return nil, nil, err
 	}
+
 	chart, err := loader.LoadFiles(append(files, partials...))
 	if err != nil {
 		return nil, nil, err
@@ -435,13 +432,13 @@ func renderCRDs(ctx context.Context, k *k8s.KubernetesAPI, w io.Writer, options
 	for _, template := range TemplatesCrdFiles {
 		files = append(files, &loader.BufferedFile{Name: template})
 	}
-	if err := charts.FilesReader(static.Templates, l5dcharts.HelmChartDirCrds+"/", files); err != nil {
+	if err := chartspkg.FilesReader(charts.Templates, l5dcharts.HelmChartDirCrds+"/", files); err != nil {
 		return err
 	}
 
 	// Load defaults from values.yaml
 	valuesFile := &loader.BufferedFile{Name: l5dcharts.HelmChartDirCrds + "/values.yaml"}
-	if err := charts.ReadFile(static.Templates, "/", valuesFile); err != nil {
+	if err := chartspkg.ReadFile(charts.Templates, "", valuesFile); err != nil {
 		return err
 	}
 	// Ensure the map is not nil, even if the default `values.yaml` is empty ---
@@ -470,7 +467,7 @@ func renderCRDs(ctx context.Context, k *k8s.KubernetesAPI, w io.Writer, options
 		}
 
 		defaultValues = updateDefaultValues(installed, defaultValues)
-		finalValues := charts.MergeMaps(defaultValues, valuesOverrides)
+		finalValues := chartspkg.MergeMaps(defaultValues, valuesOverrides)
 
 		if err := validateFinalValues(installed, finalValues); err != nil {
 			return err
@@ -492,7 +489,7 @@ func renderControlPlane(w io.Writer, values *l5dcharts.Values, valuesOverrides m
 	for _, template := range TemplatesControlPlane {
 		files = append(files, &loader.BufferedFile{Name: template})
 	}
-	if err := charts.FilesReader(static.Templates, l5dcharts.HelmChartDirCP+"/", files); err != nil {
+	if err := chartspkg.FilesReader(charts.Templates, l5dcharts.HelmChartDirCP+"/", files); err != nil {
 		return err
 	}
 
diff --git a/controller/Dockerfile b/controller/Dockerfile
index 283bd8969..986277143 100644
--- a/controller/Dockerfile
+++ b/controller/Dockerfile
@@ -14,14 +14,12 @@ FROM go-deps AS golang
 WORKDIR /linkerd-build
 COPY controller/gen controller/gen
 COPY pkg pkg
+COPY charts charts
 COPY controller controller
 COPY charts/patch charts/patch
 COPY charts/partials charts/partials
 COPY multicluster multicluster
 
-# Generate static templates
-# TODO: `go generate` does not honor -mod=readonly
-RUN go generate -mod=readonly ./pkg/charts/static
 ARG TARGETARCH
 RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -o /out/controller -tags prod -mod=readonly -ldflags "-s -w" ./controller/cmd
 
diff --git a/jaeger/charts/templates.go b/jaeger/charts/templates.go
new file mode 100644
index 000000000..7a24a2f31
--- /dev/null
+++ b/jaeger/charts/templates.go
@@ -0,0 +1,8 @@
+package charts
+
+import (
+	"embed"
+)
+
+//go:embed linkerd-jaeger
+var Templates embed.FS
diff --git a/jaeger/cmd/install.go b/jaeger/cmd/install.go
index 58f05dc75..d310f5a1e 100644
--- a/jaeger/cmd/install.go
+++ b/jaeger/cmd/install.go
@@ -8,9 +8,8 @@ import (
 	"path"
 	"time"
 
-	"github.com/linkerd/linkerd2/jaeger/static"
-	"github.com/linkerd/linkerd2/pkg/charts"
-	partials "github.com/linkerd/linkerd2/pkg/charts/static"
+	"github.com/linkerd/linkerd2/jaeger/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
 	"github.com/linkerd/linkerd2/pkg/flags"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
@@ -126,20 +125,13 @@ func render(w io.Writer, valuesOverrides map[string]interface{}, registry string
 		)
 	}
 
-	var partialFiles []*loader.BufferedFile
-	for _, template := range charts.L5dPartials {
-		partialFiles = append(partialFiles,
-			&loader.BufferedFile{Name: template},
-		)
-	}
-
 	// Load all jaeger chart files into buffer
-	if err := charts.FilesReader(static.Templates, "linkerd-jaeger/", files); err != nil {
+	if err := chartspkg.FilesReader(charts.Templates, "linkerd-jaeger/", files); err != nil {
 		return err
 	}
 
-	// Load all partial chart files into buffer
-	if err := charts.FilesReader(partials.Templates, "", partialFiles); err != nil {
+	partialFiles, err := chartspkg.LoadPartials()
+	if err != nil {
 		return err
 	}
 
@@ -154,7 +146,7 @@ func render(w io.Writer, valuesOverrides map[string]interface{}, registry string
 		return err
 	}
 
-	vals, err = charts.InsertVersionValues(vals)
+	vals, err = chartspkg.InsertVersionValues(vals)
 	if err != nil {
 		return err
 	}
diff --git a/jaeger/static/generate.go b/jaeger/static/generate.go
deleted file mode 100644
index cb0637209..000000000
--- a/jaeger/static/generate.go
+++ /dev/null
@@ -1,21 +0,0 @@
-//go:build ignore
-
-package main
-
-import (
-	"github.com/linkerd/linkerd2/jaeger/static"
-	"github.com/shurcooL/vfsgen"
-	log "github.com/sirupsen/logrus"
-)
-
-func main() {
-	err := vfsgen.Generate(static.Templates, vfsgen.Options{
-		Filename:     "generated_jaeger_templates.gogen.go",
-		PackageName:  "static",
-		BuildTags:    "prod",
-		VariableName: "Templates",
-	})
-	if err != nil {
-		log.Fatalln(err)
-	}
-}
diff --git a/jaeger/static/templates.go b/jaeger/static/templates.go
deleted file mode 100644
index a132ddda4..000000000
--- a/jaeger/static/templates.go
+++ /dev/null
@@ -1,15 +0,0 @@
-//go:generate go run generate.go
-//go:build !prod
-
-package static
-
-import (
-	"net/http"
-	"path"
-
-	"github.com/linkerd/linkerd2/pkg/charts/static"
-)
-
-// Templates that will be rendered by `linkerd install`. This is only used on
-// dev builds.
-var Templates http.FileSystem = http.Dir(path.Join(static.GetRepoRoot(), "jaeger/charts"))
diff --git a/multicluster/charts/templates.go b/multicluster/charts/templates.go
new file mode 100644
index 000000000..1c6ee6d1f
--- /dev/null
+++ b/multicluster/charts/templates.go
@@ -0,0 +1,8 @@
+package charts
+
+import (
+	"embed"
+)
+
+//go:embed linkerd-multicluster linkerd-multicluster-link
+var Templates embed.FS
diff --git a/multicluster/cmd/allow.go b/multicluster/cmd/allow.go
index 5d955c879..760593d42 100644
--- a/multicluster/cmd/allow.go
+++ b/multicluster/cmd/allow.go
@@ -5,9 +5,9 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/linkerd/linkerd2/multicluster/static"
+	"github.com/linkerd/linkerd2/multicluster/charts"
 	mccharts "github.com/linkerd/linkerd2/multicluster/values"
-	"github.com/linkerd/linkerd2/pkg/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/version"
@@ -59,13 +59,13 @@ func newAllowCommand() *cobra.Command {
 				{Name: "templates/remote-access-service-mirror-rbac.yaml"},
 			}
 
-			chart := &charts.Chart{
-				Name:      helmMulticlusterDefaultChartName,
-				Dir:       helmMulticlusterDefaultChartName,
+			chart := &chartspkg.Chart{
+				Name:      mccharts.HelmDefaultChartDir,
+				Dir:       mccharts.HelmDefaultChartDir,
 				Namespace: opts.namespace,
 				RawValues: rawValues,
 				Files:     files,
-				Fs:        static.Templates,
+				Fs:        charts.Templates,
 			}
 			buf, err := chart.Render()
 			if err != nil {
diff --git a/multicluster/cmd/install.go b/multicluster/cmd/install.go
index 8365b4ccc..a90b0dc96 100644
--- a/multicluster/cmd/install.go
+++ b/multicluster/cmd/install.go
@@ -11,10 +11,9 @@ import (
 	"strings"
 	"time"
 
-	"github.com/linkerd/linkerd2/multicluster/static"
+	"github.com/linkerd/linkerd2/multicluster/charts"
 	multicluster "github.com/linkerd/linkerd2/multicluster/values"
-	"github.com/linkerd/linkerd2/pkg/charts"
-	partials "github.com/linkerd/linkerd2/pkg/charts/static"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
 	"github.com/linkerd/linkerd2/pkg/flags"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
@@ -137,7 +136,7 @@ func install(ctx context.Context, w io.Writer, options *multiclusterInstallOptio
 	}
 
 	if ha {
-		valuesOverrides, err = charts.OverrideFromFile(valuesOverrides, static.Templates, helmMulticlusterDefaultChartName, "values-ha.yaml")
+		valuesOverrides, err = chartspkg.OverrideFromFile(valuesOverrides, charts.Templates, multicluster.HelmDefaultChartDir, "values-ha.yaml")
 		if err != nil {
 			return err
 		}
@@ -156,20 +155,13 @@ func render(w io.Writer, values *multicluster.Values, valuesOverrides map[string
 		files = append(files, &loader.BufferedFile{Name: template})
 	}
 
-	var partialFiles []*loader.BufferedFile
-	for _, template := range charts.L5dPartials {
-		partialFiles = append(partialFiles,
-			&loader.BufferedFile{Name: template},
-		)
-	}
-
 	// Load all multicluster install chart files into buffer
-	if err := charts.FilesReader(static.Templates, helmMulticlusterDefaultChartName+"/", files); err != nil {
+	if err := chartspkg.FilesReader(charts.Templates, multicluster.HelmDefaultChartDir+"/", files); err != nil {
 		return err
 	}
 
-	// Load all partial chart files into buffer
-	if err := charts.FilesReader(partials.Templates, "", partialFiles); err != nil {
+	partialFiles, err := chartspkg.LoadPartials()
+	if err != nil {
 		return err
 	}
 
diff --git a/multicluster/cmd/link.go b/multicluster/cmd/link.go
index 3511b39db..d21476504 100644
--- a/multicluster/cmd/link.go
+++ b/multicluster/cmd/link.go
@@ -10,10 +10,9 @@ import (
 	"strings"
 
 	"github.com/linkerd/linkerd2/controller/gen/apis/link/v1alpha3"
-	"github.com/linkerd/linkerd2/multicluster/static"
+	"github.com/linkerd/linkerd2/multicluster/charts"
 	multicluster "github.com/linkerd/linkerd2/multicluster/values"
-	"github.com/linkerd/linkerd2/pkg/charts"
-	partials "github.com/linkerd/linkerd2/pkg/charts/static"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
 	"github.com/linkerd/linkerd2/pkg/flags"
 	"github.com/linkerd/linkerd2/pkg/k8s"
@@ -346,10 +345,10 @@ A full list of configurable values can be found at https://github.com/linkerd/li
 			}
 
 			if opts.ha {
-				if valuesOverrides, err = charts.OverrideFromFile(
+				if valuesOverrides, err = chartspkg.OverrideFromFile(
 					valuesOverrides,
-					static.Templates,
-					helmMulticlusterLinkDefaultChartName,
+					charts.Templates,
+					multicluster.HelmDefaultLinkChartDir,
 					"values-ha.yaml",
 				); err != nil {
 					return err
@@ -415,20 +414,13 @@ func renderServiceMirror(values *multicluster.Values, valuesOverrides map[string
 		{Name: "templates/gateway-mirror.yaml"},
 	}
 
-	var partialFiles []*chartloader.BufferedFile
-	for _, template := range charts.L5dPartials {
-		partialFiles = append(partialFiles,
-			&chartloader.BufferedFile{Name: template},
-		)
-	}
-
 	// Load all multicluster link chart files into buffer
-	if err := charts.FilesReader(static.Templates, helmMulticlusterLinkDefaultChartName+"/", files); err != nil {
+	if err := chartspkg.FilesReader(charts.Templates, multicluster.HelmDefaultLinkChartDir+"/", files); err != nil {
 		return nil, err
 	}
 
-	// Load all partial chart files into buffer
-	if err := charts.FilesReader(partials.Templates, "", partialFiles); err != nil {
+	partialFiles, err := chartspkg.LoadPartials()
+	if err != nil {
 		return nil, err
 	}
 
diff --git a/multicluster/cmd/root.go b/multicluster/cmd/root.go
index a420c2842..a2d2e6930 100644
--- a/multicluster/cmd/root.go
+++ b/multicluster/cmd/root.go
@@ -17,7 +17,6 @@ const (
 	defaultLinkerdNamespace              = "linkerd"
 	defaultMulticlusterNamespace         = "linkerd-multicluster"
 	defaultGatewayName                   = "linkerd-gateway"
-	helmMulticlusterDefaultChartName     = "linkerd-multicluster"
 	helmMulticlusterLinkDefaultChartName = "linkerd-multicluster-link"
 	tokenKey                             = "token"
 
@@ -30,6 +29,8 @@ const (
 )
 
 var (
+	HelmMulticlusterDefaultChartName = "linkerd-multicluster"
+
 	apiAddr               string // An empty value means "use the Kubernetes configuration"
 	controlPlaneNamespace string
 	kubeconfigPath        string
diff --git a/multicluster/static/generate.go b/multicluster/static/generate.go
deleted file mode 100644
index cd8049a28..000000000
--- a/multicluster/static/generate.go
+++ /dev/null
@@ -1,21 +0,0 @@
-//go:build ignore
-
-package main
-
-import (
-	"github.com/linkerd/linkerd2/multicluster/static"
-	"github.com/shurcooL/vfsgen"
-	log "github.com/sirupsen/logrus"
-)
-
-func main() {
-	err := vfsgen.Generate(static.Templates, vfsgen.Options{
-		Filename:     "generated_multicluster_templates.gogen.go",
-		PackageName:  "static",
-		BuildTags:    "prod",
-		VariableName: "Templates",
-	})
-	if err != nil {
-		log.Fatalln(err)
-	}
-}
diff --git a/multicluster/static/templates.go b/multicluster/static/templates.go
deleted file mode 100644
index c85a765da..000000000
--- a/multicluster/static/templates.go
+++ /dev/null
@@ -1,15 +0,0 @@
-//go:generate go run generate.go
-//go:build !prod
-
-package static
-
-import (
-	"net/http"
-	"path"
-
-	"github.com/linkerd/linkerd2/pkg/charts/static"
-)
-
-// Templates that will be rendered by `linkerd install`. This is only used on
-// dev builds.
-var Templates http.FileSystem = http.Dir(path.Join(static.GetRepoRoot(), "multicluster/charts"))
diff --git a/multicluster/values/values.go b/multicluster/values/values.go
index 65cd8eabd..84aa6af79 100644
--- a/multicluster/values/values.go
+++ b/multicluster/values/values.go
@@ -3,8 +3,8 @@ package values
 import (
 	"fmt"
 
-	"github.com/linkerd/linkerd2/multicluster/static"
-	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/multicluster/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"helm.sh/helm/v3/pkg/chart/loader"
@@ -13,9 +13,9 @@ import (
 	"sigs.k8s.io/yaml"
 )
 
-const (
-	helmDefaultChartDir     = "linkerd-multicluster"
-	helmDefaultLinkChartDir = "linkerd-multicluster-link"
+var (
+	HelmDefaultChartDir     = "linkerd-multicluster"
+	HelmDefaultLinkChartDir = "linkerd-multicluster-link"
 )
 
 // Values contains the top-level elements in the Helm charts
@@ -113,7 +113,7 @@ type ControllerDefaultsProbe struct {
 
 // NewInstallValues returns a new instance of the Values type.
 func NewInstallValues() (*Values, error) {
-	chartDir := fmt.Sprintf("%s/", helmDefaultChartDir)
+	chartDir := fmt.Sprintf("%s/", HelmDefaultChartDir)
 	v, err := readDefaults(chartDir)
 	if err != nil {
 		return nil, err
@@ -125,7 +125,7 @@ func NewInstallValues() (*Values, error) {
 
 // NewLinkValues returns a new instance of the Values type.
 func NewLinkValues() (*Values, error) {
-	chartDir := fmt.Sprintf("%s/", helmDefaultLinkChartDir)
+	chartDir := fmt.Sprintf("%s/", HelmDefaultLinkChartDir)
 	v, err := readDefaults(chartDir)
 	if err != nil {
 		return nil, err
@@ -141,11 +141,11 @@ func readDefaults(chartDir string) (*Values, error) {
 	file := &loader.BufferedFile{
 		Name: chartutil.ValuesfileName,
 	}
-	if err := charts.ReadFile(static.Templates, chartDir, file); err != nil {
+	if err := chartspkg.ReadFile(charts.Templates, chartDir, file); err != nil {
 		return nil, err
 	}
 	values := Values{}
-	if err := yaml.Unmarshal(charts.InsertVersion(file.Data), &values); err != nil {
+	if err := yaml.Unmarshal(chartspkg.InsertVersion(file.Data), &values); err != nil {
 		return nil, err
 	}
 	return &values, nil
diff --git a/pkg/charts/charts.go b/pkg/charts/charts.go
index 7b7f16e39..9ce576c63 100644
--- a/pkg/charts/charts.go
+++ b/pkg/charts/charts.go
@@ -2,12 +2,12 @@ package charts
 
 import (
 	"bytes"
+	"embed"
 	"errors"
-	"net/http"
 	"path"
 	"strings"
 
-	"github.com/linkerd/linkerd2/pkg/charts/static"
+	"github.com/linkerd/linkerd2/charts"
 	"github.com/linkerd/linkerd2/pkg/version"
 	"helm.sh/helm/v3/pkg/chart/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
@@ -19,25 +19,25 @@ const versionPlaceholder = "linkerdVersionValue"
 
 var (
 	// L5dPartials is the list of templates in partials chart
-	// Keep this slice synced with the contents of /charts/partials
+	// Keep this slice synced with the contents of charts/partials
 	L5dPartials = []string{
-		"charts/partials/" + chartutil.ChartfileName,
-		"charts/partials/templates/_affinity.tpl",
-		"charts/partials/templates/_capabilities.tpl",
-		"charts/partials/templates/_debug.tpl",
-		"charts/partials/templates/_helpers.tpl",
-		"charts/partials/templates/_metadata.tpl",
-		"charts/partials/templates/_nodeselector.tpl",
-		"charts/partials/templates/_network-validator.tpl",
-		"charts/partials/templates/_proxy-config-ann.tpl",
-		"charts/partials/templates/_proxy-init.tpl",
-		"charts/partials/templates/_proxy.tpl",
-		"charts/partials/templates/_pull-secrets.tpl",
-		"charts/partials/templates/_resources.tpl",
-		"charts/partials/templates/_tolerations.tpl",
-		"charts/partials/templates/_trace.tpl",
-		"charts/partials/templates/_validate.tpl",
-		"charts/partials/templates/_volumes.tpl",
+		"partials/" + chartutil.ChartfileName,
+		"partials/templates/_affinity.tpl",
+		"partials/templates/_capabilities.tpl",
+		"partials/templates/_debug.tpl",
+		"partials/templates/_helpers.tpl",
+		"partials/templates/_metadata.tpl",
+		"partials/templates/_nodeselector.tpl",
+		"partials/templates/_network-validator.tpl",
+		"partials/templates/_proxy-config-ann.tpl",
+		"partials/templates/_proxy-init.tpl",
+		"partials/templates/_proxy.tpl",
+		"partials/templates/_pull-secrets.tpl",
+		"partials/templates/_resources.tpl",
+		"partials/templates/_tolerations.tpl",
+		"partials/templates/_trace.tpl",
+		"partials/templates/_validate.tpl",
+		"partials/templates/_volumes.tpl",
 	}
 )
 
@@ -56,7 +56,7 @@ type Chart struct {
 	Values map[string]any
 
 	Files []*loader.BufferedFile
-	Fs    http.FileSystem
+	Fs    embed.FS
 }
 
 func (c *Chart) render(partialsFiles []*loader.BufferedFile) (bytes.Buffer, error) {
@@ -64,11 +64,6 @@ func (c *Chart) render(partialsFiles []*loader.BufferedFile) (bytes.Buffer, erro
 		return bytes.Buffer{}, err
 	}
 
-	// static.Templates is used as partials are always available there
-	if err := FilesReader(static.Templates, "", partialsFiles); err != nil {
-		return bytes.Buffer{}, err
-	}
-
 	// Create chart and render templates
 	chart, err := loader.LoadFiles(append(c.Files, partialsFiles...))
 	if err != nil {
@@ -119,36 +114,40 @@ func (c *Chart) render(partialsFiles []*loader.BufferedFile) (bytes.Buffer, erro
 // Render returns a bytes buffer with the result of rendering a Helm chart
 func (c *Chart) Render() (bytes.Buffer, error) {
 
-	l5dPartials := []*loader.BufferedFile{}
-	for _, template := range L5dPartials {
-		l5dPartials = append(l5dPartials, &loader.BufferedFile{
-			Name: template,
-		})
+	partials, err := LoadPartials()
+	if err != nil {
+		return bytes.Buffer{}, err
 	}
 
-	return c.render(l5dPartials)
+	return c.render(partials)
 }
 
 // RenderCNI returns a bytes buffer with the result of rendering a Helm chart
 func (c *Chart) RenderCNI() (bytes.Buffer, error) {
 	cniPartials := []*loader.BufferedFile{
-		{Name: "charts/partials/" + chartutil.ChartfileName},
-		{Name: "charts/partials/templates/_helpers.tpl"},
-		{Name: "charts/partials/templates/_metadata.tpl"},
-		{Name: "charts/partials/templates/_pull-secrets.tpl"},
-		{Name: "charts/partials/templates/_tolerations.tpl"},
-		{Name: "charts/partials/templates/_resources.tpl"},
+		{Name: "partials/" + chartutil.ChartfileName},
+		{Name: "partials/templates/_helpers.tpl"},
+		{Name: "partials/templates/_metadata.tpl"},
+		{Name: "partials/templates/_pull-secrets.tpl"},
+		{Name: "partials/templates/_tolerations.tpl"},
+		{Name: "partials/templates/_resources.tpl"},
+	}
+
+	// Load all partial chart files into buffer
+	if err := FilesReader(charts.Templates, "", cniPartials); err != nil {
+		return bytes.Buffer{}, err
+	}
+
+	// The partials files must have the "charts/" prefix to be loaded correctly.
+	for i, f := range cniPartials {
+		cniPartials[i].Name = path.Join("charts", f.Name)
 	}
 	return c.render(cniPartials)
 }
 
 // ReadFile updates the buffered file with the data read from disk
-func ReadFile(fs http.FileSystem, dir string, f *loader.BufferedFile) error {
-	filename := dir + f.Name
-	if dir == "" {
-		filename = filename[7:]
-	}
-	file, err := fs.Open(filename)
+func ReadFile(fs embed.FS, dir string, f *loader.BufferedFile) error {
+	file, err := fs.Open(dir + f.Name)
 	if err != nil {
 		return err
 	}
@@ -164,7 +163,7 @@ func ReadFile(fs http.FileSystem, dir string, f *loader.BufferedFile) error {
 }
 
 // FilesReader reads all the files from a directory
-func FilesReader(fs http.FileSystem, dir string, files []*loader.BufferedFile) error {
+func FilesReader(fs embed.FS, dir string, files []*loader.BufferedFile) error {
 	for _, f := range files {
 		if err := ReadFile(fs, dir, f); err != nil {
 			return err
@@ -173,6 +172,26 @@ func FilesReader(fs http.FileSystem, dir string, files []*loader.BufferedFile) e
 	return nil
 }
 
+func LoadPartials() ([]*loader.BufferedFile, error) {
+	var partialFiles []*loader.BufferedFile
+	for _, template := range L5dPartials {
+		partialFiles = append(partialFiles,
+			&loader.BufferedFile{Name: template},
+		)
+	}
+
+	// Load all partial chart files into buffer
+	if err := FilesReader(charts.Templates, "", partialFiles); err != nil {
+		return nil, err
+	}
+
+	// The partials files must have the "charts/" prefix to be loaded correctly.
+	for i, f := range partialFiles {
+		partialFiles[i].Name = path.Join("charts", f.Name)
+	}
+	return partialFiles, nil
+}
+
 // InsertVersion returns the chart values file contents passed in
 // with the version placeholder replaced with the current version
 func InsertVersion(data []byte) []byte {
@@ -191,7 +210,7 @@ func InsertVersionValues(values chartutil.Values) (chartutil.Values, error) {
 }
 
 // OverrideFromFile overrides the given map with the given file from FS
-func OverrideFromFile(values map[string]interface{}, fs http.FileSystem, chartName, name string) (map[string]interface{}, error) {
+func OverrideFromFile(values map[string]interface{}, fs embed.FS, chartName, name string) (map[string]interface{}, error) {
 	// Load Values file
 	valuesOverride := loader.BufferedFile{
 		Name: name,
diff --git a/pkg/charts/cni/values.go b/pkg/charts/cni/values.go
index 935cd331c..46b56d531 100644
--- a/pkg/charts/cni/values.go
+++ b/pkg/charts/cni/values.go
@@ -3,8 +3,8 @@ package cni
 import (
 	"fmt"
 
-	"github.com/linkerd/linkerd2/pkg/charts"
-	"github.com/linkerd/linkerd2/pkg/charts/static"
+	"github.com/linkerd/linkerd2/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"helm.sh/helm/v3/pkg/chart/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
@@ -111,11 +111,11 @@ func readDefaults(chartDir string) (*Values, error) {
 	file := &loader.BufferedFile{
 		Name: chartutil.ValuesfileName,
 	}
-	if err := charts.ReadFile(static.Templates, chartDir, file); err != nil {
+	if err := chartspkg.ReadFile(charts.Templates, chartDir, file); err != nil {
 		return nil, err
 	}
 	values := Values{}
-	if err := yaml.Unmarshal(charts.InsertVersion(file.Data), &values); err != nil {
+	if err := yaml.Unmarshal(chartspkg.InsertVersion(file.Data), &values); err != nil {
 		return nil, err
 	}
 	return &values, nil
diff --git a/pkg/charts/linkerd2/values.go b/pkg/charts/linkerd2/values.go
index 6c7e3c263..48b3f4d56 100644
--- a/pkg/charts/linkerd2/values.go
+++ b/pkg/charts/linkerd2/values.go
@@ -5,8 +5,8 @@ import (
 	"fmt"
 
 	"github.com/imdario/mergo"
-	"github.com/linkerd/linkerd2/pkg/charts"
-	"github.com/linkerd/linkerd2/pkg/charts/static"
+	"github.com/linkerd/linkerd2/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/version"
 	"helm.sh/helm/v3/pkg/chart/loader"
@@ -15,7 +15,7 @@ import (
 	"sigs.k8s.io/yaml"
 )
 
-const (
+var (
 	// HelmChartDirCrds is the directory name for the linkerd-crds chart
 	HelmChartDirCrds = "linkerd-crds"
 
@@ -443,12 +443,12 @@ func MergeHAValues(values *Values) error {
 // readDefaults read all the default variables from filename.
 func readDefaults(filename string) (*Values, error) {
 	valuesFile := &loader.BufferedFile{Name: filename}
-	if err := charts.ReadFile(static.Templates, "/", valuesFile); err != nil {
+	if err := chartspkg.ReadFile(charts.Templates, "", valuesFile); err != nil {
 		return nil, err
 	}
 
 	var values Values
-	err := yaml.Unmarshal(charts.InsertVersion(valuesFile.Data), &values)
+	err := yaml.Unmarshal(chartspkg.InsertVersion(valuesFile.Data), &values)
 
 	return &values, err
 }
diff --git a/pkg/charts/static/generate.go b/pkg/charts/static/generate.go
deleted file mode 100644
index ae84c0735..000000000
--- a/pkg/charts/static/generate.go
+++ /dev/null
@@ -1,21 +0,0 @@
-//go:build ignore
-
-package main
-
-import (
-	"github.com/linkerd/linkerd2/pkg/charts/static"
-	"github.com/shurcooL/vfsgen"
-	log "github.com/sirupsen/logrus"
-)
-
-func main() {
-	err := vfsgen.Generate(static.Templates, vfsgen.Options{
-		Filename:     "generated_linkerd_templates.gogen.go",
-		PackageName:  "static",
-		BuildTags:    "prod",
-		VariableName: "Templates",
-	})
-	if err != nil {
-		log.Fatalln(err)
-	}
-}
diff --git a/pkg/charts/static/helpers.go b/pkg/charts/static/helpers.go
deleted file mode 100644
index b88dea365..000000000
--- a/pkg/charts/static/helpers.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package static
-
-import (
-	"path"
-	"path/filepath"
-	"runtime"
-)
-
-// GetRepoRoot returns the full path to the root of the repo. We assume this
-// function is only called from the `Templates` var above, and that this source
-// file lives at `pkg/charts/static`, relative to the root of the repo.
-func GetRepoRoot() string {
-	// /foo/bar/linkerd2/pkg/charts/static/templates.go
-	_, filename, _, _ := runtime.Caller(0)
-
-	// /foo/bar/linkerd2/pkg/charts/static
-	dir := filepath.Dir(filename)
-
-	// filepath.Dir returns the parent directory, so that combined with joining
-	// ".." walks 3 levels up the tree:
-	// /foo/bar/linkerd2
-	return filepath.Dir(path.Join(dir, "../.."))
-}
diff --git a/pkg/charts/static/templates.go b/pkg/charts/static/templates.go
deleted file mode 100644
index e613ec36a..000000000
--- a/pkg/charts/static/templates.go
+++ /dev/null
@@ -1,13 +0,0 @@
-//go:generate go run generate.go
-//go:build !prod
-
-package static
-
-import (
-	"net/http"
-	"path"
-)
-
-// Templates that will be rendered by `linkerd install`. This is only used on
-// dev builds.
-var Templates http.FileSystem = http.Dir(path.Join(GetRepoRoot(), "charts"))
diff --git a/pkg/inject/inject.go b/pkg/inject/inject.go
index 28e016c70..2291339f1 100644
--- a/pkg/inject/inject.go
+++ b/pkg/inject/inject.go
@@ -16,9 +16,9 @@ import (
 	"time"
 
 	jsonfilter "github.com/clarketm/json"
-	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
-	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/util"
 	log "github.com/sirupsen/logrus"
@@ -704,13 +704,13 @@ func GetPodPatch(conf *ResourceConfig, injectProxy bool, values *OverriddenValue
 		{Name: "templates/patch.json"},
 	}
 
-	chart := &charts.Chart{
+	chart := &chartspkg.Chart{
 		Name:      "patch",
 		Dir:       "patch",
 		Namespace: conf.namespace,
 		RawValues: rawValues,
 		Files:     files,
-		Fs:        static.Templates,
+		Fs:        charts.Templates,
 	}
 	buf, err := chart.Render()
 	if err != nil {
diff --git a/viz/charts/templates.go b/viz/charts/templates.go
new file mode 100644
index 000000000..7eb9af74a
--- /dev/null
+++ b/viz/charts/templates.go
@@ -0,0 +1,8 @@
+package charts
+
+import (
+	"embed"
+)
+
+//go:embed linkerd-viz
+var Templates embed.FS
diff --git a/viz/cmd/allow-scrapes.go b/viz/cmd/allow-scrapes.go
index 54a8d408e..76922b410 100644
--- a/viz/cmd/allow-scrapes.go
+++ b/viz/cmd/allow-scrapes.go
@@ -121,7 +121,7 @@ func newCmdAllowScrapes() *cobra.Command {
 	output := "yaml"
 	options := templateOptions{
 		ExtensionName: ExtensionName,
-		ChartName:     vizChartName,
+		ChartName:     VizChartName,
 		Version:       version.Version,
 		VizNs:         defaultNamespace,
 	}
diff --git a/viz/cmd/install.go b/viz/cmd/install.go
index 0e07338a3..f4dc2cd0c 100644
--- a/viz/cmd/install.go
+++ b/viz/cmd/install.go
@@ -7,12 +7,11 @@ import (
 	"path"
 	"time"
 
-	"github.com/linkerd/linkerd2/pkg/charts"
-	partials "github.com/linkerd/linkerd2/pkg/charts/static"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
 	"github.com/linkerd/linkerd2/pkg/flags"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
-	"github.com/linkerd/linkerd2/viz/static"
+	"github.com/linkerd/linkerd2/viz/charts"
 	"github.com/spf13/cobra"
 	"helm.sh/helm/v3/pkg/chart/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
@@ -112,7 +111,7 @@ func install(w io.Writer, options values.Options, ha, cniEnabled bool, format st
 	}
 
 	if ha {
-		valuesOverrides, err = charts.OverrideFromFile(valuesOverrides, static.Templates, vizChartName, "values-ha.yaml")
+		valuesOverrides, err = chartspkg.OverrideFromFile(valuesOverrides, charts.Templates, VizChartName, "values-ha.yaml")
 		if err != nil {
 			return err
 		}
@@ -140,20 +139,13 @@ func render(w io.Writer, valuesOverrides map[string]interface{}, format string)
 		)
 	}
 
-	var partialFiles []*loader.BufferedFile
-	for _, template := range charts.L5dPartials {
-		partialFiles = append(partialFiles,
-			&loader.BufferedFile{Name: template},
-		)
-	}
-
 	// Load all Viz chart files into buffer
-	if err := charts.FilesReader(static.Templates, vizChartName+"/", files); err != nil {
+	if err := chartspkg.FilesReader(charts.Templates, VizChartName+"/", files); err != nil {
 		return err
 	}
 
-	// Load all partial chart files into buffer
-	if err := charts.FilesReader(partials.Templates, "", partialFiles); err != nil {
+	partialFiles, err := chartspkg.LoadPartials()
+	if err != nil {
 		return err
 	}
 
@@ -168,7 +160,7 @@ func render(w io.Writer, valuesOverrides map[string]interface{}, format string)
 		return err
 	}
 
-	vals, err = charts.InsertVersionValues(vals)
+	vals, err = chartspkg.InsertVersionValues(vals)
 	if err != nil {
 		return err
 	}
diff --git a/viz/cmd/root.go b/viz/cmd/root.go
index 5213f5edd..85b273220 100644
--- a/viz/cmd/root.go
+++ b/viz/cmd/root.go
@@ -19,7 +19,6 @@ const (
 	// until 15d1809bd043192bb21cacbc96112cce35bf384f
 	LegacyExtensionName = "linkerd-viz"
 
-	vizChartName            = "linkerd-viz"
 	defaultNamespace        = "linkerd-viz"
 	defaultLinkerdNamespace = "linkerd"
 	maxRps                  = 100.0
@@ -31,6 +30,8 @@ const (
 )
 
 var (
+	VizChartName = "linkerd-viz"
+
 	// special handling for Windows, on all other platforms these resolve to
 	// os.Stdout and os.Stderr, thanks to https://github.com/mattn/go-colorable
 	stdout = color.Output
diff --git a/viz/static/generate.go b/viz/static/generate.go
deleted file mode 100644
index 28785ffbc..000000000
--- a/viz/static/generate.go
+++ /dev/null
@@ -1,21 +0,0 @@
-//go:build ignore
-
-package main
-
-import (
-	"github.com/linkerd/linkerd2/viz/static"
-	"github.com/shurcooL/vfsgen"
-	log "github.com/sirupsen/logrus"
-)
-
-func main() {
-	err := vfsgen.Generate(static.Templates, vfsgen.Options{
-		Filename:     "generated_viz_templates.gogen.go",
-		PackageName:  "static",
-		BuildTags:    "prod",
-		VariableName: "Templates",
-	})
-	if err != nil {
-		log.Fatalln(err)
-	}
-}
diff --git a/viz/static/templates.go b/viz/static/templates.go
deleted file mode 100644
index c240343e5..000000000
--- a/viz/static/templates.go
+++ /dev/null
@@ -1,15 +0,0 @@
-//go:generate go run generate.go
-//go:build !prod
-
-package static
-
-import (
-	"net/http"
-	"path"
-
-	"github.com/linkerd/linkerd2/pkg/charts/static"
-)
-
-// Templates that will be rendered by `linkerd viz install`. This is only used on
-// dev builds.
-var Templates http.FileSystem = http.Dir(path.Join(static.GetRepoRoot(), "viz/charts"))
diff --git a/web/Dockerfile b/web/Dockerfile
index b32abbf03..f557dbf84 100644
--- a/web/Dockerfile
+++ b/web/Dockerfile
@@ -43,6 +43,7 @@ COPY viz/pkg viz/pkg
 COPY viz/tap/gen/tap viz/tap/gen/tap
 COPY viz/tap/pkg viz/tap/pkg
 COPY pkg pkg
+COPY charts charts
 ARG TARGETARCH
 RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -mod=readonly -o web/web -ldflags "-s -w" ./web
 
diff --git a/web/app/package.json b/web/app/package.json
index 96e79af7f..02268fe32 100644
--- a/web/app/package.json
+++ b/web/app/package.json
@@ -29,7 +29,7 @@
     "make-plural": "^7.4.0",
     "path": "0.12.7",
     "prop-types": "15.8.1",
-    "query-string": "^9.2.1",
+    "query-string": "^9.2.2",
     "react": "16.14.0",
     "react-dom": "16.14.0",
     "react-linkify": "1.0.0-alpha",
diff --git a/web/app/yarn.lock b/web/app/yarn.lock
index eb3e7b014..025a56780 100644
--- a/web/app/yarn.lock
+++ b/web/app/yarn.lock
@@ -8161,10 +8161,10 @@ query-string@^7.0.0:
     split-on-first "^1.0.0"
     strict-uri-encode "^2.0.0"
 
-query-string@^9.2.1:
-  version "9.2.1"
-  resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.2.1.tgz#67bd95f6e2cb64eafecfb0504be7cc38bcd4dd11"
-  integrity sha512-3jTGGLRzlhu/1ws2zlr4Q+GVMLCQTLFOj8CMX5x44cdZG9FQE07x2mQhaNxaKVPNmIDu0mvJ/cEwtY7Pim7hqA==
+query-string@^9.2.2:
+  version "9.2.2"
+  resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.2.2.tgz#a0104824edfdd2c1db2f18af71cef7abf6a3b20f"
+  integrity sha512-pDSIZJ9sFuOp6VnD+5IkakSVf+rICAuuU88Hcsr6AKL0QtxSIfVuKiVP2oahFI7tk3CRSexwV+Ya6MOoTxzg9g==
   dependencies:
     decode-uri-component "^0.4.1"
     filter-obj "^5.1.0"


Test patch:
diff --git a/cli/cmd/install_helm_test.go b/cli/cmd/install_helm_test.go
index c1ee4cdf2..b963460df 100644
--- a/cli/cmd/install_helm_test.go
+++ b/cli/cmd/install_helm_test.go
@@ -5,9 +5,9 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/charts"
+	chartspkg "github.com/linkerd/linkerd2/pkg/charts"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
-	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/testutil"
 	"helm.sh/helm/v3/pkg/chart"
@@ -227,7 +227,7 @@ func chartCrds(t *testing.T) *chart.Chart {
 
 	// Load defaults from values.yaml
 	valuesFile := &loader.BufferedFile{Name: l5dcharts.HelmChartDirCrds + "/values.yaml"}
-	if err := charts.ReadFile(static.Templates, "/", valuesFile); err != nil {
+	if err := chartspkg.ReadFile(charts.Templates, "", valuesFile); err != nil {
 		t.Fatal(err)
 	}
 	defaultValues := make(map[string]interface{})