Repository: GoogleContainerTools/skaffold
Pull request #8164 (2022-11-28 18:48:27)

docs: update helm docs to reflect new prioritization and changes to setValueTemplates
Docs changes related to:
#8147 

codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) Report
> Merging [#8164](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (8975380) into [main](https://codecov.io/gh/GoogleContainerTools/skaffold/commit/290280e8c6c19231e9ed5b5c027f2c01d22f75be?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (290280e) will **decrease** coverage by `4.26%`.
> The diff coverage is `53.26%`.

```diff
@@            Coverage Diff             @@
##             main    #8164      +/-   ##
==========================================
- Coverage   70.48%   66.21%   -4.27%     
==========================================
  Files         515      599      +84     
  Lines       23150    29346    +6196     
==========================================
+ Hits        16317    19432    +3115     
- Misses       5776     8459    +2683     
- Partials     1057     1455     +398     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | Coverage Δ | |
|---|---|---|
| [cmd/skaffold/app/cmd/completion.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY29tcGxldGlvbi5nbw==) | `13.04% <0.00%> (-1.25%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/credits/export.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY3JlZGl0cy9leHBvcnQuZ28=) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/deploy.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGVwbG95Lmdv) | `40.90% <0.00%> (-12.94%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/test.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvdGVzdC5nbw==) | `44.44% <0.00%> (ø)` | |
| [cmd/skaffold/app/exitcode.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9leGl0Y29kZS5nbw==) | `100.00% <ø> (+6.66%)` | :arrow_up: |
| [cmd/skaffold/skaffold.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL3NrYWZmb2xkLmdv) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_tests.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF90ZXN0cy5nbw==) | `62.50% <14.28%> (-1.14%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/render.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcmVuZGVyLmdv) | `35.48% <18.18%> (-5.90%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/lsp.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvbHNwLmdv) | `28.12% <28.12%> (ø)` | |
| [cmd/skaffold/app/cmd/run.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcnVuLmdv) | `64.28% <33.33%> (-9.63%)` | :arrow_down: |
| ... and [393 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8164/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools)


Patch:
diff --git a/.github/workflows/integration-linux.yml b/.github/workflows/integration-linux.yml
index 93750149c..b3825e6af 100644
--- a/.github/workflows/integration-linux.yml
+++ b/.github/workflows/integration-linux.yml
@@ -19,6 +19,7 @@ jobs:
         kustomize_version: [3.5.4]
         ko_version: [0.4.0]
         kompose_version: [1.21.0]
+        kpt_version: [1.0.0-beta.13]
         gcloud_sdk_version: [410.0.0]
         container_structure_tests_version: [1.8.0]
         integration_test_partitions: [0, 1, 2, 3]
@@ -78,6 +79,12 @@ jobs:
         wget -O kompose https://github.com/kubernetes/kompose/releases/download/v${{ matrix.kompose_version }}/kompose-linux-amd64 && chmod +x kompose
         sudo mv kompose /usr/local/bin/
 
+    - name: Install Kpt
+      if: ${{ env.NON_DOCS_FILES_CHANGED != 0 }}
+      run: |
+        wget -O kpt https://github.com/GoogleContainerTools/kpt/releases/download/v${{ matrix.kpt_version }}/kpt_linux_amd64 && chmod +x kpt
+        sudo mv kpt /usr/local/bin/
+
     - name: Install GCloud
       if: ${{ env.NON_DOCS_FILES_CHANGED != 0 }}
       run: |
diff --git a/deploy/skaffold/Dockerfile.deps b/deploy/skaffold/Dockerfile.deps
index 8a4319479..b71045b30 100644
--- a/deploy/skaffold/Dockerfile.deps
+++ b/deploy/skaffold/Dockerfile.deps
@@ -143,8 +143,10 @@ RUN /google-cloud-sdk/install.sh \
     --bash-completion=false \
     --disable-installation-options
 ENV PATH=$PATH:/google-cloud-sdk/bin
-RUN gcloud auth configure-docker && \
-    gcloud components install gke-gcloud-auth-plugin
+RUN gcloud auth configure-docker && gcloud components install --quiet \
+    gke-gcloud-auth-plugin \
+    cloud-run-proxy \
+    log-streaming
 
 FROM runtime_deps
 RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
diff --git a/deploy/skaffold/Dockerfile.deps.lts b/deploy/skaffold/Dockerfile.deps.lts
index ecccac2ef..f5ecc3d00 100644
--- a/deploy/skaffold/Dockerfile.deps.lts
+++ b/deploy/skaffold/Dockerfile.deps.lts
@@ -87,8 +87,10 @@ RUN /google-cloud-sdk/install.sh \
     --bash-completion=false \
     --disable-installation-options
 ENV PATH=$PATH:/google-cloud-sdk/bin
-RUN gcloud auth configure-docker && \
-    gcloud components install gke-gcloud-auth-plugin
+RUN gcloud auth configure-docker && gcloud components install --quiet \
+    gke-gcloud-auth-plugin \
+    cloud-run-proxy \
+    log-streaming
 
 FROM runtime_deps
 RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
diff --git a/deploy/skaffold/Dockerfile.deps.slim b/deploy/skaffold/Dockerfile.deps.slim
index ecccac2ef..f5ecc3d00 100644
--- a/deploy/skaffold/Dockerfile.deps.slim
+++ b/deploy/skaffold/Dockerfile.deps.slim
@@ -87,8 +87,10 @@ RUN /google-cloud-sdk/install.sh \
     --bash-completion=false \
     --disable-installation-options
 ENV PATH=$PATH:/google-cloud-sdk/bin
-RUN gcloud auth configure-docker && \
-    gcloud components install gke-gcloud-auth-plugin
+RUN gcloud auth configure-docker && gcloud components install --quiet \
+    gke-gcloud-auth-plugin \
+    cloud-run-proxy \
+    log-streaming
 
 FROM runtime_deps
 RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
diff --git a/docs-v2/content/en/docs/pipeline-stages/deployers/helm.md b/docs-v2/content/en/docs/pipeline-stages/deployers/helm.md
index f82d63888..857b8f475 100644
--- a/docs-v2/content/en/docs/pipeline-stages/deployers/helm.md
+++ b/docs-v2/content/en/docs/pipeline-stages/deployers/helm.md
@@ -19,24 +19,32 @@ To use `helm` with Skaffold, the `helm` binary must be installed on your machine
 Skaffold supports projects set up to render and/or deploy with Helm, but certain aspects of the project need to be configured correctly in order for Skaffold to work properly. This guide should demystify some of the nuance around using Skaffold with Helm to help you get started quickly.
 
 {{< alert title="No more `artifactOverrides` or `imageStrategy`" >}}
-Skaffold no longer requires the intricate configuring of `artifactOverrides` or `imageStrategy` fields. See docs [here]({{< relref "#image-reference-strategies" >}}) on how old `artifactOverrides` and `imageStrategy` values translate to `setValues` entires in the latest Skaffold schemas (`apiVersion: skaffold/v3alpha1` or skaffold binary version `v2.0.0` onwards)
+Skaffold no longer requires the intricate configuring of `artifactOverrides` or `imageStrategy` fields. See docs [here]({{< relref "#image-reference-strategies" >}}) on how old `artifactOverrides` and `imageStrategy` values translate to `setValueTemplates` entries in the latest Skaffold schemas (`apiVersion: skaffold/v3alpha1` or skaffold binary version `v2.0.0` onwards)
 {{< /alert >}}
 
 {{< alert title="Note" >}}
 In Skaffold `v2` the primary difference between the helm renderer (`manifest.helm.*`) and the helm deployer (`deploy.helm.*`) is the use of `helm template` vs `helm install`
 {{< /alert >}}
 
+
 ## How `helm` render support works in Skaffold
-In the latest version of Skaffold, the primary methods of using `helm` templating with Skaffold involve the `deploy.helm.setValues` and the `deploy.helm.setValueTemplates` fields.  `deploy.helm.setValues` supplies the key:value pair to substitute from a users `values.yaml` file (a standard `helm` file for rendering).  `deploy.helm.setValueTemplates` does a similar thing only the key:value value comes from an environment variable instead of a given value.  The thing to note here is that when using a value that is the same name as an artifact name, that value will be replaced by the fully qualified artifact name (eg: `image: gcr.io/example-repo/skaffold-helm-image:latest@sha256:<sha256-hash>`) for the current build (or the specified value from additional configuration eg: CLI flags or config from `skaffold.yaml`).  Depending on how a user's `values.yaml` and charts specify `image: $IMAGE_TEMPLATE`, the docs [here]({{< relref "#image-reference-strategies" >}})  explain the proper `setValues` to use.  When migrating from schema version `v2beta29` or less, Skaffold will automatically configure these values to continue to work.
+In the latest version of Skaffold, the primary methods of using `helm` templating with Skaffold involve the `deploy.helm.setValueTemplates` and the `deploy.helm.setValues` fields.  `deploy.helm.setValues` supplies the key:value pair to substitute from a users `values.yaml` file (a standard `helm` file for rendering).  `deploy.helm.setValueTemplates` does a similar thing only the key:value value comes from an environment variable instead of a given value. FDepending on how a user's `values.yaml` and how `charts/templates` specify `image: $IMAGE_TEMPLATE`, the docs [here]({{< relref "#image-reference-strategies" >}})  explain the proper `setValueTemplates` to use.  When migrating from schema version `v2beta29` or less, Skaffold will automatically configure these values to continue to work.
+
+
+`helm` deploy support in Skaffold is accomplished by calling `helm install ...` with the appropriate `--set` flags for the variables Skaffold will inject as well as uses the `skaffold` binary as a `helm` `--post-renderer`.  Using `skaffold` as a post-renderer is done to inject Skaffold specific labels primarily the `run-id` label which Skaffold uses to tag K8s objects it will manage via it's status checking.
 
 
-`helm` deploy support in Skaffold is accomplished by calling `helm install ...` and using the `skaffold` binary as a `helm` `--post-renderer`.  This works by having Skaffold run `helm install ...` taking into consideration all of the supplied flags, skaffold.yaml configuration, etc. and creating an intermediate yaml manifest with all helm replacements except that the fully qualified image from the current run is NOT added but instead a placeholder with the artifact name - eg: `skaffold-helm-image`.  Then the skaffold post-renderer is called to convert `image: skaffold-helm-image` -> `image: gcr.io/example-repo/skaffold-helm-image:latest@sha256:<sha256-hash>` in specified locations (specific allowlisted k8s objects and/or k8s object fields).  This above replacement is nearly identical to how it works for values.yaml files using only the `image` key in `values.yaml` - eg:
+This works by having Skaffold run `helm install ...` taking into consideration all of the supplied flags, skaffold.yaml configuration, etc. and creating an intermediate yaml manifest with all helm replacements except that the fully qualified image from the current run is NOT added but instead a placeholder with the artifact name - eg: `skaffold-helm-image`.  Then the skaffold post-renderer is called to convert `image: skaffold-helm-image` -> `image: gcr.io/example-repo/skaffold-helm-image:latest@sha256:<sha256-hash>` in specified locations (specific allowlisted k8s objects and/or k8s object fields).  This above replacement is nearly identical to how it works for values.yaml files using only the `image` key in `values.yaml` - eg:
 `image: "{{.Values.image}}"`
 
-When using `image.repoistory` + `image.tag` or `image.registry` + `image.repository` + `image.tag` - eg:
+When using `image.repository` + `image.tag` or `image.registry` + `image.repository` + `image.tag` - eg:
 `image: "{{.Values.image.repository}}:{{.Values.image.tag}}"`
 `image: "{{.Values.image.registry}}/{{.Values.image.repository}}:{{.Values.image.tag}}"`
-there is some specialized logic that the skaffold `post-renderer` uses to properly handling these cases.  See the docs [here]({{< relref "#image-reference-strategies" >}}) on the correct way to specify these for Skaffold using `setValues`
+there is some specialized logic that the skaffold `post-renderer` uses to properly handling these cases.  See the docs [here]({{< relref "#image-reference-strategies" >}}) on the correct way to specify these for Skaffold using `setValueTemplates`
+
+{{< alert title="Note" >}}
+Starting in Skaffold `v2.1.0`, Skaffold will output additional `setValueTemplates`
+{{< /alert >}}
 
 ## Image Configuration
 The normal Helm convention for defining image references is through the `values.yaml` file. Often, image information is configured through an `image` stanza in the values file, which might look something like this:
@@ -44,12 +52,16 @@ The normal Helm convention for defining image references is through the `values.
 ```project_root/values.yaml```
 ```yaml
 image:
-  repository: my-image
-  tag: v1.2.0
-  pullPolicy: IfNotPresent
+  repository: gcr.io/my-repo # default repo
+  tag: v1.2.0 # default tag 
+  pullPolicy: IfNotPresent # default PullPolicy
+image2:
+  repository: gcr.io/my-repo-2 # default repo
+  tag: latest # default tag 
+  pullPolicy: IfNotPresent # default PullPolicy
 ```
 
