Repository: GoogleContainerTools/skaffold
Pull request #7794 (2022-08-23 11:01:35)

chore: transform installation of go-licenses from go-get to go install
**Description**
<!-- Describe your changes here. The more detail, the easier the review! -->

Starting in Go 1.17, installing executables with go get is deprecated... Given the project is still using go 1.17, at some point, there might be a need to transform to go install. 
This PR was proposed as I had errors while I was trying to build the project locally...

before:
```shell
make
hack/generate-embedded-files.sh
Installing go-licenses
.
.
go: added gopkg.in/src-d/go-git.v4 v4.13.1
go: added gopkg.in/warnings.v0 v0.1.2
~/go/src/github.com/boranx/skaffold
Collecting licenses
hack/generate-embedded-files.sh: line 42: /home/boran/go/src/github.com/boranx/skaffold/hack/bin/go-licenses: No such file or directory
make: *** [Makefile:334: fs/assets/check.txt] Error 127
```

after: / expected
```shell
make
hack/generate-embedded-files.sh
Collecting licenses
Collecting schemas
mkdir -p ./out
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 \
    go build -gcflags="all=-N -l" -tags "osusergo netgo static_build release " -ldflags "-X github.com/GoogleContainerTools/skaffold/pkg/skaffold/version.version=ac51b4a90 -X github.com/GoogleContainerTools/skaffold/pkg/skaffold/version.buildDate=2022-08-23T12:59:57Z -X github.com/GoogleContainerTools/skaffold/pkg/skaffold/version.gitCommit=ac51b4a90b45a75e43100bf4dda1877f277d7d09 -s -w -extldflags \"-static\"" -o out/skaffold github.com/GoogleContainerTools/skaffold/cmd/skaffold
```


