From c3d08f7f8be712d1c6e377afb40452a1cd61f630 Mon Sep 17 00:00:00 2001
From: chris48s <chris48s@users.noreply.github.com>
Date: Sat, 31 Dec 2022 15:56:04 +0000
Subject: [PATCH] Send `X-GitHub-Api-Version` when calling [GitHub] v3 API
 (#8669)

* send X-GitHub-Api-Version when calling GitHub v3 API

* TODO: invesitgate

* read baseUrl from config.service.baseUri

* add workflow to check for new GH api releases on schedule

* format config/default.yml to match yaml.dump() format

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
---
 .github/workflows/update-github-api.yml     | 33 +++++++++++++++++++++
 config/default.yml                          | 12 ++------
 core/server/server.js                       |  1 +
 scripts/update-github-api.js                | 19 ++++++++++++
 services/github/github-api-provider.js      |  3 ++
 services/github/github-auth-service.spec.js |  2 ++
 services/github/github-constellation.js     |  3 +-
 7 files changed, 62 insertions(+), 11 deletions(-)
 create mode 100644 .github/workflows/update-github-api.yml
 create mode 100644 scripts/update-github-api.js

diff --git a/.github/workflows/update-github-api.yml b/.github/workflows/update-github-api.yml
new file mode 100644
index 0000000000..b25a2db23f
--- /dev/null
+++ b/.github/workflows/update-github-api.yml
@@ -0,0 +1,33 @@
+name: Update GitHub API Version
+on:
+  schedule:
+    - cron: '0 7 * * 6'
+    # At 07:00 on Saturday
+  workflow_dispatch:
+
+permissions:
+  pull-requests: write
+  contents: write
+
+jobs:
+  update-github-api:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+
+      - name: Setup
+        uses: ./.github/actions/setup
+        with:
+          node-version: 16
+
+      - name: Check for new GitHub API version
+        run: node scripts/update-github-api.js
+
+      - name: Create Pull Request if config has changed
+        uses: peter-evans/create-pull-request@v4
+        with:
+          token: '${{ secrets.GITHUB_TOKEN }}'
+          commit-message: Update GitHub API Version
+          title: Update [GitHub] API Version
+          branch-suffix: random
diff --git a/config/default.yml b/config/default.yml
index 0a7d5f1743..5a6fb10393 100644
--- a/config/default.yml
+++ b/config/default.yml
@@ -1,7 +1,6 @@
 public:
   bind:
     address: '::'
-
   metrics:
     prometheus:
       enabled: false
@@ -12,33 +11,26 @@ public:
       intervalSeconds: 15
   ssl:
     isSecure: false
-
   cors:
     allowedOrigin: []
-
   services:
     github:
-      baseUri: 'https://api.github.com/'
+      baseUri: 'https://api.github.com'
       debug:
         enabled: false
         intervalSeconds: 200
+      restApiVersion: '2022-11-28'
     obs:
       authorizedOrigins: 'https://api.opensuse.org'
     weblate:
       authorizedOrigins: 'https://hosted.weblate.org'
     trace: false
-
   cacheHeaders:
     defaultCacheLengthSeconds: 120
-
   handleInternalErrors: true
-
   fetchLimit: '10MB'
   userAgentBase: 'shields (self-hosted)'
-
   requestTimeoutSeconds: 120
   requestTimeoutMaxAgeSeconds: 30
-
   requireCloudflare: false
-
 private: {}
diff --git a/core/server/server.js b/core/server/server.js
index 25cb3f9ddc..fcc6de4f2a 100644
--- a/core/server/server.js
+++ b/core/server/server.js
@@ -126,6 +126,7 @@ const publicConfigSchema = Joi.object({
         enabled: Joi.boolean().required(),
         intervalSeconds: Joi.number().integer().min(1).required(),
       },
+      restApiVersion: Joi.date().raw().required(),
     },
     gitlab: defaultService,
     jira: defaultService,
diff --git a/scripts/update-github-api.js b/scripts/update-github-api.js
new file mode 100644
index 0000000000..d66f9426c0
--- /dev/null
+++ b/scripts/update-github-api.js
@@ -0,0 +1,19 @@
+import fs from 'fs/promises'
+import got from 'got'
+import yaml from 'js-yaml'
+
+const resp = await got('https://api.github.com/versions').json()
+const latestDate = resp.sort()[resp.length - 1]
+
+const config = yaml.load(await fs.readFile('./config/default.yml', 'utf8'))
+
+if (latestDate === config.public.services.github.restApiVersion) {
+  console.log("We're already using the latest version. No change needed.")
+  process.exit(0)
+}
+
+config.public.services.github.restApiVersion = latestDate
+await fs.writeFile(
+  './config/default.yml',
+  yaml.dump(config, { forceQuotes: true })
+)
diff --git a/services/github/github-api-provider.js b/services/github/github-api-provider.js
index 96cb7a6f06..0c28521d85 100644
--- a/services/github/github-api-provider.js
+++ b/services/github/github-api-provider.js
@@ -41,6 +41,7 @@ class GithubApiProvider {
     onTokenInvalidated = tokenString => {},
     globalToken,
     reserveFraction = 0.25,
+    restApiVersion,
   }) {
     Object.assign(this, {
       baseUrl,
@@ -55,6 +56,7 @@ class GithubApiProvider {
       this.searchTokens = new TokenPool({ batchSize: 5 })
       this.graphqlTokens = new TokenPool({ batchSize: 25 })
     }
+    this.restApiVersion = restApiVersion
   }
 
   addToken(tokenString) {
@@ -175,6 +177,7 @@ class GithubApiProvider {
         headers: {
           'User-Agent': userAgent,
           Authorization: `token ${tokenString}`,
+          'X-GitHub-Api-Version': this.restApiVersion,
           ...options.headers,
         },
       },
diff --git a/services/github/github-auth-service.spec.js b/services/github/github-auth-service.spec.js
index 3569fba848..0e931bd4bb 100644
--- a/services/github/github-auth-service.spec.js
+++ b/services/github/github-auth-service.spec.js
@@ -41,6 +41,7 @@ describe('GithubAuthV3Service', function () {
     )
     const githubApiProvider = new GithubApiProvider({
       baseUrl: 'https://github-api.example.com',
+      restApiVersion: '2022-11-28',
     })
     const mockToken = { update: sinon.mock(), invalidate: sinon.mock() }
     sinon.stub(githubApiProvider.standardTokens, 'next').returns(mockToken)
@@ -57,6 +58,7 @@ describe('GithubAuthV3Service', function () {
           'User-Agent': 'shields (self-hosted)/dev',
           Accept: 'application/vnd.github.antiope-preview+json',
           Authorization: 'token undefined',
+          'X-GitHub-Api-Version': '2022-11-28',
         },
       }
     )
diff --git a/services/github/github-constellation.js b/services/github/github-constellation.js
index 429f8a9fde..dd4016b496 100644
--- a/services/github/github-constellation.js
+++ b/services/github/github-constellation.js
@@ -33,10 +33,11 @@ class GithubConstellation {
     }
 
     this.apiProvider = new GithubApiProvider({
-      baseUrl: process.env.GITHUB_URL || 'https://api.github.com',
+      baseUrl: config.service.baseUri,
       globalToken,
       withPooling: !globalToken,
       onTokenInvalidated: tokenString => this.onTokenInvalidated(tokenString),
+      restApiVersion: config.service.restApiVersion,
     })
 
     this.oauthHelper = this.constructor._createOauthHelper(config)
-- 
GitLab