From 879f7cc2120f676a0776be90de184e2191fda4e7 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Mon, 13 May 2024 15:31:56 +0200
Subject: [PATCH] feat(packageRules): migrate matchers and excludes (#28602)

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
---
 docs/usage/configuration-options.md           | 194 ++-------------
 docs/usage/faq.md                             |   8 +-
 docs/usage/key-concepts/automerge.md          |   4 +-
 docs/usage/key-concepts/dashboard.md          |   2 +-
 docs/usage/modules/versioning/index.md        |   2 +-
 docs/usage/noise-reduction.md                 |  10 +-
 .../__snapshots__/migration.spec.ts.snap      |  31 ++-
 .../__snapshots__/validation.spec.ts.snap     |  26 +-
 lib/config/migration.spec.ts                  |  36 +--
 .../custom/package-rules-migration.spec.ts    | 115 ++++++++-
 .../custom/package-rules-migration.ts         |  98 +++++++-
 .../custom/packages-migration.spec.ts         |  13 +-
 lib/config/options/index.ts                   | 165 ------------
 .../presets/__snapshots__/index.spec.ts.snap  |  55 ++--
 lib/config/presets/index.spec.ts              |  42 +---
 lib/config/presets/index.ts                   |  10 +-
 lib/config/presets/internal/default.ts        |  10 +-
 lib/config/presets/internal/group.ts          | 176 ++++++-------
 lib/config/presets/internal/monorepos.ts      |   4 +-
 lib/config/presets/internal/packages.ts       |  57 ++---
 lib/config/presets/internal/security.ts       |   4 +-
 lib/config/presets/internal/workarounds.ts    |  46 ++--
 lib/config/types.ts                           |  12 -
 lib/config/validation.spec.ts                 |  25 +-
 lib/config/validation.ts                      |  28 +--
 lib/data/monorepo.json                        |  34 +--
 lib/data/replacements.json                    |  34 +--
 lib/modules/datasource/npm/npmrc.spec.ts      |   4 +-
 lib/modules/datasource/npm/npmrc.ts           |   8 +-
 lib/modules/versioning/regex/readme.md        |   2 +-
 lib/util/package-rules/base.ts                |  13 -
 lib/util/package-rules/dep-names.spec.ts      |  14 --
 lib/util/package-rules/dep-names.ts           |  13 -
 lib/util/package-rules/dep-patterns.spec.ts   |  81 ------
 lib/util/package-rules/dep-patterns.ts        |  41 ---
 lib/util/package-rules/dep-prefixes.spec.ts   | 105 --------
 lib/util/package-rules/dep-prefixes.ts        |  35 ---
 lib/util/package-rules/index.spec.ts          | 234 +++---------------
 lib/util/package-rules/index.ts               |  41 +--
 lib/util/package-rules/matchers.ts            |  45 ++--
 lib/util/package-rules/matchers.ts.orig       |  67 +++++
 lib/util/package-rules/package-names.spec.ts  |  22 +-
 lib/util/package-rules/package-names.ts       |  14 --
 .../package-rules/package-patterns.spec.ts    |  58 -----
 lib/util/package-rules/package-patterns.ts    |  44 ----
 .../package-rules/package-prefixes.spec.ts    |  57 -----
 lib/util/package-rules/package-prefixes.ts    |  42 ----
 lib/util/package-rules/repositories.spec.ts   |  64 ++---
 lib/util/package-rules/repositories.ts        |  15 --
 lib/util/package-rules/sourceurl-prefixes.ts  |  22 --
 lib/util/package-rules/types.ts               |   4 -
 lib/util/package-rules/utils.ts               |  35 ---
 .../repository/process/lookup/index.spec.ts   |   2 +-
 53 files changed, 658 insertions(+), 1665 deletions(-)
 delete mode 100644 lib/util/package-rules/dep-patterns.spec.ts
 delete mode 100644 lib/util/package-rules/dep-patterns.ts
 delete mode 100644 lib/util/package-rules/dep-prefixes.spec.ts
 delete mode 100644 lib/util/package-rules/dep-prefixes.ts
 create mode 100644 lib/util/package-rules/matchers.ts.orig
 delete mode 100644 lib/util/package-rules/package-patterns.spec.ts
 delete mode 100644 lib/util/package-rules/package-patterns.ts
 delete mode 100644 lib/util/package-rules/package-prefixes.spec.ts
 delete mode 100644 lib/util/package-rules/package-prefixes.ts
 delete mode 100644 lib/util/package-rules/sourceurl-prefixes.ts
 delete mode 100644 lib/util/package-rules/utils.ts

diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index f7d10762e9..f4c55ad27a 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -62,7 +62,7 @@ Consider this example:
   "labels": ["dependencies"],
   "packageRules": [
     {
-      "matchPackagePatterns": ["eslint"],
+      "matchPackageNames": ["/eslint/"],
       "labels": ["linting"]
     },
     {
@@ -1084,7 +1084,7 @@ If you want to approve _specific_ packages, set `dependencyDashboardApproval` to
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["^@package-name"],
+      "matchPackageNames": ["/^@package-name/"],
       "dependencyDashboardApproval": true
     }
   ]
@@ -1165,7 +1165,7 @@ To disable Renovate for all `eslint` packages, you can configure a package rule
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["^eslint"],
+      "matchPackageNames": ["eslint**"],
       "enabled": false
     }
   ]
@@ -2174,7 +2174,7 @@ Consider this example:
   "labels": ["dependencies"],
   "packageRules": [
     {
-      "matchPackagePatterns": ["eslint"],
+      "matchPackageNames": ["/eslint/"],
       "labels": ["linting"]
     }
   ]
@@ -2414,9 +2414,6 @@ The matching process for a package rule:
   `matchCurrentVersion`, `matchCurrentValue`, `matchNewValue`, `matchConfidence`, `matchCurrentAge`,
   `matchManagers`, `matchDatasources`, `matchCategories`, `matchDepTypes`, `matchUpdateTypes`,
   `matchRepositories`/`excludeRepositories`, `matchBaseBranches`, `matchFileNames`
-- Two special groups of matchers provide alternatives (they're OR-ed within their respective groups, and AND-ed with others):
-  - Source URL: `matchSourceUrls`, `matchSourceUrlPrefixes`
-  - Package/Dep identifiers: `matchDepNames`/`excludeDepNames`, `matchDepPatterns`/`excludeDepPatterns`, `matchDepPrefixes`/`excludeDepPrefixes`, `matchPackageNames`/`excludePackageNames`, `matchPackagePatterns`/`excludePackagePatterns`, `matchPackagePrefixes`/`excludePackagePrefixes`
 
 Here is an example if you want to group together all packages starting with `eslint` into a single branch/PR:
 
@@ -2424,14 +2421,14 @@ Here is an example if you want to group together all packages starting with `esl
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["^eslint"],
+      "matchPackageNames": ["eslint**"],
       "groupName": "eslint packages"
     }
   ]
 }
 ```
 
-Note how the above uses `matchPackagePatterns` with a regex value.
+Note how the above uses `matchPackageNames` with a prefix pattern.
 
 Here's an example config to limit the "noisy" `aws-sdk` package to weekly updates:
 
@@ -2448,24 +2445,22 @@ Here's an example config to limit the "noisy" `aws-sdk` package to weekly update
 
 For Maven dependencies, the package name is `<groupId:artefactId>`, e.g. `"matchPackageNames": ["com.thoughtworks.xstream:xstream"]`
 
-Note how the above uses `matchPackageNames` instead of `matchPackagePatterns` because it is an exact match package name.
-This is the equivalent of defining `"matchPackagePatterns": ["^aws\-sdk$"]`.
-However you can mix together both `matchPackageNames` and `matchPackagePatterns` in the same package rule and the rule will be applied if _either_ match.
+Note how the above uses an exact match string for `matchPackageNames` instead of a pattern
+However you can mix together both patterns and exact matches in the same package rule and the rule will be applied if _either_ match.
 Example:
 
 ```json
 {
   "packageRules": [
     {
-      "matchPackageNames": ["neutrino"],
-      "matchPackagePatterns": ["^@neutrino/"],
+      "matchPackageNames": ["neutrino", "@neutrino/**"],
       "groupName": "neutrino monorepo"
     }
   ]
 }
 ```
 
-The above rule will group together the `neutrino` package and any package matching `@neutrino/*`.
+The above rule will group together the `neutrino` package and any package starting with `@neutrino/`.
 
 File name matches are convenient to use if you wish to apply configuration rules to certain package or lock files using patterns.
 For example, if you have an `examples` directory and you want all updates to those examples to use the `chore` prefix instead of `fix`, then you could add this configuration:
@@ -2585,86 +2580,6 @@ To read the changelogs you must use the link.
 
 For more details on supported syntax see Renovate's [string pattern matching documentation](./string-pattern-matching.md).
 
-### excludeDepNames
-
-### excludeDepPatterns
-
-### excludeDepPrefixes
-
-### excludePackageNames
-
-**Important**: Do not mix this up with the option `ignoreDeps`.
-Use `ignoreDeps` instead if all you want to do is have a list of package names for Renovate to ignore.
-
-Use `excludePackageNames` if you want to have one or more exact name matches excluded in your package rule.
-See also `matchPackageNames`.
-
-```json
-{
-  "packageRules": [
-    {
-      "matchPackagePatterns": ["^eslint"],
-      "excludePackageNames": ["eslint-foo"]
-    }
-  ]
-}
-```
-
-The above will match all package names starting with `eslint` but exclude the specific package `eslint-foo`.
-
-### excludePackagePatterns
-
-Use this field if you want to have one or more package name patterns excluded in your package rule.
-See also `matchPackagePatterns`.
-
-```json
-{
-  "packageRules": [
-    {
-      "matchPackagePatterns": ["^eslint"],
-      "excludePackagePatterns": ["^eslint-foo"]
-    }
-  ]
-}
-```
-
-The above will match all package names starting with `eslint` but exclude ones starting with `eslint-foo`.
-
-### excludePackagePrefixes
-
-Use this field if you want to have one or more package name prefixes excluded in your package rule, without needing to write a regex.
-See also `matchPackagePrefixes`.
-
-```json
-{
-  "packageRules": [
-    {
-      "matchPackagePrefixes": ["eslint"],
-      "excludePackagePrefixes": ["eslint-foo"]
-    }
-  ]
-}
-```
-
-The above will match all package names starting with `eslint` but exclude ones starting with `eslint-foo`.
-
-### excludeRepositories
-
-Use this field to restrict rules to a particular repository. e.g.
-
-```json
-{
-  "packageRules": [
-    {
-      "excludeRepositories": ["literal/repo", "/^some/.*$/", "**/*-archived"],
-      "enabled": false
-    }
-  ]
-}
-```
-
-This field supports Regular Expressions if they begin and end with `/`, otherwise it will use `minimatch`.
-
 ### matchBaseBranches
 
 Use this field to restrict rules to a particular branch. e.g.
@@ -2785,7 +2700,7 @@ Regular Expressions must begin and end with `/`.
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["io.github.resilience4j"],
+      "matchPackageNames": ["io.github.resilience4j**"],
       "matchCurrentValue": "/^1\\./"
     }
   ]
@@ -2799,7 +2714,7 @@ Use the syntax `!/ /` like this:
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["io.github.resilience4j"],
+      "matchPackageNames": ["io.github.resilience4j**"],
       "matchCurrentValue": "!/^0\\./"
     }
   ]
@@ -2840,7 +2755,7 @@ For example, the following enforces that only `1.*` versions will be used:
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["io.github.resilience4j"],
+      "matchPackageNames": ["io.github.resilience4j**"],
       "matchCurrentVersion": "/^1\\./"
     }
   ]
@@ -2854,7 +2769,7 @@ Use the syntax `!/ /` like this:
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["io.github.resilience4j"],
+      "matchPackageNames": ["io.github.resilience4j**"],
       "matchCurrentVersion": "!/^0\\./"
     }
   ]
@@ -2880,10 +2795,6 @@ Use this field to restrict rules to a particular datasource. e.g.
 
 This field behaves the same as `matchPackageNames` except it matches against `depName` instead of `packageName`.
 
-### matchDepPatterns
-
-### matchDepPrefixes
-
 ### matchDepTypes
 
 Use this field if you want to limit a `packageRule` to certain `depType` values.
@@ -2978,7 +2889,7 @@ Regular Expressions must begin and end with `/`.
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["io.github.resilience4j"],
+      "matchPackageNames": ["io.github.resilience4j**"],
       "matchNewValue": "/^1\\./"
     }
   ]
@@ -2992,7 +2903,7 @@ Use the syntax `!/ /` like this:
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["io.github.resilience4j"],
+      "matchPackageNames": ["io.github.resilience4j**"],
       "matchNewValue": "!/^0\\./"
     }
   ]
@@ -3027,15 +2938,14 @@ The above will configure `rangeStrategy` to `pin` only for the npm package `angu
 {
   "packageRules": [
     {
-      "matchDatasources": ["npm"],
-      "matchPackageNames": ["@angular/*", "!@angular/abc"],
-      "groupName": "Angular"
+      "matchPackagePatterns": ["^angular", "!@angular/abc"],
+      "rangeStrategy": "replace"
     }
   ]
 }
 ```
 
-The above will group together any npm package which starts with `@angular/` except `@angular/abc`.
+The above will set a replaceStrategy for any npm package which starts with `@angular/` except `@angular/abc`.
 
 ```json title="pattern match using RegEx"
 {
@@ -3051,68 +2961,8 @@ The above will group together any npm package which starts with `@angular/` exce
 
 The above will group together any npm package which starts with the string `angular`.
 
-### matchPackagePatterns
-
-<!-- prettier-ignore -->
-!!! note
-    `matchPackageNames` now supports pattern matching and should be used instead.
-    Use of `matchPackagePatterns` is now deprecated and will be migrated in future.
-
-### matchPackagePrefixes
-
-<!-- prettier-ignore -->
-!!! note
-    `matchPackageNames` now supports pattern matching and should be used instead.
-    Use of `matchPackagePrefixes` is now deprecated and will be migrated in future.
-
-Use this field to match a package prefix without needing to write a regex expression.
-See also `excludePackagePrefixes`.
-
-```json
-{
-  "packageRules": [
-    {
-      "matchPackagePrefixes": ["angular"],
-      "rangeStrategy": "replace"
-    }
-  ]
-}
-```
-
-Like the earlier `matchPackagePatterns` example, the above will configure `rangeStrategy` to `replace` for any package starting with `angular`.
-
 ### matchRepositories
 
-Use this field to restrict rules to a particular repository. e.g.
-
-```json
-{
-  "packageRules": [
-    {
-      "matchRepositories": ["literal/repo", "/^some/.*$/", "**/*-archived"],
-      "enabled": false
-    }
-  ]
-}
-```
-
-For more details on supported syntax see Renovate's [string pattern matching documentation](./string-pattern-matching.md).
-
-### matchSourceUrlPrefixes
-
-Here's an example of where you use this to group together all packages from the `renovatebot` GitHub org:
-
-```json
-{
-  "packageRules": [
-    {
-      "matchSourceUrlPrefixes": ["https://github.com/renovatebot/"],
-      "groupName": "All renovate packages"
-    }
-  ]
-}
-```
-
 ### matchSourceUrls
 
 Here's an example of where you use this to group together all packages from the Vue monorepo:
@@ -3240,7 +3090,7 @@ For example, the following package rule can be used to replace the registry for
   "packageRules": [
     {
       "matchDatasources": ["docker"],
-      "matchPackagePatterns": ["^docker\\.io/.+"],
+      "matchPackageNames": ["docker.io/**"],
       "replacementNameTemplate": "{{{replace 'docker\\.io/' 'ghcr.io/' packageName}}}"
     }
   ]
@@ -3255,13 +3105,13 @@ Or, to add a registry prefix to any `docker` images that do not contain an expli
     {
       "description": "official images",
       "matchDatasources": ["docker"],
-      "matchPackagePatterns": ["^[a-z-]+$"],
+      "matchPackageNames": ["/^[a-z-]+$/"],
       "replacementNameTemplate": "some.registry.org/library/{{{packageName}}}"
     },
     {
       "description": "non-official images",
       "matchDatasources": ["docker"],
-      "matchPackagePatterns": ["^[a-z-]+/[a-z-]+$"],
+      "matchPackageNames": ["/^[a-z-]+/[a-z-]+$/"],
       "replacementNameTemplate": "some.registry.org/{{{packageName}}}"
     }
   ]
diff --git a/docs/usage/faq.md b/docs/usage/faq.md
index 6ee4c11169..3ff913c2d7 100644
--- a/docs/usage/faq.md
+++ b/docs/usage/faq.md
@@ -120,7 +120,7 @@ The basic idea is that you create a new `packageRules` entry and describe what k
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["^jest"],
+      "matchPackageNames": ["jest"],
       "matchUpdateTypes": ["major"],
       "dependencyDashboardApproval": true
     }
@@ -223,13 +223,13 @@ e.g.
 
 ### Apply a rule, but only for packages starting with `abc`
 
-Do the same as above, but instead of using `matchPackageNames`, use `matchPackagePatterns` and a regex:
+Do the same as above, but instead of an exact match, use a glob prefix:
 
 ```json
 {
   "packageRules": [
     {
-      "matchPackagePatterns": "^abc",
+      "matchPackageNames": "abc**",
       "assignees": ["importantreviewer"]
     }
   ]
@@ -244,7 +244,7 @@ As above, but apply a `groupName`:
 {
   "packageRules": [
     {
-      "matchPackagePatterns": "^abc",
+      "matchPackageNames": "abc**",
       "groupName": ["abc packages"]
     }
   ]
diff --git a/docs/usage/key-concepts/automerge.md b/docs/usage/key-concepts/automerge.md
index 43095d66b7..2f24b7ae2e 100644
--- a/docs/usage/key-concepts/automerge.md
+++ b/docs/usage/key-concepts/automerge.md
@@ -56,7 +56,7 @@ But in many cases the new version(s) will pass tests, and if so then there's rea
   "packageRules": [
     {
       "matchDepTypes": ["devDependencies"],
-      "matchPackagePatterns": ["lint", "prettier"],
+      "matchPackageNames": ["lint", "prettier"],
       "automerge": true
     }
   ]
@@ -263,7 +263,7 @@ To turn off automerge for all dependencies of a selected repository, you need to
   "extends": ["local>org-name/.github:renovate-config"],
   "packageRules": [
     {
-      "matchPackagePatterns": ["*"],
+      "matchPackageNames": ["*"],
       "automerge": false
     }
   ]
diff --git a/docs/usage/key-concepts/dashboard.md b/docs/usage/key-concepts/dashboard.md
index 3199cb1359..67e7245106 100644
--- a/docs/usage/key-concepts/dashboard.md
+++ b/docs/usage/key-concepts/dashboard.md
@@ -138,7 +138,7 @@ If you want to approve specific packages, set `dependencyDashboardApproval` to `
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["^@somescope"],
+      "matchPackageName": ["@somescope/**"],
       "dependencyDashboardApproval": true
     }
   ]
diff --git a/docs/usage/modules/versioning/index.md b/docs/usage/modules/versioning/index.md
index eab2373bcb..e9bad3fa2c 100644
--- a/docs/usage/modules/versioning/index.md
+++ b/docs/usage/modules/versioning/index.md
@@ -23,7 +23,7 @@ Configuring or overriding the default `versioning` can be extra helpful for ecos
 
 - Although you can reconfigure versioning per-manager or per-datasource, you probably don't need such a broad change
 - More commonly you would need to configure `versioning` for individual packages or potentially package patterns
-- The best way to do this is with `packageRules`, with a combination of `matchManagers`, `matchDatasources`, `matchPackageNames` and `matchPackagePatterns`.
+- The best way to do this is with `packageRules`, with a combination of `matchManagers`, `matchDatasources`, and `matchPackageNames`.
   Avoid configuring `versioning` in a rule that also uses `matchUpdateTypes`, as the update types aren't known at the time the `versioning` is applied
 
 ## Examples of versioning overrides
diff --git a/docs/usage/noise-reduction.md b/docs/usage/noise-reduction.md
index 468c5383dc..d88a39671a 100644
--- a/docs/usage/noise-reduction.md
+++ b/docs/usage/noise-reduction.md
@@ -31,14 +31,14 @@ In that case you might create a config like this:
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["eslint"],
+      "matchPackageNames": ["/eslint/"],
       "groupName": "eslint"
     }
   ]
 }
 ```
 
-By setting `matchPackagePatterns` to "eslint", it means that any package with ESLint anywhere in its name will be grouped into a `renovate/eslint` branch and related PR.
+By setting `matchPackageNames` to `/eslint/`, it means that any package with eslint anywhere in its name will be grouped into a `renovate/eslint` branch and related PR.
 
 ### Be smart about grouping dependencies
 
@@ -91,7 +91,7 @@ You don't want to get too far behind, so how about we update `eslint` packages o
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["eslint"],
+      "matchPackageNames": ["/eslint/"],
       "groupName": "eslint",
       "schedule": ["on the first day of the month"]
     }
@@ -105,7 +105,7 @@ Or perhaps at least weekly:
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["eslint"],
+      "matchPackageNames": ["/eslint/"],
       "groupName": "eslint",
       "schedule": ["before 4am on monday"]
     }