-This image would then be referenced in a templated resource file, maybe like this:
+This images would then be referenced in a templated resource file, maybe like this:
 
 ```project_root/templates/deployment.yaml:```
 ```yaml
@@ -60,6 +72,9 @@ spec:
         - name: {{ .Chart.Name }}
           image: {{ .Values.image.repository }}:{{ .Values.image.tag}}
           imagePullPolicy: {{ .Values.image.pullPolicy }}
+        - name: {{ .Chart.Name }}
+          image: {{ .Values.image2.repository }}:{{ .Values.image2.tag}}
+          imagePullPolicy: {{ .Values.image2.pullPolicy }}
 ```
 
 **IMPORTANT: To get Skaffold to work with Helm, the `image` key must be configured in the skaffold.yaml.**
@@ -69,18 +84,28 @@ Associating the Helm image key allows Skaffold to track the image being built, a
 ```yaml
 build:
   artifacts:
-    - image: my-image # must match in setValues
-    - image: my-image-2 # must match in setValues
+    - image: myFirstImage # must match in setValueTemplates
+    - image: mySecondImage # must match in setValueTemplates
 deploy:
   helm:
     releases:
     - name: my-release
+      setValueTemplates:
+        image.repository: "{{.myFirstImage.IMAGE_REPO}}"
+        image.tag: "{{.myFirstImage.IMAGE_TAG}}"
+        image2.repository: "{{.mySecondImage.IMAGE_REPO}}"
+        image2.tag: "{{.mySecondImage.IMAGE_TAG}}"
       setValues:
-        image: my-image # no tag present!
-        image2: my-image-2 # no tag present!
+        image.pullPolicy: "IfNotPresent"
+        image2.pullPolicy: "IfNotPresent"
 ```
 
