Repository: GoogleContainerTools/skaffold
Pull request #8365 (2023-01-25 19:03:49)

feat: parameterization 
 Fixes: #8245, #7954

 Future work:  #8306, #8406, #8410  add kpt examples

### Description: 
 - move transformers responsibility out of specific kpt render as
   - transformers and validators are KRM functions, those functions should not be tied to a specific KRM function executors(kpt) in this case. We may support running krm functions without kpt(kustomize, or internal implementation).
   - ``Kustomize`` removes comments when building manifests,  the previous approach for using transformers doesn't work for manifests with comments. 
   -  creating extra hidden directory is not necessary, also it's contaminating users' workspace
   -  ``kpt render `` command outputs Kptfile content as part of render result unless using kpt with version under 1.0.0-beta.15 which is less preferred, this actually breaks krm function specs, and will cause `kubectl apply -f -` fail if piped with the render result. 
   - support parameterizations for all renders
     - kustomize
       - as kustomize build erases comments in the source manifests and we don't want to change users' source manifests either, we mirror kustomize directory to temporary folder and do transformation(apply-setters)  in a imperative way, then run kustomize build against the transformed manifests.
     - helm 
       - using helm native support approach ``--set flag``
       - This is not working if the render config is not directly set for manifests.helm,
     - kubectl  
     - kpt 
       - no extra folder, use ``kpt render`` command and output unwrapped result into memory, then running transformers against the output.
       - This currently doesn't work if kpt version is above 1.0.0-beta.15, will fix that in another pr. 


### Manual Test 
   - add kpt setters comment in your manifests, run ``skaffold render --set key1=value1 --set key2=value2``, should be able to see values with kpt setter comments are replaced by provided values from command line. 




 
  



codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) Report
> Merging [#8365](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (8bd8405) 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 `5.29%`.
> The diff coverage is `54.85%`.

```diff
@@            Coverage Diff             @@
##             main    #8365      +/-   ##
==========================================
- Coverage   70.48%   65.19%   -5.29%     
==========================================
  Files         515      607      +92     
  Lines       23150    30075    +6925     
==========================================
+ Hits        16317    19608    +3291     
- Misses       5776     8990    +3214     
- Partials     1057     1477     +420     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?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/8365?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/config/list.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY29uZmlnL2xpc3QuZ28=) | `65.21% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/config/set.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY29uZmlnL3NldC5nbw==) | `88.72% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/config/util.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY29uZmlnL3V0aWwuZ28=) | `54.28% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/credits.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY3JlZGl0cy5nbw==) | `100.00% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/credits/export.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?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/8365?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/generate\_pipeline.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZ2VuZXJhdGVfcGlwZWxpbmUuZ28=) | `60.00% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_modules.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF9tb2R1bGVzLmdv) | `65.00% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_profiles.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF9wcm9maWxlcy5nbw==) | `66.66% <ø> (ø)` | |
| ... and [417 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8365?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 54dff900d..704c5841a 100644
--- a/.github/workflows/integration-linux.yml
+++ b/.github/workflows/integration-linux.yml
@@ -19,7 +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]
+        kpt_version: [1.0.0-beta.24]
         gcloud_sdk_version: [410.0.0]
         container_structure_tests_version: [1.8.0]
         integration_test_partitions: [0, 1, 2, 3]
diff --git a/Makefile b/Makefile
index 7cdcced86..3d6bf2ebc 100644
--- a/Makefile
+++ b/Makefile
@@ -59,6 +59,8 @@ GO_LDFLAGS += -X $(VERSION_PACKAGE).buildDate=$(BUILD_DATE)
 GO_LDFLAGS += -X $(VERSION_PACKAGE).gitCommit=$(COMMIT)
 GO_LDFLAGS += -s -w
 
+GO_BUILD_TAGS = timetzdata
+
 GO_BUILD_TAGS_linux = osusergo netgo static_build release
 LDFLAGS_linux = -static
 
@@ -73,7 +75,7 @@ endif
 # when build for local development (`LOCAL=true make install` can skip license check)
 $(BUILD_DIR)/$(PROJECT): $(EMBEDDED_FILES_CHECK) $(GO_FILES) $(BUILD_DIR)
 	$(eval ldflags = $(GO_LDFLAGS) $(patsubst %,-extldflags \"%\",$(LDFLAGS_$(GOOS))))
-	$(eval tags = $(GO_BUILD_TAGS_$(GOOS)) $(GO_BUILD_TAGS_$(GOOS)_$(GOARCH)))
+	$(eval tags = $(GO_BUILD_TAGS) $(GO_BUILD_TAGS_$(GOOS)) $(GO_BUILD_TAGS_$(GOOS)_$(GOARCH)))
 	GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=1 \
 	    go build -gcflags="all=-N -l" -tags "$(tags)" -ldflags "$(ldflags)" -o $@ $(BUILD_PACKAGE)
 ifeq ($(GOOS),darwin)
@@ -95,7 +97,7 @@ $(BUILD_DIR)/$(PROJECT)-%: $(EMBEDDED_FILES_CHECK) $(GO_FILES) $(BUILD_DIR)
 	$(eval os = $(firstword $(subst -, ,$*)))
 	$(eval arch = $(lastword $(subst -, ,$(subst .exe,,$*))))
 	$(eval ldflags = $(GO_LDFLAGS) $(patsubst %,-extldflags \"%\",$(LDFLAGS_$(os))))
-	$(eval tags = $(GO_BUILD_TAGS_$(os)) $(GO_BUILD_TAGS_$(os)_$(arch)))
+	$(eval tags = $(GO_BUILD_TAGS) $(GO_BUILD_TAGS_$(os)) $(GO_BUILD_TAGS_$(os)_$(arch)))
 	GOOS=$(os) GOARCH=$(arch) CGO_ENABLED=1 go build -tags "$(tags)" -ldflags "$(ldflags)" -o $@ ./cmd/skaffold
 	(cd `dirname $@`; shasum -a 256 `basename $@`) | tee $@.sha256
 	file $@ || true
diff --git a/cmd/skaffold/app/cmd/cmd.go b/cmd/skaffold/app/cmd/cmd.go
index 3b4c23541..e4cadb0c8 100644
--- a/cmd/skaffold/app/cmd/cmd.go
+++ b/cmd/skaffold/app/cmd/cmd.go
@@ -23,6 +23,7 @@ import (
 	"os"
 	"strings"
 
+	"github.com/joho/godotenv"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 	"k8s.io/kubectl/pkg/util/templates"
@@ -208,6 +209,7 @@ func NewSkaffoldCommand(out, errOut io.Writer) *cobra.Command {
 	rootCmd.PersistentFlags().BoolVar(&timestamps, "timestamps", false, "Print timestamps in logs")
 	rootCmd.PersistentFlags().MarkHidden("force-colors")
 
+	setEnvVariablesFromFile()
 	setFlagsFromEnvVariables(rootCmd)
 
 	return rootCmd
@@ -225,6 +227,18 @@ func NewCmdOptions() *cobra.Command {
 	return cmd
 }
 
+// setEnvVariablesFromFile will read the `skaffold.env` file and load them into ENV for this process.
+func setEnvVariablesFromFile() {
+	if _, err := os.Stat(constants.SkaffoldEnvFile); os.IsNotExist(err) {
+		log.Entry(context.TODO()).Debugf("Skipped loading environment variables from file %q: %s", constants.SkaffoldEnvFile, err)
+		return
+	}
+	err := godotenv.Load(constants.SkaffoldEnvFile)
+	if err != nil {
+		log.Entry(context.TODO()).Warnf("Failed to load environment variables from file %q: %s", constants.SkaffoldEnvFile, err)
+	}
+}
+
 // Each flag can also be set with an env variable whose name starts with `SKAFFOLD_`.
 func setFlagsFromEnvVariables(rootCmd *cobra.Command) {
 	rootCmd.PersistentFlags().VisitAll(func(f *pflag.Flag) {
diff --git a/cmd/skaffold/app/cmd/flags.go b/cmd/skaffold/app/cmd/flags.go
index 99161eae3..e2d6f6b76 100644
--- a/cmd/skaffold/app/cmd/flags.go
+++ b/cmd/skaffold/app/cmd/flags.go
@@ -746,6 +746,13 @@ The build result from a previous 'skaffold build --file-output' run can be used
 		DefValue:      false,
 		FlagAddMethod: "BoolVar",
 		DefinedOn:     []string{"dev", "debug"},
+	}, {
+		Name:          "set",
+		Usage:         "overrides templated manifest fields by provided key-value pairs",
+		Value:         &opts.ManifestsOverrides,
+		DefValue:      []string{},
+		FlagAddMethod: "StringSliceVar",
+		DefinedOn:     []string{"render"},
 	},
 }
 
diff --git a/docs-v2/content/en/docs/environment/_index.md b/docs-v2/content/en/docs/environment/_index.md
index 5aefddf71..d96d51d05 100644
--- a/docs-v2/content/en/docs/environment/_index.md
+++ b/docs-v2/content/en/docs/environment/_index.md
@@ -8,6 +8,7 @@ no_list: true
 
 | Environment Management with Skaffold | |
 |----------|---|
+| [Environment Variables File]({{< relref "env-file.md" >}}) | Loading environment variables from a file |
 | [Image Registry Handling]({{< relref "image-registries.md" >}}) | Controlling where your images are pushed |
 | [kube-context]({{< relref "kube-context.md" >}}) | Managing the active Kubernetes context for your cluster |
 | [Local Cluster]({{< relref "local-cluster.md" >}}) | Offline development with Skaffold and Minikube |
diff --git a/docs-v2/content/en/docs/environment/env-file.md b/docs-v2/content/en/docs/environment/env-file.md
new file mode 100644
index 000000000..dd6dd70ca
--- /dev/null
+++ b/docs-v2/content/en/docs/environment/env-file.md
@@ -0,0 +1,38 @@
+---
+title: "Load environment variables from a file"
+linkTitle: "Load ENV from a file"
+weight: 50
+aliases: [/docs/concepts/env_file]
+---
+
+In Skaffold, a `skaffold.env` file can be defined in the project root directory to specify environment variables that Skaffold will load into the process. This provides a more organized and manageable way of setting environment variables, rather than passing them as command line arguments.
+
+The `skaffold.env` file should be in the format of `KEY=value` pairs, with one pair per line. Skaffold will automatically load these variables into the environment before running any commands.
+
+Here is an example `skaffold.env` file:
+
+```txt
+ENV_VAR_1=value1
+ENV_VAR_2=value2
+```
+
+{{< alert title="Note" >}}
+Values set in a `skaffold.env` file will not overwrite existing environment variables in the process.
+{{< /alert >}}
+
+### Setting Skaffold Flags with Environment Variables
+
+In addition to loading environment variables from the `skaffold.env` file, Skaffold also allows users to set flags using environment variables. To set a flag using an environment variable, use the `SKAFFOLD_` prefix and convert the flag name to uppercase.
+
+For example, to set the `--cache-artifacts` flag to `true`, the equivalent environment variable would be `SKAFFOLD_CACHE_ARTIFACTS=true`.
+
+Here is an example usage in the `skaffold.env` file:
+
+```txt
+SKAFFOLD_CACHE_ARTIFACTS=true
+SKAFFOLD_NAMESPACE=mynamespace
+```
+
+{{< alert title="Note" >}}
+If a flag is set both in the `skaffold.env` file and as a command line argument, the value specified in the command line argument will take precedence.
+{{< /alert >}}
diff --git a/docs-v2/content/en/docs/references/cli/_index.md b/docs-v2/content/en/docs/references/cli/_index.md
index 183f0d960..bcbf7a7c7 100644
--- a/docs-v2/content/en/docs/references/cli/_index.md
+++ b/docs-v2/content/en/docs/references/cli/_index.md
@@ -1041,6 +1041,7 @@ The build result from a previous 'skaffold build --file-output' run can be used
       --propagate-profiles=true: Setting '--propagate-profiles=false' disables propagating profiles set by the '--profile' flag across config dependencies. This mean that only profiles defined directly in the target 'skaffold.yaml' file are activated.
       --remote-cache-dir='': Specify the location of the git repositories cache (default $HOME/.skaffold/repos)
       --resource-selector-rules-file='': Path to JSON file specifying the deny list of yaml objects for skaffold to NOT transform with 'image' and 'label' field replacements.  NOTE: this list is additive to skaffold's default denylist and denylist has priority over allowlist
+      --set=[]: overrides templated manifest fields by provided key-value pairs
       --sync-remote-cache='always': Controls how Skaffold manages the remote config cache (see `remote-cache-dir`). One of `always` (default), `missing`, or `never`. `always` syncs remote repositories to latest on access. `missing` only clones remote repositories if they do not exist locally. `never` means the user takes responsibility for updating remote repositories.
   -t, --tag='': The optional custom tag to use for images which overrides the current Tagger configuration
       --wait-for-connection=false: Blocks ending execution of skaffold until the /v2/events gRPC/HTTP endpoint is hit
@@ -1075,6 +1076,7 @@ Env vars:
 * `SKAFFOLD_PROPAGATE_PROFILES` (same as `--propagate-profiles`)
 * `SKAFFOLD_REMOTE_CACHE_DIR` (same as `--remote-cache-dir`)
 * `SKAFFOLD_RESOURCE_SELECTOR_RULES_FILE` (same as `--resource-selector-rules-file`)
+* `SKAFFOLD_SET` (same as `--set`)
 * `SKAFFOLD_SYNC_REMOTE_CACHE` (same as `--sync-remote-cache`)
 * `SKAFFOLD_TAG` (same as `--tag`)
 * `SKAFFOLD_WAIT_FOR_CONNECTION` (same as `--wait-for-connection`)
diff --git a/docs-v2/content/en/docs/workflows/debug.md b/docs-v2/content/en/docs/workflows/debug.md
index 249f1954a..c2c0726db 100644
--- a/docs-v2/content/en/docs/workflows/debug.md
+++ b/docs-v2/content/en/docs/workflows/debug.md
@@ -16,7 +16,7 @@ Debugging is currently supported for five language runtimes.
   - Python 3.5+ (runtime ID: `python`) using `debugpy` (Debug Adapter Protocol) or `pydevd`
   - .NET Core (runtime ID: `netcore`) using `vsdbg` (only for VS Code)
 
-Skaffold can usually detect the correct language runtime if present. However if you encounter difficulties then checkout the [Supported Language Runtimes]({{< relref "#supported-language-runtimes">}}) section for the exact heuristics that Skaffold uses and you can modify your application accordingly, or read about [how you can manually configure your container image]({{relref "#can-images-be-debugged-without-the-runtime-support-images"}}).
+Skaffold can usually detect the correct language runtime if present. However if you encounter difficulties then checkout the [Supported Language Runtimes]({{< relref "#supported-language-runtimes">}}) section for the exact heuristics that Skaffold uses and you can modify your application accordingly, or read about [how you can manually configure your container image]({{< relref "#can-images-be-debugged-without-the-runtime-support-images" >}}).
 
 ## (Recommended) Debugging using Cloud Code
 
@@ -426,7 +426,7 @@ binary or launch script (i.e., a `pip`-generated launcher script such as `gunico
 Skaffold cannot debug Go and Python applications that use user-created
 shell scripts, or that use shell constructs like `exec` or `eval`.
 Either rewrite your container image command-line or