@@ -165,7 +165,7 @@ Let's automerge it if all the linting updates pass:
 {
   "packageRules": [
     {
-      "matchPackagePatterns": ["eslint"],
+      "matchPackageNames": ["/eslint/"],
       "groupName": "eslint",
       "schedule": ["before 4am on monday"],
       "automerge": true,
diff --git a/lib/config/__snapshots__/migration.spec.ts.snap b/lib/config/__snapshots__/migration.spec.ts.snap
index c10def2c22..5da56cd976 100644
--- a/lib/config/__snapshots__/migration.spec.ts.snap
+++ b/lib/config/__snapshots__/migration.spec.ts.snap
@@ -58,22 +58,20 @@ exports[`config/migration it migrates nested packageRules 1`] = `
     },
     {
       "automerge": true,
-      "excludePackageNames": [
-        "@types/react-table",
-      ],
       "groupName": "definitelyTyped",
-      "matchPackagePrefixes": [
-        "@types/",
+      "matchPackageNames": [
+        "!@types/react-table",
+        "@types/**",
       ],
     },
     {
       "automerge": false,
-      "excludePackageNames": [
-        "@types/react-table",
-      ],
       "matchDepTypes": [
         "dependencies",
       ],
+      "matchPackageNames": [
+        "!@types/react-table",
+      ],
     },
   ],
 }
@@ -190,23 +188,22 @@ exports[`config/migration migrateConfig(config, parentConfig) migrates config 1`
       ],
     },
     {
-      "excludePackageNames": "foo",
       "groupName": "angular packages",
-      "matchPackagePatterns": "^(@angular|typescript)",
+      "matchPackageNames": [
+        "/^(@angular|typescript)/",
+      ],
     },
     {
       "groupName": "foo",
-      "matchPackagePatterns": [
-        "^foo",
+      "matchPackageNames": [
+        "/^foo/",
       ],
     },
     {
       "enabled": false,
       "matchPackageNames": [
         "angular",
-      ],
-      "matchPackagePatterns": [
-        "ang",
+        "/ang/",
       ],
     },
     {
@@ -398,7 +395,9 @@ exports[`config/migration migrateConfig(config, parentConfig) overrides existing
       "major": {
         "automerge": false,
       },
-      "matchPackagePatterns": "^(@angular|typescript)",
+      "matchPackageNames": [
+        "/^(@angular|typescript)/",
+      ],
       "minor": {
         "automerge": false,
       },
diff --git a/lib/config/__snapshots__/validation.spec.ts.snap b/lib/config/__snapshots__/validation.spec.ts.snap
index 3887cf9491..4bfe7e328d 100644
--- a/lib/config/__snapshots__/validation.spec.ts.snap
+++ b/lib/config/__snapshots__/validation.spec.ts.snap
@@ -54,11 +54,7 @@ exports[`config/validation validateConfig(config) errors for all types 1`] = `
     "topic": "Configuration Error",
   },
   {
-    "message": "Configuration option \`packageRules[3].matchDepPatterns\` should be a list (Array)",
-    "topic": "Configuration Error",
-  },
-  {
-    "message": "Configuration option \`packageRules[3].matchPackagePatterns\` should be a list (Array)",
+    "message": "Configuration option \`packageRules[2].matchPackageNames\` should be a list (Array)",
     "topic": "Configuration Error",
   },
   {
@@ -66,15 +62,7 @@ exports[`config/validation validateConfig(config) errors for all types 1`] = `
     "topic": "Configuration Error",
   },
   {
-    "message": "Invalid configuration option: packageRules[1].foo",
-    "topic": "Configuration Error",
-  },
-  {
-    "message": "Invalid regExp for packageRules[3].excludeDepPatterns: \`abc ([a-z]+) ([a-z]+))\`",
-    "topic": "Configuration Error",
-  },
-  {
-    "message": "Invalid regExp for packageRules[3].excludePackagePatterns: \`abc ([a-z]+) ([a-z]+))\`",
+    "message": "Invalid configuration option: packageRules[0].foo",
     "topic": "Configuration Error",
   },
   {
@@ -90,7 +78,7 @@ exports[`config/validation validateConfig(config) errors for all types 1`] = `
     "topic": "Configuration Error",
   },
   {
-    "message": "packageRules[1]: Each packageRule must contain at least one match* or exclude* selector. Rule: {"foo":1}",
+    "message": "packageRules[0]: Each packageRule must contain at least one match* or exclude* selector. Rule: {"foo":1}",
     "topic": "Configuration Error",
   },
   {
@@ -220,7 +208,11 @@ exports[`config/validation validateConfig(config) returns nested errors 1`] = `
     "topic": "Configuration Error",
   },
   {
-    "message": "Invalid regExp for packageRules[0].excludePackagePatterns: \`abc ([a-z]+) ([a-z]+))\`",
+    "message": "Invalid regExp for packageRules[0].matchPackageNames: \`!/abc ([a-z]+) ([a-z]+))/\`",
+    "topic": "Configuration Error",
+  },
+  {
+    "message": "Invalid regExp for packageRules[0].matchPackageNames: \`/abc ([a-z]+) ([a-z]+))/\`",
     "topic": "Configuration Error",
   },
 ]
@@ -268,7 +260,7 @@ exports[`config/validation validateConfig(config) warns if hostType has the wron
 exports[`config/validation validateConfig(config) warns if only selectors in packageRules 1`] = `
 [
   {
-    "message": "packageRules[0]: Each packageRule must contain at least one non-match* or non-exclude* field. Rule: {"matchDepTypes":["foo"],"excludePackageNames":["bar"]}",
+    "message": "packageRules[0]: Each packageRule must contain at least one non-match* or non-exclude* field. Rule: {"matchDepTypes":["foo"],"matchPackageNames":["bar"]}",
     "topic": "Configuration Error",
   },
 ]
diff --git a/lib/config/migration.spec.ts b/lib/config/migration.spec.ts
index baa90a4aa5..f588404695 100644
--- a/lib/config/migration.spec.ts
+++ b/lib/config/migration.spec.ts
@@ -254,14 +254,14 @@ describe('config/migration', () => {
             groupName: ['angular packages'],
           },
         ],
-      };
+      } as unknown as RenovateConfig;
       const { isMigrated, migratedConfig } =
         configMigration.migrateConfig(config);
       expect(isMigrated).toBeTrue();
       expect(migratedConfig).toEqual({
         packageRules: [
           {
-            matchPackagePatterns: '^(@angular|typescript)',
+            matchPackageNames: ['/^(@angular|typescript)/'],
             groupName: 'angular packages',
           },
         ],
@@ -505,27 +505,6 @@ describe('config/migration', () => {
       });
     });
 