google-cla[bot]: Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this [failed invocation](https://github.com/GoogleContainerTools/skaffold/pull/7794/checks?check_run_id=7971671453) of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.
codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) Report
> Merging [#7794](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (ac51b4a) 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 `3.98%`.
> The diff coverage is `53.59%`.

```diff
@@            Coverage Diff             @@
##             main    #7794      +/-   ##
==========================================
- Coverage   70.48%   66.50%   -3.99%     
==========================================
  Files         515      592      +77     
  Lines       23150    28591    +5441     
==========================================
+ Hits        16317    19014    +2697     
- Misses       5776     8172    +2396     
- Partials     1057     1405     +348     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794?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/credits/export.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY3JlZGl0cy9leHBvcnQuZ28=) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/deploy.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGVwbG95Lmdv) | `40.90% <0.00%> (-12.94%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/test.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvdGVzdC5nbw==) | `44.44% <0.00%> (ø)` | |
| [cmd/skaffold/skaffold.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL3NrYWZmb2xkLmdv) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_tests.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF90ZXN0cy5nbw==) | `62.50% <14.28%> (-1.14%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/render.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcmVuZGVyLmdv) | `35.48% <18.18%> (-5.90%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/lsp.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvbHNwLmdv) | `28.12% <28.12%> (ø)` | |
| [cmd/skaffold/app/cmd/run.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcnVuLmdv) | `64.28% <33.33%> (-9.63%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/fix.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZml4Lmdv) | `56.41% <37.50%> (-20.07%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/verify.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvdmVyaWZ5Lmdv) | `41.17% <41.17%> (ø)` | |
| ... and [360 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7794/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | |

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


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa422bff9..733c4e9c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,78 @@
+# v2.0.0-beta2 Release - 08/30/2022
+**Linux amd64**
+`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-linux-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin`
+
+**Linux arm64**
+`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-linux-arm64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin`
+
+**macOS amd64**
+`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-darwin-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin`
+
+**macOS arm64**
+`curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-darwin-arm64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin`
+
+**Windows**
+https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-windows-amd64.exe
+
+**Docker image**
+`gcr.io/k8s-skaffold/skaffold:v2.0.0-beta2`
+
+Note: This release comes with a new config version, `v3alpha1`. To upgrade your skaffold.yaml, use `skaffold fix`. If you choose not to upgrade, skaffold will auto-upgrade as best as it can.
+
+Skaffold’s v2 beta release `v2.0.0-beta2` is out today!  To try it out, you can go to our [v2.0.0-beta2 installation guide](https://skaffold-v2.web.app/docs/install/) or use the instructions above.  For information on migrating from skaffold v1.X.Y to skaffold v2.0.0-beta2 see our [upgrade guide here](https://skaffold-v2.web.app/docs/upgrading-to-v2/).  TLDR; there should not be any changes required for most use cases, see the [upgrade guide here](https://skaffold-v2.web.app/docs/upgrading-to-v2/) for the full details.  
+
+Highlights:
+
+See Skaffold's [v2.0.0-beta1 Release Notes](https://github.com/GoogleContainerTools/skaffold/releases/tag/v2.0.0-beta1) "Highlights" section for additional information on the large feature additions and changes Skaffold V2 brings.  Below will be the incremental highlights from v2.0.0-beta1 to v2.0.0-beta2
+* cross-platform AND multi-platform support for Skaffold is now feature complete!  See the [cross-platform and multi-platform docs](https://skaffold-v2-latest.web.app/docs/pipeline-stages/builders/#cross-platform-and-multi-platform-build-support/) and the [Managing ARM Workloads docs](https://skaffold-v2.web.app/docs/workflows/handling-platforms/) for the full information on the feature.  See also the [sample skaffold.yaml & app](https://github.com/GoogleContainerTools/skaffold/blob/main/integration/examples/cross-platform-builds/skaffold.yaml) here.
+* skaffold render phase now supports lifecycle hooks
+* skaffold verify now supports profiles
+* skaffold init functionality for helm applications has increased support for Skaffold V2 - apiVersion:skaffold/v3alpha1
+
+
+New Features and Additions:
+* feat: add profile support to skaffold verify [#7807](https://github.com/GoogleContainerTools/skaffold/pull/7807)
+* feat: upgrade profiles when running skaffold fix [#7800](https://github.com/GoogleContainerTools/skaffold/pull/7800)
+* feat: add hooks support to render schema and phase [#7785](https://github.com/GoogleContainerTools/skaffold/pull/7785)
+
+Fixes:
+* fix: helm template warnings breaking yaml parsing [#7825](https://github.com/GoogleContainerTools/skaffold/pull/7825)
+* fix: Remove the flag from deploy command [#7823](https://github.com/GoogleContainerTools/skaffold/pull/7823)
+* fix: Fill IMAGE_TAG,etc on Docker builds [#7788](https://github.com/GoogleContainerTools/skaffold/pull/7788)
+* chore: Add integration test for build cross platform images [#7818](https://github.com/GoogleContainerTools/skaffold/pull/7818)
+* feat: add toleration for GKE ARM nodes taint [#7789](https://github.com/GoogleContainerTools/skaffold/pull/7789)
+* fix: add auth to image pull [#7814](https://github.com/GoogleContainerTools/skaffold/pull/7814)
+* fix: check platform values for known OS and Arch, and fail fast [#7817](https://github.com/GoogleContainerTools/skaffold/pull/7817)
+* fix: add host platform metric to platform type events [#7821](https://github.com/GoogleContainerTools/skaffold/pull/7821)
+* fix: make helm renderer to use manifest.ManifestList  [#7795](https://github.com/GoogleContainerTools/skaffold/pull/7795)
+* fix: add node unschedulable to retriable errors [#7798](https://github.com/GoogleContainerTools/skaffold/pull/7798)
+* fix: remove required tag from cloud run region [#7784](https://github.com/GoogleContainerTools/skaffold/pull/7784)
+* fix: added apply to --rpc-port and --rpc-http-port definedons [#7799](https://github.com/GoogleContainerTools/skaffold/pull/7799)
+
+Updates and Refactors:
+* chore: raise error when there is a multiplatform build without container registry [#7786](https://github.com/GoogleContainerTools/skaffold/pull/7786)
+* chore: safe type assersions [#7770](https://github.com/GoogleContainerTools/skaffold/pull/7770)
+* chore: add-render-event-in-runner [#7781](https://github.com/GoogleContainerTools/skaffold/pull/7781)
+* refactor: remove v1 runner interfaces and simplify code  [#7724](https://github.com/GoogleContainerTools/skaffold/pull/7724)
+
+Docs, Test, and Release Updates:
+fix: improve multi-arch docs [#7767](https://github.com/GoogleContainerTools/skaffold/pull/7767)
+
+Huge thanks goes out to all of our contributors for this release:
+
+- 조태혁
+- Aaron Prindle
+- Benjamin Kaplan
+- dependabot[bot]
+- dhodun
+- ericzzzzzzz
+- Gaurav
+- Halvard Skogsrud
+- Karolína Lišková
+- Oladapo Ajala
+- Pablo Caderno
+- Renzo Rojas
+- Tejal Desai
 # v2.0.0-beta1 Release - 08/03/2022
 **Linux amd64**
 `curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-linux-amd64 && chmod +x skaffold && sudo mv skaffold /usr/local/bin`
@@ -19,7 +94,7 @@ https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-windows-a
 
 Note: This release comes with a new config version, `v3alpha1`. To upgrade your skaffold.yaml, use `skaffold fix`. If you choose not to upgrade, skaffold will auto-upgrade as best as it can.
 
-Skaffold’s v2 beta release `v2.0.0-beta1` is out today!  To try it out, you can go to our [v2.0.0-beta1 installation guide](https://skaffold-v2.web.app/docs/install/) or use the instructions above.  For information on migrating from skaffold v1.*.* to skaffold v2.0.0-beta1 see our [upgrade guide here](https://skaffold-v2.web.app/docs/upgrading-to-v2/).  TLDR; there should not be any changes required for most use cases, see the [upgrade guide here](https://skaffold-v2.web.app/docs/upgrading-to-v2/) for the full details.  
+Skaffold’s v2 beta release `v2.0.0-beta1` is out today!  To try it out, you can go to our [v2.0.0-beta1 installation guide](https://skaffold-v2.web.app/docs/install/) or use the instructions above.  For information on migrating from skaffold v1.X.Y to skaffold v2.0.0-beta1 see our [upgrade guide here](https://skaffold-v2.web.app/docs/upgrading-to-v2/).  TLDR; there should not be any changes required for most use cases, see the [upgrade guide here](https://skaffold-v2.web.app/docs/upgrading-to-v2/) for the full details.  
 
 
 Highlights:
diff --git a/cmd/skaffold/app/cmd/flags.go b/cmd/skaffold/app/cmd/flags.go
index b3b54ef4a..39148fe2d 100644
--- a/cmd/skaffold/app/cmd/flags.go
+++ b/cmd/skaffold/app/cmd/flags.go
@@ -104,7 +104,7 @@ var flagRegistry = []Flag{
 		Value:         &opts.Profiles,
 		DefValue:      []string{},
 		FlagAddMethod: "StringSliceVar",
-		DefinedOn:     []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "apply", "test"},
+		DefinedOn:     []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "apply", "test", "verify"},
 	},
 	{
 		Name:          "namespace",
@@ -213,7 +213,7 @@ var flagRegistry = []Flag{
 		Value:         &opts.RPCPort,
 		DefValue:      nil,
 		FlagAddMethod: "Var",
-		DefinedOn:     []string{"dev", "build", "run", "debug", "deploy", "test", "verify"},
+		DefinedOn:     []string{"dev", "build", "run", "debug", "deploy", "test", "verify", "apply"},
 	},
 	{
 		Name:          "rpc-http-port",
@@ -221,7 +221,7 @@ var flagRegistry = []Flag{
 		Value:         &opts.RPCHTTPPort,
 		DefValue:      nil,
 		FlagAddMethod: "Var",
-		DefinedOn:     []string{"dev", "build", "run", "debug", "deploy", "test", "verify"},
+		DefinedOn:     []string{"dev", "build", "run", "debug", "deploy", "test", "verify", "apply"},
 	},
 	{
 		Name:          "label",
@@ -401,7 +401,7 @@ var flagRegistry = []Flag{
 		Value:         &opts.Platforms,
 		DefValue:      []string{},
 		FlagAddMethod: "StringSliceVar",
-		DefinedOn:     []string{"build", "debug", "dev", "run", "deploy"},
+		DefinedOn:     []string{"build", "debug", "dev", "run"},
 	},
 	{
 		Name:          "minikube-profile",
@@ -421,7 +421,7 @@ var flagRegistry = []Flag{
 		Value:         &opts.ProfileAutoActivation,
 		DefValue:      true,
 		FlagAddMethod: "BoolVar",
-		DefinedOn:     []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "test"},
+		DefinedOn:     []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "test", "verify"},
 		IsEnum:        true,
 	},
 	{
@@ -430,7 +430,7 @@ var flagRegistry = []Flag{
 		Value:         &opts.PropagateProfiles,
 		DefValue:      true,
 		FlagAddMethod: "BoolVar",
-		DefinedOn:     []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "test"},
+		DefinedOn:     []string{"dev", "run", "debug", "deploy", "render", "build", "delete", "diagnose", "test", "verify"},
 		IsEnum:        true,
 	},
 	{
diff --git a/docs-v2/content/en/docs/install/_index.md b/docs-v2/content/en/docs/install/_index.md
index ca533800e..ed4a33697 100644
--- a/docs-v2/content/en/docs/install/_index.md
+++ b/docs-v2/content/en/docs/install/_index.md
@@ -1,6 +1,6 @@
 ---
-title: "Installing Skaffold v2.0.0-beta1 [NEW]"
-linkTitle: "Installing Skaffold v2.0.0-beta1 [NEW]"
+title: "Installing Skaffold v2.0.0-beta2 [NEW]"
+linkTitle: "Installing Skaffold v2.0.0-beta2 [NEW]"
 weight: 10
 aliases: [/docs/getting-started]
 ---
@@ -10,20 +10,20 @@ aliases: [/docs/getting-started]
 
 {{% tab "LINUX" %}}
 The latest **stable** v2.0.0 beta binaries can be found here:
-- Linux x86_64 (amd64): https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-linux-amd64
-- Linux ARMv8 (arm64): https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-linux-arm64
+- Linux x86_64 (amd64): https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-linux-amd64
+- Linux ARMv8 (arm64): https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-linux-arm64
 
 Simply download the appropriate binary and add it to your `PATH`. Or, copy+paste one of the following commands in your terminal:
 
 ```bash
 # For Linux x86_64 (amd64)
-curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-linux-amd64 && \
+curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-linux-amd64 && \
 sudo install skaffold /usr/local/bin/
 ```
 
 ```bash
 # For Linux ARMv8 (arm64)
-curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-linux-arm64 && \
+curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-linux-arm64 && \
 sudo install skaffold /usr/local/bin/
 ```
 
@@ -34,13 +34,13 @@ We also release a v2.0.0 **bleeding edge** latest build, built from the latest c
 
 ```bash
 # For Linux on x86_64 (amd64)
-curl -Lo skaffold https://storage.googleapis.com/skaffold/builds/v2.0.0-beta1/skaffold-linux-amd64 && \
+curl -Lo skaffold https://storage.googleapis.com/skaffold/builds/v2.0.0-beta2/skaffold-linux-amd64 && \
 sudo install skaffold /usr/local/bin/
 ```
 
 ```bash
 # For Linux on ARMv8 (arm64)
-curl -Lo skaffold https://storage.googleapis.com/skaffold/builds/v2.0.0-beta1/skaffold-linux-arm64 && \
+curl -Lo skaffold https://storage.googleapis.com/skaffold/builds/v2.0.0-beta2/skaffold-linux-arm64 && \
 sudo install skaffold /usr/local/bin/
 ```
 
@@ -50,20 +50,20 @@ sudo install skaffold /usr/local/bin/
 
 The latest **stable** binaries can be found here:
 
-- Darwin x86_64 (amd64): https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-darwin-amd64
-- Darwin ARMv8 (arm64): https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-darwin-arm64
+- Darwin x86_64 (amd64): https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-darwin-amd64
+- Darwin ARMv8 (arm64): https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-darwin-arm64
 
 Simply download the appropriate binary and add it to your `PATH`. Or, copy+paste one of the following commands in your terminal:
 
 ```bash
 # For macOS on x86_64 (amd64)
-curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-darwin-amd64 && \
+curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-darwin-amd64 && \
 sudo install skaffold /usr/local/bin/
 ```
 
 ```bash
 # For macOS on ARMv8 (arm64)
-curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-darwin-arm64 && \
+curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-darwin-arm64 && \
 sudo install skaffold /usr/local/bin/
 ```
 
@@ -90,7 +90,7 @@ sudo install skaffold /usr/local/bin/
 
 The latest **stable** release binary can be found here:
 
-https://storage.googleapis.com/skaffold/releases/v2.0.0-beta1/skaffold-windows-amd64.exe
+https://storage.googleapis.com/skaffold/releases/v2.0.0-beta2/skaffold-windows-amd64.exe
 
 Simply download it and place it in your `PATH` as `skaffold.exe`.
 
diff --git a/docs-v2/content/en/docs/pipeline-stages/verify.md b/docs-v2/content/en/docs/pipeline-stages/verify.md
index 8fb47f31c..248f5e966 100644
--- a/docs-v2/content/en/docs/pipeline-stages/verify.md
+++ b/docs-v2/content/en/docs/pipeline-stages/verify.md
@@ -6,7 +6,7 @@ featureId: verify
 aliases: [/docs/how-tos/verify]
 ---
 
-In skaffold `v2.0.0-beta1`, skaffold now supports running post-deployment verification tests.  This is done via a new `verify` phase and associated [`verify` schema configuration]({{< relref "/docs/references/yaml#verify" >}}) that allows users to add a list of test containers (either standalone containers or built by skaffold) to be run post-deployment and monitored for success/failure. Currently in the `v2.0.0-beta1` release the `verify` phase is not run in the `skaffold dev` flow but is only accessible via the standalone `skaffold verify` command which runs only the `verify` phase.
+In skaffold `v2.0.0-beta2`, skaffold now supports running post-deployment verification tests.  This is done via a new `verify` phase and associated [`verify` schema configuration]({{< relref "/docs/references/yaml#verify" >}}) that allows users to add a list of test containers (either standalone containers or built by skaffold) to be run post-deployment and monitored for success/failure. Currently in the `v2.0.0-beta2` release the `verify` phase is not run in the `skaffold dev` flow but is only accessible via the standalone `skaffold verify` command which runs only the `verify` phase.
 
 Below is an example of a `skaffold.yaml` file with a `verify` configuration that runs 3 verification tests (which all succeed) against deployments including a user built `integration-test-container`, a user built `metrics-test-container`, and a simple health check done via "off the shelf" alpine using its installed `wget`:
 
diff --git a/docs-v2/content/en/docs/upgrading/_index.md b/docs-v2/content/en/docs/upgrading/_index.md
index d78049397..3012a641a 100644
--- a/docs-v2/content/en/docs/upgrading/_index.md
+++ b/docs-v2/content/en/docs/upgrading/_index.md
@@ -10,7 +10,7 @@ This release comes with a new schema version `v3alpha1`. This schema introduced
 * you can configure kpt deployer to render and apply kubernetes yaml, helm charts or
 * you can configure the kubectl deployer to apply helm charts and helm to render the charts. 
 
-Upgrading from skaffold `v1.*.*` to skaffold `v2.0.0-beta1` should not require any manual skaffold.yaml changes or CLI command modification for most common use cases.  Skaffold `v2` includes the same CLI surface as `v1` and has backwards compatibility for all previous skaffold.yaml schema `apiVersion` for example - `v2beta*`, `v1beta*` and `v1alpha*`.  
+Upgrading from skaffold `v1.*.*` to skaffold `v2.0.0-beta2` should not require any manual skaffold.yaml changes or CLI command modification for most common use cases.  Skaffold `v2` includes the same CLI surface as `v1` and has backwards compatibility for all previous skaffold.yaml schema `apiVersion` for example - `v2beta*`, `v1beta*` and `v1alpha*`.  
 
 If you wish to update your skaffold.yaml to the latest `apiVersion` (`apiVersion: v3alpha1`) run `skaffold fix` which will output an updated skaffold.yaml with the schema fields updated for `v3alpha1`.  With this new `v3alpha1` configuration schema you can access the new v2 functionality via the `v3alpha1` configuration fields [here]({{< relref "/docs/references/yaml#manifests" >}})  Example usage of `skaffold fix`:
 ```console
@@ -29,7 +29,7 @@ deploy:
   kubectl: {}
 ```
 
-The list of features that were supported in skaffold `v1` but are no longer support or require manual changes for `v2.0.0-beta1` include:
+The list of features that were supported in skaffold `v1` but are no longer support or require manual changes for `v2.0.0-beta2` include:
 * `v1` `kpt` deployer usage is not upgradeable via `skaffold fix` given the numerous changes made to the `kpt` workflow.  Manual changes might be required to get users pipelines working as expected.
 * using multiple renderers WITH the `kpt` deployer being one of them (using combinations of any other renderer(s) works as it did previously).
 
@@ -39,4 +39,4 @@ Outside of the above, there are currently no known other regressions when migrat
 - `skaffold render` flags usage (see [render docs]({{< relref "/docs/pipeline-stages/renderers" >}}) and [render schema]({{< relref "/docs/references/yaml#manifests" >}}) for more details)
 
 
-If you encounter any issues using skaffold `v2.0.0-beta1`, particularly any regressions that used to work differently or succeed in `v1`, please file an issue at[GoogleContainerTools/skaffold](https://github.com/GoogleContainerTools/skaffold/issues).
+If you encounter any issues using skaffold `v2.0.0-beta2`, particularly any regressions that used to work differently or succeed in `v1`, please file an issue at[GoogleContainerTools/skaffold](https://github.com/GoogleContainerTools/skaffold/issues).
diff --git a/docs-v2/content/en/schemas/v3alpha1.json b/docs-v2/content/en/schemas/v3alpha1.json
index 712fbd9b4..5340ecb9c 100755
--- a/docs-v2/content/en/schemas/v3alpha1.json
+++ b/docs-v2/content/en/schemas/v3alpha1.json
@@ -3364,6 +3364,11 @@
           "description": "defines the helm charts used in the application. NOTE: Defines cherts in this section to render via helm but deployed via kubectl or kpt deployer. To use helm to deploy, please see deploy.helm section.",
           "x-intellij-html-description": "defines the helm charts used in the application. NOTE: Defines cherts in this section to render via helm but deployed via kubectl or kpt deployer. To use helm to deploy, please see deploy.helm section."
         },
+        "hooks": {
+          "$ref": "#/definitions/RenderHooks",
+          "description": "describes a set of lifecycle hooks that are executed before and after every render.",
+          "x-intellij-html-description": "describes a set of lifecycle hooks that are executed before and after every render."
+        },
         "kpt": {
           "items": {
             "type": "string"
@@ -3406,6 +3411,7 @@
         "kustomize",
         "helm",
         "kpt",
+        "hooks",
         "transform",
         "validate",
         "output"
@@ -3415,6 +3421,50 @@
       "description": "contains all the configuration needed by the render steps.",
       "x-intellij-html-description": "contains all the configuration needed by the render steps."
     },
+    "RenderHookItem": {
+      "properties": {
+        "host": {
+          "$ref": "#/definitions/HostHook",
+          "description": "describes a single lifecycle hook to run on the host machine.",
+          "x-intellij-html-description": "describes a single lifecycle hook to run on the host machine."
+        }
+      },
+      "preferredOrder": [
+        "host"
+      ],
+      "additionalProperties": false,
+      "type": "object",
+      "description": "describes a single lifecycle hook to execute before or after each deployer step.",
+      "x-intellij-html-description": "describes a single lifecycle hook to execute before or after each deployer step."
+    },
+    "RenderHooks": {
+      "properties": {
+        "after": {
+          "items": {
+            "$ref": "#/definitions/RenderHookItem"
+          },
+          "type": "array",
+          "description": "describes the list of lifecycle hooks to execute *after* each render step.",
+          "x-intellij-html-description": "describes the list of lifecycle hooks to execute <em>after</em> each render step."
+        },
+        "before": {
+          "items": {
+            "$ref": "#/definitions/RenderHookItem"
+          },
+          "type": "array",
+          "description": "describes the list of lifecycle hooks to execute *before* each render step. Container hooks will only run if the container exists from a previous deployment step (for instance the successive iterations of a dev-loop during `skaffold dev`).",
+          "x-intellij-html-description": "describes the list of lifecycle hooks to execute <em>before</em> each render step. Container hooks will only run if the container exists from a previous deployment step (for instance the successive iterations of a dev-loop during <code>skaffold dev</code>)."
+        }
+      },
+      "preferredOrder": [
+        "before",
+        "after"
+      ],
+      "additionalProperties": false,
+      "type": "object",
+      "description": "describes the list of lifecycle hooks to execute before and after each render step.",
+      "x-intellij-html-description": "describes the list of lifecycle hooks to execute before and after each render step."
+    },
     "ResourceFilter": {
       "required": [
         "groupKind"
diff --git a/docs/content/en/docs/references/cli/_index.md b/docs/content/en/docs/references/cli/_index.md
index 1569db194..7821ee266 100644
--- a/docs/content/en/docs/references/cli/_index.md
+++ b/docs/content/en/docs/references/cli/_index.md
@@ -134,6 +134,8 @@ Options:
   -n, --namespace='': Runs deployments in the specified namespace. When used with 'render' command, renders manifests contain the namespace
   -p, --profile=[]: Activate profiles by name (prefixed with `-` to disable a profile)
       --remote-cache-dir='': Specify the location of the git repositories cache (default $HOME/.skaffold/repos)
+      --rpc-http-port=: tcp port to expose the Skaffold API over HTTP REST
+      --rpc-port=: tcp port to expose the Skaffold API over gRPC
       --status-check=: Wait for deployed resources to stabilize
       --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.
       --tail=false: Stream logs from deployed objects
@@ -161,6 +163,8 @@ Env vars:
 * `SKAFFOLD_NAMESPACE` (same as `--namespace`)
 * `SKAFFOLD_PROFILE` (same as `--profile`)
 * `SKAFFOLD_REMOTE_CACHE_DIR` (same as `--remote-cache-dir`)
+* `SKAFFOLD_RPC_HTTP_PORT` (same as `--rpc-http-port`)
+* `SKAFFOLD_RPC_PORT` (same as `--rpc-port`)
 * `SKAFFOLD_STATUS_CHECK` (same as `--status-check`)
 * `SKAFFOLD_SYNC_REMOTE_CACHE` (same as `--sync-remote-cache`)
 * `SKAFFOLD_TAIL` (same as `--tail`)
@@ -617,7 +621,6 @@ Options:
   -m, --module=[]: Filter Skaffold configs to only the provided named modules
       --mute-logs=[]: mute logs for specified stages in pipeline (build, deploy, status-check, none, all)
   -n, --namespace='': Runs deployments in the specified namespace. When used with 'render' command, renders manifests contain the namespace
-      --platform=[]: The platform to target for the build artifacts
       --port-forward=off: Port-forward exposes service ports and container ports within pods and other resources (off, user, services, debug, pods)
   -p, --profile=[]: Activate profiles by name (prefixed with `-` to disable a profile)
       --profile-auto-activation=true: Set to false to disable profile auto activation
@@ -666,7 +669,6 @@ Env vars:
 * `SKAFFOLD_MODULE` (same as `--module`)
 * `SKAFFOLD_MUTE_LOGS` (same as `--mute-logs`)
 * `SKAFFOLD_NAMESPACE` (same as `--namespace`)
-* `SKAFFOLD_PLATFORM` (same as `--platform`)
 * `SKAFFOLD_PORT_FORWARD` (same as `--port-forward`)
 * `SKAFFOLD_PROFILE` (same as `--profile`)
 * `SKAFFOLD_PROFILE_AUTO_ACTIVATION` (same as `--profile-auto-activation`)
@@ -1283,6 +1285,9 @@ Options:
   -f, --filename='skaffold.yaml': Path or URL to the Skaffold config file
   -m, --module=[]: Filter Skaffold configs to only the provided named modules
       --port-forward=off: Port-forward exposes service ports and container ports within pods and other resources (off, user, services, debug, pods)
+  -p, --profile=[]: Activate profiles by name (prefixed with `-` to disable a profile)
+      --profile-auto-activation=true: Set to false to disable profile auto activation
+      --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)
       --rpc-http-port=: tcp port to expose the Skaffold API over HTTP REST
       --rpc-port=: tcp port to expose the Skaffold API over gRPC
@@ -1305,6 +1310,9 @@ Env vars:
 * `SKAFFOLD_FILENAME` (same as `--filename`)
 * `SKAFFOLD_MODULE` (same as `--module`)
 * `SKAFFOLD_PORT_FORWARD` (same as `--port-forward`)
+* `SKAFFOLD_PROFILE` (same as `--profile`)
+* `SKAFFOLD_PROFILE_AUTO_ACTIVATION` (same as `--profile-auto-activation`)
+* `SKAFFOLD_PROPAGATE_PROFILES` (same as `--propagate-profiles`)
 * `SKAFFOLD_REMOTE_CACHE_DIR` (same as `--remote-cache-dir`)
 * `SKAFFOLD_RPC_HTTP_PORT` (same as `--rpc-http-port`)
 * `SKAFFOLD_RPC_PORT` (same as `--rpc-port`)
diff --git a/examples/jib-gradle/build.gradle b/examples/jib-gradle/build.gradle
index 703daddb2..35614504c 100644
--- a/examples/jib-gradle/build.gradle
+++ b/examples/jib-gradle/build.gradle
@@ -3,7 +3,7 @@ plugins {
     id 'groovy'
     id 'io.spring.dependency-management' version '1.0.6.RELEASE'
     id 'net.ltgt.apt-idea' version '0.18'
-    id 'com.google.cloud.tools.jib' version '3.2.1'
+    id 'com.google.cloud.tools.jib' version '3.3.0'
 }
 
 sourceCompatibility = 1.8
diff --git a/examples/jib-multimodule/pom.xml b/examples/jib-multimodule/pom.xml
index 4deaefd07..b065cef39 100644
--- a/examples/jib-multimodule/pom.xml
+++ b/examples/jib-multimodule/pom.xml
@@ -18,7 +18,7 @@
 
   <properties>
     <java.version>1.8</java.version>
-    <jib.maven-plugin-version>3.2.1</jib.maven-plugin-version>
+    <jib.maven-plugin-version>3.3.0</jib.maven-plugin-version>
   </properties>
 
   <dependencies>
diff --git a/examples/jib-sync/build.gradle b/examples/jib-sync/build.gradle
index 3522345ba..54508242f 100644
--- a/examples/jib-sync/build.gradle
+++ b/examples/jib-sync/build.gradle
@@ -2,7 +2,7 @@ plugins {
     id 'java'
     id 'org.springframework.boot' version '2.0.5.RELEASE'
     id 'io.spring.dependency-management' version '1.0.7.RELEASE'
-    id 'com.google.cloud.tools.jib' version '3.2.1'
+    id 'com.google.cloud.tools.jib' version '3.3.0'
 }
 
 repositories {
diff --git a/examples/jib-sync/pom.xml b/examples/jib-sync/pom.xml
index 68b2ca82f..ea24d966c 100644
--- a/examples/jib-sync/pom.xml
+++ b/examples/jib-sync/pom.xml
@@ -9,7 +9,7 @@
 
   <properties>
     <java.version>1.8</java.version>
-    <jib.maven-plugin-version>3.2.1</jib.maven-plugin-version>
+    <jib.maven-plugin-version>3.3.0</jib.maven-plugin-version>
   </properties>
 
   <parent>
diff --git a/examples/jib/pom.xml b/examples/jib/pom.xml
index 7712fa53b..3d2ced74c 100644
--- a/examples/jib/pom.xml
+++ b/examples/jib/pom.xml
@@ -9,7 +9,7 @@
 
     <properties>
         <java.version>1.8</java.version>
-        <jib.maven-plugin-version>3.2.1</jib.maven-plugin-version>
+        <jib.maven-plugin-version>3.3.0</jib.maven-plugin-version>
     </properties>
 
     <parent>
diff --git a/hack/generate-embedded-files.sh b/hack/generate-embedded-files.sh
index f5a7c4341..f331062f2 100755
--- a/hack/generate-embedded-files.sh
+++ b/hack/generate-embedded-files.sh
@@ -32,8 +32,7 @@ elif ! [ -x "$(command -v ${LICENSES})" ]; then
     # from a dependency.
    echo "Installing go-licenses"
      pushd $(mktemp -d ${TMPDIR:-/tmp}/generate-embedded.XXXXXX)
-     #TODO(marlongamez): unpin this version once we're able to build using go 1.16.x
-     go mod init tmp; GOBIN=${BIN} go get github.com/google/go-licenses@9376cf9847a05cae04f4589fe4898b9bce37e684
+     go mod init tmp; GOBIN=${BIN} go install github.com/google/go-licenses@v1.0.0
      popd
 fi
 
diff --git a/integration/binpack/gcp.go b/integration/binpack/gcp.go
index 1704e8ea0..3ae226fe2 100644
--- a/integration/binpack/gcp.go
+++ b/integration/binpack/gcp.go
@@ -21,6 +21,7 @@ package binpack
 // GCPTimings contains the timings for tests marked with NeedsGcp
 var GCPTimings = []Timing{
 	{"TestBuildKanikoInsecureRegistry", 10.00},
+	{"TestBuildWithMultiPlatforms", 20.00},
 	{"TestBuildKanikoWithExplicitRepo", 10.00},
 	{"TestBuildInCluster", 10.00},
 	{"TestBuildGCBWithExplicitRepo", 10.00},
diff --git a/integration/examples/cross-platform-builds/skaffold.yaml.v2 b/integration/examples/cross-platform-builds/skaffold.yaml.v2
deleted file mode 100644
index a2a0f34f7..000000000
--- a/integration/examples/cross-platform-builds/skaffold.yaml.v2
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: skaffold/v2beta29
-kind: Config
-build:
-  platforms: ["linux/amd64"]
-  artifacts:
-  - image: skaffold-example
-deploy:
-  kubectl:
-    manifests:
-      - k8s-*
diff --git a/integration/examples/jib-gradle/build.gradle b/integration/examples/jib-gradle/build.gradle
index 703daddb2..35614504c 100644
--- a/integration/examples/jib-gradle/build.gradle
+++ b/integration/examples/jib-gradle/build.gradle
@@ -3,7 +3,7 @@ plugins {
     id 'groovy'
     id 'io.spring.dependency-management' version '1.0.6.RELEASE'
     id 'net.ltgt.apt-idea' version '0.18'
-    id 'com.google.cloud.tools.jib' version '3.2.1'
+    id 'com.google.cloud.tools.jib' version '3.3.0'
 }
 
 sourceCompatibility = 1.8
diff --git a/integration/examples/jib-multimodule/pom.xml b/integration/examples/jib-multimodule/pom.xml
index 4deaefd07..b065cef39 100644
--- a/integration/examples/jib-multimodule/pom.xml
+++ b/integration/examples/jib-multimodule/pom.xml
@@ -18,7 +18,7 @@
 
   <properties>
     <java.version>1.8</java.version>
-    <jib.maven-plugin-version>3.2.1</jib.maven-plugin-version>
+    <jib.maven-plugin-version>3.3.0</jib.maven-plugin-version>
   </properties>
 
   <dependencies>
diff --git a/integration/examples/jib-sync/build.gradle b/integration/examples/jib-sync/build.gradle
index 3522345ba..54508242f 100644
--- a/integration/examples/jib-sync/build.gradle
+++ b/integration/examples/jib-sync/build.gradle
@@ -2,7 +2,7 @@ plugins {
     id 'java'
     id 'org.springframework.boot' version '2.0.5.RELEASE'
     id 'io.spring.dependency-management' version '1.0.7.RELEASE'
-    id 'com.google.cloud.tools.jib' version '3.2.1'
+    id 'com.google.cloud.tools.jib' version '3.3.0'
 }
 
 repositories {
diff --git a/integration/examples/jib-sync/pom.xml b/integration/examples/jib-sync/pom.xml
index 68b2ca82f..ea24d966c 100644
--- a/integration/examples/jib-sync/pom.xml
+++ b/integration/examples/jib-sync/pom.xml
@@ -9,7 +9,7 @@
 
   <properties>
     <java.version>1.8</java.version>
-    <jib.maven-plugin-version>3.2.1</jib.maven-plugin-version>
+    <jib.maven-plugin-version>3.3.0</jib.maven-plugin-version>
   </properties>
 
   <parent>
diff --git a/integration/examples/jib/pom.xml b/integration/examples/jib/pom.xml
index 7712fa53b..3d2ced74c 100644
--- a/integration/examples/jib/pom.xml
+++ b/integration/examples/jib/pom.xml
@@ -9,7 +9,7 @@
 
     <properties>
         <java.version>1.8</java.version>
-        <jib.maven-plugin-version>3.2.1</jib.maven-plugin-version>
+        <jib.maven-plugin-version>3.3.0</jib.maven-plugin-version>
     </properties>
 
     <parent>
diff --git a/integration/examples/lifecycle-hooks/k8s-pod.yaml b/integration/examples/lifecycle-hooks/k8s-pod.yaml
new file mode 100644
index 000000000..27fdd6799
--- /dev/null
+++ b/integration/examples/lifecycle-hooks/k8s-pod.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+spec:
+  containers:
+  - name: getting-started
+    image: skaffold-example
diff --git a/integration/examples/lifecycle-hooks/skaffold.yaml b/integration/examples/lifecycle-hooks/skaffold.yaml
index 07bdf6638..56f8997aa 100644
--- a/integration/examples/lifecycle-hooks/skaffold.yaml
+++ b/integration/examples/lifecycle-hooks/skaffold.yaml
@@ -1,5 +1,15 @@
 apiVersion: skaffold/v3alpha1
 kind: Config
+manifests:
+  rawYaml:
+  - k8s-pod.yaml
+  hooks:
+    before:
+      - host:
+          command: ["sh", "-c", "echo pre-render host hook running on $(hostname)!"]
+    after:
+      - host:
+          command: ["sh", "-c", "echo post-render host hook running on $(hostname)!"]
 build:
   artifacts:
   - image: hooks-example
@@ -31,9 +41,6 @@ build:
         after:
           - container:
               command: ["sh", "-c", "set -x; kill -HUP 1"]
-manifests:
-  rawYaml:
-  - deployment.yaml
 deploy:
   kubectl:
     hooks:
@@ -49,6 +56,7 @@ deploy:
       after:
         - host:
             command: ["sh", "-c", "echo post-deploy host hook running on $(hostname)!"]
+            os: [darwin, linux]
         - container:
             command: ["sh", "-c", "echo post-deploy container hook running on $(hostname)!"]
             containerName: hooks-example* # use a glob pattern to prefix-match the container name and pod name for deployments, stateful-sets, etc.
diff --git a/pkg/skaffold/build/docker/docker.go b/pkg/skaffold/build/docker/docker.go
index 07367d3ee..d7f17472a 100644
--- a/pkg/skaffold/build/docker/docker.go
+++ b/pkg/skaffold/build/docker/docker.go
@@ -93,7 +93,16 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, a *latest.Artifact,
 
 func (b *Builder) dockerCLIBuild(ctx context.Context, out io.Writer, name string, workspace string, dockerfilePath string, a *latest.DockerArtifact, opts docker.BuildOptions, pl v1.Platform) (string, error) {
 	args := []string{"build", workspace, "--file", dockerfilePath, "-t", opts.Tag}
-	ba, err := docker.EvalBuildArgs(b.cfg.Mode(), workspace, a.DockerfilePath, a.BuildArgs, opts.ExtraBuildArgs)
+	imgRef, err := docker.ParseReference(opts.Tag)
+	if err != nil {
+		return "", fmt.Errorf("couldn't parse image tag: %w", err)
+	}
+	imageInfoEnv := map[string]string{
+		"IMAGE_REPO": imgRef.Repo,
+		"IMAGE_NAME": imgRef.Name,
+		"IMAGE_TAG":  imgRef.Tag,
+	}
+	ba, err := docker.EvalBuildArgsWithEnv(b.cfg.Mode(), workspace, a.DockerfilePath, a.BuildArgs, opts.ExtraBuildArgs, imageInfoEnv)
 	if err != nil {
 		return "", fmt.Errorf("unable to evaluate build args: %w", err)
 	}
diff --git a/pkg/skaffold/docker/image.go b/pkg/skaffold/docker/image.go
index 9e179326d..836543ce5 100644
--- a/pkg/skaffold/docker/image.go
+++ b/pkg/skaffold/docker/image.go
@@ -312,7 +312,16 @@ func (l *localDaemon) Build(ctx context.Context, out io.Writer, workspace string
 	if err := l.CheckCompatible(a); err != nil {
 		return "", err
 	}
-	buildArgs, err := EvalBuildArgs(opts.Mode, workspace, a.DockerfilePath, a.BuildArgs, opts.ExtraBuildArgs)
+	imgRef, err := ParseReference(opts.Tag)
+	if err != nil {
+		return "", fmt.Errorf("couldn't parse image tag: %w", err)
+	}
+	imageInfoEnv := map[string]string{
+		"IMAGE_REPO": imgRef.Repo,
+		"IMAGE_NAME": imgRef.Name,
+		"IMAGE_TAG":  imgRef.Tag,
+	}
+	buildArgs, err := EvalBuildArgsWithEnv(opts.Mode, workspace, a.DockerfilePath, a.BuildArgs, opts.ExtraBuildArgs, imageInfoEnv)
 	if err != nil {
 		return "", fmt.Errorf("unable to evaluate build args: %w", err)
 	}
diff --git a/pkg/skaffold/docker/manifest_list.go b/pkg/skaffold/docker/manifest_list.go
index 27de3b1f3..c826390fe 100644
--- a/pkg/skaffold/docker/manifest_list.go
+++ b/pkg/skaffold/docker/manifest_list.go
@@ -55,7 +55,7 @@ func CreateManifestList(ctx context.Context, images []SinglePlatformImage, targe
 			return "", err
 		}
 
-		img, err := remoteImage(ref)
+		img, err := remoteImage(ref, remote.WithAuthFromKeychain(primaryKeychain))
 		if err != nil {
 			return "", err
 		}
diff --git a/pkg/skaffold/helm/bin.go b/pkg/skaffold/helm/bin.go
index 6ead97ac1..f0c06423d 100644
--- a/pkg/skaffold/helm/bin.go
+++ b/pkg/skaffold/helm/bin.go
@@ -85,7 +85,7 @@ func PrepareSkaffoldFilter(h Client, builds []graph.Artifact) (skaffoldBinary st
 	return
 }
 
-// generateSkaffoldFilter creates a `skaffold filter`` command-line for applying the various
+// generateSkaffoldFilter creates a "skaffold filter" command-line for applying the various
 // Skaffold manifest filters, such a debugging, image replacement, and applying labels.
 func generateSkaffoldFilter(h Client, buildsFile string) []string {
 	args := []string{"filter", "--kube-context", h.KubeContext()}
@@ -106,8 +106,7 @@ func generateSkaffoldFilter(h Client, buildsFile string) []string {
 	return args
 }
 
-// Exec executes the helm command, writing combined stdout/stderr to the provided writer
-func Exec(ctx context.Context, h Client, out io.Writer, useSecrets bool, env []string, args ...string) error {
+func generateHelmCommand(ctx context.Context, h Client, useSecrets bool, env []string, args ...string) *exec.Cmd {
 	args = append([]string{"--kube-context", h.KubeContext()}, args...)
 	args = append(args, h.GlobalFlags()...)
 
@@ -123,8 +122,23 @@ func Exec(ctx context.Context, h Client, out io.Writer, useSecrets bool, env []s
 	if len(env) > 0 {
 		cmd.Env = env
 	}
+	return cmd
+}
+
+// Exec executes the helm command, writing combined stdout/stderr to the provided writer
+func Exec(ctx context.Context, h Client, out io.Writer, useSecrets bool, env []string, args ...string) error {
+	cmd := generateHelmCommand(ctx, h, useSecrets, env, args...)
 	cmd.Stdout = out
 	cmd.Stderr = out
 
 	return util.RunCmd(ctx, cmd)
 }
+
+// ExecWithStdoutAndStderr executes the helm command, writing combined stdout and stderr to the provided writers
+func ExecWithStdoutAndStderr(ctx context.Context, h Client, stdout io.Writer, stderr io.Writer, useSecrets bool, env []string, args ...string) error {
+	cmd := generateHelmCommand(ctx, h, useSecrets, env, args...)
+	cmd.Stdout = stdout
+	cmd.Stderr = stderr
+
+	return util.RunCmd(ctx, cmd)
+}
diff --git a/pkg/skaffold/hooks/env.go b/pkg/skaffold/hooks/env.go
index b2767ef1a..0d619faba 100644
--- a/pkg/skaffold/hooks/env.go
+++ b/pkg/skaffold/hooks/env.go
@@ -53,6 +53,12 @@ type SyncEnvOpts struct {
 	Namespaces           string
 }
 
+// RenderEnvOpts contains the environment variables to be set in a deploy type lifecycle hook executor.
+type RenderEnvOpts struct {
+	KubeContext string
+	Namespaces  string
+}
+
 // DeployEnvOpts contains the environment variables to be set in a deploy type lifecycle hook executor.
 type DeployEnvOpts struct {
 	RunID       string
diff --git a/pkg/skaffold/hooks/render.go b/pkg/skaffold/hooks/render.go
new file mode 100644
index 000000000..af4c54ef3
--- /dev/null
+++ b/pkg/skaffold/hooks/render.go
@@ -0,0 +1,84 @@
+/*
+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 hooks
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"strings"
+	"sync"
+
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
+)
+
+// for testing
+var (
+	NewRenderRunner = newRenderRunner
+)
+
+func newRenderRunner(r latest.RenderHooks, namespaces *[]string, opts RenderEnvOpts) Runner {
+	return renderRunner{r, namespaces, opts, new(sync.Map)}
+}
+
+func NewRenderEnvOpts(kubeContext string, namespaces []string) RenderEnvOpts {
+	return RenderEnvOpts{
+		KubeContext: kubeContext,
+		Namespaces:  strings.Join(namespaces, ","),
+	}
+}
+
+type renderRunner struct {
+	latest.RenderHooks
+	namespaces        *[]string
+	opts              RenderEnvOpts
+	visitedContainers *sync.Map // maintain a list of previous iteration containers, so that they can be skipped
+}
+
+func (r renderRunner) RunPreHooks(ctx context.Context, out io.Writer) error {
+	return r.run(ctx, out, r.PreHooks, phases.PreRender)
+}
+
+func (r renderRunner) RunPostHooks(ctx context.Context, out io.Writer) error {
+	return r.run(ctx, out, r.PostHooks, phases.PostRender)
+}
+
+func (r renderRunner) getEnv() []string {
+	common := getEnv(staticEnvOpts)
+	render := getEnv(r.opts)
+	return append(common, render...)
+}
+
+func (r renderRunner) run(ctx context.Context, out io.Writer, hooks []latest.RenderHookItem, phase phase) error {
+	if len(hooks) > 0 {
+		output.Default.Fprintln(out, fmt.Sprintf("Starting %s hooks...", phase))
+	}
+	env := r.getEnv()
+	for _, h := range hooks {
+		if h.HostHook != nil {
+			hook := hostHook{*h.HostHook, env}
+			if err := hook.run(ctx, out); err != nil {
+				return err
+			}
+		}
+	}
+	if len(hooks) > 0 {
+		output.Default.Fprintln(out, fmt.Sprintf("Completed %s hooks", phase))
+	}
+	return nil
+}
diff --git a/pkg/skaffold/hooks/types.go b/pkg/skaffold/hooks/types.go
index 809e9cf5f..66f56f487 100644
--- a/pkg/skaffold/hooks/types.go
+++ b/pkg/skaffold/hooks/types.go
@@ -36,6 +36,8 @@ var phases = struct {
 	PostBuild  phase
 	PreSync    phase
 	PostSync   phase
+	PreRender  phase
+	PostRender phase
 	PreDeploy  phase
 	PostDeploy phase
 }{
@@ -43,6 +45,8 @@ var phases = struct {
 	PostBuild:  "post-build",
 	PreSync:    "pre-sync",
 	PostSync:   "post-sync",
+	PreRender:  "pre-render",
+	PostRender: "post-render",
 	PreDeploy:  "pre-deploy",
 	PostDeploy: "post-deploy",
 }
diff --git a/pkg/skaffold/instrumentation/export.go b/pkg/skaffold/instrumentation/export.go
index 42240bf79..e5c29f761 100644
--- a/pkg/skaffold/instrumentation/export.go
+++ b/pkg/skaffold/instrumentation/export.go
@@ -182,7 +182,7 @@ func createMetrics(ctx context.Context, meter skaffoldMeter) {
 		sharedLabels = append(sharedLabels, attribute.String("user", meter.User))
 	}
 	labels = append(labels, sharedLabels...)
-
+	platformLabel := attribute.String("host_os_arch", fmt.Sprintf("%s/%s", meter.OS, meter.Arch))
 	runCounter := metric.Must(m).NewInt64ValueRecorder("launches", metric.WithDescription("Skaffold Invocations"))
 	runCounter.Record(ctx, 1, labels...)
 
@@ -194,7 +194,7 @@ func createMetrics(ctx context.Context, meter skaffoldMeter) {
 		flagMetrics(ctx, meter, m, randLabel)
 		hooksMetrics(ctx, meter, m, labels...)
 		if doesBuild.Contains(meter.Command) {
-			builderMetrics(ctx, meter, m, sharedLabels...)
+			builderMetrics(ctx, meter, m, platformLabel, sharedLabels...)
 		}
 		if doesDeploy.Contains(meter.Command) {
 			deployerMetrics(ctx, meter, m, sharedLabels...)
@@ -205,7 +205,7 @@ func createMetrics(ctx context.Context, meter skaffoldMeter) {
 	}
 
 	if meter.ErrorCode != 0 {
-		errorMetrics(ctx, meter, m, sharedLabels...)
+		errorMetrics(ctx, meter, m, append(sharedLabels, platformLabel)...)
 	}
 }
 
@@ -274,7 +274,7 @@ func resourceSelectorMetrics(ctx context.Context, meter skaffoldMeter, m metric.
 	}
 }
 
-func builderMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, labels ...attribute.KeyValue) {
+func builderMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, platformLabel attribute.KeyValue, labels ...attribute.KeyValue) {
 	builderCounter := metric.Must(m).NewInt64ValueRecorder("builders", metric.WithDescription("Builders used"))
 	artifactCounter := metric.Must(m).NewInt64ValueRecorder("artifacts", metric.WithDescription("Number of artifacts used"))
 	dependenciesCounter := metric.Must(m).NewInt64ValueRecorder("artifact-dependencies", metric.WithDescription("Number of artifacts with dependencies"))
@@ -284,24 +284,24 @@ func builderMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, la
 		builderCounter.Record(ctx, 1, append(labels, bLabel)...)
 		artifactCounter.Record(ctx, int64(count), append(labels, bLabel)...)
 		dependenciesCounter.Record(ctx, int64(meter.BuildDependencies[builder]), append(labels, bLabel)...)
-		platformsCounter.Record(ctx, int64(meter.BuildWithPlatforms[builder]), append(labels, bLabel)...)
+		platformsCounter.Record(ctx, int64(meter.BuildWithPlatforms[builder]), append(labels, platformLabel, bLabel)...)
 	}
 
 	if len(meter.ResolvedBuildTargetPlatforms) > 0 {
 		platforms := metric.Must(m).NewInt64ValueRecorder("build-platforms", metric.WithDescription("The resolved build target platforms for each run"))
 		for _, buildPlatform := range meter.ResolvedBuildTargetPlatforms {
-			platforms.Record(ctx, 1, append(labels, attribute.String("os_arch", buildPlatform))...)
+			platforms.Record(ctx, 1, append(labels, platformLabel, attribute.String("os_arch", buildPlatform))...)
 		}
 	}
 
 	if len(meter.CliBuildTargetPlatforms) > 0 {
 		platforms := metric.Must(m).NewInt64ValueRecorder("cli-platforms", metric.WithDescription("The build target platforms specified via CLI flag --platform"))
-		platforms.Record(ctx, 1, append(labels, attribute.String("os_arch", meter.CliBuildTargetPlatforms))...)
+		platforms.Record(ctx, 1, append(labels, platformLabel, attribute.String("os_arch", meter.CliBuildTargetPlatforms))...)
 	}
 
 	if len(meter.DeployNodePlatforms) > 0 {
 		platforms := metric.Must(m).NewInt64ValueRecorder("node-platforms", metric.WithDescription("The kubernetes cluster node platforms"))
-		platforms.Record(ctx, 1, append(labels, attribute.String("os_arch", meter.DeployNodePlatforms))...)
+		platforms.Record(ctx, 1, append(labels, platformLabel, attribute.String("os_arch", meter.DeployNodePlatforms))...)
 	}
 }
 
diff --git a/pkg/skaffold/kubernetes/status/resource/deployment.go b/pkg/skaffold/kubernetes/status/resource/deployment.go
index 59c546ddf..b887ea673 100644
--- a/pkg/skaffold/kubernetes/status/resource/deployment.go
+++ b/pkg/skaffold/kubernetes/status/resource/deployment.go
@@ -384,7 +384,9 @@ func isErrAndNotRetryAble(statusCode proto.StatusCode) bool {
 	return statusCode != proto.StatusCode_STATUSCHECK_KUBECTL_CONNECTION_ERR &&
 		statusCode != proto.StatusCode_STATUSCHECK_DEPLOYMENT_ROLLOUT_PENDING &&
 		statusCode != proto.StatusCode_STATUSCHECK_STANDALONE_PODS_PENDING &&
-		statusCode != proto.StatusCode_STATUSCHECK_CONFIG_CONNECTOR_IN_PROGRESS
+		statusCode != proto.StatusCode_STATUSCHECK_CONFIG_CONNECTOR_IN_PROGRESS &&
+		statusCode != proto.StatusCode_STATUSCHECK_NODE_UNSCHEDULABLE &&
+		statusCode != proto.StatusCode_STATUSCHECK_UNKNOWN_UNSCHEDULABLE
 }
 
 // HasEncounteredUnrecoverableError goes through all pod statuses and return true
diff --git a/pkg/skaffold/platform/platform.go b/pkg/skaffold/platform/platform.go
index 99a228121..1e41e901b 100644
--- a/pkg/skaffold/platform/platform.go
+++ b/pkg/skaffold/platform/platform.go
@@ -18,6 +18,7 @@ package platform
 
 import (
 	"context"
+	"fmt"
 	"strings"
 
 	"github.com/containerd/containerd/platforms"
@@ -109,9 +110,42 @@ func Parse(ps []string) (Matcher, error) {
 		if err != nil {
 			return Matcher{}, UnknownPlatformCLIFlag(p, err)
 		}
+		if err := isKnownPlatform(platform); err != nil {
+			return Matcher{}, UnknownPlatformCLIFlag(p, err)
+		}
 		sl = append(sl, platform)
 	}
 	return Matcher{Platforms: sl}, nil
 }
 
+func isKnownPlatform(p specs.Platform) error {
+	if err := isKnownOS(p.OS); err != nil {
+		return err
+	}
+	if err := isKnownArch(p.Architecture); err != nil {
+		return err
+	}
+	return nil
+}
+
+// isKnownOS returns an error if we don't know about the operating system.
+// Unexported function copied from "github.com/containerd/containerd/platforms"
+func isKnownOS(os string) error {
+	switch os {
+	case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos":
+		return nil
+	}
+	return fmt.Errorf("unknown operating system %q", os)
+}
+
+// isKnownArch returns an error if we don't know about the architecture.
+// Unexported function copied from "github.com/containerd/containerd/platforms"
+func isKnownArch(arch string) error {
+	switch arch {
+	case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm":
+		return nil
+	}
+	return fmt.Errorf("unknown architecture %q", arch)
+}
+
 func Format(pl specs.Platform) string { return platforms.Format(pl) }
diff --git a/pkg/skaffold/render/generate/generate.go b/pkg/skaffold/render/generate/generate.go
index 6693cac60..37f1801b9 100644
--- a/pkg/skaffold/render/generate/generate.go
+++ b/pkg/skaffold/render/generate/generate.go
@@ -120,7 +120,9 @@ func (g Generator) Generate(ctx context.Context, out io.Writer) (manifest.Manife
 	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
@@ -129,9 +131,10 @@ func (g Generator) Generate(ctx context.Context, out io.Writer) (manifest.Manife
 			manifests.Append(m)
 		}
 	}
+	endTrace()
 
 	// Generate in-place hydrated kpt Manifests
-	_, endTrace := instrumentation.StartTrace(ctx, "Render_expandGlobKptManifests")
+	_, 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))
@@ -145,6 +148,9 @@ func (g Generator) Generate(ctx context.Context, out io.Writer) (manifest.Manife
 		}
 	}
 	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.
diff --git a/pkg/skaffold/render/renderer/helm/helm.go b/pkg/skaffold/render/renderer/helm/helm.go
index 52fc0043f..e72145cca 100644
--- a/pkg/skaffold/render/renderer/helm/helm.go
+++ b/pkg/skaffold/render/renderer/helm/helm.go
@@ -97,7 +97,7 @@ func (h Helm) Render(ctx context.Context, out io.Writer, builds []graph.Artifact
 }
 
 func (h Helm) generateHelmManifests(ctx context.Context, builds []graph.Artifact) (manifest.ManifestList, error) {
-	renderedManifests := new(bytes.Buffer)
+	var renderedManifests manifest.ManifestList
 	helmEnv := sUtil.OSEnviron()
 	var postRendererArgs []string
 
@@ -143,17 +143,15 @@ func (h Helm) generateHelmManifests(ctx context.Context, builds []graph.Artifact
 		}
 
 		outBuffer := new(bytes.Buffer)
-		if err := helm.Exec(ctx, h, outBuffer, false, helmEnv, args...); err != nil {
+		errBuffer := new(bytes.Buffer)
+		if err := helm.ExecWithStdoutAndStderr(ctx, h, outBuffer, errBuffer, false, helmEnv, args...); err != nil {
 			return nil, helm.UserErr("std out err", fmt.Errorf(outBuffer.String()))
 		}
-		renderedManifests.Write(outBuffer.Bytes())
-	}
-	manifests, err := manifest.Load(bytes.NewReader(renderedManifests.Bytes()))
-	if err != nil {
-		return nil, err
+		log.Entry(ctx).Errorf(errBuffer.String())
+		renderedManifests.Append(outBuffer.Bytes())
 	}
 
-	manifests, err = manifests.SetLabels(h.labels, manifest.NewResourceSelectorLabels(h.transformAllowlist, h.transformDenylist))
+	manifests, err := renderedManifests.SetLabels(h.labels, manifest.NewResourceSelectorLabels(h.transformAllowlist, h.transformDenylist))
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/skaffold/render/renderer/kubectl/kubectl.go b/pkg/skaffold/render/renderer/kubectl/kubectl.go
index 7aa48e595..c7182ed1b 100644
--- a/pkg/skaffold/render/renderer/kubectl/kubectl.go
+++ b/pkg/skaffold/render/renderer/kubectl/kubectl.go
@@ -65,7 +65,7 @@ func New(cfg render.Config, rCfg latest.RenderConfig, labels map[string]string,
 
 func (r Kubectl) Render(ctx context.Context, out io.Writer, builds []graph.Artifact, offline bool) (manifest.ManifestListByConfig, error) {
 	_, endTrace := instrumentation.StartTrace(ctx, "Render_KubectlManifests")
-	log.Entry(ctx).Infof("rendering using kubectl")
+	log.Entry(ctx).Infof("starting render process")
 	instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
 		"RendererType": "kubectl",
 	})
diff --git a/pkg/skaffold/render/renderer/render_mux.go b/pkg/skaffold/render/renderer/render_mux.go
index c3d55f6e7..6e7da1248 100644
--- a/pkg/skaffold/render/renderer/render_mux.go
+++ b/pkg/skaffold/render/renderer/render_mux.go
@@ -24,31 +24,43 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
 	eventV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/v2"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/hooks"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringset"
 )
 
-// GroupRenderer maintains the slice of all `Renderer`s defined in a single Skaffold config.
-type GroupRenderer []Renderer
+// GroupRenderer maintains the slice of all `Renderer`s and their respective lifecycle hooks defined in a single Skaffold config.
+type GroupRenderer struct {
+	Renderers  []Renderer
+	HookRunner hooks.Runner
+}
 
 // RenderMux forwards all method calls to the renderers it contains.
 // When encountering an error, it aborts and returns the error. Otherwise,
 // it collects the results and returns all the manifests.
 type RenderMux struct {
-	renderers GroupRenderer
+	gr GroupRenderer
 }
 
 func NewRenderMux(renderers GroupRenderer) Renderer {
-	return RenderMux{renderers: renderers}
+	return RenderMux{gr: renderers}
 }
 
 func (r RenderMux) Render(ctx context.Context, out io.Writer, artifacts []graph.Artifact, offline bool) (manifest.ManifestListByConfig, error) {
 	allManifests := manifest.NewManifestListByConfig()
-	for i, renderer := range r.renderers {
+
+	w, ctx := output.WithEventContext(ctx, out, constants.Render, constants.SubtaskIDNone)
+	if r.gr.HookRunner != nil {
+		if err := r.gr.HookRunner.RunPreHooks(ctx, w); err != nil {
+			return manifest.ManifestListByConfig{}, err
+		}
+	}
+
+	for i, renderer := range r.gr.Renderers {
 		eventV2.RendererInProgress(i)
-		w, ctx := output.WithEventContext(ctx, out, constants.Render, strconv.Itoa(i))
+		w, ctx = output.WithEventContext(ctx, out, constants.Render, strconv.Itoa(i))
 		ctx, endTrace := instrumentation.StartTrace(ctx, "Render")
 		manifestsByConfig, err := renderer.Render(ctx, w, artifacts, offline)
 		if err != nil {
@@ -64,16 +76,21 @@ func (r RenderMux) Render(ctx context.Context, out io.Writer, artifacts []graph.
 				allManifests.Add(configName, manifests)
 			}
 		}
-
 		eventV2.RendererSucceeded(i)
 		endTrace()
 	}
+	w, ctx = output.WithEventContext(ctx, out, constants.Render, constants.SubtaskIDNone)
+	if r.gr.HookRunner != nil {
+		if err := r.gr.HookRunner.RunPostHooks(ctx, w); err != nil {
+			return manifest.ManifestListByConfig{}, err
+		}
+	}
 	return allManifests, nil
 }
 
 func (r RenderMux) ManifestDeps() ([]string, error) {
 	deps := stringset.New()
-	for _, renderer := range r.renderers {
+	for _, renderer := range r.gr.Renderers {
 		result, err := renderer.ManifestDeps()
 		if err != nil {
 			return nil, err
@@ -82,3 +99,23 @@ func (r RenderMux) ManifestDeps() ([]string, error) {
 	}
 	return deps.ToList(), nil
 }
+
+func (r RenderMux) PreRenderHooks(ctx context.Context, out io.Writer) error {
+	childCtx, endTrace := instrumentation.StartTrace(ctx, "Render_PreHooks")
+	if err := r.gr.HookRunner.RunPreHooks(childCtx, out); err != nil {
+		endTrace(instrumentation.TraceEndError(err))
+		return err
+	}
+	endTrace()
+	return nil
+}
+
+func (r RenderMux) PostRenderHooks(ctx context.Context, out io.Writer) error {
+	childCtx, endTrace := instrumentation.StartTrace(ctx, "Render_PostHooks")
+	if err := r.gr.HookRunner.RunPostHooks(childCtx, out); err != nil {
+		endTrace(instrumentation.TraceEndError(err))
+		return err
+	}
+	endTrace()
+	return nil
+}
diff --git a/pkg/skaffold/render/renderer/renderer.go b/pkg/skaffold/render/renderer/renderer.go
index 2fa973e7f..2e4ce01f9 100644
--- a/pkg/skaffold/render/renderer/renderer.go
+++ b/pkg/skaffold/render/renderer/renderer.go
@@ -21,6 +21,7 @@ import (
 	"io"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/hooks"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render"
@@ -42,26 +43,30 @@ func New(ctx context.Context, cfg render.Config, renderCfg latest.RenderConfig,
 	if renderCfg.Validate != nil || renderCfg.Transform != nil || renderCfg.Kpt != nil {
 		r, err := kpt.New(cfg, renderCfg, hydrationDir, labels, configName, cfg.GetNamespace())
 		if err != nil {
-			return nil, err
+			return GroupRenderer{}, err
 		}
 		log.Entry(ctx).Infof("setting up kpt renderer")
-		return []Renderer{r}, nil
+		return GroupRenderer{
+			Renderers:  []Renderer{r},
+			HookRunner: hooks.NewRenderRunner(renderCfg.Generate.LifecycleHooks, &[]string{cfg.GetNamespace()}, hooks.NewRenderEnvOpts(cfg.GetKubeContext(), []string{cfg.GetNamespace()})),
+		}, err
 	}
 
 	var rs GroupRenderer
+	rs.HookRunner = hooks.NewRenderRunner(renderCfg.Generate.LifecycleHooks, &[]string{cfg.GetNamespace()}, hooks.NewRenderEnvOpts(cfg.GetKubeContext(), []string{cfg.GetNamespace()}))
 	if renderCfg.RawK8s != nil || renderCfg.Kustomize != nil {
 		r, err := kubectl.New(cfg, renderCfg, labels, configName, cfg.GetNamespace())
 		if err != nil {
-			return nil, err
+			return GroupRenderer{}, err
 		}
-		rs = append(rs, r)
+		rs.Renderers = append(rs.Renderers, r)
 	}
 	if renderCfg.Helm != nil {
 		r, err := helm.New(cfg, renderCfg, labels, configName)
 		if err != nil {
-			return nil, err
+			return GroupRenderer{}, err
 		}
-		rs = append(rs, r)
+		rs.Renderers = append(rs.Renderers, r)
 	}
 	return rs, nil
 }
diff --git a/pkg/skaffold/runner/renderer.go b/pkg/skaffold/runner/renderer.go
index ef6d878a0..f03512cf0 100644
--- a/pkg/skaffold/runner/renderer.go
+++ b/pkg/skaffold/runner/renderer.go
@@ -19,6 +19,7 @@ package runner
 import (
 	"context"
 
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/hooks"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/helm"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
@@ -29,13 +30,16 @@ import (
 func GetRenderer(ctx context.Context, runCtx *runcontext.RunContext, hydrationDir string, labels map[string]string, usingLegacyHelmDeploy bool) (renderer.Renderer, error) {
 	ps := runCtx.Pipelines.AllByConfigNames()
 
-	var renderers renderer.GroupRenderer
+	var gr renderer.GroupRenderer
+	gr.HookRunner = hooks.NewRenderRunner(runCtx.GetRenderConfig().LifecycleHooks, &[]string{runCtx.GetNamespace()},
+		hooks.NewRenderEnvOpts(runCtx.KubeContext, []string{runCtx.GetNamespace()}))
 	for configName, p := range ps {
 		rs, err := renderer.New(ctx, runCtx, p.Render, hydrationDir, labels, configName)
 		if err != nil {
 			return nil, err
 		}
-		renderers = append(renderers, rs...)
+		gr.Renderers = append(gr.Renderers, rs.Renderers...)
+		gr.HookRunner = rs.HookRunner
 	}
 	// In case of legacy helm deployer configured and render command used
 	// force a helm renderer from deploy helm config
@@ -55,8 +59,8 @@ func GetRenderer(ctx context.Context, runCtx *runcontext.RunContext, hydrationDi
 			if err != nil {
 				return nil, err
 			}
-			renderers = append(renderers, r)
+			gr.Renderers = append(gr.Renderers, r)
 		}
 	}
-	return renderer.NewRenderMux(renderers), nil
+	return renderer.NewRenderMux(gr), nil
 }
diff --git a/pkg/skaffold/schema/v2beta29/upgrade.go b/pkg/skaffold/schema/v2beta29/upgrade.go
index 53efb1068..60a9b11d8 100644
--- a/pkg/skaffold/schema/v2beta29/upgrade.go
+++ b/pkg/skaffold/schema/v2beta29/upgrade.go
@@ -17,15 +17,25 @@ limitations under the License.
 package v2beta29
 
 import (
+	"context"
 	"encoding/json"
+	"strings"
 
 	"github.com/pkg/errors"
 
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	next "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util"
 	pkgutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
 )
 
+var migrations = map[string]string{
+	"/deploy/kubectl":             "/manifests/rawYaml",
+	"/deploy/kustomize/paths":     "/manifests/kustomize/paths",
+	"/deploy/kustomize/buildArgs": "/manifests/kustomize/buildArgs",
+	"/deploy/helm":                "/manifests/helm",
+}
+
 // Upgrade upgrades a configuration to the next version.
 // v2beta29 is the last config version for skaffold v1, and future version will
 // follow the naming scheme of v3*
@@ -35,7 +45,24 @@ func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) {
 	newConfig.APIVersion = next.Version
 
 	err := util.UpgradePipelines(c, &newConfig, upgradeOnePipeline)
-	return &newConfig, err
+	if err != nil {
+		return &newConfig, err
+	}
+
+	var newProfiles []next.Profile
+	// seed with existing Profiles
+	if c.Profiles != nil {
+		pkgutil.CloneThroughJSON(newConfig.Profiles, &newProfiles)
+	}
+
+	// Update profiles patches
+	for i, p := range c.Profiles {
+		upgradePatches(p.Patches, newProfiles[i].Patches)
+	}
+
+	newConfig.Profiles = newProfiles
+
+	return &newConfig, nil
 }
 
 func upgradeOnePipeline(oldPipeline, newPipeline interface{}) error {
@@ -56,12 +83,8 @@ func upgradeOnePipeline(oldPipeline, newPipeline interface{}) error {
 		if len(newPL.Render.Kustomize.Paths) == 0 {
 			newPL.Render.Kustomize.Paths = append(newPL.Render.Kustomize.Paths, ".")
 		}
-		if len(oldPL.Deploy.KustomizeDeploy.BuildArgs) != 0 {
-			return errors.New("converting deploy.kustomize.buildArgs isn't currently supported")
-		}
 	}
 
-	// TODO(marlongamez): what should happen when migrating v2?
 	// Copy Kpt deploy config to render config
 	if oldPL.Deploy.KptDeploy != nil {
 		return errors.New("converting deploy.kpt isn't currently supported")
@@ -124,6 +147,18 @@ func upgradeOnePipeline(oldPipeline, newPipeline interface{}) error {
 		newPL.Deploy.LegacyHelmDeploy = &next.LegacyHelmDeploy{}
 		newPL.Deploy.LegacyHelmDeploy.LifecycleHooks = newHelm.LifecycleHooks
 	}
-
 	return nil
 }
+
+func upgradePatches(olds []JSONPatch, news []next.JSONPatch) {
+	for i, old := range olds {
+		for str, repStr := range migrations {
+			if strings.Contains(old.Path, str) {
+				news[i].Path = strings.ReplaceAll(old.Path, str, repStr)
+			}
+			if strings.Contains(old.Path, "/deploy/kpt") {
+				log.Entry(context.TODO()).Warn("skip migrating kpt deploy sections. Please migrate these over manually")
+			}
+		}
+	}
+}
diff --git a/pkg/skaffold/verify/docker/verify.go b/pkg/skaffold/verify/docker/verify.go
index cc9cc7ea5..2174709da 100644
--- a/pkg/skaffold/verify/docker/verify.go
+++ b/pkg/skaffold/verify/docker/verify.go
@@ -134,6 +134,16 @@ func (v *Verifier) Verify(ctx context.Context, out io.Writer, allbuilds []graph.
 		for _, b := range allbuilds {
 			if tc.Container.Image == b.ImageName {
 				foundArtifact = true
+				imageID, err := v.client.ImageID(ctx, b.Tag)
+				if err != nil {
+					return fmt.Errorf("getting imageID for %q: %w", b.Tag, err)
+				}
+				if imageID == "" {
+					// not available in local docker daemon, needs to be pulled
+					if err := v.client.Pull(ctx, out, b.Tag, v1.Platform{}); err != nil {
+						return err
+					}
+				}
 				na = graph.Artifact{
 					ImageName: tc.Container.Image,
 					Tag:       b.Tag,
@@ -146,8 +156,7 @@ func (v *Verifier) Verify(ctx context.Context, out io.Writer, allbuilds []graph.
 			}
 		}
 		if !foundArtifact {
-			err = v.client.Pull(ctx, out, tc.Container.Image, v1.Platform{})
-			if err != nil {
+			if err := v.client.Pull(ctx, out, tc.Container.Image, v1.Platform{}); err != nil {
 				return err
 			}
 			na = graph.Artifact{


Test patch:
diff --git a/integration/build_test.go b/integration/build_test.go
index 22e315cf7..0dee0d98f 100644
--- a/integration/build_test.go
+++ b/integration/build_test.go
@@ -25,13 +25,16 @@ import (
 	"time"
 
 	"4d63.com/tz"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 
+	"github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/flags"
 	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
 	kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
+	"github.com/GoogleContainerTools/skaffold/testutil"
 )
 
 const imageName = "gcr.io/k8s-skaffold/simple-build:"
@@ -109,9 +112,79 @@ func TestBuild(t *testing.T) {
 	}
 }
 
+func TestBuildWithWithPlatform(t *testing.T) {
+	MarkIntegrationTest(t, CanRunWithoutGcp)
+
+	tests := []struct {
+		description       string
+		dir               string
+		args              []string
+		image             string
+		expectedPlatforms []v1.Platform
+	}{
+		{
+			description:       "docker build linux/amd64",
+			dir:               "testdata/build/docker-with-platform-amd",
+			args:              []string{"--platform", "linux/amd64"},
+			expectedPlatforms: []v1.Platform{{OS: "linux", Architecture: "amd64"}},
+		},
+		{
+			description:       "docker build linux/arm64",
+			dir:               "testdata/build/docker-with-platform-arm",
+			args:              []string{"--platform", "linux/arm64"},
+			expectedPlatforms: []v1.Platform{{OS: "linux", Architecture: "arm64"}},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.description, func(t *testing.T) {
+			tmpfile := testutil.TempFile(t, "", []byte{})
+			args := append(test.args, "--file-output", tmpfile)
+			skaffold.Build(args...).InDir(test.dir).RunOrFail(t)
+			bytes, err := os.ReadFile(tmpfile)
+			failNowIfError(t, err)
+			buildArtifacts, err := flags.ParseBuildOutput(bytes)
+			failNowIfError(t, err)
+			checkLocalImagePlatforms(t, buildArtifacts.Builds[0].Tag, test.expectedPlatforms)
+		})
+	}
+}
+
+func TestBuildWithMultiPlatforms(t *testing.T) {
+	MarkIntegrationTest(t, NeedsGcp)
+
+	tests := []struct {
+		description       string
+		dir               string
+		args              []string
+		image             string
+		expectedPlatforms []v1.Platform
+	}{
+		{
+			description:       "build cross platform images with gcb",
+			dir:               "testdata/build/gcb-with-platform",
+			args:              []string{"--platform", "linux/arm64,linux/amd64"},
+			expectedPlatforms: []v1.Platform{{OS: "linux", Architecture: "arm64"}, {OS: "linux", Architecture: "amd64"}},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.description, func(t *testing.T) {
+			tmpfile := testutil.TempFile(t, "", []byte{})
+			args := append(test.args, "--file-output", tmpfile)
+			skaffold.Build(args...).InDir(test.dir).RunOrFail(t)
+			bytes, err := os.ReadFile(tmpfile)
+			failNowIfError(t, err)
+			buildArtifacts, err := flags.ParseBuildOutput(bytes)
+			failNowIfError(t, err)
+			checkRemoteImagePlatforms(t, buildArtifacts.Builds[0].Tag, test.expectedPlatforms)
+		})
+	}
+}
+
 // TestExpectedBuildFailures verifies that `skaffold build` fails in expected ways
 func TestExpectedBuildFailures(t *testing.T) {
-	MarkIntegrationTest(t, CanRunWithoutGcp)
+	MarkIntegrationTest(t, NeedsGcp)
 	if !jib.JVMFound(context.Background()) {
 		t.Fatal("test requires Java VM")
 	}
@@ -142,6 +215,59 @@ func TestExpectedBuildFailures(t *testing.T) {
 	}
 }
 
+func checkLocalImagePlatforms(t *testing.T, image string, expected []v1.Platform) {
+	if expected == nil {
+		return
+	}
+	t.Helper()
+
+	cfg, err := kubectx.CurrentConfig()
+	failNowIfError(t, err)
+
+	client, err := docker.NewAPIClient(context.Background(), &runcontext.RunContext{
+		KubeContext: cfg.CurrentContext,
+	})
+	failNowIfError(t, err)
+	inspect, _, err := client.ImageInspectWithRaw(context.Background(), image)
+	failNowIfError(t, err)
+
+	actual := []v1.Platform{{Architecture: inspect.Architecture, OS: inspect.Os}}
+	checkPlatformsEqual(t, actual, expected)
+}
+
+func checkRemoteImagePlatforms(t *testing.T, image string, expected []v1.Platform) {
+	if expected == nil {
+		return
+	}
+	t.Helper()
+	actual, err := docker.GetPlatforms(image)
+	if err != nil {
+		t.Error(err)
+	}
+	checkPlatformsEqual(t, actual, expected)
+}
+
+func checkPlatformsEqual(t *testing.T, actual, expected []v1.Platform) {
+	expectedLen := len(expected)
+	actualLen := len(actual)
+
+	if expectedLen != actualLen {
+		t.Fatalf("length of the slices differ: Expected %d, but was %d", expectedLen, actualLen)
+	}
+	wmap := make(map[string]int)
+	for _, elem := range expected {
+		wmap[elem.OS+"/"+elem.Architecture]++
+	}
+	for _, elem := range actual {
+		wmap[elem.OS+"/"+elem.Architecture]--
+	}
+	for _, v := range wmap {
+		if v != 0 {
+			t.Fatalf("elements are missing (negative integers) or excess (positive integers): %#v", wmap)
+		}
+	}
+}
+
 // checkImageExists asserts that the given image is present
 func checkImageExists(t *testing.T, image string) {
 	t.Helper()
diff --git a/integration/testdata/build/docker-with-platform-amd/Dockerfile b/integration/testdata/build/docker-with-platform-amd/Dockerfile
new file mode 100644
index 000000000..8f6762484
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-amd/Dockerfile
@@ -0,0 +1,7 @@
+FROM golang:1.15-alpine as builder
+COPY main.go .
+RUN go build -o /app main.go
+
+FROM alpine:3
+CMD ["./app"]
+COPY --from=builder /app .
diff --git a/integration/testdata/build/docker-with-platform-amd/k8s/deployment.yaml b/integration/testdata/build/docker-with-platform-amd/k8s/deployment.yaml
new file mode 100644
index 000000000..f1c71fd8b
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-amd/k8s/deployment.yaml
@@ -0,0 +1,18 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: getting-started
+  labels:
+    app: getting-started
+spec:
+  selector:
+    matchLabels:
+      app: getting-started
+  template:
+    metadata:
+      labels:
+        app: getting-started
+    spec:
+      containers:
+      - name: getting-started
+        image: skaffold-hello
diff --git a/integration/testdata/build/docker-with-platform-amd/k8s/pod.yaml b/integration/testdata/build/docker-with-platform-amd/k8s/pod.yaml
new file mode 100644
index 000000000..f80ddcdf6
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-amd/k8s/pod.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+spec:
+  containers:
+  - name: getting-started
+    image: skaffold-hello
diff --git a/integration/testdata/build/docker-with-platform-amd/main.go b/integration/testdata/build/docker-with-platform-amd/main.go
new file mode 100644
index 000000000..8c3e45267
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-amd/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"fmt"
+	"time"
+)
+
+func main() {
+	for counter := 0; ; counter++ {
+		fmt.Println("Hello world!", counter)
+
+		time.Sleep(time.Second * 1)
+	}
+}
diff --git a/integration/testdata/build/docker-with-platform-amd/skaffold.yaml b/integration/testdata/build/docker-with-platform-amd/skaffold.yaml
new file mode 100644
index 000000000..8ed4fda2d
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-amd/skaffold.yaml
@@ -0,0 +1,15 @@
+apiVersion: skaffold/v3alpha1
+kind: Config
+build:
+  artifacts:
+  - image: docker-with-platform-amd
+
+profiles:
+  - name: pod
+    manifests:
+      rawYaml:
+      - k8s/pod.yaml
+  - name: deployment
+    manifests:
+      rawYaml:
+      - k8s/deployment.yaml
\ No newline at end of file
diff --git a/integration/testdata/build/docker-with-platform-arm/Dockerfile b/integration/testdata/build/docker-with-platform-arm/Dockerfile
new file mode 100644
index 000000000..8f6762484
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-arm/Dockerfile
@@ -0,0 +1,7 @@
+FROM golang:1.15-alpine as builder
+COPY main.go .
+RUN go build -o /app main.go
+
+FROM alpine:3
+CMD ["./app"]
+COPY --from=builder /app .
diff --git a/integration/testdata/build/docker-with-platform-arm/k8s/deployment.yaml b/integration/testdata/build/docker-with-platform-arm/k8s/deployment.yaml
new file mode 100644
index 000000000..f1c71fd8b
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-arm/k8s/deployment.yaml
@@ -0,0 +1,18 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: getting-started
+  labels:
+    app: getting-started
+spec:
+  selector:
+    matchLabels:
+      app: getting-started
+  template:
+    metadata:
+      labels:
+        app: getting-started
+    spec:
+      containers:
+      - name: getting-started
+        image: skaffold-hello
diff --git a/integration/testdata/build/docker-with-platform-arm/k8s/pod.yaml b/integration/testdata/build/docker-with-platform-arm/k8s/pod.yaml
new file mode 100644
index 000000000..f80ddcdf6
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-arm/k8s/pod.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+spec:
+  containers:
+  - name: getting-started
+    image: skaffold-hello
diff --git a/integration/testdata/build/docker-with-platform-arm/main.go b/integration/testdata/build/docker-with-platform-arm/main.go
new file mode 100644
index 000000000..8c3e45267
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-arm/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"fmt"
+	"time"
+)
+
+func main() {
+	for counter := 0; ; counter++ {
+		fmt.Println("Hello world!", counter)
+
+		time.Sleep(time.Second * 1)
+	}
+}
diff --git a/integration/testdata/build/docker-with-platform-arm/skaffold.yaml b/integration/testdata/build/docker-with-platform-arm/skaffold.yaml
new file mode 100644
index 000000000..2baaaf30d
--- /dev/null
+++ b/integration/testdata/build/docker-with-platform-arm/skaffold.yaml
@@ -0,0 +1,15 @@
+apiVersion: skaffold/v3alpha1
+kind: Config
+build:
+  artifacts:
+  - image: docker-with-platform-arm
+
+profiles:
+  - name: pod
+    manifests:
+      rawYaml:
+      - k8s/pod.yaml
+  - name: deployment
+    manifests:
+      rawYaml:
+      - k8s/deployment.yaml
\ No newline at end of file
diff --git a/integration/testdata/build/gcb-with-platform/Dockerfile b/integration/testdata/build/gcb-with-platform/Dockerfile
new file mode 100644
index 000000000..67f8c3cbb
--- /dev/null
+++ b/integration/testdata/build/gcb-with-platform/Dockerfile
@@ -0,0 +1,14 @@
+FROM golang:1.18 as builder
+WORKDIR /code
+COPY main.go .
+COPY go.mod .
+# `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
+ARG SKAFFOLD_GO_GCFLAGS
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -trimpath -o /app main.go
+
+FROM alpine:3
+# Define GOTRACEBACK to mark this container as using the Go language runtime
+# for `skaffold debug` (https://skaffold.dev/docs/workflows/debug/).
+ENV GOTRACEBACK=single
+CMD ["./app"]
+COPY --from=builder /app .
diff --git a/integration/testdata/build/gcb-with-platform/go.mod b/integration/testdata/build/gcb-with-platform/go.mod
new file mode 100644
index 000000000..adee4fb0a
--- /dev/null
+++ b/integration/testdata/build/gcb-with-platform/go.mod
@@ -0,0 +1,3 @@
+module github.com/GoogleContainerTools/skaffold/examples/google-cloud-build
+
+go 1.18
diff --git a/integration/testdata/build/gcb-with-platform/k8s-pod.yaml b/integration/testdata/build/gcb-with-platform/k8s-pod.yaml
new file mode 100644
index 000000000..e8e559450
--- /dev/null
+++ b/integration/testdata/build/gcb-with-platform/k8s-pod.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: getting-started
+spec:
+  containers:
+  - name: getting-started
+    image: gcr.io/k8s-skaffold/skaffold-example-with-location
diff --git a/integration/testdata/build/gcb-with-platform/main.go b/integration/testdata/build/gcb-with-platform/main.go
new file mode 100644
index 000000000..593721cfe
--- /dev/null
+++ b/integration/testdata/build/gcb-with-platform/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"fmt"
+	"time"
+)
+
+func main() {
+	for {
+		fmt.Println("Hello world!")
+
+		time.Sleep(time.Second * 1)
+	}
+}
diff --git a/integration/testdata/build/gcb-with-platform/skaffold.yaml b/integration/testdata/build/gcb-with-platform/skaffold.yaml
new file mode 100644
index 000000000..79658f19a
--- /dev/null
+++ b/integration/testdata/build/gcb-with-platform/skaffold.yaml
@@ -0,0 +1,11 @@
+apiVersion: skaffold/v3alpha1
+kind: Config
+build:
+  artifacts:
+    - image: gcr.io/k8s-skaffold/gcb-with-platform
+  googleCloudBuild:
+    projectId: k8s-skaffold
+    region: asia-east1
+manifests:
+  rawYaml:
+    - k8s-*
\ No newline at end of file
diff --git a/integration/testdata/debug/java/pom.xml b/integration/testdata/debug/java/pom.xml
index 14277a770..02e8460d9 100644
--- a/integration/testdata/debug/java/pom.xml
+++ b/integration/testdata/debug/java/pom.xml
@@ -8,7 +8,7 @@
   <description>Simple Java server with Skaffold and Jib</description>
 
   <properties>
-    <jib.maven-plugin-version>3.2.1</jib.maven-plugin-version>
+    <jib.maven-plugin-version>3.3.0</jib.maven-plugin-version>
     <maven.compiler.source>1.8</maven.compiler.source>
     <maven.compiler.target>1.8</maven.compiler.target>
   </properties>
diff --git a/integration/testdata/jib/pom.xml b/integration/testdata/jib/pom.xml
index f7bbbfd94..52d91a8d6 100644
--- a/integration/testdata/jib/pom.xml
+++ b/integration/testdata/jib/pom.xml
@@ -8,7 +8,7 @@
     <description>Simple Java server with Skaffold and Jib</description>
 
     <properties>
-        <jib.maven-plugin-version>3.2.1</jib.maven-plugin-version>
+        <jib.maven-plugin-version>3.3.0</jib.maven-plugin-version>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
     </properties>
diff --git a/pkg/skaffold/build/cache/retrieve_test.go b/pkg/skaffold/build/cache/retrieve_test.go
index 4d4ae6730..125aeb881 100644
--- a/pkg/skaffold/build/cache/retrieve_test.go
+++ b/pkg/skaffold/build/cache/retrieve_test.go
@@ -142,6 +142,9 @@ func TestCacheBuildLocal(t *testing.T) {
 		t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
 			return args, nil
 		})
+		t.Override(&docker.EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
+			return args, nil
+		})
 
 		// Create cache
 		cfg := &mockConfig{
@@ -239,6 +242,9 @@ func TestCacheBuildRemote(t *testing.T) {
 		t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
 			return args, nil
 		})
+		t.Override(&docker.EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
+			return args, nil
+		})
 
 		// Create cache
 		cfg := &mockConfig{
@@ -324,6 +330,9 @@ func TestCacheFindMissing(t *testing.T) {
 		t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
 			return args, nil
 		})
+		t.Override(&docker.EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
+			return args, nil
+		})
 
 		// Create cache
 		cfg := &mockConfig{
diff --git a/pkg/skaffold/build/docker/docker_test.go b/pkg/skaffold/build/docker/docker_test.go
index b46212cc2..a7c4ec1cd 100644
--- a/pkg/skaffold/build/docker/docker_test.go
+++ b/pkg/skaffold/build/docker/docker_test.go
@@ -134,7 +134,7 @@ func TestDockerCLIBuild(t *testing.T) {
 		testutil.Run(t, test.description, func(t *testutil.T) {
 			t.NewTempDir().Touch("Dockerfile").Chdir()
 			dockerfilePath, _ := filepath.Abs("Dockerfile")
-			t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
+			t.Override(&docker.EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
 				return args, nil
 			})
 			t.Override(&docker.DefaultAuthHelper, stubAuth{})
@@ -233,7 +233,7 @@ func TestDockerCLICheckCacheFromArgs(t *testing.T) {
 			a.Workspace = "."
 			a.DockerArtifact.DockerfilePath = dockerfilePath
 			t.Override(&docker.DefaultAuthHelper, stubAuth{})
-			t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
+			t.Override(&docker.EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
 				return args, nil
 			})
 
diff --git a/pkg/skaffold/build/docker/errors_test.go b/pkg/skaffold/build/docker/errors_test.go
index ccc694698..6f92bdad2 100644
--- a/pkg/skaffold/build/docker/errors_test.go
+++ b/pkg/skaffold/build/docker/errors_test.go
@@ -53,7 +53,7 @@ Refer https://skaffold.dev/docs/references/yaml/#build-artifacts-docker for deta
 		testutil.Run(t, test.description, func(t *testutil.T) {
 			t.NewTempDir().Touch("Dockerfile").Chdir()
 			dockerfilePath, _ := filepath.Abs("Dockerfile")
-			t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
+			t.Override(&docker.EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
 				return args, nil
 			})
 			t.Override(&util.DefaultExecCommand, testutil.CmdRun(
diff --git a/pkg/skaffold/build/local/local_test.go b/pkg/skaffold/build/local/local_test.go
index 8cd330e73..b9bc45b17 100644
--- a/pkg/skaffold/build/local/local_test.go
+++ b/pkg/skaffold/build/local/local_test.go
@@ -228,7 +228,7 @@ func TestLocalRun(t *testing.T) {
 			t.Override(&docker.NewAPIClient, func(context.Context, docker.Config) (docker.LocalDaemon, error) {
 				return fakeLocalDaemon(test.api), nil
 			})
-			t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
+			t.Override(&docker.EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
 				return args, nil
 			})
 			testEvent.InitializeState([]latest.Pipeline{{
@@ -402,7 +402,7 @@ func TestGetArtifactBuilder(t *testing.T) {
 			t.Override(&docker.NewAPIClient, func(context.Context, docker.Config) (docker.LocalDaemon, error) {
 				return fakeLocalDaemon(&testutil.FakeAPIClient{}), nil
 			})
-			t.Override(&docker.EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
+			t.Override(&docker.EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
 				return args, nil
 			})
 
diff --git a/pkg/skaffold/docker/image_test.go b/pkg/skaffold/docker/image_test.go
index 7467a86d9..8a6e4090d 100644
--- a/pkg/skaffold/docker/image_test.go
+++ b/pkg/skaffold/docker/image_test.go
@@ -195,7 +195,7 @@ func TestBuild(t *testing.T) {
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
 			t.Override(&DefaultAuthHelper, testAuthHelper{})
-			t.Override(&EvalBuildArgs, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string) (map[string]*string, error) {
+			t.Override(&EvalBuildArgsWithEnv, func(_ config.RunMode, _ string, _ string, args map[string]*string, _ map[string]*string, _ map[string]string) (map[string]*string, error) {
 				return util.EvaluateEnvTemplateMap(args)
 			})
 			t.SetEnvs(test.env)
diff --git a/pkg/skaffold/hooks/deploy_test.go b/pkg/skaffold/hooks/deploy_test.go
index 5a181a7ca..71d758bd7 100644
--- a/pkg/skaffold/hooks/deploy_test.go
+++ b/pkg/skaffold/hooks/deploy_test.go
@@ -32,6 +32,8 @@ import (
 	"github.com/GoogleContainerTools/skaffold/testutil"
 )
 
+const testKubeContext = "context1"
+
 func TestDeployHooks(t *testing.T) {
 	testutil.Run(t, "TestDeployHooks", func(t *testutil.T) {
 		hooks := latest.DeployHooks{
@@ -91,10 +93,9 @@ func TestDeployHooks(t *testing.T) {
 			LifecycleHooks: hooks,
 		}
 		namespaces := []string{"np1", "np2"}
-		kubeContext := "context1"
-		opts := NewDeployEnvOpts("run_id", kubeContext, namespaces)
+		opts := NewDeployEnvOpts("run_id", testKubeContext, namespaces)
 		formatter := func(corev1.Pod, corev1.ContainerStatus, func() bool) log.Formatter { return mockLogFormatter{} }
-		runner := NewDeployRunner(&kubectl.CLI{KubeContext: kubeContext}, deployer.LifecycleHooks, &namespaces, formatter, opts)
+		runner := NewDeployRunner(&kubectl.CLI{KubeContext: testKubeContext}, deployer.LifecycleHooks, &namespaces, formatter, opts)
 
 		t.Override(&util.DefaultExecCommand,
 			testutil.CmdRunWithOutput("kubectl --context context1 exec pod1 --namespace np1 -c container1 -- foo pre-hook", preContainerHookOut).
diff --git a/pkg/skaffold/hooks/render_test.go b/pkg/skaffold/hooks/render_test.go
new file mode 100644
index 000000000..e67427775
--- /dev/null
+++ b/pkg/skaffold/hooks/render_test.go
@@ -0,0 +1,75 @@
+/*
+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 hooks
+
+import (
+	"bytes"
+	"context"
+	"testing"
+
+	kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
+	"github.com/GoogleContainerTools/skaffold/testutil"
+)
+
+func TestRenderHooks(t *testing.T) {
+	testutil.Run(t, "TestRenderHooks", func(t *testutil.T) {
+		hooks := latest.RenderHooks{
+			PreHooks: []latest.RenderHookItem{
+				{
+					HostHook: &latest.HostHook{
+						OS:      []string{"linux", "darwin"},
+						Command: []string{"sh", "-c", "echo pre-hook running with SKAFFOLD_KUBE_CONTEXT=$SKAFFOLD_KUBE_CONTEXT,SKAFFOLD_NAMESPACES=$SKAFFOLD_NAMESPACES"},
+					},
+				},
+				{
+					HostHook: &latest.HostHook{
+						OS:      []string{"windows"},
+						Command: []string{"cmd.exe", "/C", "echo pre-hook running with SKAFFOLD_KUBE_CONTEXT=%SKAFFOLD_KUBE_CONTEXT%,SKAFFOLD_NAMESPACES=%SKAFFOLD_NAMESPACES%"},
+					},
+				},
+			},
+			PostHooks: []latest.RenderHookItem{
+				{
+					HostHook: &latest.HostHook{
+						OS:      []string{"linux", "darwin"},
+						Command: []string{"sh", "-c", "echo post-hook running with SKAFFOLD_KUBE_CONTEXT=$SKAFFOLD_KUBE_CONTEXT,SKAFFOLD_NAMESPACES=$SKAFFOLD_NAMESPACES"},
+					},
+				},
+				{
+					HostHook: &latest.HostHook{
+						OS:      []string{"windows"},
+						Command: []string{"cmd.exe", "/C", "echo post-hook running with SKAFFOLD_KUBE_CONTEXT=%SKAFFOLD_KUBE_CONTEXT%,SKAFFOLD_NAMESPACES=%SKAFFOLD_NAMESPACES%"},
+					},
+				},
+			},
+		}
+		preHostHookOut := "pre-hook running with SKAFFOLD_KUBE_CONTEXT=context1,SKAFFOLD_NAMESPACES=np1,np2"
+		postHostHookOut := "post-hook running with SKAFFOLD_KUBE_CONTEXT=context1,SKAFFOLD_NAMESPACES=np1,np2"
+		namespaces := []string{"np1", "np2"}
+		opts := NewRenderEnvOpts(testKubeContext, namespaces)
+		runner := newRenderRunner(hooks, &namespaces, opts)
+		t.Override(&kubernetesclient.Client, fakeKubernetesClient)
+		var preOut, postOut bytes.Buffer
+		err := runner.RunPreHooks(context.Background(), &preOut)
+		t.CheckNoError(err)
+		t.CheckContains(preHostHookOut, preOut.String())
+		err = runner.RunPostHooks(context.Background(), &postOut)
+		t.CheckNoError(err)
+		t.CheckContains(postHostHookOut, postOut.String())
+	})
+}
diff --git a/pkg/skaffold/kubernetes/status/resource/deployment_test.go b/pkg/skaffold/kubernetes/status/resource/deployment_test.go
index e8c2fcb3e..829b33e12 100644
--- a/pkg/skaffold/kubernetes/status/resource/deployment_test.go
+++ b/pkg/skaffold/kubernetes/status/resource/deployment_test.go
@@ -204,6 +204,14 @@ func TestIsErrAndNotRetriable(t *testing.T) {
 			statusCode:  proto.StatusCode_STATUSCHECK_DEADLINE_EXCEEDED,
 			expected:    true,
 		},
+		{
+			description: "unschedule error code",
+			statusCode:  proto.StatusCode_STATUSCHECK_NODE_UNSCHEDULABLE,
+		},
+		{
+			description: "unschedule unknown error",
+			statusCode:  proto.StatusCode_STATUSCHECK_UNKNOWN_UNSCHEDULABLE,
+		},
 		{
 			description: "rollout status nil error",
 			statusCode:  proto.StatusCode_STATUSCHECK_SUCCESS,
diff --git a/pkg/skaffold/platform/platform_test.go b/pkg/skaffold/platform/platform_test.go
index d3c90dfb7..df60cb9bc 100644
--- a/pkg/skaffold/platform/platform_test.go
+++ b/pkg/skaffold/platform/platform_test.go
@@ -253,3 +253,33 @@ func TestIntersect(t *testing.T) {
 		})
 	}
 }
+
+func TestParse(t *testing.T) {
+	tests := []struct {
+		description string
+		input       []string
+		shouldErr   bool
+	}{
+		{
+			description: "known platforms",
+			input:       []string{"linux/arm64", "darwin/amd64", "freebsd/arm"},
+			shouldErr:   false,
+		},
+		{
+			description: "unknown os",
+			input:       []string{"foo/arm64", "darwin/amd64", "freebsd/arm"},
+			shouldErr:   true,
+		},
+		{
+			description: "unknown arch",
+			input:       []string{"linux/arm64", "darwin/foo", "freebsd/arm"},
+			shouldErr:   true,
+		},
+	}
+	for _, test := range tests {
+		testutil.Run(t, test.description, func(t *testutil.T) {
+			_, err := Parse(test.input)
+			t.CheckError(test.shouldErr, err)
+		})
+	}
+}
diff --git a/pkg/skaffold/render/renderer/render_mux_test.go b/pkg/skaffold/render/renderer/render_mux_test.go
index 6fe72af4b..dbc2e1546 100644
--- a/pkg/skaffold/render/renderer/render_mux_test.go
+++ b/pkg/skaffold/render/renderer/render_mux_test.go
@@ -41,24 +41,31 @@ func TestRenderMux_Render(t *testing.T) {
 		{
 			name: "concatenates render results with separator",
 			renderers: GroupRenderer{
-				mock{configName: "config1", manifests: "manifest-1", deps: []string{"file1.txt", "file2.txt"}},
-				mock{configName: "config2", manifests: "manifest-2", deps: []string{"file2.txt", "file3.txt"}}},
+				Renderers: []Renderer{
+					mock{configName: "config1", manifests: "manifest-1", deps: []string{"file1.txt", "file2.txt"}},
+					mock{configName: "config2", manifests: "manifest-2", deps: []string{"file2.txt", "file3.txt"}},
+				},
+			},
 			expected:     "manifest-1\n---\nmanifest-2",
 			expectedDeps: []string{"file1.txt", "file2.txt", "file3.txt"},
 		},
 		{
 			name: "returns empty string when any call fails",
 			renderers: GroupRenderer{
-				mock{manifests: "manifest-1", deps: []string{"file1.txt"}},
-				mock{deps: []string{"file2.txt"}, shouldErr: true}},
+				Renderers: []Renderer{
+					mock{manifests: "manifest-1", deps: []string{"file1.txt"}},
+					mock{deps: []string{"file2.txt"}, shouldErr: true}},
+			},
 			expectedDeps: []string{"file1.txt", "file2.txt"},
 			shouldErr:    true,
 		},
 		{
 			name: "short-circuits when first call fails",
 			renderers: GroupRenderer{
-				mock{deps: []string{"file1.txt"}, shouldErr: true},
-				mock{manifests: "manifest-2", deps: []string{"file2.txt"}}},
+				Renderers: []Renderer{
+					mock{deps: []string{"file1.txt"}, shouldErr: true},
+					mock{manifests: "manifest-2", deps: []string{"file2.txt"}}},
+			},
 			expectedDeps: []string{"file1.txt", "file2.txt"},
 			shouldErr:    true,
 		},
diff --git a/pkg/skaffold/runner/renderer_test.go b/pkg/skaffold/runner/renderer_test.go
index 8b68d7fcd..22659329c 100644
--- a/pkg/skaffold/runner/renderer_test.go
+++ b/pkg/skaffold/runner/renderer_test.go
@@ -22,8 +22,6 @@ import (
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/helm"
-	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/kpt"
-	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/renderer/kubectl"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
 	"github.com/GoogleContainerTools/skaffold/testutil"
@@ -82,32 +80,50 @@ func TestGetRenderer(tOuter *testing.T) {
 						},
 					},
 				},
-				expected: renderer.NewRenderMux([]renderer.Renderer{
-					t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)}),
+				// expected: renderer.NewRenderMux([]renderer.Renderer{
+				// 	t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)}),
+				expected: renderer.NewRenderMux(
+					renderer.GroupRenderer{
+						Renderers: []renderer.Renderer{
+							t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)},
+					},
+				),
 			},
 			{
 				description: "helm renderer",
 				cfg: latest.Pipeline{
 					Render: helmConfig,
 				},
-				expected: renderer.NewRenderMux([]renderer.Renderer{
-					t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)}),
+				expected: renderer.NewRenderMux(
+					renderer.GroupRenderer{
+						Renderers: []renderer.Renderer{
+							t.RequireNonNilResult(helm.New(rc, helmConfig, labels, "")).(renderer.Renderer)},
+					},
+				),
 			},
 			{
 				description: "kubectl renderer",
 				cfg: latest.Pipeline{
 					Render: kubectlCfg,
 				},
-				expected: renderer.NewRenderMux([]renderer.Renderer{
-					t.RequireNonNilResult(kubectl.New(rc, kubectlCfg, labels, "", "")).(renderer.Renderer)}),
+				expected: renderer.NewRenderMux(
+					renderer.GroupRenderer{
+						Renderers: []renderer.Renderer{
+							t.RequireNonNilResult(helm.New(rc, kubectlCfg, labels, "")).(renderer.Renderer)},
+					},
+				),
 			},
 			{
 				description: "kpt renderer",
 				cfg: latest.Pipeline{
 					Render: kptConfig,
 				},
-				expected: renderer.NewRenderMux([]renderer.Renderer{
-					t.RequireNonNilResult(kpt.New(rc, kptConfig, "", labels, "", "")).(renderer.Renderer)}),
+				expected: renderer.NewRenderMux(
+					renderer.GroupRenderer{
+						Renderers: []renderer.Renderer{
+							t.RequireNonNilResult(helm.New(rc, kptConfig, labels, "")).(renderer.Renderer)},
+					},
+				),
 			},
 			{
 				description: "kpt renderer when validate configured",
@@ -117,8 +133,12 @@ func TestGetRenderer(tOuter *testing.T) {
 						Validate: &[]latest.Validator{{Name: "kubeval"}},
 					},
 				},
-				expected: renderer.NewRenderMux([]renderer.Renderer{
-					t.RequireNonNilResult(kpt.New(rc, kptConfig, "", labels, "", "")).(renderer.Renderer)}),
+				expected: renderer.NewRenderMux(
+					renderer.GroupRenderer{
+						Renderers: []renderer.Renderer{
+							t.RequireNonNilResult(helm.New(rc, kptConfig, labels, "")).(renderer.Renderer)},
+					},
+				),
 			},
 		}
 		for _, test := range tests {
diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go
index 2ca997d5f..2e70b9a76 100644
--- a/pkg/skaffold/schema/latest/config.go
+++ b/pkg/skaffold/schema/latest/config.go
@@ -601,6 +601,9 @@ type Generate struct {
 
 	// Kpt defines the kpt resources in the application.
 	Kpt []string `yaml:"kpt,omitempty" skaffold:"filepath"`
+
+	// LifecycleHooks describes a set of lifecycle hooks that are executed before and after every render.
+	LifecycleHooks RenderHooks `yaml:"hooks,omitempty"`
 }
 
 // Kustomize defines the paths to be modified with kustomize, along with
@@ -1522,6 +1525,20 @@ type SyncHooks struct {
 	PostHooks []SyncHookItem `yaml:"after,omitempty"`
 }
 
+// RenderHookItem describes a single lifecycle hook to execute before or after each deployer step.
+type RenderHookItem struct {
+	// HostHook describes a single lifecycle hook to run on the host machine.
+	HostHook *HostHook `yaml:"host,omitempty" yamltags:"oneOf=render_hook"`
+}
+
+// RenderHooks describes the list of lifecycle hooks to execute before and after each render step.
+type RenderHooks struct {
+	// PreHooks describes the list of lifecycle hooks to execute *before* each render step. Container hooks will only run if the container exists from a previous deployment step (for instance the successive iterations of a dev-loop during `skaffold dev`).
+	PreHooks []RenderHookItem `yaml:"before,omitempty"`
+	// PostHooks describes the list of lifecycle hooks to execute *after* each render step.
+	PostHooks []RenderHookItem `yaml:"after,omitempty"`
+}
+
 // DeployHookItem describes a single lifecycle hook to execute before or after each deployer step.
 type DeployHookItem struct {
 	// HostHook describes a single lifecycle hook to run on the host machine.
diff --git a/pkg/skaffold/schema/v2beta29/upgrade_test.go b/pkg/skaffold/schema/v2beta29/upgrade_test.go
index dbc5dbff7..3e3c79a34 100644
--- a/pkg/skaffold/schema/v2beta29/upgrade_test.go
+++ b/pkg/skaffold/schema/v2beta29/upgrade_test.go
@@ -223,3 +223,57 @@ func verifyUpgrade(t *testing.T, input, output string) {
 
 	testutil.CheckErrorAndDeepEqual(t, false, err, expected, upgraded)
 }
+
+func TestUpgradePatches(t *testing.T) {
+	yaml := `apiVersion: skaffold/v2beta29
+kind: Config
+build:
+  artifacts:
+  - image: skaffold-debug-java
+    context: java
+deploy:
+  kubectl:
+    manifests:
+    - java/k8s/web.yaml
+  helm:
+   releases:
+   - name: test
+profiles:
+- name: kustomize
+  patches:
+  - op: remove
+    path: /deploy/kubectl
+  - op: remove
+    path: /deploy/helm
+  deploy:
+    kustomize: {}
+`
+	expected := `apiVersion: skaffold/v3alpha1
+kind: Config
+build:
+  artifacts:
+  - image: skaffold-debug-java
+    context: java
+manifests:
+  rawYaml:
+    - java/k8s/web.yaml
+  helm:
+   releases:
+   - name: test
+deploy:
+    helm: {}
+    kubectl: {}
+profiles:
+- name: kustomize
+  patches:
+  - op: remove
+    path: /manifests/rawYaml
+  - op: remove
+    path: /manifests/helm
+  manifests:
+    kustomize:
+      paths:
+      - "."
+`
+	verifyUpgrade(t, yaml, expected)
+}