From 09334f3a17c4005814a7ec82569426ab2013b8cf Mon Sep 17 00:00:00 2001
From: RahulGautamSingh <rahultesnik@gmail.com>
Date: Tue, 28 May 2024 12:39:53 +0545
Subject: [PATCH] feat(config/validation): `matchBaseBranches` validation
 (#29283)

---
 .../match-base-branches.spec.ts               | 26 ++++++++++++++
 .../validation-helpers/match-base-branches.ts | 24 +++++++++++++
 lib/config/validation-helpers/types.ts        |  6 ++++
 lib/config/validation.spec.ts                 | 35 +++++++++++++++++++
 lib/config/validation.ts                      |  8 +++++
 5 files changed, 99 insertions(+)
 create mode 100644 lib/config/validation-helpers/match-base-branches.spec.ts
 create mode 100644 lib/config/validation-helpers/match-base-branches.ts

diff --git a/lib/config/validation-helpers/match-base-branches.spec.ts b/lib/config/validation-helpers/match-base-branches.spec.ts
new file mode 100644
index 0000000000..4386718ed7
--- /dev/null
+++ b/lib/config/validation-helpers/match-base-branches.spec.ts
@@ -0,0 +1,26 @@
+import { check } from './match-base-branches';
+
+describe('config/validation-helpers/match-base-branches', () => {
+  it('returns error when baseBranches is not defined', () => {
+    const res = check({
+      resolvedRule: { matchBaseBranches: ['develop'], addLabels: ['develop'] },
+      currentPath: 'packageRules[0]',
+    });
+    expect(res).toEqual([
+      {
+        topic: 'Configuration Error',
+        message:
+          'packageRules[0]: You must configure baseBranches inorder to use them inside matchBaseBranches.',
+      },
+    ]);
+  });
+
+  it('returns empty array for valid configuration', () => {
+    const res = check({
+      resolvedRule: { matchBaseBranches: ['develop'], addLabels: ['develop'] },
+      currentPath: 'packageRules[0]',
+      baseBranches: ['develop', 'main'],
+    });
+    expect(res).toBeEmptyArray();
+  });
+});
diff --git a/lib/config/validation-helpers/match-base-branches.ts b/lib/config/validation-helpers/match-base-branches.ts
new file mode 100644
index 0000000000..e64017b2e7
--- /dev/null
+++ b/lib/config/validation-helpers/match-base-branches.ts
@@ -0,0 +1,24 @@
+import is from '@sindresorhus/is';
+import type { ValidationMessage } from '../types';
+import type { CheckBaseBranchesArgs } from './types';
+
+/**
+ * Only if type condition or context condition violated then errors array will be mutated to store metadata
+ */
+export function check({
+  resolvedRule,
+  currentPath,
+  baseBranches,
+}: CheckBaseBranchesArgs): ValidationMessage[] {
+  const warnings: ValidationMessage[] = [];
+  if (Array.isArray(resolvedRule.matchBaseBranches)) {
+    if (!is.nonEmptyArray(baseBranches)) {
+      warnings.push({
+        topic: 'Configuration Error',
+        message: `${currentPath}: You must configure baseBranches inorder to use them inside matchBaseBranches.`,
+      });
+    }
+  }
+
+  return warnings;
+}
diff --git a/lib/config/validation-helpers/types.ts b/lib/config/validation-helpers/types.ts
index 68e1082582..873f039821 100644
--- a/lib/config/validation-helpers/types.ts
+++ b/lib/config/validation-helpers/types.ts
@@ -9,3 +9,9 @@ export interface CheckMatcherArgs {
   val: unknown;
   currentPath: string;
 }
+
+export interface CheckBaseBranchesArgs {
+  resolvedRule: PackageRule;
+  currentPath: string;
+  baseBranches?: string[];
+}
diff --git a/lib/config/validation.spec.ts b/lib/config/validation.spec.ts
index f0bfcda1f9..39e6d11cea 100644
--- a/lib/config/validation.spec.ts
+++ b/lib/config/validation.spec.ts
@@ -208,6 +208,41 @@ describe('config/validation', () => {
       expect(errors).toHaveLength(1);
     });
 
+    it('validates matchBaseBranches', async () => {
+      const config = {
+        baseBranches: ['foo'],
+        packageRules: [
+          {
+            matchBaseBranches: ['foo'],
+            addLabels: ['foo'],
+          },
+        ],
+      };
+      const { errors, warnings } = await configValidation.validateConfig(
+        'repo',
+        config,
+      );
+      expect(errors).toHaveLength(0);
+      expect(warnings).toHaveLength(0);
+    });
+
+    it('catches invalid matchBaseBranches when baseBranches is not defined', async () => {
+      const config = {
+        packageRules: [
+          {
+            matchBaseBranches: ['foo'],
+            addLabels: ['foo'],
+          },
+        ],
+      };
+      const { errors, warnings } = await configValidation.validateConfig(
+        'repo',
+        config,
+      );
+      expect(errors).toHaveLength(0);
+      expect(warnings).toHaveLength(1);
+    });
+
     it('catches invalid matchCurrentVersion regex', async () => {
       const config = {
         packageRules: [
diff --git a/lib/config/validation.ts b/lib/config/validation.ts
index f7050c2ac7..cbc87d6da6 100644
--- a/lib/config/validation.ts
+++ b/lib/config/validation.ts
@@ -35,6 +35,7 @@ import {
   allowedStatusCheckStrings,
 } from './types';
 import * as managerValidator from './validation-helpers/managers';
+import * as matchBaseBranchesValidator from './validation-helpers/match-base-branches';
 import * as regexOrGlobValidator from './validation-helpers/regex-glob-matchers';
 
 const options = getOptions();
@@ -440,6 +441,13 @@ export async function validateConfig(
                   errors.push(
                     ...managerValidator.check({ resolvedRule, currentPath }),
                   );
+                  warnings.push(
+                    ...matchBaseBranchesValidator.check({
+                      resolvedRule,
+                      currentPath: `${currentPath}[${subIndex}]`,
+                      baseBranches: config.baseBranches!,
+                    }),
+                  );
                   const selectorLength = Object.keys(resolvedRule).filter(
                     (ruleKey) => selectors.includes(ruleKey),
                   ).length;
-- 
GitLab