-The `setValues` configuration binds a Helm key to the specified value.
+The `setValues` configuration binds a Helm key to the specified value. The `setValueTemplates` configuration binds a Helm key to an environment variable.  Skaffold generates some environment variables for each build artifact (value in build.artifacts\[x\].image).  Currenty these include:
+- `.<artifactName>.IMAGE_FULLY_QUALIFIED` (ex: `{{.myImage.IMAGE_FULLY_QUALIFIED}})` -> `gcr.io/example-repo/skaffold-helm-image:latest@sha256:<sha256-hash>`
+- `.<artifactName>.IMAGE_REPO` (ex:)
+- `.<artifactName>.IMAGE_TAG`
+- `.<artifactName>.IMAGE_DOMAIN`
+- `.<artifactName>.IMAGE_REPO_NO_DOMAIN`
 
 ### Multiple image overrides
 
@@ -104,9 +129,9 @@ deploy:
   helm:
     releases:
     - name: my-release
-      setValues:
-        firstContainerImage: gcr.io/my-project/first-image # no tag present!
-        secondContainerImage: gcr.io/my-project/second-image # no tag present!
+      setValueTemplates:
+        firstContainerImage: "{{.firstContainer.IMAGE_FULLY_QUALIFIED}}"
+        secondContainerImage: "{{.secondContainerImage.IMAGE_FULLY_QUALIFIED}}"
 ```
 
 ### Image reference strategies
@@ -125,16 +150,16 @@ The `skaffold.yaml` setup:
 ```yaml
 build:
   artifacts:
-    - image: my-image  # must match in setValues
-    - image: my-image-2  # must match in setValues
+    - image: myFirstImage  # must match in setValueTemplates
+    - image: mySecondImage  # must match in setValueTemplates
 deploy:
   helm:
     releases:
       - name: my-chart
         chartPath: helm
-        setValues:
-          image: my-image # no tag present!
-          image2: my-image-2 # no tag present!
+        setValueTemplates:
+          image: {{.myFirstImage.IMAGE_FULLY_QUALIFIED}} # no tag present!
+          image2: {{.mySecondImage.IMAGE_FULLY_QUALIFIED}} # no tag present!
 ```
 The `values.yaml` (note that Skaffold overrides this value):
 ```
@@ -165,18 +190,18 @@ The `skaffold.yaml` setup:
 ```yaml
 build:
   artifacts:
-    - image: my-image # must match in setValues
-    - image: my-image-2 # must match in setValues
+    - image: myFirstImage # must match in setValueTemplates
+    - image: mySecondImage # must match in setValueTemplates
 deploy:
   helm:
     releases:
       - name: my-chart
         chartPath: helm
-        setValues:
-          image.repository: my-image
-          image.tag: my-image
-          image2.repository: my-image-2
-          image2.tag: my-image-2
+        setValueTemplates:
+          image.repository: "{{.myFirstImage.IMAGE_REPO}}"
+          image.tag: "{{.myFirstImage.IMAGE_TAG}}"
+          image2.repository: "{{.mySecondImage.IMAGE_REPO}}"
+          image2.tag: "{{.mySecondImage.IMAGE_TAG}}"
 ```
 
 The `values.yaml` (note that Skaffold overrides these values):
@@ -212,20 +237,20 @@ The `skaffold.yaml` setup:
 ```yaml
 build:
   artifacts:
-    - image: my-image # must match in setValues
-    - image: my-image-2 # must match in setValues
+    - image: myFirstImage # must match in setValueTemplates
+    - image: mySecondImage # must match in setValueTemplates
 deploy:
   helm:
     releases:
       - name: my-chart
         chartPath: helm
-        setValues:
-          image.registry: my-image
-          image.repository: my-image
-          image.tag: my-image
-          image2.registry: my-image-2
-          image2.repository: my-image-2
-          image2.tag: my-image-2
+        setValueTemplates:
+          image.registry: "{{.myFirstImage.IMAGE_DOMAIN}}"
+          image.repository: "{{.myFirstImage.IMAGE_REPO_NO_DOMAIN}}"
+          image.tag: "{{.myFirstImage.IMAGE_TAG}}"
+          image2.registry: "{{.mySecondImage.IMAGE_DOMAIN}}"
+          image2.repository: "{{.mySecondImage.IMAGE_REPO_NO_DOMAIN}}"
+          image2.tag: "{{.mySecondImage.IMAGE_TAG}}"
 ```
 
 The `values.yaml` (note that Skaffold overrides these values):
diff --git a/docs-v2/content/en/docs/pipeline-stages/renderers/helm.md b/docs-v2/content/en/docs/pipeline-stages/renderers/helm.md
index 1c35000a2..afabbc447 100644
--- a/docs-v2/content/en/docs/pipeline-stages/renderers/helm.md
+++ b/docs-v2/content/en/docs/pipeline-stages/renderers/helm.md
@@ -19,24 +19,32 @@ To use `helm` with Skaffold, the `helm` binary must be installed on your machine
 Skaffold supports projects set up to render and/or deploy with Helm, but certain aspects of the project need to be configured correctly in order for Skaffold to work properly. This guide should demystify some of the nuance around using Skaffold with Helm to help you get started quickly.
 
 {{< alert title="No more `artifactOverrides` or `imageStrategy`" >}}
-Skaffold no longer requires the intricate configuring of `artifactOverrides` or `imageStrategy` fields. See docs [here]({{< relref "#image-reference-strategies" >}}) on how old `artifactOverrides` and `imageStrategy` values translate to `setValues` entires in the latest Skaffold schemas (`apiVersion: skaffold/v3alpha1` or skaffold binary version `v2.0.0` onwards)
+Skaffold no longer requires the intricate configuring of `artifactOverrides` or `imageStrategy` fields. See docs [here]({{< relref "#image-reference-strategies" >}}) on how old `artifactOverrides` and `imageStrategy` values translate to `setValueTemplates` entries in the latest Skaffold schemas (`apiVersion: skaffold/v3alpha1` or skaffold binary version `v2.0.0` onwards)
 {{< /alert >}}
 
 {{< alert title="Note" >}}
-In Skaffold `v2` the primary difference between the helm renderer (`manifest.helm.*`) and the helm deployer (`deploy.helm.*`) is the use of `helm template` vs `helm install`
+In Skaffold `v2` the primary difference between the helm renderer (`manifest.helm.*`) and the helm deployer (`deploy.helm.*`) is the use of `helm install` vs `helm template`
 {{< /alert >}}
 
+
 ## How `helm` render support works in Skaffold
-In the latest version of Skaffold, the primary methods of using `helm` templating with Skaffold involve the `manifests.helm.setValues` and the `manifests.helm.setValueTemplates` fields.  `manifests.helm.setValues` supplies the key:value pair to substitute from a users `values.yaml` file (a standard `helm` file for rendering).  `manifests.helm.setValueTemplates` does a similar thing only the key:value value comes from an environment variable instead of a given value.  The thing to note here is that when using a value that is the same name as an artifact name, that value will be replaced by the fully qualified artifact name (eg: `image: gcr.io/example-repo/skaffold-helm-image:latest@sha256:<sha256-hash>`) for the current build (or the specified value from additional configuration eg: CLI flags or config from `skaffold.yaml`).  Depending on how a user's `values.yaml` and charts specify `image: $IMAGE_TEMPLATE`, the docs [here]({{< relref "#image-reference-strategies" >}})  explain the proper `setValues` to use.  When migrating from schema version `v2beta29` or less, Skaffold will automatically configure these values to continue to work.
+In the latest version of Skaffold, the primary methods of using `helm` templating with Skaffold involve the `deploy.helm.setValueTemplates` and the `deploy.helm.setValues` fields.  `deploy.helm.setValues` supplies the key:value pair to substitute from a users `values.yaml` file (a standard `helm` file for rendering).  `deploy.helm.setValueTemplates` does a similar thing only the key:value value comes from an environment variable instead of a given value.  Depending on how a user's `values.yaml` and how `charts/templates` specify `image: $IMAGE_TEMPLATE`, the docs [here]({{< relref "#image-reference-strategies" >}})  explain the proper `setValueTemplates` to use.  When migrating from schema version `v2beta29` or less, Skaffold will automatically configure these values to continue to work.
+
+
+`helm` deploy support in Skaffold is accomplished by calling `helm template ...` with the appropriate `--set` flags for the variables Skaffold will inject as well as uses the `skaffold` binary as a `helm` `--post-renderer`.  Using `skaffold` as a post-renderer is done to inject Skaffold specific labels primarily the `run-id` label which Skaffold uses to tag K8s objects it will manage via it's status checking.
 
 
-`helm` render support in Skaffold is accomplished by calling `helm template ...` and using the `skaffold` binary as a `helm` `--post-renderer`.  This works by having Skaffold run `helm template ...` taking into consideration all of the supplied flags, skaffold.yaml configuration, etc. and creating an intermediate yaml manifest with all helm replacements except that the fully qualified image from the current run is NOT added but instead a placeholder with the artifact name - eg: `skaffold-helm-image`.  Then the skaffold post-renderer is called to convert `image: skaffold-helm-image` -> `image: gcr.io/example-repo/skaffold-helm-image:latest@sha256:<sha256-hash>` in specified locations (specific allowlisted k8s objects and/or k8s object fields).  This above replacement is nearly identical to how it works for values.yaml files using only the `image` key in `values.yaml` - eg:
+This works by having Skaffold run `helm template ...` taking into consideration all of the supplied flags, skaffold.yaml configuration, etc. and creating an intermediate yaml manifest with all helm replacements except that the fully qualified image from the current run is NOT added but instead a placeholder with the artifact name - eg: `skaffold-helm-image`.  Then the skaffold post-renderer is called to convert `image: skaffold-helm-image` -> `image: gcr.io/example-repo/skaffold-helm-image:latest@sha256:<sha256-hash>` in specified locations (specific allowlisted k8s objects and/or k8s object fields).  This above replacement is nearly identical to how it works for values.yaml files using only the `image` key in `values.yaml` - eg:
 `image: "{{.Values.image}}"`
 
-When using `image.repoistory` + `image.tag` or `image.registry` + `image.repository` + `image.tag` - eg:
+When using `image.repository` + `image.tag` or `image.registry` + `image.repository` + `image.tag` - eg:
 `image: "{{.Values.image.repository}}:{{.Values.image.tag}}"`
 `image: "{{.Values.image.registry}}/{{.Values.image.repository}}:{{.Values.image.tag}}"`
-there is some specialized logic that the skaffold `post-renderer` uses to properly handling these cases.  See the docs [here]({{< relref "#image-reference-strategies" >}}) on the correct way to specify these for Skaffold using `setValues`
+there is some specialized logic that the skaffold `post-renderer` uses to properly handling these cases.  See the docs [here]({{< relref "#image-reference-strategies" >}}) on the correct way to specify these for Skaffold using `setValueTemplates`
+
+{{< alert title="Note" >}}
+Starting in Skaffold `v2.1.0`, Skaffold will output additional `setValueTemplates`
+{{< /alert >}}
 
 ## Image Configuration
 The normal Helm convention for defining image references is through the `values.yaml` file. Often, image information is configured through an `image` stanza in the values file, which might look something like this:
@@ -44,12 +52,16 @@ The normal Helm convention for defining image references is through the `values.
 ```project_root/values.yaml```
 ```yaml
 image:
-  repository: my-image
-  tag: v1.2.0
-  pullPolicy: IfNotPresent
+  repository: gcr.io/my-repo # default repo
+  tag: v1.2.0 # default tag 
+  pullPolicy: IfNotPresent # default PullPolicy
+image2:
+  repository: gcr.io/my-repo-2 # default repo
+  tag: latest # default tag 
+  pullPolicy: IfNotPresent # default PullPolicy
 ```
 
-This image would then be referenced in a templated resource file, maybe like this:
+This images would then be referenced in a templated resource file, maybe like this:
 
 ```project_root/templates/deployment.yaml:```
 ```yaml
@@ -60,6 +72,9 @@ spec:
         - name: {{ .Chart.Name }}
           image: {{ .Values.image.repository }}:{{ .Values.image.tag}}
           imagePullPolicy: {{ .Values.image.pullPolicy }}
+        - name: {{ .Chart.Name }}
+          image: {{ .Values.image2.repository }}:{{ .Values.image2.tag}}
+          imagePullPolicy: {{ .Values.image2.pullPolicy }}
 ```
 
 **IMPORTANT: To get Skaffold to work with Helm, the `image` key must be configured in the skaffold.yaml.**
@@ -69,18 +84,28 @@ Associating the Helm image key allows Skaffold to track the image being built, a
 ```yaml
 build:
   artifacts:
-    - image: my-image # must match in setValues
-    - image: my-image-2 # must match in setValues
-manifests:
+    - image: myFirstImage # must match in setValueTemplates
+    - image: mySecondImage # must match in setValueTemplates
+deploy:
   helm:
     releases:
     - name: my-release
+      setValueTemplates:
+        image.repository: "{{.myFirstImage.IMAGE_REPO}}"
+        image.tag: "{{.myFirstImage.IMAGE_TAG}}"
+        image2.repository: "{{.mySecondImage.IMAGE_REPO}}"
+        image2.tag: "{{.mySecondImage.IMAGE_TAG}}"
       setValues:
-        image: my-image # no tag present!
-        image2: my-image-2 # no tag present!
+        image.pullPolicy: "IfNotPresent"
+        image2.pullPolicy: "IfNotPresent"
 ```
 
-The `setValues` configuration binds a Helm key to the specified value.
+The `setValues` configuration binds a Helm key to the specified value. The `setValueTemplates` configuration binds a Helm key to an environment variable.  Skaffold generates some environment variables for each build artifact (value in build.artifacts\[x\].image).  Currenty these include:
+- `.<artifactName>.IMAGE_FULLY_QUALIFIED` (ex: `{{.myImage.IMAGE_FULLY_QUALIFIED}})` -> `gcr.io/example-repo/skaffold-helm-image:latest@sha256:<sha256-hash>`
+- `.<artifactName>.IMAGE_REPO` (ex:)
+- `.<artifactName>.IMAGE_TAG`
+- `.<artifactName>.IMAGE_DOMAIN`
+- `.<artifactName>.IMAGE_REPO_NO_DOMAIN`
 
 ### Multiple image overrides
 
@@ -100,13 +125,13 @@ spec:
 can be overriden with:
 
 ```yaml
