Repository: GoogleContainerTools/skaffold
Pull request #7134 (2022-02-21 22:10:46)

feat: implement cross-platform support for GCB build env
<!-- Thank you for your contribution! -->

<!-- Include if applicable: -->
Related: #6120 <!-- tracking issues that this PR will close -->

**Description**
<!-- Describe your changes here. The more detail, the easier the review! -->
This PR implements support for running cross-platform builds for GoogleCloudBuild build environment.

**Testing instructions**
1. In this branch, go to directory `integration/examples/cross-platform-builds`
2. Run:
    ```
    skaffold build --default-repo=gcr.io/k8s-skaffold --platform=linux/arm64 -p googleCloudBuild --cache-artifacts=false
    ```
    This will build the image for platform `linux/arm64` on the GoogleCloudBuild build env.
3. Verify the image was built for the right target, run:
     ```
     docker run --platform=linux/arm64 gcr.io/k8s-skaffold/skaffold-example:<tag>
     ```
     Should produce the output:
     ```
     Hello world! Running on linux/arm64
     Hello world! Running on linux/arm64
     ...
     ```

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

[![Impacted file tree graph](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/graphs/tree.svg?width=650&height=150&src=pr&token=mVPJs21wYu&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools)](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools)

```diff
@@            Coverage Diff             @@
##             main    #7134      +/-   ##
==========================================
- Coverage   70.48%   68.32%   -2.17%     
==========================================
  Files         515      560      +45     
  Lines       23150    26239    +3089     
==========================================
+ Hits        16317    17927    +1610     
- Misses       5776     7072    +1296     
- Partials     1057     1240     +183     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134?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/deploy.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGVwbG95Lmdv) | `52.00% <ø> (-1.85%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/dev.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGV2Lmdv) | `84.61% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/render.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcmVuZGVyLmdv) | `36.66% <0.00%> (-4.72%)` | :arrow_down: |
| [cmd/skaffold/skaffold.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/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/7134/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/lsp.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/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/fix.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZml4Lmdv) | `68.85% <40.00%> (-7.62%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/lint.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvbGludC5nbw==) | `42.85% <42.85%> (ø)` | |
| [cmd/skaffold/app/cmd/find\_configs.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZmluZF9jb25maWdzLmdv) | `48.88% <50.00%> (+0.24%)` | :arrow_up: |
| [cmd/skaffold/app/skaffold.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9za2FmZm9sZC5nbw==) | `76.19% <70.00%> (-8.43%)` | :arrow_down: |
| ... and [212 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools). Last update [9de79cd...37fc75b](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7134?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools).


