Repository: GoogleContainerTools/skaffold
Pull request #8599 (2023-03-28 20:41:03)

chore: restore firelog exporter  (#8555)
This reverts commit 710963c8c58268f7268c07d2b81fc29a5721429d.

<!-- Thank you for your contribution! -->

<!-- Include if applicable: -->
Fixes: https://github.com/GoogleContainerTools/skaffold/issues/8432 <!-- tracking issues that this PR will close -->
**Related**: _Relevant tracking issues, for context_
**Merge before/after**: _Dependent or prerequisite PRs_

**Description**
<!-- Describe your changes here. The more detail, the easier the review! -->

**User facing changes (remove if N/A)**
<!-- Describe any user facing changes this PR introduces. -->
<!-- "Before" and "After" sections work great - bonus points for screenshots! -->
<!-- Be sure all docs have been updated as well! -->

**Follow-up Work (remove if N/A)**
<!-- Mention any related follow up work to this PR. -->


<!--
Please be sure your PR includes unit tests - we don't merge code that brings down test coverage! 
Integration tests are sometimes an appropriate substitute. 
-->


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

```diff
@@            Coverage Diff             @@
##             main    #8599      +/-   ##
==========================================
- Coverage   70.48%   64.21%   -6.27%     
==========================================
  Files         515      613      +98     
  Lines       23150    30689    +7539     
==========================================
+ Hits        16317    19708    +3391     
- Misses       5776     9505    +3729     
- Partials     1057     1476     +419     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | Coverage Δ | |
|---|---|---|
| [cmd/skaffold/app/cmd/completion.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY29tcGxldGlvbi5nbw==) | `13.04% <0.00%> (-1.25%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/config/list.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY29uZmlnL2xpc3QuZ28=) | `65.21% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/config/set.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY29uZmlnL3NldC5nbw==) | `88.72% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/config/util.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY29uZmlnL3V0aWwuZ28=) | `54.28% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/credits.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvY3JlZGl0cy5nbw==) | `100.00% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/credits/export.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?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/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGVwbG95Lmdv) | `40.90% <0.00%> (-12.94%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/generate\_pipeline.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZ2VuZXJhdGVfcGlwZWxpbmUuZ28=) | `60.00% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_modules.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF9tb2R1bGVzLmdv) | `65.00% <ø> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_profiles.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF9wcm9maWxlcy5nbw==) | `66.66% <ø> (ø)` | |
| ... and [39 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | |

... and [391 files with indirect coverage changes](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/8599/indirect-changes?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/examples/bazel/skaffold.yaml b/examples/bazel/skaffold.yaml
index 42f232546..670688121 100644
--- a/examples/bazel/skaffold.yaml
+++ b/examples/bazel/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/buildpacks-java/skaffold.yaml b/examples/buildpacks-java/skaffold.yaml
index 23cf068e5..01723896d 100644
--- a/examples/buildpacks-java/skaffold.yaml
+++ b/examples/buildpacks-java/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/buildpacks-node/skaffold.yaml b/examples/buildpacks-node/skaffold.yaml
index 2610838d6..4c0f2c492 100644
--- a/examples/buildpacks-node/skaffold.yaml
+++ b/examples/buildpacks-node/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/buildpacks-python/skaffold.yaml b/examples/buildpacks-python/skaffold.yaml
index 78577389e..f0c80ca13 100644
--- a/examples/buildpacks-python/skaffold.yaml
+++ b/examples/buildpacks-python/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/buildpacks/skaffold.yaml b/examples/buildpacks/skaffold.yaml
index 00baec886..17fa09bd4 100644
--- a/examples/buildpacks/skaffold.yaml
+++ b/examples/buildpacks/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/cross-platform-builds/skaffold.yaml b/examples/cross-platform-builds/skaffold.yaml
index 0cabda4b4..1c53088f0 100644
--- a/examples/cross-platform-builds/skaffold.yaml
+++ b/examples/cross-platform-builds/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/custom-buildx/skaffold.yaml b/examples/custom-buildx/skaffold.yaml
index 2aab382a9..e99b9e5e4 100644
--- a/examples/custom-buildx/skaffold.yaml
+++ b/examples/custom-buildx/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   platforms: ["linux/amd64", "linux/arm64"]
diff --git a/examples/custom/skaffold.yaml b/examples/custom/skaffold.yaml
index c0fd512a8..45bac9d53 100644
--- a/examples/custom/skaffold.yaml
+++ b/examples/custom/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/dev-journey-buildpacks/skaffold.yaml b/examples/dev-journey-buildpacks/skaffold.yaml
index 136d78def..ec8e85781 100644
--- a/examples/dev-journey-buildpacks/skaffold.yaml
+++ b/examples/dev-journey-buildpacks/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/docker-deploy/skaffold.yaml b/examples/docker-deploy/skaffold.yaml
index ccec1890b..3f7375df1 100644
--- a/examples/docker-deploy/skaffold.yaml
+++ b/examples/docker-deploy/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   local:
diff --git a/examples/gcb-kaniko/skaffold.yaml b/examples/gcb-kaniko/skaffold.yaml
index f68b4d1c8..550d35473 100644
--- a/examples/gcb-kaniko/skaffold.yaml
+++ b/examples/gcb-kaniko/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   googleCloudBuild:
diff --git a/examples/generate-pipeline/skaffold.yaml b/examples/generate-pipeline/skaffold.yaml
index 9f81b92da..58f9e32b8 100644
--- a/examples/generate-pipeline/skaffold.yaml
+++ b/examples/generate-pipeline/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/getting-started-kustomize/skaffold.yaml b/examples/getting-started-kustomize/skaffold.yaml
index 9444d372c..5d3899f32 100644
--- a/examples/getting-started-kustomize/skaffold.yaml
+++ b/examples/getting-started-kustomize/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 metadata:
   name: getting-started-kustomize
diff --git a/examples/getting-started/skaffold.yaml b/examples/getting-started/skaffold.yaml
index 7b0f7aac5..b9d33f158 100644
--- a/examples/getting-started/skaffold.yaml
+++ b/examples/getting-started/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/google-cloud-build/skaffold.yaml b/examples/google-cloud-build/skaffold.yaml
index 909f051e4..da1d116ff 100644
--- a/examples/google-cloud-build/skaffold.yaml
+++ b/examples/google-cloud-build/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   googleCloudBuild:
diff --git a/examples/helm-deployment-dependencies/skaffold.yaml b/examples/helm-deployment-dependencies/skaffold.yaml
index 5b855d482..802545d39 100644
--- a/examples/helm-deployment-dependencies/skaffold.yaml
+++ b/examples/helm-deployment-dependencies/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   tagPolicy:
diff --git a/examples/helm-deployment/skaffold.yaml b/examples/helm-deployment/skaffold.yaml
index d14c728be..ffde3ae49 100644
--- a/examples/helm-deployment/skaffold.yaml
+++ b/examples/helm-deployment/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/helm-remote-repo/skaffold.yaml b/examples/helm-remote-repo/skaffold.yaml
index cc50252f7..57380c069 100644
--- a/examples/helm-remote-repo/skaffold.yaml
+++ b/examples/helm-remote-repo/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 deploy:
   helm:
diff --git a/examples/helm-render/skaffold.yaml b/examples/helm-render/skaffold.yaml
index 5f9a820f8..f832154a4 100644
--- a/examples/helm-render/skaffold.yaml
+++ b/examples/helm-render/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/hot-reload/skaffold.yaml b/examples/hot-reload/skaffold.yaml
index 6ed43e717..bfacb510a 100644
--- a/examples/hot-reload/skaffold.yaml
+++ b/examples/hot-reload/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/jib-gradle/skaffold.yaml b/examples/jib-gradle/skaffold.yaml
index 7a00a8c4c..e8221f81d 100644
--- a/examples/jib-gradle/skaffold.yaml
+++ b/examples/jib-gradle/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/jib-multimodule/skaffold.yaml b/examples/jib-multimodule/skaffold.yaml
index e5898e4e2..ee7e65ef7 100644
--- a/examples/jib-multimodule/skaffold.yaml
+++ b/examples/jib-multimodule/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/jib-sync/skaffold-gradle.yaml b/examples/jib-sync/skaffold-gradle.yaml
index ebcf79d84..1da2cdba5 100644
--- a/examples/jib-sync/skaffold-gradle.yaml
+++ b/examples/jib-sync/skaffold-gradle.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/jib-sync/skaffold-maven.yaml b/examples/jib-sync/skaffold-maven.yaml
index 8e6123910..0aec95e34 100644
--- a/examples/jib-sync/skaffold-maven.yaml
+++ b/examples/jib-sync/skaffold-maven.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/jib/skaffold.yaml b/examples/jib/skaffold.yaml
index f15189040..d02a03df0 100644
--- a/examples/jib/skaffold.yaml
+++ b/examples/jib/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/kaniko/skaffold.yaml b/examples/kaniko/skaffold.yaml
index b3322b812..0c9de8a29 100644
--- a/examples/kaniko/skaffold.yaml
+++ b/examples/kaniko/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/ko-sync-infer/skaffold.yaml b/examples/ko-sync-infer/skaffold.yaml
index e093f0fa1..8d73b531d 100644
--- a/examples/ko-sync-infer/skaffold.yaml
+++ b/examples/ko-sync-infer/skaffold.yaml
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/ko/skaffold.yaml b/examples/ko/skaffold.yaml
index 8f409d6f3..fa1996a4e 100644
--- a/examples/ko/skaffold.yaml
+++ b/examples/ko/skaffold.yaml
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/kustomize/skaffold-kustomize-args.yaml b/examples/kustomize/skaffold-kustomize-args.yaml
index 4786a1bac..b51bce05a 100644
--- a/examples/kustomize/skaffold-kustomize-args.yaml
+++ b/examples/kustomize/skaffold-kustomize-args.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 deploy:
   kustomize:
diff --git a/examples/kustomize/skaffold.yaml b/examples/kustomize/skaffold.yaml
index b92eb203b..ed1b46185 100644
--- a/examples/kustomize/skaffold.yaml
+++ b/examples/kustomize/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 manifests:
   kustomize:
diff --git a/examples/lifecycle-hooks/skaffold.yaml b/examples/lifecycle-hooks/skaffold.yaml
index b7db9d760..4419bfb7e 100644
--- a/examples/lifecycle-hooks/skaffold.yaml
+++ b/examples/lifecycle-hooks/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 manifests:
   rawYaml:
diff --git a/examples/microservices/skaffold.yaml b/examples/microservices/skaffold.yaml
index 786e2028b..3de8e7ba6 100644
--- a/examples/microservices/skaffold.yaml
+++ b/examples/microservices/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/multi-config-microservices/base/skaffold.yaml b/examples/multi-config-microservices/base/skaffold.yaml
index fb8e24706..55e4e3810 100644
--- a/examples/multi-config-microservices/base/skaffold.yaml
+++ b/examples/multi-config-microservices/base/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/multi-config-microservices/leeroy-app/skaffold.yaml b/examples/multi-config-microservices/leeroy-app/skaffold.yaml
index 34fb167ba..db9ef5fce 100644
--- a/examples/multi-config-microservices/leeroy-app/skaffold.yaml
+++ b/examples/multi-config-microservices/leeroy-app/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 metadata:
   name: app-config
diff --git a/examples/multi-config-microservices/leeroy-web/skaffold.yaml b/examples/multi-config-microservices/leeroy-web/skaffold.yaml
index 780434a6b..dd8312978 100644
--- a/examples/multi-config-microservices/leeroy-web/skaffold.yaml
+++ b/examples/multi-config-microservices/leeroy-web/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 metadata:
   name: web-config
diff --git a/examples/multi-config-microservices/skaffold.yaml b/examples/multi-config-microservices/skaffold.yaml
index 8f97fc77b..87d708570 100644
--- a/examples/multi-config-microservices/skaffold.yaml
+++ b/examples/multi-config-microservices/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 requires:
 - path: ./leeroy-app
diff --git a/examples/multiple-renderers/skaffold.yaml b/examples/multiple-renderers/skaffold.yaml
index 30c340504..5d7a011e4 100644
--- a/examples/multiple-renderers/skaffold.yaml
+++ b/examples/multiple-renderers/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 metadata:
   name: go-guestbook
diff --git a/examples/nodejs/skaffold.yaml b/examples/nodejs/skaffold.yaml
index b1d3856f8..091500b1c 100644
--- a/examples/nodejs/skaffold.yaml
+++ b/examples/nodejs/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 
 build:
diff --git a/examples/profile-patches/skaffold.yaml b/examples/profile-patches/skaffold.yaml
index a294ad16e..cad6dc5f4 100644
--- a/examples/profile-patches/skaffold.yaml
+++ b/examples/profile-patches/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   # only build and deploy "base-service" on main profile
diff --git a/examples/profiles/skaffold.yaml b/examples/profiles/skaffold.yaml
index cebf74853..16bffac24 100644
--- a/examples/profiles/skaffold.yaml
+++ b/examples/profiles/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   # only build and deploy "world-service" on main profile
diff --git a/examples/react-reload-docker/skaffold.yaml b/examples/react-reload-docker/skaffold.yaml
index 8d84a97e2..cb576c93c 100644
--- a/examples/react-reload-docker/skaffold.yaml
+++ b/examples/react-reload-docker/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   local:
diff --git a/examples/react-reload/skaffold.yaml b/examples/react-reload/skaffold.yaml
index 13e36f297..f68f400ae 100644
--- a/examples/react-reload/skaffold.yaml
+++ b/examples/react-reload/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/remote-multi-config-microservices/skaffold.yaml b/examples/remote-multi-config-microservices/skaffold.yaml
index 4780c8c3d..507a578fb 100644
--- a/examples/remote-multi-config-microservices/skaffold.yaml
+++ b/examples/remote-multi-config-microservices/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 requires:
 - git:
diff --git a/examples/ruby/skaffold.yaml b/examples/ruby/skaffold.yaml
index c9ca746e5..be7ba5882 100644
--- a/examples/ruby/skaffold.yaml
+++ b/examples/ruby/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/simple-artifact-dependency/skaffold.yaml b/examples/simple-artifact-dependency/skaffold.yaml
index b3e45c2b4..0500ce37a 100644
--- a/examples/simple-artifact-dependency/skaffold.yaml
+++ b/examples/simple-artifact-dependency/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/tagging-with-environment-variables/skaffold.yaml b/examples/tagging-with-environment-variables/skaffold.yaml
index 221c98a4e..f05315f91 100644
--- a/examples/tagging-with-environment-variables/skaffold.yaml
+++ b/examples/tagging-with-environment-variables/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/templated-fields/skaffold.yaml b/examples/templated-fields/skaffold.yaml
index 2410e3917..fa9ad71e1 100644
--- a/examples/templated-fields/skaffold.yaml
+++ b/examples/templated-fields/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 metadata:
   name: my-app
diff --git a/examples/typescript/skaffold.yaml b/examples/typescript/skaffold.yaml
index 6697e4708..1270982b5 100644
--- a/examples/typescript/skaffold.yaml
+++ b/examples/typescript/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 
 build:
diff --git a/examples/using-env-file/skaffold.yaml b/examples/using-env-file/skaffold.yaml
index 9cd4b9e19..2da8e1bbe 100644
--- a/examples/using-env-file/skaffold.yaml
+++ b/examples/using-env-file/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/go.mod b/go.mod
index 3ecbdb098..6a0a06a4a 100644
--- a/go.mod
+++ b/go.mod
@@ -58,6 +58,7 @@ require (
 	github.com/docker/docker v20.10.20+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/dustin/go-humanize v1.0.1
+	github.com/evanphx/json-patch v4.12.0+incompatible
 	github.com/fatih/semgroup v1.2.0
 	github.com/go-git/go-git/v5 v5.5.2
 	github.com/golang/glog v1.0.0
@@ -184,7 +185,6 @@ require (
 	github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
 	github.com/emicklei/go-restful/v3 v3.9.0 // indirect
 	github.com/emirpasic/gods v1.18.1 // indirect
-	github.com/evanphx/json-patch v4.12.0+incompatible // indirect
 	github.com/evanphx/json-patch/v5 v5.6.0 // indirect
 	github.com/francoispqt/gojay v1.2.13 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
@@ -249,7 +249,7 @@ require (
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/oklog/ulid v1.3.1 // indirect
-	github.com/opencontainers/runc v1.1.4 // indirect
+	github.com/opencontainers/runc v1.1.5 // indirect
 	github.com/opencontainers/selinux v1.11.0 // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
diff --git a/go.sum b/go.sum
index dff725684..5777cb7bb 100644
--- a/go.sum
+++ b/go.sum
@@ -2054,8 +2054,8 @@ github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84
 github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
 github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
 github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
-github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg=
-github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
+github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
+github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
 github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
diff --git a/pkg/skaffold/instrumentation/ci.go b/pkg/skaffold/instrumentation/ci.go
new file mode 100644
index 000000000..e42355b85
--- /dev/null
+++ b/pkg/skaffold/instrumentation/ci.go
@@ -0,0 +1,32 @@
+/*
+Copyright 2020 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 instrumentation
+
+var ciMap = map[string]string{
+	"TF_BUILD":           "azure-pipelines",
+	"bamboo_buildKey":    "bamboo",
+	"BUILDKITE":          "buildkite",
+	"CIRCLECI":           "circle-ci",
+	"CIRRUS_CI":          "cirrus-ci",
+	"CODEBUILD_BUILD_ID": "code-build",
+	"GITHUB_ACTIONS":     "github-actions",
+	"GITLAB_CI":          "gitlab-ci",
+	"HEROKU_TEST_RUN_ID": "heroku-ci",
+	"JENKINS_URL":        "jenkins",
+	"TEAMCITY_VERSION":   "team-city",
+	"TRAVIS":             "travis-ci",
+}
diff --git a/pkg/skaffold/instrumentation/export.go b/pkg/skaffold/instrumentation/export.go
index 2fc2a3be8..43545581a 100644
--- a/pkg/skaffold/instrumentation/export.go
+++ b/pkg/skaffold/instrumentation/export.go
@@ -94,6 +94,11 @@ func exportMetrics(ctx context.Context, filename string, meter skaffoldMeter) er
 	}
 	var meters []skaffoldMeter
 	err = json.Unmarshal(b, &meters)
+	// each meter contains around 20 datapoints, and each datapoint requires a request to firelog api
+	// we send at most 10 meters stored in skaffold metrics as too many request may result in the firelog server returning 429.
+	if len(meters) >= 10 {
+		meters = meters[:10]
+	}
 	if err != nil {
 		meters = []skaffoldMeter{}
 	}
@@ -160,17 +165,13 @@ func devStdOutExporter() (sdkmetric.Exporter, error) {
 }
 
 func createMetrics(ctx context.Context, meter skaffoldMeter) {
-	// There is a minimum 10 second interval that metrics are allowed to upload to Cloud monitoring
+	m := global.Meter("skaffold")
+
 	// A metric is uniquely identified by the metric name and the labels and corresponding values
 	// This random number is used as a label to differentiate the metrics per user so if two users
 	// run `skaffold build` at the same time they will both have their metrics recorded
 	randLabel := attribute.String("randomizer", strconv.Itoa(rand.Intn(75000)))
 
-	m := global.Meter("skaffold")
-
-	// cloud monitoring only supports string type labels
-	// cloud monitoring only supports 10 labels per metric descriptor
-	// be careful when appending new values to this `labels` slice
 	labels := []attribute.KeyValue{
 		attribute.String("version", meter.Version),
 		attribute.String("os", meter.OS),
@@ -180,15 +181,13 @@ func createMetrics(ctx context.Context, meter skaffoldMeter) {
 		attribute.String("platform_type", meter.PlatformType),
 		attribute.String("config_count", strconv.Itoa(meter.ConfigCount)),
 		attribute.String("cluster_type", meter.ClusterType),
-	}
-	sharedLabels := []attribute.KeyValue{
+		attribute.String("ci_cd_platform", meter.CISystem),
 		randLabel,
 	}
 
 	if allowedUser := user.IsAllowedUser(meter.User); allowedUser {
-		sharedLabels = append(sharedLabels, attribute.String("user", meter.User))
+		labels = append(labels, attribute.String("user", meter.User))
 	}
-	labels = append(labels, sharedLabels...)
 	platformLabel := attribute.String("host_os_arch", fmt.Sprintf("%s/%s", meter.OS, meter.Arch))
 	runCounter := NewInt64ValueRecorder(m, "launches", instrument.WithDescription("Skaffold Invocations"))
 	runCounter.Record(ctx, 1, labels...)
@@ -197,36 +196,36 @@ func createMetrics(ctx context.Context, meter skaffoldMeter) {
 		instrument.WithDescription("durations of skaffold commands in seconds"))
 	durationRecorder.Record(ctx, meter.Duration.Seconds(), labels...)
 	if meter.Command != "" {
-		commandMetrics(ctx, meter, m, sharedLabels...)
-		flagMetrics(ctx, meter, m, randLabel)
+		commandMetrics(ctx, meter, m, labels...)
+		flagMetrics(ctx, meter, m, labels...)
 		hooksMetrics(ctx, meter, m, labels...)
 		if doesBuild.Contains(meter.Command) {
-			builderMetrics(ctx, meter, m, platformLabel, sharedLabels...)
+			builderMetrics(ctx, meter, m, platformLabel, labels...)
 		}
 		if doesDeploy.Contains(meter.Command) {
-			deployerMetrics(ctx, meter, m, sharedLabels...)
+			deployerMetrics(ctx, meter, m, labels...)
 		}
 		if doesDeploy.Contains(meter.Command) || meter.Command == "render" {
-			resourceSelectorMetrics(ctx, meter, m, sharedLabels...)
+			resourceSelectorMetrics(ctx, meter, m, labels...)
 		}
 	}
 
 	if meter.ErrorCode != 0 {
-		errorMetrics(ctx, meter, m, append(sharedLabels, platformLabel)...)
+		errorMetrics(ctx, meter, m, append(labels, platformLabel)...)
 	}
 }
 
-func flagMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, randLabel attribute.KeyValue) {
+func flagMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, labels ...attribute.KeyValue) {
 	flagCounter := NewInt64ValueRecorder(m, "flags", instrument.WithDescription("Tracks usage of enum flags"))
 	for k, v := range meter.EnumFlags {
-		labels := []attribute.KeyValue{
+		l := []attribute.KeyValue{
 			attribute.String("flag_name", k),
 			attribute.String("flag_value", v),
 			attribute.String("command", meter.Command),
 			attribute.String("error", meter.ErrorCode.String()),
-			randLabel,
 		}
-		flagCounter.Record(ctx, 1, labels...)
+		l = append(l, labels...)
+		flagCounter.Record(ctx, 1, l...)
 	}
 }
 
@@ -249,12 +248,13 @@ func commandMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, la
 			m := counts[iteration.Intent]
 			m[iteration.ErrorCode]++
 		}
+		randomizer := attribute.String("randomizer2", strconv.Itoa(rand.Intn(75000)))
 		for intention, errorCounts := range counts {
 			for errorCode, count := range errorCounts {
 				iterationCounter.Record(ctx, int64(count),
 					append(labels,
 						attribute.String("intent", intention),
-						attribute.String("error", errorCode.String()),
+						attribute.String("error", errorCode.String()), randomizer,
 					)...)
 			}
 		}
diff --git a/pkg/skaffold/instrumentation/firelog/exporter.go b/pkg/skaffold/instrumentation/firelog/exporter.go
new file mode 100644
index 000000000..280c3beb3
--- /dev/null
+++ b/pkg/skaffold/instrumentation/firelog/exporter.go
@@ -0,0 +1,185 @@
+/*
+Copyright 2020 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 firelog
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"os"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
+	"go.opentelemetry.io/otel/sdk/metric"
+	"go.opentelemetry.io/otel/sdk/metric/aggregation"
+	"go.opentelemetry.io/otel/sdk/metric/metricdata"
+)
+
+var (
+	APIKey  = ""
+	url     = fmt.Sprintf(`https://firebaselogging-pa.googleapis.com/v1/firelog/legacy/log?key=%s`, APIKey)
+	POST    = http.Post
+	Marshal = json.Marshal
+)
+
+type Exporter struct {
+}
+
+func NewFireLogExporter() (metric.Exporter, error) {
+	if APIKey == "" {
+		// export metrics to std out if local env is set.
+		if _, ok := os.LookupEnv("SKAFFOLD_EXPORT_TO_STDOUT"); ok {
+			enc := json.NewEncoder(os.Stdout)
+			enc.SetIndent("", "  ")
+			exporter, err := stdoutmetric.New(stdoutmetric.WithEncoder(enc))
+			return exporter, err
+		}
+		return nil, nil
+	}
+	return &Exporter{}, nil
+}
+
+// Temporality returns the Temporality to use for an instrument kind.
+func (e *Exporter) Temporality(ik metric.InstrumentKind) metricdata.Temporality {
+	return metric.DefaultTemporalitySelector(ik)
+}
+
+// Aggregation returns the Aggregation to use for an instrument kind.
+func (e *Exporter) Aggregation(ik metric.InstrumentKind) aggregation.Aggregation {
+	return metric.DefaultAggregationSelector(ik)
+}
+
+func (e *Exporter) Export(ctx context.Context, md metricdata.ResourceMetrics) error {
+	for _, sm := range md.ScopeMetrics {
+		for _, m := range sm.Metrics {
+			if err := processMetrics(m); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func processMetrics(m metricdata.Metrics) error {
+	switch a := m.Data.(type) {
+	case metricdata.Gauge[int64]:
+		for _, pt := range a.DataPoints {
+			if err := sendDataPoint(m.Name, DataPointInt64(pt)); err != nil {
+				return err
+			}
+		}
+	case metricdata.Gauge[float64]:
+		for _, pt := range a.DataPoints {
+			if err := sendDataPoint(m.Name, DataPointFloat64(pt)); err != nil {
+				return err
+			}
+		}
+	case metricdata.Sum[int64]:
+		for _, pt := range a.DataPoints {
+			if err := sendDataPoint(m.Name, DataPointInt64(pt)); err != nil {
+				return err
+			}
+		}
+	case metricdata.Sum[float64]:
+		for _, pt := range a.DataPoints {
+			if err := sendDataPoint(m.Name, DataPointFloat64(pt)); err != nil {
+				return err
+			}
+		}
+	case metricdata.Histogram:
+		for _, pt := range a.DataPoints {
+			if err := sendDataPoint(m.Name, DataPointHistogram(pt)); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func sendDataPoint(name string, dp DataPoint) error {
+	kvs := toEventMetadata(dp.attributes())
+	kvs = append(kvs, KeyValue{Key: name, Value: dp.value()})
+	str, err := buildProtoStr(name, kvs)
+	if err != nil {
+		return err
+	}
+	data := buildMetricData(str, dp.eventTime(), dp.upTime())
+
+	resp, err := POST(url, "application/json", data.newReader())
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("one platform returned an non-success response: %d", resp.StatusCode)
+	}
+
+	return err
+}
+
+func buildMetricData(proto string, startTimeMS int64, upTimeMS int64) MetricData {
+	return MetricData{
+		ClientInfo: ClientInfo{ClientType: "DESKTOP"},
+		LogSource:  "CONCORD",
+		LogEvent: LogEvent{
+			EventTimeMS:                  startTimeMS,
+			EventUptimeMS:                upTimeMS,
+			SourceExtensionJSONProto3Str: proto,
+		},
+		RequestTimeMS:   startTimeMS,
+		RequestUptimeMS: upTimeMS,
+	}
+}
+
+func buildProtoStr(name string, kvs EventMetadata) (string, error) {
+	proto3 := SourceExtensionJSONProto3{
+		ProjectID:       "skaffold",
+		ConsoleType:     "SKAFFOLD",
+		ClientInstallID: "",
+		EventName:       name,
+		EventMetadata:   kvs,
+	}
+
+	b, err := Marshal(proto3)
+	if err != nil {
+		return "", fmt.Errorf("failed to marshal metricdata")
+	}
+	return string(b), nil
+}
+
+func toEventMetadata(attributes attribute.Set) EventMetadata {
+	kvs := EventMetadata{}
+	iterator := attributes.Iter()
+	for iterator.Next() {
+		attr := iterator.Attribute()
+		kv := KeyValue{
+			Key:   string(attr.Key),
+			Value: attr.Value.Emit(),
+		}
+		kvs = append(kvs, kv)
+	}
+	return kvs
+}
+
+func (e *Exporter) ForceFlush(ctx context.Context) error {
+	return ctx.Err()
+}
+
+func (e *Exporter) Shutdown(ctx context.Context) error {
+	return ctx.Err()
+}
diff --git a/pkg/skaffold/instrumentation/firelog/types.go b/pkg/skaffold/instrumentation/firelog/types.go
new file mode 100644
index 000000000..f254c5e7e
--- /dev/null
+++ b/pkg/skaffold/instrumentation/firelog/types.go
@@ -0,0 +1,134 @@
+/*
+Copyright 2020 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 firelog
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/sdk/metric/metricdata"
+)
+
+type ClientInfo struct {
+	ClientType string `json:"client_type"`
+}
+
+type MetricData struct {
+	ClientInfo      ClientInfo `json:"client_info"`
+	LogSource       string     `json:"log_source"`
+	LogEvent        LogEvent   `json:"log_event"`
+	RequestTimeMS   int64      `json:"request_time_ms"`
+	RequestUptimeMS int64      `json:"request_uptime_ms"`
+}
+
+type LogEvent struct {
+	EventTimeMS                  int64  `json:"event_time_ms"`
+	EventUptimeMS                int64  `json:"event_uptime_ms"`
+	SourceExtensionJSONProto3Str string `json:"source_extension_json_proto3"`
+}
+
+type KeyValue struct {
+	Key   string `json:"key"`
+	Value string `json:"value"`
+}
+
+type EventMetadata []KeyValue
+
+type SourceExtensionJSONProto3 struct {
+	ProjectID       string     `json:"project_id"`
+	ConsoleType     string     `json:"console_type"`
+	ClientInstallID string     `json:"client_install_id"`
+	EventName       string     `json:"event_name"`
+	EventMetadata   []KeyValue `json:"event_metadata"`
+}
+
+type Key string
+
+func (k Key) Value(v string) KeyValue {
+	return KeyValue{
+		Key:   string(k),
+		Value: v,
+	}
+}
+
+func (md MetricData) newReader() *bytes.Reader {
+	data, _ := json.Marshal(md)
+	return bytes.NewReader(data)
+}
+
+type DataPoint interface {
+	value() string
+	attributes() attribute.Set
+	eventTime() int64
+	upTime() int64
+}
+
+type DataPointInt64 metricdata.DataPoint[int64]
+
+func (d DataPointInt64) attributes() attribute.Set {
+	return d.Attributes
+}
+
+func (d DataPointInt64) eventTime() int64 {
+	return d.StartTime.UnixMilli()
+}
+
+func (d DataPointInt64) upTime() int64 {
+	return d.Time.UnixMilli()
+}
+
+type DataPointFloat64 metricdata.DataPoint[float64]
+
+func (d DataPointFloat64) attributes() attribute.Set {
+	return d.Attributes
+}
+
+func (d DataPointFloat64) eventTime() int64 {
+	return d.StartTime.UnixMilli()
+}
+
+func (d DataPointFloat64) upTime() int64 {
+	return d.Time.UnixMilli()
+}
+
+type DataPointHistogram metricdata.HistogramDataPoint
+
+func (d DataPointHistogram) attributes() attribute.Set {
+	return d.Attributes
+}
+
+func (d DataPointHistogram) eventTime() int64 {
+	return d.StartTime.UnixMilli()
+}
+
+func (d DataPointHistogram) upTime() int64 {
+	return d.Time.UnixMilli()
+}
+
+func (d DataPointInt64) value() string {
+	return fmt.Sprintf("%d", d.Value)
+}
+
+func (d DataPointFloat64) value() string {
+	return fmt.Sprintf("%f", d.Value)
+}
+
+func (d DataPointHistogram) value() string {
+	return fmt.Sprintf("%f", d.Sum)
+}
diff --git a/pkg/skaffold/instrumentation/meter.go b/pkg/skaffold/instrumentation/meter.go
index 499ef1ea1..c251544d3 100644
--- a/pkg/skaffold/instrumentation/meter.go
+++ b/pkg/skaffold/instrumentation/meter.go
@@ -18,13 +18,16 @@ package instrumentation
 
 import (
 	"net/http"
+	"os"
 	"runtime"
 	"strings"
 	"time"
 
 	flag "github.com/spf13/pflag"
 
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/instrumentation/firelog"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util/stringset"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util/stringslice"
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/version"
@@ -52,11 +55,12 @@ var (
 		Version:            version.Get().Version,
 		ExitCode:           0,
 		ErrorCode:          proto.StatusCode_OK,
+		CISystem:           CISystem(),
 	}
 	MeteredCommands     = stringset.New()
 	doesBuild           = stringset.New()
 	doesDeploy          = stringset.New()
-	initExporter        = initCloudMonitoringExporterMetrics
+	initExporter        = firelog.NewFireLogExporter
 	isOnline            bool
 	ShouldExportMetrics bool
 )
@@ -177,3 +181,24 @@ func getClusterType(deployCtx string) string {
 	// TODO (tejaldesai): Add minikube detection.
 	return others
 }
+
+// CISystem deduce ciSystem from environment variables
+func CISystem() string {
+	evs := os.Environ()
+	m := util.EnvSliceToMap(evs, "=")
+	for k := range m {
+		if vv, ok := ciMap[k]; ok {
+			return vv
+		}
+	}
+	if v, ok := m["CI"]; ok {
+		return v
+	}
+	if _, ok := m["BUILD_ID"]; ok {
+		return "unknown"
+	}
+	if _, ok := m["BUILD_NUMBER"]; ok {
+		return "unknown"
+	}
+	return "no-ci"
+}
diff --git a/pkg/skaffold/instrumentation/types.go b/pkg/skaffold/instrumentation/types.go
index 6c73610e4..bf215f9bd 100644
--- a/pkg/skaffold/instrumentation/types.go
+++ b/pkg/skaffold/instrumentation/types.go
@@ -107,6 +107,9 @@ type skaffoldMeter struct {
 
 	// DeployNodePlatforms represents the set of kubernetes cluster node platforms
 	DeployNodePlatforms string
+
+	// CI represents the platform Skaffold is running on
+	CISystem string
 }
 
 // devIteration describes how an iteration and started and if an error happened.
diff --git a/pkg/skaffold/render/renderer/helm/helm.go b/pkg/skaffold/render/renderer/helm/helm.go
index cc9e5e3e7..f473dd29b 100644
--- a/pkg/skaffold/render/renderer/helm/helm.go
+++ b/pkg/skaffold/render/renderer/helm/helm.go
@@ -159,6 +159,16 @@ func (h Helm) generateHelmManifests(ctx context.Context, builds []graph.Artifact
 		outBuffer := new(bytes.Buffer)
 		errBuffer := new(bytes.Buffer)
 
+		// Build Chart dependencies, but allow a user to skip it.
+		if !release.SkipBuildDependencies && release.ChartPath != "" {
+			log.Entry(ctx).Info("Building helm dependencies...")
+
+			if err := helm.ExecWithStdoutAndStderr(ctx, h, outBuffer, errBuffer, false, helmEnv, "dep", "build", release.ChartPath); err != nil {
+				log.Entry(ctx).Infof(errBuffer.String())
+				return nil, helm.UserErr("building helm dependencies", err)
+			}
+		}
+
 		err = helm.ExecWithStdoutAndStderr(ctx, h, outBuffer, errBuffer, false, helmEnv, args...)
 		errorMsg := errBuffer.String()
 


Test patch:
diff --git a/examples/custom-tests/skaffold.yaml b/examples/custom-tests/skaffold.yaml
index 521b23176..0e1478cbd 100644
--- a/examples/custom-tests/skaffold.yaml
+++ b/examples/custom-tests/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/examples/grpc-e2e-tests/skaffold.yaml b/examples/grpc-e2e-tests/skaffold.yaml
index e0e3caf3a..d5af503eb 100644
--- a/examples/grpc-e2e-tests/skaffold.yaml
+++ b/examples/grpc-e2e-tests/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 metadata:
   name: visitor-counter-e2e
diff --git a/examples/structure-tests/skaffold.yaml b/examples/structure-tests/skaffold.yaml
index 914aa3fcc..ec0a7d5f6 100644
--- a/examples/structure-tests/skaffold.yaml
+++ b/examples/structure-tests/skaffold.yaml
@@ -1,4 +1,4 @@
-apiVersion: skaffold/v4beta3
+apiVersion: skaffold/v4beta4
 kind: Config
 build:
   artifacts:
diff --git a/pkg/skaffold/deploy/helm/helm_test.go b/pkg/skaffold/deploy/helm/helm_test.go
index 781f7e266..efb7e31f8 100644
--- a/pkg/skaffold/deploy/helm/helm_test.go
+++ b/pkg/skaffold/deploy/helm/helm_test.go
@@ -1162,7 +1162,8 @@ func TestHelmRender(t *testing.T) {
 			description: "normal render v3",
 			shouldErr:   false,
 			commands: testutil.
-				CmdRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --kubeconfig kubeconfig"),
+				CmdRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+				AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --kubeconfig kubeconfig"),
 			helm: testDeployConfig,
 			builds: []graph.Artifact{
 				{
@@ -1174,7 +1175,8 @@ func TestHelmRender(t *testing.T) {
 			description: "render with templated config",
 			shouldErr:   false,
 			commands: testutil.
-				CmdRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set image.name=skaffold-helm --set image.tag=skaffold-helm:tag1 --set missing.key=<MISSING> --set other.key=FOOBAR --set some.key=somevalue --set FOOBAR=somevalue --kubeconfig kubeconfig"),
+				CmdRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+				AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set image.name=skaffold-helm --set image.tag=skaffold-helm:tag1 --set missing.key=<MISSING> --set other.key=FOOBAR --set some.key=somevalue --set FOOBAR=somevalue --kubeconfig kubeconfig"),
 			helm: testDeployConfigTemplated,
 			builds: []graph.Artifact{
 				{
@@ -1185,8 +1187,10 @@ func TestHelmRender(t *testing.T) {
 		{
 			description: "render with templated values file",
 			shouldErr:   false,
-			commands:    testutil.CmdRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY -f /some/file-FOOBAR.yaml --kubeconfig kubeconfig"),
-			helm:        testDeployConfigValuesFilesTemplated,
+			commands: testutil.
+				CmdRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+				AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY -f /some/file-FOOBAR.yaml --kubeconfig kubeconfig"),
+			helm: testDeployConfigValuesFilesTemplated,
 			builds: []graph.Artifact{
 				{
 					ImageName: "skaffold-helm",
@@ -1203,15 +1207,18 @@ func TestHelmRender(t *testing.T) {
 			description: "render with templated release name",
 			env:         []string{"USER=user"},
 			commands: testutil.
-				CmdRun("helm --kube-context kubecontext template user-skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --kubeconfig kubeconfig"),
+				CmdRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+				AndRun("helm --kube-context kubecontext template user-skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --kubeconfig kubeconfig"),
 			helm:   testDeployWithTemplatedName,
 			builds: testBuilds,
 		},
 		{
 			description: "render with namespace",
 			shouldErr:   false,
-			commands:    testutil.CmdRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --namespace testReleaseNamespace --kubeconfig kubeconfig"),
-			helm:        testDeployNamespacedConfig,
+			commands: testutil.
+				CmdRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+				AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --namespace testReleaseNamespace --kubeconfig kubeconfig"),
+			helm: testDeployNamespacedConfig,
 			builds: []graph.Artifact{
 				{
 					ImageName: "skaffold-helm",
@@ -1221,8 +1228,10 @@ func TestHelmRender(t *testing.T) {
 		{
 			description: "render with namespace",
 			shouldErr:   false,
-			commands:    testutil.CmdRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --namespace testReleaseFOOBARNamespace --kubeconfig kubeconfig"),
-			helm:        testDeployEnvTemplateNamespacedConfig,
+			commands: testutil.
+				CmdRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+				AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --namespace testReleaseFOOBARNamespace --kubeconfig kubeconfig"),
+			helm: testDeployEnvTemplateNamespacedConfig,
 			builds: []graph.Artifact{
 				{
 					ImageName: "skaffold-helm",
@@ -1232,8 +1241,10 @@ func TestHelmRender(t *testing.T) {
 		{
 			description: "render with remote repo",
 			shouldErr:   false,
-			commands:    testutil.CmdRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --repo https://charts.helm.sh/stable --kubeconfig kubeconfig"),
-			helm:        testDeployConfigRemoteRepo,
+			commands: testutil.
+				CmdRun("helm --kube-context kubecontext dep build examples/test --kubeconfig kubeconfig").
+				AndRun("helm --kube-context kubecontext template skaffold-helm examples/test --post-renderer SKAFFOLD-BINARY --set some.key=somevalue --repo https://charts.helm.sh/stable --kubeconfig kubeconfig"),
+			helm: testDeployConfigRemoteRepo,
 			builds: []graph.Artifact{
 				{
 					ImageName: "skaffold-helm",
@@ -1243,14 +1254,23 @@ func TestHelmRender(t *testing.T) {
 		{
 			description: "render with remote chart",
 			shouldErr:   false,
-			commands:    testutil.CmdRun("helm --kube-context kubecontext template skaffold-helm-remote stable/chartmuseum --repo https://charts.helm.sh/stable --kubeconfig kubeconfig"),
-			helm:        testDeployRemoteChart,
+			commands: testutil.
+				CmdRun("helm --kube-context kubecontext template skaffold-helm-remote stable/chartmuseum --repo https://charts.helm.sh/stable --kubeconfig kubeconfig"),
+			helm: testDeployRemoteChart,
 		},
 		{
 			description: "render with remote chart with version",
 			shouldErr:   false,
-			commands:    testutil.CmdRun("helm --kube-context kubecontext template skaffold-helm-remote stable/chartmuseum --version 1.0.0 --repo https://charts.helm.sh/stable --kubeconfig kubeconfig"),
-			helm:        testDeployRemoteChartVersion,
+			commands: testutil.
+				CmdRun("helm --kube-context kubecontext template skaffold-helm-remote stable/chartmuseum --version 1.0.0 --repo https://charts.helm.sh/stable --kubeconfig kubeconfig"),
+			helm: testDeployRemoteChartVersion,
+		},
+		{
+			description: "render without building chart dependencies",
+			shouldErr:   false,
+			commands: testutil.
+				CmdRun("helm --kube-context kubecontext template skaffold-helm stable/chartmuseum --kubeconfig kubeconfig"),
+			helm: testDeploySkipBuildDependencies,
 		},
 		// https://github.com/GoogleContainerTools/skaffold/issues/7595
 		// {
diff --git a/pkg/skaffold/instrumentation/export_test.go b/pkg/skaffold/instrumentation/export_test.go
index 7e04d0429..7b8cff6db 100644
--- a/pkg/skaffold/instrumentation/export_test.go
+++ b/pkg/skaffold/instrumentation/export_test.go
@@ -31,6 +31,7 @@ import (
 	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
 
 	"github.com/GoogleContainerTools/skaffold/v2/fs"
+	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/instrumentation/firelog"
 	"github.com/GoogleContainerTools/skaffold/v2/proto/v1"
 	"github.com/GoogleContainerTools/skaffold/v2/testutil"
 )
@@ -105,7 +106,7 @@ func TestExportMetrics(t *testing.T) {
 		StartTime:                    startTime.Add(time.Hour * 24 * 10),
 		Duration:                     time.Minute * 4,
 	}
-	metersBytes, _ := json.Marshal([]skaffoldMeter{buildMeter, devMeter, debugMeter})
+	metersBytes, _ := json.Marshal([]skaffoldMeter{devMeter, debugMeter, buildMeter})
 	fakeFS := testutil.FakeFileSystem{
 		Files: map[string][]byte{
 			"assets/secrets_generated/keys.json": []byte(testKey),
@@ -168,6 +169,7 @@ func TestExportMetrics(t *testing.T) {
 
 			fs.AssetsFS = fakeFS
 			t.Override(&isOnline, test.isOnline)
+			t.Override(&firelog.APIKey, "no-empty")
 
 			if test.isOnline {
 				tmpFile, err := os.OpenFile(tmp.Path(openTelFilename), os.O_RDWR|os.O_CREATE, os.ModePerm)
@@ -406,6 +408,7 @@ func checkOutput(t *testutil.T, meters []skaffoldMeter, b []byte) {
 	platform := make(map[interface{}]int)
 	buildPlatforms := make(map[interface{}]int)
 	cliPlatforms := make(map[interface{}]int)
+	ciCDPlatforms := make(map[interface{}]int)
 	nodePlatforms := make(map[interface{}]int)
 
 	testMaps := []map[interface{}]int{
@@ -419,6 +422,7 @@ func checkOutput(t *testutil.T, meters []skaffoldMeter, b []byte) {
 		commandCount[meter.Command]++
 		errorCount[meter.ErrorCode.String()]++
 		platform[meter.PlatformType]++
+		ciCDPlatforms[meter.CISystem]++
 
 		for k, v := range meter.EnumFlags {
 			n := strings.ReplaceAll(k, "-", "_")
@@ -485,10 +489,6 @@ func checkOutput(t *testutil.T, meters []skaffoldMeter, b []byte) {
 			}
 		}
 	}
-	for _, l := range lines {
-		fmt.Println(l.Name)
-		fmt.Println(l.Labels)
-	}
 
 	for _, l := range lines {
 		switch l.Name {
@@ -578,6 +578,10 @@ func checkOutput(t *testutil.T, meters []skaffoldMeter, b []byte) {
 			if v, ok := l.Labels["cli-platforms"]; ok {
 				cliPlatforms[v]++
 			}
+		case "ci-cd-platforms":
+			if v, ok := l.Labels["ci-cd-platforms"]; ok {
+				ciCDPlatforms[v]++
+			}
 		default:
 			switch {
 			case MeteredCommands.Contains(l.Name):
diff --git a/pkg/skaffold/instrumentation/firelog/exporter_test.go b/pkg/skaffold/instrumentation/firelog/exporter_test.go
new file mode 100644
index 000000000..802578b37
--- /dev/null
+++ b/pkg/skaffold/instrumentation/firelog/exporter_test.go
@@ -0,0 +1,272 @@
+/*
+Copyright 2020 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 firelog
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"net/http"
+	"testing"
+	"time"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/sdk/metric"
+
+	"github.com/GoogleContainerTools/skaffold/v2/testutil"
+)
+
+func TestNewFireLogExporter(t *testing.T) {
+	var tests = []struct {
+		name     string
+		expected metric.Exporter
+		apiKey   string
+		wantErr  bool
+	}{
+		{
+			name:     "no api key",
+			expected: nil,
+		},
+		{
+			name:     "has api key",
+			expected: &Exporter{},
+			apiKey:   "test key",
+		},
+	}
+	for _, test := range tests {
+		testutil.Run(t, test.name, func(t *testutil.T) {
+			APIKey = test.apiKey
+			out, err := NewFireLogExporter()
+			t.CheckError(test.wantErr, err)
+			t.CheckDeepEqual(test.expected, out)
+		})
+	}
+}
+func TestToEventMetadata(t *testing.T) {
+	tests := []struct {
+		name       string
+		attributes attribute.Set
+		expected   EventMetadata
+	}{
+		{
+			name:       "no attributes",
+			attributes: attribute.Set{},
+			expected:   EventMetadata{},
+		},
+		{
+			name:       "one attribute",
+			attributes: attribute.NewSet(attribute.String("key1", "value1")),
+			expected: EventMetadata{
+				KeyValue{
+					Key:   "key1",
+					Value: "value1",
+				},
+			},
+		},
+		{
+			name: "two attributes",
+			attributes: attribute.NewSet(attribute.String("key1", "value1"),
+				attribute.String("key2", "value2")),
+			expected: EventMetadata{
+				KeyValue{
+					Key:   "key1",
+					Value: "value1",
+				},
+				KeyValue{
+					Key:   "key2",
+					Value: "value2",
+				},
+			},
+		},
+		{
+			name: "two attributes mixed types",
+			attributes: attribute.NewSet(attribute.String("key1", "value1"),
+				attribute.Int("key2", 50)),
+			expected: EventMetadata{
+				KeyValue{
+					Key:   "key1",
+					Value: "value1",
+				},
+				KeyValue{
+					Key:   "key2",
+					Value: "50",
+				},
+			},
+		},
+	}
+	for _, test := range tests {
+		testutil.Run(t, test.name, func(t *testutil.T) {
+			actual := toEventMetadata(test.attributes)
+			t.CheckDeepEqual(test.expected, actual)
+		})
+	}
+}
+
+func TestBuildMetricData(t *testing.T) {
+	tests := []struct {
+		name        string
+		proto       string
+		startTimeMS int64
+		upTimeMS    int64
+		expected    MetricData
+	}{
+		{
+			name:        "build metrics data",
+			proto:       "{foo:a}",
+			startTimeMS: 100,
+			upTimeMS:    200,
+			expected: MetricData{
+				ClientInfo: ClientInfo{ClientType: "DESKTOP"},
+				LogSource:  "CONCORD",
+				LogEvent: LogEvent{
+					EventTimeMS:                  100,
+					EventUptimeMS:                200,
+					SourceExtensionJSONProto3Str: "{foo:a}",
+				},
+				RequestTimeMS:   100,
+				RequestUptimeMS: 200,
+			},
+		},
+	}
+
+	for _, test := range tests {
+		testutil.Run(t, test.name, func(t *testutil.T) {
+			actual := buildMetricData(test.proto, test.startTimeMS, test.upTimeMS)
+			t.CheckDeepEqual(test.expected, actual)
+		})
+	}
+}
+
+func TestBuildProtoStr(t *testing.T) {
+	tests := []struct {
+		name        string
+		eventName   string
+		kvs         EventMetadata
+		expected    string
+		wantErr     bool
+		marshalFunc func(v any) ([]byte, error)
+	}{
+		{
+			name:      "no kvs",
+			eventName: "no kvs",
+			kvs:       EventMetadata{},
+			expected:  `{"project_id":"skaffold","console_type":"SKAFFOLD","client_install_id":"","event_name":"no kvs","event_metadata":[]}`,
+			wantErr:   false,
+		},
+		{
+			name:      "with kvs",
+			eventName: "with kvs",
+			kvs: EventMetadata{
+				{"key1", "value1"},
+				{"key2", "value2"},
+			},
+			expected: `{"project_id":"skaffold","console_type":"SKAFFOLD","client_install_id":"","event_name":"with kvs","event_metadata":[{"key":"key1","value":"value1"},{"key":"key2","value":"value2"}]}`,
+			wantErr:  false,
+		},
+		{
+			name: "fail to marshal",
+			kvs: EventMetadata{
+				{"key1", "value1"},
+				{"key2", "value2"},
+			},
+			expected: "",
+			marshalFunc: func(v any) ([]byte, error) {
+				return nil, fmt.Errorf("failed to marshal")
+			},
+			wantErr: true,
+		},
+	}
+
+	for _, test := range tests {
+		testutil.Run(t, test.name, func(t *testutil.T) {
+			if test.marshalFunc != nil {
+				t.Override(&Marshal, test.marshalFunc)
+			}
+			out, err := buildProtoStr(test.eventName, test.kvs)
+			t.CheckErrorAndDeepEqual(test.wantErr, err, test.expected, out)
+		})
+	}
+}
+
+func TestSendDataPoint(t *testing.T) {
+	tests := []struct {
+		name      string
+		dp        DataPoint
+		POSTFunc  func(url, contentType string, body io.Reader) (resp *http.Response, err error)
+		shouldErr bool
+	}{
+		{
+			name: "no attributes",
+			dp: DataPointInt64{
+				Value:      10,
+				Attributes: attribute.NewSet(),
+				StartTime:  time.UnixMilli(123),
+				Time:       time.UnixMilli(123),
+			},
+			POSTFunc: func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
+				responseBody := io.NopCloser(bytes.NewReader([]byte(`{"value":"fixed"}`)))
+				return &http.Response{
+					StatusCode: http.StatusOK,
+					Body:       responseBody,
+				}, nil
+			},
+			shouldErr: false,
+		},
+		{
+			name: "with attributes",
+			dp: DataPointInt64{
+				Value:      10,
+				Attributes: attribute.NewSet(attribute.Int("iteration", 10)),
+				StartTime:  time.UnixMilli(123),
+				Time:       time.UnixMilli(123),
+			},
+			POSTFunc: func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
+				responseBody := io.NopCloser(bytes.NewReader([]byte(`{"value":"fixed"}`)))
+				return &http.Response{
+					StatusCode: http.StatusOK,
+					Body:       responseBody,
+				}, nil
+			},
+			shouldErr: false,
+		},
+		{
+			name: "http non-200",
+			dp: DataPointInt64{
+				Value:      10,
+				Attributes: attribute.NewSet(attribute.Int("iteration", 10)),
+				StartTime:  time.UnixMilli(123),
+				Time:       time.UnixMilli(123),
+			},
+			POSTFunc: func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
+				responseBody := io.NopCloser(bytes.NewReader([]byte(`{"value":"fixed"}`)))
+				return &http.Response{
+					StatusCode: http.StatusBadRequest,
+					Body:       responseBody,
+				}, nil
+			},
+			shouldErr: true,
+		},
+	}
+
+	for _, test := range tests {
+		testutil.Run(t, test.name, func(t *testutil.T) {
+			t.Override(&POST, test.POSTFunc)
+			err := sendDataPoint(test.name, test.dp)
+			t.CheckError(test.shouldErr, err)
+		})
+	}
+}
diff --git a/pkg/skaffold/instrumentation/meter_test.go b/pkg/skaffold/instrumentation/meter_test.go
new file mode 100644
index 000000000..073edc109
--- /dev/null
+++ b/pkg/skaffold/instrumentation/meter_test.go
@@ -0,0 +1,62 @@
+/*
+Copyright 2020 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 instrumentation
+
+import (
+	"testing"
+
+	"github.com/GoogleContainerTools/skaffold/v2/testutil"
+)
+
+func TestCISystem(t *testing.T) {
+	tests := []struct {
+		name     string
+		envs     map[string]string
+		expected string
+	}{
+		{
+			name:     "no CI variables",
+			envs:     map[string]string{},
+			expected: "no-ci",
+		},
+		{
+			name:     "CI variable set",
+			envs:     map[string]string{"CI": "AlphaCI"},
+			expected: "AlphaCI",
+		},
+		{
+			name:     "BUILD_ID variable set",
+			envs:     map[string]string{"BUILD_ID": "12345"},
+			expected: "unknown",
+		},
+		{
+			name:     "BUILD_NUMBER variable set",
+			envs:     map[string]string{"BUILD_NUMBER": "12345"},
+			expected: "unknown",
+		},
+	}
+
+	for _, test := range tests {
+		testutil.Run(t, test.name, func(t *testutil.T) {
+			t.UnsetEnv("GITHUB_ACTIONS")
+			t.UnsetEnv("CI")
+			t.SetEnvs(test.envs)
+			actual := CISystem()
+			t.CheckDeepEqual(test.expected, actual)
+		})
+	}
+}
diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go
index 2025303a8..88fb77620 100644
--- a/pkg/skaffold/schema/latest/config.go
+++ b/pkg/skaffold/schema/latest/config.go
@@ -25,7 +25,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/util"
 )
 
-// This config version is not yet released, it is SAFE TO MODIFY the structs in this file.
+// !!! WARNING !!! This config version is already released, please DO NOT MODIFY the structs in this file.
 const Version string = "skaffold/v4beta4"
 
 // NewSkaffoldConfig creates a SkaffoldConfig
diff --git a/testutil/env.go b/testutil/env.go
index 4c32bc797..7509971eb 100644
--- a/testutil/env.go
+++ b/testutil/env.go
@@ -16,6 +16,8 @@ limitations under the License.
 
 package testutil
 
+import "os"
+
 // SetEnvs takes a map of key values to set using t.Setenv and restore
 // the environment variable to its original value after the test.
 func (t *T) SetEnvs(envs map[string]string) {
@@ -23,3 +25,15 @@ func (t *T) SetEnvs(envs map[string]string) {
 		t.Setenv(key, value)
 	}
 }
+
+func (t *T) UnsetEnv(key string) {
+	prevValue, ok := os.LookupEnv(key)
+	if ok {
+		if err := os.Unsetenv(key); err != nil {
+			t.Fatalf("cannot unset environment variable: %v", err)
+		}
+		t.Cleanup(func() {
+			os.Setenv(key, prevValue)
+		})
+	}
+}