From a02c85045366abc7720263407536a242befd119c Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Mon, 8 Jul 2024 14:14:27 +0200
Subject: [PATCH] fix(dependency-dashboard): fix truncated issue body (#30081)

---
 lib/modules/platform/azure/index.ts                 |  6 +++++-
 lib/modules/platform/bitbucket-server/index.ts      |  6 +++++-
 lib/modules/platform/bitbucket/index.ts             |  6 +++++-
 lib/modules/platform/codecommit/index.spec.ts       |  4 ++++
 lib/modules/platform/codecommit/index.ts            |  7 +++++++
 lib/modules/platform/gerrit/index.ts                |  6 +++++-
 lib/modules/platform/gitea/index.spec.ts            |  4 ++++
 lib/modules/platform/gitea/index.ts                 |  8 +++++++-
 lib/modules/platform/github/index.ts                | 10 +++++++---
 lib/modules/platform/gitlab/index.ts                | 12 ++++++------
 lib/modules/platform/local/index.spec.ts            |  4 ++++
 lib/modules/platform/local/index.ts                 |  7 +++++++
 lib/modules/platform/types.ts                       |  2 ++
 lib/workers/repository/dependency-dashboard.spec.ts |  8 +++-----
 lib/workers/repository/dependency-dashboard.ts      |  3 +--
 15 files changed, 72 insertions(+), 21 deletions(-)

diff --git a/lib/modules/platform/azure/index.ts b/lib/modules/platform/azure/index.ts
index 773a7e2622..f866e0dcdf 100644
--- a/lib/modules/platform/azure/index.ts
+++ b/lib/modules/platform/azure/index.ts
@@ -809,7 +809,7 @@ export async function mergePr({
 
 export function massageMarkdown(input: string): string {
   // Remove any HTML we use
-  return smartTruncate(input, 4000)
+  return smartTruncate(input, maxBodyLength())
     .replace(
       'you tick the rebase/retry checkbox',
       'rename PR to start with "rebase!"',
@@ -822,6 +822,10 @@ export function massageMarkdown(input: string): string {
     .replace(regEx(/<!--renovate-(?:debug|config-hash):.*?-->/g), '');
 }
 
+export function maxBodyLength(): number {
+  return 4000;
+}
+
 /* istanbul ignore next */
 export function findIssue(): Promise<Issue | null> {
   logger.warn(`findIssue() is not implemented`);
diff --git a/lib/modules/platform/bitbucket-server/index.ts b/lib/modules/platform/bitbucket-server/index.ts
index 1d0fb5f607..caa5fc34b9 100644
--- a/lib/modules/platform/bitbucket-server/index.ts
+++ b/lib/modules/platform/bitbucket-server/index.ts
@@ -1068,7 +1068,7 @@ export async function mergePr({
 export function massageMarkdown(input: string): string {
   logger.debug(`massageMarkdown(${input.split(newlineRegex)[0]})`);
   // Remove any HTML we use
-  return smartTruncate(input, 30000)
+  return smartTruncate(input, maxBodyLength())
     .replace(
       'you tick the rebase/retry checkbox',
       'rename PR to start with "rebase!"',
@@ -1082,3 +1082,7 @@ export function massageMarkdown(input: string): string {
     .replace(regEx(`\n---\n\n.*?<!-- rebase-check -->.*?(\n|$)`), '')
     .replace(regEx('<!--.*?-->', 'g'), '');
 }
+
+export function maxBodyLength(): number {
+  return 30000;
+}
diff --git a/lib/modules/platform/bitbucket/index.ts b/lib/modules/platform/bitbucket/index.ts
index bbd7bf9979..b47fcd4421 100644
--- a/lib/modules/platform/bitbucket/index.ts
+++ b/lib/modules/platform/bitbucket/index.ts
@@ -570,7 +570,7 @@ async function closeIssue(issueNumber: number): Promise<void> {
 
 export function massageMarkdown(input: string): string {
   // Remove any HTML we use
-  return smartTruncate(input, 50000)
+  return smartTruncate(input, maxBodyLength())
     .replace(
       'you tick the rebase/retry checkbox',
       'by renaming this PR to start with "rebase!"',
@@ -586,6 +586,10 @@ export function massageMarkdown(input: string): string {
     .replace(regEx(/<!--renovate-(?:debug|config-hash):.*?-->/g), '');
 }
 
+export function maxBodyLength(): number {
+  return 50000;
+}
+
 export async function ensureIssue({
   title,
   reuseTitle,
diff --git a/lib/modules/platform/codecommit/index.spec.ts b/lib/modules/platform/codecommit/index.spec.ts
index 487e09f12b..e249c401b2 100644
--- a/lib/modules/platform/codecommit/index.spec.ts
+++ b/lib/modules/platform/codecommit/index.spec.ts
@@ -63,6 +63,10 @@ describe('modules/platform/codecommit/index', () => {
     );
   });
 
+  it('maxBodyLength', () => {
+    expect(codeCommit.maxBodyLength()).toBe(Infinity);
+  });
+
   describe('initPlatform()', () => {
     it('should init', async () => {
       expect(
diff --git a/lib/modules/platform/codecommit/index.ts b/lib/modules/platform/codecommit/index.ts
index 741eb18a5e..35c49684ca 100644
--- a/lib/modules/platform/codecommit/index.ts
+++ b/lib/modules/platform/codecommit/index.ts
@@ -322,6 +322,13 @@ export function massageMarkdown(input: string): string {
     );
 }
 
+/**
+ * Unsed, no Dashboard
+ */
+export function maxBodyLength(): number {
+  return Infinity;
+}
+
 export async function getJsonFile(
   fileName: string,
   repoName?: string,
diff --git a/lib/modules/platform/gerrit/index.ts b/lib/modules/platform/gerrit/index.ts
index ddd058f21e..db740964fe 100644
--- a/lib/modules/platform/gerrit/index.ts
+++ b/lib/modules/platform/gerrit/index.ts
@@ -396,7 +396,7 @@ export async function ensureComment(
 
 export function massageMarkdown(prBody: string): string {
   //TODO: do more Gerrit specific replacements?
-  return smartTruncate(readOnlyIssueBody(prBody), 16384) //TODO: check the real gerrit limit (max. chars)
+  return smartTruncate(readOnlyIssueBody(prBody), maxBodyLength())
     .replace(regEx(/Pull Request(s)?/g), 'Change-Request$1')
     .replace(regEx(/\bPR(s)?\b/g), 'Change-Request$1')
     .replace(regEx(/<\/?summary>/g), '**')
@@ -419,6 +419,10 @@ export function massageMarkdown(prBody: string): string {
     .replace(regEx(/<!--renovate-(?:debug|config-hash):.*?-->/g), '');
 }
 
+export function maxBodyLength(): number {
+  return 16384; //TODO: check the real gerrit limit (max. chars)
+}
+
 export function deleteLabel(number: number, label: string): Promise<void> {
   return Promise.resolve();
 }
diff --git a/lib/modules/platform/gitea/index.spec.ts b/lib/modules/platform/gitea/index.spec.ts
index f74c4270c1..61141ec63c 100644
--- a/lib/modules/platform/gitea/index.spec.ts
+++ b/lib/modules/platform/gitea/index.spec.ts
@@ -2834,6 +2834,10 @@ describe('modules/platform/gitea/index', () => {
     });
   });
 
+  it('maxBodyLength', () => {
+    expect(gitea.maxBodyLength()).toBe(1000000);
+  });
+
   describe('getJsonFile()', () => {
     it('returns file content', async () => {
       const data = { foo: 'bar' };
diff --git a/lib/modules/platform/gitea/index.ts b/lib/modules/platform/gitea/index.ts
index b4859dfe99..0423273396 100644
--- a/lib/modules/platform/gitea/index.ts
+++ b/lib/modules/platform/gitea/index.ts
@@ -1001,10 +1001,16 @@ const platform: Platform = {
   },
 
   massageMarkdown(prBody: string): string {
-    return smartTruncate(smartLinks(prBody), 1000000);
+    return smartTruncate(smartLinks(prBody), maxBodyLength());
   },
+
+  maxBodyLength,
 };
 
+export function maxBodyLength(): number {
+  return 1000000;
+}
+
 /* eslint-disable @typescript-eslint/unbound-method */
 export const {
   addAssignees,
diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts
index 764e6fe33f..a3457a0253 100644
--- a/lib/modules/platform/github/index.ts
+++ b/lib/modules/platform/github/index.ts
@@ -96,7 +96,7 @@ export const id = 'github';
 let config: LocalRepoConfig;
 let platformConfig: PlatformConfig;
 
-export const GitHubMaxPrBodyLen = 60000;
+const GitHubMaxPrBodyLen = 60000;
 
 export function resetConfigs(): void {
   config = {} as never;
@@ -1938,7 +1938,7 @@ export async function mergePr({
 
 export function massageMarkdown(input: string): string {
   if (platformConfig.isGhe) {
-    return smartTruncate(input, GitHubMaxPrBodyLen);
+    return smartTruncate(input, maxBodyLength());
   }
   const massagedInput = massageMarkdownLinks(input)
     // to be safe, replace all github.com links with renovatebot redirector
@@ -1952,7 +1952,11 @@ export function massageMarkdown(input: string): string {
     .replace('> ⚠ **Warning**\n> \n', '> [!WARNING]\n')
     .replace('> ⚠️ **Warning**\n> \n', '> [!WARNING]\n')
     .replace('> ❗ **Important**\n> \n', '> [!IMPORTANT]\n');
-  return smartTruncate(massagedInput, GitHubMaxPrBodyLen);
+  return smartTruncate(massagedInput, maxBodyLength());
+}
+
+export function maxBodyLength(): number {
+  return GitHubMaxPrBodyLen;
 }
 
 export async function getVulnerabilityAlerts(): Promise<VulnerabilityAlert[]> {
diff --git a/lib/modules/platform/gitlab/index.ts b/lib/modules/platform/gitlab/index.ts
index 4a28db74a0..4fb612ee4e 100644
--- a/lib/modules/platform/gitlab/index.ts
+++ b/lib/modules/platform/gitlab/index.ts
@@ -897,26 +897,26 @@ export async function mergePr({ id }: MergePRConfig): Promise<boolean> {
 }
 
 export function massageMarkdown(input: string): string {
-  let desc = input
+  const desc = input
     .replace(regEx(/Pull Request/g), 'Merge Request')
     .replace(regEx(/\bPR\b/g), 'MR')
     .replace(regEx(/\bPRs\b/g), 'MRs')
     .replace(regEx(/\]\(\.\.\/pull\//g), '](!')
     // Strip unicode null characters as GitLab markdown does not permit them
     .replace(regEx(/\u0000/g), ''); // eslint-disable-line no-control-regex
+  return smartTruncate(desc, maxBodyLength());
+}
 
+export function maxBodyLength(): number {
   if (semver.lt(defaults.version, '13.4.0')) {
     logger.debug(
       { version: defaults.version },
       'GitLab versions earlier than 13.4 have issues with long descriptions, truncating to 25K characters',
     );
-
-    desc = smartTruncate(desc, 25000);
+    return 25000;
   } else {
-    desc = smartTruncate(desc, 1000000);
+    return 1000000;
   }
-
-  return desc;
 }
 
 // Branch
diff --git a/lib/modules/platform/local/index.spec.ts b/lib/modules/platform/local/index.spec.ts
index dbb631d469..140f38ec6f 100644
--- a/lib/modules/platform/local/index.spec.ts
+++ b/lib/modules/platform/local/index.spec.ts
@@ -65,6 +65,10 @@ describe('modules/platform/local/index', () => {
       expect(platform.massageMarkdown('foo')).toBe('foo');
     });
 
+    it('maxBodyLength', () => {
+      expect(platform.maxBodyLength()).toBe(Infinity);
+    });
+
     it('updatePr', async () => {
       expect(await platform.updatePr()).toBeUndefined();
     });
diff --git a/lib/modules/platform/local/index.ts b/lib/modules/platform/local/index.ts
index 186460ab2e..c3646ebb4f 100644
--- a/lib/modules/platform/local/index.ts
+++ b/lib/modules/platform/local/index.ts
@@ -62,6 +62,13 @@ export function massageMarkdown(input: string): string {
   return input;
 }
 
+/**
+ * Unsed, no Dashboard
+ */
+export function maxBodyLength(): number {
+  return Infinity;
+}
+
 export function updatePr(): Promise<void> {
   return Promise.resolve();
 }
diff --git a/lib/modules/platform/types.ts b/lib/modules/platform/types.ts
index 28f622cd61..e9f21232d7 100644
--- a/lib/modules/platform/types.ts
+++ b/lib/modules/platform/types.ts
@@ -277,6 +277,8 @@ export interface Platform {
   filterUnavailableUsers?(users: string[]): Promise<string[]>;
   commitFiles?(config: CommitFilesConfig): Promise<LongCommitSha | null>;
   expandGroupMembers?(reviewersOrAssignees: string[]): Promise<string[]>;
+
+  maxBodyLength(): number;
 }
 
 export interface PlatformScm {
diff --git a/lib/workers/repository/dependency-dashboard.spec.ts b/lib/workers/repository/dependency-dashboard.spec.ts
index d82ab52dd9..63f0b79cea 100644
--- a/lib/workers/repository/dependency-dashboard.spec.ts
+++ b/lib/workers/repository/dependency-dashboard.spec.ts
@@ -15,10 +15,7 @@ import type {
   PackageFile,
 } from '../../modules/manager/types';
 import type { Platform } from '../../modules/platform';
-import {
-  GitHubMaxPrBodyLen,
-  massageMarkdown,
-} from '../../modules/platform/github';
+import { massageMarkdown } from '../../modules/platform/github';
 import { clone } from '../../util/clone';
 import { regEx } from '../../util/regex';
 import type { BranchConfig, BranchUpgradeConfig } from '../types';
@@ -47,6 +44,7 @@ let config: RenovateConfig;
 
 beforeEach(() => {
   massageMdSpy.mockImplementation(massageMarkdown);
+  platform.maxBodyLength.mockReturnValue(60000); // Github Limit
   config = getConfig();
   config.platform = 'github';
   config.errors = [];
@@ -1063,7 +1061,7 @@ None detected
           expect(platform.ensureIssue).toHaveBeenCalledTimes(1);
           expect(
             platform.ensureIssue.mock.calls[0][0].body.length <
-              GitHubMaxPrBodyLen,
+              platform.maxBodyLength(),
           ).toBeTrue();
 
           // same with dry run
diff --git a/lib/workers/repository/dependency-dashboard.ts b/lib/workers/repository/dependency-dashboard.ts
index a1233d6851..d23e5e70be 100644
--- a/lib/workers/repository/dependency-dashboard.ts
+++ b/lib/workers/repository/dependency-dashboard.ts
@@ -4,7 +4,6 @@ import type { RenovateConfig } from '../../config/types';
 import { logger } from '../../logger';
 import type { PackageFile } from '../../modules/manager/types';
 import { platform } from '../../modules/platform';
-import { GitHubMaxPrBodyLen } from '../../modules/platform/github';
 import { regEx } from '../../util/regex';
 import { coerceString } from '../../util/string';
 import * as template from '../../util/template';
@@ -468,7 +467,7 @@ export async function ensureDependencyDashboard(
   // fit the detected dependencies section
   const footer = getFooter(config);
   issueBody += PackageFiles.getDashboardMarkdown(
-    GitHubMaxPrBodyLen - issueBody.length - footer.length,
+    platform.maxBodyLength() - issueBody.length - footer.length,
   );
 
   issueBody += footer;
-- 
GitLab