From b5b714d4b525f96d1469b83e26a2a7a24d6cd504 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@keylocation.sg>
Date: Thu, 14 Dec 2017 11:47:00 +0100
Subject: [PATCH] fix: writeToken -> forkToken (#1295)

Rename writeToken to forkToken to make it clearer. This enables GitHub API to use one token for all reads, and another token just for writing to the forked repository.
---
 lib/config/validation.js                      |  2 +-
 lib/logger/config-serializer.js               |  2 +-
 lib/platform/github/gh-got-wrapper.js         | 14 +---
 lib/platform/github/index.js                  | 67 +++++++++++++++----
 lib/workers/repository/init/apis.js           |  2 +-
 .../__snapshots__/gh-got-wrapper.spec.js.snap | 14 ----
 .../github/__snapshots__/index.spec.js.snap   |  2 +
 test/platform/github/gh-got-wrapper.spec.js   |  6 --
 8 files changed, 61 insertions(+), 48 deletions(-)
 delete mode 100644 test/platform/github/__snapshots__/gh-got-wrapper.spec.js.snap

diff --git a/lib/config/validation.js b/lib/config/validation.js
index 02a7f0ca5e..673ebf8dd5 100644
--- a/lib/config/validation.js
+++ b/lib/config/validation.js
@@ -18,7 +18,7 @@ function validateConfig(config) {
   let warnings = [];
 
   function isIgnored(key) {
-    const ignoredNodes = ['depType', 'npmToken', 'packageFile', 'writeToken'];
+    const ignoredNodes = ['depType', 'npmToken', 'packageFile', 'forkToken'];
     return ignoredNodes.indexOf(key) !== -1;
   }
 
diff --git a/lib/logger/config-serializer.js b/lib/logger/config-serializer.js
index 0bab777969..5cbecb5188 100644
--- a/lib/logger/config-serializer.js
+++ b/lib/logger/config-serializer.js
@@ -11,7 +11,7 @@ function configSerializer(config) {
     'yarnrc',
     'privateKey',
     'gitPrivateKey',
-    'writeToken',
+    'forkToken',
   ];
   const templateFields = ['commitMessage', 'prTitle', 'prBody'];
   // eslint-disable-next-line array-callback-return
diff --git a/lib/platform/github/gh-got-wrapper.js b/lib/platform/github/gh-got-wrapper.js
index f418e356bc..3d72ca8506 100644
--- a/lib/platform/github/gh-got-wrapper.js
+++ b/lib/platform/github/gh-got-wrapper.js
@@ -1,7 +1,6 @@
 const ghGot = require('gh-got');
 const parseLinkHeader = require('parse-link-header');
 
-let writeToken;
 let cache = {};
 
 // istanbul ignore next
@@ -117,14 +116,8 @@ async function get(path, opts, retries = 5) {
 const helpers = ['get', 'post', 'put', 'patch', 'head', 'delete'];
 
 for (const x of helpers) {
-  // eslint-disable-next-line no-loop-func
-  get[x] = (url, opts) => {
-    const options = Object.assign({}, opts, { method: x.toUpperCase() });
-    if (writeToken && ['post', 'put', 'patch', 'delete'].includes(x)) {
-      options.token = writeToken;
-    }
-    return get(url, options);
-  };
+  get[x] = (url, opts) =>
+    get(url, Object.assign({}, opts, { method: x.toUpperCase() }));
 }
 
 let appMode = false;
@@ -132,9 +125,8 @@ get.setAppMode = function setAppMode(val) {
   appMode = val;
 };
 
-get.reset = function reset(token) {
+get.reset = function reset() {
   cache = {};
-  writeToken = token;
 };
 
 module.exports = get;
diff --git a/lib/platform/github/index.js b/lib/platform/github/index.js
index 142bb9b0c1..29f37f1947 100644
--- a/lib/platform/github/index.js
+++ b/lib/platform/github/index.js
@@ -67,7 +67,7 @@ async function getRepos(token, endpoint) {
 }
 
 // Initialize GitHub by getting base branch and SHA
-async function initRepo(repoName, token, endpoint, forkMode, writeToken) {
+async function initRepo(repoName, token, endpoint, forkMode, forkToken) {
   logger.debug(`initRepo("${repoName}")`);
   if (token) {
     process.env.GITHUB_TOKEN = token;
@@ -78,7 +78,7 @@ async function initRepo(repoName, token, endpoint, forkMode, writeToken) {
     process.env.GITHUB_ENDPOINT = endpoint;
   }
   config = {};
-  get.reset(writeToken);
+  get.reset();
   config.repoName = repoName;
   const platformConfig = {};
   let res;
@@ -111,6 +111,7 @@ async function initRepo(repoName, token, endpoint, forkMode, writeToken) {
   await Promise.all([getPrList(), getFileList()]);
   if (forkMode) {
     logger.info('Renovate is in forkMode');
+    config.forkToken = forkToken;
     // Save parent SHA then delete
     config.parentSha = await getBaseCommitSHA();
     delete config.baseCommitSHA;
@@ -119,11 +120,13 @@ async function initRepo(repoName, token, endpoint, forkMode, writeToken) {
     delete config.repoName;
     // Get list of existing repos
     const existingRepos = (await get('user/repos?per_page=100', {
+      token: forkToken || process.env.GITHUB_TOKEN,
       paginate: true,
     })).body.map(r => r.full_name);
-    config.repoName = (await get.post(
-      `repos/${repoName}/forks`
-    )).body.full_name;
+
+    config.repoName = (await get.post(`repos/${repoName}/forks`, {
+      token: forkToken || process.env.GITHUB_TOKEN,
+    })).body.full_name;
     if (existingRepos.includes(config.repoName)) {
       logger.info({ repository_fork: config.repoName }, 'Found existing fork');
       // Need to update base branch
@@ -137,6 +140,7 @@ async function initRepo(repoName, token, endpoint, forkMode, writeToken) {
           body: {
             sha: config.parentSha,
           },
+          token: forkToken || process.env.GITHUB_TOKEN,
         }
       );
     } else {
@@ -354,7 +358,11 @@ async function setBranchStatus(
 }
 
 async function deleteBranch(branchName) {
-  await get.delete(`repos/${config.repoName}/git/refs/heads/${branchName}`);
+  const options = config.forkToken ? { token: config.forkToken } : undefined;
+  await get.delete(
+    `repos/${config.repoName}/git/refs/heads/${branchName}`,
+    options
+  );
 }
 
 async function mergeBranch(branchName, mergeType) {
@@ -859,23 +867,36 @@ async function commitFilesToBranch(
 
 // Creates a new branch with provided commit
 async function createBranch(branchName, sha) {
-  await get.post(`repos/${config.repoName}/git/refs`, {
+  const options = {
     body: {
       ref: `refs/heads/${branchName}`,
       sha,
     },
-  });
+  };
+  // istanbul ignore if
+  if (config.forkToken) {
+    options.token = config.forkToken;
+  }
+  await get.post(`repos/${config.repoName}/git/refs`, options);
 }
 
 // Internal: Updates an existing branch to new commit sha
 async function updateBranch(branchName, commit) {
   logger.debug(`Updating branch ${branchName} with commit ${commit}`);
-  await get.patch(`repos/${config.repoName}/git/refs/heads/${branchName}`, {
+  const options = {
     body: {
       sha: commit,
       force: true,
     },
-  });
+  };
+  // istanbul ignore if
+  if (config.forkToken) {
+    options.token = config.forkToken;
+  }
+  await get.patch(
+    `repos/${config.repoName}/git/refs/heads/${branchName}`,
+    options
+  );
 }
 
 // Low-level commit operations
@@ -883,12 +904,18 @@ async function updateBranch(branchName, commit) {
 // Create a blob with fileContents and return sha
 async function createBlob(fileContents) {
   logger.debug('Creating blob');
-  return (await get.post(`repos/${config.repoName}/git/blobs`, {
+  const options = {
     body: {
       encoding: 'base64',
       content: Buffer.from(fileContents).toString('base64'),
     },
-  })).body.sha;
+  };
+  // istanbul ignore if
+  if (config.forkToken) {
+    options.token = config.forkToken;
+  }
+  return (await get.post(`repos/${config.repoName}/git/blobs`, options)).body
+    .sha;
 }
 
 // Return the commit SHA for a branch
@@ -928,7 +955,12 @@ async function createTree(baseTree, files) {
     });
   });
   logger.trace({ body }, 'createTree body');
-  return (await get.post(`repos/${config.repoName}/git/trees`, { body })).body
+  const options = { body };
+  // istanbul ignore if
+  if (config.forkToken) {
+    options.token = config.forkToken;
+  }
+  return (await get.post(`repos/${config.repoName}/git/trees`, options)).body
     .sha;
 }
 
@@ -975,7 +1007,14 @@ async function createCommit(parent, tree, message, gitAuthor, gitPrivateKey) {
       body.signature = signature;
     }
   }
-  return (await get.post(`repos/${config.repoName}/git/commits`, { body })).body
+  const options = {
+    body,
+  };
+  // istanbul ignore if
+  if (config.forkToken) {
+    options.token = config.forkToken;
+  }
+  return (await get.post(`repos/${config.repoName}/git/commits`, options)).body
     .sha;
 }
 
diff --git a/lib/workers/repository/init/apis.js b/lib/workers/repository/init/apis.js
index 95d90c65a9..62a2f4da50 100644
--- a/lib/workers/repository/init/apis.js
+++ b/lib/workers/repository/init/apis.js
@@ -13,7 +13,7 @@ async function getPlatformConfig(config) {
     config.token,
     config.endpoint,
     config.forkMode,
-    config.writeToken
+    config.forkToken
   );
   return {
     ...config,
diff --git a/test/platform/github/__snapshots__/gh-got-wrapper.spec.js.snap b/test/platform/github/__snapshots__/gh-got-wrapper.spec.js.snap
deleted file mode 100644
index bb757516aa..0000000000
--- a/test/platform/github/__snapshots__/gh-got-wrapper.spec.js.snap
+++ /dev/null
@@ -1,14 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`platform/gh-got-wrapper uses writeToken 1`] = `
-Array [
-  Array [
-    "some-url",
-    Object {
-      "body": Object {},
-      "method": "PUT",
-      "token": "some-write-token",
-    },
-  ],
-]
-`;
diff --git a/test/platform/github/__snapshots__/index.spec.js.snap b/test/platform/github/__snapshots__/index.spec.js.snap
index 26d8382c04..f32faa920e 100644
--- a/test/platform/github/__snapshots__/index.spec.js.snap
+++ b/test/platform/github/__snapshots__/index.spec.js.snap
@@ -702,6 +702,7 @@ exports[`platform/github mergeBranch(branchName, mergeType) should perform a bra
 Array [
   Array [
     "repos/some/repo/git/refs/heads/thebranchname",
+    undefined,
   ],
 ]
 `;
@@ -747,6 +748,7 @@ exports[`platform/github mergeBranch(branchName, mergeType) should perform a bra
 Array [
   Array [
     "repos/some/repo/git/refs/heads/thebranchname",
+    undefined,
   ],
 ]
 `;
diff --git a/test/platform/github/gh-got-wrapper.spec.js b/test/platform/github/gh-got-wrapper.spec.js
index c326e4a8e1..6f148dd343 100644
--- a/test/platform/github/gh-got-wrapper.spec.js
+++ b/test/platform/github/gh-got-wrapper.spec.js
@@ -7,7 +7,6 @@ describe('platform/gh-got-wrapper', () => {
   const body = ['a', 'b'];
   beforeEach(() => {
     jest.resetAllMocks();
-    get.reset();
     get.setAppMode(false);
   });
   it('supports app mode', async () => {
@@ -17,11 +16,6 @@ describe('platform/gh-got-wrapper', () => {
       'application/vnd.github.machine-man-preview+json, some-accept'
     );
   });
-  it('uses writeToken', async () => {
-    get.reset('some-write-token');
-    await get.put('some-url', { body: {} });
-    expect(ghGot.mock.calls).toMatchSnapshot();
-  });
   it('paginates', async () => {
     ghGot.mockReturnValueOnce({
       headers: {
-- 
GitLab