Patch:
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 3a561b138..140cbd517 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -9,7 +9,7 @@ This doc explains the development workflow so you can get started
 You must install these tools:
 
 1. [`go`](https://golang.org/doc/install): The language skaffold is
-   built in (version >= go 1.14)
+   built in (version >= go 1.17)
 1. [`git`](https://help.github.com/articles/set-up-git/): For source control
 1. [`make`](https://www.gnu.org/software/make/): For building skaffold.
 
@@ -18,8 +18,7 @@ You must install these tools:
 First you will need to setup your GitHub account and create a fork:
 
 1. Create [a GitHub account](https://github.com/join)
-1. Setup [GitHub access via
-   SSH](https://help.github.com/articles/connecting-to-github-with-ssh/)
+1. Setup [GitHub access via SSH](https://help.github.com/articles/connecting-to-github-with-ssh/)
 1. [Create and checkout a repo fork](#checkout-your-fork)
 
 Once you have those, you can iterate on skaffold:
@@ -31,8 +30,6 @@ Once you have those, you can iterate on skaffold:
 
 When you're ready, you can [create a PR](#creating-a-pr)!
 
-You may also be interested in [contributing to the docs](#contributing-to-skaffold-docs).
-
 ## Checkout your fork
 
 To make local changes to skaffold and eventually submit a pull request to the repo,
@@ -47,8 +44,27 @@ Once you've done this, clone your fork to your local machine:
    git remote set-url --push upstream no_push
    ```
 
-   _Adding the `upstream` remote sets you up nicely for regularly [syncing your
-   fork](https://help.github.com/articles/syncing-a-fork/)._
+   _Adding the `upstream` remote sets you up nicely for regularly [syncing your fork](https://help.github.com/articles/syncing-a-fork/)._
+
+## Creating a PR
+
+When you have changes you would like to propose to skaffold, you will need to:
+
+1. Ensure the commit message(s) follow the [conventional commits guidelines](https://www.conventionalcommits.org/en/v1.0.0/). The Skaffold repo has a bot to check that commit messages follow this style.
+2. Ensure the PR description describes what issue you are fixing and how you are fixing it
+   (include references to [issue numbers](https://help.github.com/articles/closing-issues-using-keywords/)
+   if appropriate)
+3. Add unit tests. Unit test coverage should increase or stay the same with every PR.
+4. Add integration test if applicable
+5. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
+
+Please follow our [small Pull Requests guidelines](./docs/community/small-prs.md) for quicker response time.
+
+### Reviews
+
+Each PR must be reviewed by a maintainer. This maintainer will add the `kokoro:run` label
+to a PR to kick off [the integration tests](#integration-tests), which must pass for the PR
+to be submitted.
 
 ## Making a config change
 
@@ -86,7 +102,7 @@ For more details behind the logic of config changes see [the Skaffold config man
 
 ## Making changes to the Skaffold API
 
-We build the API directly through gRPC, which gets translated into REST API through a reverse proxy gateway library. When adding new message types, make changes to [proto/v1/skaffold.proto](https://github.com/GoogleContainerTools/skaffold/blob/main/proto/v1/skaffold.proto), and when adding new enum types, make changes to [proto/enums/enums.proto](https://github.com/GoogleContainerTools/skaffold/blob/main/proto/enums/enums.proto). When changing either of these files, you can run `./hack/generate-proto.sh` to generate the equivalent Go code.
+We build the API directly through gRPC, which gets translated into REST API through a reverse proxy gateway library. When adding new message types, make changes to [proto/v2/skaffold.proto](https://github.com/GoogleContainerTools/skaffold/blob/main/proto/v2/skaffold.proto), and when adding new enum types, make changes to [proto/enums/enums.proto](https://github.com/GoogleContainerTools/skaffold/blob/main/proto/enums/enums.proto). When changing either of these files, you can run `./hack/generate-proto.sh` to generate the equivalent Go code.
 
 ## Adding actionable error messages to code.
 Skaffold has a built-in framework to provide actionable error messages for user to help bootstrap skaffold errors.
@@ -136,7 +152,7 @@ e.g In this example [PR](https://github.com/GoogleContainerTools/skaffold/pull/5
    ```
 
 With above two changes, skaffold will now show a meaning full error message when this error condition is met. 
-```shell script
+```shell
 skaffold dev 
 unsupported validator "foo" please only use the following validators in skaffold-managed mode: [kubeval].
 To use custom validators, please use kpt-managed mode.
@@ -186,13 +202,13 @@ skaffold has both [unit tests](#unit-tests) and [integration tests](#integration
 The unit tests live with the code they test and can be run with:
 
 ```shell
-make test
+make quicktest
 ```
 
-You can also run a smaller set of unit tests that run quicker with:
+You can also run a larger suite of tests/checks (unit tests, docs check, linters check) using:
 
 ```shell
-make quicktest
+make test
 ```
 
 In case you see a linter error such as:
@@ -265,49 +281,30 @@ which at release time will be published with the latest release to https://skaff
 
 Mark your PR with `docs-modifications` label. Our PR review process will answer in comments in ~5 minutes with the URL of your preview and will remove the label.
 
-## Testing the Skaffold binary release process
+## Testing the Skaffold release image building process
 
-Skaffold release process works with Google Cloud Build within our own project `k8s-skaffold` and the skaffold release bucket, `gs://skaffold`.
+Skaffold's release image build process works with Google Cloud Build within our own project `k8s-skaffold`.
 
-In order to be able to iterate/fix the release process you can pass in your own project and bucket as parameters to the build.
+In order to be able to iterate/fix the release process you can pass in your own project as a parameter to the build.
 
-We continuously release **builds** under `gs://skaffold/builds`. This is done by triggering `cloudbuild.yaml` on every push to `main`.
+We continuously release edge images under `gcr.io/k8s-skaffold/skaffold:edge`. This is done by triggering `cloudbuild.yaml` on every push to `main`.
 
 To run a build on your own project:
 
 ```
-gcloud builds submit --config deploy/cloudbuild.yaml --substitutions=_RELEASE_BUCKET=<personal-bucket>,COMMIT_SHA=$(git rev-parse HEAD) --project <personalproject>
+gcloud builds submit --config deploy/cloudbuild.yaml --substitutions=COMMIT_SHA=$(git rev-parse HEAD) --project <personal_project>
 ```
 
-We **release** stable versions under `gs://skaffold/releases`. This is done by triggering `cloudbuild-release.yaml` on every new tag in our Github repo.
+We build the latest stable release image under `gcr.io/k8s-skaffold/skaffold:latest`. This is done by triggering `cloudbuild-release.yaml` on every new tag in our Github repo.
 
 To test a release on your own project:
 
 ```
-gcloud builds submit --config deploy/cloudbuild-release.yaml --substitutions=_RELEASE_BUCKET=<personal-bucket>,TAG_NAME=testrelease_v1234 --project <personalproject>
+gcloud builds submit --config deploy/cloudbuild-release.yaml --substitutions=TAG_NAME=<release_tag> --project <personal_project>
 ```
 
-To just run a release without Google Cloud Build only using your local Docker daemon, you can run:
+To just run a release image build without Google Cloud Build only using your local Docker daemon, you can run:
 
 ```
-make -j release GCP_PROJECT=<personalproject> RELEASE_BUCKET=<personal-bucket>
+make -j release GCP_PROJECT=<personalproject>
 ```
-
-## Creating a PR
-
-When you have changes you would like to propose to skaffold, you will need to:
-
-1. Ensure the commit message(s) follow the [conventional commits guidelines](https://www.conventionalcommits.org/en/v1.0.0/). The Skaffold repo has a bot to check that commit messages follow this style.
-1. Ensure the PR description describes what issue you are fixing and how you are fixing it
-   (include references to [issue numbers](https://help.github.com/articles/closing-issues-using-keywords/)
-   if appropriate)
-1. Add unit tests. Unit test coverage should increase or stay the same with every PR.
-1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
-
-Please follow our [small Pull Requests guidelines](./docs/community/small-prs.md) for quicker response time.
-
-### Reviews
-
-Each PR must be reviewed by a maintainer. This maintainer will add the `kokoro:run` label
-to a PR to kick off [the integration tests](#integration-tests), which must pass for the PR
-to be submitted.
diff --git a/deploy/docs/local-preview.sh b/deploy/docs/local-preview.sh
index fbb264ca3..1ccaff59d 100755
--- a/deploy/docs/local-preview.sh
+++ b/deploy/docs/local-preview.sh
@@ -26,5 +26,5 @@ for dir in $(find ${DOCS_DIR} -mindepth 1 -maxdepth 1 -type d | grep -v themes |
     MOUNTS="${MOUNTS} -v $dir:/app/docs/$(basename $dir):ro"
 done
 
-docker build -t skaffold-docs-previewer --target runtime_deps deploy/webhook
-docker run --rm -ti -p 1313:1313 ${MOUNTS} skaffold-docs-previewer $@
+docker build --platform linux/amd64 -t skaffold-docs-previewer --target runtime_deps deploy/webhook
+docker run --platform linux/amd64 --rm -ti -p 1313:1313 ${MOUNTS} skaffold-docs-previewer $@
diff --git a/integration/examples/cross-platform-builds/Dockerfile b/integration/examples/cross-platform-builds/Dockerfile
index 415a8456f..0066fb077 100644
--- a/integration/examples/cross-platform-builds/Dockerfile
+++ b/integration/examples/cross-platform-builds/Dockerfile
@@ -1,8 +1,10 @@
-FROM golang:1.15 as builder
+FROM --platform=$BUILDPLATFORM golang:1.15 as builder
 COPY main.go .
 # `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
+ARG TARGETOS
+ARG TARGETARCH
 ARG SKAFFOLD_GO_GCFLAGS
-RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
+RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go
 
 FROM alpine:3
 # Define GOTRACEBACK to mark this container as using the Go language runtime
diff --git a/integration/examples/cross-platform-builds/skaffold.yaml b/integration/examples/cross-platform-builds/skaffold.yaml
index 4f6a84c4b..023ed2e58 100644
--- a/integration/examples/cross-platform-builds/skaffold.yaml
+++ b/integration/examples/cross-platform-builds/skaffold.yaml
@@ -8,3 +8,8 @@ deploy:
   kubectl:
     manifests:
       - k8s-*
+profiles:
+  - name: googleCloudBuild
+    build:
+      googleCloudBuild:
+        projectId: k8s-skaffold
diff --git a/pkg/skaffold/build/gcb/cloud_build.go b/pkg/skaffold/build/gcb/cloud_build.go
index 75851848b..383dbd920 100644
--- a/pkg/skaffold/build/gcb/cloud_build.go
+++ b/pkg/skaffold/build/gcb/cloud_build.go
@@ -152,7 +152,7 @@ func (b *Builder) buildArtifactWithCloudBuild(ctx context.Context, out io.Writer
 		})
 	}
 
-	buildSpec, err := b.buildSpec(ctx, artifact, tag, cbBucket, buildObject)
+	buildSpec, err := b.buildSpec(ctx, artifact, tag, platform, cbBucket, buildObject)
 	if err != nil {
 		return "", sErrors.NewErrorWithStatusCode(&proto.ActionableErr{
 			ErrCode: proto.StatusCode_BUILD_GCB_GENERATE_BUILD_DESCRIPTOR_ERR,
diff --git a/pkg/skaffold/build/gcb/docker.go b/pkg/skaffold/build/gcb/docker.go
index 4fc90306d..6ec54b79a 100644
--- a/pkg/skaffold/build/gcb/docker.go
+++ b/pkg/skaffold/build/gcb/docker.go
@@ -24,24 +24,29 @@ import (
 	cloudbuild "google.golang.org/api/cloudbuild/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringslice"
 )
 
 // dockerBuildSpec lists the build steps required to build a docker image.
-func (b *Builder) dockerBuildSpec(a *latestV1.Artifact, tag string) (cloudbuild.Build, error) {
+func (b *Builder) dockerBuildSpec(a *latestV1.Artifact, tag string, platforms platform.Matcher) (cloudbuild.Build, error) {
 	a = adjustCacheFrom(a, tag)
 
-	args, err := b.dockerBuildArgs(a, tag, a.Dependencies)
+	args, err := b.dockerBuildArgs(a, tag, a.Dependencies, platforms)
 	if err != nil {
 		return cloudbuild.Build{}, err
 	}
-
-	steps := b.cacheFromSteps(a.DockerArtifact)
-	steps = append(steps, &cloudbuild.BuildStep{
+	steps := b.cacheFromSteps(a.DockerArtifact, platforms)
+	buildStep := &cloudbuild.BuildStep{
 		Name: b.DockerImage,
 		Args: args,
-	})
+	}
+	if platforms.IsNotEmpty() {
+		// cross-platform build requires buildkit enabled
+		buildStep.Env = append(buildStep.Env, "DOCKER_BUILDKIT=1")
+	}
+	steps = append(steps, buildStep)
 
 	return cloudbuild.Build{
 		Steps:  steps,
@@ -50,14 +55,17 @@ func (b *Builder) dockerBuildSpec(a *latestV1.Artifact, tag string) (cloudbuild.
 }
 
 // cacheFromSteps pulls images used by `--cache-from`.
-func (b *Builder) cacheFromSteps(artifact *latestV1.DockerArtifact) []*cloudbuild.BuildStep {
+func (b *Builder) cacheFromSteps(artifact *latestV1.DockerArtifact, platforms platform.Matcher) []*cloudbuild.BuildStep {
 	var steps []*cloudbuild.BuildStep
-
+	argFmt := "docker pull %s || true"
+	if platforms.IsNotEmpty() {
+		argFmt = "docker pull --platform " + platforms.String() + " %s || true"
+	}
 	for _, cacheFrom := range artifact.CacheFrom {
 		steps = append(steps, &cloudbuild.BuildStep{
 			Name:       b.DockerImage,
 			Entrypoint: "sh",
-			Args:       []string{"-c", fmt.Sprintf("docker pull %s || true", cacheFrom)},
+			Args:       []string{"-c", fmt.Sprintf(argFmt, cacheFrom)},
 		})
 	}
 
@@ -65,7 +73,7 @@ func (b *Builder) cacheFromSteps(artifact *latestV1.DockerArtifact) []*cloudbuil
 }
 
 // dockerBuildArgs lists the arguments passed to `docker` to build a given image.
-func (b *Builder) dockerBuildArgs(a *latestV1.Artifact, tag string, deps []*latestV1.ArtifactDependency) ([]string, error) {
+func (b *Builder) dockerBuildArgs(a *latestV1.Artifact, tag string, deps []*latestV1.ArtifactDependency, platforms platform.Matcher) ([]string, error) {
 	d := a.DockerArtifact
 	// TODO(nkubala): remove when buildkit is supported in GCB (#4773)
 	if len(d.Secrets) > 0 || d.SSH != "" {
@@ -83,6 +91,9 @@ func (b *Builder) dockerBuildArgs(a *latestV1.Artifact, tag string, deps []*late
 	}
 
 	args := []string{"build", "--tag", tag, "-f", d.DockerfilePath}
+	if platforms.IsNotEmpty() {
+		args = append(args, "--platform", platforms.String())
+	}
 	args = append(args, ba...)
 	args = append(args, ".")
 
diff --git a/pkg/skaffold/build/gcb/spec.go b/pkg/skaffold/build/gcb/spec.go
index 0c0246579..aff80a03e 100644
--- a/pkg/skaffold/build/gcb/spec.go
+++ b/pkg/skaffold/build/gcb/spec.go
@@ -20,6 +20,7 @@ import (
 	"context"
 	"fmt"
 
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 	"google.golang.org/api/cloudbuild/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/misc"
@@ -27,9 +28,9 @@ import (
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 )
 
-func (b *Builder) buildSpec(ctx context.Context, artifact *latestV1.Artifact, tag, bucket, object string) (cloudbuild.Build, error) {
+func (b *Builder) buildSpec(ctx context.Context, artifact *latestV1.Artifact, tag string, platforms platform.Matcher, bucket, object string) (cloudbuild.Build, error) {
 	// Artifact specific build spec
-	buildSpec, err := b.buildSpecForArtifact(ctx, artifact, tag)
+	buildSpec, err := b.buildSpecForArtifact(ctx, artifact, tag, platforms)
 	if err != nil {
 		return buildSpec, err
 	}
@@ -57,18 +58,22 @@ func (b *Builder) buildSpec(ctx context.Context, artifact *latestV1.Artifact, ta
 	return buildSpec, nil
 }
 
-func (b *Builder) buildSpecForArtifact(ctx context.Context, a *latestV1.Artifact, tag string) (cloudbuild.Build, error) {
+func (b *Builder) buildSpecForArtifact(ctx context.Context, a *latestV1.Artifact, tag string, platforms platform.Matcher) (cloudbuild.Build, error) {
 	switch {
 	case a.KanikoArtifact != nil:
 		return b.kanikoBuildSpec(a, tag)
 
 	case a.DockerArtifact != nil:
-		return b.dockerBuildSpec(a, tag)
+		return b.dockerBuildSpec(a, tag, platforms)
 
 	case a.JibArtifact != nil:
-		return b.jibBuildSpec(ctx, a, tag, platform.Matcher{}) // TODO: pass correct platform matcher for GCB builds
+		return b.jibBuildSpec(ctx, a, tag, platforms)
 
 	case a.BuildpackArtifact != nil:
+		// TODO: Buildpacks only supports building for platform linux/amd64. See https://github.com/GoogleCloudPlatform/buildpacks/issues/112
+		if platforms.IsNotEmpty() && platforms.Intersect(platform.Matcher{Platforms: []v1.Platform{{OS: "linux", Architecture: "amd64"}}}).IsEmpty() {
+			return cloudbuild.Build{}, fmt.Errorf("buildpacks builder doesn't support building for platforms %s. Cannot build gcb artifact:\n%s", platforms.String(), misc.FormatArtifact(a))
+		}
 		return b.buildpackBuildSpec(a.BuildpackArtifact, tag, a.Dependencies)
 
 	default:
diff --git a/pkg/skaffold/build/gcb/types.go b/pkg/skaffold/build/gcb/types.go
index a915604ad..7e03bbea4 100644
--- a/pkg/skaffold/build/gcb/types.go
+++ b/pkg/skaffold/build/gcb/types.go
@@ -21,7 +21,6 @@ import (
 	"io"
 	"time"
 
-	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 	"k8s.io/apimachinery/pkg/util/wait"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
@@ -124,5 +123,5 @@ func (b *Builder) PushImages() bool {
 }
 
 func (b *Builder) SupportedPlatforms() platform.Matcher {
-	return platform.Matcher{Platforms: []v1.Platform{{OS: "linux", Architecture: "amd64"}}}
+	return platform.All
 }
diff --git a/pkg/skaffold/platform/resolver.go b/pkg/skaffold/platform/resolver.go
index fdcc05c01..9c6e9a054 100644
--- a/pkg/skaffold/platform/resolver.go
+++ b/pkg/skaffold/platform/resolver.go
@@ -63,7 +63,7 @@ func NewResolver(ctx context.Context, pipelines []latestV1.Pipeline, cliPlatform
 		fromClusterNodes, err = getClusterPlatforms(ctx, kubeContext, runMode == config.RunModes.Dev || runMode == config.RunModes.Debug)
 		if err != nil {
 			log.Entry(ctx).Debugf("failed to get cluster node details: %v", err)
-			log.Entry(ctx).Warnln("failed to detect active kubernetes cluter node platform. Specify the correct build platform in the `skaffold.yaml` file or using the `--platform` flag")
+			log.Entry(ctx).Warnln("failed to detect active kubernetes cluster node platform. Specify the correct build platform in the `skaffold.yaml` file or using the `--platform` flag")
 		}
 	}
 	log.Entry(ctx).Debugf("platforms detected from active kubernetes cluster nodes: %q", fromClusterNodes)


Test patch:
diff --git a/pkg/skaffold/build/gcb/buildpacks_test.go b/pkg/skaffold/build/gcb/buildpacks_test.go
index af39b66f8..dcaced50e 100644
--- a/pkg/skaffold/build/gcb/buildpacks_test.go
+++ b/pkg/skaffold/build/gcb/buildpacks_test.go
@@ -22,6 +22,7 @@ import (
 
 	"google.golang.org/api/cloudbuild/v1"
 
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
 	"github.com/GoogleContainerTools/skaffold/testutil"
@@ -194,7 +195,7 @@ func TestBuildpackBuildSpec(t *testing.T) {
 			builder := NewBuilder(&mockBuilderContext{artifactStore: store}, &latestV1.GoogleCloudBuild{
 				PackImage: "pack/image",
 			})
-			buildSpec, err := builder.buildSpec(context.Background(), artifact, "img", "bucket", "object")
+			buildSpec, err := builder.buildSpec(context.Background(), artifact, "img", platform.Matcher{}, "bucket", "object")
 			t.CheckError(test.shouldErr, err)
 
 			if !test.shouldErr {
diff --git a/pkg/skaffold/build/gcb/docker_test.go b/pkg/skaffold/build/gcb/docker_test.go
index 3e55e45f3..28fb9df3e 100644
--- a/pkg/skaffold/build/gcb/docker_test.go
+++ b/pkg/skaffold/build/gcb/docker_test.go
@@ -20,10 +20,12 @@ import (
 	"context"
 	"testing"
 
-	cloudbuild "google.golang.org/api/cloudbuild/v1"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
+	"google.golang.org/api/cloudbuild/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
 	"github.com/GoogleContainerTools/skaffold/testutil"
@@ -33,6 +35,7 @@ func TestDockerBuildSpec(t *testing.T) {
 	tests := []struct {
 		description string
 		artifact    *latestV1.Artifact
+		platforms   platform.Matcher
 		expected    cloudbuild.Build
 		shouldErr   bool
 	}{
@@ -130,6 +133,42 @@ func TestDockerBuildSpec(t *testing.T) {
 			},
 			shouldErr: true,
 		},
+
+		{
+			description: "cross-platform build",
+			artifact: &latestV1.Artifact{
+				ArtifactType: latestV1.ArtifactType{
+					DockerArtifact: &latestV1.DockerArtifact{
+						DockerfilePath: "Dockerfile",
+						BuildArgs: map[string]*string{
+							"arg1": util.StringPtr("value1"),
+							"arg2": nil,
+						},
+					},
+				},
+			},
+			platforms: platform.Matcher{Platforms: []v1.Platform{{Architecture: "arm", OS: "freebsd"}}},
+			expected: cloudbuild.Build{
+				LogsBucket: "bucket",
+				Source: &cloudbuild.Source{
+					StorageSource: &cloudbuild.StorageSource{
+						Bucket: "bucket",
+						Object: "object",
+					},
+				},
+				Steps: []*cloudbuild.BuildStep{{
+					Name: "docker/docker",
+					Args: []string{"build", "--tag", "nginx", "-f", "Dockerfile", "--platform", "freebsd/arm", "--build-arg", "arg1=value1", "--build-arg", "arg2", "."},
+					Env:  []string{"DOCKER_BUILDKIT=1"},
+				}},
+				Images: []string{"nginx"},
+				Options: &cloudbuild.BuildOptions{
+					DiskSizeGb:  100,
+					MachineType: "n1-standard-1",
+				},
+				Timeout: "10m",
+			},
+		},
 	}
 
 	for _, test := range tests {
@@ -157,7 +196,7 @@ func TestDockerBuildSpec(t *testing.T) {
 				Timeout:     "10m",
 			})
 
-			desc, err := builder.buildSpec(context.Background(), test.artifact, "nginx", "bucket", "object")
+			desc, err := builder.buildSpec(context.Background(), test.artifact, "nginx", test.platforms, "bucket", "object")
 			t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, desc)
 		})
 	}
@@ -168,6 +207,7 @@ func TestPullCacheFrom(t *testing.T) {
 		description string
 		artifact    *latestV1.Artifact
 		tag         string
+		platforms   platform.Matcher
 		expected    []*cloudbuild.BuildStep
 		shouldErr   bool
 	}{
@@ -216,6 +256,32 @@ func TestPullCacheFrom(t *testing.T) {
 				Args: []string{"build", "--tag", "gcr.io/k8s-skaffold/test:tagged", "-f", "Dockerfile", "--cache-from", "gcr.io/k8s-skaffold/test:tagged", "."},
 			}},
 		},
+		{
+			description: "cross-platform cache-from images",
+			artifact: &latestV1.Artifact{
+				ArtifactType: latestV1.ArtifactType{
+					DockerArtifact: &latestV1.DockerArtifact{
+						DockerfilePath: "Dockerfile",
+						CacheFrom:      []string{"from/image1", "from/image2"},
+					},
+				},
+			},
+			tag:       "nginx2",
+			platforms: platform.Matcher{Platforms: []v1.Platform{{Architecture: "arm", OS: "freebsd"}}},
+			expected: []*cloudbuild.BuildStep{{
+				Name:       "docker/docker",
+				Entrypoint: "sh",
+				Args:       []string{"-c", "docker pull --platform freebsd/arm from/image1 || true"},
+			}, {
+				Name:       "docker/docker",
+				Entrypoint: "sh",
+				Args:       []string{"-c", "docker pull --platform freebsd/arm from/image2 || true"},
+			}, {
+				Name: "docker/docker",
+				Args: []string{"build", "--tag", "nginx2", "-f", "Dockerfile", "--platform", "freebsd/arm", "--cache-from", "from/image1", "--cache-from", "from/image2", "."},
+				Env:  []string{"DOCKER_BUILDKIT=1"},
+			}},
+		},
 	}
 
 	for _, test := range tests {
@@ -226,7 +292,7 @@ func TestPullCacheFrom(t *testing.T) {
 			builder := NewBuilder(&mockBuilderContext{}, &latestV1.GoogleCloudBuild{
 				DockerImage: "docker/docker",
 			})
-			desc, err := builder.dockerBuildSpec(test.artifact, test.tag)
+			desc, err := builder.dockerBuildSpec(test.artifact, test.tag, test.platforms)
 
 			t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expected, desc.Steps)
 		})
diff --git a/pkg/skaffold/build/gcb/jib_test.go b/pkg/skaffold/build/gcb/jib_test.go
index b4341bfc2..6ea0f6d44 100644
--- a/pkg/skaffold/build/gcb/jib_test.go
+++ b/pkg/skaffold/build/gcb/jib_test.go
@@ -22,9 +22,11 @@ import (
 	"path/filepath"
 	"testing"
 
-	cloudbuild "google.golang.org/api/cloudbuild/v1"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
+	"google.golang.org/api/cloudbuild/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/testutil"
 )
@@ -40,6 +42,7 @@ func TestJibMavenBuildSpec(t *testing.T) {
 		description  string
 		skipTests    bool
 		baseImage    string
+		platforms    platform.Matcher
 		expectedArgs []string
 	}{
 		{
@@ -64,6 +67,16 @@ func TestJibMavenBuildSpec(t *testing.T) {
 			skipTests:    false,
 			expectedArgs: []string{"-c", "mvn -Duser.home=$$HOME --batch-mode jib:_skaffold-fail-if-jib-out-of-date -Djib.requiredVersion=" + jib.MinimumJibMavenVersion + " --non-recursive prepare-package jib:build -Djib.from.image=img2:tag -Dimage=img"},
 		},
+		{
+			description:  "cross platform",
+			platforms:    platform.Matcher{Platforms: []v1.Platform{{Architecture: "arm", OS: "freebsd"}}},
+			expectedArgs: []string{"-c", "mvn -Duser.home=$$HOME --batch-mode jib:_skaffold-fail-if-jib-out-of-date -Djib.requiredVersion=" + jib.MinimumJibMavenVersionForCrossPlatform + " --non-recursive prepare-package jib:build -Djib.from.platforms=freebsd/arm -Dimage=img"},
+		},
+		{
+			description:  "multi platform",
+			platforms:    platform.Matcher{Platforms: []v1.Platform{{Architecture: "arm", OS: "freebsd"}, {Architecture: "arm64", OS: "linux"}}},
+			expectedArgs: []string{"-c", "mvn -Duser.home=$$HOME --batch-mode jib:_skaffold-fail-if-jib-out-of-date -Djib.requiredVersion=" + jib.MinimumJibMavenVersionForCrossPlatform + " --non-recursive prepare-package jib:build -Djib.from.platforms=freebsd/arm,linux/arm64 -Dimage=img"},
+		},
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
@@ -86,7 +99,7 @@ func TestJibMavenBuildSpec(t *testing.T) {
 			})
 			builder.skipTests = test.skipTests
 
-			buildSpec, err := builder.buildSpec(context.Background(), artifact, "img", "bucket", "object")
+			buildSpec, err := builder.buildSpec(context.Background(), artifact, "img", test.platforms, "bucket", "object")
 			t.CheckNoError(err)
 
 			expected := []*cloudbuild.BuildStep{{
@@ -105,6 +118,7 @@ func TestJibGradleBuildSpec(t *testing.T) {
 	tests := []struct {
 		description  string
 		skipTests    bool
+		platforms    platform.Matcher
 		expectedArgs []string
 	}{
 		{
@@ -117,6 +131,16 @@ func TestJibGradleBuildSpec(t *testing.T) {
 			skipTests:    false,
 			expectedArgs: []string{"-c", "gradle -Duser.home=$$HOME --console=plain _skaffoldFailIfJibOutOfDate -Djib.requiredVersion=" + jib.MinimumJibGradleVersion + " :jib --image=img"},
 		},
+		{
+			description:  "cross platform",
+			platforms:    platform.Matcher{Platforms: []v1.Platform{{Architecture: "arm", OS: "freebsd"}}},
+			expectedArgs: []string{"-c", "gradle -Duser.home=$$HOME --console=plain _skaffoldFailIfJibOutOfDate -Djib.requiredVersion=" + jib.MinimumJibGradleVersionForCrossPlatform + " :jib -Djib.from.platforms=freebsd/arm --image=img"},
+		},
+		{
+			description:  "multi platform",
+			platforms:    platform.Matcher{Platforms: []v1.Platform{{Architecture: "arm", OS: "freebsd"}, {Architecture: "arm64", OS: "linux"}}},
+			expectedArgs: []string{"-c", "gradle -Duser.home=$$HOME --console=plain _skaffoldFailIfJibOutOfDate -Djib.requiredVersion=" + jib.MinimumJibGradleVersionForCrossPlatform + " :jib -Djib.from.platforms=freebsd/arm,linux/arm64 --image=img"},
+		},
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
@@ -131,7 +155,7 @@ func TestJibGradleBuildSpec(t *testing.T) {
 			})
 			builder.skipTests = test.skipTests
 
-			buildSpec, err := builder.buildSpec(context.Background(), artifact, "img", "bucket", "object")
+			buildSpec, err := builder.buildSpec(context.Background(), artifact, "img", test.platforms, "bucket", "object")
 			t.CheckNoError(err)
 
 			expected := []*cloudbuild.BuildStep{{
diff --git a/pkg/skaffold/build/gcb/kaniko_test.go b/pkg/skaffold/build/gcb/kaniko_test.go
index c2ffe4301..87b556f5e 100644
--- a/pkg/skaffold/build/gcb/kaniko_test.go
+++ b/pkg/skaffold/build/gcb/kaniko_test.go
@@ -27,6 +27,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
 	"github.com/GoogleContainerTools/skaffold/testutil"
@@ -438,7 +439,7 @@ func TestKanikoBuildSpec(t *testing.T) {
 				}
 				return m, nil
 			})
-			desc, err := builder.buildSpec(context.Background(), artifact, "gcr.io/nginx", "bucket", "object")
+			desc, err := builder.buildSpec(context.Background(), artifact, "gcr.io/nginx", platform.Matcher{}, "bucket", "object")
 
 			expected := cloudbuild.Build{
 				LogsBucket: "bucket",
diff --git a/pkg/skaffold/build/gcb/spec_test.go b/pkg/skaffold/build/gcb/spec_test.go
index cc4ba2dd8..76a8e0ad0 100644
--- a/pkg/skaffold/build/gcb/spec_test.go
+++ b/pkg/skaffold/build/gcb/spec_test.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/testutil"
@@ -49,7 +50,7 @@ func TestBuildSpecFail(t *testing.T) {
 		testutil.Run(t, test.description, func(t *testutil.T) {
 			builder := NewBuilder(&mockBuilderContext{}, &latestV1.GoogleCloudBuild{})
 
-			_, err := builder.buildSpec(context.Background(), test.artifact, "tag", "bucket", "object")
+			_, err := builder.buildSpec(context.Background(), test.artifact, "tag", platform.Matcher{}, "bucket", "object")
 
 			t.CheckError(true, err)
 		})
diff --git a/pkg/skaffold/platform/platform_test.go b/pkg/skaffold/platform/platform_test.go
index ffa9aa14e..2be5e51e0 100644
--- a/pkg/skaffold/platform/platform_test.go
+++ b/pkg/skaffold/platform/platform_test.go
@@ -78,7 +78,7 @@ func TestIsMultiOrCrossPlatform(t *testing.T) {
 		},
 		{
 			description:     "single platform target",
-			m:               Matcher{Platforms: []v1.Platform{{Architecture: "arm64"}}},
+			m:               Matcher{Platforms: []v1.Platform{{Architecture: "arm", OS: "freebsd"}}},
 			isMultiPlatform: false,
 			isCrossPlatform: true,
 		},