From 16aff7419fb25397ba9f144cb95b619cc1369745 Mon Sep 17 00:00:00 2001
From: Derek <dschaller@users.noreply.github.com>
Date: Thu, 28 Oct 2021 00:19:20 -0700
Subject: [PATCH] feat(yarn): ignore yarn-path if binary does not exist
 (#12322)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 lib/manager/npm/post-update/yarn.spec.ts | 34 ++++++++++++++++++++++++
 lib/manager/npm/post-update/yarn.ts      | 11 +++++++-
 2 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/lib/manager/npm/post-update/yarn.spec.ts b/lib/manager/npm/post-update/yarn.spec.ts
index 8383acc0ba..9f6d1ec8f9 100644
--- a/lib/manager/npm/post-update/yarn.spec.ts
+++ b/lib/manager/npm/post-update/yarn.spec.ts
@@ -223,6 +223,12 @@ describe('manager/npm/post-update/yarn', () => {
 
   describe('checkYarnrc()', () => {
     it('returns offline mirror and yarn path', async () => {
+      fs.exists.mockImplementation((path) => {
+        if (path === './.yarn/cli.js') {
+          return new Promise<boolean>((resolve) => resolve(true));
+        }
+        return new Promise<boolean>((resolve) => resolve(false));
+      });
       fs.readFile.mockImplementation((filename, encoding) => {
         if (filename.endsWith('.yarnrc')) {
           return new Promise<string>((resolve) =>
@@ -238,6 +244,12 @@ describe('manager/npm/post-update/yarn', () => {
     });
 
     it('returns no offline mirror and unquoted yarn path', async () => {
+      fs.exists.mockImplementation((path) => {
+        if (path === './.yarn/cli.js') {
+          return new Promise<boolean>((resolve) => resolve(true));
+        }
+        return new Promise<boolean>((resolve) => resolve(false));
+      });
       fs.readFile.mockImplementation((filename, encoding) => {
         if (filename.endsWith('.yarnrc')) {
           return new Promise<string>((resolve) =>
@@ -249,5 +261,27 @@ describe('manager/npm/post-update/yarn', () => {
       // FIXME: explicit assert condition
       expect(await _yarnHelper.checkYarnrc('/tmp/renovate')).toMatchSnapshot();
     });
+
+    it('returns offline mirror and no yarn path for non-existant yarn-path binary', async () => {
+      let yarnrcContents = 'yarn-path ./.yarn/cli.js\n';
+      fs.writeFile.mockImplementation((filename, fileContents) => {
+        if (filename.endsWith('.yarnrc')) {
+          yarnrcContents = fileContents;
+        }
+        return new Promise<void>((resolve) => resolve());
+      });
+      fs.readFile.mockImplementation((filename, encoding) => {
+        if (filename.endsWith('.yarnrc')) {
+          return new Promise<string>((resolve) => resolve(yarnrcContents));
+        }
+        return new Promise<string>((resolve) => resolve(''));
+      });
+      const { offlineMirror, yarnPath } = await _yarnHelper.checkYarnrc(
+        '/tmp/renovate'
+      );
+      expect(offlineMirror).toBeFalse();
+      expect(yarnPath).toBeNull();
+      expect(yarnrcContents).not.toContain('yarn-path');
+    });
   });
 });
diff --git a/lib/manager/npm/post-update/yarn.ts b/lib/manager/npm/post-update/yarn.ts
index d6a517b1a3..24120d5724 100644
--- a/lib/manager/npm/post-update/yarn.ts
+++ b/lib/manager/npm/post-update/yarn.ts
@@ -11,7 +11,7 @@ import { id as npmId } from '../../../datasource/npm';
 import { logger } from '../../../logger';
 import { ExternalHostError } from '../../../types/errors/external-host-error';
 import { ExecOptions, exec } from '../../../util/exec';
-import { readFile, remove } from '../../../util/fs';
+import { exists, readFile, remove, writeFile } from '../../../util/fs';
 import { regEx } from '../../../util/regex';
 import type { PostUpdateConfig, Upgrade } from '../../types';
 import { getNodeConstraint } from './node-version';
@@ -35,6 +35,15 @@ export async function checkYarnrc(
       if (pathLine) {
         yarnPath = pathLine.replace(regEx(/^yarn-path\s+"?(.+?)"?$/), '$1');
       }
+      const yarnBinaryExists = await exists(yarnPath);
+      if (!yarnBinaryExists) {
+        const scrubbedYarnrc = yarnrc.replace(
+          regEx(/^yarn-path\s+"?.+?"?$/gm),
+          ''
+        );
+        await writeFile(`${cwd}/.yarnrc`, scrubbedYarnrc);
+        yarnPath = null;
+      }
     }
   } catch (err) /* istanbul ignore next */ {
     // not found
-- 
GitLab