From a6d93dcbc61a0d08149c4034d896d3efdb00be06 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Fri, 1 Nov 2024 16:01:41 +0100
Subject: [PATCH] feat(branchNameStrict)!: remove forward slashes from
 non-prefix part of branchName (#32278)

Update logic for branchNameStrict=true so that it also removes forward slashes from branch names, with the exception of the `branchPrefix` part. In other words, if you leave the default `branchPrefix="renovate/"` then you'll get branch names like `renovate/foo-bar-42-x` instead of today's `renovate/foo/bar-42-x`.

BREAKING CHANGE: Branch names with multiple forward slashes will change if branchNameStrict=true
---
 docs/usage/configuration-options.md              |  7 ++++++-
 docs/usage/examples/self-hosting.md              |  2 +-
 .../repository/updates/branch-name.spec.ts       | 16 ++++++++++++++++
 lib/workers/repository/updates/branch-name.ts    | 13 +++++++++++--
 tools/docker/Dockerfile                          |  2 +-
 5 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index 99bb5ca138..02f2d3394c 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -394,7 +394,12 @@ If `true`, Renovate removes special characters when slugifying the branch name:
 - only alphabetic characters are allowed
 - hyphens `-` are used to separate sections
 
-The default `false` behavior will mean that special characters like `.` may end up in the branch name.
+The default `false` behavior will mean that special characters like `.` and `/` may end up in the branch name.
+
+<!-- prettier-ignore -->
+!!! note
+    Renovate will not apply any search/replace to the `branchPrefix` part of the branch name.
+    If you don't want any `/` in your branch name then you will also need to change `branchPrefix` from the default `renovate/` value to something like `renovate-`.
 
 ## branchPrefix
 
diff --git a/docs/usage/examples/self-hosting.md b/docs/usage/examples/self-hosting.md
index 705ffd1768..edc1b91a38 100644
--- a/docs/usage/examples/self-hosting.md
+++ b/docs/usage/examples/self-hosting.md
@@ -436,7 +436,7 @@ COPY self-signed-certificate.crt /usr/local/share/ca-certificates/
 RUN update-ca-certificates
 
 # Change back to the Ubuntu user
-USER 1000
+USER 12021
 
 # OpenSSL
 ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
diff --git a/lib/workers/repository/updates/branch-name.spec.ts b/lib/workers/repository/updates/branch-name.spec.ts
index 297d7012bc..a4b4dd5993 100644
--- a/lib/workers/repository/updates/branch-name.spec.ts
+++ b/lib/workers/repository/updates/branch-name.spec.ts
@@ -213,6 +213,22 @@ describe('workers/repository/updates/branch-name', () => {
       expect(upgrade.branchName).toBe('renovate/jest-42-x');
     });
 
+    it('removes slashes from the non-suffix part', () => {
+      const upgrade: RenovateConfig = {
+        branchNameStrict: true,
+        branchName:
+          '{{{branchPrefix}}}{{{additionalBranchPrefix}}}{{{branchTopic}}}',
+        branchTopic:
+          '{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}',
+        branchPrefix: 'renovate/',
+        depNameSanitized: '@foo/jest',
+        newMajor: '42',
+        group: {},
+      };
+      generateBranchName(upgrade);
+      expect(upgrade.branchName).toBe('renovate/foo-jest-42-x');
+    });
+
     it('hashedBranchLength hashing', () => {
       const upgrade: RenovateConfig = {
         branchName:
diff --git a/lib/workers/repository/updates/branch-name.ts b/lib/workers/repository/updates/branch-name.ts
index bc42966fd0..d151c0772b 100644
--- a/lib/workers/repository/updates/branch-name.ts
+++ b/lib/workers/repository/updates/branch-name.ts
@@ -11,7 +11,7 @@ const MIN_HASH_LENGTH = 6;
 
 const RE_MULTIPLE_DASH = regEx(/--+/g);
 
-const RE_SPECIAL_CHARS_STRICT = regEx(/[`~!@#$%^&*()_=+[\]\\|{};':",.<>?]/g);
+const RE_SPECIAL_CHARS_STRICT = regEx(/[`~!@#$%^&*()_=+[\]\\|{};':",.<>?/]/g);
 
 /**
  * Clean git branch name
@@ -26,12 +26,20 @@ const RE_SPECIAL_CHARS_STRICT = regEx(/[`~!@#$%^&*()_=+[\]\\|{};':",.<>?]/g);
  */
 function cleanBranchName(
   branchName: string,
+  branchPrefix: string,
   branchNameStrict?: boolean,
 ): string {
   let cleanedBranchName = branchName;
 
+  let existingBranchPrefix = '';
   if (branchNameStrict) {
-    cleanedBranchName = cleanedBranchName.replace(RE_SPECIAL_CHARS_STRICT, '-'); // massage out all special characters that slip through slugify
+    if (cleanedBranchName.startsWith(branchPrefix)) {
+      existingBranchPrefix = branchPrefix;
+      cleanedBranchName = cleanedBranchName.slice(branchPrefix.length);
+    }
+    cleanedBranchName =
+      existingBranchPrefix +
+      cleanedBranchName.replace(RE_SPECIAL_CHARS_STRICT, '-'); // massage out all special characters that slip through slugify
   }
 
   return cleanGitRef
@@ -125,6 +133,7 @@ export function generateBranchName(update: RenovateConfig): void {
   }
   update.branchName = cleanBranchName(
     update.branchName,
+    update.branchPrefix!,
     update.branchNameStrict,
   );
 }
diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile
index 4ff5831162..bf755d6010 100644
--- a/tools/docker/Dockerfile
+++ b/tools/docker/Dockerfile
@@ -104,4 +104,4 @@ LABEL \
   org.label-schema.version="${RENOVATE_VERSION}"
 
 # Numeric user ID for the ubuntu user. Used to indicate a non-root user to OpenShift
-USER 1000
+USER 12021
-- 
GitLab