From 9a103cbb51269ebba7bf1230430c2895d7b1046b Mon Sep 17 00:00:00 2001
From: Ivan Katliarchuk <5395690+ivankatliarchuk@users.noreply.github.com>
Date: Mon, 21 Feb 2022 20:47:51 +0000
Subject: [PATCH] fix: ignore empty labels during label merge and templating
 (#14322)

---
 lib/util/string.ts           |  6 ++++++
 lib/util/template/index.ts   |  6 +++---
 lib/workers/pr/index.spec.ts | 29 +++++++++++++++++++++++++++++
 lib/workers/pr/index.ts      |  8 +++++---
 4 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/lib/util/string.ts b/lib/util/string.ts
index 2cad78d692..8cb2fe951f 100644
--- a/lib/util/string.ts
+++ b/lib/util/string.ts
@@ -1,3 +1,4 @@
+import is from '@sindresorhus/is';
 import { logger } from '../logger';
 
 // Return true if the match string is found at index in content
@@ -23,3 +24,8 @@ export function replaceAt(
     content.substr(index + oldString.length)
   );
 }
+
+// Return true if the input is non-empty and not whitespace string
+export function nonEmptyStringAndNotWhitespace(input: unknown): boolean {
+  return is.nonEmptyString(input) && !is.emptyStringOrWhitespace(input);
+}
diff --git a/lib/util/template/index.ts b/lib/util/template/index.ts
index 2bf1bc75af..e2903315e9 100644
--- a/lib/util/template/index.ts
+++ b/lib/util/template/index.ts
@@ -161,9 +161,9 @@ function getFilteredObject(input: CompileInput): FilteredObject {
   for (const field of allAllowed) {
     const value = obj[field];
     if (is.array(value)) {
-      res[field] = value.map((element) =>
-        getFilteredObject(element as CompileInput)
-      );
+      res[field] = value
+        .filter(is.plainObject)
+        .map((element) => getFilteredObject(element as CompileInput));
     } else if (is.plainObject(value)) {
       res[field] = getFilteredObject(value);
     } else if (!is.undefined(value)) {
diff --git a/lib/workers/pr/index.spec.ts b/lib/workers/pr/index.spec.ts
index 574ccd2888..25bbf2595d 100644
--- a/lib/workers/pr/index.spec.ts
+++ b/lib/workers/pr/index.spec.ts
@@ -807,6 +807,26 @@ describe('workers/pr/index', () => {
       expect(result).toEqual(['labelA', 'labelB', 'labelC']);
     });
 
+    it('empty labels ignored', () => {
+      const result = prWorker.prepareLabels({
+        labels: ['labelA', ''],
+        addLabels: [' ', 'labelB'],
+      });
+      expect(result).toBeArrayOfSize(2);
+      expect(result).toEqual(['labelA', 'labelB']);
+    });
+
+    it('null labels ignored', () => {
+      const result = prWorker.prepareLabels({
+        labels: ['labelA', null],
+        // an empty space between two commas in an array is categorized as a null value
+        // eslint-disable-next-line no-sparse-arrays
+        addLabels: ['labelB', '', undefined, , ,],
+      });
+      expect(result).toBeArrayOfSize(2);
+      expect(result).toEqual(['labelA', 'labelB']);
+    });
+
     it('template labels', () => {
       const result = prWorker.prepareLabels({
         labels: ['datasource-{{{datasource}}}'],
@@ -815,5 +835,14 @@ describe('workers/pr/index', () => {
       expect(result).toBeArrayOfSize(1);
       expect(result).toEqual(['datasource-npm']);
     });
+
+    it('template labels with empty datasource', () => {
+      const result = prWorker.prepareLabels({
+        labels: ['{{{datasource}}}', ' {{{datasource}}} '],
+        datasource: null,
+      });
+      expect(result).toBeArrayOfSize(0);
+      expect(result).toEqual([]);
+    });
   });
 });
diff --git a/lib/workers/pr/index.ts b/lib/workers/pr/index.ts
index 38a02645ab..42c1d3dbd4 100644
--- a/lib/workers/pr/index.ts
+++ b/lib/workers/pr/index.ts
@@ -13,6 +13,7 @@ import { sampleSize } from '../../util';
 import { stripEmojis } from '../../util/emoji';
 import { deleteBranch, getBranchLastCommitTime } from '../../util/git';
 import { regEx } from '../../util/regex';
+import { nonEmptyStringAndNotWhitespace } from '../../util/string';
 import * as template from '../../util/template';
 import { resolveBranchStatus } from '../branch/status-checks';
 import { Limit, incLimitedValue, isLimitReached } from '../global/limits';
@@ -56,9 +57,10 @@ function prepareAssigneesReviewers(
 export function prepareLabels(config: RenovateConfig): string[] {
   const labels = config.labels ?? [];
   const addLabels = config.addLabels ?? [];
-  return [...new Set([...labels, ...addLabels])].map((label) =>
-    template.compile(label, config)
-  );
+  return [...new Set([...labels, ...addLabels])]
+    .filter(nonEmptyStringAndNotWhitespace)
+    .map((label) => template.compile(label, config))
+    .filter(nonEmptyStringAndNotWhitespace);
 }
 
 export async function addAssigneesReviewers(
-- 
GitLab