-[manually configure your container for debugging]({{ relref "#can-images-be-debugged-without-the-runtime-support-images" }}).
+[manually configure your container for debugging]({{< relref "#can-images-be-debugged-without-the-runtime-support-images" >}}).
 For example:
 ```
 ❌ CMD launch.sh
diff --git a/examples/getting-started-kustomize/skaffold.yaml b/examples/getting-started-kustomize/skaffold.yaml
index 5b17326c3..6d4160528 100644
--- a/examples/getting-started-kustomize/skaffold.yaml
+++ b/examples/getting-started-kustomize/skaffold.yaml
@@ -24,4 +24,4 @@ profiles:
   manifests:
     kustomize:
       paths:
-      - overlays/staging
+      - overlays/staging
\ No newline at end of file
diff --git a/examples/kustomize/kustomization.yaml b/examples/kustomize/kustomization.yaml
index 88b1320b5..b8ccd4d90 100644
--- a/examples/kustomize/kustomization.yaml
+++ b/examples/kustomize/kustomization.yaml
@@ -1,4 +1,4 @@
 resources:
   - deployment.yaml
-patches:
+patchesStrategicMerge:
   - patch.yaml
diff --git a/examples/kustomize/skaffold.yaml b/examples/kustomize/skaffold.yaml
index 35e54e30e..9144247c6 100644
--- a/examples/kustomize/skaffold.yaml
+++ b/examples/kustomize/skaffold.yaml
@@ -3,4 +3,4 @@ kind: Config
 manifests:
   kustomize:
     paths:
-      - .
+      - .
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 113e1ad34..8aecda84f 100644
--- a/go.mod
+++ b/go.mod
@@ -64,6 +64,7 @@ require (
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
 	github.com/heroku/color v0.0.6
 	github.com/imdario/mergo v0.3.12
+	github.com/joho/godotenv v1.4.0
 	github.com/karrick/godirwalk v1.16.1
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
 	github.com/krishicks/yaml-patch v0.0.10
@@ -111,6 +112,7 @@ require (
 	k8s.io/kubectl v0.21.6
 	k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b
 	sigs.k8s.io/cli-utils v0.22.0
+	sigs.k8s.io/kustomize/api v0.8.8
 	sigs.k8s.io/kustomize/kyaml v0.10.17
 	sigs.k8s.io/yaml v1.3.0
 )
@@ -158,19 +160,12 @@ require (
 	github.com/aws/smithy-go v1.10.0 // indirect
 	github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220216180153-3d7835abdf40 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
-	github.com/bits-and-blooms/bitset v1.2.0 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
-	github.com/checkpoint-restore/go-criu/v5 v5.3.0 // indirect
 	github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21 // indirect
-	github.com/cilium/ebpf v0.7.0 // indirect
 	github.com/containerd/cgroups v1.0.1 // indirect
-	github.com/containerd/console v1.0.3 // indirect
 	github.com/containerd/stargz-snapshotter/estargz v0.11.3 // indirect
 	github.com/containerd/ttrpc v1.1.0 // indirect
 	github.com/containerd/typeurl v1.0.2 // indirect
-	github.com/coreos/go-systemd/v22 v22.3.2 // indirect
-	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
-	github.com/cyphar/filepath-securejoin v0.2.3 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dimchansky/utfbom v1.1.1 // indirect
 	github.com/docker/docker-credential-helpers v0.6.4 // indirect
@@ -194,7 +189,6 @@ require (
 	github.com/go-openapi/spec v0.20.4 // indirect
 	github.com/go-openapi/swag v0.19.15 // indirect
 	github.com/gobuffalo/here v0.6.0 // indirect
-	github.com/godbus/dbus/v5 v5.0.6 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
@@ -233,9 +227,7 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
 	github.com/morikuni/aec v1.0.0 // indirect
-	github.com/mrunalp/fileutils v0.5.0 // indirect
 	github.com/opencontainers/runc v1.1.2 // indirect
-	github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
 	github.com/opencontainers/selinux v1.10.0 // indirect
 	github.com/pelletier/go-toml v1.9.4 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -247,7 +239,6 @@ require (
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/russross/blackfriday v1.5.2 // indirect
 	github.com/sabhiram/go-gitignore v0.0.0-20201211074657-223ce5d391b0 // indirect
-	github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 // indirect
 	github.com/segmentio/encoding v0.2.7 // indirect
 	github.com/sergi/go-diff v1.2.0 // indirect
 	github.com/sigstore/cosign v1.3.2-0.20211120003522-90e2dcfe7b92 // indirect
@@ -258,12 +249,8 @@ require (
 	github.com/src-d/gcfg v1.4.0 // indirect
 	github.com/stretchr/testify v1.7.0 // indirect
 	github.com/subosito/gotenv v1.2.0 // indirect
-	github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
 	github.com/theupdateframework/go-tuf v0.0.0-20210722233521-90e262754396 // indirect
-	github.com/urfave/cli v1.22.5 // indirect
 	github.com/vbatts/tar-split v0.11.2 // indirect
-	github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 // indirect
-	github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
 	github.com/xanzy/ssh-agent v0.3.0 // indirect
 	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
diff --git a/go.sum b/go.sum
index 417eab07f..c14d8b050 100644
--- a/go.sum
+++ b/go.sum
@@ -434,7 +434,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
-github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
 github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
 github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
 github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
@@ -493,7 +492,6 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
 github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
 github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
 github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
-github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8=
 github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
 github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21 h1:XlpL9EHrPOBJMLDDOf35/G4t5rGAFNNAZQ3cDcWavtc=
 github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21/go.mod h1:Zlre/PVxuSI9y6/UV4NwGixQ48RHQDSPiUkofr6rbMU=
@@ -505,7 +503,6 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLI
 github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
 github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
 github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
-github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k=
 github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
@@ -551,7 +548,6 @@ github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4g
 github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
 github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
 github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
-github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
 github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
 github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
 github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
@@ -650,21 +646,17 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
 github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
 github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -673,7 +665,6 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ
 github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
 github.com/cyberphone/json-canonicalization v0.0.0-20210823021906-dc406ceaf94b/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
 github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
-github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
 github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
 github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
@@ -1060,11 +1051,9 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E
 github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
-github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
 github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
 github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
 github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
@@ -1463,6 +1452,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
 github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
 github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
+github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -1675,7 +1666,6 @@ github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7s
 github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
 github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
 github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
-github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
 github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
 github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI=
 github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
@@ -1703,7 +1693,6 @@ github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:
 github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
 github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
 github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
-github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=
 github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -1780,7 +1769,6 @@ github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h
 github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE=
 github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
-github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg=
 github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
 github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw=
 github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
@@ -1790,12 +1778,10 @@ github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.m
 github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
 github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
 github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
-github.com/opencontainers/selinux v1.8.2 h1:c4ca10UMgRcvZ6h0K4HtS15UaVSBEaE+iln2LVpAuGc=
 github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
 github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU=
 github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
@@ -1976,7 +1962,6 @@ github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
 github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
-github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 h1:58EBmR2dMNL2n/FnbQewK3D14nXr0V9CObDSvMJLq+Y=
 github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
 github.com/secure-systems-lab/go-securesystemslib v0.1.0/go.mod h1:eIjBmIP8LD2MLBL/DkQWayLiz006Q4p+hCu79rvWleY=
 github.com/securego/gosec v0.0.0-20200103095621-79fbf3af8d83/go.mod h1:vvbZ2Ae7AzSq3/kywjUDxSNq2SJ27RxCz2un0H3ePqE=
@@ -2122,7 +2107,6 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
@@ -2179,7 +2163,6 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
 github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
 github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
@@ -2197,11 +2180,9 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u
 github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
 github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
 github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
-github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA=
 github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
 github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
 github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
-github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
 github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
 github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
 github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
@@ -3441,6 +3422,7 @@ sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2
 sigs.k8s.io/kind v0.12.0 h1:LFynXwQkH1MrWI8pM1FQty0oUwEKjU5EkMaVZaPld8E=
 sigs.k8s.io/kind v0.12.0/go.mod h1:EcgDSBVxz8Bvm19fx8xkioFrf9dC30fMJdOTXBSGNoM=
 sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
+sigs.k8s.io/kustomize/api v0.8.8 h1:G2z6JPSSjtWWgMeWSoHdXqyftJNmMmyxXpwENGoOtGE=
 sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY=
 sigs.k8s.io/kustomize/cmd/config v0.9.10/go.mod h1:Mrby0WnRH7hA6OwOYnYpfpiY0WJIMgYrEDfwOeFdMK0=
 sigs.k8s.io/kustomize/kustomize/v4 v4.1.2/go.mod h1:PxBvo4WGYlCLeRPL+ziT64wBXqbgfcalOS/SXa/tcyo=
diff --git a/integration/examples/README.md b/integration/examples/README.md
index 14fd5c948..5bad94922 100644
--- a/integration/examples/README.md
+++ b/integration/examples/README.md
@@ -22,10 +22,11 @@ When deploying to a [local cluster](https://skaffold.dev/docs/environment/local-
 
 ## Deploying to a remote cluster
 
-When deploying to a remote cluster you have to point Skaffold to your default image repository in one of the four ways:
+When deploying to a remote cluster you have to point Skaffold to your default image repository in one of the five ways:
 
 * flag: `skaffold dev --default-repo <myrepo>`
 * env var: `SKAFFOLD_DEFAULT_REPO=<myrepo> skaffold dev`
+* env var file: create a file `skaffold.env` in the project root, with the line `SKAFFOLD_DEFAULT_REPO=<myrepo>`
 * global skaffold config (one time): `skaffold config set --global default-repo <myrepo>`
 * skaffold config for current kubectl context: `skaffold config set default-repo <myrepo>`
 
diff --git a/integration/examples/using-env-file/Dockerfile b/integration/examples/using-env-file/Dockerfile
new file mode 100644
index 000000000..af76a83ae
--- /dev/null
+++ b/integration/examples/using-env-file/Dockerfile
@@ -0,0 +1,11 @@
+FROM golang:1.18 as builder
+WORKDIR /code
+COPY main.go .
+COPY go.mod .
+RUN go build -trimpath -o /app main.go
+
+FROM alpine:3
+ARG MESSAGE_ARG
+ENV MESSAGE=$MESSAGE_ARG
+CMD ["./app"]
+COPY --from=builder /app .
diff --git a/integration/examples/using-env-file/README.md b/integration/examples/using-env-file/README.md
new file mode 100644
index 000000000..3fb3bd55b
--- /dev/null
+++ b/integration/examples/using-env-file/README.md
@@ -0,0 +1,4 @@
+### Example: Reading environment variables from a file into the Skaffold process
+[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/GoogleContainerTools/skaffold&cloudshell_open_in_editor=README.md&cloudshell_workspace=examples/using-env-file)
+
+This is a simple example to show how an environment variable can be read from a `skaffold.env` file and propagated to the running container.
diff --git a/integration/examples/using-env-file/go.mod b/integration/examples/using-env-file/go.mod
new file mode 100644
index 000000000..bc78b13a6
--- /dev/null
+++ b/integration/examples/using-env-file/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/getting-started
+
+go 1.18
diff --git a/integration/examples/using-env-file/k8s-pod.yaml b/integration/examples/using-env-file/k8s-pod.yaml
new file mode 100644
index 000000000..541e0fc7c
--- /dev/null
+++ b/integration/examples/using-env-file/k8s-pod.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: example-app
+spec:
+  containers:
+  - name: example-app
+    image: skaffold-example
diff --git a/integration/examples/using-env-file/main.go b/integration/examples/using-env-file/main.go
new file mode 100644
index 000000000..37f5265e8
--- /dev/null
+++ b/integration/examples/using-env-file/main.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"time"
+)
+
+func main() {
+	message := os.Getenv("MESSAGE")
+	for {
+		fmt.Println(message)
+		time.Sleep(time.Second * 1)
+	}
+}
diff --git a/integration/examples/using-env-file/skaffold.env b/integration/examples/using-env-file/skaffold.env
new file mode 100644
index 000000000..a8b71e074
--- /dev/null
+++ b/integration/examples/using-env-file/skaffold.env
@@ -0,0 +1,1 @@
+MESSAGE=Hello from Skaffold!
diff --git a/integration/examples/using-env-file/skaffold.yaml b/integration/examples/using-env-file/skaffold.yaml
new file mode 100644
index 000000000..a14538a8c
--- /dev/null
+++ b/integration/examples/using-env-file/skaffold.yaml
@@ -0,0 +1,11 @@
+apiVersion: skaffold/v4beta2
+kind: Config
+build:
+  artifacts:
+  - image: skaffold-example
+    docker:
+      buildArgs:
+        MESSAGE_ARG: '{{.MESSAGE}}'
+manifests:
+  rawYaml:
+  - k8s-pod.yaml
diff --git a/pkg/skaffold/config/options.go b/pkg/skaffold/config/options.go
index 85d3b84bf..557b254a4 100644
--- a/pkg/skaffold/config/options.go
+++ b/pkg/skaffold/config/options.go
@@ -109,6 +109,7 @@ type SkaffoldOptions struct {
 	DefaultRepo                 StringOrUndefined
 	SyncRemoteCache             SyncRemoteCacheOption
 	WaitForDeletions            WaitForDeletions
+	ManifestsOverrides          []string
 }
 
 type RunMode string
diff --git a/pkg/skaffold/constants/constants.go b/pkg/skaffold/constants/constants.go
index 650ba1e6a..8cad38a0d 100644
--- a/pkg/skaffold/constants/constants.go
+++ b/pkg/skaffold/constants/constants.go
@@ -57,6 +57,9 @@ const (
 	DefaultCacheFile   = "cache"
 	DefaultMetricFile  = "metrics"
 
+	// SkaffoldEnvFile is the file that is parsed to set environment variables in the process
+	SkaffoldEnvFile = "skaffold.env"
+
 	DefaultPortForwardAddress = "127.0.0.1"
 
 	DefaultProjectDescriptor = "project.toml"
diff --git a/pkg/skaffold/deploy/helm/args.go b/pkg/skaffold/deploy/helm/args.go
index e3e0513ac..163950586 100644
--- a/pkg/skaffold/deploy/helm/args.go
+++ b/pkg/skaffold/deploy/helm/args.go
@@ -93,7 +93,7 @@ func (h *Deployer) installArgs(r latest.HelmRelease, builds []graph.Artifact, o
 		args = append(args, "--create-namespace")
 	}
 
-	args, err := helm.ConstructOverrideArgs(&r, builds, args)
+	args, err := helm.ConstructOverrideArgs(&r, builds, args, nil)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/skaffold/deploy/kubectl/kubectl.go b/pkg/skaffold/deploy/kubectl/kubectl.go
index 72aa16249..160219975 100644
--- a/pkg/skaffold/deploy/kubectl/kubectl.go
+++ b/pkg/skaffold/deploy/kubectl/kubectl.go
@@ -86,7 +86,7 @@ type Deployer struct {
 // with the needed configuration for `kubectl apply`
 func NewDeployer(cfg Config, labeller *label.DefaultLabeller, d *latest.KubectlDeploy, artifacts []*latest.Artifact, configName string) (*Deployer, error) {
 	defaultNamespace := ""
-	b, err := (&util.Commander{}).RunCmdOut(context.Background(), exec.Command("kubectl", "config", "view", "--minify", "-o", "jsonpath='{..namespace}'"))
+	b, err := util.RunCmdOutOnce(context.TODO(), exec.Command("kubectl", "config", "view", "--minify", "-o", "jsonpath='{..namespace}'"))
 	if err == nil {
 		defaultNamespace = strings.Trim(string(b), "'")
 		if defaultNamespace == "default" {
diff --git a/pkg/skaffold/helm/args.go b/pkg/skaffold/helm/args.go
index e4ce5742c..3357cd6fa 100644
--- a/pkg/skaffold/helm/args.go
+++ b/pkg/skaffold/helm/args.go
@@ -35,7 +35,7 @@ import (
 )
 
 // ConstructOverrideArgs creates the command line arguments for overrides
-func ConstructOverrideArgs(r *latest.HelmRelease, builds []graph.Artifact, args []string) ([]string, error) {
+func ConstructOverrideArgs(r *latest.HelmRelease, builds []graph.Artifact, args []string, manifestOverrides map[string]string) ([]string, error) {
 	for _, k := range maps.SortKeys(r.SetValues) {
 		args = append(args, "--set", fmt.Sprintf("%s=%s", k, r.SetValues[k]))
 	}
@@ -98,6 +98,11 @@ func ConstructOverrideArgs(r *latest.HelmRelease, builds []graph.Artifact, args
 
 		args = append(args, "-f", exp)
 	}
+
+	for _, k := range maps.SortKeys(manifestOverrides) {
+		args = append(args, "--set", fmt.Sprintf("%s=%s", k, manifestOverrides[k]))
+	}
+
 	return args, nil
 }
 
diff --git a/pkg/skaffold/render/generate/generate.go b/pkg/skaffold/render/generate/generate.go
index f3e7a4758..21cdc2479 100644
--- a/pkg/skaffold/render/generate/generate.go
+++ b/pkg/skaffold/render/generate/generate.go
@@ -22,14 +22,11 @@ import (
 	"fmt"
 	"io"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"strings"
 
-	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/constants"
 	sErrors "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/errors"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/event"
-	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/instrumentation"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubectl"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubernetes"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubernetes/manifest"
@@ -122,53 +119,6 @@ func resolveRemoteAndLocal(paths []string, workdir string) ([]string, error) {
 func (g Generator) Generate(ctx context.Context, out io.Writer) (manifest.ManifestList, error) {
 	var manifests manifest.ManifestList
 
-	// Generate kustomize Manifests
-	_, endTrace := instrumentation.StartTrace(ctx, "Render_kustomize")
-	if g.config.Kustomize != nil && len(g.config.Kustomize.Paths) != 0 {
-		log.Entry(ctx).Infof("rendering using kustomize")
-		kustomizeManifests, err := g.generateKustomizeManifests(ctx)
-		if err != nil {
-			return nil, err
-		}
-		for _, m := range kustomizeManifests {
-			manifests.Append(m)
-		}
-	}
-	endTrace()
-
-	// Generate in-place hydrated kpt Manifests
-	_, endTrace = instrumentation.StartTrace(ctx, "Render_expandGlobKptManifests")
-	kptPaths, err := resolveRemoteAndLocal(g.config.Kpt, g.workingDir)
-	if err != nil {
-		event.DeployInfoEvent(fmt.Errorf("could not expand the glob kpt manifests: %w", err))
-		return nil, err
-	}
-	endTrace()
-	kptPathMap := make(map[string]bool)
-	for _, path := range kptPaths {
-		if dir, ok := isKptDir(path); ok {
-			kptPathMap[dir] = true
-		}
-	}
-	var kptManifests []string
-	if len(kptPathMap) != 0 {
-		log.Entry(ctx).Infof("rendering using kpt")
-	}
-	for kPath := range kptPathMap {
-		// kpt manifests will be hydrated and stored in the subdir of the hydrated dir, where the subdir name
-		// matches the kPath dir name.
-		outputDir := filepath.Join(g.hydrationDir, filepath.Base(kPath))
-		tCtx, endTrace := instrumentation.StartTrace(ctx, "Render_generateKptManifests")
-		cmd := exec.CommandContext(tCtx, "kpt", "fn", "render", kPath,
-			fmt.Sprintf("--output=%v", outputDir))
-		cmd.Stderr = out
-		if err = util.RunCmd(ctx, cmd); err != nil {
-			endTrace(instrumentation.TraceEndError(err))
-			return nil, err
-		}
-		kptManifests = append(kptManifests, outputDir)
-	}
-
 	// Generate Raw Manifests
 	sourceManifests, err := resolveRemoteAndLocal(g.config.RawK8s, g.workingDir)
 	if err != nil {
@@ -176,8 +126,7 @@ func (g Generator) Generate(ctx context.Context, out io.Writer) (manifest.Manife
 		return nil, err
 	}
 
-	hydratedManifests := append(sourceManifests, kptManifests...)
-	for _, nkPath := range hydratedManifests {
+	for _, nkPath := range sourceManifests {
 		if !kubernetes.HasKubernetesFileExtension(nkPath) {
 			if !stringslice.Contains(g.config.RawK8s, nkPath) {
 				log.Entry(ctx).Infof("refusing to deploy/delete non {json, yaml} file %s", nkPath)
@@ -225,109 +174,6 @@ func (g Generator) readRemoteManifest(ctx context.Context, rm latest.RemoteManif
 	return manifest.Bytes(), nil
 }
 
-func (g Generator) generateKustomizeManifests(ctx context.Context) ([][]byte, error) {
-	var manifests [][]byte
-
-	_, endTrace := instrumentation.StartTrace(ctx, "Render_expandGlobKustomizeManifests")
-	kustomizePaths, err := resolveRemoteAndLocal(g.config.Kustomize.Paths, g.workingDir)
-	if err != nil {
-		event.DeployInfoEvent(fmt.Errorf("could not expand the glob kustomize manifests: %w", err))
-		return nil, err
-	}
-	endTrace()
-	kustomizePathMap := make(map[string]bool)
-	for _, path := range kustomizePaths {
-		if dir, ok := isKustomizeDir(path); ok {
-			kustomizePathMap[dir] = true
-		}
-	}
-
-	kCLI := kubectl.NewCLI(kCfg{}, "")
-	useKubectlKustomize := !KustomizeBinaryCheck() && KubectlVersionCheck(kCLI)
-
-	for kPath := range kustomizePathMap {
-		var out []byte
-		var err error
-		if useKubectlKustomize {
-			out, err = kCLI.Kustomize(ctx, kustomizeBuildArgs(g.config.Kustomize.BuildArgs, kPath))
-		} else {
-			cmd := exec.CommandContext(ctx, "kustomize", append([]string{"build"}, kustomizeBuildArgs(g.config.Kustomize.BuildArgs, kPath)...)...)
-			out, err = util.RunCmdOut(ctx, cmd)
-		}
-		if err != nil {
-			return nil, err
-		}
-		if len(out) == 0 {
-			continue
-		}
-		manifests = append(manifests, out)
-	}
-
-	return manifests, nil
-}
-
-// isKustomizeDir checks if the path is managed by kustomize. A more reliable approach is parsing the kustomize content
-// resources, bases, overlays. However, this switches the manifests parsing from kustomize/kpt to skaffold. To avoid
-// skaffold render.generate mis-use, we expect the users do not place non-kustomize manifests under the kustomization.yaml directory, so as the kpt manifests.
-func isKustomizeDir(path string) (string, bool) {
-	fileInfo, err := os.Stat(path)
-	if err != nil {
-		return "", false
-	}
-	var dir string
-	switch mode := fileInfo.Mode(); {
-	// TODO: Check if regular file contains kpt functions. if so, we may want to abstract that info as well.
-	case mode.IsDir():
-		dir = path
-	case mode.IsRegular():
-		dir = filepath.Dir(path)
-	}
-
-	for _, base := range constants.KustomizeFilePaths {
-		if _, err := os.Stat(filepath.Join(dir, base)); os.IsNotExist(err) {
-			continue
-		}
-		return dir, true
-	}
-	return "", false
-}
-
-// kustomizeBuildArgs returns a list of build args to be passed to kustomize build.
-func kustomizeBuildArgs(buildArgs []string, kustomizePath string) []string {
-	var args []string
-
-	if len(buildArgs) > 0 {
-		for _, v := range buildArgs {
-			parts := strings.Split(v, " ")
-			args = append(args, parts...)
-		}
-	}
-
-	if len(kustomizePath) > 0 {
-		args = append(args, kustomizePath)
-	}
-
-	return args
-}
-
-func isKptDir(path string) (string, bool) {
-	fileInfo, err := os.Stat(path)
-	if err != nil {
-		return "", false
-	}
-	var dir string
-	switch mode := fileInfo.Mode(); {
-	case mode.IsDir():
-		dir = path
-	case mode.IsRegular():
-		dir = filepath.Dir(path)
-	}
-	if _, err := os.Stat(filepath.Join(dir, kptfile.KptFileName)); os.IsNotExist(err) {
-		return "", false
-	}
-	return dir, true
-}
-
 // walkLocalManifests finds out all the manifests from the `.manifests.generate`, so they can be registered in the file watcher.
 // Note: the logic about manifest dependencies shall separate from the "Generate" function, which requires "context" and
 // only be called when a rendering action is needed (normally happens after the file watcher registration).
diff --git a/pkg/skaffold/render/renderer/helm/helm.go b/pkg/skaffold/render/renderer/helm/helm.go
index 3e1543114..c2c4362f0 100644
--- a/pkg/skaffold/render/renderer/helm/helm.go
+++ b/pkg/skaffold/render/renderer/helm/helm.go
@@ -51,6 +51,7 @@ type Helm struct {
 	enableDebug       bool
 	overrideProtocols []string
 
+	manifestOverrides  map[string]string
 	transformAllowlist map[apimachinery.GroupKind]latest.ResourceFilter
 	transformDenylist  map[apimachinery.GroupKind]latest.ResourceFilter
 }
@@ -63,7 +64,7 @@ func (h Helm) KubeConfig() string          { return h.kubeConfig }
 func (h Helm) Labels() map[string]string   { return h.labels }
 func (h Helm) GlobalFlags() []string       { return h.config.Flags.Global }
 
-func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string, configName string) (Helm, error) {
+func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string, configName string, manifestOverrides map[string]string) (Helm, error) {
 	generator := generate.NewGenerator(cfg.GetWorkingDir(), rCfg.Generate, "")
 	transformAllowlist, transformDenylist, err := util.ConsolidateTransformConfiguration(cfg)
 	if err != nil {
@@ -81,6 +82,7 @@ func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string,
 		kubeConfig:        cfg.GetKubeConfig(),
 		labels:            labels,
 		namespace:         cfg.GetKubeNamespace(),
+		manifestOverrides: manifestOverrides,
 
 		transformAllowlist: transformAllowlist,
 		transformDenylist:  transformDenylist,
@@ -129,7 +131,7 @@ func (h Helm) generateHelmManifests(ctx context.Context, builds []graph.Artifact
 			args = append(args, "--version", release.Version)
 		}
 
-		args, err = helm.ConstructOverrideArgs(&release, builds, args)
+		args, err = helm.ConstructOverrideArgs(&release, builds, args, h.manifestOverrides)
 		if err != nil {
 			return nil, helm.UserErr("construct override args", err)
 		}
diff --git a/pkg/skaffold/render/renderer/kpt/kpt.go b/pkg/skaffold/render/renderer/kpt/kpt.go
index def47c08a..4143de45b 100644
--- a/pkg/skaffold/render/renderer/kpt/kpt.go
+++ b/pkg/skaffold/render/renderer/kpt/kpt.go
@@ -20,28 +20,21 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"os"
 	"os/exec"
-	"path/filepath"
 
 	"github.com/blang/semver"
 	apimachinery "k8s.io/apimachinery/pkg/runtime/schema"
 
-	sErrors "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/errors"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/graph"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/instrumentation"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubernetes/manifest"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render"
-	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/errors"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/generate"
-	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/kptfile"
 	rUtil "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/util"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/transform"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/validate"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
-	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/yaml"
-	"github.com/GoogleContainerTools/skaffold/v2/proto/v1"
 )
 
 type Kpt struct {
@@ -51,9 +44,11 @@ type Kpt struct {
 	generate.Generator
 	validate.Validator
 	transform.Transformer
-	hydrationDir string
-	labels       map[string]string
-	namespace    string
+	hydrationDir      string
+	labels            map[string]string
+	namespace         string
+	pkgDir            []string
+	manifestOverrides map[string]string
 
 	transformAllowlist map[apimachinery.GroupKind]latest.ResourceFilter
 	transformDenylist  map[apimachinery.GroupKind]latest.ResourceFilter
@@ -65,40 +60,43 @@ const (
 
 var (
 	KptVersion                      = currentKptVersion
-	maxKptVersionAllowedForDeployer = "1.0.0-beta.13"
+	maxKptVersionAllowedForDeployer = "1.0.0-beta.24"
 )
 
-func New(cfg render.Config, rCfg latest.RenderConfig, hydrationDir string, labels map[string]string, configName string, ns string) (*Kpt, error) {
-	generator := generate.NewGenerator(cfg.GetWorkingDir(), rCfg.Generate, hydrationDir)
+func New(cfg render.Config, rCfg latest.RenderConfig, hydrationDir string, labels map[string]string, configName string, ns string, manifestOverrides map[string]string) (*Kpt, error) {
 	transformAllowlist, transformDenylist, err := rUtil.ConsolidateTransformConfiguration(cfg)
 	if err != nil {
 		return nil, err
 	}
+
 	var validator validate.Validator
 	if rCfg.Validate != nil {
-		var err error
 		validator, err = validate.NewValidator(*rCfg.Validate)
 		if err != nil {
 			return nil, err
 		}
-	} else {
-		validator, _ = validate.NewValidator([]latest.Validator{})
 	}
 
 	var transformer transform.Transformer
 	if rCfg.Transform != nil {
-		var err error
 		transformer, err = transform.NewTransformer(*rCfg.Transform)
 		if err != nil {
 			return nil, err
 		}
-	} else {
-		transformer, _ = transform.NewTransformer([]latest.Transformer{})
 	}
+
+	if len(manifestOverrides) > 0 {
+		err := transformer.Append(latest.Transformer{Name: "apply-setters", ConfigMap: util.EnvMapToSlice(manifestOverrides, ":")})
+		if err != nil {
+			return nil, err
+		}
+	}
+
 	return &Kpt{
 		cfg:                cfg,
 		configName:         configName,
-		Generator:          generator,
+		pkgDir:             rCfg.Kpt,
+		manifestOverrides:  manifestOverrides,
 		Validator:          validator,
 		Transformer:        transformer,
 		hydrationDir:       hydrationDir,
@@ -111,51 +109,28 @@ func New(cfg render.Config, rCfg latest.RenderConfig, hydrationDir string, label
 
 func (r *Kpt) Render(ctx context.Context, out io.Writer, builds []graph.Artifact, offline bool) (manifest.ManifestListByConfig, error) {
 	var ml manifest.ManifestListByConfig
-	kptfilePath := filepath.Join(r.hydrationDir, kptfile.KptFileName)
-	kfConfig := &kptfile.KptFile{}
-
-	// Initialize the kpt hydration directory.
-	// This directory is used to cache DRY config and hydrates the DRY config to WET config in-place.
-	// This is needed because kpt v1 only supports in-place config while users may not want to have their config be
-	// hydrated in place.
-	// Once Kptfile is initialized, its "pipeline" field will be updated in each skaffold render, and its "inventory"
-	// will keep the same to guarantee accurate `kpt live apply`.
-	_, endTrace := instrumentation.StartTrace(ctx, "Render_initKptfile")
-	if _, err := os.Stat(kptfilePath); os.IsNotExist(err) {
-		if err := os.MkdirAll(r.hydrationDir, os.ModePerm); err != nil {
-			endTrace(instrumentation.TraceEndError(fmt.Errorf("create hydration dir %v:%w", r.hydrationDir, err)))
+	var manifestList manifest.ManifestList
+
+	rCtx, endTrace := instrumentation.StartTrace(ctx, "Render_kptRenderCommand")
+
+	for _, p := range r.pkgDir {
+		cmd := exec.Command("kpt", "fn", "render", p, "-o", "unwrap")
+
+		if buf, err := util.RunCmdOut(rCtx, cmd); err == nil {
+			manifestList.Append(buf)
+		} else {
+			endTrace(instrumentation.TraceEndError(err))
+			// TODO(yuwenma): How to guide users when they face kpt error (may due to bad user config)?
 			return ml, err
 		}
-		cmd := exec.CommandContext(ctx, "kpt", "pkg", "init", r.hydrationDir)
-		if _, err := util.RunCmdOut(ctx, cmd); err != nil {
-			return ml, sErrors.NewError(err,
-				&proto.ActionableErr{
-					Message: fmt.Sprintf("unable to initialize Kptfile in %v", r.hydrationDir),
-					ErrCode: proto.StatusCode_RENDER_KPTFILE_INIT_ERR,
-					Suggestions: []*proto.Suggestion{
-						{
-							SuggestionCode: proto.SuggestionCode_KPTFILE_MANUAL_INIT,
-							Action:         fmt.Sprintf("please manually run `kpt pkg init %v`", r.hydrationDir),
-						},
-					},
-				})
-		}
 	}
-	endTrace()
-	_, endTrace = instrumentation.StartTrace(ctx, "Render_readKptfile")
-	kptfileBytes, err := os.ReadFile(kptfilePath)
+
+	manifestList, err := r.Transformer.Transform(ctx, manifestList)
+
 	if err != nil {
-		endTrace(instrumentation.TraceEndError(fmt.Errorf("read Kptfile from %v: %w",
-			filepath.Dir(kptfilePath), err)))
-		return ml, err
-	}
-	if err := yaml.UnmarshalStrict(kptfileBytes, &kfConfig); err != nil {
-		return ml, errors.ParseKptfileError(err, r.hydrationDir)
+		return manifest.ManifestListByConfig{}, err
 	}
-	if err := os.RemoveAll(r.hydrationDir); err != nil {
-		return ml, errors.DeleteKptfileError(err, r.hydrationDir)
-	}
-	endTrace()
+
 	opts := rUtil.GenerateHydratedManifestsOptions{
 		TransformAllowList:         r.transformAllowlist,
 		TransformDenylist:          r.transformDenylist,
@@ -164,69 +139,21 @@ func (r *Kpt) Render(ctx context.Context, out io.Writer, builds []graph.Artifact
 		Offline:                    offline,
 		KubeContext:                r.cfg.GetKubeContext(),
 	}
-	manifests, errH := rUtil.GenerateHydratedManifests(ctx, out, builds, r.Generator, r.labels, r.namespace, opts)
-	if errH != nil {
-		return ml, errH
-	}
-	// Write generated dry manifests.
-	_, endTrace = instrumentation.StartTrace(ctx, "Render_cacheDryConfig")
-	dryConfigPath := filepath.Join(r.hydrationDir, DryFileName)
-	if err := os.MkdirAll(r.hydrationDir, os.ModePerm); err != nil {
-		return ml, err
-	}
-	if err := manifest.Write(manifests.String(), dryConfigPath, io.Discard); err != nil {
-		return ml, err
-	}
-	endTrace()
-	if kfConfig.Pipeline == nil {
-		kfConfig.Pipeline = &kptfile.Pipeline{}
-	}
-	kfConfig.Pipeline.Validators = r.GetDeclarativeValidators()
-	kfConfig.Pipeline.Mutators, err = r.GetDeclarativeTransformers()
-	if err != nil {
-		return ml, err
-	}
-	configByte, err := yaml.Marshal(kfConfig)
+
+	manifestList, err = rUtil.BaseTransform(ctx, manifestList, builds, opts, r.labels, r.namespace)
 	if err != nil {
-		return ml, err
+		return manifest.ManifestListByConfig{}, err
 	}
-	if err = os.WriteFile(kptfilePath, configByte, 0644); err != nil {
-		manifestListByConfig := manifest.NewManifestListByConfig()
-		manifestListByConfig.Add(r.configName, manifests)
-		return manifestListByConfig, err
-	}
-	endTrace()
 
-	rCtx, endTrace := instrumentation.StartTrace(ctx, "Render_kptRenderCommand")
-	cmd := exec.CommandContext(rCtx, "kpt", "fn", "render", r.hydrationDir)
-	cmd.Stdout = out
-	cmd.Stderr = out
-	if err := util.RunCmd(ctx, cmd); err != nil {
-		endTrace(instrumentation.TraceEndError(err))
-		// TODO(yuwenma): How to guide users when they face kpt error (may due to bad user config)?
-		return ml, err
-	}
-	return r.unwrapManifests(ctx, out)
-}
+	err = r.Validator.Validate(ctx, manifestList)
 
-// unwrapManifests converts the structured manifest to a flatten format
-func (r *Kpt) unwrapManifests(ctx context.Context, out io.Writer) (manifest.ManifestListByConfig, error) {
-	var m manifest.ManifestList
-	rCtx, endTrace := instrumentation.StartTrace(ctx, "Render_outputManifests")
-	cmd := exec.CommandContext(rCtx, "kpt", "fn", "source", r.hydrationDir, "-o", "unwrap")
-	var buf []byte
-	cmd.Stderr = out
-	buf, err := util.RunCmdOut(ctx, cmd)
 	if err != nil {
-		endTrace(instrumentation.TraceEndError(err))
-		manifestListByConfig := manifest.NewManifestListByConfig()
-		manifestListByConfig.Add(r.configName, m)
-		return manifestListByConfig, err
+		return manifest.ManifestListByConfig{}, err
 	}
-	m = append(m, buf)
-	manifestListByConfig := manifest.NewManifestListByConfig()
-	manifestListByConfig.Add(r.configName, m)
-	return manifestListByConfig, nil
+
+	ml = manifest.NewManifestListByConfig()
+	ml.Add(r.configName, manifestList)
+	return ml, err
 }
 
 func CheckIsProperBinVersion(ctx context.Context) error {
diff --git a/pkg/skaffold/render/renderer/kubectl/kubectl.go b/pkg/skaffold/render/renderer/kubectl/kubectl.go
index ef17a4461..02bca4ee2 100644
--- a/pkg/skaffold/render/renderer/kubectl/kubectl.go
+++ b/pkg/skaffold/render/renderer/kubectl/kubectl.go
@@ -19,6 +19,9 @@ package kubectl
 import (
 	"context"
 	"io"
+	"os"
+	"path/filepath"
+	"strings"
 
 	apimachinery "k8s.io/apimachinery/pkg/runtime/schema"
 
@@ -28,36 +31,68 @@ import (
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/output/log"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/generate"
-	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/util"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/kptfile"
+	rUtil "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/util"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/transform"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/validate"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
 )
 
 type Kubectl struct {
-	cfg render.Config
-
-	configName string
-	namespace  string
-
-	generate.Generator
-	labels map[string]string
-
+	cfg                render.Config
+	rCfg               latest.RenderConfig
+	configName         string
+	namespace          string
+	Generator          generate.Generator
+	labels             map[string]string
+	manifestOverrides  map[string]string
+	transformer        transform.Transformer
+	validator          validate.Validator
 	transformAllowlist map[apimachinery.GroupKind]latest.ResourceFilter
 	transformDenylist  map[apimachinery.GroupKind]latest.ResourceFilter
 }
 
-func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string, configName string, ns string) (Kubectl, error) {
+func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string, configName string, ns string, manifestOverrides map[string]string) (Kubectl, error) {
+	transformAllowlist, transformDenylist, err := rUtil.ConsolidateTransformConfiguration(cfg)
 	generator := generate.NewGenerator(cfg.GetWorkingDir(), rCfg.Generate, "")
-	transformAllowlist, transformDenylist, err := util.ConsolidateTransformConfiguration(cfg)
 	if err != nil {
 		return Kubectl{}, err
 	}
-	return Kubectl{
-		cfg:        cfg,
-		configName: configName,
-		Generator:  generator,
-		namespace:  ns,
-		labels:     labels,
 
+	var validator validate.Validator
+	if rCfg.Validate != nil {
+		validator, err = validate.NewValidator(*rCfg.Validate)
+		if err != nil {
+			return Kubectl{}, err
+		}
+	}
+
+	var transformer transform.Transformer
+	if rCfg.Transform != nil {
+		transformer, err = transform.NewTransformer(*rCfg.Transform)
+		if err != nil {
+			return Kubectl{}, err
+		}
+	}
+
+	if len(manifestOverrides) > 0 {
+		err := transformer.Append(latest.Transformer{Name: "apply-setters", ConfigMap: util.EnvMapToSlice(manifestOverrides, ":")})
+		if err != nil {
+			return Kubectl{}, err
+		}
+	}
+
+	return Kubectl{
+		cfg:                cfg,
+		configName:         configName,
+		rCfg:               rCfg,
+		namespace:          ns,
+		Generator:          generator,
+		labels:             labels,
+		manifestOverrides:  manifestOverrides,
+		validator:          validator,
+		transformer:        transformer,
 		transformAllowlist: transformAllowlist,
 		transformDenylist:  transformDenylist,
 	}, nil
@@ -69,7 +104,19 @@ func (r Kubectl) Render(ctx context.Context, out io.Writer, builds []graph.Artif
 	instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
 		"RendererType": "kubectl",
 	})
-	opts := util.GenerateHydratedManifestsOptions{
+	// get manifest contents from rawManifests and remoteManifests
+	manifests, err := r.Generator.Generate(ctx, out)
+	if err != nil {
+		return manifest.ManifestListByConfig{}, err
+	}
+
+	manifests, err = r.transformer.Transform(ctx, manifests)
+
+	if err != nil {
+		return manifest.ManifestListByConfig{}, err
+	}
+
+	opts := rUtil.GenerateHydratedManifestsOptions{
 		TransformAllowList:         r.transformAllowlist,
 		TransformDenylist:          r.transformDenylist,
 		EnablePlatformNodeAffinity: r.cfg.EnablePlatformNodeAffinityInRenderedManifests(),
@@ -77,9 +124,55 @@ func (r Kubectl) Render(ctx context.Context, out io.Writer, builds []graph.Artif
 		Offline:                    offline,
 		KubeContext:                r.cfg.GetKubeContext(),
 	}
-	manifests, err := util.GenerateHydratedManifests(ctx, out, builds, r.Generator, r.labels, r.namespace, opts)
+	manifests, err = rUtil.BaseTransform(ctx, manifests, builds, opts, r.labels, r.namespace)
+
+	if err != nil {
+		return manifest.ManifestListByConfig{}, err
+	}
+
+	err = r.validator.Validate(ctx, manifests)
+	if err != nil {
+		return manifest.ManifestListByConfig{}, err
+	}
+
 	endTrace()
 	manifestListByConfig := manifest.NewManifestListByConfig()
 	manifestListByConfig.Add(r.configName, manifests)
 	return manifestListByConfig, err
 }
+
+func (r Kubectl) ManifestDeps() ([]string, error) {
+	var localPaths []string
+	for _, path := range r.rCfg.RawK8s {
+		switch {
+		case util.IsURL(path):
+		case strings.HasPrefix(path, "gs://"):
+		default:
+			localPaths = append(localPaths, path)
+		}
+	}
+
+	dependencyPaths, err := util.ExpandPathsGlob(r.cfg.GetWorkingDir(), localPaths)
+	if err != nil {
+		return []string{}, err
+	}
+	var deps []string
+
+	for _, path := range dependencyPaths {
+		err := filepath.Walk(path,
+			func(p string, info os.FileInfo, err error) error {
+				if err != nil {
+					return err
+				}
+				fname := filepath.Base(p)
+				if strings.HasSuffix(fname, ".yaml") || strings.HasSuffix(fname, ".yml") || fname == kptfile.KptFileName {
+					deps = append(deps, p)
+				}
+				return nil
+			})
+		if err != nil {
+			return nil, err
+		}
+	}
+	return deps, nil
+}
diff --git a/pkg/skaffold/render/renderer/kustomize/fs.go b/pkg/skaffold/render/renderer/kustomize/fs.go
new file mode 100644
index 000000000..5bf9d952a
--- /dev/null
+++ b/pkg/skaffold/render/renderer/kustomize/fs.go
@@ -0,0 +1,75 @@
+/*
+Copyright 2022 The Skaffold Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kustomize
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var NewTmpFS = newTmpFS
+
+type TmpFSReal struct {
+	root string
+}
+
+func (f TmpFSReal) WriteTo(path string, content []byte) error {
+	dst, err := f.GetPath(path)
+	if err != nil {
+		return err
+	}
+	dir := filepath.Dir(dst)
+
+	if err := os.MkdirAll(dir, os.ModePerm); err != nil {
+		return err
+	}
+
+	return os.WriteFile(dst, content, os.ModePerm)
+}
+
+func newTmpFS(rootPath string) TmpFS {
+	return TmpFSReal{
+		root: filepath.Clean(rootPath),
+	}
+}
+
+func (f TmpFSReal) Cleanup() {
+	os.RemoveAll(f.root)
+}
+
+func (f TmpFSReal) GetPath(path string) (string, error) {
+	res := filepath.Join(f.root, path)
+	if err := f.validate(res); err != nil {
+		return "", err
+	}
+	return res, nil
+}
+
+func (f TmpFSReal) validate(path string) error {
+	if path != f.root && !strings.HasPrefix(path, f.root+"/") {
+		return fmt.Errorf("temporary file system operation is out of boundary, root: %s, trying to access %s", f.root, path)
+	}
+	return nil
+}
+
+type TmpFS interface {
+	WriteTo(path string, content []byte) error
+	Cleanup()
+	GetPath(path string) (string, error)
+}
diff --git a/pkg/skaffold/render/renderer/kustomize/kustomize.go b/pkg/skaffold/render/renderer/kustomize/kustomize.go
new file mode 100644
index 000000000..ace8d07bf
--- /dev/null
+++ b/pkg/skaffold/render/renderer/kustomize/kustomize.go
@@ -0,0 +1,537 @@
+/*
+Copyright 2022 The Skaffold Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kustomize
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+
+	"gopkg.in/yaml.v3"
+	apimachinery "k8s.io/apimachinery/pkg/runtime/schema"
+	"sigs.k8s.io/kustomize/api/types"
+
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/constants"
+	sErrors "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/errors"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubectl"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubernetes/manifest"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/generate"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/util"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/transform"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/validate"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
+	sUtil "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util/stringset"
+	"github.com/GoogleContainerTools/skaffold/v2/proto/v1"
+)
+
+type Kustomize struct {
+	cfg  render.Config
+	rCfg latest.RenderConfig
+
+	configName string
+	namespace  string
+
+	labels            map[string]string
+	manifestOverrides map[string]string
+
+	transformer        transform.Transformer
+	validator          validate.Validator
+	transformAllowlist map[apimachinery.GroupKind]latest.ResourceFilter
+	transformDenylist  map[apimachinery.GroupKind]latest.ResourceFilter
+}
+
+func (k Kustomize) Render(ctx context.Context, out io.Writer, builds []graph.Artifact, offline bool) (manifest.ManifestListByConfig, error) {
+	var manifests manifest.ManifestList
+	kCLI := kubectl.NewCLI(k.cfg, "")
+	useKubectlKustomize := !generate.KustomizeBinaryCheck() && generate.KubectlVersionCheck(kCLI)
+
+	for _, kustomizePath := range sUtil.AbsolutePaths(k.cfg.GetWorkingDir(), k.rCfg.Kustomize.Paths) {
+		out, err := k.render(ctx, kustomizePath, useKubectlKustomize, kCLI)
+		if err != nil {
+			return manifest.ManifestListByConfig{}, err
+		}
+		if len(out) == 0 {
+			continue
+		}
+		manifests.Append(out)
+	}
+
+	opts := util.GenerateHydratedManifestsOptions{
+		TransformAllowList:         k.transformAllowlist,
+		TransformDenylist:          k.transformDenylist,
+		EnablePlatformNodeAffinity: k.cfg.EnablePlatformNodeAffinityInRenderedManifests(),
+		EnableGKEARMNodeToleration: k.cfg.EnableGKEARMNodeTolerationInRenderedManifests(),
+		Offline:                    offline,
+		KubeContext:                k.cfg.GetKubeContext(),
+	}
+
+	ns := k.namespace
+	if k.cfg.GetKubeNamespace() != "" {
+		ns = k.cfg.GetKubeNamespace()
+	}
+	manifests, err := util.BaseTransform(ctx, manifests, builds, opts, k.labels, ns)
+	if err != nil {
+		return manifest.ManifestListByConfig{}, err
+	}
+
+	manifestListByConfig := manifest.NewManifestListByConfig()
+	manifestListByConfig.Add(k.configName, manifests)
+
+	return manifestListByConfig, nil
+}
+
+func (k Kustomize) render(ctx context.Context, kustomizePath string, useKubectlKustomize bool, kCLI *kubectl.CLI) ([]byte, error) {
+	var out []byte
+	var err error
+	kPath, err := sUtil.ExpandEnvTemplate(kustomizePath, nil)
+	if err != nil {
+		return out, fmt.Errorf("unable to parse path %q: %w", kustomizePath, err)
+	}
+
+	if !k.transformer.IsEmpty() && !sUtil.IsURL(kustomizePath) {
+		temp, err := os.MkdirTemp("", "*")
+		if err != nil {
+			return out, err
+		}
+		fs := newTmpFS(temp)
+		defer fs.Cleanup()
+
+		if err := k.mirror(kPath, fs); err == nil {
+			kPath = filepath.Join(temp, kPath)
+		} else {
+			return out, err
+		}
+	}
+
+	if useKubectlKustomize {
+		return kCLI.Kustomize(ctx, kustomizeBuildArgs(k.rCfg.Kustomize.BuildArgs, kPath))
+	} else {
+		cmd := exec.CommandContext(ctx, "kustomize", append([]string{"build"}, kustomizeBuildArgs(k.rCfg.Kustomize.BuildArgs, kPath)...)...)
+		return sUtil.RunCmdOut(ctx, cmd)
+	}
+}
+
+func getKustomizationFile(kusDir string) (string, error) {
+	for _, path := range constants.KustomizeFilePaths {
+		kPath := filepath.Join(kusDir, path)
+		_, err := os.Stat(kPath)
+		if err != nil {
+			continue
+		}
+		return kPath, nil
+	}
+	return "", fmt.Errorf("cannot locate kustomization file from provided directory: %s", kusDir)
+}
+
+func (k Kustomize) mirror(kusDir string, fs TmpFS) error {
+	kFile, err := getKustomizationFile(kusDir)
+	if err != nil {
+		return err
+	}
+
+	bytes, err := ioutil.ReadFile(kFile)
+	if err != nil {
+		return err
+	}
+
+	if err := fs.WriteTo(kFile, bytes); err != nil {
+		return err
+	}
+
+	kustomization := types.Kustomization{}
+	if err := yaml.Unmarshal(bytes, &kustomization); err != nil {
+		return err
+	}
+	if err := k.mirrorPatchesStrategicMerge(kusDir, fs, kustomization.PatchesStrategicMerge); err != nil {
+		return err
+	}
+	if err := k.mirrorPatchesJSON6902(kusDir, fs, kustomization.PatchesJson6902); err != nil {
+		return err
+	}
+	if err := k.mirrorPatches(kusDir, fs, kustomization.Patches); err != nil {
+		return err
+	}
+	if err := k.mirrorResources(kusDir, fs, kustomization.Resources); err != nil {
+		return err
+	}
+	if err := k.mirrorCrds(kusDir, fs, kustomization.Crds); err != nil {
+		return err
+	}
+	if err := k.mirrorBases(kusDir, fs, kustomization.Bases); err != nil {
+		return err
+	}
+	if err := k.mirrorConfigurations(kusDir, fs, kustomization.Configurations); err != nil {
+		return err
+	}
+	if err := k.mirrorGenerators(kusDir, fs, kustomization.Generators); err != nil {
+		return err
+	}
+	if err := k.mirrorTransformers(kusDir, fs, kustomization.Transformers); err != nil {
+		return err
+	}
+	if err := k.mirrorValidators(kusDir, fs, kustomization.Validators); err != nil {
+		return err
+	}
+	if err := k.mirrorSecretGenerators(kusDir, fs, kustomization.SecretGenerator); err != nil {
+		return err
+	}
+	if err := k.mirrorConfigMapGenerators(kusDir, fs, kustomization.ConfigMapGenerator); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string, configName string, ns string, manifestOverrides map[string]string) (Kustomize, error) {
+	transformAllowlist, transformDenylist, err := util.ConsolidateTransformConfiguration(cfg)
+	if err != nil {
+		return Kustomize{}, err
+	}
+
+	var validator validate.Validator
+	if rCfg.Validate != nil {
+		validator, err = validate.NewValidator(*rCfg.Validate)
+		if err != nil {
+			return Kustomize{}, err
+		}
+	}
+
+	var transformer transform.Transformer
+	if rCfg.Transform != nil {
+		transformer, err = transform.NewTransformer(*rCfg.Transform)
+		if err != nil {
+			return Kustomize{}, err
+		}
+	}
+
+	if len(manifestOverrides) > 0 {
+		err := transformer.Append(latest.Transformer{Name: "apply-setters", ConfigMap: sUtil.EnvMapToSlice(manifestOverrides, ":")})
+		if err != nil {
+			return Kustomize{}, err
+		}
+	}
+
+	return Kustomize{
+		cfg:               cfg,
+		configName:        configName,
+		namespace:         ns,
+		labels:            labels,
+		rCfg:              rCfg,
+		manifestOverrides: manifestOverrides,
+		validator:         validator,
+		transformer:       transformer,
+
+		transformAllowlist: transformAllowlist,
+		transformDenylist:  transformDenylist,
+	}, nil
+}
+
+func (k Kustomize) ManifestDeps() ([]string, error) {
+	return kustomizeDependencies(k.cfg.GetWorkingDir(), k.rCfg.Kustomize.Paths)
+}
+
+func (k Kustomize) mirrorPatchesStrategicMerge(kusDir string, fs TmpFS, merges []types.PatchStrategicMerge) error {
+	for _, p := range merges {
+		if err := k.mirrorFile(kusDir, fs, string(p)); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (k Kustomize) mirrorResources(kusDir string, fs TmpFS, resources []string) error {
+	for _, r := range resources {
+		// note that r is relative to kustomization file not working dir here
+		rPath := filepath.Join(kusDir, r)
+		stat, err := os.Stat(rPath)
+		if err != nil {
+			return err
+		}
+		if stat.IsDir() {
+			if err := k.mirror(rPath, fs); err != nil {
+				return err
+			}
+		} else {
+			if err := k.mirrorFile(kusDir, fs, r); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func (k Kustomize) mirrorFile(kusDir string, fs TmpFS, path string) error {
+	if sUtil.IsURL(path) {
+		return nil
+	}
+	pFile := filepath.Join(kusDir, path)
+	bytes, err := ioutil.ReadFile(pFile)
+	if err != nil {
+		return err
+	}
+	if err := fs.WriteTo(pFile, bytes); err != nil {
+		return err
+	}
+	fsPath, err := fs.GetPath(pFile)
+
+	if err != nil {
+		return err
+	}
+
+	err = k.transformer.TransformPath(fsPath)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (k Kustomize) mirrorFiles(kusDir string, fs TmpFS, paths []string) error {
+	for _, path := range paths {
+		err := k.mirrorFile(kusDir, fs, path)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (k Kustomize) mirrorBases(kusDir string, fs TmpFS, bases []string) error {
+	for _, b := range bases {
+		if err := k.mirror(filepath.Join(kusDir, b), fs); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (k Kustomize) mirrorCrds(kusDir string, fs TmpFS, crds []string) error {
+	return k.mirrorFiles(kusDir, fs, crds)
+}
+
+func (k Kustomize) mirrorConfigurations(kusDir string, fs TmpFS, configurations []string) error {
+	return k.mirrorFiles(kusDir, fs, configurations)
+}
+
+func (k Kustomize) mirrorGenerators(kusDir string, fs TmpFS, generators []string) error {
+	return k.mirrorFiles(kusDir, fs, generators)
+}
+
+func (k Kustomize) mirrorTransformers(kusDir string, fs TmpFS, transformers []string) error {
+	return k.mirrorFiles(kusDir, fs, transformers)
+}
+
+func (k Kustomize) mirrorValidators(kusDir string, fs TmpFS, validators []string) error {
+	return k.mirrorFiles(kusDir, fs, validators)
+}
+
+func (k Kustomize) mirrorPatches(kusDir string, fs TmpFS, patches []types.Patch) error {
+	for _, patch := range patches {
+		if err := k.mirrorFile(kusDir, fs, patch.Path); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (k Kustomize) mirrorPatchesJSON6902(kusDir string, fs TmpFS, patches []types.Patch) error {
+	return k.mirrorPatches(kusDir, fs, patches)
+}
+
+func (k Kustomize) mirrorSecretGenerators(kusDir string, fs TmpFS, args []types.SecretArgs) error {
+	for _, arg := range args {
+		if arg.FileSources != nil {
+			for _, f := range arg.FileSources {
+				if err := k.mirrorFile(kusDir, fs, f); err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (k Kustomize) mirrorConfigMapGenerators(kusDir string, fs TmpFS, args []types.ConfigMapArgs) error {
+	for _, arg := range args {
+		if arg.FileSources != nil {
+			for _, f := range arg.FileSources {
+				if err := k.mirrorFile(kusDir, fs, f); err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func kustomizeDependencies(workdir string, paths []string) ([]string, error) {
+	deps := stringset.New()
+	for _, kustomizePath := range paths {
+		expandedKustomizePath, err := sUtil.ExpandEnvTemplate(kustomizePath, nil)
+		if err != nil {
+			return nil, fmt.Errorf("unable to parse path %q: %w", kustomizePath, err)
+		}
+
+		if !filepath.IsAbs(expandedKustomizePath) {
+			expandedKustomizePath = filepath.Join(workdir, expandedKustomizePath)
+		}
+		depsForKustomization, err := DependenciesForKustomization(expandedKustomizePath)
+		if err != nil {
+			return nil, sErrors.NewError(err,
+				&proto.ActionableErr{
+					Message: err.Error(),
+					ErrCode: proto.StatusCode_DEPLOY_KUSTOMIZE_USER_ERR,
+				})
+		}
+		deps.Insert(depsForKustomization...)
+	}
+	return deps.ToList(), nil
+}
+
+// DependenciesForKustomization finds common kustomize artifacts relative to the
+// provided working dir, and collects them into a list of files to be passed
+// to the file watcher.
+func DependenciesForKustomization(dir string) ([]string, error) {
+	var deps []string
+
+	path, err := FindKustomizationConfig(dir)
+	if err != nil {
+		// No kustomization config found so assume it's remote and stop traversing
+		return deps, nil
+	}
+
+	buf, err := os.ReadFile(path)
+	if err != nil {
+		return nil, err
+	}
+
+	content := types.Kustomization{}
+	if err := yaml.Unmarshal(buf, &content); err != nil {
+		return nil, fmt.Errorf("kustomization parse error in %v: %w", path, err)
+	}
+
+	deps = append(deps, path)
+
+	candidates := append(content.Bases, content.Resources...)
+	candidates = append(candidates, content.Components...)
+
+	for _, candidate := range candidates {
+		// If the file doesn't exist locally, we can assume it's a remote file and
+		// skip it, since we can't monitor remote files. Kustomize itself will
+		// handle invalid/missing files.
+		local, mode := pathExistsLocally(candidate, dir)
+		if !local {
+			continue
+		}
+
+		if mode.IsDir() {
+			candidateDeps, err := DependenciesForKustomization(filepath.Join(dir, candidate))
+			if err != nil {
+				return nil, err
+			}
+			deps = append(deps, candidateDeps...)
+		} else {
+			deps = append(deps, filepath.Join(dir, candidate))
+		}
+	}
+
+	for _, patch := range content.PatchesStrategicMerge {
+		deps = append(deps, filepath.Join(dir, string(patch)))
+	}
+
+	deps = append(deps, sUtil.AbsolutePaths(dir, content.Crds)...)
+
+	for _, patch := range content.Patches {
+		if patch.Path != "" {
+			deps = append(deps, filepath.Join(dir, patch.Path))
+		}
+	}
+
+	for _, jsonPatch := range content.PatchesJson6902 {
+		if jsonPatch.Path != "" {
+			deps = append(deps, filepath.Join(dir, jsonPatch.Path))
+		}
+	}
+
+	for _, generator := range content.ConfigMapGenerator {
+		deps = append(deps, sUtil.AbsolutePaths(dir, generator.FileSources)...)
+		envs := generator.EnvSources
+		if generator.EnvSource != "" {
+			envs = append(envs, generator.EnvSource)
+		}
+		deps = append(deps, sUtil.AbsolutePaths(dir, envs)...)
+	}
+
+	for _, generator := range content.SecretGenerator {
+		deps = append(deps, sUtil.AbsolutePaths(dir, generator.FileSources)...)
+		envs := generator.EnvSources
+		if generator.EnvSource != "" {
+			envs = append(envs, generator.EnvSource)
+		}
+		deps = append(deps, sUtil.AbsolutePaths(dir, envs)...)
+	}
+
+	return deps, nil
+}
+
+// FindKustomizationConfig finds the kustomization config relative to the provided dir.
+// A Kustomization config must be at the root of the directory. Kustomize will
+// error if more than one of these files exists so order doesn't matter.
+func FindKustomizationConfig(dir string) (string, error) {
+	for _, candidate := range constants.KustomizeFilePaths {
+		if local, _ := pathExistsLocally(candidate, dir); local {
+			return filepath.Join(dir, candidate), nil
+		}
+	}
+	return "", fmt.Errorf("no Kustomization configuration found in directory: %s", dir)
+}
+
+func pathExistsLocally(filename string, workingDir string) (bool, os.FileMode) {
+	path := filename
+	if !filepath.IsAbs(filename) {
+		path = filepath.Join(workingDir, filename)
+	}
+	if f, err := os.Stat(path); err == nil {
+		return true, f.Mode()
+	}
+	return false, 0
+}
+
+// kustomizeBuildArgs returns a list of build args to be passed to kustomize build.
+func kustomizeBuildArgs(buildArgs []string, kustomizePath string) []string {
+	var args []string
+
+	if len(buildArgs) > 0 {
+		for _, v := range buildArgs {
+			parts := strings.Split(v, " ")
+			args = append(args, parts...)
+		}
+	}
+
+	if len(kustomizePath) > 0 {
+		args = append(args, kustomizePath)
+	}
+
+	return args
+}
diff --git a/pkg/skaffold/render/renderer/renderer.go b/pkg/skaffold/render/renderer/renderer.go
index 286d758f3..f8332855c 100644
--- a/pkg/skaffold/render/renderer/renderer.go
+++ b/pkg/skaffold/render/renderer/renderer.go
@@ -28,6 +28,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/helm"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/kpt"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/kubectl"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/kustomize"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
 )
 
@@ -39,19 +40,28 @@ type Renderer interface {
 }
 
 // New creates a new Renderer object from the latestV2 API schema.
-func New(ctx context.Context, cfg render.Config, renderCfg latest.RenderConfig, hydrationDir string, labels map[string]string, configName string) (GroupRenderer, error) {
+func New(ctx context.Context, cfg render.Config, renderCfg latest.RenderConfig, hydrationDir string, labels map[string]string, configName string, manifestOverrides map[string]string) (GroupRenderer, error) {
 	var rs GroupRenderer
 	rs.HookRunners = []hooks.Runner{hooks.NewRenderRunner(renderCfg.Generate.LifecycleHooks, &[]string{cfg.GetNamespace()}, hooks.NewRenderEnvOpts(cfg.GetKubeContext(), []string{cfg.GetNamespace()}))}
 
-	if renderCfg.Validate != nil || renderCfg.Transform != nil || renderCfg.Kpt != nil {
-		r, err := kpt.New(cfg, renderCfg, hydrationDir, labels, configName, cfg.GetNamespace())
+	if renderCfg.Kpt != nil {
+		r, err := kpt.New(cfg, renderCfg, hydrationDir, labels, configName, cfg.GetNamespace(), manifestOverrides)
 		if err != nil {
 			return GroupRenderer{}, err
 		}
 		log.Entry(ctx).Infof("setting up kpt renderer")
 		rs.Renderers = append(rs.Renderers, r)
-	} else if renderCfg.RawK8s != nil || renderCfg.Kustomize != nil {
-		r, err := kubectl.New(cfg, renderCfg, labels, configName, cfg.GetNamespace())
+	}
+
+	if renderCfg.RawK8s != nil || renderCfg.RemoteManifests != nil {
+		r, err := kubectl.New(cfg, renderCfg, labels, configName, cfg.GetNamespace(), manifestOverrides)
+		if err != nil {
+			return GroupRenderer{}, err
+		}
+		rs.Renderers = append(rs.Renderers, r)
+	}
+	if renderCfg.Kustomize != nil {
+		r, err := kustomize.New(cfg, renderCfg, labels, configName, cfg.GetNamespace(), manifestOverrides)
 		if err != nil {
 			return GroupRenderer{}, err
 		}
@@ -59,7 +69,7 @@ func New(ctx context.Context, cfg render.Config, renderCfg latest.RenderConfig,
 	}
 
 	if renderCfg.Helm != nil {
-		r, err := helm.New(cfg, renderCfg, labels, configName)
+		r, err := helm.New(cfg, renderCfg, labels, configName, manifestOverrides)
 		if err != nil {
 			return GroupRenderer{}, err
 		}
diff --git a/pkg/skaffold/render/renderer/util/util.go b/pkg/skaffold/render/renderer/util/util.go
index 2ebe568b8..54a739078 100644
--- a/pkg/skaffold/render/renderer/util/util.go
+++ b/pkg/skaffold/render/renderer/util/util.go
@@ -51,23 +51,27 @@ func GenerateHydratedManifests(ctx context.Context, out io.Writer, builds []grap
 	}
 	endTrace()
 
+	return BaseTransform(ctx, manifests, builds, opts, labels, ns)
+}
+
+// BaseTransform skaffold controlled manifests fields
+func BaseTransform(ctx context.Context, manifests manifest.ManifestList, builds []graph.Artifact, opts GenerateHydratedManifestsOptions, labels map[string]string, ns string) (manifest.ManifestList, error) {
 	// Update image labels.renderer_test.go
-	rCtx, endTrace = instrumentation.StartTrace(ctx, "Render_setSkaffoldLabels")
+	rCtx, endTrace := instrumentation.StartTrace(ctx, "Render_setSkaffoldLabels")
 
-	manifests, err = manifests.ReplaceImages(rCtx, builds, manifest.NewResourceSelectorImages(opts.TransformAllowList, opts.TransformDenylist))
+	manifests, err := manifests.ReplaceImages(rCtx, builds, manifest.NewResourceSelectorImages(opts.TransformAllowList, opts.TransformDenylist))
 	if err != nil {
-		return nil, err
+		return manifests, err
 	}
 	rs := manifest.NewResourceSelectorLabels(opts.TransformAllowList, opts.TransformDenylist)
 
 	if manifests, err = manifests.SetLabels(labels, manifest.NewResourceSelectorLabels(opts.TransformAllowList, opts.TransformDenylist)); err != nil {
-		return nil, err
+		return manifests, err
 	}
-	// TODO(tejaldesai) consult with cloud deploy team if namespaces can be set in offline mode
-	// in case namespace is set on the skaffold render cli command.
+
 	if !opts.Offline {
 		if manifests, err = manifests.SetNamespace(ns, rs); err != nil {
-			return nil, err
+			return manifests, err
 		}
 		endTrace()
 	}
diff --git a/pkg/skaffold/render/transform/transform.go b/pkg/skaffold/render/transform/transform.go
index 2d57bdde6..94edbdfe0 100644
--- a/pkg/skaffold/render/transform/transform.go
+++ b/pkg/skaffold/render/transform/transform.go
@@ -17,12 +17,17 @@ limitations under the License.
 package transform
 
 import (
+	"bytes"
+	"context"
 	"fmt"
+	"os/exec"
 	"strings"
 
 	sErrors "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/errors"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubernetes/manifest"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/kptfile"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
 	"github.com/GoogleContainerTools/skaffold/v2/proto/v1"
 )
 
@@ -89,6 +94,62 @@ func (v *Transformer) GetDeclarativeTransformers() ([]kptfile.Function, error) {
 	return v.kptFn, nil
 }
 
+func (v *Transformer) Append(ts ...latest.Transformer) error {
+	kptfns, err := validateTransformers(ts)
+	if err != nil {
+		return err
+	}
+	v.config = append(v.config, ts...)
+	v.kptFn = append(v.kptFn, kptfns...)
+	return nil
+}
+
+func (v *Transformer) IsEmpty() bool {
+	return v.config == nil || len(v.config) == 0
+}
+
+func (v *Transformer) Transform(ctx context.Context, ml manifest.ManifestList) (manifest.ManifestList, error) {
+	if v.kptFn == nil {
+		return ml, nil
+	}
+	var err error
+	for _, transformer := range v.kptFn {
+		slice := util.EnvMapToSlice(transformer.ConfigMap, "=")
+		args := []string{"fn", "eval", "-i", transformer.Image, "-o", "unwrap", "-", "--"}
+		args = append(args, slice...)
+		cmd := exec.CommandContext(ctx, "kpt", args...)
+		reader := ml.Reader()
+		buffer := &bytes.Buffer{}
+		cmd.Stdin = reader
+		cmd.Stdout = buffer
+
+		err := cmd.Run()
+		if err != nil {
+			return ml, err
+		}
+		ml, err = manifest.Load(buffer)
+		if err != nil {
+			return ml, err
+		}
+	}
+	return ml, err
+}
+
+// TransformPath transform manifests in-place in filepath.
+func (v *Transformer) TransformPath(path string) error {
+	for _, transformer := range v.kptFn {
+		kvs := util.EnvMapToSlice(transformer.ConfigMap, "=")
+		args := []string{"fn", "eval", "-i", transformer.Image, path, "--"}
+		args = append(args, kvs...)
+		command := exec.Command("kpt", args...)
+		err := command.Run()
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 func validateTransformers(config []latest.Transformer) ([]kptfile.Function, error) {
 	var newFuncs []kptfile.Function
 	for _, c := range config {
diff --git a/pkg/skaffold/render/validate/validate.go b/pkg/skaffold/render/validate/validate.go
index c33b4b7bb..1edff6572 100644
--- a/pkg/skaffold/render/validate/validate.go
+++ b/pkg/skaffold/render/validate/validate.go
@@ -17,11 +17,15 @@ limitations under the License.
 package validate
 
 import (
+	"context"
 	"fmt"
+	"os/exec"
 
 	sErrors "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/errors"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubernetes/manifest"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/kptfile"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
 	"github.com/GoogleContainerTools/skaffold/v2/proto/v1"
 )
 
@@ -68,3 +72,23 @@ func (v Validator) GetDeclarativeValidators() []kptfile.Function {
 	// TODO: guarantee the v.kptFn is updated once users changed skaffold.yaml file.
 	return v.kptFn
 }
+
+func (v Validator) Validate(ctx context.Context, ml manifest.ManifestList) error {
+	if v.kptFn == nil || len(v.kptFn) == 0 {
+		return nil
+	}
+
+	for _, validator := range v.kptFn {
+		kvs := util.EnvMapToSlice(validator.ConfigMap, "=")
+		args := []string{"fn", "eval", "-i", validator.Image, "-o", "unwrap", "-", "--"}
+		args = append(args, kvs...)
+		cmd := exec.CommandContext(ctx, "kpt", args...)
+		reader := ml.Reader()
+		cmd.Stdin = reader
+		err := cmd.Run()
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/pkg/skaffold/runner/new.go b/pkg/skaffold/runner/new.go
index 54724f000..44a91e29e 100644
--- a/pkg/skaffold/runner/new.go
+++ b/pkg/skaffold/runner/new.go
@@ -260,7 +260,7 @@ func isKptRendererOrDeployerUsed(pipelines runcontext.Pipelines) bool {
 		renderConfig := pipeline.Render
 		deployConfig := pipeline.Deploy
 
-		if renderConfig.Validate != nil || renderConfig.Transform != nil || renderConfig.Kpt != nil {
+		if renderConfig.Kpt != nil {
 			return true
 		}
 
diff --git a/pkg/skaffold/runner/renderer.go b/pkg/skaffold/runner/renderer.go
index 5c728ff9f..a33007720 100644
--- a/pkg/skaffold/runner/renderer.go
+++ b/pkg/skaffold/runner/renderer.go
@@ -24,6 +24,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/helm"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/runner/runcontext"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
+	pkgutil "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
 )
 
 // GetRenderer creates a renderer from a given RunContext and pipeline definitions.
@@ -33,7 +34,7 @@ func GetRenderer(ctx context.Context, runCtx *runcontext.RunContext, hydrationDi
 	var gr renderer.GroupRenderer
 	for _, configName := range configNames {
 		p := runCtx.Pipelines.GetForConfigName(configName)
-		rs, err := renderer.New(ctx, runCtx, p.Render, hydrationDir, labels, configName)
+		rs, err := renderer.New(ctx, runCtx, p.Render, hydrationDir, labels, configName, pkgutil.EnvSliceToMap(runCtx.Opts.ManifestsOverrides, "="))
 		if err != nil {
 			return nil, err
 		}
@@ -57,7 +58,7 @@ func GetRenderer(ctx context.Context, runCtx *runcontext.RunContext, hydrationDi
 					},
 				},
 			}
-			r, err := helm.New(runCtx, rCfg, labels, configName)
+			r, err := helm.New(runCtx, rCfg, labels, configName, nil)
 			if err != nil {
 				return nil, err
 			}
diff --git a/pkg/skaffold/runner/runcontext/context.go b/pkg/skaffold/runner/runcontext/context.go
index 271227977..99accfc81 100644
--- a/pkg/skaffold/runner/runcontext/context.go
+++ b/pkg/skaffold/runner/runcontext/context.go
@@ -278,7 +278,7 @@ func (rc *RunContext) GetNamespace() string {
 
 		return defaultNamespace
 	}
-	b, err := (&util.Commander{}).RunCmdOut(context.Background(), exec.Command("kubectl", "config", "view", "--minify", "-o", "jsonpath='{..namespace}'"))
+	b, err := util.RunCmdOutOnce(context.Background(), exec.Command("kubectl", "config", "view", "--minify", "-o", "jsonpath='{..namespace}'"))
 	if err != nil {
 		return rc.Opts.Namespace
 	}
diff --git a/pkg/skaffold/tag/date_time.go b/pkg/skaffold/tag/date_time.go
index 5916bc7af..7ece09b4e 100644
--- a/pkg/skaffold/tag/date_time.go
+++ b/pkg/skaffold/tag/date_time.go
@@ -21,8 +21,6 @@ import (
 	"fmt"
 	"time"
 
-	"4d63.com/tz"
-
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
 )
 
@@ -57,7 +55,7 @@ func (t *dateTimeTagger) GenerateTag(ctx context.Context, image latest.Artifact)
 		timezone = t.TimeZone
 	}
 
-	loc, err := tz.LoadLocation(timezone)
+	loc, err := time.LoadLocation(timezone)
 	if err != nil {
 		return "", fmt.Errorf("bad timezone provided: %q, error: %s", timezone, err)
 	}
diff --git a/pkg/skaffold/util/cmd.go b/pkg/skaffold/util/cmd.go
index 2d7d88643..60c390186 100644
--- a/pkg/skaffold/util/cmd.go
+++ b/pkg/skaffold/util/cmd.go
@@ -48,13 +48,20 @@ func (e *cmdError) ExitCode() int {
 }
 
 // DefaultExecCommand runs commands using exec.Cmd
-var DefaultExecCommand Command = &Commander{}
+var DefaultExecCommand Command = newCommander()
+
+func newCommander() *Commander {
+	return &Commander{
+		store: NewSyncStore[[]byte](),
+	}
+}
 
 // Command is an interface used to run commands. All packages should use this
 // interface instead of calling exec.Cmd directly.
 type Command interface {
 	RunCmdOut(ctx context.Context, cmd *exec.Cmd) ([]byte, error)
 	RunCmd(ctx context.Context, cmd *exec.Cmd) error
+	RunCmdOutOnce(ctx context.Context, cmd *exec.Cmd) ([]byte, error)
 }
 
 func RunCmdOut(ctx context.Context, cmd *exec.Cmd) ([]byte, error) {
@@ -65,8 +72,14 @@ func RunCmd(ctx context.Context, cmd *exec.Cmd) error {
 	return DefaultExecCommand.RunCmd(ctx, cmd)
 }
 
+func RunCmdOutOnce(ctx context.Context, cmd *exec.Cmd) ([]byte, error) {
+	return DefaultExecCommand.RunCmdOutOnce(ctx, cmd)
+}
+
 // Commander is the exec.Cmd implementation of the Command interface
-type Commander struct{}
+type Commander struct {
+	store *SyncStore[[]byte]
+}
 
 // RunCmdOut runs an exec.Command and returns the stdout and error.
 func (*Commander) RunCmdOut(ctx context.Context, cmd *exec.Cmd) ([]byte, error) {
@@ -104,3 +117,9 @@ func (*Commander) RunCmd(ctx context.Context, cmd *exec.Cmd) error {
 	log.Entry(ctx).Debugf("Running command: %s", cmd.Args)
 	return cmd.Run()
 }
+
+func (c *Commander) RunCmdOutOnce(ctx context.Context, cmd *exec.Cmd) ([]byte, error) {
+	return c.store.Exec(cmd.String(), func() ([]byte, error) {
+		return RunCmdOut(ctx, cmd)
+	})
+}


Test patch:
diff --git a/integration/env_file_test.go b/integration/env_file_test.go
new file mode 100644
index 000000000..6460c88f5
--- /dev/null
+++ b/integration/env_file_test.go
@@ -0,0 +1,35 @@
+/*
+Copyright 2021 The Skaffold Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package integration
+
+import (
+	"testing"
+
+	"github.com/GoogleContainerTools/skaffold/v2/integration/skaffold"
+)
+
+func TestEnvFile(t *testing.T) {
+	// This test verifies that Skaffold can load environment variables from the `skaffold.env` file.
+	// In the project `examples/using-env-file` run `skaffold run` and verify that environment variables were loaded from the `skaffold.env` file
+
+	MarkIntegrationTest(t, CanRunWithoutGcp)
+	ns, _ := SetupNamespace(t)
+	out := skaffold.Run("--tail").InDir("examples/using-env-file").InNs(ns.Name).RunLive(t)
+
+	WaitForLogs(t, out, "Hello from Skaffold!")
+	skaffold.Delete().InDir("examples/using-env-file").InNs(ns.Name).RunOrFail(t)
+}
diff --git a/integration/render_test.go b/integration/render_test.go
index 716b80521..263950457 100644
--- a/integration/render_test.go
+++ b/integration/render_test.go
@@ -77,7 +77,7 @@ spec:
 				RawK8s: []string{"deployment.yaml"}},
 		}
 		mockCfg := render.MockConfig{WorkingDir: tmpDir.Root()}
-		r, err := kubectl.New(mockCfg, rc, map[string]string{}, "default", ns.Name)
+		r, err := kubectl.New(mockCfg, rc, map[string]string{}, "default", ns.Name, nil)
 		t.RequireNoError(err)
 		var b bytes.Buffer
 		l, err := r.Render(context.Background(), &b, test.builds, false)
@@ -220,7 +220,7 @@ spec:
 					RawK8s: []string{"deployment.yaml"}},
 			}
 			mockCfg := render.MockConfig{WorkingDir: tmpDir.Root()}
-			r, err := kubectl.New(mockCfg, rc, map[string]string{}, "default", ns.Name)
+			r, err := kubectl.New(mockCfg, rc, map[string]string{}, "default", ns.Name, nil)
 			t.RequireNoError(err)
 			var b bytes.Buffer
 			l, err := r.Render(context.Background(), &b, test.builds, false)
@@ -390,6 +390,34 @@ spec:
       containers:
       - image: skaffold-helm:latest
         name: skaffold-helm
+`,
+		}, {
+			description:      "Template replicaCount with --set flag",
+			dir:              "testdata/helm-render-simple",
+			args:             []string{"--set", "replicaCount=3"},
+			withoutBuildJSON: true,
+			expectedOut: `apiVersion: apps/v1
+kind: Deployment
+metadata:
+  labels:
+    app: skaffold-helm
+    skaffold.dev/run-id: phony-run-id
+  name: skaffold-helm
+  namespace: helm-namespace
+spec:
+  replicas: 3
+  selector:
+    matchLabels:
+      app: skaffold-helm
+  template:
+    metadata:
+      labels:
+        app: skaffold-helm
+        skaffold.dev/run-id: phony-run-id
+    spec:
+      containers:
+      - image: skaffold-helm:latest
+        name: skaffold-helm
 `,
 		},
 	}
