Repository: codecov/uploader
Pull request #1250 (2023-10-20 21:16:20)

feat: add user agent to requests to the codecov api
add `User-Agent: codecov-uploader/{version}` header to outgoing requests to the codecov api so we can track API usage compared to gazebo and `codecov-cli` or by uploader version

it appears we send a `package` param to a legacy upload endpoint with this information already which can tell us what proportion of that endpoint's traffic is from this uploader, but setting a user agent across the board tells us for all endpoints, or what proportion of overall API traffic is from this uploader

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/codecov/uploader/pull/1250?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) Report
> Merging [#1250](https://app.codecov.io/gh/codecov/uploader/pull/1250?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) (8b38a6d) into [main](https://app.codecov.io/gh/codecov/uploader/commit/e60489bd1ef452d4d492241ed7924257b8775aae?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) (e60489b) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##             main    #1250   +/-   ##
=======================================
  Coverage   92.48%   92.48%           
=======================================
  Files          36       36           
  Lines        1357     1358    +1     
  Branches      273      273           
=======================================
+ Hits         1255     1256    +1     
  Misses         70       70           
  Partials       32       32           
```

| [Flag](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | Coverage Δ | |
|---|---|---|
| [aarch64](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [aarch64-without-git](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [alpine](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [alpine-proxy](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [alpine-without-git](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [linux](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [linux-without-git](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [macos](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [macos-without-git](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `92.48% <100.00%> (+<0.01%)` | :arrow_up: |
| [windows](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `?` | |
| [windows-without-git](https://app.codecov.io/gh/codecov/uploader/pull/1250/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files](https://app.codecov.io/gh/codecov/uploader/pull/1250?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | Coverage Δ | |
|---|---|---|
| [src/helpers/web.ts](https://app.codecov.io/gh/codecov/uploader/pull/1250?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov#diff-c3JjL2hlbHBlcnMvd2ViLnRz) | `85.18% <100.00%> (+0.18%)` | :arrow_up: |


Patch:
diff --git a/src/helpers/web.ts b/src/helpers/web.ts
index 7e5617f..9e5b5ce 100644
--- a/src/helpers/web.ts
+++ b/src/helpers/web.ts
@@ -19,6 +19,7 @@ import { sleep } from './util'
 
 const maxRetries = 4
 const baseBackoffDelayMs = 1000 // Adjust this value based on your needs.
+export const userAgent: string = `codecov-uploader/${version}`
 
 /**
  *
@@ -209,9 +210,10 @@ export function generateRequestHeadersPOST(
     postURL,
   )
 
-  const headers = {
+  const headers: Record<string, string> = {
     'X-Upload-Token': token,
     'X-Reduced-Redundancy': 'false',
+    'User-Agent': userAgent,
   }
 
   return {
@@ -232,9 +234,10 @@ export function generateRequestHeadersPUT(
   envs: UploaderEnvs,
   args: UploaderArgs,
 ): IRequestHeaders {
-  const headers = {
+  const headers: Record<string, string> = {
     'Content-Type': 'text/plain',
     'Content-Encoding': 'gzip',
+    'User-Agent': userAgent,
   }
 
   return {


Test patch:
diff --git a/test/helpers/web.test.ts b/test/helpers/web.test.ts
index 5788f97..247d203 100644
--- a/test/helpers/web.test.ts
+++ b/test/helpers/web.test.ts
@@ -17,12 +17,17 @@ import {
   populateBuildParams,
   uploadToCodecovPOST,
   uploadToCodecovPUT,
+  userAgent,
 } from '../../src/helpers/web'
 import { IServiceParams, PostResults, UploaderArgs, UploaderEnvs } from '../../src/types.js'
 import { createEmptyArgs } from '../test_helpers'
 
 import * as utilModule from '../../src/helpers/util'
+import { IncomingHttpHeaders } from 'undici/types/header'
 
+function hasUserAgent(headers: IncomingHttpHeaders | string[]): headers is IncomingHttpHeaders {
+  return "User-Agent" in headers
+}
 
 describe('Web Helpers', () => {
   let uploadURL: string
@@ -544,6 +549,13 @@ describe('generateRequestHeadersPOST()', () => {
         'G',
       )}&token=134&slug=testOrg/testUploader`,
     )
+
+    const headers = requestHeaders.options.headers!
+    expect(hasUserAgent(headers)).toEqual(true)
+    if (hasUserAgent(headers)) {
+      expect(headers['User-Agent']).toEqual(userAgent)
+    }
+
     expect(typeof requestHeaders.options.body).toEqual('undefined')
     expect(typeof requestHeaders.agent).toEqual('undefined')
   })
@@ -566,6 +578,12 @@ describe('generateRequestHeadersPOST()', () => {
       )}&token=134&slug=testOrg/testUploader`,
     )
 
+    const headers = requestHeaders.options.headers!
+    expect(hasUserAgent(headers)).toEqual(true)
+    if (hasUserAgent(headers)) {
+      expect(headers['User-Agent']).toEqual(userAgent)
+    }
+
     expect(typeof requestHeaders.options.body).toEqual('undefined')
     expect(requestHeaders.agent).toBeInstanceOf(ProxyAgent)
   })
@@ -585,6 +603,13 @@ describe('generateRequestHeadersPUT()', () => {
     )
 
     expect(requestHeaders.url.href).toEqual('https://localhost.local/')
+
+    const headers = requestHeaders.options.headers!
+    expect(hasUserAgent(headers)).toEqual(true)
+    if (hasUserAgent(headers)) {
+      expect(headers['User-Agent']).toEqual(userAgent)
+    }
+
     expect(requestHeaders.options.body).toEqual("I'm a coverage report!")
     expect(typeof requestHeaders.agent).toEqual('undefined')
   })
@@ -600,6 +625,13 @@ describe('generateRequestHeadersPUT()', () => {
     )
 
     expect(requestHeaders.url.href).toEqual('https://localhost.local/')
+
+    const headers = requestHeaders.options.headers!
+    expect(hasUserAgent(headers)).toEqual(true)
+    if (hasUserAgent(headers)) {
+      expect(headers['User-Agent']).toEqual(userAgent)
+    }
+
     expect(requestHeaders.options.body).toEqual("I'm a coverage report!")
     expect(requestHeaders.agent).toBeInstanceOf(ProxyAgent)
   })