From b6b85eb69eb904a5cef85e4dd393d498a350385a Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Sun, 21 Jul 2024 12:29:13 +0200
Subject: [PATCH] feat(package-rules): set skipStage (#30264)

---
 lib/config/types.ts                           |  2 ++
 lib/types/skip-reason.ts                      | 10 +++++++++
 lib/util/package-rules/index.spec.ts          | 21 ++++++++++++++++++-
 lib/util/package-rules/index.ts               |  9 ++++++++
 lib/workers/repository/process/fetch.ts       |  2 +-
 .../process/lookup/filter-checks.ts           |  2 +-
 .../repository/process/lookup/index.ts        | 10 +++++++--
 lib/workers/repository/updates/flatten.ts     | 14 +++++++++----
 8 files changed, 61 insertions(+), 9 deletions(-)

diff --git a/lib/config/types.ts b/lib/config/types.ts
index 796731ebc1..25fb95d59d 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -4,6 +4,7 @@ import type { LogLevelRemap } from '../logger/types';
 import type { CustomManager } from '../modules/manager/custom/types';
 import type { RepoSortMethod, SortMethod } from '../modules/platform/types';
 import type { HostRule, SkipReason } from '../types';
+import type { StageName } from '../types/skip-reason';
 import type { GitNoVerifyOption } from '../util/git/types';
 import type { MergeConfidence } from '../util/merge-confidence/types';
 
@@ -555,6 +556,7 @@ export interface PackageRuleInputConfig extends Record<string, unknown> {
   currentVersionTimestamp?: string;
   enabled?: boolean;
   skipReason?: SkipReason;
+  skipStage?: StageName;
 }
 
 export interface ConfigMigration {
diff --git a/lib/types/skip-reason.ts b/lib/types/skip-reason.ts
index 2d2ff12a33..534d37b445 100644
--- a/lib/types/skip-reason.ts
+++ b/lib/types/skip-reason.ts
@@ -46,3 +46,13 @@ export type SkipReason =
   | 'recursive-placeholder'
   | 'github-token-required'
   | 'inherited-dependency';
+
+export type StageName =
+  | 'current-timestamp'
+  | 'datasource-merge'
+  | 'lock-file-maintenance-merge'
+  | 'lock-file-maintenance-merge-2'
+  | 'pre-lookup'
+  | 'source-url'
+  | 'update-type'
+  | 'update-type-merge';
diff --git a/lib/util/package-rules/index.spec.ts b/lib/util/package-rules/index.spec.ts
index b47ca9e99b..222aaa544d 100644
--- a/lib/util/package-rules/index.spec.ts
+++ b/lib/util/package-rules/index.spec.ts
@@ -219,9 +219,28 @@ describe('util/package-rules/index', () => {
         },
       ],
     };
-    const res = applyPackageRules(dep);
+    const res = applyPackageRules(dep, 'datasource-merge');
     expect(res.enabled).toBeFalse();
     expect(res.skipReason).toBe('package-rules');
+    expect(res.skipStage).toBe('datasource-merge');
+  });
+
+  it('unsets skipReason=package-rules if enabled=true', () => {
+    const dep: any = {
+      depName: 'foo',
+      packageRules: [
+        {
+          enabled: false,
+        },
+        {
+          enabled: true,
+        },
+      ],
+    };
+    const res = applyPackageRules(dep, 'datasource-merge');
+    expect(res.enabled).toBeTrue();
+    expect(res.skipReason).toBeUndefined();
+    expect(res.skipStage).toBeUndefined();
   });
 
   it('skips skipReason=package-rules if enabled=true', () => {
diff --git a/lib/util/package-rules/index.ts b/lib/util/package-rules/index.ts
index f625ea5b24..73036d5cda 100644
--- a/lib/util/package-rules/index.ts
+++ b/lib/util/package-rules/index.ts
@@ -3,6 +3,7 @@ import slugify from 'slugify';
 import { mergeChildConfig } from '../../config';
 import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
 import { logger } from '../../logger';
+import type { StageName } from '../../types/skip-reason';
 import matchers from './matchers';
 import { matcherOR } from './utils';
 
@@ -62,6 +63,7 @@ function matchesRule(
 
 export function applyPackageRules<T extends PackageRuleInputConfig>(
   inputConfig: T,
+  stageName?: StageName,
 ): T {
   let config = { ...inputConfig };
   const packageRules = config.packageRules ?? [];
@@ -82,6 +84,13 @@ export function applyPackageRules<T extends PackageRuleInputConfig>(
       }
       if (toApply.enabled === false && config.enabled !== false) {
         config.skipReason = 'package-rules';
+        if (stageName) {
+          config.skipStage = stageName;
+        }
+      }
+      if (toApply.enabled === true && config.enabled === false) {
+        delete config.skipReason;
+        delete config.skipStage;
       }
       config = mergeChildConfig(config, toApply);
     }
diff --git a/lib/workers/repository/process/fetch.ts b/lib/workers/repository/process/fetch.ts
index 4e95f8b9ee..025aea391b 100644
--- a/lib/workers/repository/process/fetch.ts
+++ b/lib/workers/repository/process/fetch.ts
@@ -44,7 +44,7 @@ async function fetchDepUpdates(
   const datasourceDefaultConfig = await getDefaultConfig(depConfig.datasource!);
   depConfig = mergeChildConfig(depConfig, datasourceDefaultConfig);
   depConfig.versioning ??= getDefaultVersioning(depConfig.datasource);
-  depConfig = applyPackageRules(depConfig);
+  depConfig = applyPackageRules(depConfig, 'pre-lookup');
   depConfig.packageName ??= depConfig.depName;
   if (depConfig.ignoreDeps!.includes(depName!)) {
     // TODO: fix types (#22198)
diff --git a/lib/workers/repository/process/lookup/filter-checks.ts b/lib/workers/repository/process/lookup/filter-checks.ts
index d8f3f631cd..47fd605f15 100644
--- a/lib/workers/repository/process/lookup/filter-checks.ts
+++ b/lib/workers/repository/process/lookup/filter-checks.ts
@@ -52,7 +52,7 @@ export async function filterInternalChecks(
         releaseConfig[releaseConfig.updateType]!,
       );
       // Apply packageRules in case any apply to updateType
-      releaseConfig = applyPackageRules(releaseConfig);
+      releaseConfig = applyPackageRules(releaseConfig, 'update-type');
       // Now check for a minimumReleaseAge config
       const {
         minimumConfidence,
diff --git a/lib/workers/repository/process/lookup/index.ts b/lib/workers/repository/process/lookup/index.ts
index 3f0762b6f1..952ea0d538 100644
--- a/lib/workers/repository/process/lookup/index.ts
+++ b/lib/workers/repository/process/lookup/index.ts
@@ -200,7 +200,10 @@ export async function lookupUpdates(
         }
       }
       // Reapply package rules in case we missed something from sourceUrl
-      config = applyPackageRules({ ...config, sourceUrl: res.sourceUrl });
+      config = applyPackageRules(
+        { ...config, sourceUrl: res.sourceUrl },
+        'source-url',
+      );
       if (config.followTag) {
         const taggedVersion = dependency.tags?.[config.followTag];
         if (!taggedVersion) {
@@ -313,7 +316,10 @@ export async function lookupUpdates(
           )
         ) {
           // Reapply package rules to check matches for matchCurrentAge
-          config = applyPackageRules({ ...config, currentVersionTimestamp });
+          config = applyPackageRules(
+            { ...config, currentVersionTimestamp },
+            'current-timestamp',
+          );
         }
       }
 
diff --git a/lib/workers/repository/updates/flatten.ts b/lib/workers/repository/updates/flatten.ts
index 3832a2f585..1ee8a7326f 100644
--- a/lib/workers/repository/updates/flatten.ts
+++ b/lib/workers/repository/updates/flatten.ts
@@ -113,7 +113,7 @@ export async function flattenUpdates(
               depConfig.datasource,
             );
             updateConfig = mergeChildConfig(updateConfig, datasourceConfig);
-            updateConfig = applyPackageRules(updateConfig);
+            updateConfig = applyPackageRules(updateConfig, 'datasource-merge');
             // apply major/minor/patch/pin/digest
             updateConfig = mergeChildConfig(
               updateConfig,
@@ -123,7 +123,7 @@ export async function flattenUpdates(
               delete updateConfig[updateType];
             }
             // Apply again in case any were added by the updateType config
-            updateConfig = applyPackageRules(updateConfig);
+            updateConfig = applyPackageRules(updateConfig, 'update-type-merge');
             updateConfig = applyUpdateConfig(updateConfig);
             updateConfig.baseDeps = packageFile.deps;
             update.branchName = updateConfig.branchName;
@@ -143,13 +143,19 @@ export async function flattenUpdates(
         );
         lockFileConfig.updateType = 'lockFileMaintenance';
         lockFileConfig.isLockFileMaintenance = true;
-        lockFileConfig = applyPackageRules(lockFileConfig);
+        lockFileConfig = applyPackageRules(
+          lockFileConfig,
+          'lock-file-maintenance-merge',
+        );
         // Apply lockFileMaintenance and packageRules again
         lockFileConfig = mergeChildConfig(
           lockFileConfig,
           lockFileConfig.lockFileMaintenance,
         );
-        lockFileConfig = applyPackageRules(lockFileConfig);
+        lockFileConfig = applyPackageRules(
+          lockFileConfig,
+          'lock-file-maintenance-merge-2',
+        );
         // Remove unnecessary objects
         for (const updateType of updateTypes) {
           delete lockFileConfig[updateType];
-- 
GitLab