@@ -799,8 +827,8 @@ spec:
       image: skaffold-example`,
 		},
 		{
-			description:              "project with rawYaml and transform should create hydration dir (uses kpt renderer)",
-			shouldCreateHydrationDir: true,
+			description:              "project with rawYaml and transform should not create hydration dir (uses kpt renderer)",
+			shouldCreateHydrationDir: false,
 			config: `
 apiVersion: skaffold/v4beta1
 kind: Config
@@ -873,6 +901,267 @@ spec:
 	}
 }
 
+func TestRenderWithTransformer(t *testing.T) {
+	tests := []struct {
+		description string
+		args        []string
+		config      string
+		input       map[string]string // file path => content
+		expectedOut string
+	}{
+		{
+			description: "kubectl set manifest label with apply-setters",
+			args:        []string{"--offline=true"},
+			config: `apiVersion: skaffold/v4beta2
+kind: Config
+manifests:
+  rawYaml:
+    - k8s-pod.yaml
+  transform:
+    - name: apply-setters
+      configMap:
+        - "app1:from-apply-setters-1"
+`,
+			input: map[string]string{
+				"k8s-pod.yaml": `apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+  labels:
+    a: hhhh # kpt-set: ${app1}
+spec:
+  containers:
+  - name: getting-started
+    image: skaffold-example`,
+			}, expectedOut: `apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+  labels:
+    a: from-apply-setters-1
+spec:
+  containers:
+  - name: getting-started
+    image: skaffold-example
+`,
+		},
+		{
+			description: "kubectl set manifest label with --set flag overrides transform in manifest",
+			args:        []string{"--offline=true", "--set", "app1=from-command-line"},
+			config: `apiVersion: skaffold/v4beta2
+kind: Config
+manifests:
+  rawYaml:
+  - k8s-pod.yaml
+  transform:
+    - name: apply-setters
+      configMap:
+        - "app1:from-apply-setters-1"
+`,
+			input: map[string]string{
+				"k8s-pod.yaml": `apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+  labels:
+    a: hhhh # kpt-set: ${app1}
+spec:
+  containers:
+  - name: getting-started
+    image: skaffold-example`,
+			}, expectedOut: `apiVersion: v1
+		kind: Pod
+		metadata:
+		 name: getting-started
+		 labels:
+		   a: from-command-line
+		spec:
+		 containers:
+		 - name: getting-started
+		   image: skaffold-example
+`,
+		},
+		{description: "kustomize set annotation with set-annotations transformer",
+			args: []string{"--offline=true"},
+			config: `apiVersion: skaffold/v4beta2
+kind: Config
+manifests:
+  kustomize:
+    paths:
+      - .
+  transform:
+    - name: set-annotations
+      configMap:
+        - "author:fake-author"`,
+			input: map[string]string{
+				"kustomization.yaml": `resources:
+  - deployment.yaml
+patchesStrategicMerge:
+  - patch.yaml
+`, "deployment.yaml": `apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: kustomize-test
+  labels:
+    app: kustomize-test
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: kustomize-test
+  template:
+    metadata:
+      labels:
+        app: kustomize-test
+    spec:
+      containers:
+      - name: kustomize-test
+        image: not/a/valid/image
+`, "patch.yaml": `apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: kustomize-test
+spec:
+  template:
+    spec:
+      containers:
+      - name: kustomize-test
+        image: index.docker.io/library/busybox
+        command:
+          - sleep
+          - "3600"
+`},
+			expectedOut: `apiVersion: apps/v1
+		kind: Deployment
+		metadata:
+		 annotations:
+		   author: fake-author
+		 labels:
+		   app: kustomize-test
+		 name: kustomize-test
+		spec:
+		 replicas: 1
+		 selector:
+		   matchLabels:
+		     app: kustomize-test
+		 template:
+		   metadata:
+		     annotations:
+		       author: fake-author
+		     labels:
+		       app: kustomize-test
+		   spec:
+		     containers:
+		     - command:
+		       - sleep
+		       - "3600"
+		       image: index.docker.io/library/busybox
+		       name: kustomize-test
+`},
+		{
+			description: "kustomize/overlay parameterization with --set flag",
+			args:        []string{"--offline", "--set", "env2=222a", "--set", "app1=111a"},
+			config: `apiVersion: skaffold/v4beta2
+kind: Config
+metadata:
+  name: getting-started-kustomize
+manifests:
+  kustomize:
+    paths:
+    - overlays/dev
+`, input: map[string]string{
+				"base/kustomization.yaml": `apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+  - deployment.yaml
+`, "base/deployment.yaml": `apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: skaffold-kustomize
+  labels:
+    app: skaffold-kustomize # kpt-set: ${app1}
+spec:
+  selector:
+    matchLabels:
+      app: skaffold-kustomize
+  template:
+    metadata:
+      labels:
+        app: skaffold-kustomize
+    spec:
+      containers:
+      - name: skaffold-kustomize
+        image: skaffold-kustomize
+`, "overlays/dev/kustomization.yaml": `apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+# namespace: dev
+nameSuffix: -dev
+
+patchesStrategicMerge:
+- deployment.yaml
+
+resources:
+- ../../base
+`, "overlays/dev/deployment.yaml": `apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: skaffold-kustomize
+  labels:
+    env: dev # kpt-set: ${env2}
+`}, expectedOut: `
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  labels:
+    app: 111a
+    env: 222a
+  name: skaffold-kustomize-dev
+spec:
+  selector:
+    matchLabels:
+      app: skaffold-kustomize
+  template:
+    metadata:
+      labels:
+        app: skaffold-kustomize
+    spec:
+      containers:
+      - image: skaffold-kustomize
+        name: skaffold-kustomize
+`,
+		},
+	}
+
+	for _, test := range tests {
+		testutil.Run(t, test.description, func(t *testutil.T) {
+			MarkIntegrationTest(t.T, CanRunWithoutGcp)
+			tmpDir := t.NewTempDir()
+			tmpDir.Write("skaffold.yaml", test.config)
+
+			for filePath, content := range test.input {
+				tmpDir.Write(filePath, content)
+			}
+
+			tmpDir.Chdir()
+			output := skaffold.Render(test.args...).RunOrFailOutput(t.T)
+			var out map[string]any
+			var ex map[string]any
+			err := yaml.Unmarshal(output, &out)
+			if err != nil {
+				return
+			}
+			err = yaml.Unmarshal([]byte(test.expectedOut), &ex)
+			if err != nil {
+				return
+			}
+
+			t.CheckDeepEqual(ex, out)
+		})
+	}
+}
+
 func TestRenderWithTagFlag(t *testing.T) {
 	MarkIntegrationTest(t, CanRunWithoutGcp)
 
diff --git a/integration/testdata/helm-render-simple/charts/Chart.yaml b/integration/testdata/helm-render-simple/charts/Chart.yaml
new file mode 100644
index 000000000..82cbe036a
--- /dev/null
+++ b/integration/testdata/helm-render-simple/charts/Chart.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+description: Skaffold example with Helm
+name: skaffold-helm
+version: 0.1.0
diff --git a/integration/testdata/helm-render-simple/charts/templates/deployment.yaml b/integration/testdata/helm-render-simple/charts/templates/deployment.yaml
new file mode 100644
index 000000000..aea1d4fdd
--- /dev/null
+++ b/integration/testdata/helm-render-simple/charts/templates/deployment.yaml
@@ -0,0 +1,20 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Chart.Name }}
+  namespace: {{ .Release.Namespace }}
+  labels:
+    app: {{ .Chart.Name }}
+spec:
+  selector:
+    matchLabels:
+      app: {{ .Chart.Name }}
+  replicas: {{ .Values.replicaCount }}
+  template:
+    metadata:
+      labels:
+        app: {{ .Chart.Name }}
+    spec:
+      containers:
+      - name: {{ .Chart.Name }}
+        image: {{ .Values.image }}
diff --git a/integration/testdata/helm-render-simple/charts/values.yaml b/integration/testdata/helm-render-simple/charts/values.yaml
new file mode 100644
index 000000000..b50415242
--- /dev/null
+++ b/integration/testdata/helm-render-simple/charts/values.yaml
@@ -0,0 +1,5 @@
+# Default values for skaffold-helm.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+replicaCount: 2
+image: skaffold-helm:latest
diff --git a/integration/testdata/helm-render-simple/skaffold.yaml b/integration/testdata/helm-render-simple/skaffold.yaml
new file mode 100644
index 000000000..4b41e74c5
--- /dev/null
+++ b/integration/testdata/helm-render-simple/skaffold.yaml
@@ -0,0 +1,8 @@
+apiVersion: skaffold/v4beta2
+kind: Config
+manifests:
+  helm:
+    releases:
+      - name: skaffold-helm
+        chartPath: charts
+        namespace: helm-namespace
\ No newline at end of file
diff --git a/pkg/skaffold/deploy/helm/helm_test.go b/pkg/skaffold/deploy/helm/helm_test.go
index 507fbd27a..cee6efb35 100644
--- a/pkg/skaffold/deploy/helm/helm_test.go
+++ b/pkg/skaffold/deploy/helm/helm_test.go
@@ -1360,7 +1360,7 @@ func TestHelmRender(t *testing.T) {
 				namespace: test.namespace,
 			}, latest.RenderConfig{
 				Generate: latest.Generate{Helm: &latest.Helm{Flags: test.helm.Flags, Releases: test.helm.Releases}},
-			}, labels, "default")
+			}, labels, "default", nil)
 			t.RequireNoError(err)
 			_, err = helmRenderer.Render(context.Background(), io.Discard, test.builds, true)
 			t.CheckError(test.shouldErr, err)
