Repository: GoogleContainerTools/skaffold
Pull request #7135 (2022-02-22 01:06:54)

fix: specifying platforms in ko builder
<!-- 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! -->
The `ko` builder already supports building multi-platform and cross-platform images. This PR removes the property `artifact.ko.platforms` from the config schema and uses `artifact.platforms` which makes it conform with all the other builders.


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

[![Impacted file tree graph](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7135/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/7135?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools)

```diff
@@            Coverage Diff             @@
##             main    #7135      +/-   ##
==========================================
- Coverage   70.48%   68.34%   -2.15%     
==========================================
  Files         515      560      +45     
  Lines       23150    26237    +3087     
==========================================
+ Hits        16317    17931    +1614     
- Misses       5776     7065    +1289     
- Partials     1057     1241     +184     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7135?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/7135/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/7135/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/7135/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/7135/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/7135/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/7135/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/7135/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/7135/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/7135/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/7135/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/7135/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/7135?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/7135?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools). Last update [9de79cd...3f645a2](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7135?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).

halvards: Thanks for doing this @gsquared94.

There's one regression: The `ko` builder supports the value `all` in the platforms field: https://skaffold.dev/docs/pipeline-stages/builders/ko/#multi-platform-images

Using `all` results in `ko` building for all platforms supported by the base image (`ko` docs here: https://github.com/google/ko#multi-platform-images). It's a useful feature, and it'd be great to retain it. I appreciate that this might mean having to support it for all the builders, or at least handle `all` in a sensible way.
gsquared94: > Thanks for doing this @gsquared94.
> 
> There's one regression: The `ko` builder supports the value `all` in the platforms field: https://skaffold.dev/docs/pipeline-stages/builders/ko/#multi-platform-images
> 
> Using `all` results in `ko` building for all platforms supported by the base image (`ko` docs here: https://github.com/google/ko#multi-platform-images). It's a useful feature, and it'd be great to retain it. I appreciate that this might mean having to support it for all the builders, or at least handle `all` in a sensible way.

Thanks for the quick review @halvards . I've fixed the missing parsing logic for `all` platform value. 
halvards: LGTM. We'll have to update the ko builder documentation page before the next release, since it references the old config field.
gsquared94: > LGTM. We'll have to update the ko builder documentation page before the next release, since it references the old config field.

updated the docs.

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/docs/content/en/docs/pipeline-stages/builders/ko.md b/docs/content/en/docs/pipeline-stages/builders/ko.md
index 87f79e100..e9ded288e 100644
--- a/docs/content/en/docs/pipeline-stages/builders/ko.md
+++ b/docs/content/en/docs/pipeline-stages/builders/ko.md
@@ -76,14 +76,8 @@ a shell, you can use this configuration:
 
 The ko builder supports building multi-platform images. The default platform
 is `linux/amd64`, but you can configure a list of platforms using the
-`platforms` configuration field, e.g.:
-
-```yaml
-    ko:
-      platforms:
-      - linux/amd64
-      - linux/arm64
-```
+artifact's `platforms` configuration field, e.g.:
+{{% readfile file="samples/builders/ko-platforms.yaml" %}}
 
 You can also supply `["all"]` as the value of `platforms`. `all` means that the
 ko builder builds images for all platforms supported by the base image.
diff --git a/docs/content/en/samples/builders/ko-platforms.yaml b/docs/content/en/samples/builders/ko-platforms.yaml
new file mode 100644
index 000000000..d2e365a58
--- /dev/null
+++ b/docs/content/en/samples/builders/ko-platforms.yaml
@@ -0,0 +1,7 @@
+build:
+  artifacts:
+  - image: foo
+    ko: {}
+    platforms:
+    - linux/amd64
+    - linux/arm64
\ No newline at end of file
diff --git a/docs/content/en/schemas/v2beta28.json b/docs/content/en/schemas/v2beta28.json
index 142ee1736..9c954cc65 100755
--- a/docs/content/en/schemas/v2beta28.json
+++ b/docs/content/en/schemas/v2beta28.json
@@ -2714,15 +2714,6 @@
           "type": "string",
           "description": "location of the main package. It is the pattern passed to `go build`. If main is specified as a relative path, it is relative to the `context` directory. If main is empty, the ko builder uses a default value of `.`. If main is a pattern with wildcards, such as `./...`, the expansion must contain only one main package, otherwise ko fails. Main is ignored if the `ImageName` starts with `ko://`. Example: `./cmd/foo`.",
           "x-intellij-html-description": "location of the main package. It is the pattern passed to <code>go build</code>. If main is specified as a relative path, it is relative to the <code>context</code> directory. If main is empty, the ko builder uses a default value of <code>.</code>. If main is a pattern with wildcards, such as <code>./...</code>, the expansion must contain only one main package, otherwise ko fails. Main is ignored if the <code>ImageName</code> starts with <code>ko://</code>. Example: <code>./cmd/foo</code>."
-        },
-        "platforms": {
-          "items": {
-            "type": "string"
-          },
-          "type": "array",
-          "description": "list of platforms to build images for. Each platform is of the format `os[/arch[/variant]]`, e.g., `linux/amd64`. Use `[\"all\"]` to build for all platforms supported by the base image. If empty, the builder uses the ko default (`[\"linux/amd64\"]`). Example: `[\"linux/amd64\", \"linux/arm64\"]`.",
-          "x-intellij-html-description": "list of platforms to build images for. Each platform is of the format <code>os[/arch[/variant]]</code>, e.g., <code>linux/amd64</code>. Use <code>[&quot;all&quot;]</code> to build for all platforms supported by the base image. If empty, the builder uses the ko default (<code>[&quot;linux/amd64&quot;]</code>). Example: <code>[&quot;linux/amd64&quot;, &quot;linux/arm64&quot;]</code>.",
-          "default": "[]"
         }
       },
       "preferredOrder": [
@@ -2733,8 +2724,7 @@
         "flags",
         "labels",
         "ldflags",
-        "main",
-        "platforms"
+        "main"
       ],
       "additionalProperties": false,
       "type": "object",
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/build/ko/build.go b/pkg/skaffold/build/ko/build.go
index 3b4e1f242..8d0474963 100644
--- a/pkg/skaffold/build/ko/build.go
+++ b/pkg/skaffold/build/ko/build.go
@@ -41,7 +41,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, a *latestV1.Artifact
 	if b.pushImages && strings.HasPrefix(ref, build.StrictScheme) {
 		return "", fmt.Errorf("default repo must be set when using the 'ko://' prefix and pushing to a registry: %s, see https://skaffold.dev/docs/environment/image-registries/", a.ImageName)
 	}