-manifests:
+deploy:
   helm:
     releases:
     - name: my-release
-      setValues:
-        firstContainerImage: gcr.io/my-project/first-image # no tag present!
-        secondContainerImage: gcr.io/my-project/second-image # no tag present!
+      setValueTemplates:
+        firstContainerImage: "{{.firstContainer.IMAGE_FULLY_QUALIFIED}}"
+        secondContainerImage: "{{.secondContainerImage.IMAGE_FULLY_QUALIFIED}}"
 ```
 
 ### Image reference strategies
@@ -125,16 +150,16 @@ The `skaffold.yaml` setup:
 ```yaml
 build:
   artifacts:
-    - image: my-image  # must match in setValues
-    - image: my-image-2  # must match in setValues
-manifests:
+    - image: myFirstImage  # must match in setValueTemplates
+    - image: mySecondImage  # must match in setValueTemplates
+deploy:
   helm:
     releases:
       - name: my-chart
         chartPath: helm
-        setValues:
-          image: my-image # no tag present!
-          image2: my-image-2 # no tag present!
+        setValueTemplates:
+          image: {{.myFirstImage.IMAGE_FULLY_QUALIFIED}} # no tag present!
+          image2: {{.mySecondImage.IMAGE_FULLY_QUALIFIED}} # no tag present!
 ```
 The `values.yaml` (note that Skaffold overrides this value):
 ```