diff --git a/pkg/skaffold/deploy/kubectl/kubectl_test.go b/pkg/skaffold/deploy/kubectl/kubectl_test.go
index c81ef6024..9be31744a 100644
--- a/pkg/skaffold/deploy/kubectl/kubectl_test.go
+++ b/pkg/skaffold/deploy/kubectl/kubectl_test.go
@@ -58,6 +58,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			kubectl:          latest.KubectlDeploy{},
 			shouldErr:        true,
 			waitForDeletions: true,
+			commands: testutil.
+				CmdRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "default"),
 		},
 		{
 			description: "deploy success (disable validation)",
@@ -71,7 +73,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			},
 			commands: testutil.
 				CmdRunOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", "").
-				AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --validate=false"),
+				AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --validate=false").
+				AndRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "testNamespace"),
 			builds: []graph.Artifact{{
 				ImageName: "leeroy-web",
 				Tag:       "leeroy-web:v1",
@@ -85,7 +88,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			},
 			commands: testutil.
 				CmdRunOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", "").
-				AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --force --grace-period=0"),
+				AndRun("kubectl --context kubecontext --namespace testNamespace apply -f - --force --grace-period=0").
+				AndRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "testNamespace"),
 			builds: []graph.Artifact{{
 				ImageName: "leeroy-web",
 				Tag:       "leeroy-web:v1",
@@ -100,7 +104,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			},
 			commands: testutil.
 				CmdRunOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", "").
-				AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -"),
+				AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -").
+				AndRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "testNamespace"),
 			builds: []graph.Artifact{{
 				ImageName: "leeroy-web",
 				Tag:       "leeroy-web:v1",
@@ -114,7 +119,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			},
 			commands: testutil.
 				CmdRunOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", "").
-				AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -"),
+				AndRun("kubectl --context kubecontext --namespace testNamespace apply -f -").
+				AndRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "testNamespace"),
 			builds: []graph.Artifact{{
 				ImageName: "leeroy-web",
 				Tag:       "leeroy-web:v1",
@@ -128,7 +134,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			},
 			commands: testutil.
 				CmdRunOut("kubectl --context kubecontext get -f - --ignore-not-found -ojson", "").
-				AndRun("kubectl --context kubecontext apply -f -"),
+				AndRun("kubectl --context kubecontext apply -f -").
+				AndRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "default"),
 			builds: []graph.Artifact{{
 				ImageName: "leeroy-web",
 				Tag:       "leeroy-web:v1",
@@ -146,7 +153,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			},
 			commands: testutil.
 				CmdRunOut("kubectl --context kubecontext --namespace testNamespace2 get -f - --ignore-not-found -ojson", "").
-				AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f -"),
+				AndRun("kubectl --context kubecontext --namespace testNamespace2 apply -f -").
+				AndRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "testNamespace2"),
 			builds: []graph.Artifact{{
 				ImageName: "leeroy-web",
 				Tag:       "leeroy-web:v1",
@@ -164,7 +172,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			},
 			commands: testutil.
 				CmdRunOut("kubectl --context kubecontext --namespace testNamespace get -f - --ignore-not-found -ojson", "").
-				AndRunErr("kubectl --context kubecontext --namespace testNamespace apply -f -", fmt.Errorf("")),
+				AndRunErr("kubectl --context kubecontext --namespace testNamespace apply -f -", fmt.Errorf("")).
+				AndRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "testNamespace"),
 			builds: []graph.Artifact{{
 				ImageName: "leeroy-web",
 				Tag:       "leeroy-web:v1",
@@ -186,7 +195,8 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 			},
 			commands: testutil.
 				CmdRunOut("kubectl --context kubecontext --namespace testNamespace get -v=0 -f - --ignore-not-found -ojson", "").
-				AndRunErr("kubectl --context kubecontext --namespace testNamespace apply -v=0 --overwrite=true -f -", fmt.Errorf("")),
+				AndRunErr("kubectl --context kubecontext --namespace testNamespace apply -v=0 --overwrite=true -f -", fmt.Errorf("")).
+				AndRunOutOnce("kubectl config view --minify -o jsonpath='{..namespace}'", "testNamespace"),
 			builds: []graph.Artifact{{
 				ImageName: "leeroy-web",
 				Tag:       "leeroy-web:v1",
@@ -235,7 +245,7 @@ func TestKubectlV1RenderDeploy(t *testing.T) {
 				},
 			}
 
-			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, configName, "")
+			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, configName, "", nil)
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			m, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
@@ -330,7 +340,7 @@ func TestKubectlCleanup(t *testing.T) {
 				},
 			}
 