-	koBuilder, err := b.newKoBuilder(ctx, a)
+	koBuilder, err := b.newKoBuilder(ctx, a, platforms)
 	if err != nil {
 		return "", fmt.Errorf("error creating ko builder: %w", err)
 	}
diff --git a/pkg/skaffold/build/ko/builder.go b/pkg/skaffold/build/ko/builder.go
index e74eca5da..f8d0bd283 100644
--- a/pkg/skaffold/build/ko/builder.go
+++ b/pkg/skaffold/build/ko/builder.go
@@ -27,20 +27,21 @@ import (
 	"github.com/google/ko/pkg/commands/options"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
+	"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/pkg/skaffold/version"
 )
 
-func (b *Builder) newKoBuilder(ctx context.Context, a *latestV1.Artifact) (build.Interface, error) {
-	bo, err := buildOptions(a, b.runMode)
+func (b *Builder) newKoBuilder(ctx context.Context, a *latestV1.Artifact, platforms platform.Matcher) (build.Interface, error) {
+	bo, err := buildOptions(a, b.runMode, platforms)
 	if err != nil {
 		return nil, fmt.Errorf("could not construct ko build options: %v", err)
 	}
 	return commands.NewBuilder(ctx, bo)
 }
 
-func buildOptions(a *latestV1.Artifact, runMode config.RunMode) (*options.BuildOptions, error) {
+func buildOptions(a *latestV1.Artifact, runMode config.RunMode, platforms platform.Matcher) (*options.BuildOptions, error) {
 	buildconfig, err := buildConfig(a)
 	if err != nil {
 		return nil, fmt.Errorf("could not create ko build config: %v", err)
@@ -55,7 +56,7 @@ func buildOptions(a *latestV1.Artifact, runMode config.RunMode) (*options.BuildO
 		ConcurrentBuilds:     1, // we could plug in Skaffold's max builds here, but it'd be incorrect if users build more than one artifact
 		DisableOptimizations: runMode == config.RunModes.Debug,
 		Labels:               imageLabels,
-		Platform:             strings.Join(a.KoArtifact.Platforms, ","),
+		Platform:             platforms.String(),
 		Trimpath:             runMode != config.RunModes.Debug,
 		UserAgent:            version.UserAgentWithClient(),
 		WorkingDirectory:     filepath.Join(a.Workspace, a.KoArtifact.Dir),
diff --git a/pkg/skaffold/platform/platform.go b/pkg/skaffold/platform/platform.go
index e652cddd2..280be6732 100644
--- a/pkg/skaffold/platform/platform.go
+++ b/pkg/skaffold/platform/platform.go
@@ -89,6 +89,9 @@ func (m Matcher) Intersect(other Matcher) Matcher {
 func Parse(ps []string) (Matcher, error) {
 	var sl []specs.Platform
 	for _, p := range ps {
+		if strings.ToLower(p) == "all" {
+			return All, nil
+		}
 		platform, err := platforms.Parse(p)
 		if err != nil {
 			return Matcher{}, UnknownPlatformCLIFlag(p, err)
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)
diff --git a/pkg/skaffold/schema/v2beta27/upgrade.go b/pkg/skaffold/schema/v2beta27/upgrade.go
index ce953a1a6..d8f46bf5f 100755
--- a/pkg/skaffold/schema/v2beta27/upgrade.go
+++ b/pkg/skaffold/schema/v2beta27/upgrade.go
@@ -34,5 +34,18 @@ func (c *SkaffoldConfig) Upgrade() (util.VersionedConfig, error) {
 }
 
 func upgradeOnePipeline(oldPipeline, newPipeline interface{}) error {
+	oldBuild := &oldPipeline.(*Pipeline).Build
+	newBuild := &newPipeline.(*next.Pipeline).Build
+
+	// move: artifact.ko.Platforms
+	//   to: artifact.Platforms
+	for i, newArtifact := range newBuild.Artifacts {
+		oldArtifact := oldBuild.Artifacts[i]
+		if oldArtifact.KoArtifact == nil || len(oldArtifact.KoArtifact.Platforms) == 0 {
+			continue
+		}
+		newArtifact.Platforms = oldArtifact.KoArtifact.Platforms
+	}
+
 	return nil
 }


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/build/ko/builder_test.go b/pkg/skaffold/build/ko/builder_test.go
index 19462af2c..25064b4f9 100644
--- a/pkg/skaffold/build/ko/builder_test.go
+++ b/pkg/skaffold/build/ko/builder_test.go
@@ -25,8 +25,10 @@ import (
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/google/ko/pkg/build"
 	"github.com/google/ko/pkg/commands/options"
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
 
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"
 	"github.com/GoogleContainerTools/skaffold/testutil"
@@ -40,6 +42,7 @@ func TestBuildOptions(t *testing.T) {
 	tests := []struct {
 		description string
 		artifact    latestV1.Artifact
+		platforms   platform.Matcher
 		envVarValue string
 		runMode     config.RunMode
 		wantBo      options.BuildOptions
@@ -82,13 +85,14 @@ func TestBuildOptions(t *testing.T) {
 							fmt.Sprintf("-ldflag-{{.%s}}", testKoBuildOptionsEnvVar),
 							fmt.Sprintf("-ldflag2-{{.Env.%s}}", testKoBuildOptionsEnvVar),
 						},
-						Main:      "cmd/app",
-						Platforms: []string{"linux/amd64", "linux/arm64"},
+						Main: "cmd/app",
 					},
 				},
 				ImageName: "ko://example.com/foo",
 				Workspace: "workdir",
 			},
+			platforms: platform.Matcher{Platforms: []specs.Platform{{OS: "linux", Architecture: "amd64"}, {OS: "linux", Architecture: "arm64"}}},
+
 			envVarValue: "baz",
 			runMode:     config.RunModes.Debug,
 			wantBo: options.BuildOptions{
@@ -148,7 +152,7 @@ func TestBuildOptions(t *testing.T) {
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
 			os.Setenv(testKoBuildOptionsEnvVar, test.envVarValue)
-			gotBo, err := buildOptions(&test.artifact, test.runMode)
+			gotBo, err := buildOptions(&test.artifact, test.runMode, test.platforms)
 			defer os.Unsetenv(testKoBuildOptionsEnvVar)
 			t.CheckErrorAndFailNow(false, err)
 			t.CheckDeepEqual(test.wantBo, *gotBo,
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,
 		},
diff --git a/pkg/skaffold/schema/latest/v1/config.go b/pkg/skaffold/schema/latest/v1/config.go
index 1449f99c3..59889d4bf 100644
--- a/pkg/skaffold/schema/latest/v1/config.go
+++ b/pkg/skaffold/schema/latest/v1/config.go
@@ -1403,13 +1403,6 @@ type KoArtifact struct {
 	// Main is ignored if the `ImageName` starts with `ko://`.
 	// Example: `./cmd/foo`.
 	Main string `yaml:"main,omitempty"`
-
-	// Platforms is the list of platforms to build images for.
-	// Each platform is of the format `os[/arch[/variant]]`, e.g., `linux/amd64`.
-	// Use `["all"]` to build for all platforms supported by the base image.
-	// If empty, the builder uses the ko default (`["linux/amd64"]`).
-	// Example: `["linux/amd64", "linux/arm64"]`.
-	Platforms []string `yaml:"platforms,omitempty"`
 }
 
 // KoDependencies is used to specify dependencies for an artifact built by ko.
diff --git a/pkg/skaffold/schema/v2beta27/upgrade_test.go b/pkg/skaffold/schema/v2beta27/upgrade_test.go
index b33b27889..88bed0ba7 100755
--- a/pkg/skaffold/schema/v2beta27/upgrade_test.go
+++ b/pkg/skaffold/schema/v2beta27/upgrade_test.go
@@ -51,7 +51,8 @@ build:
     sync:
       auto: true
   - image: ko://github.com/GoogleContainerTools/skaffold/cmd/skaffold
-    ko: {}
+    ko:
+      platforms: ['linux/arm64', 'linux/amd64']
   googleCloudBuild:
     projectId: test-project
 test:
@@ -133,6 +134,7 @@ build:
       auto: true
   - image: ko://github.com/GoogleContainerTools/skaffold/cmd/skaffold
     ko: {}
+    platforms: ['linux/arm64', 'linux/amd64']
   googleCloudBuild:
     projectId: test-project
 test: