From e9d86a23a012ac02eebcf034ac36b2b72c95d181 Mon Sep 17 00:00:00 2001
From: Richard Dalziel <3485935+RDalziel@users.noreply.github.com>
Date: Tue, 4 Mar 2025 15:06:36 +0000
Subject: [PATCH] feat(datasource/azure-pipelines-tasks):  Add in support for
 matching on id and contribution combinations (#34398)

Co-authored-by: Richard Dalziel <Richard.dalziel@hymans.co.uk>
---
 .../__fixtures__/tasks.json                   | 91 ++++++++++++++++++-
 .../azure-pipelines-tasks/index.spec.ts       | 69 ++++++++++++++
 .../datasource/azure-pipelines-tasks/index.ts | 12 ++-
 .../azure-pipelines-tasks/schema.ts           |  2 +
 4 files changed, 171 insertions(+), 3 deletions(-)

diff --git a/lib/modules/datasource/azure-pipelines-tasks/__fixtures__/tasks.json b/lib/modules/datasource/azure-pipelines-tasks/__fixtures__/tasks.json
index df4a3ea7a7..9a5fbc31e8 100644
--- a/lib/modules/datasource/azure-pipelines-tasks/__fixtures__/tasks.json
+++ b/lib/modules/datasource/azure-pipelines-tasks/__fixtures__/tasks.json
@@ -1,5 +1,5 @@
 {
-  "count": 3,
+  "count": 4,
   "value": [
     {
       "visibility": [
@@ -570,6 +570,93 @@
         "Default": "5.248.2",
         "Node20_229_2": "5.248.3"
       }
+    },
+    {
+      "runsOn": [
+        "Agent",
+        "DeploymentGroup"
+      ],
+      "id": "5d437bf5-f193-4449-b531-c4c69eebaa48",
+      "name": "gitreleasemanager/open",
+      "version": {
+        "major": 3,
+        "minor": 1,
+        "patch": 11,
+        "isTest": false
+      },
+      "contentsUploaded": true,
+      "iconUrl": "https://dev.azure.com/test_organisation/_apis/distributedtask/tasks/5d437bf5-f193-4449-b531-c4c69eebaa48/3.1.11/icon",
+      "minimumAgentVersion": "3.224.0",
+      "friendlyName": "Open GitReleaseManager Task",
+      "description": "Tool for creating and exporting releases for software applications hosted on GitHub",
+      "category": "Build",
+      "helpMarkDown": "See the [documentation](https://gittools.github.io/GitReleaseManager/docs/) for help",
+      "contributionIdentifier": "gittools.gittools.open-gitreleasemanager-task",
+      "contributionVersion": "3.1.11.25012214",
+      "definitionType": "task",
+      "author": "GitTools Contributors",
+      "demands": [],
+      "groups": [],
+      "inputs": [
+        {
+          "aliases": [],
+          "name": "owner",
+          "label": "The owner of the repository",
+          "defaultValue": "",
+          "required": true,
+          "type": "string",
+          "helpMarkDown": "The owner of the repository"
+        },
+        {
+          "aliases": [],
+          "name": "repository",
+          "label": "The name of the repository",
+          "defaultValue": "",
+          "required": true,
+          "type": "string",
+          "helpMarkDown": "The name of the repository"
+        },
+        {
+          "aliases": [],
+          "name": "token",
+          "label": "The access token to access GitHub with",
+          "defaultValue": "",
+          "required": true,
+          "type": "string",
+          "helpMarkDown": "The access token to access GitHub with"
+        },
+        {
+          "aliases": [],
+          "name": "milestone",
+          "label": "The milestone to use",
+          "defaultValue": "",
+          "required": true,
+          "type": "string",
+          "helpMarkDown": "The milestone to use"
+        },
+        {
+          "aliases": [],
+          "name": "targetDirectory",
+          "label": "The directory on which GitReleaseManager should be executed. Defaults to current directory",
+          "defaultValue": "",
+          "type": "string",
+          "helpMarkDown": "The directory on which GitReleaseManager should be executed. Defaults to current directory"
+        }
+      ],
+      "satisfies": [],
+      "sourceDefinitions": [],
+      "dataSourceBindings": [],
+      "instanceNameFormat": "gitreleasemanager/open",
+      "preJobExecution": {},
+      "execution": {
+        "Node20_1": {
+          "target": "main.mjs",
+          "argumentFormat": "",
+          "workingDirectory": "."
+        }
+      },
+      "postJobExecution": {},
+      "_buildConfigMapping": {}
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/lib/modules/datasource/azure-pipelines-tasks/index.spec.ts b/lib/modules/datasource/azure-pipelines-tasks/index.spec.ts
index 94d73d131b..656aaeb718 100644
--- a/lib/modules/datasource/azure-pipelines-tasks/index.spec.ts
+++ b/lib/modules/datasource/azure-pipelines-tasks/index.spec.ts
@@ -99,6 +99,74 @@ describe('modules/datasource/azure-pipelines-tasks/index', () => {
     ).toEqual({ releases: [{ version: '5.248.3' }] });
   });
 
+  it('identifies task based on task id', async () => {
+    GlobalConfig.set({
+      platform: 'azure',
+      endpoint: 'https://my.custom.domain',
+    });
+    hostRules.add({
+      hostType: AzurePipelinesTasksDatasource.id,
+      matchHost: 'my.custom.domain',
+      token: '123test',
+    });
+    httpMock
+      .scope('https://my.custom.domain')
+      .get('/_apis/distributedtask/tasks/')
+      .reply(200, Fixtures.get('tasks.json'));
+    expect(
+      await getPkgReleases({
+        datasource: AzurePipelinesTasksDatasource.id,
+        packageName: '5d437bf5-f193-4449-b531-c4c69eebaa48',
+      }),
+    ).toEqual({ releases: [{ version: '3.1.11' }] });
+  });
+
+  it('identifies task based on contributionIdentifier and id', async () => {
+    GlobalConfig.set({
+      platform: 'azure',
+      endpoint: 'https://my.custom.domain',
+    });
+    hostRules.add({
+      hostType: AzurePipelinesTasksDatasource.id,
+      matchHost: 'my.custom.domain',
+      token: '123test',
+    });
+    httpMock
+      .scope('https://my.custom.domain')
+      .get('/_apis/distributedtask/tasks/')
+      .reply(200, Fixtures.get('tasks.json'));
+    expect(
+      await getPkgReleases({
+        datasource: AzurePipelinesTasksDatasource.id,
+        packageName:
+          'gittools.gittools.open-gitreleasemanager-task.5d437bf5-f193-4449-b531-c4c69eebaa48',
+      }),
+    ).toEqual({ releases: [{ version: '3.1.11' }] });
+  });
+
+  it('identifies task based on contributionIdentifier and name', async () => {
+    GlobalConfig.set({
+      platform: 'azure',
+      endpoint: 'https://my.custom.domain',
+    });
+    hostRules.add({
+      hostType: AzurePipelinesTasksDatasource.id,
+      matchHost: 'my.custom.domain',
+      token: '123test',
+    });
+    httpMock
+      .scope('https://my.custom.domain')
+      .get('/_apis/distributedtask/tasks/')
+      .reply(200, Fixtures.get('tasks.json'));
+    expect(
+      await getPkgReleases({
+        datasource: AzurePipelinesTasksDatasource.id,
+        packageName:
+          'gittools.gittools.open-gitreleasemanager-task.gitreleasemanager/open',
+      }),
+    ).toEqual({ releases: [{ version: '3.1.11' }] });
+  });
+
   it('returns organization task with multiple versions', async () => {
     GlobalConfig.set({
       platform: 'azure',
@@ -151,6 +219,7 @@ describe('modules/datasource/azure-pipelines-tasks/index', () => {
             : null;
 
         return AzurePipelinesTask.parse({
+          id: '',
           name: '',
           deprecated: false,
           version,
diff --git a/lib/modules/datasource/azure-pipelines-tasks/index.ts b/lib/modules/datasource/azure-pipelines-tasks/index.ts
index d7c81d19e9..4dbfa0e555 100644
--- a/lib/modules/datasource/azure-pipelines-tasks/index.ts
+++ b/lib/modules/datasource/azure-pipelines-tasks/index.ts
@@ -52,7 +52,17 @@ export class AzurePipelinesTasksDatasource extends Datasource {
       const result: ReleaseResult = { releases: [] };
 
       results.value
-        .filter((task) => task.name === packageName)
+        .filter((task) => {
+          const matchers = [
+            task.id === packageName,
+            task.name === packageName,
+            task.contributionIdentifier !== null &&
+              `${task.contributionIdentifier}.${task.id}` === packageName,
+            task.contributionIdentifier !== null &&
+              `${task.contributionIdentifier}.${task.name}` === packageName,
+          ];
+          return matchers.some((match) => match);
+        })
         .sort(AzurePipelinesTasksDatasource.compareSemanticVersions('version'))
         .forEach((task) => {
           result.releases.push({
diff --git a/lib/modules/datasource/azure-pipelines-tasks/schema.ts b/lib/modules/datasource/azure-pipelines-tasks/schema.ts
index eb20bc97dd..a857142790 100644
--- a/lib/modules/datasource/azure-pipelines-tasks/schema.ts
+++ b/lib/modules/datasource/azure-pipelines-tasks/schema.ts
@@ -7,9 +7,11 @@ export const AzurePipelinesTaskVersion = z.object({
 });
 
 export const AzurePipelinesTask = z.object({
+  id: z.string(),
   name: z.string(),
   deprecated: z.boolean().optional(),
   version: AzurePipelinesTaskVersion.nullable(),
+  contributionIdentifier: z.string().optional(),
 });
 
 export const AzurePipelinesJSON = z.object({
-- 
GitLab