From e416df1865c575b7e148348412d9f95aca633de5 Mon Sep 17 00:00:00 2001
From: Sebastian Poxhofer <secustor@users.noreply.github.com>
Date: Tue, 5 Oct 2021 14:21:11 +0200
Subject: [PATCH] feat(manager/regex): allow defining autoReplaceStringTemplate
 (#12019)

---
 docs/usage/configuration-options.md           | 38 +++++++++++++++++++
 lib/config/options/index.ts                   |  9 +++++
 lib/config/types.ts                           |  1 +
 lib/config/validation.ts                      |  1 +
 .../regex/__snapshots__/index.spec.ts.snap    | 19 ++++++++++
 lib/manager/regex/index.spec.ts               | 19 ++++++++++
 lib/manager/regex/index.ts                    |  3 ++
 lib/manager/types.ts                          |  2 +
 8 files changed, 92 insertions(+)

diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index daf4c3c282..8dfac52d90 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -2163,6 +2163,44 @@ It will be compiled using Handlebars and the regex `groups` result.
 If the `registryUrls` for a dependency is not captured with a named group then it can be defined in config using this field.
 It will be compiled using Handlebars and the regex `groups` result.
 
+### autoReplaceStringTemplate
+
+Allows overwriting how the matched string is replaced.
+This allows for some migration strategies.
+E.g. moving from one Docker image repository to another one.
+
+helm-values.yaml:
+
+```yaml
+# The image of the service <registry>/<repo>/<image>:<tag>
+image: my.old.registry/aRepository/andImage:1.18-alpine
+```
+
+regex definition:
+
+```json
+{
+  "regexManagers": [
+    {
+      "fileMatch": ["values.yaml$"],
+      "matchStrings": [
+        "image:\\s+(?<depName>my\\.old\\.registry\\/aRepository\\/andImage):(?<currentValue>[^\\s]+)"
+      ],
+      "depNameTemplate": "my.new.registry/aRepository/andImage",
+      "autoReplaceStringTemplate": "image: {{{depName}}}:{{{newValue}}}",
+      "datasourceTemplate": "docker"
+    }
+  ]
+}
+```
+
+This will lead to following update where `1.21-alpine` is the newest version of `my.new.registry/aRepository/andImage`:
+
+```yaml
+# The image of the service <registry>/<repo>/<image>:<tag>
+image: my.new.registry/aRepository/andImage:1.21-alpine
+```
+
 ## registryUrls
 
 Usually Renovate is able to either (a) use the default registries for a datasource, or (b) automatically detect during the manager extract phase which custom registries are in use.
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 382a560472..dc557c23e7 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -2016,6 +2016,15 @@ const options: RenovateOptions[] = [
     cli: false,
     env: false,
   },
+  {
+    name: 'autoReplaceStringTemplate',
+    description:
+      'Optional extractVersion for extracted dependencies. Valid only within a `regexManagers` object.',
+    type: 'string',
+    parent: 'regexManagers',
+    cli: false,
+    env: false,
+  },
   {
     name: 'fetchReleaseNotes',
     description: 'Allow to disable release notes fetching.',
diff --git a/lib/config/types.ts b/lib/config/types.ts
index a773d15247..c13b6fc752 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -150,6 +150,7 @@ export interface CustomManager {
   datasourceTemplate?: string;
   lookupNameTemplate?: string;
   versioningTemplate?: string;
+  autoReplaceStringTemplate?: string;
 }
 
 // TODO: Proper typings
diff --git a/lib/config/validation.ts b/lib/config/validation.ts
index 0684c34456..905b22a165 100644
--- a/lib/config/validation.ts
+++ b/lib/config/validation.ts
@@ -388,6 +388,7 @@ export async function validateConfig(
                 'registryUrlTemplate',
                 'currentValueTemplate',
                 'extractVersionTemplate',
+                'autoReplaceStringTemplate',
               ];
               // TODO: fix types
               for (const regexManager of val as any[]) {
diff --git a/lib/manager/regex/__snapshots__/index.spec.ts.snap b/lib/manager/regex/__snapshots__/index.spec.ts.snap
index 94aa2090dc..6071a39e35 100644
--- a/lib/manager/regex/__snapshots__/index.spec.ts.snap
+++ b/lib/manager/regex/__snapshots__/index.spec.ts.snap
@@ -41,6 +41,25 @@ Object {
 }
 `;
 
+exports[`manager/regex/index extracts dependency with autoReplaceStringTemplate 1`] = `
+Object {
+  "autoReplaceStringTemplate": "image: {{{depName}}}:{{{newValue}}}",
+  "datasourceTemplate": "docker",
+  "depNameTemplate": "my.new.registry/aRepository/andImage",
+  "deps": Array [
+    Object {
+      "currentValue": "1.18-alpine",
+      "datasource": "docker",
+      "depName": "my.new.registry/aRepository/andImage",
+      "replaceString": "image: my.old.registry/aRepository/andImage:1.18-alpine",
+    },
+  ],
+  "matchStrings": Array [
+    "image:\\\\s+(?<depName>my\\\\.old\\\\.registry\\\\/aRepository\\\\/andImage):(?<currentValue>[^\\\\s]+)",
+  ],
+}
+`;
+
 exports[`manager/regex/index extracts extractVersion 1`] = `
 Object {
   "deps": Array [
diff --git a/lib/manager/regex/index.spec.ts b/lib/manager/regex/index.spec.ts
index 035c2854cc..7f3d9bdfc6 100644
--- a/lib/manager/regex/index.spec.ts
+++ b/lib/manager/regex/index.spec.ts
@@ -168,6 +168,25 @@ describe('manager/regex/index', () => {
       'maven'
     );
   });
+
+  it('extracts dependency with autoReplaceStringTemplate', async () => {
+    const config = {
+      matchStrings: [
+        'image:\\s+(?<depName>my\\.old\\.registry\\/aRepository\\/andImage):(?<currentValue>[^\\s]+)',
+      ],
+      depNameTemplate: 'my.new.registry/aRepository/andImage',
+      autoReplaceStringTemplate: 'image: {{{depName}}}:{{{newValue}}}',
+      datasourceTemplate: 'docker',
+    };
+    const res = await extractPackageFile(
+      'image: my.old.registry/aRepository/andImage:1.18-alpine',
+      'values.yaml',
+      config
+    );
+    expect(res).toMatchSnapshot();
+    expect(res.deps).toHaveLength(1);
+  });
+
   it('extracts with combination strategy', async () => {
     const config: CustomExtractConfig = {
       matchStrings: [
diff --git a/lib/manager/regex/index.ts b/lib/manager/regex/index.ts
index 9c38450312..70f9c181ef 100644
--- a/lib/manager/regex/index.ts
+++ b/lib/manager/regex/index.ts
@@ -221,6 +221,9 @@ export function extractPackageFile(
         res[field] = config[field];
       }
     }
+    if (config.autoReplaceStringTemplate) {
+      res.autoReplaceStringTemplate = config.autoReplaceStringTemplate;
+    }
     return res;
   }
 
diff --git a/lib/manager/types.ts b/lib/manager/types.ts
index bb512be8b2..c84b69c132 100644
--- a/lib/manager/types.ts
+++ b/lib/manager/types.ts
@@ -27,6 +27,7 @@ export interface ExtractConfig {
 }
 
 export interface CustomExtractConfig extends ExtractConfig {
+  autoReplaceStringTemplate?: string;
   matchStrings: string[];
   matchStringsStrategy?: MatchStringsStrategy;
   depNameTemplate?: string;
@@ -69,6 +70,7 @@ export interface NpmLockFiles {
 export interface PackageFile<T = Record<string, any>>
   extends NpmLockFiles,
     ManagerData<T> {
+  autoReplaceStringTemplate?: string;
   hasYarnWorkspaces?: boolean;
   constraints?: Record<string, string>;
   datasource?: string;
-- 
GitLab