From 23e2508897156b76d124a58dd617d3750f55e5cd Mon Sep 17 00:00:00 2001
From: Gabriel-Ladzaretti
 <97394622+Gabriel-Ladzaretti@users.noreply.github.com>
Date: Tue, 20 Sep 2022 10:09:05 +0300
Subject: [PATCH] feat(code/onboarding): infrastructure for onboarding PR
 rebase/retry checkbox (#17673)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
---
 lib/modules/platform/azure/index.spec.ts     |  9 ++--
 lib/modules/platform/azure/index.ts          |  2 +-
 lib/modules/platform/bitbucket/index.spec.ts |  9 ++--
 lib/modules/platform/bitbucket/index.ts      |  2 +-
 lib/modules/platform/pr-body.spec.ts         | 44 ++++++++++++++++++--
 lib/modules/platform/pr-body.ts              | 28 +++++++++++--
 lib/modules/platform/types.ts                |  1 +
 7 files changed, 78 insertions(+), 17 deletions(-)

diff --git a/lib/modules/platform/azure/index.spec.ts b/lib/modules/platform/azure/index.spec.ts
index c234df8795..08774ad666 100644
--- a/lib/modules/platform/azure/index.spec.ts
+++ b/lib/modules/platform/azure/index.spec.ts
@@ -1087,10 +1087,11 @@ describe('modules/platform/azure/index', () => {
 
   describe('massageMarkdown(input)', () => {
     it('returns updated pr body', () => {
-      const input =
-        '\n---\n\n - [ ] <!-- rebase-check --> rebase\nplus also [a link](https://github.com/foo/bar/issues/5)';
-      expect(azure.massageMarkdown(input)).toMatchInlineSnapshot(
-        `"plus also [a link](https://github.com/foo/bar/issues/5)"`
+      const prBody =
+        '\n---\n\n - [ ] <!-- rebase-check --> rebase\n<!--renovate-config-hash:-->' +
+        'plus also [a link](https://github.com/foo/bar/issues/5)';
+      expect(azure.massageMarkdown(prBody)).toBe(
+        'plus also [a link](https://github.com/foo/bar/issues/5)'
       );
     });
   });
diff --git a/lib/modules/platform/azure/index.ts b/lib/modules/platform/azure/index.ts
index 5119106235..0730237afb 100644
--- a/lib/modules/platform/azure/index.ts
+++ b/lib/modules/platform/azure/index.ts
@@ -746,7 +746,7 @@ export function massageMarkdown(input: string): string {
       'rename PR to start with "rebase!"'
     )
     .replace(regEx(`\n---\n\n.*?<!-- rebase-check -->.*?\n`), '')
-    .replace(regEx(/<!--renovate-debug:.*?-->/), '');
+    .replace(regEx(/<!--renovate-(?:debug|config-hash):.*?-->/g), '');
 }
 
 /* istanbul ignore next */
diff --git a/lib/modules/platform/bitbucket/index.spec.ts b/lib/modules/platform/bitbucket/index.spec.ts
index 35aef41c28..ceb70ab7a7 100644
--- a/lib/modules/platform/bitbucket/index.spec.ts
+++ b/lib/modules/platform/bitbucket/index.spec.ts
@@ -996,11 +996,10 @@ describe('modules/platform/bitbucket/index', () => {
 
   describe('massageMarkdown()', () => {
     it('returns diff files', () => {
-      expect(
-        bitbucket.massageMarkdown(
-          '<details><summary>foo</summary>bar</details>text<details>'
-        )
-      ).toMatchSnapshot();
+      const prBody =
+        '<details><summary>foo</summary>bar</details>text<details>' +
+        '\n---\n\n - [ ] <!-- rebase-check --> rebase\n<!--renovate-config-hash:-->';
+      expect(bitbucket.massageMarkdown(prBody)).toMatchSnapshot();
     });
   });
 
diff --git a/lib/modules/platform/bitbucket/index.ts b/lib/modules/platform/bitbucket/index.ts
index b839c2ecdf..6d63ecf1a2 100644
--- a/lib/modules/platform/bitbucket/index.ts
+++ b/lib/modules/platform/bitbucket/index.ts
@@ -488,7 +488,7 @@ export function massageMarkdown(input: string): string {
     .replace(regEx(/<\/?details>/g), '')
     .replace(regEx(`\n---\n\n.*?<!-- rebase-check -->.*?\n`), '')
     .replace(regEx(/\]\(\.\.\/pull\//g), '](../../pull-requests/')
-    .replace(regEx(/<!--renovate-debug:.*?-->/), '');
+    .replace(regEx(/<!--renovate-(?:debug|config-hash):.*?-->/g), '');
 }
 
 export async function ensureIssue({
diff --git a/lib/modules/platform/pr-body.spec.ts b/lib/modules/platform/pr-body.spec.ts
index 1439ee6156..89967f8a31 100644
--- a/lib/modules/platform/pr-body.spec.ts
+++ b/lib/modules/platform/pr-body.spec.ts
@@ -1,3 +1,4 @@
+import hasha from 'hasha';
 import { getPrBodyStruct, hashBody } from './pr-body';
 
 describe('modules/platform/pr-body', () => {
@@ -47,13 +48,50 @@ describe('modules/platform/pr-body', () => {
       );
     });
 
-    it('returns rebaseRequested flag', () => {
-      expect(getPrBodyStruct('- [x] <!-- rebase-check -->')).toEqual({
-        hash: '023952693e1e00a52a71b65d9b4804bca6ca9f215c20f6e029dbf420f322d541',
+    it('hashes an undefined body', () => {
+      // nullish operator branch coverage
+      const hash =
+        'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
+      expect(hashBody(undefined)).toBe(hash);
+    });
+
+    it('returns rebaseRequested=true flag', () => {
+      const input = '- [x] <!-- rebase-check -->';
+      const hash = hashBody(input);
+      expect(getPrBodyStruct(input)).toEqual({
+        hash,
         rebaseRequested: true,
       });
     });
 
+    it('returns rebaseRequested=false flag', () => {
+      const input = '- [ ] <!-- rebase-check -->';
+      const hash = hashBody(input);
+      expect(getPrBodyStruct(input)).toEqual({
+        hash,
+        rebaseRequested: false,
+      });
+    });
+
+    it('returns rebaseRequested=undefined flag', () => {
+      const input = '-  <!-- rebase-check -->';
+      const hash = hashBody(input);
+      expect(getPrBodyStruct(input)).toEqual({
+        hash,
+      });
+    });
+
+    it('returns raw config hash', () => {
+      const config = '{}';
+      const rawConfigHash = hasha(config, { algorithm: 'sha256' });
+      const input = `<!--renovate-config-hash:${rawConfigHash}-->`;
+      const hash = hashBody(input);
+      expect(getPrBodyStruct(input)).toEqual({
+        hash,
+        rawConfigHash,
+      });
+    });
+
     it('strips reviewable section', () => {
       expect(getPrBodyStruct('foo<!-- Reviewable:start -->bar')).toEqual({
         hash: hashBody('foo'),
diff --git a/lib/modules/platform/pr-body.ts b/lib/modules/platform/pr-body.ts
index 5fe70dd8dd..88f28826bc 100644
--- a/lib/modules/platform/pr-body.ts
+++ b/lib/modules/platform/pr-body.ts
@@ -1,3 +1,4 @@
+import is from '@sindresorhus/is';
 import hasha from 'hasha';
 import { logger } from '../../logger';
 import { stripEmojis } from '../../util/emoji';
@@ -9,6 +10,12 @@ export const prDebugDataRe = regEx(
   /\n?<!--renovate-debug:(?<payload>.*?)-->\n?/
 );
 
+const renovateConfigHashRe = regEx(
+  /\n?<!--renovate-config-hash:(?<payload>.*?)-->\n?/
+);
+
+const prCheckboxRe = regEx(/- (?<checkbox>\[[\sx]]) <!-- rebase-check -->/);
+
 function noWhitespaceOrHeadings(input: string): string {
   return input.replace(regEx(/\r?\n|\r|\s|#/g), '');
 }
@@ -28,8 +35,12 @@ export function hashBody(body: string | undefined): string {
   return result;
 }
 
-function isRebaseRequested(body: string | undefined): boolean {
-  return !!body?.includes(`- [x] <!-- rebase-check -->`);
+function isRebaseRequested(body: string): boolean | undefined {
+  const match = prCheckboxRe.exec(body);
+  if (!match) {
+    return undefined;
+  }
+  return match.groups?.checkbox === '[x]';
 }
 
 export function getRenovateDebugPayload(body: string): string | undefined {
@@ -37,6 +48,11 @@ export function getRenovateDebugPayload(body: string): string | undefined {
   return match?.groups?.payload;
 }
 
+export function getRenovateConfigHashPayload(body: string): string | undefined {
+  const match = renovateConfigHashRe.exec(body);
+  return match?.groups?.payload;
+}
+
 export function getPrBodyStruct(
   input: string | undefined | null
 ): PrBodyStruct {
@@ -45,10 +61,16 @@ export function getPrBodyStruct(
   const result: PrBodyStruct = { hash };
 
   const rebaseRequested = isRebaseRequested(body);
-  if (rebaseRequested) {
+
+  if (!is.undefined(rebaseRequested)) {
     result.rebaseRequested = rebaseRequested;
   }
 
+  const rawConfigHash = getRenovateConfigHashPayload(body);
+  if (rawConfigHash) {
+    result.rawConfigHash = rawConfigHash;
+  }
+
   const debugPayload = getRenovateDebugPayload(body);
   if (debugPayload) {
     try {
diff --git a/lib/modules/platform/types.ts b/lib/modules/platform/types.ts
index ff2b266f2c..56a625eab0 100644
--- a/lib/modules/platform/types.ts
+++ b/lib/modules/platform/types.ts
@@ -52,6 +52,7 @@ export interface PrDebugData {
 
 export interface PrBodyStruct {
   hash: string;
+  rawConfigHash?: string;
   rebaseRequested?: boolean;
   debugData?: PrDebugData;
 }
-- 
GitLab