@@ -165,18 +190,18 @@ The `skaffold.yaml` setup:
 ```yaml
 build:
   artifacts:
-    - image: my-image # must match in setValues
-    - image: my-image-2 # must match in setValues
-manifests:
+    - image: myFirstImage # must match in setValueTemplates
+    - image: mySecondImage # must match in setValueTemplates
+deploy:
   helm:
     releases:
       - name: my-chart
         chartPath: helm
-        setValues:
-          image.repository: my-image
-          image.tag: my-image
-          image2.repository: my-image-2
-          image2.tag: my-image-2
+        setValueTemplates:
+          image.repository: "{{.myFirstImage.IMAGE_REPO}}"
+          image.tag: "{{.myFirstImage.IMAGE_TAG}}"
+          image2.repository: "{{.mySecondImage.IMAGE_REPO}}"
+          image2.tag: "{{.mySecondImage.IMAGE_TAG}}"
 ```
 
 The `values.yaml` (note that Skaffold overrides these values):
@@ -212,20 +237,20 @@ The `skaffold.yaml` setup:
 ```yaml
 build:
   artifacts:
-    - image: my-image # must match in setValues
-    - image: my-image-2 # must match in setValues
-manifests:
+    - image: myFirstImage # must match in setValueTemplates
+    - image: mySecondImage # must match in setValueTemplates
+deploy:
   helm:
     releases:
       - name: my-chart
         chartPath: helm
-        setValues:
-          image.registry: my-image
-          image.repository: my-image
-          image.tag: my-image
-          image2.registry: my-image-2
-          image2.repository: my-image-2
-          image2.tag: my-image-2
+        setValueTemplates:
+          image.registry: "{{.myFirstImage.IMAGE_DOMAIN}}"
+          image.repository: "{{.myFirstImage.IMAGE_REPO_NO_DOMAIN}}"
+          image.tag: "{{.myFirstImage.IMAGE_TAG}}"
+          image2.registry: "{{.mySecondImage.IMAGE_DOMAIN}}"
+          image2.repository: "{{.mySecondImage.IMAGE_REPO_NO_DOMAIN}}"
+          image2.tag: "{{.mySecondImage.IMAGE_TAG}}"
 ```
 
 The `values.yaml` (note that Skaffold overrides these values):
@@ -250,7 +275,7 @@ spec:
 
 Skaffold will invoke
 ```
-helm template <chart> <chart-path> --set-string image.registry=<artifact-name>,image.repository=<artifact-name>,image.tag=<artifact-name>,image2.registry=<artifact-name>,image2.repository=<artifact-name>,image2.tag=<artifact-name> --post-renderer=<path-to-skaffold-binary-from-original-invocation>
+helm template <chart> <chart-path> --set-string image.registry=<artifact-name>,image.repository=<artifact-name>,image.tag=<artifact-name>,image2.registry=<artifact-name>,image2.repository=<artifact-name>,image2.tag=<artifcact-name>  --post-renderer=<path-to-skaffold-binary-from-original-invocation>
 ```
 
 ### Helm Build Dependencies
diff --git a/docs-v2/content/en/docs/tutorials/skaffold-resource-selector.md b/docs-v2/content/en/docs/tutorials/skaffold-resource-selector.md
index 51757f070..57e423c94 100644
--- a/docs-v2/content/en/docs/tutorials/skaffold-resource-selector.md
+++ b/docs-v2/content/en/docs/tutorials/skaffold-resource-selector.md
@@ -12,7 +12,7 @@ Common Use Cases This Page Helps Resolve:
 * Users who are seeing issues with skaffold's default resource field overwriting for a given resource - eg: skaffold errors as it tries to mutate immutable config on re-deployment
 
 Currently skaffold modifies the manifests it renders and deploys for the following functionality:
-- status checking - done by mutating the manifest/K8s-Object by adding a label - skaffold/dev/run-id.  Skaffold uses this run-id to identify ... (TODO add doc link to run-id explanation)
+- status checking - done by mutating the manifest/K8s-Object by adding a label - skaffold/dev/run-id.  Skaffold uses this run-id to identify the deployments Skaffold manages with it's status checking.
 - image label overwriting - done by mutating the manifest/K8s-Object by substituting the `image:$ORIGINAL_IMAGE_TAG` value(s) in a manifest with `image:$RECENT_SKAFFOLD_BUILT_IMAGE`
 
 