-			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, configName, "")
+			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, configName, "", nil)
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			m, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
@@ -534,7 +544,7 @@ func TestGCSManifests(t *testing.T) {
 					}, []string{configName}),
 				},
 			}
-			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, configName, "")
+			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, configName, "", nil)
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			m, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
diff --git a/pkg/skaffold/deploy/kubectl/kustomize_test.go b/pkg/skaffold/deploy/kubectl/kustomize_test.go
index bdeeb657a..a9e3ef04f 100644
--- a/pkg/skaffold/deploy/kubectl/kustomize_test.go
+++ b/pkg/skaffold/deploy/kubectl/kustomize_test.go
@@ -32,7 +32,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubectl"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/kubernetes/client"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/generate"
-	kubectlR "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/kubectl"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/render/renderer/kustomize"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/runner/runcontext"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
@@ -142,7 +142,7 @@ func TestKustomizeRenderDeploy(t *testing.T) {
 			mockCfg := &kubectlConfig{
 				RunContext: runcontext.RunContext{},
 			}
-			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, configName, "")
+			r, err := kustomize.New(mockCfg, rc, map[string]string{}, configName, "", nil)
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			m, errR := r.Render(context.Background(), &b, test.builds, true)
@@ -224,7 +224,7 @@ func TestKustomizeCleanup(t *testing.T) {
 				workingDir: tmpDir.Root(),
 				RunContext: runcontext.RunContext{},
 			}
