From 96b8e6ddc98655ece4b412cd7477c96528ceef12 Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Thu, 8 Feb 2024 08:59:28 +0100
Subject: [PATCH] feat(manager/github-actions): support full qualified actions
 (#27117)

---
 .../manager/github-actions/extract.spec.ts    | 44 +++++++++++++++++++
 lib/modules/manager/github-actions/extract.ts | 29 ++++++++++--
 lib/modules/manager/github-actions/index.ts   |  2 +
 3 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/lib/modules/manager/github-actions/extract.spec.ts b/lib/modules/manager/github-actions/extract.spec.ts
index 4f96931d87..7234e08c81 100644
--- a/lib/modules/manager/github-actions/extract.spec.ts
+++ b/lib/modules/manager/github-actions/extract.spec.ts
@@ -1,3 +1,4 @@
+import { codeBlock } from 'common-tags';
 import { Fixtures } from '../../../../test/fixtures';
 import { GlobalConfig } from '../../../config/global';
 import { extractPackageFile } from '.';
@@ -387,6 +388,49 @@ describe('modules/manager/github-actions/extract', () => {
       ]);
     });
 
+    it('extracts actions with fqdn', () => {
+      const res = extractPackageFile(
+        codeBlock`
+        jobs:
+          build:
+            steps:
+              - name: "test1"
+                uses: https://github.com/actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # tag=v3.1.1
+              - name: "test2"
+                uses: https://code.forgejo.org/actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # v3.1.1
+              - name: "test3"
+                uses: https://code.domain.test/actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # v3.1.1
+
+          `,
+        'sample.yml',
+      );
+      expect(res).toMatchObject({
+        deps: [
+          {
+            currentDigest: '56337c425554a6be30cdef71bf441f15be286854',
+            currentValue: 'v3.1.1',
+            replaceString:
+              'https://github.com/actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # tag=v3.1.1',
+            datasource: 'github-tags',
+            registryUrls: ['https://github.com/'],
+          },
+          {
+            currentDigest: '56337c425554a6be30cdef71bf441f15be286854',
+            currentValue: 'v3.1.1',
+            replaceString:
+              'https://code.forgejo.org/actions/setup-node@56337c425554a6be30cdef71bf441f15be286854 # v3.1.1',
+            datasource: 'gitea-tags',
+            registryUrls: ['https://code.forgejo.org/'],
+          },
+          {
+            skipReason: 'unsupported-url',
+          },
+        ],
+      });
+
+      expect(res!.deps[2]).not.toHaveProperty('registryUrls');
+    });
+
     it('extracts multiple action runners from yaml configuration file', () => {
       const res = extractPackageFile(runnerTestWorkflow, 'workflow.yml');
 
diff --git a/lib/modules/manager/github-actions/extract.ts b/lib/modules/manager/github-actions/extract.ts
index c1446e5f84..9b6ccf4a72 100644
--- a/lib/modules/manager/github-actions/extract.ts
+++ b/lib/modules/manager/github-actions/extract.ts
@@ -2,8 +2,10 @@ import is from '@sindresorhus/is';
 import { GlobalConfig } from '../../../config/global';
 import { logger } from '../../../logger';
 import { isNotNullOrUndefined } from '../../../util/array';
+import { detectPlatform } from '../../../util/common';
 import { newlineRegex, regEx } from '../../../util/regex';
 import { parseSingleYaml } from '../../../util/yaml';
+import { GiteaTagsDatasource } from '../../datasource/gitea-tags';
 import { GithubRunnersDatasource } from '../../datasource/github-runners';
 import { GithubTagsDatasource } from '../../datasource/github-tags';
 import * as dockerVersioning from '../../versioning/docker';
@@ -13,7 +15,7 @@ import type { Workflow } from './types';
 
 const dockerActionRe = regEx(/^\s+uses: ['"]?docker:\/\/([^'"]+)\s*$/);
 const actionRe = regEx(
-  /^\s+-?\s+?uses: (?<replaceString>['"]?(?<depName>[\w-]+\/[.\w-]+)(?<path>\/.*)?@(?<currentValue>[^\s'"]+)['"]?(?:\s+#\s*(?:renovate\s*:\s*)?(?:pin\s+|tag\s*=\s*)?@?(?<tag>v?\d+(?:\.\d+(?:\.\d+)?)?))?)/,
+  /^\s+-?\s+?uses: (?<replaceString>['"]?(?<registryUrl>https:\/\/[.\w-]+\/)?(?<depName>[\w-]+\/[.\w-]+)(?<path>\/.*)?@(?<currentValue>[^\s'"]+)['"]?(?:\s+#\s*(?:renovate\s*:\s*)?(?:pin\s+|tag\s*=\s*)?@?(?<tag>v?\d+(?:\.\d+(?:\.\d+)?)?))?)/,
 );
 
 // SHA1 or SHA256, see https://github.blog/2020-10-19-git-2-29-released/
@@ -69,6 +71,7 @@ function extractWithRegex(content: string): PackageDependency[] {
         path = '',
         tag,
         replaceString,
+        registryUrl = '',
       } = tagMatch.groups;
       let quotes = '';
       if (replaceString.indexOf("'") >= 0) {
@@ -84,8 +87,10 @@ function extractWithRegex(content: string): PackageDependency[] {
         versioning: dockerVersioning.id,
         depType: 'action',
         replaceString,
-        autoReplaceStringTemplate: `${quotes}{{depName}}${path}@{{#if newDigest}}{{newDigest}}${quotes}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}${quotes}{{/unless}}`,
-        ...customRegistryUrlsPackageDependency,
+        autoReplaceStringTemplate: `${quotes}${registryUrl}{{depName}}${path}@{{#if newDigest}}{{newDigest}}${quotes}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}${quotes}{{/unless}}`,
+        ...(registryUrl
+          ? detectDatasource(registryUrl)
+          : customRegistryUrlsPackageDependency),
       };
       if (shaRe.test(currentValue)) {
         dep.currentValue = tag;
@@ -105,6 +110,24 @@ function extractWithRegex(content: string): PackageDependency[] {
   return deps;
 }
 
+function detectDatasource(registryUrl: string): PackageDependency {
+  const platform = detectPlatform(registryUrl);
+
+  switch (platform) {
+    case 'github':
+      return { registryUrls: [registryUrl] };
+    case 'gitea':
+      return {
+        registryUrls: [registryUrl],
+        datasource: GiteaTagsDatasource.id,
+      };
+  }
+
+  return {
+    skipReason: 'unsupported-url',
+  };
+}
+
 function extractContainer(container: unknown): PackageDependency | undefined {
   if (is.string(container)) {
     return getDep(container);
diff --git a/lib/modules/manager/github-actions/index.ts b/lib/modules/manager/github-actions/index.ts
index 305543bcf6..a50258fed8 100644
--- a/lib/modules/manager/github-actions/index.ts
+++ b/lib/modules/manager/github-actions/index.ts
@@ -1,4 +1,5 @@
 import type { Category } from '../../../constants';
+import { GiteaTagsDatasource } from '../../datasource/gitea-tags';
 import { GithubRunnersDatasource } from '../../datasource/github-runners';
 import { GithubTagsDatasource } from '../../datasource/github-tags';
 export { extractPackageFile } from './extract';
@@ -13,6 +14,7 @@ export const defaultConfig = {
 export const categories: Category[] = ['ci'];
 
 export const supportedDatasources = [
+  GiteaTagsDatasource.id,
   GithubTagsDatasource.id,
   GithubRunnersDatasource.id,
 ];
-- 
GitLab