@@ -47,7 +47,7 @@ _* *.image (changes `image:` value to be the skaffold built image ONLY IF skaffo
 
 
 The GroupKind's that Skaffold manages (via resource field overwriting) are user configurable via the `resourceSelector:` top level configuration.  The `resourceSelector` configuration allows users to modify and extend which resources and what fields of those resources skaffold modifies.  Currently skaffold only supports `label:` and `.metadata.labels` related modifications.
-(TODO add `resourceSelector` schema overview and allowable inputs)
+
 `resourceSelector` spec (from `pkg/skaffold/schema/latest/config.go`)
 ```
 // ResourceSelector describes user defined filters describing how skaffold should treat objects/fields during rendering.
diff --git a/pkg/skaffold/deploy/util/util.go b/pkg/skaffold/deploy/util/util.go
index d55ee9db6..b69636154 100644
--- a/pkg/skaffold/deploy/util/util.go
+++ b/pkg/skaffold/deploy/util/util.go
@@ -18,7 +18,6 @@ package util
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -38,7 +37,6 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
-	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringset"
 )
 
@@ -106,10 +104,15 @@ func ConsolidateNamespaces(original, new []string) []string {
 }
 
 // GetHydrationDir points to the directory where the manifest rendering happens. By default, it is set to "<WORKDIR>/.kpt-pipeline".
-func GetHydrationDir(opts config.SkaffoldOptions, workingDir string, promptIfNeeded bool) (string, error) {
+func GetHydrationDir(opts config.SkaffoldOptions, workingDir string, promptIfNeeded bool, isKptRendererOrDeployerUsed bool) (string, error) {
 	var hydratedDir string
 	var err error
 
+	if !isKptRendererOrDeployerUsed {
+		log.Entry(context.TODO()).Info("no kpt renderer or deployer found, skipping hydrated-dir creation")
+		return "", nil
+	}
+
 	if opts.HydrationDir == constants.DefaultHydrationDir {
 		hydratedDir = filepath.Join(workingDir, constants.DefaultHydrationDir)
 		promptIfNeeded = false
@@ -164,37 +167,6 @@ func GroupVersionResource(disco discovery.DiscoveryInterface, gvk schema.GroupVe
 	return false, schema.GroupVersionResource{}, fmt.Errorf("could not find resource for %s", gvk.String())
 }
 
-func GetManifestsFromHydrationDir(ctx context.Context, opts config.SkaffoldOptions) (manifest.ManifestList, error) {
-	workDir, err := util.RealWorkDir()
-	if err != nil {
-		return nil, fmt.Errorf("getting working directory: %w", err)
-	}
-	hydrationDir, err := GetHydrationDir(config.SkaffoldOptions{HydrationDir: opts.HydrationDir, AssumeYes: opts.AssumeYes},
-		workDir,
-		true)
-	if err != nil {
-		return nil, fmt.Errorf("getting render output path: %w", err)
-	}
-	// TODO(aaron-prindle) verify that using manifests.yaml here will work in all cases
-	hydratedManifestPath := filepath.Join(hydrationDir, "manifests.yaml")
-	if _, err := os.Stat(hydratedManifestPath); errors.Is(err, os.ErrNotExist) {
-		log.Entry(ctx).Warn(fmt.Errorf("unable to find manifests, attempted location %s: %w", hydratedManifestPath, err))
-		return nil, nil
-	}
-
-	f, err := os.Open(hydratedManifestPath)
-	if err != nil {
-		return nil, fmt.Errorf("opening hydrated manifest at %s: %w", hydratedManifestPath, err)
-	}
-	defer f.Close()
-
-	manifests, err := manifest.Load(f)
-	if err != nil {
-		return nil, fmt.Errorf("parsing manifests file into manifest list object: %w", err)
-	}
-	return manifests, nil
-}
-
 func GetManifestsFromHydratedManifests(ctx context.Context, hydratedManifests []string) (manifest.ManifestList, error) {
 	var manifests manifest.ManifestList
 	for _, path := range hydratedManifests {
diff --git a/pkg/skaffold/helm/args.go b/pkg/skaffold/helm/args.go
index 46ce47267..2c05e967a 100644
--- a/pkg/skaffold/helm/args.go
+++ b/pkg/skaffold/helm/args.go
@@ -52,13 +52,17 @@ func ConstructOverrideArgs(r *latest.HelmRelease, builds []graph.Artifact, args
 
 	envMap := map[string]string{}
 	for idx, b := range builds {
-		suffix := ""
+		idxSuffix := ""
+		// replace commonly used image name chars that are illegal helm template chars "/" & "-" with "_"
+		namePrefix := strings.ReplaceAll(strings.ReplaceAll(b.ImageName, "-", "_"), "/", "_")
+
 		if idx > 0 {
-			suffix = strconv.Itoa(idx + 1)
+			idxSuffix = strconv.Itoa(idx + 1)
 		}
 
 		for k, v := range envVarForImage(b.ImageName, b.Tag) {
-			envMap[k+suffix] = v
+			envMap[k+idxSuffix] = v
+			envMap["."+namePrefix+"."+k] = v
 		}
 	}
 	log.Entry(context.TODO()).Debugf("EnvVarMap: %+v\n", envMap)
@@ -131,8 +135,9 @@ func envVarForImage(imageName string, digest string) map[string]string {
 		customMap["DIGEST_HEX"] = digest
 	}
 
-	// IMAGE_DOMAIN and IMAGE_REPO_NO_DOMAIN added for v2beta* 'helm+explicitRegistry' -> v3alpha* compatibility
+	// IMAGE_DOMAIN and IMAGE_REPO_NO_DOMAIN added for v2beta* 'helm+explicitRegistry' -> v3alpha* and beyond compatibility
 	customMap["IMAGE_DOMAIN"] = ref.Domain
 	customMap["IMAGE_REPO_NO_DOMAIN"] = strings.TrimPrefix(ref.BaseName, ref.Domain+"/")
+	customMap["IMAGE_FULLY_QUALIFIED"] = digest
 	return customMap
 }
diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go
index 274c50820..1d4d77a50 100644
--- a/pkg/skaffold/runner/new.go
+++ b/pkg/skaffold/runner/new.go
@@ -74,7 +74,7 @@ func NewForConfig(ctx context.Context, runCtx *runcontext.RunContext) (*Skaffold
 
 	var deployer deploy.Deployer
 
-	hydrationDir, err := util.GetHydrationDir(runCtx.Opts, runCtx.WorkingDir, true)
+	hydrationDir, err := util.GetHydrationDir(runCtx.Opts, runCtx.WorkingDir, true, isKptRendererOrDeployerUsed(runCtx.Pipelines))
 
 	if err != nil {
 		return nil, fmt.Errorf("getting render output path: %w", err)
@@ -253,3 +253,21 @@ func getTester(ctx context.Context, cfg test.Config, isLocalImage func(imageName
 
 	return tester, nil
 }
+
+func isKptRendererOrDeployerUsed(pipelines runcontext.Pipelines) bool {
+	for _, configName := range pipelines.AllOrderedConfigNames() {
+		pipeline := pipelines.GetForConfigName(configName)
+		renderConfig := pipeline.Render
+		deployConfig := pipeline.Deploy
+
+		if renderConfig.Validate != nil || renderConfig.Transform != nil || renderConfig.Kpt != nil {
+			return true
+		}
+
+		if deployConfig.KptDeploy != nil {
+			return true
+		}
+	}
+
+	return false
+}
diff --git a/pkg/skaffold/runner/runcontext/context.go b/pkg/skaffold/runner/runcontext/context.go
index a2d3a55a6..4eab76015 100644
--- a/pkg/skaffold/runner/runcontext/context.go
+++ b/pkg/skaffold/runner/runcontext/context.go
@@ -261,6 +261,18 @@ func (rc *RunContext) GetNamespace() string {
 	if rc.Opts.Namespace != "" {
 		return rc.Opts.Namespace
 	}
+	var defaultNamespace string
+	for _, p := range rc.GetPipelines() {
+		if p.Deploy.KubectlDeploy != nil && p.Deploy.KubectlDeploy.DefaultNamespace != nil {
+			if defaultNamespace != "" {
+				log.Entry(context.TODO()).Warn("multiple deploy.kubectl.defaultNamespace values set, only last pipeline's value will be used")
+			}
+			defaultNamespace = *p.Deploy.KubectlDeploy.DefaultNamespace
+		}
+	}
+	if defaultNamespace != "" {
+		return defaultNamespace
+	}
 	b, err := (&util.Commander{}).RunCmdOut(context.Background(), exec.Command("kubectl", "config", "view", "--minify", "-o", "jsonpath='{..namespace}'"))
 	if err != nil {
 		return rc.Opts.Namespace
diff --git a/pkg/skaffold/schema/util/util.go b/pkg/skaffold/schema/util/util.go
index 765478876..153733670 100644
--- a/pkg/skaffold/schema/util/util.go
+++ b/pkg/skaffold/schema/util/util.go
@@ -92,45 +92,34 @@ func (m *FlatMap) UnmarshalYAML(unmarshal func(interface{}) error) error {
 		return err
 	}
 	result := make(map[string]string)
-	if err := buildFlatMap(obj, result, ""); err != nil {
-		return err
-	}
+	buildFlatMap(obj, result, "")
 	*m = result
 	return nil
 }
 
-func buildFlatMap(obj map[string]interface{}, result map[string]string, currK string) (err error) {
-	var prevK string
-	for k, v := range obj {
-		prevK = currK
-		if currK == "" {
-			currK = fmt.Sprintf("%v", k)
-		} else {
-			currK = fmt.Sprintf("%v.%v", currK, k)
-		}
+func buildFlatList(list []interface{}, result map[string]string, parentK string) {
+	for idx, item := range list {
+		currK := fmt.Sprintf("%v[%d]", parentK, idx)
+		processItem(item, result, currK)
+	}
+}
 
-		switch v := v.(type) {
-		case map[string]interface{}:
-			if err = buildFlatMap(v, result, currK); err != nil {
-				return
-			}
-		case []interface{}:
-			for idx, i := range v {
-				if m, ok := i.(map[string]interface{}); ok {
-					currIdx := fmt.Sprintf("%v[%d]", currK, idx)
-					if err = buildFlatMap(m, result, currIdx); err != nil {
-						return
-					}
-				}
-			}
-		case string:
-			result[currK] = v
-		default:
-			result[currK] = fmt.Sprintf("%v", v)
-		}
-		currK = prevK
+func buildFlatMap(obj map[string]interface{}, result map[string]string, parentK string) {
+	for key, item := range obj {
+		currK := fmt.Sprintf("%v%v", parentK, key)
+		processItem(item, result, currK)
+	}
+}
+
+func processItem(item interface{}, result map[string]string, currK string) {
+	switch v := item.(type) {
+	case map[string]interface{}:
+		buildFlatMap(v, result, fmt.Sprintf("%v.", currK))
+	case []interface{}:
+		buildFlatList(v, result, currK)
+	default:
+		result[currK] = fmt.Sprintf("%v", v)
 	}
-	return err
 }
 
 func marshalInlineYaml(in interface{}) ([]byte, error) {
diff --git a/pkg/skaffold/schema/v2beta29/upgrade.go b/pkg/skaffold/schema/v2beta29/upgrade.go
index 02c06b303..1df297a2a 100644
--- a/pkg/skaffold/schema/v2beta29/upgrade.go
+++ b/pkg/skaffold/schema/v2beta29/upgrade.go
@@ -19,6 +19,7 @@ package v2beta29
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"strings"
 
 	"github.com/pkg/errors"
@@ -118,16 +119,30 @@ func upgradeOnePipeline(oldPipeline, newPipeline interface{}) error {
 			if oldPL.Deploy.HelmDeploy.Releases[i].ImageStrategy.HelmConventionConfig != nil {
 				// is 'helm' imageStrategy
 				for k, v := range aos {
-					svs[k+".repository"] = v
-					svs[k+".tag"] = v
+					if svts == nil {
+						svts = map[string]string{}
+					}
+
+					// replace commonly used image name chars that are illegal helm template chars "/" & "-" with "_"
+					validV := strings.ReplaceAll(strings.ReplaceAll(v, "-", "_"), "/", "_")
+
+					svts[k+".tag"] = fmt.Sprintf("{{.%s.IMAGE_TAG_}}@{{.%s.IMAGE_DIGEST}}", validV, validV)
+					svts[k+".repository"] = fmt.Sprintf("{{.%s.IMAGE_REPO}}", validV)
 					if oldPL.Deploy.HelmDeploy.Releases[i].ImageStrategy.HelmConventionConfig.ExplicitRegistry {
-						svs[k+".registry"] = v
+						// is 'helm' imageStrategy + explicitRegistry
+						svts[k+".registry"] = fmt.Sprintf("{{.%s.IMAGE_DOMAIN_}}", validV)
+						svts[k+".repository"] = fmt.Sprintf("{{.%s.IMAGE_REPO_NO_DOMAIN}}", validV)
 					}
 				}
 			} else {
 				// is 'fqn' imageStrategy
 				for k, v := range aos {
-					svs[k] = v
+					if svts == nil {
+						svts = map[string]string{}
+					}
+					// replace commonly used image name chars that are illegal helm template chars "/" & "-" with "_"
+					validV := strings.ReplaceAll(strings.ReplaceAll(v, "-", "_"), "/", "_")
+					svts[k] = fmt.Sprintf("{{.%s.IMAGE_FULLY_QUALIFIED}}", validV)
 				}
 			}
 			newPL.Render.Helm.Releases[i].SetValues = svs


Test patch:
diff --git a/integration/render_test.go b/integration/render_test.go
index 4549f277c..1394bfa07 100644
--- a/integration/render_test.go
+++ b/integration/render_test.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"os"
 	"path"
+	"path/filepath"
 	"regexp"
 	"testing"
 
@@ -705,3 +706,134 @@ spec:
 		})
 	}
 }
+
+func TestRenderHydrationDirCreation(t *testing.T) {
+	MarkIntegrationTest(t, CanRunWithoutGcp)
+	const hydrationDir = "hydration-dir"
+
+	tests := []struct {
+		description              string
+		shouldCreateHydrationDir bool
+		config                   string
+		manifest                 string
+	}{
+		{
+			description:              "project with kpt renderer should create hydration dir",
+			shouldCreateHydrationDir: true,
+			config: `
+apiVersion: skaffold/v4beta1
+kind: Config
+
+build:
+  artifacts: []
+manifests:
+  kpt: []
+`,
+			manifest: `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+spec:
+  containers:
+    - name: getting-started
+      image: skaffold-example`,
+		},
+		{
+			description:              "project with kpt deployer should create hydration dir",
+			shouldCreateHydrationDir: true,
+			config: `
+apiVersion: skaffold/v4beta1
+kind: Config
+
+build:
+  artifacts: []
+manifests:
+  kpt: []
+deploy:
+  kpt: {}
+`,
+			manifest: `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+spec:
+  containers:
+    - name: getting-started
+      image: skaffold-example`,
+		},
+		{
+			description:              "project with rawYaml and transform should create hydration dir (uses kpt renderer)",
+			shouldCreateHydrationDir: true,
+			config: `
+apiVersion: skaffold/v4beta1
+kind: Config
+
+build:
+  artifacts: []
+manifests:
+  rawYaml:
+    - k8s/k8s-pod.yaml
+  transform:
+    - name: set-annotations
+      configMap:
+        - "author:fake-author"
+`,
+			manifest: `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+spec:
+  containers:
+    - name: getting-started
+      image: skaffold-example`,
+		},
+		{
+			description:              "project without kpt should not create hydration dir",
+			shouldCreateHydrationDir: false,
+			config: `
+apiVersion: skaffold/v4beta1
+kind: Config
+
+build:
+  artifacts: []
+manifests:
+  rawYaml:
+    - k8s/k8s-pod.yaml
+`,
+			manifest: `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+spec:
+  containers:
+  - name: getting-started
+    image: skaffold-example`,
+		},
+	}
+
+	for _, test := range tests {
+		testutil.Run(t, test.description, func(t *testutil.T) {
+			tmpDir := t.NewTempDir()
+			tmpDir.Mkdir("k8s")
+			tmpDir.Write("skaffold.yaml", test.config)
+			tmpDir.Write("k8s/k8s-pod.yaml", test.manifest)
+			tmpDir.Chdir()
+
+			args := []string{"--hydration-dir", hydrationDir}
+
+			skaffold.Render(args...).RunOrFail(t.T)
+
+			_, err := os.Stat(filepath.Join(tmpDir.Root(), hydrationDir))
+
+			if test.shouldCreateHydrationDir {
+				t.CheckFalse(os.IsNotExist(err))
+			} else {
+				t.CheckTrue(os.IsNotExist(err))
+			}
+		})
+	}
+}
diff --git a/integration/testdata/helm-multi-config/skaffold/app1/skaffold.yml b/integration/testdata/helm-multi-config/skaffold/app1/skaffold.yml
index 3e7afcc70..5324b10be 100644
--- a/integration/testdata/helm-multi-config/skaffold/app1/skaffold.yml
+++ b/integration/testdata/helm-multi-config/skaffold/app1/skaffold.yml
@@ -14,6 +14,6 @@ manifests:
       - chartPath: ../../charts/app
         createNamespace: true
         name: app1
-        setValues:
-          image.repository: app1
-          image.tag: app1
+        setValueTemplates:
+          image.repository: '{{.app1.IMAGE_REPO}}'
+          image.tag: "{{.app1.IMAGE_TAG}}@{{.app1.IMAGE_DIGEST}}"
diff --git a/integration/testdata/helm-multi-config/skaffold/app2/skaffold.yml b/integration/testdata/helm-multi-config/skaffold/app2/skaffold.yml
index 062c31102..eac7e2063 100644
--- a/integration/testdata/helm-multi-config/skaffold/app2/skaffold.yml
+++ b/integration/testdata/helm-multi-config/skaffold/app2/skaffold.yml
@@ -14,6 +14,6 @@ manifests:
       - chartPath: ../../charts/app
         createNamespace: true
         name: app2
-        setValues:
-          image.repository: app2
-          image.tag: app2
\ No newline at end of file
+        setValueTemplates:
+          image.repository: '{{.app2.IMAGE_REPO}}'
+          image.tag: "{{.app2.IMAGE_TAG}}@{{.app2.IMAGE_DIGEST}}"
\ No newline at end of file
diff --git a/pkg/skaffold/deploy/util/util_test.go b/pkg/skaffold/deploy/util/util_test.go
index 191a66214..f9573b768 100644
--- a/pkg/skaffold/deploy/util/util_test.go
+++ b/pkg/skaffold/deploy/util/util_test.go
@@ -112,7 +112,7 @@ func TestGetHydrationDir_Default(t *testing.T) {
 		tmpDir.Chdir()
 		actual, err := GetHydrationDir(
 			config.SkaffoldOptions{HydrationDir: constants.DefaultHydrationDir, AssumeYes: true},
-			tmpDir.Root(), false)
+			tmpDir.Root(), false, true)
 		t.CheckNoError(err)
 		t.CheckDeepEqual(filepath.Join(tmpDir.Root(), ".kpt-pipeline"), actual)
 	})
@@ -124,7 +124,7 @@ func TestGetHydrationDir_CustomHydrationDir(t *testing.T) {
 		tmpDir.Chdir()
 		expected := filepath.Join(tmpDir.Root(), "test-hydration")
 		actual, err := GetHydrationDir(
-			config.SkaffoldOptions{HydrationDir: expected, AssumeYes: true}, "", false)
+			config.SkaffoldOptions{HydrationDir: expected, AssumeYes: true}, "", false, true)
 		t.CheckNoError(err)
 		t.CheckDeepEqual(expected, actual)
 		_, err = os.Stat(actual)
diff --git a/pkg/skaffold/schema/util/util_test.go b/pkg/skaffold/schema/util/util_test.go
index 9a4a41623..269ab39b1 100644
--- a/pkg/skaffold/schema/util/util_test.go
+++ b/pkg/skaffold/schema/util/util_test.go
@@ -174,3 +174,71 @@ values.val3[0].val9: foo4
   values.val3[0].val9: foo4
 `, string(out))
 }