-			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, configName, "")
+			r, err := kustomize.New(mockCfg, rc, map[string]string{}, configName, "", nil)
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			m, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
@@ -278,9 +278,11 @@ func TestDependenciesForKustomization(t *testing.T) {
 			expected: []string{"kustomization.yaml"},
 		},
 		{
-			description:    "patches legacy",
-			kustomizations: map[string]string{"kustomization.yaml": `patches: [patch1.yaml, path/patch2.yaml]`},
-			expected:       []string{"kustomization.yaml", "patch1.yaml", "path/patch2.yaml"},
+			description: "patches legacy",
+			kustomizations: map[string]string{"kustomization.yaml": `patches: 
+- path: patch1.yaml 
+- path: path/patch2.yaml`},
+			expected: []string{"kustomization.yaml", "patch1.yaml", "path/patch2.yaml"},
 		},
 		{
 			description:    "patchesStrategicMerge",
@@ -288,15 +290,15 @@ func TestDependenciesForKustomization(t *testing.T) {
 			expected:       []string{"kustomization.yaml", "patch1.yaml", "patch2.yaml", "path/patch3.yaml"},
 		},
 		{
-			description: "inline patchesStrategicMerge",
-			kustomizations: map[string]string{"kustomization.yaml": `patchesStrategicMerge:
-- |-
- apiVersion: v1`},
+			description: "inline patches",
+			kustomizations: map[string]string{"kustomization.yaml": `patches:
+- patch: |-
+  apiVersion: v1`},
 			expected: []string{"kustomization.yaml"},
 		},
 		{
 			description:    "crds",
-			kustomizations: map[string]string{"kustomization.yaml": `patches: [crd1.yaml, path/crd2.yaml]`},
+			kustomizations: map[string]string{"kustomization.yaml": `crds: [crd1.yaml, path/crd2.yaml]`},
 			expected:       []string{"crd1.yaml", "kustomization.yaml", "path/crd2.yaml"},
 		},
 		{
@@ -444,7 +446,7 @@ func TestDependenciesForKustomization(t *testing.T) {
 			mockCfg := &kubectlConfig{
 				RunContext: runcontext.RunContext{},
 			}
-			r, err := kubectlR.New(mockCfg, rc, map[string]string{}, "default", "")
+			r, err := kustomize.New(mockCfg, rc, map[string]string{}, "default", "", nil)
 			t.CheckNoError(err)
 
 			deps, err := r.ManifestDeps()
diff --git a/pkg/skaffold/render/generate/generate_test.go b/pkg/skaffold/render/generate/generate_test.go
index cd602e150..a0c66abf2 100644
--- a/pkg/skaffold/render/generate/generate_test.go
+++ b/pkg/skaffold/render/generate/generate_test.go
@@ -62,57 +62,6 @@ spec:
   containers:
   - name: leeroy-web3
     image: leeroy-web3
-`
-
-	// Test file under <tmp>/base/patch.yaml
-	patchYaml = `apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: kustomize-test
-spec:
-  template:
-    spec:
-      containers:
-      - name: kustomize-test
-        image: index.docker.io/library/busybox
-        command:
-          - sleep
-          - "3600"
-`
-	// Test file under <tmp>/base/deployment.yaml
-	kustomizeDeploymentYaml = `apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: kustomize-test
-  labels:
-    app: kustomize-test
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: kustomize-test
-  template:
-    metadata:
-      labels:
-        app: kustomize-test
-    spec:
-      containers:
-      - name: kustomize-test
-        image: not/a/valid/image
-`
-
-	// Test file under <tmp>/base/kustomization.yaml
-	kustomizeYaml = `resources:
-  - deployment.yaml
-patches:
-  - patch.yaml
-`
-	// Test file under <tmp>/fn/Kptfile
-	kptfileYaml = `apiVersion: kpt.dev/v1
-kind: Kptfile
-metadata:
-  name: fake-fn
-pipeline:
 `
 )
 
@@ -138,38 +87,6 @@ func TestGenerate(t *testing.T) {
 			},
 			expected: manifest.ManifestList{[]byte(podYaml), []byte(podsYaml)},
 		},
-		{
-			description: "render kustomize manifests",
-			generateConfig: latest.Generate{
-				Kustomize: &latest.Kustomize{
-					Paths: []string{"base"},
-				},
-			},
-			commands: testutil.CmdRunOut("kustomize build base", podsYaml),
-			expected: manifest.ManifestList{[]byte(podsYaml)},
-		},
-		{
-			description: "render kustomize manifests - kubectl",
-			generateConfig: latest.Generate{
-				Kustomize: &latest.Kustomize{
-					Paths: []string{"base"},
-				},
-			},
-			useKubectlKustomize: true,
-			commands:            testutil.CmdRunOut("kustomize build base", patchYaml),
-			expected:            manifest.ManifestList{[]byte(patchYaml)},
-		},
-
-		{
-			description: "render kpt manifests",
-			generateConfig: latest.Generate{
-				Kpt: []string{"Kptfile"},
-			},
-			// Using "filepath" to join path so as the result can fix when running in either linux or
-			// windows (skaffold integration test).
-			commands: testutil.CmdRun(fmt.Sprintf("kpt fn render fn --output=%v", "fn")),
-			expected: manifest.ManifestList{},
-		},
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
@@ -183,17 +100,12 @@ func TestGenerate(t *testing.T) {
 			t.NewTempDir().
 				Write("pod.yaml", podYaml).
 				Write("pods.yaml", podsYaml).
-				Write("base/kustomization.yaml", kustomizeYaml).
-				Write("base/patch.yaml", patchYaml).
-				Write("base/deployment.yaml", kustomizeDeploymentYaml).
-				Write("fn/Kptfile", kptfileYaml).
 				Touch("empty.ignored").
 				Chdir()
 
 			g := NewGenerator(".", test.generateConfig, "")
 			var output bytes.Buffer
 			actual, err := g.Generate(context.Background(), &output)
-			defer os.RemoveAll(".kpt-pipeline")
 			t.CheckNoError(err)
 			t.CheckDeepEqual(actual.String(), test.expected.String())
 		})
@@ -239,38 +151,14 @@ func TestManifestDeps(t *testing.T) {
 			},
 			expected: []string{"rawYaml-sample/pod.yaml"},
 		},
-		{
-			description: "kustomize dir",
-			generateConfig: latest.Generate{
-				Kustomize: &latest.Kustomize{
-					Paths: []string{"kustomize-sample"},
-				},
-			},
-			expected: []string{"kustomize-sample/kustomization.yaml", "kustomize-sample/patch.yaml"},
-		},
-		{
-			description: "kpt dir",
-			generateConfig: latest.Generate{
-				Kpt: []string{"kpt-sample"},
-			},
-			expected: []string{"kpt-sample/Kptfile", "kpt-sample/deployment.yaml"},
-		},
 		{
 			description: "multi manifest, mixed dir and file",
 			generateConfig: latest.Generate{
 				RawK8s: []string{"rawYaml-sample"},
-				Kustomize: &latest.Kustomize{
-					Paths: []string{"kustomize-sample"},
-				},
-				Kpt: []string{"kpt-sample"},
 			},
 			expected: []string{
-				"kpt-sample/Kptfile",
-				"kpt-sample/deployment.yaml",
 				"rawYaml-sample/pod.yaml",
 				"rawYaml-sample/pods2.yaml",
-				"kustomize-sample/kustomization.yaml",
-				"kustomize-sample/patch.yaml",
 			},
 		},
 	}
@@ -280,10 +168,6 @@ func TestManifestDeps(t *testing.T) {
 			tmpDir.Write("rawYaml-sample/pod.yaml", podYaml).
 				Write("rawYaml-sample/pods2.yaml", podsYaml).
 				Write("rawYaml-sample/irrelevant.txt", "").
-				Write("kustomize-sample/kustomization.yaml", kustomizeYaml).
-				Write("kustomize-sample/patch.yaml", patchYaml).
-				Write("kpt-sample/Kptfile", kptfileYaml).
-				Write("kpt-sample/deployment.yaml", kustomizeDeploymentYaml).
 				Touch("empty.ignored").
 				Chdir()
 			expectedPaths := []string{}
@@ -297,68 +181,3 @@ func TestManifestDeps(t *testing.T) {
 		})
 	}
 }
-
-func TestBuildCommandArgs(t *testing.T) {
-	tests := []struct {
-		description   string
-		buildArgs     []string
-		kustomizePath string
-		expectedArgs  []string
-	}{
-		{
-			description:   "no BuildArgs, empty KustomizePaths ",
-			buildArgs:     []string{},
-			kustomizePath: "",
-			expectedArgs:  nil,
-		},
-		{
-			description:   "One BuildArg, empty KustomizePaths",
-			buildArgs:     []string{"--foo"},
-			kustomizePath: "",
-			expectedArgs:  []string{"--foo"},
-		},
-		{
-			description:   "no BuildArgs, non-empty KustomizePaths",
-			buildArgs:     []string{},
-			kustomizePath: "foo",
-			expectedArgs:  []string{"foo"},
-		},
-		{
-			description:   "One BuildArg, non-empty KustomizePaths",
-			buildArgs:     []string{"--foo"},
-			kustomizePath: "bar",
-			expectedArgs:  []string{"--foo", "bar"},
-		},
-		{
-			description:   "Multiple BuildArg, empty KustomizePaths",
-			buildArgs:     []string{"--foo", "--bar"},
-			kustomizePath: "",
-			expectedArgs:  []string{"--foo", "--bar"},
-		},
-		{
-			description:   "Multiple BuildArg with spaces, empty KustomizePaths",
-			buildArgs:     []string{"--foo bar", "--baz"},
-			kustomizePath: "",
-			expectedArgs:  []string{"--foo", "bar", "--baz"},
-		},
-		{
-			description:   "Multiple BuildArg with spaces, non-empty KustomizePaths",
-			buildArgs:     []string{"--foo bar", "--baz"},
-			kustomizePath: "barfoo",
-			expectedArgs:  []string{"--foo", "bar", "--baz", "barfoo"},
-		},
-		{
-			description:   "Multiple BuildArg no spaces, non-empty KustomizePaths",
-			buildArgs:     []string{"--foo", "bar", "--baz"},
-			kustomizePath: "barfoo",
-			expectedArgs:  []string{"--foo", "bar", "--baz", "barfoo"},
-		},
-	}
-
-	for _, test := range tests {
-		testutil.Run(t, test.description, func(t *testutil.T) {
-			args := kustomizeBuildArgs(test.buildArgs, test.kustomizePath)
-			t.CheckDeepEqual(test.expectedArgs, args)
-		})
-	}
-}
diff --git a/pkg/skaffold/render/renderer/kpt/kpt_test.go b/pkg/skaffold/render/renderer/kpt/kpt_test.go
index 3b12ee42a..2586e71b5 100644
--- a/pkg/skaffold/render/renderer/kpt/kpt_test.go
+++ b/pkg/skaffold/render/renderer/kpt/kpt_test.go
@@ -71,99 +71,11 @@ func TestRender(t *testing.T) {
 		updatedKptfile  string
 	}{
 		{
-			description: "single manifest, no hydration rule",
+			description: "single manifest",
 			renderConfig: latest.RenderConfig{
-				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
+				Generate: latest.Generate{},
 			},
 			originalKptfile: initKptfile,
-			updatedKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline: {}
-`,
-		},
-		{
-			description: "manifests with validation rule.",
-			renderConfig: latest.RenderConfig{
-				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
-				Validate: &[]latest.Validator{{Name: "kubeval"}},
-			},
-			originalKptfile: initKptfile,
-			updatedKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline:
-  validators:
-  - image: gcr.io/kpt-fn/kubeval:v0.1
-`,
-		},
-		{
-			description: "manifests with updated validation rule.",
-			renderConfig: latest.RenderConfig{
-				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
-				Validate: &[]latest.Validator{{Name: "kubeval"}},
-			},
-			originalKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline:
-  validators:
-  - image: gcr.io/kpt-fn/SOME-OTHER-FUNC
-`,
-			updatedKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline:
-  validators:
-  - image: gcr.io/kpt-fn/kubeval:v0.1
-`,
-		},
-		{
-			description: "manifests with transformation rule.",
-			renderConfig: latest.RenderConfig{
-				Generate:  latest.Generate{RawK8s: []string{"pod.yaml"}},
-				Transform: &[]latest.Transformer{{Name: "set-labels", ConfigMap: []string{"owner:tester"}}},
-			},
-			originalKptfile: initKptfile,
-			updatedKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline:
-  mutators:
-  - image: gcr.io/kpt-fn/set-labels:v0.1
-    configMap:
-      owner: tester
-`,
-		},
-		{
-			description: "manifests with updated transformation rule.",
-			renderConfig: latest.RenderConfig{
-				Generate:  latest.Generate{RawK8s: []string{"pod.yaml"}},
-				Transform: &[]latest.Transformer{{Name: "set-labels", ConfigMap: []string{"owner:tester"}}},
-			},
-			originalKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline:
-  mutators:
-  - image: gcr.io/kpt-fn/SOME-OTHER-FUNC
-`,
-			updatedKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline:
-  mutators:
-  - image: gcr.io/kpt-fn/set-labels:v0.1
-    configMap:
-      owner: tester
-`,
 		},
 	}
 	for _, test := range tests {
@@ -176,90 +88,18 @@ pipeline:
 			mockCfg := render.MockConfig{
 				WorkingDir: tmpDirObj.Root(),
 			}
-			r, err := New(mockCfg, test.renderConfig, filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir), map[string]string{}, "default", "")
+			test.renderConfig.Kpt = []string{filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir)}
+			r, err := New(mockCfg, test.renderConfig, filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir), map[string]string{}, "default", "", nil)
 			t.CheckNoError(err)
 			t.Override(&util.DefaultExecCommand,
-				testutil.CmdRun(fmt.Sprintf("kpt fn render %v",
-					filepath.Join(tmpDirObj.Root(), ".kpt-pipeline"))).
-					AndRunOut(fmt.Sprintf("kpt fn source %v -o unwrap",
-						filepath.Join(tmpDirObj.Root(), ".kpt-pipeline")), ""))
+				testutil.CmdRunOut(fmt.Sprintf("kpt fn render %v -o unwrap",
+					filepath.Join(tmpDirObj.Root(), ".kpt-pipeline")), podYaml))
 			var b bytes.Buffer