-    it('migrates combinations of packageRules', () => {
-      let config: TestRenovateConfig;
-      let res: MigratedConfig;
-
-      config = {
-        packages: [{ matchPackagePatterns: ['*'] }],
-        packageRules: [{ matchPackageNames: [] }],
-      } as never;
-      res = configMigration.migrateConfig(config);
-      expect(res.isMigrated).toBeTrue();
-      expect(res.migratedConfig.packageRules).toHaveLength(2);
-
-      config = {
-        packageRules: [{ matchPpackageNames: [] }],
-        packages: [{ matchPackagePatterns: ['*'] }],
-      } as never;
-      res = configMigration.migrateConfig(config);
-      expect(res.isMigrated).toBeTrue();
-      expect(res.migratedConfig.packageRules).toHaveLength(2);
-    });
-
     it('it migrates packageRules', () => {
       const config: TestRenovateConfig = {
         packageRules: [
@@ -540,6 +519,7 @@ describe('config/migration', () => {
             packagePatterns: ['^bar'],
             excludePackageNames: ['baz'],
             excludePackagePatterns: ['^baz'],
+            excludeRepositories: ['abc/def'],
             sourceUrlPrefixes: ['https://github.com/lodash'],
             updateTypes: ['major'],
           },
@@ -551,17 +531,15 @@ describe('config/migration', () => {
       expect(migratedConfig).toEqual({
         packageRules: [
           {
-            excludePackageNames: ['baz'],
-            excludePackagePatterns: ['^baz'],
             matchBaseBranches: ['master'],
             matchDatasources: ['orb'],
             matchDepTypes: ['peerDependencies'],
             matchCategories: ['python'],
             matchManagers: ['dockerfile'],
-            matchPackageNames: ['foo'],
-            matchPackagePatterns: ['^bar'],
+            matchPackageNames: ['foo', '/^bar/', '!baz', '!/^baz/'],
+            matchRepositories: ['!abc/def'],
             matchFileNames: ['package.json'],
-            matchSourceUrlPrefixes: ['https://github.com/lodash'],
+            matchSourceUrls: ['https://github.com/lodash**'],
             matchUpdateTypes: ['major'],
           },
         ],
@@ -610,7 +588,7 @@ describe('config/migration', () => {
           packageRules: [
             {
               groupName: 'definitelyTyped',
-              matchPackagePrefixes: ['@types/'],
+              matchPackageNames: ['@types/**'],
             },
             {
               matchDepTypes: ['dependencies'],
diff --git a/lib/config/migrations/custom/package-rules-migration.spec.ts b/lib/config/migrations/custom/package-rules-migration.spec.ts
index 592e361005..2075a57274 100644
--- a/lib/config/migrations/custom/package-rules-migration.spec.ts
+++ b/lib/config/migrations/custom/package-rules-migration.spec.ts
@@ -16,8 +16,6 @@ describe('config/migrations/custom/package-rules-migration', () => {
           depTypeList: [],
           addLabels: [],
           packageNames: [],
-          packagePatterns: [],
-          sourceUrlPrefixes: [],
           updateTypes: [],
         },
       ],
@@ -107,4 +105,117 @@ describe('config/migrations/custom/package-rules-migration', () => {
       },
     );
   });
+
+  it('should migrate excludePackageNames to matchPackageNames', () => {
+    expect(PackageRulesMigration).toMigrate(
+      {
+        packageRules: [
+          {
+            excludePackageNames: ['foo', 'bar'],
+            automerge: true,
+          },
+          {
+            matchPackageNames: ['baz'],
+            excludePackageNames: ['foo', 'bar'],
+            automerge: true,
+          },
+        ],
+      },
+      {
+        packageRules: [
+          {
+            automerge: true,
+            matchPackageNames: ['!foo', '!bar'],
+          },
+          {
+            automerge: true,
+            matchPackageNames: ['baz', '!foo', '!bar'],
+          },
+        ],
+      },
+    );
+  });
+
+  it('should migrate matchPackagePatterns to matchPackageNames', () => {
+    expect(PackageRulesMigration).toMigrate(
+      {
+        packageRules: [
+          {
+            matchPackagePatterns: ['foo', 'bar'],
+            automerge: true,
+          },
+          {
+            matchPackageNames: ['baz'],
+            matchPackagePatterns: ['foo', 'bar'],
+            automerge: true,
+          },
+        ],
+      },
+      {
+        packageRules: [
+          {
+            automerge: true,
+            matchPackageNames: ['/foo/', '/bar/'],
+          },
+          {
+            automerge: true,
+            matchPackageNames: ['baz', '/foo/', '/bar/'],
+          },
+        ],
+      },
+    );
+  });
+
+  it('should migrate all match/exclude at once', () => {
+    expect(PackageRulesMigration).toMigrate(
+      {
+        packageRules: [
+          {
+            matchPackagePatterns: ['pattern'],
+            matchPackagePrefixes: ['prefix1', 'prefix2'],
+            matchSourceUrlPrefixes: ['prefix1', 'prefix2'],
+            excludePackageNames: ['excluded'],
+            excludePackagePatterns: ['excludepattern'],
+            excludePackagePrefixes: ['prefix1b'],
+            matchPackageNames: ['mpn1', 'mpn2'],
+            matchDepPatterns: ['pattern'],
+            matchDepPrefixes: ['prefix1', 'prefix2'],
+            excludeDepNames: ['excluded'],
+            excludeDepPatterns: ['excludepattern'],
+            excludeDepPrefixes: ['prefix1b'],
+            matchDepNames: ['mpn1', 'mpn2'],
+            automerge: true,
+          },
+        ],
+      },
+      {
+        packageRules: [
+          {
+            matchPackageNames: [
+              'mpn1',
+              'mpn2',
+              '/pattern/',
+              'prefix1**',
+              'prefix2**',
+              '!excluded',
+              '!/excludepattern/',
+              '!prefix1b**',
+            ],
+            matchDepNames: [
+              'mpn1',
+              'mpn2',
+              '/pattern/',
+              'prefix1**',
+              'prefix2**',
+              '!excluded',
+              '!/excludepattern/',
+              '!prefix1b**',
+            ],
+            matchSourceUrls: ['prefix1**', 'prefix2**'],
+            automerge: true,
+          },
+        ],
+      },
+    );
+  });
 });
diff --git a/lib/config/migrations/custom/package-rules-migration.ts b/lib/config/migrations/custom/package-rules-migration.ts
index 3e6450956a..6b8aea4780 100644
--- a/lib/config/migrations/custom/package-rules-migration.ts
+++ b/lib/config/migrations/custom/package-rules-migration.ts
@@ -27,6 +27,102 @@ function renameKeys(packageRule: PackageRule): PackageRule {
   return newPackageRule;
 }
 
+function mergeMatchers(packageRule: PackageRule): PackageRule {
+  const newPackageRule: PackageRule = { ...packageRule };
+  for (const [key, val] of Object.entries(packageRule)) {
+    // depName
+    if (key === 'matchDepPrefixes') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchDepNames ??= [];
+        newPackageRule.matchDepNames.push(...val.map((v) => `${v}**`));
+      }
+      delete newPackageRule.matchDepPrefixes;
+    }
+    if (key === 'matchDepPatterns') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchDepNames ??= [];
+        newPackageRule.matchDepNames.push(...val.map((v) => `/${v}/`));
+      }
+      delete newPackageRule.matchDepPatterns;
+    }
+    if (key === 'excludeDepNames') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchDepNames ??= [];
+        newPackageRule.matchDepNames.push(...val.map((v) => `!${v}`));
+      }
+      delete newPackageRule.excludeDepNames;
+    }
+    if (key === 'excludeDepPrefixes') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchDepNames ??= [];
+        newPackageRule.matchDepNames.push(...val.map((v) => `!${v}**`));
+      }
+      delete newPackageRule.excludeDepPrefixes;
+    }
+    if (key === 'excludeDepPatterns') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchDepNames ??= [];
+        newPackageRule.matchDepNames.push(...val.map((v) => `!/${v}/`));
+      }
+      delete newPackageRule.excludeDepPatterns;
+    }
+    // packageName
+    if (key === 'matchPackagePrefixes') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchPackageNames ??= [];
+        newPackageRule.matchPackageNames.push(...val.map((v) => `${v}**`));
+      }
+      delete newPackageRule.matchPackagePrefixes;
+    }
+    if (key === 'matchPackagePatterns') {
+      const patterns = is.string(val) ? [val] : val;
+      if (is.array(patterns, is.string)) {
+        newPackageRule.matchPackageNames ??= [];
+        newPackageRule.matchPackageNames.push(...patterns.map((v) => `/${v}/`));
+      }
+      delete newPackageRule.matchPackagePatterns;
+    }
+    if (key === 'excludePackageNames') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchPackageNames ??= [];
+        newPackageRule.matchPackageNames.push(...val.map((v) => `!${v}`));
+      }
+      delete newPackageRule.excludePackageNames;
+    }
+    if (key === 'excludePackagePrefixes') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchPackageNames ??= [];
+        newPackageRule.matchPackageNames.push(...val.map((v) => `!${v}**`));
+      }
+      delete newPackageRule.excludePackagePrefixes;
+    }
+    if (key === 'excludePackagePatterns') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchPackageNames ??= [];
+        newPackageRule.matchPackageNames.push(...val.map((v) => `!/${v}/`));
+      }
+      delete newPackageRule.excludePackagePatterns;
+    }
+    // sourceUrl
+    if (key === 'matchSourceUrlPrefixes') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchSourceUrls ??= [];
+        newPackageRule.matchSourceUrls.push(...val.map((v) => `${v}**`));
+      }
+      delete newPackageRule.matchSourceUrlPrefixes;
+    }
+    // repository
+    if (key === 'excludeRepositories') {
+      if (is.array(val, is.string)) {
+        newPackageRule.matchRepositories ??= [];
+        newPackageRule.matchRepositories.push(...val.map((v) => `!${v}`));
+      }
+      delete newPackageRule.excludeRepositories;
+    }
+  }
+  return newPackageRule;
+}
+
 export class PackageRulesMigration extends AbstractMigration {
   override readonly propertyName = 'packageRules';
 
@@ -34,7 +130,7 @@ export class PackageRulesMigration extends AbstractMigration {
     let packageRules = this.get('packageRules') as PackageRule[];
     if (is.nonEmptyArray(packageRules)) {
       packageRules = packageRules.map(renameKeys);
-
+      packageRules = packageRules.map(mergeMatchers);
       this.rewrite(packageRules);
     }
   }
diff --git a/lib/config/migrations/custom/packages-migration.spec.ts b/lib/config/migrations/custom/packages-migration.spec.ts
index 255f9561c7..666c862776 100644
--- a/lib/config/migrations/custom/packages-migration.spec.ts
+++ b/lib/config/migrations/custom/packages-migration.spec.ts
@@ -4,10 +4,10 @@ describe('config/migrations/custom/packages-migration', () => {
   it('should migrate to package rules', () => {
     expect(PackagesMigration).toMigrate(
       {
-        packages: [{ matchPackagePatterns: ['*'] }],
+        packages: [{ matchPackageNames: ['*'] }],
       },
       {
-        packageRules: [{ matchPackagePatterns: ['*'] }],
+        packageRules: [{ matchPackageNames: ['*'] }],
       },
     );
   });
@@ -15,14 +15,11 @@ describe('config/migrations/custom/packages-migration', () => {
   it('should concat with existing package rules', () => {
     expect(PackagesMigration).toMigrate(
       {
-        packages: [{ matchPackagePatterns: ['*'] }],
+        packages: [{ matchPackageNames: ['*'] }],
         packageRules: [{ matchPackageNames: [] }],
       },
       {
-        packageRules: [
-          { matchPackageNames: [] },
-          { matchPackagePatterns: ['*'] },
-        ],
+        packageRules: [{ matchPackageNames: [] }, { matchPackageNames: ['*'] }],
       },
     );
   });
@@ -30,7 +27,7 @@ describe('config/migrations/custom/packages-migration', () => {
   it('should ignore non array value', () => {
     expect(PackagesMigration).toMigrate(
       {
-        packages: { matchPackagePatterns: ['*'] },
+        packages: { matchPackageNames: ['*'] },
         packageRules: [{ matchPackageNames: [] }],
       },
       {
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 0a523e8e03..3919e0c8fc 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -1305,19 +1305,6 @@ const options: RenovateOptions[] = [
     env: false,
     patternMatch: true,
   },
-  {
-    name: 'excludeRepositories',
-    description:
-      'List of repositories to exclude (e.g. `["**/*-archived"]`). Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-  },
   {
     name: 'matchBaseBranches',
     description:
@@ -1383,19 +1370,6 @@ const options: RenovateOptions[] = [
     cli: false,
     env: false,
   },
-  {
-    name: 'excludePackageNames',
-    description:
-      'Package names to exclude. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-  },
   {
     name: 'matchDepNames',
     description:
@@ -1409,132 +1383,6 @@ const options: RenovateOptions[] = [
     cli: false,
     env: false,
   },
-  {
-    name: 'excludeDepNames',
-    description:
-      'Dep names to exclude. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-    advancedUse: true,
-  },
-  {
-    name: 'matchPackagePrefixes',
-    description:
-      'Package name prefixes to match. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-  },
-  {
-    name: 'excludePackagePrefixes',
-    description:
-      'Package name prefixes to exclude. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-  },
-  {
-    name: 'matchDepPrefixes',
-    description:
-      'Dep names prefixes to match. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-    advancedUse: true,
-  },
-  {
-    name: 'excludeDepPrefixes',
-    description:
-      'Dep names prefixes to exclude. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-    advancedUse: true,
-  },
-  {
-    name: 'matchPackagePatterns',
-    description:
-      'Package name patterns to match. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    format: 'regex',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-  },
-  {
-    name: 'excludePackagePatterns',
-    description:
-      'Package name patterns to exclude. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    format: 'regex',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-  },
-  {
-    name: 'matchDepPatterns',
-    description:
-      'Dep name patterns to match. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    format: 'regex',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-    advancedUse: true,
-  },
-  {
-    name: 'excludeDepPatterns',
-    description:
-      'Dep name patterns to exclude. Valid only within a `packageRules` object.',
-    type: 'array',
-    subType: 'string',
-    format: 'regex',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-    advancedUse: true,
-  },
   {
     name: 'matchCurrentValue',
     description:
@@ -1585,19 +1433,6 @@ const options: RenovateOptions[] = [
     cli: false,
     env: false,
   },
-  {
-    name: 'matchSourceUrlPrefixes',
-    description:
-      'A list of source URL prefixes to match against, commonly used to group monorepos or packages from the same organization.',
-    type: 'array',
-    subType: 'string',
-    allowString: true,
-    stage: 'package',
-    parents: ['packageRules'],
-    mergeable: true,
-    cli: false,
-    env: false,
-  },
   {
     name: 'matchSourceUrls',
     description: 'A list of source URLs to exact match against.',
diff --git a/lib/config/presets/__snapshots__/index.spec.ts.snap b/lib/config/presets/__snapshots__/index.spec.ts.snap
index b3fac71cdc..433ee0a7f9 100644
--- a/lib/config/presets/__snapshots__/index.spec.ts.snap
+++ b/lib/config/presets/__snapshots__/index.spec.ts.snap
@@ -56,7 +56,7 @@ exports[`config/presets/index resolvePreset migrates automerge in presets 1`] =
   },
   "packageRules": [
     {
-      "matchPackagePatterns": [
+      "matchPackageNames": [
         "*",
       ],
       "semanticCommitType": "chore",
@@ -83,7 +83,7 @@ exports[`config/presets/index resolvePreset migrates automerge in presets 1`] =
       "semanticCommitType": "fix",
     },
     {
-      "matchPackagePatterns": [
+      "matchPackageNames": [
         "*",
       ],
       "rangeStrategy": "replace",
@@ -120,13 +120,10 @@ exports[`config/presets/index resolvePreset resolves eslint 1`] = `
     "@types/eslint",
     "babel-eslint",
     "@babel/eslint-parser",
-  ],
-  "matchPackagePrefixes": [
-    "@eslint/",
-    "@stylistic/eslint-plugin",
-    "@types/eslint__",
-    "@typescript-eslint/",
-    "eslint",
+    "@eslint/**",
+    "@types/eslint__**",
+    "@typescript-eslint/**",
+    "eslint**",
   ],
 }
 `;
@@ -137,30 +134,24 @@ exports[`config/presets/index resolvePreset resolves linters 1`] = `
     "All lint-related packages.",
   ],
   "matchPackageNames": [
+    "ember-template-lint**",
     "@types/eslint",
     "babel-eslint",
     "@babel/eslint-parser",
+    "@eslint/**",
+    "@types/eslint__**",
+    "@typescript-eslint/**",
+    "eslint**",
     "friendsofphp/php-cs-fixer",
     "squizlabs/php_codesniffer",
     "symplify/easy-coding-standard",
-    "@stylistic/stylelint-plugin",
+    "stylelint**",
     "codelyzer",
+    "/\\btslint\\b/",
     "prettier",
     "remark-lint",
     "standard",
   ],
-  "matchPackagePatterns": [
-    "\\btslint\\b",
-  ],
-  "matchPackagePrefixes": [
-    "ember-template-lint",
-    "@eslint/",
-    "@stylistic/eslint-plugin",
-    "@types/eslint__",
-    "@typescript-eslint/",
-    "eslint",
-    "stylelint",
-  ],
 }
 `;
 
@@ -176,30 +167,24 @@ exports[`config/presets/index resolvePreset resolves nested groups 1`] = `
         "All lint-related packages.",
       ],
       "matchPackageNames": [
+        "ember-template-lint**",
         "@types/eslint",
         "babel-eslint",
         "@babel/eslint-parser",
+        "@eslint/**",
+        "@types/eslint__**",
+        "@typescript-eslint/**",
+        "eslint**",
         "friendsofphp/php-cs-fixer",
         "squizlabs/php_codesniffer",
         "symplify/easy-coding-standard",
-        "@stylistic/stylelint-plugin",
+        "stylelint**",
         "codelyzer",
+        "/\\btslint\\b/",
         "prettier",
         "remark-lint",
         "standard",
       ],
-      "matchPackagePatterns": [
-        "\\btslint\\b",
-      ],
-      "matchPackagePrefixes": [
-        "ember-template-lint",
-        "@eslint/",
-        "@stylistic/eslint-plugin",
-        "@types/eslint__",
-        "@typescript-eslint/",
-        "eslint",
-        "stylelint",
-      ],
     },
   ],
 }
diff --git a/lib/config/presets/index.spec.ts b/lib/config/presets/index.spec.ts
index a55ae33645..8443eea157 100644
--- a/lib/config/presets/index.spec.ts
+++ b/lib/config/presets/index.spec.ts
@@ -229,27 +229,6 @@ describe('config/presets/index', () => {
       expect(e!.validationMessage).toBeUndefined();
     });
 
-    it('combines two package alls', async () => {
-      config.extends = ['packages:eslint', 'packages:stylelint'];
-      const res = await presets.resolveConfigPresets(config);
-      expect(res).toEqual({
-        matchPackageNames: [
-          '@types/eslint',
-          'babel-eslint',
-          '@babel/eslint-parser',
-          '@stylistic/stylelint-plugin',
-        ],
-        matchPackagePrefixes: [
-          '@eslint/',
-          '@stylistic/eslint-plugin',
-          '@types/eslint__',
-          '@typescript-eslint/',
-          'eslint',
-          'stylelint',
-        ],
-      });
-    });
-
     it('resolves packageRule', async () => {
       config.packageRules = [
         {
@@ -266,13 +245,10 @@ describe('config/presets/index', () => {
               '@types/eslint',
               'babel-eslint',
               '@babel/eslint-parser',
-            ],
-            matchPackagePrefixes: [
-              '@eslint/',
-              '@stylistic/eslint-plugin',
-              '@types/eslint__',
-              '@typescript-eslint/',
-              'eslint',
+              '@eslint/**',
+              '@types/eslint__**',
+              '@typescript-eslint/**',
+              'eslint**',
             ],
           },
         ],
@@ -283,16 +259,14 @@ describe('config/presets/index', () => {
       config.extends = ['packages:eslint'];
       const res = await presets.resolveConfigPresets(config);
       expect(res).toMatchSnapshot();
-      expect(res.matchPackagePrefixes).toHaveLength(5);
+      expect(res.matchPackageNames).toHaveLength(7);
     });
 
     it('resolves linters', async () => {
       config.extends = ['packages:linters'];
       const res = await presets.resolveConfigPresets(config);
       expect(res).toMatchSnapshot();
-      expect(res.matchPackageNames).toHaveLength(11);
-      expect(res.matchPackagePatterns).toHaveLength(1);
-      expect(res.matchPackagePrefixes).toHaveLength(7);
+      expect(res.matchPackageNames).toHaveLength(17);
     });
 
     it('resolves nested groups', async () => {
@@ -301,9 +275,7 @@ describe('config/presets/index', () => {
       expect(res).toMatchSnapshot();
       const rule = res.packageRules![0];
       expect(rule.automerge).toBeTrue();
-      expect(rule.matchPackageNames).toHaveLength(11);
-      expect(rule.matchPackagePatterns).toHaveLength(1);
-      expect(rule.matchPackagePrefixes).toHaveLength(7);
+      expect(rule.matchPackageNames).toHaveLength(17);
     });
 
     it('migrates automerge in presets', async () => {
diff --git a/lib/config/presets/index.ts b/lib/config/presets/index.ts
index 93e8e1fc08..9a96ed1f6f 100644
--- a/lib/config/presets/index.ts
+++ b/lib/config/presets/index.ts
@@ -288,15 +288,7 @@ export async function getPreset(
     // preset is just a collection of other presets
     delete presetConfig.description;
   }
-  const packageListKeys = [
-    'description',
-    'matchPackageNames',
-    'excludePackageNames',
-    'matchPackagePatterns',
-    'excludePackagePatterns',
-    'matchPackagePrefixes',
-    'excludePackagePrefixes',
-  ];
+  const packageListKeys = ['description', 'matchPackageNames'];
   if (presetKeys.every((key) => packageListKeys.includes(key))) {
     delete presetConfig.description;
   }
diff --git a/lib/config/presets/internal/default.ts b/lib/config/presets/internal/default.ts
index 29fe0081b4..d06839b4b9 100644
--- a/lib/config/presets/internal/default.ts
+++ b/lib/config/presets/internal/default.ts
@@ -115,7 +115,7 @@ export const presets: Record<string, Preset> = {
     packageRules: [
       {
         automerge: true,
-        matchPackagePrefixes: ['@types/'],
+        matchPackageNames: ['@types/**'],
       },
     ],
   },
@@ -375,7 +375,7 @@ export const presets: Record<string, Preset> = {
     description: 'Pin all dependency versions except `peerDependencies`.',
     packageRules: [
       {
-        matchPackagePatterns: ['*'],
+        matchPackageNames: ['*'],
         rangeStrategy: 'pin',
       },
       {
@@ -412,7 +412,7 @@ export const presets: Record<string, Preset> = {
       'Pin dependency versions for `devDependencies` and retain SemVer ranges for others.',
     packageRules: [
       {
-        matchPackagePatterns: ['*'],
+        matchPackageNames: ['*'],
         rangeStrategy: 'replace',
       },
       {
@@ -451,7 +451,7 @@ export const presets: Record<string, Preset> = {
   preserveSemverRanges: {
     description:
       'Preserve (but continue to upgrade) any existing SemVer ranges.',
-    packageRules: [{ matchPackagePatterns: ['*'], rangeStrategy: 'replace' }],
+    packageRules: [{ matchPackageNames: ['*'], rangeStrategy: 'replace' }],
   },
   prHourlyLimit1: {
     description: 'Rate limit PR creation to a maximum of one per hour.',
@@ -542,7 +542,7 @@ export const presets: Record<string, Preset> = {
       'Use semantic commit type `fix` for dependencies and `chore` for all others if semantic commits are in use.',
     packageRules: [
       {
-        matchPackagePatterns: ['*'],
+        matchPackageNames: ['*'],
         semanticCommitType: 'chore',
       },
       {
diff --git a/lib/config/presets/internal/group.ts b/lib/config/presets/internal/group.ts
index e8544dd064..97898bab9d 100644
--- a/lib/config/presets/internal/group.ts
+++ b/lib/config/presets/internal/group.ts
@@ -17,7 +17,7 @@ const staticGroups = {
       {
         groupName: 'all dependencies',
         groupSlug: 'all',
-        matchPackagePatterns: ['*'],
+        matchPackageNames: ['*'],
       },
     ],
 
@@ -38,7 +38,7 @@ const staticGroups = {
       {
         groupName: 'all digest updates',
         groupSlug: 'all-digest',
-        matchPackagePatterns: ['*'],
+        matchPackageNames: ['*'],
         matchUpdateTypes: ['digest'],
       },
     ],
@@ -49,7 +49,7 @@ const staticGroups = {
       {
         groupName: 'all non-major dependencies',
         groupSlug: 'all-minor-patch',
-        matchPackagePatterns: ['*'],
+        matchPackageNames: ['*'],
         matchUpdateTypes: ['minor', 'patch'],
       },
     ],
@@ -59,7 +59,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'CodeMirror',
-        matchPackagePrefixes: ['@codemirror/'],
+        matchPackageNames: ['@codemirror/**'],
       },
     ],
   },
@@ -68,7 +68,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'definitelyTyped',
-        matchPackagePrefixes: ['@types/'],
+        matchPackageNames: ['@types/**'],
       },
     ],
   },
@@ -78,7 +78,7 @@ const staticGroups = {
       {
         groupName: '.NET Core Docker containers',
         matchDatasources: ['docker'],
-        matchPackagePrefixes: ['mcr.microsoft.com/dotnet/'],
+        matchPackageNames: ['mcr.microsoft.com/dotnet/**'],
       },
     ],
   },
@@ -87,7 +87,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'flyway',
-        matchPackagePrefixes: ['org.flywaydb:'],
+        matchPackageNames: ['org.flywaydb:*'],
       },
     ],
   },
@@ -96,7 +96,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'Font Awesome',
-        matchPackagePrefixes: ['@fortawesome/'],
+        matchPackageNames: ['@fortawesome/**'],
       },
     ],
   },
@@ -107,8 +107,10 @@ const staticGroups = {
       'fusion-core',
       'fusion-test-utils',
       'fusion-tokens',
+      'fusion-plugin-**',
+      'fusion-react**',
+      'fusion-apollo**',
     ],
-    matchPackagePrefixes: ['fusion-plugin-', 'fusion-react', '^usion-apollo'],
   },
   githubArtifactActions: {
     description:
@@ -151,7 +153,7 @@ const staticGroups = {
         groupName: 'go-openapi packages',
         groupSlug: 'go-openapi',
         matchDatasources: ['go'],
-        matchPackagePrefixes: ['github.com/go-openapi/'],
+        matchPackageNames: ['github.com/go-openapi/**'],
       },
     ],
   },
@@ -160,7 +162,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'hibernate commons',
-        matchPackagePrefixes: ['org.hibernate.common:'],
+        matchPackageNames: ['org.hibernate.common:**'],
       },
     ],
   },
@@ -169,7 +171,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'hibernate core',
-        matchPackagePrefixes: ['org.hibernate:'],
+        matchPackageNames: ['org.hibernate:**'],
       },
     ],
   },
@@ -178,7 +180,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'hibernate ogm',
-        matchPackagePrefixes: ['org.hibernate.ogm:'],
+        matchPackageNames: ['org.hibernate.ogm:**'],
       },
     ],
   },
@@ -187,7 +189,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'hibernate validator',
-        matchPackagePrefixes: ['org.hibernate.validator:'],
+        matchPackageNames: ['org.hibernate.validator:**'],
       },
     ],
   },
@@ -197,7 +199,7 @@ const staticGroups = {
       {
         groupName: 'illuminate packages',
         groupSlug: 'illuminate',
-        matchPackagePrefixes: ['illuminate/'],
+        matchPackageNames: ['illuminate/**'],
       },
     ],
   },
@@ -206,9 +208,9 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'jekyll ecosystem packages',
-        matchSourceUrlPrefixes: [
-          'https://github.com/jekyll/',
-          'https://github.com/github/pages-gem',
+        matchSourceUrls: [
+          'https://github.com/jekyll/**',
+          'https://github.com/github/pages-gem**',
         ],
       },
     ],
@@ -278,7 +280,7 @@ const staticGroups = {
       {
         groupName: 'JWT Framework packages',
         matchDatasources: ['packagist'],
-        matchPackagePrefixes: ['web-token/'],
+        matchPackageNames: ['web-token/**'],
       },
     ],
   },
@@ -289,34 +291,34 @@ const staticGroups = {
         groupName: 'kubernetes packages',
         groupSlug: 'kubernetes-go',
         matchDatasources: ['go'],
-        matchPackagePrefixes: [
-          'k8s.io/api',
-          'k8s.io/apiextensions-apiserver',
-          'k8s.io/apimachinery',
-          'k8s.io/apiserver',
-          'k8s.io/cli-runtime',
-          'k8s.io/client-go',
-          'k8s.io/cloud-provider',
-          'k8s.io/cluster-bootstrap',
-          'k8s.io/code-generator',
-          'k8s.io/component-base',
-          'k8s.io/controller-manager',
-          'k8s.io/cri-api',
+        matchPackageNames: [
+          'k8s.io/api**',
+          'k8s.io/apiextensions-apiserver**',
+          'k8s.io/apimachinery**',
+          'k8s.io/apiserver**',
+          'k8s.io/cli-runtime**',
+          'k8s.io/client-go**',
+          'k8s.io/cloud-provider**',
+          'k8s.io/cluster-bootstrap**',
+          'k8s.io/code-generator**',
+          'k8s.io/component-base**',
+          'k8s.io/controller-manager**',
+          'k8s.io/cri-api**',
           // 'k8s.io/csi-api', has not go.mod set up and does not follow the versioning of other repos
-          'k8s.io/csi-translation-lib',
-          'k8s.io/kube-aggregator',
-          'k8s.io/kube-controller-manager',
-          'k8s.io/kube-proxy',
-          'k8s.io/kube-scheduler',
-          'k8s.io/kubectl',
-          'k8s.io/kubelet',
-          'k8s.io/legacy-cloud-providers',
-          'k8s.io/metrics',
-          'k8s.io/mount-utils',
-          'k8s.io/pod-security-admission',
-          'k8s.io/sample-apiserver',
-          'k8s.io/sample-cli-plugin',
-          'k8s.io/sample-controller',
+          'k8s.io/csi-translation-lib**',
+          'k8s.io/kube-aggregator**',
+          'k8s.io/kube-controller-manager**',
+          'k8s.io/kube-proxy**',
+          'k8s.io/kube-scheduler**',
+          'k8s.io/kubectl**',
+          'k8s.io/kubelet**',
+          'k8s.io/legacy-cloud-providers**',
+          'k8s.io/metrics**',
+          'k8s.io/mount-utils**',
+          'k8s.io/pod-security-admission**',
+          'k8s.io/sample-apiserver**',
+          'k8s.io/sample-cli-plugin**',
+          'k8s.io/sample-controller**',
         ],
       },
     ],
@@ -336,14 +338,14 @@ const staticGroups = {
     packageRules: [
       {
         commitMessageTopic: 'Node.js',
-        excludePackageNames: [
-          'calico/node',
-          'docker.io/calico/node',
-          'kindest/node',
-        ],
         matchDatasources: ['docker'],
         matchDepNames: ['node'],
-        matchPackagePatterns: ['/node$'],
+        matchPackageNames: [
+          '//node$/', // Ends with "node"
+          '!calico/node',
+          '!docker.io/calico/node',
+          '!kindest/node',
+        ],
       },
     ],
   },
@@ -353,7 +355,7 @@ const staticGroups = {
       {
         groupName: 'PHPStan packages',
         matchDatasources: ['packagist'],
-        matchPackagePatterns: ['^phpstan/phpstan$', '/phpstan-', '/larastan'],
+        matchPackageNames: ['phpstan/phpstan', '//phpstan-/', '//larastan/'],
       },
     ],
   },
@@ -362,7 +364,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'polymer packages',
-        matchPackagePrefixes: ['@polymer/'],
+        matchPackageNames: ['@polymer/**'],
       },
     ],
   },
@@ -383,35 +385,35 @@ const staticGroups = {
         groupName: 'Pulumi',
         groupSlug: 'pulumi-node',
         matchDatasources: ['npm'],
-        matchPackagePrefixes: ['@pulumi/'],
+        matchPackageNames: ['@pulumi/**'],
       },
       {
         description: 'Group Pulumi Python packages together.',
         groupName: 'Pulumi',
         groupSlug: 'pulumi-python',
         matchDatasources: ['pypi'],
-        matchPackagePrefixes: ['pulumi-'],
+        matchPackageNames: ['pulumi-**'],
       },
       {
         description: 'Group Pulumi Go packages together.',
         groupName: 'Pulumi',
         groupSlug: 'pulumi-go',
         matchDatasources: ['go'],
-        matchPackagePrefixes: ['github.com/pulumi/'],
+        matchPackageNames: ['github.com/pulumi/**'],
       },
       {
         description: 'Group Pulumi Java packages together.',
         groupName: 'Pulumi',
         groupSlug: 'pulumi-java',
         matchDatasources: ['maven'],
-        matchPackagePrefixes: ['com.pulumi'],
+        matchPackageNames: ['com.pulumi**'],
       },
       {
         description: 'Group Pulumi .NET packages together.',
         groupName: 'Pulumi',
         groupSlug: 'pulumi-dotnet',
         matchDatasources: ['nuget'],
-        matchPackagePrefixes: ['Pulumi'],
+        matchPackageNames: ['Pulumi**'],
       },
     ],
   },
@@ -488,7 +490,7 @@ const staticGroups = {
       {
         groupName: 'remark',
         matchDatasources: ['npm'],
-        matchSourceUrlPrefixes: ['https://github.com/remarkjs/'],
+        matchSourceUrls: ['https://github.com/remarkjs/**'],
       },
     ],
   },
@@ -497,7 +499,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'resilience4j',
-        matchPackagePrefixes: ['io.github.resilience4j:'],
+        matchPackageNames: ['io.github.resilience4j:**'],
       },
     ],
   },
@@ -507,7 +509,7 @@ const staticGroups = {
       {
         groupName: 'omniauth packages',
         matchDatasources: ['rubygems'],
-        matchPackagePrefixes: ['omniauth'],
+        matchPackageNames: ['omniauth**'],
       },
     ],
   },
@@ -540,7 +542,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'socket.io packages',
-        matchPackagePrefixes: ['socket.io'],
+        matchPackageNames: ['socket.io**'],
       },
     ],
   },
@@ -549,7 +551,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring amqp',
-        matchPackagePrefixes: ['org.springframework.amqp:'],
+        matchPackageNames: ['org.springframework.amqp:**'],
       },
     ],
   },
@@ -558,7 +560,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring android',
-        matchPackagePrefixes: ['org.springframework.android:'],
+        matchPackageNames: ['org.springframework.android:**'],
       },
     ],
   },
@@ -567,7 +569,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring batch',
-        matchPackagePrefixes: ['org.springframework.batch:'],
+        matchPackageNames: ['org.springframework.batch:**'],
       },
     ],
   },
@@ -577,7 +579,7 @@ const staticGroups = {
       {
         groupName: 'spring boot',
         matchDepNames: ['org.springframework.boot'],
-        matchPackagePrefixes: ['org.springframework.boot:'],
+        matchPackageNames: ['org.springframework.boot:**'],
       },
     ],
   },
@@ -586,7 +588,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring cloud',
-        matchPackagePrefixes: ['org.springframework.cloud:'],
+        matchPackageNames: ['org.springframework.cloud:**'],
       },
     ],
   },
@@ -595,7 +597,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring core',
-        matchPackagePrefixes: ['org.springframework:'],
+        matchPackageNames: ['org.springframework:**'],
       },
     ],
   },
@@ -604,7 +606,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring data',
-        matchPackagePrefixes: ['org.springframework.data:'],
+        matchPackageNames: ['org.springframework.data:**'],
       },
     ],
   },
@@ -613,7 +615,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring hateoas',
-        matchPackagePrefixes: ['org.springframework.hateoas:'],
+        matchPackageNames: ['org.springframework.hateoas:**'],
       },
     ],
   },
@@ -622,7 +624,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring integration',
-        matchPackagePrefixes: ['org.springframework.integration:'],
+        matchPackageNames: ['org.springframework.integration:**'],
       },
     ],
   },
@@ -631,7 +633,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring kafka',
-        matchPackagePrefixes: ['org.springframework.kafka:'],
+        matchPackageNames: ['org.springframework.kafka:**'],
       },
     ],
   },
@@ -640,7 +642,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring ldap',
-        matchPackagePrefixes: ['org.springframework.ldap:'],
+        matchPackageNames: ['org.springframework.ldap:**'],
       },
     ],
   },
@@ -649,7 +651,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring mobile',
-        matchPackagePrefixes: ['org.springframework.mobile:'],
+        matchPackageNames: ['org.springframework.mobile:**'],
       },
     ],
   },
@@ -658,7 +660,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring osgi',
-        matchPackagePrefixes: ['org.springframework.osgi:'],
+        matchPackageNames: ['org.springframework.osgi:**'],
       },
     ],
   },
@@ -667,7 +669,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring restdocs',
-        matchPackagePrefixes: ['org.springframework.restdocs:'],
+        matchPackageNames: ['org.springframework.restdocs:**'],
       },
     ],
   },
@@ -676,7 +678,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring roo',
-        matchPackagePrefixes: ['org.springframework.roo:'],
+        matchPackageNames: ['org.springframework.roo:**'],
       },
     ],
   },
@@ -685,7 +687,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring scala',
-        matchPackagePrefixes: ['org.springframework.scala:'],
+        matchPackageNames: ['org.springframework.scala:**'],
       },
     ],
   },
@@ -694,7 +696,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring security',
-        matchPackagePrefixes: ['org.springframework.security:'],
+        matchPackageNames: ['org.springframework.security:**'],
       },
     ],
   },
@@ -703,7 +705,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring session',
-        matchPackagePrefixes: ['org.springframework.session:'],
+        matchPackageNames: ['org.springframework.session:**'],
       },
     ],
   },
@@ -712,7 +714,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring shell',
-        matchPackagePrefixes: ['org.springframework.shell:'],
+        matchPackageNames: ['org.springframework.shell:**'],
       },
     ],
   },
@@ -721,7 +723,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring social',
-        matchPackagePrefixes: ['org.springframework.social:'],
+        matchPackageNames: ['org.springframework.social:**'],
       },
     ],
   },
@@ -730,7 +732,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring statemachine',
-        matchPackagePrefixes: ['org.springframework.statemachine:'],
+        matchPackageNames: ['org.springframework.statemachine:**'],
       },
     ],
   },
@@ -739,7 +741,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring webflow',
-        matchPackagePrefixes: ['org.springframework.webflow:'],
+        matchPackageNames: ['org.springframework.webflow:**'],
       },
     ],
   },
@@ -748,7 +750,7 @@ const staticGroups = {
     packageRules: [
       {
         groupName: 'spring ws',
-        matchPackagePrefixes: ['org.springframework.ws:'],
+        matchPackageNames: ['org.springframework.ws:**'],
       },
     ],
   },
@@ -758,7 +760,7 @@ const staticGroups = {
       {
         groupName: 'symfony packages',
         groupSlug: 'symfony',
-        matchPackagePrefixes: ['symfony/'],
+        matchPackageNames: ['symfony/**'],
       },
     ],
   },
diff --git a/lib/config/presets/internal/monorepos.ts b/lib/config/presets/internal/monorepos.ts
index 68cb30ef66..d339553e68 100644
--- a/lib/config/presets/internal/monorepos.ts
+++ b/lib/config/presets/internal/monorepos.ts
@@ -14,13 +14,13 @@ for (const [name, value] of Object.entries(monorepoGroups.repoGroups)) {
 for (const [name, value] of Object.entries(monorepoGroups.orgGroups)) {
   presets[name] = {
     description: `${name} monorepo`,
-    matchSourceUrlPrefixes: toArray(value),
+    matchSourceUrls: toArray(value).map((url: string) => `${url}**`),
   };
 }
 
 for (const [name, value] of Object.entries(monorepoGroups.patternGroups)) {
   presets[name] = {
     description: `${name} monorepo`,
-    matchPackagePatterns: toArray(value),
+    matchPackageNames: toArray(value),
   };
 }
diff --git a/lib/config/presets/internal/packages.ts b/lib/config/presets/internal/packages.ts
index daac49f540..1056e63497 100644
--- a/lib/config/presets/internal/packages.ts
+++ b/lib/config/presets/internal/packages.ts
@@ -14,11 +14,11 @@ export const presets: Record<string, Preset> = {
   },
   apollographql: {
     description: 'All packages published by Apollo GraphQL.',
-    matchSourceUrlPrefixes: ['https://github.com/apollographql/'],
+    matchSourceUrls: ['https://github.com/apollographql/**'],
   },
   emberTemplateLint: {
     description: 'All ember-template-lint packages.',
-    matchPackagePrefixes: ['ember-template-lint'],
+    matchPackageNames: ['ember-template-lint**'],
   },
   eslint: {
     description: 'All ESLint packages.',
@@ -26,13 +26,10 @@ export const presets: Record<string, Preset> = {
       '@types/eslint',
       'babel-eslint',
       '@babel/eslint-parser',
-    ],
-    matchPackagePrefixes: [
-      '@eslint/',
-      '@stylistic/eslint-plugin',
-      '@types/eslint__',
-      '@typescript-eslint/',
-      'eslint',
+      '@eslint/**',
+      '@types/eslint__**',
+      '@typescript-eslint/**',
+      'eslint**',
     ],
   },
   gatsby: {
@@ -42,8 +39,7 @@ export const presets: Record<string, Preset> = {
   googleapis: {
     description: 'All `googleapis` packages.',
     matchDatasources: ['npm'],
-    matchPackageNames: ['google-auth-library'],
-    matchPackagePrefixes: ['@google-cloud/'],
+    matchPackageNames: ['google-auth-library', '@google-cloud/**'],
   },
   jsTest: {
     description: 'JavaScript test packages.',
@@ -77,17 +73,15 @@ export const presets: Record<string, Preset> = {
       'ts-auto-mock',
       'ts-jest',
       'vitest',
-    ],
-    matchPackagePrefixes: [
-      '@testing-library',
-      '@types/testing-library__',
-      '@vitest',
-      'chai',
-      'jest',
-      'mocha',
-      'qunit',
-      'should',
-      'sinon',
+      '@testing-library**',
+      '@types/testing-library__**',
+      '@vitest**',
+      'chai**',
+      'jest**',
+      'mocha**',
+      'qunit**',
+      'should**',
+      'sinon**',
     ],
   },
   linters: {
@@ -103,7 +97,7 @@ export const presets: Record<string, Preset> = {
   },
   mapbox: {
     description: 'All Mapbox-related packages.',
-    matchPackagePrefixes: ['leaflet', 'mapbox'],
+    matchPackageNames: ['leaflet**', 'mapbox**'],
   },
   phpLinters: {
     description: 'All PHP lint-related packages.',
@@ -124,23 +118,21 @@ export const presets: Record<string, Preset> = {
       'phpspec/prophecy-phpunit',
       'phpspec/phpspec',
       'phpunit/phpunit',
+      'pestphp/**',
+      'php-mock/**',
     ],
-    matchPackagePrefixes: ['pestphp/', 'php-mock/'],
   },
   postcss: {
     description: 'All PostCSS packages.',
-    matchPackageNames: ['postcss'],
-    matchPackagePrefixes: ['postcss-'],
+    matchPackageNames: ['postcss', 'postcss-**'],
   },
   react: {
     description: 'All React packages.',
-    matchPackageNames: ['@types/react'],
-    matchPackagePrefixes: ['react'],
+    matchPackageNames: ['@types/react', 'react**'],
   },
   stylelint: {
     description: 'All Stylelint packages.',
-    matchPackageNames: ['@stylistic/stylelint-plugin'],
-    matchPackagePrefixes: ['stylelint'],
+    matchPackageNames: ['stylelint**'],
   },
   test: {
     description: 'Test packages.',
@@ -148,8 +140,7 @@ export const presets: Record<string, Preset> = {
   },
   tslint: {
     description: 'All TSLint packages.',
-    matchPackageNames: ['codelyzer'],
-    matchPackagePatterns: ['\\btslint\\b'],
+    matchPackageNames: ['codelyzer', '/\\btslint\\b/'],
   },
   unitTest: {
     description: 'All unit test packages.',
@@ -158,6 +149,6 @@ export const presets: Record<string, Preset> = {
   vite: {
     description: 'All Vite related packages',
     matchDatasources: ['npm'],
-    matchPackagePatterns: ['^vite$', 'vite-plugin', '@vitejs'],
+    matchPackageNames: ['vite', '**vite-plugin**', '@vitejs/**'],
   },
 };
diff --git a/lib/config/presets/internal/security.ts b/lib/config/presets/internal/security.ts
index e14b05efe1..00998d05ba 100644
--- a/lib/config/presets/internal/security.ts
+++ b/lib/config/presets/internal/security.ts
@@ -5,7 +5,7 @@ export const presets: Record<string, Preset> = {
     description: 'Show OpenSSF badge on pull requests.',
     packageRules: [
       {
-        matchSourceUrlPrefixes: ['https://github.com/'],
+        matchSourceUrls: ['https://github.com/**'],
         prBodyDefinitions: {
           OpenSSF:
             '[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/{{sourceRepo}}/badge)](https://securityscorecards.dev/viewer/?uri=github.com/{{sourceRepo}})',
@@ -28,7 +28,7 @@ export const presets: Record<string, Preset> = {
     packageRules: [
       {
         enabled: false,
-        matchPackagePatterns: ['*'],
+        matchPackageNames: ['*'],
       },
     ],
     vulnerabilityAlerts: {
diff --git a/lib/config/presets/internal/workarounds.ts b/lib/config/presets/internal/workarounds.ts
index 158515222e..708783b154 100644
--- a/lib/config/presets/internal/workarounds.ts
+++ b/lib/config/presets/internal/workarounds.ts
@@ -32,10 +32,10 @@ export const presets: Record<string, Preset> = {
         matchCurrentValue:
           '/^(?<major>\\d+)(?:\\.(?<minor>\\d+)(?:\\.(?<patch>\\d+))?)?-(?<compatibility>.+)-(?<build>\\d+)(?:-r(?<revision>\\d+))?$/',
         matchDatasources: ['docker'],
-        matchPackagePrefixes: [
-          'bitnami/',
-          'docker.io/bitnami/',
-          'gcr.io/bitnami-containers/',
+        matchPackageNames: [
+          'bitnami/*',
+          'docker.io/bitnami/*',
+          'gcr.io/bitnami-containers/*',
         ],
         versioning:
           'regex:^(?<major>\\d+)(?:\\.(?<minor>\\d+)(?:\\.(?<patch>\\d+))?)?(:?-(?<compatibility>.+)-(?<build>\\d+)(?:-r(?<revision>\\d+))?)?$',
@@ -49,8 +49,8 @@ export const presets: Record<string, Preset> = {
         description:
           'Use node versioning for `(containerbase|renovate)/node` images',
         matchDatasources: ['docker'],
-        matchPackagePatterns: [
-          '^(?:(?:docker|ghcr)\\.io/)?(?:containerbase|renovate)/node$',
+        matchPackageNames: [
+          '/^(?:(?:docker|ghcr)\\.io/)?(?:containerbase|renovate)/node$/',
         ],
         versioning: 'node',
       },
@@ -100,7 +100,7 @@ export const presets: Record<string, Preset> = {
       {
         allowedVersions: `!/^1\\.0-\\d+-[a-fA-F0-9]{7}$/`,
         matchManagers: ['sbt'],
-        matchPackagePrefixes: ['org.http4s:'],
+        matchPackageNames: ['org.http4s:**'],
       },
     ],
   },
@@ -142,11 +142,9 @@ export const presets: Record<string, Preset> = {
           'java',
           'java-jre',
           'sapmachine',
-        ],
-        matchPackagePatterns: [
-          '^azul/zulu-openjdk',
-          '^bellsoft/liberica-openj(dk|re)-',
-          '^cimg/openjdk',
+          '/^azul/zulu-openjdk/',
+          '/^bellsoft/liberica-openj(dk|re)-/',
+          '/^cimg/openjdk/',
         ],
         versioning:
           'regex:^(?<major>\\d+)?(\\.(?<minor>\\d+))?(\\.(?<patch>\\d+))?([\\._+](?<build>(\\d\\.?)+)(LTS)?)?(-(?<compatibility>.*))?$',
@@ -176,7 +174,7 @@ export const presets: Record<string, Preset> = {
       {
         allowedVersions: '!/^200\\d{5}(\\.\\d+)?/',
         matchDatasources: ['maven', 'sbt-package'],
-        matchPackagePrefixes: ['commons-'],
+        matchPackageNames: ['commons-**'],
       },
     ],
   },
@@ -213,18 +211,16 @@ export const presets: Record<string, Preset> = {
           'registry.access.redhat.com/rhel-atomic',
           'registry.access.redhat.com/rhel-init',
           'registry.access.redhat.com/rhel-minimal',
-        ],
-        matchPackagePrefixes: [
-          'registry.access.redhat.com/rhceph/',
-          'registry.access.redhat.com/rhgs3/',
-          'registry.access.redhat.com/rhel7',
-          'registry.access.redhat.com/rhel8/',
-          'registry.access.redhat.com/rhel9/',
-          'registry.access.redhat.com/rhscl/',
-          'registry.access.redhat.com/ubi7',
-          'registry.access.redhat.com/ubi8',
-          'registry.access.redhat.com/ubi9',
-          'redhat/',
+          'registry.access.redhat.com/rhceph/**',
+          'registry.access.redhat.com/rhgs3/**',
+          'registry.access.redhat.com/rhel7**',
+          'registry.access.redhat.com/rhel8/**',
+          'registry.access.redhat.com/rhel9/**',
+          'registry.access.redhat.com/rhscl/**',
+          'registry.access.redhat.com/ubi7**',
+          'registry.access.redhat.com/ubi8**',
+          'registry.access.redhat.com/ubi9**',
+          'redhat/**',
         ],
         versioning: 'redhat',
       },
diff --git a/lib/config/types.ts b/lib/config/types.ts
index f872f47931..3d58ebb42e 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -360,13 +360,6 @@ export interface PackageRule
     UpdateConfig,
     Record<string, unknown> {
   description?: string | string[];
-  excludeDepNames?: string[];
-  excludeDepPatterns?: string[];
-  excludeDepPrefixes?: string[];
-  excludePackageNames?: string[];
-  excludePackagePatterns?: string[];
-  excludePackagePrefixes?: string[];
-  excludeRepositories?: string[];
   isVulnerabilityAlert?: boolean;
   matchBaseBranches?: string[];
   matchCategories?: string[];
@@ -376,17 +369,12 @@ export interface PackageRule
   matchCurrentVersion?: string;
   matchDatasources?: string[];
   matchDepNames?: string[];
-  matchDepPatterns?: string[];
-  matchDepPrefixes?: string[];
   matchDepTypes?: string[];
   matchFileNames?: string[];
   matchManagers?: string[];
   matchNewValue?: string;
   matchPackageNames?: string[];
-  matchPackagePatterns?: string[];
-  matchPackagePrefixes?: string[];
   matchRepositories?: string[];
-  matchSourceUrlPrefixes?: string[];
   matchSourceUrls?: string[];
   matchUpdateTypes?: UpdateType[];
   registryUrls?: string[] | null;
diff --git a/lib/config/validation.spec.ts b/lib/config/validation.spec.ts
index 410e5db26c..57e646b393 100644
--- a/lib/config/validation.spec.ts
+++ b/lib/config/validation.spec.ts
@@ -369,8 +369,11 @@ describe('config/validation', () => {
         timezone: 'Asia/Singapore',
         packageRules: [
           {
-            matchPackagePatterns: ['*'],
-            excludePackagePatterns: ['abc ([a-z]+) ([a-z]+))'],
+            matchPackageNames: [
+              '*',
+              '/abc ([a-z]+) ([a-z]+))/',
+              '!/abc ([a-z]+) ([a-z]+))/',
+            ],
             enabled: true,
           },
         ],
@@ -384,7 +387,7 @@ describe('config/validation', () => {
         config,
       );
       expect(warnings).toHaveLength(0);
-      expect(errors).toHaveLength(3);
+      expect(errors).toHaveLength(4);
       expect(errors).toMatchSnapshot();
     });
 
@@ -476,19 +479,13 @@ describe('config/validation', () => {
         lockFileMaintenance: false as any,
         extends: [':timezone(Europe/Brussel)'],
         packageRules: [
-          {
-            excludePackageNames: ['foo'],
-            enabled: true,
-          },
           {
             foo: 1,
           },
           'what?' as any,
           {
-            matchDepPatterns: 'abc ([a-z]+) ([a-z]+))',
-            matchPackagePatterns: 'abc ([a-z]+) ([a-z]+))',
-            excludeDepPatterns: ['abc ([a-z]+) ([a-z]+))'],
-            excludePackagePatterns: ['abc ([a-z]+) ([a-z]+))'],
+            matchPackageNames: '/abc ([a-z]+) ([a-z]+))/',
+            matchDepNames: ['abc ([a-z]+) ([a-z]+))'],
             enabled: false,
           },
         ],
@@ -500,7 +497,7 @@ describe('config/validation', () => {
       );
       expect(warnings).toHaveLength(1);
       expect(errors).toMatchSnapshot();
-      expect(errors).toHaveLength(15);
+      expect(errors).toHaveLength(12);
     });
 
     it('selectors outside packageRules array trigger errors', async () => {
@@ -1044,9 +1041,7 @@ describe('config/validation', () => {
 
     it('warns if only selectors in packageRules', async () => {
       const config = {
-        packageRules: [
-          { matchDepTypes: ['foo'], excludePackageNames: ['bar'] },
-        ],
+        packageRules: [{ matchDepTypes: ['foo'], matchPackageNames: ['bar'] }],
       };
       const { warnings, errors } = await configValidation.validateConfig(
         'repo',
diff --git a/lib/config/validation.ts b/lib/config/validation.ts
index abdee24bff..31cd2ad44b 100644
--- a/lib/config/validation.ts
+++ b/lib/config/validation.ts
@@ -434,21 +434,9 @@ export async function validateConfig(
               'matchDatasources',
               'matchDepTypes',
               'matchDepNames',
-              'matchDepPatterns',
-              'matchDepPrefixes',
               'matchPackageNames',
-              'matchPackagePatterns',
-              'matchPackagePrefixes',
-              'excludeDepNames',
-              'excludeDepPatterns',
-              'excludeDepPrefixes',
-              'excludePackageNames',
-              'excludePackagePatterns',
-              'excludePackagePrefixes',
-              'excludeRepositories',
               'matchCurrentValue',
               'matchCurrentVersion',
-              'matchSourceUrlPrefixes',
               'matchSourceUrls',
               'matchUpdateTypes',
               'matchConfidence',
@@ -606,18 +594,14 @@ export async function validateConfig(
                 }
               }
             }
-            if (
-              [
-                'matchPackagePatterns',
-                'excludePackagePatterns',
-                'matchDepPatterns',
-                'excludeDepPatterns',
-              ].includes(key)
-            ) {
+            if (['matchPackageNames', 'matchDepNames'].includes(key)) {
+              const startPattern = regEx(/!?\//);
+              const endPattern = regEx(/\/g?i?$/);
               for (const pattern of val as string[]) {
-                if (pattern !== '*') {
+                if (startPattern.test(pattern) && endPattern.test(pattern)) {
                   try {
-                    regEx(pattern);
+                    // regEx isn't aware of our !/ prefix but can handle the suffix
+                    regEx(pattern.replace(startPattern, '/'));
                   } catch (e) {
                     errors.push({
                       topic: 'Configuration Error',
diff --git a/lib/data/monorepo.json b/lib/data/monorepo.json
index 8202f67c72..7af318292b 100644
--- a/lib/data/monorepo.json
+++ b/lib/data/monorepo.json
@@ -530,22 +530,22 @@
     "swc": "https://github.com/swc-project/"
   },
   "patternGroups": {
-    "angularmaterial": ["^@angular/material", "^@angular/cdk"],
-    "apache-camel": "^org.apache.camel:",
-    "aws-java-sdk": "^com.amazonaws:aws-java-sdk-",
-    "aws-java-sdk-v2": "^software.amazon.awssdk:",
-    "babel6": "^babel6$",
-    "clarity": ["^@cds/", "^@clr/"],
-    "embroider": "^@embroider/",
-    "forge": "^@forge/",
-    "fullcalendar": "^@fullcalendar/",
-    "hotchocolate": "^HotChocolate\\.",
-    "prometheus-simpleclient": "^io.prometheus:simpleclient",
-    "russh": ["^russh$", "^russh-keys$"],
-    "spfx": ["^@microsoft/sp-", "^@microsoft/eslint-.+-spfx$"],
-    "spock": "^org\\.spockframework:spock-",
-    "syncfusion-dotnet": "^Syncfusion\\.",
-    "testing-library": "^@testing-library/",
-    "wordpress": "^@wordpress/"
+    "angularmaterial": ["/^@angular/material/", "/^@angular/cdk/"],
+    "apache-camel": "/^org.apache.camel:/",
+    "aws-java-sdk": "/^com.amazonaws:aws-java-sdk-/",
+    "aws-java-sdk-v2": "/^software.amazon.awssdk:/",
+    "babel6": "/^babel6$/",
+    "clarity": ["/^@cds//", "/^@clr//"],
+    "embroider": "/^@embroider//",
+    "forge": "/^@forge//",
+    "fullcalendar": "/^@fullcalendar//",
+    "hotchocolate": "/^HotChocolate\\./",
+    "prometheus-simpleclient": "/^io.prometheus:simpleclient/",
+    "russh": ["/^russh$/", "/^russh-keys$/"],
+    "spfx": ["/^@microsoft/sp-/", "/^@microsoft/eslint-.+-spfx$/"],
+    "spock": "/^org\\.spockframework:spock-/",
+    "syncfusion-dotnet": "/^Syncfusion\\./",
+    "testing-library": "/^@testing-library//",
+    "wordpress": "/^@wordpress/"
   }
 }
diff --git a/lib/data/replacements.json b/lib/data/replacements.json
index c0359ac5e3..f40b50a75e 100644
--- a/lib/data/replacements.json
+++ b/lib/data/replacements.json
@@ -136,26 +136,26 @@
       {
         "description": "Replace `containerbase/(buildpack|base)` and `renovate/buildpack` with `ghcr.io/containerbase/base`.",
         "matchDatasources": ["docker"],
-        "matchPackagePatterns": [
-          "^(?:docker\\.io/)?containerbase/(?:buildpack|base)$",
-          "^ghcr\\.io/containerbase/buildpack$",
-          "^(?:docker\\.io/)?renovate/buildpack$"
+        "matchPackageNames": [
+          "/^(?:docker\\.io/)?containerbase/(?:buildpack|base)$/",
+          "/^ghcr\\.io/containerbase/buildpack$/",
+          "/^(?:docker\\.io/)?renovate/buildpack$/"
         ],
         "replacementName": "ghcr.io/containerbase/base"
       },
       {
         "description": "Replace `containerbase/node` and `renovate/node` with `ghcr.io/containerbase/node`.",
         "matchDatasources": ["docker"],
-        "matchPackagePatterns": [
-          "^(?:docker\\.io/)?(?:containerbase|renovate)/node$"
+        "matchPackageNames": [
+          "/^(?:docker\\.io/)?(?:containerbase|renovate)/node$/"
         ],
         "replacementName": "ghcr.io/containerbase/node"
       },
       {
         "description": "Replace `containerbase/sidecar` and `renovate/sidecar` with `ghcr.io/containerbase/sidecar`.",
         "matchDatasources": ["docker"],
-        "matchPackagePatterns": [
-          "^(?:docker\\.io/)?(?:containerbase|renovate)/sidecar$"
+        "matchPackageNames": [
+          "/^(?:docker\\.io/)?(?:containerbase|renovate)/sidecar$/"
         ],
         "replacementName": "ghcr.io/containerbase/sidecar"
       },
@@ -170,7 +170,7 @@
         "matchCurrentValue": "/^slim$/",
         "matchDatasources": ["docker"],
         "matchPackageNames": ["ghcr.io/renovatebot/renovate"],
-        "matchPackagePatterns": ["^(?:docker\\.io/)?renovate/renovate$"],
+        "matchPackageNames": ["/^(?:docker\\.io/)?renovate/renovate$/"],
         "replacementVersion": "latest"
       },
       {
@@ -179,7 +179,7 @@
         "matchCurrentValue": "/-slim$/",
         "matchDatasources": ["docker"],
         "matchPackageNames": ["ghcr.io/renovatebot/renovate"],
-        "matchPackagePatterns": ["^(?:docker\\.io/)?renovate/renovate$"],
+        "matchPackageNames": ["/^(?:docker\\.io/)?renovate/renovate$/"],
         "versioning": "semver"
       }
     ]
@@ -677,7 +677,7 @@
     "packageRules": [
       {
         "matchDatasources": ["docker"],
-        "matchPackagePatterns": ["^k8s\\.gcr\\.io/.+$"],
+        "matchPackageNames": ["/^k8s\\.gcr\\.io/.+$/"],
         "replacementNameTemplate": "{{{replace 'k8s\\.gcr\\.io/' 'registry.k8s.io/' packageName}}}"
       }
     ]
@@ -970,9 +970,9 @@
         "description": "The `zap-stable` image has moved to the `zaproxy` organization.",
         "matchCurrentVersion": ">=2.0.0 <2.14.0",
         "matchDatasources": ["docker"],
-        "matchPackagePatterns": [
-          "^(?:docker\\.io/)?owasp/zap2docker-stable$",
-          "^(?:docker\\.io/)?softwaresecurityproject/zap-stable$"
+        "matchPackageNames": [
+          "/^(?:docker\\.io/)?owasp/zap2docker-stable$/",
+          "/^(?:docker\\.io/)?softwaresecurityproject/zap-stable$/"
         ],
         "replacementName": "zaproxy/zap-stable",
         "replacementVersion": "2.14.0"
@@ -981,9 +981,9 @@
         "description": "The `zap-bare` image has moved to the `zaproxy` organization.",
         "matchCurrentVersion": ">=2.0.0 <2.14.0",
         "matchDatasources": ["docker"],
-        "matchPackagePatterns": [
-          "^(?:docker\\.io/)?owasp/zap2docker-bare$",
-          "^(?:docker\\.io/)?softwaresecurityproject/zap-bare$"
+        "matchPackageNames": [
+          "/^(?:docker\\.io/)?owasp/zap2docker-bare$/",
+          "/^(?:docker\\.io/)?softwaresecurityproject/zap-bare$/"
         ],
         "replacementName": "zaproxy/zap-bare",
         "replacementVersion": "2.14.0"
diff --git a/lib/modules/datasource/npm/npmrc.spec.ts b/lib/modules/datasource/npm/npmrc.spec.ts
index 707270507e..21cc3b35b6 100644
--- a/lib/modules/datasource/npm/npmrc.spec.ts
+++ b/lib/modules/datasource/npm/npmrc.spec.ts
@@ -136,8 +136,8 @@ describe('modules/datasource/npm/npmrc', () => {
               "matchDatasources": [
                 "npm",
               ],
-              "matchPackagePrefixes": [
-                "@fontawesome/",
+              "matchPackageNames": [
+                "@fontawesome/**",
               ],
               "registryUrls": [
                 "https://npm.fontawesome.com/",
diff --git a/lib/modules/datasource/npm/npmrc.ts b/lib/modules/datasource/npm/npmrc.ts
index 3e44ce476c..086bb07338 100644
--- a/lib/modules/datasource/npm/npmrc.ts
+++ b/lib/modules/datasource/npm/npmrc.ts
@@ -111,7 +111,7 @@ export function convertNpmrcToRules(npmrc: Record<string, any>): NpmrcRules {
       if (isHttpUrl(value)) {
         rules.packageRules?.push({
           matchDatasources,
-          matchPackagePrefixes: [scope + '/'],
+          matchPackageNames: [`${scope}/**`],
           registryUrls: [value],
         });
       } else {
@@ -168,10 +168,10 @@ export function setNpmrc(input?: string): void {
 export function resolveRegistryUrl(packageName: string): string {
   let registryUrl = defaultRegistryUrls[0];
   for (const rule of packageRules) {
-    const { matchPackagePrefixes, registryUrls } = rule;
+    const { matchPackageNames, registryUrls } = rule;
     if (
-      !matchPackagePrefixes ||
-      packageName.startsWith(matchPackagePrefixes[0])
+      !matchPackageNames ||
+      packageName.startsWith(matchPackageNames[0].replace(regEx(/\*\*$/), ''))
     ) {
       // TODO: fix types #22198
       registryUrl = registryUrls![0];
diff --git a/lib/modules/versioning/regex/readme.md b/lib/modules/versioning/regex/readme.md
index 2fe779d501..b8512e5c95 100644
--- a/lib/modules/versioning/regex/readme.md
+++ b/lib/modules/versioning/regex/readme.md
@@ -45,7 +45,7 @@ Here is another example, this time for handling Bitnami Docker images, which use
   "packageRules": [
     {
       "matchDatasources": ["docker"],
-      "matchPackagePrefixes": ["bitnami/", "docker.io/bitnami/"],
+      "matchPackageNamees": ["bitnami/**", "docker.io/bitnami/**"],
       "versioning": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)(?:-(?<compatibility>.+)(?<build>\\d+)-r(?<revision>\\d+))?$"
     }
   ]
diff --git a/lib/util/package-rules/base.ts b/lib/util/package-rules/base.ts
index 981a115753..121a982a7f 100644
--- a/lib/util/package-rules/base.ts
+++ b/lib/util/package-rules/base.ts
@@ -2,19 +2,6 @@ import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
 import type { MatcherApi } from './types';
 
 export abstract class Matcher implements MatcherApi {
-  /**
-   * Test exclusion packageRule against inputConfig
-   * @return null if no rules are defined, true if exclusion should be applied and else false
-   * @param inputConfig
-   * @param packageRule
-   */
-  excludes(
-    inputConfig: PackageRuleInputConfig,
-    packageRule: PackageRule,
-  ): boolean | null {
-    return null;
-  }
-
   /**
    * Test match packageRule against inputConfig
    * @return null if no rules are defined, true if match should be applied and else false
diff --git a/lib/util/package-rules/dep-names.spec.ts b/lib/util/package-rules/dep-names.spec.ts
index eb0bea92f2..3a97c5696e 100644
--- a/lib/util/package-rules/dep-names.spec.ts
+++ b/lib/util/package-rules/dep-names.spec.ts
@@ -16,18 +16,4 @@ describe('util/package-rules/dep-names', () => {
       expect(result).toBeFalse();
     });
   });
-
-  describe('exclude', () => {
-    it('should return false if packageFile is not defined', () => {
-      const result = depNameMatcher.excludes(
-        {
-          depName: undefined,
-        },
-        {
-          excludeDepNames: ['@opentelemetry/http'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-  });
 });
diff --git a/lib/util/package-rules/dep-names.ts b/lib/util/package-rules/dep-names.ts
index f9278ba650..0b808de798 100644
--- a/lib/util/package-rules/dep-names.ts
+++ b/lib/util/package-rules/dep-names.ts
@@ -16,17 +16,4 @@ export class DepNameMatcher extends Matcher {
     }
     return matchRegexOrGlobList(depName, matchDepNames);
   }
-
-  override excludes(
-    { depName }: PackageRuleInputConfig,
-    { excludeDepNames }: PackageRule,
-  ): boolean | null {
-    if (is.undefined(excludeDepNames)) {
-      return null;
-    }
-    if (is.undefined(depName)) {
-      return false;
-    }
-    return matchRegexOrGlobList(depName, excludeDepNames);
-  }
 }
diff --git a/lib/util/package-rules/dep-patterns.spec.ts b/lib/util/package-rules/dep-patterns.spec.ts
deleted file mode 100644
index 103adcfb30..0000000000
--- a/lib/util/package-rules/dep-patterns.spec.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { DepPatternsMatcher } from './dep-patterns';
-
-describe('util/package-rules/dep-patterns', () => {
-  const depPatternsMatcher = new DepPatternsMatcher();
-
-  describe('match', () => {
-    it('should return false if depName is not defined', () => {
-      const result = depPatternsMatcher.matches(
-        {
-          depName: undefined,
-        },
-        {
-          matchDepPatterns: ['@opentelemetry/http'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-
-    it('should massage wildcards', () => {
-      const result = depPatternsMatcher.matches(
-        {
-          depName: 'http',
-        },
-        {
-          matchDepPatterns: ['*'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-
-    it('should convert to regex', () => {
-      const result = depPatternsMatcher.matches(
-        {
-          depName: 'http',
-        },
-        {
-          matchDepPatterns: ['^h'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-  });
-
-  describe('exclude', () => {
-    it('should return false if depName is not defined', () => {
-      const result = depPatternsMatcher.excludes(
-        {
-          depName: undefined,
-        },
-        {
-          excludeDepPatterns: ['@opentelemetry/http'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-
-    it('should massage wildcards', () => {
-      const result = depPatternsMatcher.excludes(
-        {
-          depName: 'http',
-        },
-        {
-          excludeDepPatterns: ['*'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-
-    it('should convert to regex', () => {
-      const result = depPatternsMatcher.excludes(
-        {
-          depName: 'http',
-        },
-        {
-          excludeDepPatterns: ['^h'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-  });
-});
diff --git a/lib/util/package-rules/dep-patterns.ts b/lib/util/package-rules/dep-patterns.ts
deleted file mode 100644
index 21d5330100..0000000000
--- a/lib/util/package-rules/dep-patterns.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import is from '@sindresorhus/is';
-import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
-import { matchRegexOrGlobList } from '../string-match';
-import { Matcher } from './base';
-
-export class DepPatternsMatcher extends Matcher {
-  override matches(
-    { depName }: PackageRuleInputConfig,
-    { matchDepPatterns }: PackageRule,
-  ): boolean | null {
-    if (is.undefined(matchDepPatterns)) {
-      return null;
-    }
-
-    if (is.undefined(depName)) {
-      return false;
-    }
-
-    const massagedPatterns = matchDepPatterns.map((pattern) =>
-      pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
-    );
-    return matchRegexOrGlobList(depName, massagedPatterns);
-  }
-
-  override excludes(
-    { depName }: PackageRuleInputConfig,
-    { excludeDepPatterns }: PackageRule,
-  ): boolean | null {
-    if (is.undefined(excludeDepPatterns)) {
-      return null;
-    }
-    if (is.undefined(depName)) {
-      return false;
-    }
-
-    const massagedPatterns = excludeDepPatterns.map((pattern) =>
-      pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
-    );
-    return matchRegexOrGlobList(depName, massagedPatterns);
-  }
-}
diff --git a/lib/util/package-rules/dep-prefixes.spec.ts b/lib/util/package-rules/dep-prefixes.spec.ts
deleted file mode 100644
index d5b35da075..0000000000
--- a/lib/util/package-rules/dep-prefixes.spec.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { DepPrefixesMatcher } from './dep-prefixes';
-
-describe('util/package-rules/dep-prefixes', () => {
-  const depPrefixesMatcher = new DepPrefixesMatcher();
-
-  describe('match', () => {
-    it('should return null if matchDepPrefixes is not defined', () => {
-      const result = depPrefixesMatcher.matches(
-        {
-          depName: 'abc1',
-        },
-        {
-          matchDepPrefixes: undefined,
-        },
-      );
-      expect(result).toBeNull();
-    });
-
-    it('should return false if depName is not defined', () => {
-      const result = depPrefixesMatcher.matches(
-        {
-          depName: undefined,
-        },
-        {
-          matchDepPrefixes: ['@opentelemetry'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-
-    it('should return true if depName matched', () => {
-      const result = depPrefixesMatcher.matches(
-        {
-          depName: 'abc1',
-        },
-        {
-          matchDepPrefixes: ['abc'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-
-    it('should return false if depName does not match', () => {
-      const result = depPrefixesMatcher.matches(
-        {
-          depName: 'abc1',
-        },
-        {
-          matchDepPrefixes: ['def'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-  });
-
-  describe('exclude', () => {
-    it('should return null if excludeDepPrefixes is not defined', () => {
-      const result = depPrefixesMatcher.excludes(
-        {
-          depName: 'abc1',
-        },
-        {
-          excludeDepPrefixes: undefined,
-        },
-      );
-      expect(result).toBeNull();
-    });
-
-    it('should return false if depName is not defined', () => {
-      const result = depPrefixesMatcher.excludes(
-        {
-          depName: undefined,
-        },
-        {
-          excludeDepPrefixes: ['@opentelemetry'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-
-    it('should return true if depName matched', () => {
-      const result = depPrefixesMatcher.excludes(
-        {
-          depName: 'abc1',
-        },
-        {
-          excludeDepPrefixes: ['abc'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-
-    it('should return false if depName does not match', () => {
-      const result = depPrefixesMatcher.excludes(
-        {
-          depName: 'abc1',
-        },
-        {
-          excludeDepPrefixes: ['def'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-  });
-});
diff --git a/lib/util/package-rules/dep-prefixes.ts b/lib/util/package-rules/dep-prefixes.ts
deleted file mode 100644
index 351df41f86..0000000000
--- a/lib/util/package-rules/dep-prefixes.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import is from '@sindresorhus/is';
-import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
-import { Matcher } from './base';
-
-export class DepPrefixesMatcher extends Matcher {
-  override matches(
-    { depName }: PackageRuleInputConfig,
-    { matchDepPrefixes }: PackageRule,
-  ): boolean | null {
-    if (is.undefined(matchDepPrefixes)) {
-      return null;
-    }
-
-    if (is.undefined(depName)) {
-      return false;
-    }
-
-    return matchDepPrefixes.some((prefix) => depName.startsWith(prefix));
-  }
-
-  override excludes(
-    { depName }: PackageRuleInputConfig,
-    { excludeDepPrefixes }: PackageRule,
-  ): boolean | null {
-    if (is.undefined(excludeDepPrefixes)) {
-      return null;
-    }
-
-    if (is.undefined(depName)) {
-      return false;
-    }
-
-    return excludeDepPrefixes.some((prefix) => depName.startsWith(prefix));
-  }
-}
diff --git a/lib/util/package-rules/index.spec.ts b/lib/util/package-rules/index.spec.ts
index d3a2bce836..ce06e832e9 100644
--- a/lib/util/package-rules/index.spec.ts
+++ b/lib/util/package-rules/index.spec.ts
@@ -19,20 +19,15 @@ describe('util/package-rules/index', () => {
 
     packageRules: [
       {
-        matchPackageNames: ['a', 'b'],
-        matchPackagePrefixes: ['xyz/'],
-        excludePackagePrefixes: ['xyz/foo'],
+        matchPackageNames: ['a', 'b', 'xyz/**', '!xyz/foo**'],
         x: 2,
       },
       {
-        matchPackagePatterns: ['a', 'b'],
-        excludePackageNames: ['aa'],
-        excludePackagePatterns: ['d'],
+        matchPackageNames: ['/a/', '/b/', '!aa', '!/d/'],
         y: 2,
       },
       {
-        matchPackagePrefixes: ['xyz/'],
-        excludePackageNames: ['xyz/foo'],
+        matchPackageNames: ['xyz/**', '!xyz/foo'],
         groupName: 'xyz',
       },
     ],
@@ -46,23 +41,19 @@ describe('util/package-rules/index', () => {
       currentValue: '1.0.0',
       packageRules: [
         {
-          matchPackagePatterns: ['*'],
+          matchPackageNames: ['*'],
           matchCurrentVersion: '<= 2.0.0',
         },
         {
           matchPackageNames: ['b'],
           matchCurrentVersion: '<= 2.0.0',
         },
-        {
-          excludePackagePatterns: ['*'],
-        },
         {
           matchUpdateTypes: ['bump'],
           labels: ['bump'],
         },
         {
-          excludePackageNames: ['a'],
-          matchPackageNames: ['b'],
+          matchPackageNames: ['b', '!a'],
         },
         {
           matchCurrentVersion: '<= 2.0.0',
@@ -105,17 +96,7 @@ describe('util/package-rules/index', () => {
     expect(res.groupName).toBeUndefined();
   });
 
-  it('applies matchPackagePrefixes', () => {
-    const dep = {
-      packageName: 'xyz/abc',
-    };
-    const res = applyPackageRules({ ...config1, ...dep });
-    expect(res.x).toBe(2);
-    expect(res.y).toBe(2);
-    expect(res.groupName).toBe('xyz');
-  });
-
-  it('applies excludePackageNames', () => {
+  it('applies matchPackageNames', () => {
     const dep = {
       packageName: 'xyz/foo',
     };
@@ -123,15 +104,6 @@ describe('util/package-rules/index', () => {
     expect(res.groupName).toBeUndefined();
   });
 
-  it('applies excludePackagePrefixes', () => {
-    const dep = {
-      packageName: 'xyz/foo-a',
-    };
-    const res = applyPackageRules({ ...config1, ...dep });
-    expect(res.x).toBeUndefined();
-    expect(res.groupName).toBe('xyz');
-  });
-
   it('applies the second second rule', () => {
     const dep = {
       packageName: 'bc',
@@ -165,33 +137,14 @@ describe('util/package-rules/index', () => {
       updateType: 'lockFileMaintenance' as UpdateType,
       packageRules: [
         {
-          excludePackagePatterns: ['^foo'],
-          automerge: false,
-        },
-      ],
-    };
-    const res = applyPackageRules(dep);
-    expect(res.automerge).toBeFalse();
-    const res2 = applyPackageRules({ ...dep, packageName: 'foo' });
-    expect(res2.automerge).toBeTrue();
-  });
-
-  it('do not apply rule with empty matchPackagePattern', () => {
-    const dep = {
-      automerge: true,
-      updateType: 'lockFileMaintenance' as UpdateType,
-      packageRules: [
-        {
-          matchPackagePatterns: [],
-          excludePackagePatterns: ['^foo'],
+          matchPackageNames: ['!/^foo/'],
           automerge: false,
         },
       ],
     };
+    // This should not match
     const res = applyPackageRules(dep);
     expect(res.automerge).toBeTrue();
-    const res2 = applyPackageRules({ ...dep, packageName: 'foo' });
-    expect(res2.automerge).toBeTrue();
   });
 
   it('do apply rule with matchPackageName', () => {
@@ -262,7 +215,7 @@ describe('util/package-rules/index', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          excludePackageNames: ['foo'],
+          matchPackageNames: ['!foo'],
           x: 1,
         },
       ],
@@ -283,8 +236,7 @@ describe('util/package-rules/index', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          matchPackageNames: ['neutrino'],
-          matchPackagePatterns: ['^@neutrino\\/'],
+          matchPackageNames: ['neutrino', '/^@neutrino\\//'],
           x: 1,
         },
       ],
@@ -583,13 +535,13 @@ describe('util/package-rules/index', () => {
     expect(res.y).toBeUndefined();
   });
 
-  it('matches matchSourceUrlPrefixes', () => {
+  it('matches matchSourceUrls with glob', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          matchSourceUrlPrefixes: [
-            'https://github.com/foo/bar',
-            'https://github.com/renovatebot/',
+          matchSourceUrls: [
+            'https://github.com/foo/bar**',
+            'https://github.com/renovatebot/**',
           ],
           x: 1,
         },
@@ -605,13 +557,13 @@ describe('util/package-rules/index', () => {
     expect(res.x).toBe(1);
   });
 
-  it('non-matches matchSourceUrlPrefixes', () => {
+  it('non-matches matchSourceUrls with globs', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          matchSourceUrlPrefixes: [
-            'https://github.com/foo/bar',
-            'https://github.com/renovatebot/',
+          matchSourceUrls: [
+            'https://github.com/foo/bar**',
+            'https://github.com/renovatebot/**',
           ],
           x: 1,
         },
@@ -627,13 +579,13 @@ describe('util/package-rules/index', () => {
     expect(res.x).toBeUndefined();
   });
 
-  it('handles matchSourceUrlPrefixes when missing sourceUrl', () => {
+  it('handles matchSourceUrls when missing sourceUrl', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          matchSourceUrlPrefixes: [
-            'https://github.com/foo/bar',
-            'https://github.com/renovatebot/',
+          matchSourceUrls: [
+            'https://github.com/foo/bar**',
+            'https://github.com/renovatebot/**',
           ],
           x: 1,
         },
@@ -692,27 +644,6 @@ describe('util/package-rules/index', () => {
     expect(res.x).toBeUndefined();
   });
 
-  it('handles matchSourceUrls when missing sourceUrl', () => {
-    const config: TestConfig = {
-      packageRules: [
-        {
-          matchSourceUrls: [
-            'https://github.com/foo/bar',
-            'https://github.com/renovatebot/',
-          ],
-          x: 1,
-        },
-      ],
-    };
-    const dep = {
-      depType: 'dependencies',
-      packageName: 'a',
-      updateType: 'patch' as UpdateType,
-    };
-    const res = applyPackageRules({ ...config, ...dep });
-    expect(res.x).toBeUndefined();
-  });
-
   describe('matchConfidence', () => {
     const hostRule: HostRule = {
       hostType: 'merge-confidence',
@@ -1091,12 +1022,12 @@ describe('util/package-rules/index', () => {
       packageName: 'foo',
       packageRules: [
         {
-          matchPackagePatterns: ['*'],
+          matchPackageNames: ['*'],
           groupName: 'A',
           groupSlug: 'a',
         },
         {
-          matchPackagePatterns: ['*'],
+          matchPackageNames: ['*'],
           groupName: 'B',
         },
       ],
@@ -1105,13 +1036,13 @@ describe('util/package-rules/index', () => {
     expect(res.groupSlug).toBe('b');
   });
 
-  it('matches matchSourceUrlPrefixes(case-insensitive)', () => {
+  it('matches matchSourceUrls with patterns (case-insensitive)', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          matchSourceUrlPrefixes: [
-            'https://github.com/foo/bar',
-            'https://github.com/Renovatebot/',
+          matchSourceUrls: [
+            'https://github.com/foo/bar**',
+            'https://github.com/Renovatebot/**',
           ],
           x: 1,
         },
@@ -1224,123 +1155,20 @@ describe('util/package-rules/index', () => {
     expect(res2.x).toBeUndefined();
   });
 
-  it('matches excludeDepNames(depName)', () => {
+  it('matches if there are no matchers', () => {
     const config: TestConfig = {
       packageRules: [
         {
-          excludeDepNames: ['test1'],
           x: 1,
         },
       ],
     };
 
-    const res1 = applyPackageRules({
-      ...config,
-      depName: 'test1',
-    });
-    const res2 = applyPackageRules({
+    const res = applyPackageRules({
       ...config,
       depName: 'test2',
     });
-    applyPackageRules(config); // coverage
 
-    expect(res1.x).toBeUndefined();
-    expect(res2.x).toBe(1);
-  });
-
-  it('matches matchDepPatterns(depName)', () => {
-    const config: TestConfig = {
-      packageRules: [
-        {
-          matchDepPatterns: ['^test$'],
-          x: 1,
-        },
-      ],
-    };
-
-    const res1 = applyPackageRules({
-      ...config,
-      depName: 'test',
-    });
-    const res2 = applyPackageRules({
-      ...config,
-      depName: 'test1',
-    });
-    applyPackageRules(config); // coverage
-
-    expect(res1.x).toBe(1);
-    expect(res2.x).toBeUndefined();
-  });
-
-  it('matches excludeDepPatterns(depName)', () => {
-    const config: TestConfig = {
-      packageRules: [
-        {
-          excludeDepPatterns: ['^test$'],
-          x: 1,
-        },
-      ],
-    };
-
-    const res1 = applyPackageRules({
-      ...config,
-      depName: 'test',
-    });
-    const res2 = applyPackageRules({
-      ...config,
-      depName: 'test1',
-    });
-    applyPackageRules(config); // coverage
-
-    expect(res1.x).toBeUndefined();
-    expect(res2.x).toBe(1);
-  });
-
-  it('matches matchDepPrefixes(depName)', () => {
-    const config: TestConfig = {
-      packageRules: [
-        {
-          matchDepPrefixes: ['abc'],
-          x: 1,
-        },
-      ],
-    };
-
-    const res1 = applyPackageRules({
-      ...config,
-      depName: 'abc1',
-    });
-    const res2 = applyPackageRules({
-      ...config,
-      depName: 'def1',
-    });
-    applyPackageRules(config); // coverage
-
-    expect(res1.x).toBe(1);
-    expect(res2.x).toBeUndefined();
-  });
-
-  it('matches excludeDepPrefixes(depName)', () => {
-    const config: TestConfig = {
-      packageRules: [
-        {
-          excludeDepPrefixes: ['abc'],
-          x: 1,
-        },
-      ],
-    };
-
-    const res1 = applyPackageRules({
-      ...config,
-      depName: 'abc1',
-    });
-    const res2 = applyPackageRules({
-      ...config,
-      depName: 'def1',
-    });
-    applyPackageRules(config); // coverage
-
-    expect(res1.x).toBeUndefined();
-    expect(res2.x).toBe(1);
+    expect(res.x).toBe(1);
   });
 });
diff --git a/lib/util/package-rules/index.ts b/lib/util/package-rules/index.ts
index 73036d5cda..59f27b6049 100644
--- a/lib/util/package-rules/index.ts
+++ b/lib/util/package-rules/index.ts
@@ -5,60 +5,25 @@ 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';
 
 function matchesRule(
   inputConfig: PackageRuleInputConfig,
   packageRule: PackageRule,
 ): boolean {
-  let positiveMatch = true;
-  let matchApplied = false;
-  // matches
-  for (const groupMatchers of matchers) {
-    const isMatch = matcherOR(
-      'matches',
-      groupMatchers,
-      inputConfig,
-      packageRule,
-    );
+  for (const matcher of matchers) {
+    const isMatch = matcher.matches(inputConfig, packageRule);
 
     // no rules are defined
     if (is.nullOrUndefined(isMatch)) {
       continue;
     }
 
-    matchApplied = true;
-
     if (!is.truthy(isMatch)) {
       return false;
     }
   }
 
-  // not a single match rule is defined --> assume to match everything
-  if (!matchApplied) {
-    positiveMatch = true;
-  }
-
-  // excludes
-  for (const groupExcludes of matchers) {
-    const isExclude = matcherOR(
-      'excludes',
-      groupExcludes,
-      inputConfig,
-      packageRule,
-    );
-
-    // no rules are defined
-    if (is.nullOrUndefined(isExclude)) {
-      continue;
-    }
-
-    if (isExclude) {
-      return false;
-    }
-  }
-
-  return positiveMatch;
+  return true;
 }
 
 export function applyPackageRules<T extends PackageRuleInputConfig>(
diff --git a/lib/util/package-rules/matchers.ts b/lib/util/package-rules/matchers.ts
index 61f68973c9..9ad89afe0a 100644
--- a/lib/util/package-rules/matchers.ts
+++ b/lib/util/package-rules/matchers.ts
@@ -5,23 +5,18 @@ import { CurrentValueMatcher } from './current-value';
 import { CurrentVersionMatcher } from './current-version';
 import { DatasourcesMatcher } from './datasources';
 import { DepNameMatcher } from './dep-names';
-import { DepPatternsMatcher } from './dep-patterns';
-import { DepPrefixesMatcher } from './dep-prefixes';
 import { DepTypesMatcher } from './dep-types';
 import { FileNamesMatcher } from './files';
 import { ManagersMatcher } from './managers';
 import { MergeConfidenceMatcher } from './merge-confidence';
 import { NewValueMatcher } from './new-value';
 import { PackageNameMatcher } from './package-names';
-import { PackagePatternsMatcher } from './package-patterns';
-import { PackagePrefixesMatcher } from './package-prefixes';
 import { RepositoriesMatcher } from './repositories';
-import { SourceUrlPrefixesMatcher } from './sourceurl-prefixes';
 import { SourceUrlsMatcher } from './sourceurls';
 import type { MatcherApi } from './types';
 import { UpdateTypesMatcher } from './update-types';
 
-const matchers: MatcherApi[][] = [];
+const matchers: MatcherApi[] = [];
 export default matchers;
 
 // Each matcher under the same index will use a logical OR, if multiple matchers are applied AND will be used
@@ -29,25 +24,19 @@ export default matchers;
 // applyPackageRules evaluates matchers in the order of insertion and returns early on failure.
 // Therefore, when multiple matchers are set in a single packageRule, some may not be checked.
 // Since matchConfidence matcher can abort the run due to unauthenticated use, it should be evaluated first.
-matchers.push([new MergeConfidenceMatcher()]);
-matchers.push([new RepositoriesMatcher()]);
-matchers.push([new BaseBranchesMatcher()]);
-matchers.push([new CategoriesMatcher()]);
-matchers.push([new ManagersMatcher()]);
-matchers.push([new FileNamesMatcher()]);
-matchers.push([new DatasourcesMatcher()]);
-matchers.push([
-  new DepNameMatcher(),
-  new DepPatternsMatcher(),
-  new DepPrefixesMatcher(),
-  new PackageNameMatcher(),
-  new PackagePatternsMatcher(),
-  new PackagePrefixesMatcher(),
-]);
-matchers.push([new DepTypesMatcher()]);
-matchers.push([new CurrentValueMatcher()]);
-matchers.push([new CurrentVersionMatcher()]);
-matchers.push([new UpdateTypesMatcher()]);
-matchers.push([new SourceUrlsMatcher(), new SourceUrlPrefixesMatcher()]);
-matchers.push([new NewValueMatcher()]);
-matchers.push([new CurrentAgeMatcher()]);
+matchers.push(new MergeConfidenceMatcher());
+matchers.push(new RepositoriesMatcher());
+matchers.push(new BaseBranchesMatcher());
+matchers.push(new CategoriesMatcher());
+matchers.push(new ManagersMatcher());
+matchers.push(new FileNamesMatcher());
+matchers.push(new DatasourcesMatcher());
+matchers.push(new PackageNameMatcher());
+matchers.push(new DepNameMatcher());
+matchers.push(new DepTypesMatcher());
+matchers.push(new CurrentValueMatcher());
+matchers.push(new CurrentVersionMatcher());
+matchers.push(new UpdateTypesMatcher());
+matchers.push(new SourceUrlsMatcher());
+matchers.push(new NewValueMatcher());
+matchers.push(new CurrentAgeMatcher());
diff --git a/lib/util/package-rules/matchers.ts.orig b/lib/util/package-rules/matchers.ts.orig
new file mode 100644
index 0000000000..cab1058e1d
--- /dev/null
+++ b/lib/util/package-rules/matchers.ts.orig
@@ -0,0 +1,67 @@
+import { BaseBranchesMatcher } from './base-branches';
+import { CategoriesMatcher } from './categories';
+import { CurrentAgeMatcher } from './current-age';
+import { CurrentValueMatcher } from './current-value';
+import { CurrentVersionMatcher } from './current-version';
+import { DatasourcesMatcher } from './datasources';
+import { DepNameMatcher } from './dep-names';
+import { DepTypesMatcher } from './dep-types';
+import { FileNamesMatcher } from './files';
+import { ManagersMatcher } from './managers';
+import { MergeConfidenceMatcher } from './merge-confidence';
+import { NewValueMatcher } from './new-value';
+import { PackageNameMatcher } from './package-names';
+import { RepositoriesMatcher } from './repositories';
+import { SourceUrlsMatcher } from './sourceurls';
+import type { MatcherApi } from './types';
+import { UpdateTypesMatcher } from './update-types';
+
+const matchers: MatcherApi[] = [];
+export default matchers;
+
+// Each matcher under the same index will use a logical OR, if multiple matchers are applied AND will be used
+
+// applyPackageRules evaluates matchers in the order of insertion and returns early on failure.
+// Therefore, when multiple matchers are set in a single packageRule, some may not be checked.
+// Since matchConfidence matcher can abort the run due to unauthenticated use, it should be evaluated first.
+<<<<<<< HEAD
+matchers.push([new MergeConfidenceMatcher()]);
+matchers.push([new RepositoriesMatcher()]);
+matchers.push([new BaseBranchesMatcher()]);
+matchers.push([new CategoriesMatcher()]);
+matchers.push([new ManagersMatcher()]);
+matchers.push([new FileNamesMatcher()]);
+matchers.push([new DatasourcesMatcher()]);
+matchers.push([
+  new DepNameMatcher(),
+  new DepPatternsMatcher(),
+  new DepPrefixesMatcher(),
+  new PackageNameMatcher(),
+  new PackagePatternsMatcher(),
+  new PackagePrefixesMatcher(),
+]);
+matchers.push([new DepTypesMatcher()]);
+matchers.push([new CurrentValueMatcher()]);
+matchers.push([new CurrentVersionMatcher()]);
+matchers.push([new UpdateTypesMatcher()]);
+matchers.push([new SourceUrlsMatcher(), new SourceUrlPrefixesMatcher()]);
+matchers.push([new NewValueMatcher()]);
+matchers.push([new CurrentAgeMatcher()]);
+=======
+matchers.push(new MergeConfidenceMatcher());
+matchers.push(new DepNameMatcher());
+matchers.push(new PackageNameMatcher());
+matchers.push(new FileNamesMatcher());
+matchers.push(new DepTypesMatcher());
+matchers.push(new BaseBranchesMatcher());
+matchers.push(new ManagersMatcher());
+matchers.push(new DatasourcesMatcher());
+matchers.push(new UpdateTypesMatcher());
+matchers.push(new SourceUrlsMatcher());
+matchers.push(new CurrentValueMatcher());
+matchers.push(new NewValueMatcher());
+matchers.push(new CurrentVersionMatcher());
+matchers.push(new RepositoriesMatcher());
+matchers.push(new CategoriesMatcher());
+matchers.push(new CurrentAgeMatcher());
+>>>>>>> b66597a89 (feat(packageRules): migrate matchers and excludes (#28602))
diff --git a/lib/util/package-rules/package-names.spec.ts b/lib/util/package-rules/package-names.spec.ts
index aa6d8035b7..47fd4fcdfa 100644
--- a/lib/util/package-rules/package-names.spec.ts
+++ b/lib/util/package-rules/package-names.spec.ts
@@ -40,28 +40,14 @@ describe('util/package-rules/package-names', () => {
       );
       expect(result).toBeTrue();
     });
-  });
 
-  describe('exclude', () => {
-    it('should return false if packageName is not defined', () => {
-      const result = packageNameMatcher.excludes(
-        {
-          packageName: undefined,
-        },
-        {
-          excludePackageNames: ['@opentelemetry/http'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-
-    it('should excludePackageName', () => {
-      const result = packageNameMatcher.excludes(
+    it('should match pattern', () => {
+      const result = packageNameMatcher.matches(
         {
-          packageName: 'def',
+          packageName: 'b',
         },
         {
-          excludePackageNames: ['def'],
+          matchPackageNames: ['/b/'],
         },
       );
       expect(result).toBeTrue();
diff --git a/lib/util/package-rules/package-names.ts b/lib/util/package-rules/package-names.ts
index fe21b0b8b8..9586fb5caa 100644
--- a/lib/util/package-rules/package-names.ts
+++ b/lib/util/package-rules/package-names.ts
@@ -17,18 +17,4 @@ export class PackageNameMatcher extends Matcher {
     }
     return matchRegexOrGlobList(packageName, matchPackageNames);
   }
-
-  override excludes(
-    { packageName }: PackageRuleInputConfig,
-    packageRule: PackageRule,
-  ): boolean | null {
-    const { excludePackageNames } = packageRule;
-    if (is.undefined(excludePackageNames)) {
-      return null;
-    }
-    if (!packageName) {
-      return false;
-    }
-    return matchRegexOrGlobList(packageName, excludePackageNames);
-  }
 }
diff --git a/lib/util/package-rules/package-patterns.spec.ts b/lib/util/package-rules/package-patterns.spec.ts
deleted file mode 100644
index 684d2bfd6b..0000000000
--- a/lib/util/package-rules/package-patterns.spec.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { PackagePatternsMatcher } from './package-patterns';
-
-describe('util/package-rules/package-patterns', () => {
-  const packagePatternsMatcher = new PackagePatternsMatcher();
-
-  describe('match', () => {
-    it('should return false if packageName is not defined', () => {
-      const result = packagePatternsMatcher.matches(
-        {
-          packageName: undefined,
-        },
-        {
-          matchPackagePatterns: ['@opentelemetry/http'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-
-    it('should match packageName', () => {
-      const result = packagePatternsMatcher.matches(
-        {
-          packageName: 'def',
-        },
-        {
-          matchPackagePatterns: ['def'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-
-    it('should not fall back to matching depName', () => {
-      const result = packagePatternsMatcher.matches(
-        {
-          depName: 'abc',
-          packageName: 'def',
-        },
-        {
-          matchPackagePatterns: ['abc'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-  });
-
-  describe('exclude', () => {
-    it('should exclude packageName', () => {
-      const result = packagePatternsMatcher.excludes(
-        {
-          packageName: 'def',
-        },
-        {
-          excludePackagePatterns: ['def'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-  });
-});
diff --git a/lib/util/package-rules/package-patterns.ts b/lib/util/package-rules/package-patterns.ts
deleted file mode 100644
index 8ca7ebdfab..0000000000
--- a/lib/util/package-rules/package-patterns.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import is from '@sindresorhus/is';
-import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
-import { matchRegexOrGlobList } from '../string-match';
-import { Matcher } from './base';
-
-export class PackagePatternsMatcher extends Matcher {
-  override matches(
-    { packageName }: PackageRuleInputConfig,
-    packageRule: PackageRule,
-  ): boolean | null {
-    const { matchPackagePatterns } = packageRule;
-    if (is.undefined(matchPackagePatterns)) {
-      return null;
-    }
-
-    if (!packageName) {
-      return false;
-    }
-
-    const massagedPatterns = matchPackagePatterns.map((pattern) =>
-      pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
-    );
-    return matchRegexOrGlobList(packageName, massagedPatterns);
-  }
-
-  override excludes(
-    { packageName }: PackageRuleInputConfig,
-    packageRule: PackageRule,
-  ): boolean | null {
-    const { excludePackagePatterns } = packageRule;
-    // ignore lockFileMaintenance for backwards compatibility
-    if (is.undefined(excludePackagePatterns)) {
-      return null;
-    }
-    if (!packageName) {
-      return false;
-    }
-
-    const massagedPatterns = excludePackagePatterns.map((pattern) =>
-      pattern === '^*$' || pattern === '*' ? '*' : `/${pattern}/`,
-    );
-    return matchRegexOrGlobList(packageName, massagedPatterns);
-  }
-}
diff --git a/lib/util/package-rules/package-prefixes.spec.ts b/lib/util/package-rules/package-prefixes.spec.ts
deleted file mode 100644
index 3fc7c6edc4..0000000000
--- a/lib/util/package-rules/package-prefixes.spec.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { PackagePrefixesMatcher } from './package-prefixes';
-
-describe('util/package-rules/package-prefixes', () => {
-  const packagePrefixesMatcher = new PackagePrefixesMatcher();
-
-  describe('match', () => {
-    it('should return false if packageName is not defined', () => {
-      const result = packagePrefixesMatcher.matches(
-        {
-          packageName: undefined,
-        },
-        {
-          matchPackagePrefixes: ['@opentelemetry'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-
-    it('should return true if packageName matched', () => {
-      const result = packagePrefixesMatcher.matches(
-        {
-          packageName: 'def1',
-        },
-        {
-          matchPackagePrefixes: ['def'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-  });
-
-  describe('exclude', () => {
-    it('should return false if packageName is not defined', () => {
-      const result = packagePrefixesMatcher.excludes(
-        {
-          packageName: undefined,
-        },
-        {
-          excludePackagePrefixes: ['@opentelemetry'],
-        },
-      );
-      expect(result).toBeFalse();
-    });
-
-    it('should return true if packageName matched', () => {
-      const result = packagePrefixesMatcher.excludes(
-        {
-          packageName: 'def1',
-        },
-        {
-          excludePackagePrefixes: ['def'],
-        },
-      );
-      expect(result).toBeTrue();
-    });
-  });
-});
diff --git a/lib/util/package-rules/package-prefixes.ts b/lib/util/package-rules/package-prefixes.ts
deleted file mode 100644
index ff83d94059..0000000000
--- a/lib/util/package-rules/package-prefixes.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import is from '@sindresorhus/is';
-import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
-import { matchRegexOrGlobList } from '../string-match';
-import { Matcher } from './base';
-
-export class PackagePrefixesMatcher extends Matcher {
-  override matches(
-    { packageName }: PackageRuleInputConfig,
-    { matchPackagePrefixes }: PackageRule,
-  ): boolean | null {
-    if (is.undefined(matchPackagePrefixes)) {
-      return null;
-    }
-
-    if (!packageName) {
-      return false;
-    }
-
-    const massagedPatterns = matchPackagePrefixes.map(
-      (pattern) => `${pattern}**`,
-    );
-    return matchRegexOrGlobList(packageName, massagedPatterns);
-  }
-
-  override excludes(
-    { packageName }: PackageRuleInputConfig,
-    packageRule: PackageRule,
-  ): boolean | null {
-    const { excludePackagePrefixes } = packageRule;
-    if (is.undefined(excludePackagePrefixes)) {
-      return null;
-    }
-    if (!packageName) {
-      return false;
-    }
-
-    const massagedPatterns = excludePackagePrefixes.map(
-      (pattern) => `${pattern}**`,
-    );
-    return matchRegexOrGlobList(packageName, massagedPatterns);
-  }
-}
diff --git a/lib/util/package-rules/repositories.spec.ts b/lib/util/package-rules/repositories.spec.ts
index 369927de30..73b93cf06e 100644
--- a/lib/util/package-rules/repositories.spec.ts
+++ b/lib/util/package-rules/repositories.spec.ts
@@ -102,100 +102,88 @@ describe('util/package-rules/repositories', () => {
   });
 
   describe('excludes', () => {
-    it('should return null if exclude repositories is not defined', () => {
-      const result = repositoryMatcher.excludes(
-        {
-          repository: 'org/repo',
-        },
-        {
-          excludeRepositories: undefined,
-        },
-      );
-      expect(result).toBeNull();
-    });
-
     it('should return false if exclude repository is not defined', () => {
-      const result = repositoryMatcher.excludes(
+      const result = repositoryMatcher.matches(
         {
           repository: undefined,
         },
         {
-          excludeRepositories: ['org/repo'],
+          matchRepositories: ['!org/repo'],
         },
       );
       expect(result).toBeFalse();
     });
 
-    it('should return true if exclude repository matches regex pattern', () => {
-      const result = repositoryMatcher.excludes(
+    it('should return false if exclude repository matches regex pattern', () => {
+      const result = repositoryMatcher.matches(
         {
           repository: 'org/repo',
         },
         {
-          excludeRepositories: ['/^org/repo$/'],
+          matchRepositories: ['!/^org/repo$/'],
         },
       );
-      expect(result).toBeTrue();
+      expect(result).toBeFalse();
     });
 
-    it('should return false if exclude repository has invalid regex pattern', () => {
-      const result = repositoryMatcher.excludes(
+    it('should return true if exclude repository has invalid regex pattern', () => {
+      const result = repositoryMatcher.matches(
         {
           repository: 'org/repo',
         },
         {
-          excludeRepositories: ['/[/'],
+          matchRepositories: ['!/[/'],
         },
       );
-      expect(result).toBeFalse();
+      expect(result).toBeTrue();
     });
 
-    it('should return false if exclude repository does not match regex pattern', () => {
-      const result = repositoryMatcher.excludes(
+    it('should return true if exclude repository does not match regex pattern', () => {
+      const result = repositoryMatcher.matches(
         {
           repository: 'org/repo',
         },
         {
-          excludeRepositories: ['/^org/other-repo$/'],
+          matchRepositories: ['!/^org/other-repo$/'],
         },
       );
-      expect(result).toBeFalse();
+      expect(result).toBeTrue();
     });
 
-    it('should return true if exclude repository matches minimatch pattern', () => {
-      const result = repositoryMatcher.excludes(
+    it('should return false if exclude repository matches minimatch pattern', () => {
+      const result = repositoryMatcher.matches(
         {
           repository: 'org/repo',
         },
         {
-          excludeRepositories: ['org/**'],
+          matchRepositories: ['!org/**'],
         },
       );
-      expect(result).toBeTrue();
+      expect(result).toBeFalse();
     });
 
-    it('should return false if exclude repository does not match minimatch pattern', () => {
-      const result = repositoryMatcher.excludes(
+    it('should return true if exclude repository does not match minimatch pattern', () => {
+      const result = repositoryMatcher.matches(
         {
           repository: 'org/repo',
         },
         {
-          excludeRepositories: ['other-org/**'],
+          matchRepositories: ['!other-org/**'],
         },
       );
-      expect(result).toBeFalse();
+      expect(result).toBeTrue();
     });
 
-    it('should return true if exclude repository matches at least one pattern', () => {
-      const result = repositoryMatcher.excludes(
+    it('should return false if exclude repository matches at least one pattern', () => {
+      const result = repositoryMatcher.matches(
         {
           repository: 'org/repo-archived',
         },
         {
-          excludeRepositories: ['/^org/repo$/', '**/*-archived'],
+          matchRepositories: ['!/^org/repo$/', '!**/*-archived'],
         },
       );
-      expect(result).toBeTrue();
+      expect(result).toBeFalse();
     });
   });
 });
diff --git a/lib/util/package-rules/repositories.ts b/lib/util/package-rules/repositories.ts
index bc3c45ac8f..9e47c0c475 100644
--- a/lib/util/package-rules/repositories.ts
+++ b/lib/util/package-rules/repositories.ts
@@ -18,19 +18,4 @@ export class RepositoriesMatcher extends Matcher {
 
     return matchRegexOrGlobList(repository, matchRepositories);
   }
-
-  override excludes(
-    { repository }: PackageRuleInputConfig,
-    { excludeRepositories }: PackageRule,
-  ): boolean | null {
-    if (is.undefined(excludeRepositories)) {
-      return null;
-    }
-
-    if (is.undefined(repository)) {
-      return false;
-    }
-
-    return matchRegexOrGlobList(repository, excludeRepositories);
-  }
 }
diff --git a/lib/util/package-rules/sourceurl-prefixes.ts b/lib/util/package-rules/sourceurl-prefixes.ts
deleted file mode 100644
index 13d6d4801c..0000000000
--- a/lib/util/package-rules/sourceurl-prefixes.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import is from '@sindresorhus/is';
-import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
-import { Matcher } from './base';
-
-export class SourceUrlPrefixesMatcher extends Matcher {
-  override matches(
-    { sourceUrl }: PackageRuleInputConfig,
-    { matchSourceUrlPrefixes }: PackageRule,
-  ): boolean | null {
-    if (is.undefined(matchSourceUrlPrefixes)) {
-      return null;
-    }
-    if (is.undefined(sourceUrl)) {
-      return false;
-    }
-    const upperCaseSourceUrl = sourceUrl?.toUpperCase();
-
-    return matchSourceUrlPrefixes.some((prefix) =>
-      upperCaseSourceUrl?.startsWith(prefix.toUpperCase()),
-    );
-  }
-}
diff --git a/lib/util/package-rules/types.ts b/lib/util/package-rules/types.ts
index 0307f2e82d..938232b86a 100644
--- a/lib/util/package-rules/types.ts
+++ b/lib/util/package-rules/types.ts
@@ -7,8 +7,4 @@ export interface MatcherApi {
     inputConfig: PackageRuleInputConfig,
     packageRule: PackageRule,
   ): boolean | null;
-  excludes(
-    inputConfig: PackageRuleInputConfig,
-    packageRule: PackageRule,
-  ): boolean | null;
 }
diff --git a/lib/util/package-rules/utils.ts b/lib/util/package-rules/utils.ts
deleted file mode 100644
index 2fd138eb1a..0000000000
--- a/lib/util/package-rules/utils.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import is from '@sindresorhus/is';
-import type { PackageRule, PackageRuleInputConfig } from '../../config/types';
-import type { MatchType, MatcherApi } from './types';
-
-export function matcherOR(
-  matchType: MatchType,
-  groupMatchers: MatcherApi[],
-  inputConfig: PackageRuleInputConfig,
-  packageRule: PackageRule,
-): boolean | null {
-  let matchApplied = false;
-  for (const matcher of groupMatchers) {
-    let isMatch;
-    switch (matchType) {
-      case 'excludes':
-        isMatch = matcher.excludes(inputConfig, packageRule);
-        break;
-      case 'matches':
-        isMatch = matcher.matches(inputConfig, packageRule);
-        break;
-    }
-
-    // no rules are defined
-    if (is.nullOrUndefined(isMatch)) {
-      continue;
-    }
-
-    matchApplied = true;
-
-    if (is.truthy(isMatch)) {
-      return true;
-    }
-  }
-  return matchApplied ? false : null;
-}
diff --git a/lib/workers/repository/process/lookup/index.spec.ts b/lib/workers/repository/process/lookup/index.spec.ts
index a2aacf36f7..3c67849975 100644
--- a/lib/workers/repository/process/lookup/index.spec.ts
+++ b/lib/workers/repository/process/lookup/index.spec.ts
@@ -4071,7 +4071,7 @@ describe('workers/repository/process/lookup/index', () => {
       config.datasource = NpmDatasource.id;
       config.packageRules = [
         {
-          matchSourceUrlPrefixes: ['https://github.com/kriskowal/q'],
+          matchSourceUrls: ['https://github.com/kriskowal/**'],
           allowedVersions: '< 1.4.0',
         },
       ];
-- 
GitLab