+
+func TestFlatMap_UnmarshalYAMLNestedArrays(t *testing.T) {
+	inputYaml := `
+name: nameValue
+configs:
+  name: configName
+  defaults:
+    - prop1: bar1
+      prop2: foo1
+    - prop1: bar2
+      prop2: foo2
+  mixlist:
+    - prop1: bar3
+      prop2:
+       - foo3
+    - element2
+  matrix:
+    - - i1
+      - i2
+    - - j1
+      - j2
+`
+
+	expected := `name: nameValue
+configs.name: configName
+configs.defaults[0].prop1: bar1
+configs.defaults[0].prop2: foo1
+configs.defaults[1].prop1: bar2
+configs.defaults[1].prop2: foo2
+configs.mixlist[0].prop1: bar3
+configs.mixlist[0].prop2[0]: foo3
+configs.mixlist[1]: element2
+configs.matrix[0][0]: i1
+configs.matrix[0][1]: i2
+configs.matrix[1][0]: j1
+configs.matrix[1][1]: j2
+`
+	inputFlatmap := &FlatMap{}
+	expectedFlatmap := &FlatMap{}
+
+	err := yaml.Unmarshal([]byte(inputYaml), &inputFlatmap)
+	testutil.CheckError(t, false, err)
+
+	err = yaml.Unmarshal([]byte(expected), &expectedFlatmap)
+	testutil.CheckError(t, false, err)
+
+	testutil.CheckDeepEqual(t, *inputFlatmap, *expectedFlatmap)
+
+	out, err := yaml.Marshal(struct {
+		M *FlatMap `yaml:"value,omitempty"`
+	}{inputFlatmap})
+
+	testutil.CheckErrorAndDeepEqual(t, false, err, `value:
+  configs.defaults[0].prop1: bar1
+  configs.defaults[0].prop2: foo1
+  configs.defaults[1].prop1: bar2
+  configs.defaults[1].prop2: foo2
+  configs.matrix[0][0]: i1
+  configs.matrix[0][1]: i2
+  configs.matrix[1][0]: j1
+  configs.matrix[1][1]: j2
+  configs.mixlist[0].prop1: bar3
+  configs.mixlist[0].prop2[0]: foo3
+  configs.mixlist[1]: element2
+  configs.name: configName
+  name: nameValue
+`, string(out))
+}