-			_, err = r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
+			manifests, err := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
 				false)
 			t.CheckNoError(err)
-			t.CheckFileExistAndContent(filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir, DryFileName), []byte(labeledPodYaml))
-			t.CheckFileExistAndContent(filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir, kptfile.KptFileName), []byte(test.updatedKptfile))
-		})
-	}
-}
-
-func TestRender_StashKptinfo(t *testing.T) {
-	tests := []struct {
-		description     string
-		originalKptfile string
-		updatedKptfile  string
-	}{
-		{
-			description:     "kpt initialized, manifests are not kpt applied before (no inventory info)",
-			originalKptfile: initKptfile,
-			updatedKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline:
-  validators:
-  - image: gcr.io/kpt-fn/kubeval:v0.1
-`,
-		},
-		{
-			description: "manifests has been previously kpt applied (with inventory info)",
-			originalKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-inventory:
-  namespace: skaffold-test
-  inventoryID: 11111
-`,
-			updatedKptfile: `apiVersion: kpt.dev/v1alpha2
-kind: Kptfile
-metadata:
-  name: skaffold
-pipeline:
-  validators:
-  - image: gcr.io/kpt-fn/kubeval:v0.1
-inventory:
-  namespace: skaffold-test
-  inventoryID: "11111"
-`,
-		},
-	}
-	for _, test := range tests {
-		testutil.Run(t, test.description, func(t *testutil.T) {
-			tmpDirObj := t.NewTempDir()
-			tmpDirObj.Write("pod.yaml", podYaml).
-				Write(filepath.Join(constants.DefaultHydrationDir, kptfile.KptFileName), test.originalKptfile).
-				Chdir()
-			renderConfig := latest.RenderConfig{
-				Generate: latest.Generate{RawK8s: []string{"pod.yaml"}},
-				Validate: &[]latest.Validator{{Name: "kubeval"}},
-			}
-			mockCfg := render.MockConfig{
-				WorkingDir: tmpDirObj.Root(),
-			}
-			r, err := New(mockCfg, renderConfig, filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir), map[string]string{}, "default", "")
-			t.CheckNoError(err)
-			t.Override(&util.DefaultExecCommand,
-				testutil.CmdRun(fmt.Sprintf("kpt fn render %v",
-					filepath.Join(tmpDirObj.Root(), ".kpt-pipeline"))).
-					AndRunOut(fmt.Sprintf("kpt fn source %v -o unwrap",
-						filepath.Join(tmpDirObj.Root(), ".kpt-pipeline")), ""))
-			var b bytes.Buffer
-			_, err = r.Render(context.Background(), &b, []graph.Artifact{},
-				true)
-			t.CheckNoError(err)
-			t.CheckFileExistAndContent(filepath.Join(tmpDirObj.Root(), constants.DefaultHydrationDir, kptfile.KptFileName),
-				[]byte(test.updatedKptfile))
+			s := manifests.String() + "\n"
+			t.CheckDeepEqual(s, labeledPodYaml)
 		})
 	}
 }
diff --git a/pkg/skaffold/render/renderer/kubectl/kubectl_test.go b/pkg/skaffold/render/renderer/kubectl/kubectl_test.go
index 3bd07ea30..906104365 100644
--- a/pkg/skaffold/render/renderer/kubectl/kubectl_test.go
+++ b/pkg/skaffold/render/renderer/kubectl/kubectl_test.go
@@ -92,7 +92,7 @@ func TestRender(t *testing.T) {
 				Touch("empty.ignored").
 				Chdir()
 			mockCfg := render.MockConfig{WorkingDir: tmpDirObj.Root()}
-			r, err := New(mockCfg, test.renderConfig, test.labels, "default", "")
+			r, err := New(mockCfg, test.renderConfig, test.labels, "default", "", nil)
 			t.CheckNoError(err)
 			var b bytes.Buffer
 			manifestList, errR := r.Render(context.Background(), &b, []graph.Artifact{{ImageName: "leeroy-web", Tag: "leeroy-web:v1"}},
@@ -157,7 +157,7 @@ func TestDependencies(t *testing.T) {
 			rCfg := latest.RenderConfig{
 				Generate: latest.Generate{RawK8s: test.manifests},
 			}
-			r, err := New(mockCfg, rCfg, map[string]string{}, "default", "")
+			r, err := New(mockCfg, rCfg, map[string]string{}, "default", "", nil)
 			t.CheckNoError(err)
 
 			dependencies, err := r.ManifestDeps()
diff --git a/pkg/skaffold/render/renderer/kustomize/kustomize_test.go b/pkg/skaffold/render/renderer/kustomize/kustomize_test.go
new file mode 100644
index 000000000..3f7ca4b90
--- /dev/null
+++ b/pkg/skaffold/render/renderer/kustomize/kustomize_test.go
@@ -0,0 +1,88 @@
+/*
+Copyright 2022 The Skaffold Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kustomize
+
+import (
+	"testing"
+
+	"github.com/GoogleContainerTools/skaffold/v2/testutil"
+)
+
+func TestBuildCommandArgs(t *testing.T) {
+	tests := []struct {
+		description   string
+		buildArgs     []string
+		kustomizePath string
+		expectedArgs  []string
+	}{
+		{
+			description:   "no BuildArgs, empty KustomizePaths ",
+			buildArgs:     []string{},
+			kustomizePath: "",
+			expectedArgs:  nil,
+		},
+		{
+			description:   "One BuildArg, empty KustomizePaths",
+			buildArgs:     []string{"--foo"},
+			kustomizePath: "",
+			expectedArgs:  []string{"--foo"},
+		},
+		{
+			description:   "no BuildArgs, non-empty KustomizePaths",
+			buildArgs:     []string{},
+			kustomizePath: "foo",
+			expectedArgs:  []string{"foo"},
+		},
+		{
+			description:   "One BuildArg, non-empty KustomizePaths",
+			buildArgs:     []string{"--foo"},
+			kustomizePath: "bar",
+			expectedArgs:  []string{"--foo", "bar"},
+		},
+		{
+			description:   "Multiple BuildArg, empty KustomizePaths",
+			buildArgs:     []string{"--foo", "--bar"},
+			kustomizePath: "",
+			expectedArgs:  []string{"--foo", "--bar"},
+		},
+		{
+			description:   "Multiple BuildArg with spaces, empty KustomizePaths",
+			buildArgs:     []string{"--foo bar", "--baz"},
+			kustomizePath: "",
+			expectedArgs:  []string{"--foo", "bar", "--baz"},
+		},
+		{
+			description:   "Multiple BuildArg with spaces, non-empty KustomizePaths",
+			buildArgs:     []string{"--foo bar", "--baz"},
+			kustomizePath: "barfoo",
+			expectedArgs:  []string{"--foo", "bar", "--baz", "barfoo"},
+		},
+		{
+			description:   "Multiple BuildArg no spaces, non-empty KustomizePaths",
+			buildArgs:     []string{"--foo", "bar", "--baz"},
+			kustomizePath: "barfoo",
+			expectedArgs:  []string{"--foo", "bar", "--baz", "barfoo"},
+		},
+	}
+
+	for _, test := range tests {
+		testutil.Run(t, test.description, func(t *testutil.T) {
+			args := kustomizeBuildArgs(test.buildArgs, test.kustomizePath)
+			t.CheckDeepEqual(test.expectedArgs, args)
+		})
+	}
+}
diff --git a/pkg/skaffold/runner/renderer_test.go b/pkg/skaffold/runner/renderer_test.go
index 1407b3d5c..3ee19177b 100644
--- a/pkg/skaffold/runner/renderer_test.go
+++ b/pkg/skaffold/runner/renderer_test.go
@@ -85,7 +85,7 @@ func TestGetRenderer(tOuter *testing.T) {
 				expected: renderer.NewRenderMux(
 					renderer.GroupRenderer{
 						Renderers: []renderer.Renderer{
-							t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)},
+							t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "", nil)).(renderer.Renderer)},
 					},
 				),
 			},
@@ -97,7 +97,7 @@ func TestGetRenderer(tOuter *testing.T) {
 				expected: renderer.NewRenderMux(
 					renderer.GroupRenderer{
 						Renderers: []renderer.Renderer{
-							t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)},
+							t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "", nil)).(renderer.Renderer)},
 					},
 				),
 			},
@@ -109,7 +109,7 @@ func TestGetRenderer(tOuter *testing.T) {
 				expected: renderer.NewRenderMux(
 					renderer.GroupRenderer{
 						Renderers: []renderer.Renderer{
-							t.RequireNonNilResult(helm.New(rc, kubectlCfg, labels, "")).(renderer.Renderer)},
+							t.RequireNonNilResult(helm.New(rc, kubectlCfg, labels, "", nil)).(renderer.Renderer)},
 					},
 				),
 			},
@@ -121,7 +121,7 @@ func TestGetRenderer(tOuter *testing.T) {
 				expected: renderer.NewRenderMux(
 					renderer.GroupRenderer{
 						Renderers: []renderer.Renderer{
-							t.RequireNonNilResult(helm.New(rc, kptConfig, labels, "")).(renderer.Renderer)},
+							t.RequireNonNilResult(helm.New(rc, kptConfig, labels, "", nil)).(renderer.Renderer)},
 					},
 				),
 			},
@@ -136,7 +136,7 @@ func TestGetRenderer(tOuter *testing.T) {
 				expected: renderer.NewRenderMux(
 					renderer.GroupRenderer{
 						Renderers: []renderer.Renderer{
-							t.RequireNonNilResult(helm.New(rc, kptConfig, labels, "")).(renderer.Renderer)},
+							t.RequireNonNilResult(helm.New(rc, kptConfig, labels, "", nil)).(renderer.Renderer)},
 					},
 				),
 			},
diff --git a/pkg/skaffold/sync/sync_test.go b/pkg/skaffold/sync/sync_test.go
index d0cbd4f9f..43078cc13 100644
--- a/pkg/skaffold/sync/sync_test.go
+++ b/pkg/skaffold/sync/sync_test.go
@@ -887,6 +887,10 @@ func (t *TestCmdRecorder) RunCmdOut(ctx context.Context, cmd *exec.Cmd) ([]byte,
 	return nil, t.RunCmd(ctx, cmd)
 }
 
+func (t *TestCmdRecorder) RunCmdOutOnce(ctx context.Context, cmd *exec.Cmd) ([]byte, error) {
+	return nil, t.RunCmd(ctx, cmd)
+}
+
 func fakeCmd(ctx context.Context, _ v1.Pod, _ v1.Container, files syncMap) *exec.Cmd {
 	var args []string
 
diff --git a/testutil/cmd_helper.go b/testutil/cmd_helper.go
index b93dceeec..83290e6e6 100644
--- a/testutil/cmd_helper.go
+++ b/testutil/cmd_helper.go
@@ -19,6 +19,7 @@ package testutil
 import (
 	"context"
 	"errors"
+	"fmt"
 	"io"
 	"os/exec"
 	"strings"
@@ -29,6 +30,7 @@ type FakeCmd struct {
 	t           *testing.T
 	runs        []run
 	timesCalled int
+	runOnce     map[string]run
 }
 
 type run struct {
@@ -42,7 +44,9 @@ type run struct {
 }
 
 func newFakeCmd() *FakeCmd {
-	return &FakeCmd{}
+	return &FakeCmd{
+		runOnce: map[string]run{},
+	}
 }
 
 func (c *FakeCmd) addRun(r run) *FakeCmd {
@@ -82,6 +86,10 @@ func CmdRunOut(command string, output string) *FakeCmd {
 	return newFakeCmd().AndRunOut(command, output)
 }
 
+func CmdRunOutOnce(command string, output string) *FakeCmd {
+	return newFakeCmd().AndRunOutOnce(command, output)
+}
+
 func CmdRunDirOut(command string, dir string, output string) *FakeCmd {
 	return newFakeCmd().AndRunDirOut(command, dir, output)
 }
@@ -145,6 +153,15 @@ func (c *FakeCmd) AndRunOut(command string, output string) *FakeCmd {
 	})
 }
 
+func (c *FakeCmd) AndRunOutOnce(command string, output string) *FakeCmd {
+	r := run{
+		command: command,
+		output:  []byte(output),
+	}
+	c.runOnce[command] = r
+	return c
+}
+
 func (c *FakeCmd) AndRunDirOut(command string, dir string, output string) *FakeCmd {
 	return c.addRun(run{
 		command: command,
@@ -195,6 +212,18 @@ func (c *FakeCmd) RunCmdOut(ctx context.Context, cmd *exec.Cmd) ([]byte, error)
 	return r.output, r.err
 }
 
+func (c *FakeCmd) RunCmdOutOnce(ctx context.Context, cmd *exec.Cmd) ([]byte, error) {
+	c.timesCalled++
+	command := strings.Join(cmd.Args, " ")
+
+	r, found := c.runOnce[command]
+	if !found {
+		return nil, fmt.Errorf("expected command not found: %s", command)
+	}
+
+	return r.output, r.err
+}
+
 func (c *FakeCmd) RunCmd(ctx context.Context, cmd *exec.Cmd) error {
 	c.timesCalled++
 	command := strings.Join(cmd.Args, " ")