From 6dca10bc1c07ef2ae4dad51b3dc2563291ea31d6 Mon Sep 17 00:00:00 2001 From: Joren Thijs Date: Tue, 27 May 2025 09:26:13 +0200 Subject: [PATCH 1/8] fix: read arguments input whun using dotnet push command --- Tasks/DotNetCoreCLIV2/pushcommand.ts | 451 ++++++++++++++------------- 1 file changed, 228 insertions(+), 223 deletions(-) diff --git a/Tasks/DotNetCoreCLIV2/pushcommand.ts b/Tasks/DotNetCoreCLIV2/pushcommand.ts index 7332420c4e47..ef79c2c2b95b 100644 --- a/Tasks/DotNetCoreCLIV2/pushcommand.ts +++ b/Tasks/DotNetCoreCLIV2/pushcommand.ts @@ -15,293 +15,298 @@ import { WebRequest, WebRequestOptions, sendRequest } from 'azure-pipelines-task import { getRequestTimeout } from './Common/utility'; interface EndpointCredentials { - endpoint: string; - username?: string; - password: string; + endpoint: string; + username?: string; + password: string; } export async function run(): Promise { - let packagingLocation: pkgLocationUtils.PackagingLocation; - try { - const timeout: number = getRequestTimeout(); - const webApiOptions: RequestOptions = { - socketTimeout: timeout, - globalAgentOptions: { + let packagingLocation: pkgLocationUtils.PackagingLocation; + try { + const timeout: number = getRequestTimeout(); + const webApiOptions: RequestOptions = { + socketTimeout: timeout, + globalAgentOptions: { timeout: timeout, - } - }; - packagingLocation = await pkgLocationUtils.getPackagingUris(pkgLocationUtils.ProtocolType.NuGet, webApiOptions); - } catch (error) { - tl.debug('Unable to get packaging URIs'); - logError(error); - throw error; + } + }; + packagingLocation = await pkgLocationUtils.getPackagingUris(pkgLocationUtils.ProtocolType.NuGet, webApiOptions); + } catch (error) { + tl.debug('Unable to get packaging URIs'); + logError(error); + throw error; + } + + const buildIdentityDisplayName: string = null; + const buildIdentityAccount: string = null; + try { + // Get list of files to publish + const searchPatternInput = tl.getPathInput('searchPatternPush', true, false); + const findOptions: tl.FindOptions = {}; + const matchOptions: tl.MatchOptions = {}; + const searchPatterns: string[] = nutil.getPatternsArrayFromInput(searchPatternInput); + const filesList = tl.findMatch(undefined, searchPatterns, findOptions, matchOptions); + + filesList.forEach(packageFile => { + if (!tl.stats(packageFile).isFile()) { + throw new Error(tl.loc('Error_PushNotARegularFile', packageFile)); + } + }); + + if (filesList.length < 1) { + tl.setResult(tl.TaskResult.Failed, tl.loc('Info_NoPackagesMatchedTheSearchPattern')); + return; } - const buildIdentityDisplayName: string = null; - const buildIdentityAccount: string = null; - try { - // Get list of files to publish - const searchPatternInput = tl.getPathInput('searchPatternPush', true, false); - const findOptions: tl.FindOptions = {}; - const matchOptions: tl.MatchOptions = {}; - const searchPatterns: string[] = nutil.getPatternsArrayFromInput(searchPatternInput); - const filesList = tl.findMatch(undefined, searchPatterns, findOptions, matchOptions); - - filesList.forEach(packageFile => { - if (!tl.stats(packageFile).isFile()) { - throw new Error(tl.loc('Error_PushNotARegularFile', packageFile)); - } - }); - - if (filesList.length < 1) { - tl.setResult(tl.TaskResult.Failed, tl.loc('Info_NoPackagesMatchedTheSearchPattern')); - return; - } - - // Get the info the type of feed - let nugetFeedType = tl.getInput('nuGetFeedType') || 'internal'; + // Get the info the type of feed + let nugetFeedType = tl.getInput('nuGetFeedType') || 'internal'; - // Make sure the feed type is an expected one - const normalizedNuGetFeedType = ['internal', 'external'].find(x => nugetFeedType.toUpperCase() === x.toUpperCase()); - if (!normalizedNuGetFeedType) { - throw new Error(tl.loc('UnknownFeedType', nugetFeedType)); - } - nugetFeedType = normalizedNuGetFeedType; - - const serviceUri = tl.getEndpointUrl('SYSTEMVSSCONNECTION', false); - let urlPrefixes = packagingLocation.PackagingUris; - tl.debug(`discovered URL prefixes: ${urlPrefixes}`); - - // Note to readers: This variable will be going away once we have a fix for the location service for - // customers behind proxies - const testPrefixes = tl.getVariable('DotNetCoreCLITask.ExtraUrlPrefixesForTesting'); - if (testPrefixes) { - urlPrefixes = urlPrefixes.concat(testPrefixes.split(';')); - tl.debug(`all URL prefixes: ${urlPrefixes}`); - } + // Make sure the feed type is an expected one + const normalizedNuGetFeedType = ['internal', 'external'].find(x => nugetFeedType.toUpperCase() === x.toUpperCase()); + if (!normalizedNuGetFeedType) { + throw new Error(tl.loc('UnknownFeedType', nugetFeedType)); + } + nugetFeedType = normalizedNuGetFeedType; + + const serviceUri = tl.getEndpointUrl('SYSTEMVSSCONNECTION', false); + let urlPrefixes = packagingLocation.PackagingUris; + tl.debug(`discovered URL prefixes: ${urlPrefixes}`); + + // Note to readers: This variable will be going away once we have a fix for the location service for + // customers behind proxies + const testPrefixes = tl.getVariable('DotNetCoreCLITask.ExtraUrlPrefixesForTesting'); + if (testPrefixes) { + urlPrefixes = urlPrefixes.concat(testPrefixes.split(';')); + tl.debug(`all URL prefixes: ${urlPrefixes}`); + } - // Setting up auth info - let accessToken; - const isInternalFeed: boolean = nugetFeedType === 'internal'; - accessToken = await getAccessToken(isInternalFeed, urlPrefixes); - const internalAuthInfo = new auth.InternalAuthInfo(urlPrefixes, accessToken, /*useCredProvider*/ null, true); - - let configFile = null; - let apiKey: string; - - // dotnet nuget push does not currently accept a --config-file parameter - // so we are going to work around this by creating a temporary working directory for dotnet with - // a nuget config file it will load by default. - const tempNuGetConfigDirectory = path.join(NuGetConfigHelper2.getTempNuGetConfigBasePath(), 'NuGet_' + tl.getVariable('build.buildId')); - const tempNuGetPath = path.join(tempNuGetConfigDirectory, 'nuget.config'); - tl.mkdirP(tempNuGetConfigDirectory); - let credCleanup = () => { - if (tl.exist(tempNuGetConfigDirectory)) { + // Read arguments so users can specify extra arguments like --skip-duplicate + let dotnetArguments: string = tl.getInput('arguments', false) || ''; + + // Setting up auth info + let accessToken; + const isInternalFeed: boolean = nugetFeedType === 'internal'; + accessToken = await getAccessToken(isInternalFeed, urlPrefixes); + const internalAuthInfo = new auth.InternalAuthInfo(urlPrefixes, accessToken, /*useCredProvider*/ null, true); + + let configFile = null; + let apiKey: string; + + // dotnet nuget push does not currently accept a --config-file parameter + // so we are going to work around this by creating a temporary working directory for dotnet with + // a nuget config file it will load by default. + const tempNuGetConfigDirectory = path.join(NuGetConfigHelper2.getTempNuGetConfigBasePath(), 'NuGet_' + tl.getVariable('build.buildId')); + const tempNuGetPath = path.join(tempNuGetConfigDirectory, 'nuget.config'); + tl.mkdirP(tempNuGetConfigDirectory); + let credCleanup = () => { + if (tl.exist(tempNuGetConfigDirectory)) { tl.rmRF(tempNuGetConfigDirectory) - } - }; + } + }; - let feedUri: string = undefined; + let feedUri: string = undefined; - let authInfo: auth.NuGetExtendedAuthInfo; - let nuGetConfigHelper: NuGetConfigHelper2; + let authInfo: auth.NuGetExtendedAuthInfo; + let nuGetConfigHelper: NuGetConfigHelper2; - if (isInternalFeed) { - authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo); - nuGetConfigHelper = new NuGetConfigHelper2( - null, + if (isInternalFeed) { + authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo); + nuGetConfigHelper = new NuGetConfigHelper2( + null, null, /* nugetConfigPath */ - authInfo, - { credProviderFolder: null, extensionsDisabled: true }, - tempNuGetPath, + authInfo, + { credProviderFolder: null, extensionsDisabled: true }, + tempNuGetPath, false /* useNugetToModifyConfigFile */); - const feed = getProjectAndFeedIdFromInputParam('feedPublish'); + const feed = getProjectAndFeedIdFromInputParam('feedPublish'); feedUri = await nutil.getNuGetFeedRegistryUrl(packagingLocation.DefaultPackagingUri, feed.feedId, feed.projectId, null, accessToken, /* useSession */ true); - nuGetConfigHelper.addSourcesToTempNuGetConfig([{ feedName: feed.feedId, feedUri: feedUri, isInternal: true }]); - configFile = nuGetConfigHelper.tempNugetConfigPath; - - apiKey = 'VSTS'; - } else { - const externalAuthArr = commandHelper.GetExternalAuthInfoArray('externalEndpoint'); - authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo, externalAuthArr); - nuGetConfigHelper = new NuGetConfigHelper2( - null, + nuGetConfigHelper.addSourcesToTempNuGetConfig([{ feedName: feed.feedId, feedUri: feedUri, isInternal: true }]); + configFile = nuGetConfigHelper.tempNugetConfigPath; + + apiKey = 'VSTS'; + } else { + const externalAuthArr = commandHelper.GetExternalAuthInfoArray('externalEndpoint'); + authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo, externalAuthArr); + nuGetConfigHelper = new NuGetConfigHelper2( + null, null, /* nugetConfigPath */ - authInfo, - { credProviderFolder: null, extensionsDisabled: true }, - tempNuGetPath, + authInfo, + { credProviderFolder: null, extensionsDisabled: true }, + tempNuGetPath, false /* useNugetToModifyConfigFile */); - const externalAuth = externalAuthArr[0]; + const externalAuth = externalAuthArr[0]; - if (!externalAuth) { - tl.setResult(tl.TaskResult.Failed, tl.loc('Error_NoSourceSpecifiedForPush')); - return; - } + if (!externalAuth) { + tl.setResult(tl.TaskResult.Failed, tl.loc('Error_NoSourceSpecifiedForPush')); + return; + } - nuGetConfigHelper.addSourcesToTempNuGetConfig([externalAuth.packageSource]); - feedUri = externalAuth.packageSource.feedUri; - configFile = nuGetConfigHelper.tempNugetConfigPath; + nuGetConfigHelper.addSourcesToTempNuGetConfig([externalAuth.packageSource]); + feedUri = externalAuth.packageSource.feedUri; + configFile = nuGetConfigHelper.tempNugetConfigPath; - const authType: auth.ExternalAuthType = externalAuth.authType; - switch (authType) { + const authType: auth.ExternalAuthType = externalAuth.authType; + switch (authType) { case (auth.ExternalAuthType.UsernamePassword): case (auth.ExternalAuthType.Token): - apiKey = 'RequiredApiKey'; - break; + apiKey = 'RequiredApiKey'; + break; case (auth.ExternalAuthType.ApiKey): - const apiKeyAuthInfo = externalAuth as auth.ApiKeyExternalAuthInfo; - apiKey = apiKeyAuthInfo.apiKey; - break; - default: - break; - } - } - // Setting creds in the temp NuGet.config if needed - nuGetConfigHelper.setAuthForSourcesInTempNuGetConfig(); - const dotnetPath = tl.which('dotnet', true); - try { - for (const packageFile of filesList) { - await dotNetNuGetPushAsync(dotnetPath, packageFile, feedUri, apiKey, configFile, tempNuGetConfigDirectory); - } - } finally { - credCleanup(); - } - - tl.setResult(tl.TaskResult.Succeeded, tl.loc('PackagesPublishedSuccessfully')); + const apiKeyAuthInfo = externalAuth as auth.ApiKeyExternalAuthInfo; + apiKey = apiKeyAuthInfo.apiKey; + break; + default: + break; + } + } + // Setting creds in the temp NuGet.config if needed + nuGetConfigHelper.setAuthForSourcesInTempNuGetConfig(); + const dotnetPath = tl.which('dotnet', true); + try { + for (const packageFile of filesList) { + await dotNetNuGetPushAsync(dotnetPath, packageFile, feedUri, apiKey, dotnetArguments, configFile, tempNuGetConfigDirectory); + } + } finally { + credCleanup(); + } - } catch (err) { - tl.error(err); + tl.setResult(tl.TaskResult.Succeeded, tl.loc('PackagesPublishedSuccessfully')); - if (buildIdentityDisplayName || buildIdentityAccount) { - tl.warning(tl.loc('BuildIdentityPermissionsHint', buildIdentityDisplayName, buildIdentityAccount)); - } + } catch (err) { + tl.error(err); - tl.setResult(tl.TaskResult.Failed, tl.loc('PackagesFailedToPublish')); + if (buildIdentityDisplayName || buildIdentityAccount) { + tl.warning(tl.loc('BuildIdentityPermissionsHint', buildIdentityDisplayName, buildIdentityAccount)); } + + tl.setResult(tl.TaskResult.Failed, tl.loc('PackagesFailedToPublish')); + } } -function dotNetNuGetPushAsync(dotnetPath: string, packageFile: string, feedUri: string, apiKey: string, configFile: string, workingDirectory: string): Q.Promise { - const dotnet = tl.tool(dotnetPath); +function dotNetNuGetPushAsync(dotnetPath: string, packageFile: string, feedUri: string, apiKey: string, dotnetArguments: string, configFile: string, workingDirectory: string): Q.Promise { + const dotnet = tl.tool(dotnetPath); + + dotnet.arg('nuget'); + dotnet.arg('push'); - dotnet.arg('nuget'); - dotnet.arg('push'); + dotnet.arg(packageFile); - dotnet.arg(packageFile); + dotnet.arg('--source'); + dotnet.arg(feedUri); - dotnet.arg('--source'); - dotnet.arg(feedUri); + dotnet.arg('--api-key'); + dotnet.arg(apiKey); - dotnet.arg('--api-key'); - dotnet.arg(apiKey); + dotnet.line(dotnetArguments); - // dotnet.exe v1 and v2 do not accept the --verbosity parameter for the "nuget push"" command, although it does for other commands - const envWithProxy = ngRunner.setNuGetProxyEnvironment(process.env, /*configFile*/ null, feedUri); - return dotnet.exec({ cwd: workingDirectory, env: envWithProxy } as IExecOptions); + // dotnet.exe v1 and v2 do not accept the --verbosity parameter for the "nuget push"" command, although it does for other commands + const envWithProxy = ngRunner.setNuGetProxyEnvironment(process.env, /*configFile*/ null, feedUri); + return dotnet.exec({ cwd: workingDirectory, env: envWithProxy } as IExecOptions); } async function getAccessToken(isInternalFeed: boolean, uriPrefixes: any): Promise { - let accessToken: string; - let allowServiceConnection = tl.getVariable('PUBLISH_VIA_SERVICE_CONNECTION'); + let accessToken: string; + let allowServiceConnection = tl.getVariable('PUBLISH_VIA_SERVICE_CONNECTION'); - if (allowServiceConnection) { - let endpoint = tl.getInput('externalEndpoint', false); + if (allowServiceConnection) { + let endpoint = tl.getInput('externalEndpoint', false); - if (endpoint && isInternalFeed === true) { + if (endpoint && isInternalFeed === true) { tl.debug("Found external endpoint, will use token for auth"); - let endpointAuth = tl.getEndpointAuthorization(endpoint, true); - let endpointScheme = tl.getEndpointAuthorizationScheme(endpoint, true).toLowerCase(); - switch (endpointScheme) { + let endpointAuth = tl.getEndpointAuthorization(endpoint, true); + let endpointScheme = tl.getEndpointAuthorizationScheme(endpoint, true).toLowerCase(); + switch (endpointScheme) { case ("token"): accessToken = endpointAuth.parameters["apitoken"]; - break; - default: + break; + default: tl.warning(tl.loc("Warning_UnsupportedServiceConnectionAuth")); - break; - } - } - if (!accessToken && isInternalFeed === true) { + break; + } + } + if (!accessToken && isInternalFeed === true) { tl.debug("Checking for auth from Cred Provider."); - const feed = getProjectAndFeedIdFromInputParam('feedPublish'); + const feed = getProjectAndFeedIdFromInputParam('feedPublish'); const JsonEndpointsString = process.env["VSS_NUGET_EXTERNAL_FEED_ENDPOINTS"]; - if (JsonEndpointsString) { - tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`); - tl.debug(`Endpoints found: ${JsonEndpointsString}`); - - let endpointsArray: { endpointCredentials: EndpointCredentials[] } = JSON.parse(JsonEndpointsString); - let matchingEndpoint: EndpointCredentials; - for (const e of endpointsArray.endpointCredentials) { - for (const prefix of uriPrefixes) { - if (e.endpoint.toUpperCase().startsWith(prefix.toUpperCase())) { - let isServiceConnectionValid = await tryServiceConnection(e, feed); - if (isServiceConnectionValid) { - matchingEndpoint = e; - break; - } - } - } - if (matchingEndpoint) { - accessToken = matchingEndpoint.password; - break; - } - } + if (JsonEndpointsString) { + tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`); + tl.debug(`Endpoints found: ${JsonEndpointsString}`); + + let endpointsArray: { endpointCredentials: EndpointCredentials[] } = JSON.parse(JsonEndpointsString); + let matchingEndpoint: EndpointCredentials; + for (const e of endpointsArray.endpointCredentials) { + for (const prefix of uriPrefixes) { + if (e.endpoint.toUpperCase().startsWith(prefix.toUpperCase())) { + let isServiceConnectionValid = await tryServiceConnection(e, feed); + if (isServiceConnectionValid) { + matchingEndpoint = e; + break; + } } + } + if (matchingEndpoint) { + accessToken = matchingEndpoint.password; + break; + } } - if (accessToken) { - return accessToken; - } + } + } + if (accessToken) { + return accessToken; } + } - tl.debug('Defaulting to use the System Access Token.'); - accessToken = pkgLocationUtils.getSystemAccessToken(); - return accessToken; + tl.debug('Defaulting to use the System Access Token.'); + accessToken = pkgLocationUtils.getSystemAccessToken(); + return accessToken; } async function tryServiceConnection(endpoint: EndpointCredentials, feed: any): Promise { - // Create request - const request = new WebRequest(); - const token64 = Buffer.from(`${endpoint.username}:${endpoint.password}`).toString('base64'); - request.uri = endpoint.endpoint; - request.method = 'GET'; - request.headers = { + // Create request + const request = new WebRequest(); + const token64 = Buffer.from(`${endpoint.username}:${endpoint.password}`).toString('base64'); + request.uri = endpoint.endpoint; + request.method = 'GET'; + request.headers = { "Content-Type": "application/json", "Authorization": "Basic " + token64 - }; + }; - const timeout: number = getRequestTimeout(); + const timeout: number = getRequestTimeout(); const retriableErrorCodes = ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"]; - const retriableStatusCodes = [408, 409, 500, 502, 503, 504]; - - const options: WebRequestOptions = { - retryCount: 3, - retryIntervalInSeconds: 5, - retriableErrorCodes, - retriableStatusCodes, - retryRequestTimedout: true, - socketTimeout: timeout, - httpGlobalAgentOptions: { - timeout: timeout - } - }; + const retriableStatusCodes = [408, 409, 500, 502, 503, 504]; + + const options: WebRequestOptions = { + retryCount: 3, + retryIntervalInSeconds: 5, + retriableErrorCodes, + retriableStatusCodes, + retryRequestTimedout: true, + socketTimeout: timeout, + httpGlobalAgentOptions: { + timeout: timeout + } + }; - const response = await sendRequest(request, options); + const response = await sendRequest(request, options); - if (response.statusCode == 200) { - if (response.body) { - for (const entry of response.body.resources) { + if (response.statusCode == 200) { + if (response.body) { + for (const entry of response.body.resources) { if (entry['@type'] === 'AzureDevOpsProjectId' && !(entry['label'].toUpperCase() === feed.projectId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.projectId.toUpperCase()))) { - return false; - } + return false; + } if (entry['@type'] === 'VssFeedId' && !(entry['label'].toUpperCase() === feed.feedId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.feedId.toUpperCase()))) { - return false; - } - } - // We found matches in feedId and projectId, return the service connection - return true; + return false; } + } + // We found matches in feedId and projectId, return the service connection + return true; } - return false; + } + return false; } \ No newline at end of file From 25762390937c6dcd67c2d6bdce6a7fb1bd09a4c7 Mon Sep 17 00:00:00 2001 From: Joren Thijs Date: Tue, 27 May 2025 09:39:28 +0200 Subject: [PATCH 2/8] add test for skip-duplicate --- .../Tests/PushTests/skipDuplicate.ts | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Tasks/DotNetCoreCLIV2/Tests/PushTests/skipDuplicate.ts diff --git a/Tasks/DotNetCoreCLIV2/Tests/PushTests/skipDuplicate.ts b/Tasks/DotNetCoreCLIV2/Tests/PushTests/skipDuplicate.ts new file mode 100644 index 000000000000..6b1877b213e8 --- /dev/null +++ b/Tasks/DotNetCoreCLIV2/Tests/PushTests/skipDuplicate.ts @@ -0,0 +1,63 @@ +import ma = require('azure-pipelines-task-lib/mock-answer'); +import tmrm = require('azure-pipelines-task-lib/mock-run'); +import path = require('path'); +import util = require('../DotnetMockHelper'); + +let taskPath = path.join(__dirname, '../..', 'dotnetcore.js'); +let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); +let nmh: util.DotnetMockHelper = new util.DotnetMockHelper(tmr); +nmh.setNugetVersionInputDefault(); +tmr.setInput('command', 'push'); +tmr.setInput('searchPatternPush', 'foo.nupkg'); +tmr.setInput('nuGetFeedType', 'internal'); +tmr.setInput('feedPublish', 'ProjectId/FeedFooId'); +tmr.setInput('arguments', '--skip-duplicate'); + +let a: ma.TaskLibAnswers = { + "osType": {}, + "checkPath": { + "c:\\agent\\home\\directory\\foo.nupkg": true, + "c:\\path\\dotnet.exe": true + }, + "which": { + "dotnet": "c:\\path\\dotnet.exe" + }, + "exec": { + // First push succeeds + "c:\\path\\dotnet.exe nuget push c:\\agent\\home\\directory\\foo.nupkg --source https://vsts/packagesource --api-key VSTS": { + "code": 0, + "stdout": "dotnet output", + "stderr": "" + }, + // Second push (duplicate) also succeeds due to --skip-duplicate + "c:\\path\\dotnet.exe nuget push c:\\agent\\home\\directory\\foo.nupkg --source https://vsts/packagesource --api-key VSTS --skip-duplicate": { + "code": 0, + "stdout": "Package already exists, skipping due to --skip-duplicate", + "stderr": "" + } + }, + "exist": {}, + "stats": { + "c:\\agent\\home\\directory\\foo.nupkg": { + "isFile": true + } + }, + "rmRF": { + "c:\\agent\\home\\directory\\NuGet_1": { + "success": true + } + }, + "findMatch": { + "fromMockedUtility-foo.nupkg" : ["c:\\agent\\home\\directory\\foo.nupkg"] + } +}; +nmh.setAnswers(a); +nmh.registerNugetUtilityMock(["c:\\agent\\home\\directory\\foo.nupkg"]); +nmh.registerDefaultNugetVersionMock(); +nmh.registerToolRunnerMock(); +nmh.registerNugetConfigMock(); +nmh.RegisterLocationServiceMocks(); + +// Simulate pushing the same package twice +tmr.run(); +tmr.run(); \ No newline at end of file From 132d879350491732e8cbcf698bd3aafcfa9c3c46 Mon Sep 17 00:00:00 2001 From: Joren Thijs Date: Tue, 27 May 2025 09:55:19 +0200 Subject: [PATCH 3/8] ffix: bump version --- Tasks/DotNetCoreCLIV2/package-lock.json | 4 ++-- Tasks/DotNetCoreCLIV2/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tasks/DotNetCoreCLIV2/package-lock.json b/Tasks/DotNetCoreCLIV2/package-lock.json index d7234645f9f9..18672a09b00e 100644 --- a/Tasks/DotNetCoreCLIV2/package-lock.json +++ b/Tasks/DotNetCoreCLIV2/package-lock.json @@ -1,12 +1,12 @@ { "name": "vsts-tasks-dotnetcoreexe", - "version": "2.129.4", + "version": "2.258.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vsts-tasks-dotnetcoreexe", - "version": "2.129.4", + "version": "2.258.0", "license": "MIT", "dependencies": { "@types/mocha": "^5.2.7", diff --git a/Tasks/DotNetCoreCLIV2/package.json b/Tasks/DotNetCoreCLIV2/package.json index df6b5e0a1ffe..da3621624e03 100644 --- a/Tasks/DotNetCoreCLIV2/package.json +++ b/Tasks/DotNetCoreCLIV2/package.json @@ -1,6 +1,6 @@ { "name": "vsts-tasks-dotnetcoreexe", - "version": "2.129.4", + "version": "2.258.0", "description": "Dotnet core exe", "main": "dotnetcore.js", "scripts": { From 232e83d2109461637a47b903d975d9aa2b6d25ff Mon Sep 17 00:00:00 2001 From: Joren Thijs Date: Fri, 30 May 2025 21:11:30 +0200 Subject: [PATCH 4/8] fix: format code with default formatter --- .../Tests/PushTests/skipDuplicate.ts | 2 +- Tasks/DotNetCoreCLIV2/pushcommand.ts | 542 +++++++++--------- 2 files changed, 283 insertions(+), 261 deletions(-) diff --git a/Tasks/DotNetCoreCLIV2/Tests/PushTests/skipDuplicate.ts b/Tasks/DotNetCoreCLIV2/Tests/PushTests/skipDuplicate.ts index 6b1877b213e8..293725276038 100644 --- a/Tasks/DotNetCoreCLIV2/Tests/PushTests/skipDuplicate.ts +++ b/Tasks/DotNetCoreCLIV2/Tests/PushTests/skipDuplicate.ts @@ -48,7 +48,7 @@ let a: ma.TaskLibAnswers = { } }, "findMatch": { - "fromMockedUtility-foo.nupkg" : ["c:\\agent\\home\\directory\\foo.nupkg"] + "fromMockedUtility-foo.nupkg": ["c:\\agent\\home\\directory\\foo.nupkg"] } }; nmh.setAnswers(a); diff --git a/Tasks/DotNetCoreCLIV2/pushcommand.ts b/Tasks/DotNetCoreCLIV2/pushcommand.ts index ef79c2c2b95b..2de7124ab6e2 100644 --- a/Tasks/DotNetCoreCLIV2/pushcommand.ts +++ b/Tasks/DotNetCoreCLIV2/pushcommand.ts @@ -15,298 +15,320 @@ import { WebRequest, WebRequestOptions, sendRequest } from 'azure-pipelines-task import { getRequestTimeout } from './Common/utility'; interface EndpointCredentials { - endpoint: string; - username?: string; - password: string; + endpoint: string; + username?: string; + password: string; } export async function run(): Promise { - let packagingLocation: pkgLocationUtils.PackagingLocation; - try { - const timeout: number = getRequestTimeout(); - const webApiOptions: RequestOptions = { - socketTimeout: timeout, - globalAgentOptions: { - timeout: timeout, - } - }; - packagingLocation = await pkgLocationUtils.getPackagingUris(pkgLocationUtils.ProtocolType.NuGet, webApiOptions); - } catch (error) { - tl.debug('Unable to get packaging URIs'); - logError(error); - throw error; - } - - const buildIdentityDisplayName: string = null; - const buildIdentityAccount: string = null; - try { - // Get list of files to publish - const searchPatternInput = tl.getPathInput('searchPatternPush', true, false); - const findOptions: tl.FindOptions = {}; - const matchOptions: tl.MatchOptions = {}; - const searchPatterns: string[] = nutil.getPatternsArrayFromInput(searchPatternInput); - const filesList = tl.findMatch(undefined, searchPatterns, findOptions, matchOptions); - - filesList.forEach(packageFile => { - if (!tl.stats(packageFile).isFile()) { - throw new Error(tl.loc('Error_PushNotARegularFile', packageFile)); - } - }); - - if (filesList.length < 1) { - tl.setResult(tl.TaskResult.Failed, tl.loc('Info_NoPackagesMatchedTheSearchPattern')); - return; + let packagingLocation: pkgLocationUtils.PackagingLocation; + try { + const timeout: number = getRequestTimeout(); + const webApiOptions: RequestOptions = { + socketTimeout: timeout, + globalAgentOptions: { + timeout: timeout + } + }; + packagingLocation = await pkgLocationUtils.getPackagingUris(pkgLocationUtils.ProtocolType.NuGet, webApiOptions); + } catch (error) { + tl.debug('Unable to get packaging URIs'); + logError(error); + throw error; } - // Get the info the type of feed - let nugetFeedType = tl.getInput('nuGetFeedType') || 'internal'; + const buildIdentityDisplayName: string = null; + const buildIdentityAccount: string = null; + try { + // Get list of files to publish + const searchPatternInput = tl.getPathInput('searchPatternPush', true, false); + const findOptions: tl.FindOptions = {}; + const matchOptions: tl.MatchOptions = {}; + const searchPatterns: string[] = nutil.getPatternsArrayFromInput(searchPatternInput); + const filesList = tl.findMatch(undefined, searchPatterns, findOptions, matchOptions); + + filesList.forEach(packageFile => { + if (!tl.stats(packageFile).isFile()) { + throw new Error(tl.loc('Error_PushNotARegularFile', packageFile)); + } + }); - // Make sure the feed type is an expected one - const normalizedNuGetFeedType = ['internal', 'external'].find(x => nugetFeedType.toUpperCase() === x.toUpperCase()); - if (!normalizedNuGetFeedType) { - throw new Error(tl.loc('UnknownFeedType', nugetFeedType)); - } - nugetFeedType = normalizedNuGetFeedType; - - const serviceUri = tl.getEndpointUrl('SYSTEMVSSCONNECTION', false); - let urlPrefixes = packagingLocation.PackagingUris; - tl.debug(`discovered URL prefixes: ${urlPrefixes}`); - - // Note to readers: This variable will be going away once we have a fix for the location service for - // customers behind proxies - const testPrefixes = tl.getVariable('DotNetCoreCLITask.ExtraUrlPrefixesForTesting'); - if (testPrefixes) { - urlPrefixes = urlPrefixes.concat(testPrefixes.split(';')); - tl.debug(`all URL prefixes: ${urlPrefixes}`); - } + if (filesList.length < 1) { + tl.setResult(tl.TaskResult.Failed, tl.loc('Info_NoPackagesMatchedTheSearchPattern')); + return; + } - // Read arguments so users can specify extra arguments like --skip-duplicate - let dotnetArguments: string = tl.getInput('arguments', false) || ''; - - // Setting up auth info - let accessToken; - const isInternalFeed: boolean = nugetFeedType === 'internal'; - accessToken = await getAccessToken(isInternalFeed, urlPrefixes); - const internalAuthInfo = new auth.InternalAuthInfo(urlPrefixes, accessToken, /*useCredProvider*/ null, true); - - let configFile = null; - let apiKey: string; - - // dotnet nuget push does not currently accept a --config-file parameter - // so we are going to work around this by creating a temporary working directory for dotnet with - // a nuget config file it will load by default. - const tempNuGetConfigDirectory = path.join(NuGetConfigHelper2.getTempNuGetConfigBasePath(), 'NuGet_' + tl.getVariable('build.buildId')); - const tempNuGetPath = path.join(tempNuGetConfigDirectory, 'nuget.config'); - tl.mkdirP(tempNuGetConfigDirectory); - let credCleanup = () => { - if (tl.exist(tempNuGetConfigDirectory)) { - tl.rmRF(tempNuGetConfigDirectory) - } - }; + // Get the info the type of feed + let nugetFeedType = tl.getInput('nuGetFeedType') || 'internal'; - let feedUri: string = undefined; - - let authInfo: auth.NuGetExtendedAuthInfo; - let nuGetConfigHelper: NuGetConfigHelper2; - - if (isInternalFeed) { - authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo); - nuGetConfigHelper = new NuGetConfigHelper2( - null, - null, /* nugetConfigPath */ - authInfo, - { credProviderFolder: null, extensionsDisabled: true }, - tempNuGetPath, - false /* useNugetToModifyConfigFile */); - - const feed = getProjectAndFeedIdFromInputParam('feedPublish'); - - feedUri = await nutil.getNuGetFeedRegistryUrl(packagingLocation.DefaultPackagingUri, feed.feedId, feed.projectId, null, accessToken, /* useSession */ true); - nuGetConfigHelper.addSourcesToTempNuGetConfig([{ feedName: feed.feedId, feedUri: feedUri, isInternal: true }]); - configFile = nuGetConfigHelper.tempNugetConfigPath; - - apiKey = 'VSTS'; - } else { - const externalAuthArr = commandHelper.GetExternalAuthInfoArray('externalEndpoint'); - authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo, externalAuthArr); - nuGetConfigHelper = new NuGetConfigHelper2( - null, - null, /* nugetConfigPath */ - authInfo, - { credProviderFolder: null, extensionsDisabled: true }, - tempNuGetPath, - false /* useNugetToModifyConfigFile */); - - const externalAuth = externalAuthArr[0]; - - if (!externalAuth) { - tl.setResult(tl.TaskResult.Failed, tl.loc('Error_NoSourceSpecifiedForPush')); - return; - } - - nuGetConfigHelper.addSourcesToTempNuGetConfig([externalAuth.packageSource]); - feedUri = externalAuth.packageSource.feedUri; - configFile = nuGetConfigHelper.tempNugetConfigPath; - - const authType: auth.ExternalAuthType = externalAuth.authType; - switch (authType) { - case (auth.ExternalAuthType.UsernamePassword): - case (auth.ExternalAuthType.Token): - apiKey = 'RequiredApiKey'; - break; - case (auth.ExternalAuthType.ApiKey): - const apiKeyAuthInfo = externalAuth as auth.ApiKeyExternalAuthInfo; - apiKey = apiKeyAuthInfo.apiKey; - break; - default: - break; - } - } - // Setting creds in the temp NuGet.config if needed - nuGetConfigHelper.setAuthForSourcesInTempNuGetConfig(); - const dotnetPath = tl.which('dotnet', true); - try { - for (const packageFile of filesList) { + // Make sure the feed type is an expected one + const normalizedNuGetFeedType = ['internal', 'external'].find(x => nugetFeedType.toUpperCase() === x.toUpperCase()); + if (!normalizedNuGetFeedType) { + throw new Error(tl.loc('UnknownFeedType', nugetFeedType)); + } + nugetFeedType = normalizedNuGetFeedType; + + const serviceUri = tl.getEndpointUrl('SYSTEMVSSCONNECTION', false); + let urlPrefixes = packagingLocation.PackagingUris; + tl.debug(`discovered URL prefixes: ${urlPrefixes}`); + + // Note to readers: This variable will be going away once we have a fix for the location service for + // customers behind proxies + const testPrefixes = tl.getVariable('DotNetCoreCLITask.ExtraUrlPrefixesForTesting'); + if (testPrefixes) { + urlPrefixes = urlPrefixes.concat(testPrefixes.split(';')); + tl.debug(`all URL prefixes: ${urlPrefixes}`); + } + + // Read arguments so users can specify extra arguments like --skip-duplicate + let dotnetArguments: string = tl.getInput('arguments', false) || ''; + + // Setting up auth info + let accessToken; + const isInternalFeed: boolean = nugetFeedType === 'internal'; + accessToken = await getAccessToken(isInternalFeed, urlPrefixes); + const internalAuthInfo = new auth.InternalAuthInfo(urlPrefixes, accessToken, /*useCredProvider*/ null, true); + + let configFile = null; + let apiKey: string; + + // dotnet nuget push does not currently accept a --config-file parameter + // so we are going to work around this by creating a temporary working directory for dotnet with + // a nuget config file it will load by default. + const tempNuGetConfigDirectory = path.join(NuGetConfigHelper2.getTempNuGetConfigBasePath(), 'NuGet_' + tl.getVariable('build.buildId')); + const tempNuGetPath = path.join(tempNuGetConfigDirectory, 'nuget.config'); + tl.mkdirP(tempNuGetConfigDirectory); + let credCleanup = () => { + if (tl.exist(tempNuGetConfigDirectory)) { + tl.rmRF(tempNuGetConfigDirectory); + } + }; + + let feedUri: string = undefined; + + let authInfo: auth.NuGetExtendedAuthInfo; + let nuGetConfigHelper: NuGetConfigHelper2; + + if (isInternalFeed) { + authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo); + nuGetConfigHelper = new NuGetConfigHelper2( + null, + null /* nugetConfigPath */, + authInfo, + { credProviderFolder: null, extensionsDisabled: true }, + tempNuGetPath, + false /* useNugetToModifyConfigFile */ + ); + + const feed = getProjectAndFeedIdFromInputParam('feedPublish'); + + feedUri = await nutil.getNuGetFeedRegistryUrl( + packagingLocation.DefaultPackagingUri, + feed.feedId, + feed.projectId, + null, + accessToken, + /* useSession */ true + ); + nuGetConfigHelper.addSourcesToTempNuGetConfig([{ feedName: feed.feedId, feedUri: feedUri, isInternal: true }]); + configFile = nuGetConfigHelper.tempNugetConfigPath; + + apiKey = 'VSTS'; + } else { + const externalAuthArr = commandHelper.GetExternalAuthInfoArray('externalEndpoint'); + authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo, externalAuthArr); + nuGetConfigHelper = new NuGetConfigHelper2( + null, + null /* nugetConfigPath */, + authInfo, + { credProviderFolder: null, extensionsDisabled: true }, + tempNuGetPath, + false /* useNugetToModifyConfigFile */ + ); + + const externalAuth = externalAuthArr[0]; + + if (!externalAuth) { + tl.setResult(tl.TaskResult.Failed, tl.loc('Error_NoSourceSpecifiedForPush')); + return; + } + + nuGetConfigHelper.addSourcesToTempNuGetConfig([externalAuth.packageSource]); + feedUri = externalAuth.packageSource.feedUri; + configFile = nuGetConfigHelper.tempNugetConfigPath; + + const authType: auth.ExternalAuthType = externalAuth.authType; + switch (authType) { + case auth.ExternalAuthType.UsernamePassword: + case auth.ExternalAuthType.Token: + apiKey = 'RequiredApiKey'; + break; + case auth.ExternalAuthType.ApiKey: + const apiKeyAuthInfo = externalAuth as auth.ApiKeyExternalAuthInfo; + apiKey = apiKeyAuthInfo.apiKey; + break; + default: + break; + } + } + // Setting creds in the temp NuGet.config if needed + nuGetConfigHelper.setAuthForSourcesInTempNuGetConfig(); + const dotnetPath = tl.which('dotnet', true); + try { + for (const packageFile of filesList) { await dotNetNuGetPushAsync(dotnetPath, packageFile, feedUri, apiKey, dotnetArguments, configFile, tempNuGetConfigDirectory); - } - } finally { - credCleanup(); - } + } + } finally { + credCleanup(); + } - tl.setResult(tl.TaskResult.Succeeded, tl.loc('PackagesPublishedSuccessfully')); + tl.setResult(tl.TaskResult.Succeeded, tl.loc('PackagesPublishedSuccessfully')); + } catch (err) { + tl.error(err); - } catch (err) { - tl.error(err); + if (buildIdentityDisplayName || buildIdentityAccount) { + tl.warning(tl.loc('BuildIdentityPermissionsHint', buildIdentityDisplayName, buildIdentityAccount)); + } - if (buildIdentityDisplayName || buildIdentityAccount) { - tl.warning(tl.loc('BuildIdentityPermissionsHint', buildIdentityDisplayName, buildIdentityAccount)); + tl.setResult(tl.TaskResult.Failed, tl.loc('PackagesFailedToPublish')); } - - tl.setResult(tl.TaskResult.Failed, tl.loc('PackagesFailedToPublish')); - } } -function dotNetNuGetPushAsync(dotnetPath: string, packageFile: string, feedUri: string, apiKey: string, dotnetArguments: string, configFile: string, workingDirectory: string): Q.Promise { - const dotnet = tl.tool(dotnetPath); +function dotNetNuGetPushAsync( + dotnetPath: string, + packageFile: string, + feedUri: string, + apiKey: string, + dotnetArguments: string, + configFile: string, + workingDirectory: string +): Q.Promise { + const dotnet = tl.tool(dotnetPath); - dotnet.arg('nuget'); - dotnet.arg('push'); + dotnet.arg('nuget'); + dotnet.arg('push'); - dotnet.arg(packageFile); + dotnet.arg(packageFile); - dotnet.arg('--source'); - dotnet.arg(feedUri); + dotnet.arg('--source'); + dotnet.arg(feedUri); - dotnet.arg('--api-key'); - dotnet.arg(apiKey); + dotnet.arg('--api-key'); + dotnet.arg(apiKey); - dotnet.line(dotnetArguments); + dotnet.line(dotnetArguments); - // dotnet.exe v1 and v2 do not accept the --verbosity parameter for the "nuget push"" command, although it does for other commands - const envWithProxy = ngRunner.setNuGetProxyEnvironment(process.env, /*configFile*/ null, feedUri); - return dotnet.exec({ cwd: workingDirectory, env: envWithProxy } as IExecOptions); + // dotnet.exe v1 and v2 do not accept the --verbosity parameter for the "nuget push"" command, although it does for other commands + const envWithProxy = ngRunner.setNuGetProxyEnvironment(process.env, /*configFile*/ null, feedUri); + return dotnet.exec({ cwd: workingDirectory, env: envWithProxy } as IExecOptions); } async function getAccessToken(isInternalFeed: boolean, uriPrefixes: any): Promise { - let accessToken: string; - let allowServiceConnection = tl.getVariable('PUBLISH_VIA_SERVICE_CONNECTION'); - - if (allowServiceConnection) { - let endpoint = tl.getInput('externalEndpoint', false); - - if (endpoint && isInternalFeed === true) { - tl.debug("Found external endpoint, will use token for auth"); - let endpointAuth = tl.getEndpointAuthorization(endpoint, true); - let endpointScheme = tl.getEndpointAuthorizationScheme(endpoint, true).toLowerCase(); - switch (endpointScheme) { - case ("token"): - accessToken = endpointAuth.parameters["apitoken"]; - break; - default: - tl.warning(tl.loc("Warning_UnsupportedServiceConnectionAuth")); - break; - } - } - if (!accessToken && isInternalFeed === true) { - tl.debug("Checking for auth from Cred Provider."); - const feed = getProjectAndFeedIdFromInputParam('feedPublish'); - const JsonEndpointsString = process.env["VSS_NUGET_EXTERNAL_FEED_ENDPOINTS"]; - - if (JsonEndpointsString) { - tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`); - tl.debug(`Endpoints found: ${JsonEndpointsString}`); - - let endpointsArray: { endpointCredentials: EndpointCredentials[] } = JSON.parse(JsonEndpointsString); - let matchingEndpoint: EndpointCredentials; - for (const e of endpointsArray.endpointCredentials) { - for (const prefix of uriPrefixes) { - if (e.endpoint.toUpperCase().startsWith(prefix.toUpperCase())) { - let isServiceConnectionValid = await tryServiceConnection(e, feed); - if (isServiceConnectionValid) { - matchingEndpoint = e; - break; - } + let accessToken: string; + let allowServiceConnection = tl.getVariable('PUBLISH_VIA_SERVICE_CONNECTION'); + + if (allowServiceConnection) { + let endpoint = tl.getInput('externalEndpoint', false); + + if (endpoint && isInternalFeed === true) { + tl.debug('Found external endpoint, will use token for auth'); + let endpointAuth = tl.getEndpointAuthorization(endpoint, true); + let endpointScheme = tl.getEndpointAuthorizationScheme(endpoint, true).toLowerCase(); + switch (endpointScheme) { + case 'token': + accessToken = endpointAuth.parameters['apitoken']; + break; + default: + tl.warning(tl.loc('Warning_UnsupportedServiceConnectionAuth')); + break; } - } - if (matchingEndpoint) { - accessToken = matchingEndpoint.password; - break; - } } - } - } - if (accessToken) { - return accessToken; + if (!accessToken && isInternalFeed === true) { + tl.debug('Checking for auth from Cred Provider.'); + const feed = getProjectAndFeedIdFromInputParam('feedPublish'); + const JsonEndpointsString = process.env['VSS_NUGET_EXTERNAL_FEED_ENDPOINTS']; + + if (JsonEndpointsString) { + tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`); + tl.debug(`Endpoints found: ${JsonEndpointsString}`); + + let endpointsArray: { endpointCredentials: EndpointCredentials[] } = JSON.parse(JsonEndpointsString); + let matchingEndpoint: EndpointCredentials; + for (const e of endpointsArray.endpointCredentials) { + for (const prefix of uriPrefixes) { + if (e.endpoint.toUpperCase().startsWith(prefix.toUpperCase())) { + let isServiceConnectionValid = await tryServiceConnection(e, feed); + if (isServiceConnectionValid) { + matchingEndpoint = e; + break; + } + } + } + if (matchingEndpoint) { + accessToken = matchingEndpoint.password; + break; + } + } + } + } + if (accessToken) { + return accessToken; + } } - } - tl.debug('Defaulting to use the System Access Token.'); - accessToken = pkgLocationUtils.getSystemAccessToken(); - return accessToken; + tl.debug('Defaulting to use the System Access Token.'); + accessToken = pkgLocationUtils.getSystemAccessToken(); + return accessToken; } async function tryServiceConnection(endpoint: EndpointCredentials, feed: any): Promise { - // Create request - const request = new WebRequest(); - const token64 = Buffer.from(`${endpoint.username}:${endpoint.password}`).toString('base64'); - request.uri = endpoint.endpoint; - request.method = 'GET'; - request.headers = { - "Content-Type": "application/json", - "Authorization": "Basic " + token64 - }; - - const timeout: number = getRequestTimeout(); - const retriableErrorCodes = ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"]; - const retriableStatusCodes = [408, 409, 500, 502, 503, 504]; - - const options: WebRequestOptions = { - retryCount: 3, - retryIntervalInSeconds: 5, - retriableErrorCodes, - retriableStatusCodes, - retryRequestTimedout: true, - socketTimeout: timeout, - httpGlobalAgentOptions: { - timeout: timeout - } - }; - - const response = await sendRequest(request, options); + // Create request + const request = new WebRequest(); + const token64 = Buffer.from(`${endpoint.username}:${endpoint.password}`).toString('base64'); + request.uri = endpoint.endpoint; + request.method = 'GET'; + request.headers = { + 'Content-Type': 'application/json', + Authorization: 'Basic ' + token64 + }; - if (response.statusCode == 200) { - if (response.body) { - for (const entry of response.body.resources) { - if (entry['@type'] === 'AzureDevOpsProjectId' && !(entry['label'].toUpperCase() === feed.projectId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.projectId.toUpperCase()))) { - return false; + const timeout: number = getRequestTimeout(); + const retriableErrorCodes = ['ETIMEDOUT', 'ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE', 'EA_AGAIN']; + const retriableStatusCodes = [408, 409, 500, 502, 503, 504]; + + const options: WebRequestOptions = { + retryCount: 3, + retryIntervalInSeconds: 5, + retriableErrorCodes, + retriableStatusCodes, + retryRequestTimedout: true, + socketTimeout: timeout, + httpGlobalAgentOptions: { + timeout: timeout } - if (entry['@type'] === 'VssFeedId' && !(entry['label'].toUpperCase() === feed.feedId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.feedId.toUpperCase()))) { - return false; + }; + + const response = await sendRequest(request, options); + + if (response.statusCode == 200) { + if (response.body) { + for (const entry of response.body.resources) { + if ( + entry['@type'] === 'AzureDevOpsProjectId' && + !(entry['label'].toUpperCase() === feed.projectId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.projectId.toUpperCase())) + ) { + return false; + } + if ( + entry['@type'] === 'VssFeedId' && + !(entry['label'].toUpperCase() === feed.feedId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.feedId.toUpperCase())) + ) { + return false; + } + } + // We found matches in feedId and projectId, return the service connection + return true; } - } - // We found matches in feedId and projectId, return the service connection - return true; } - } - return false; -} \ No newline at end of file + return false; +} From 046d1a43470b0521d786e12ad968cfd00288d626 Mon Sep 17 00:00:00 2001 From: Joren Thijs Date: Fri, 30 May 2025 21:15:23 +0200 Subject: [PATCH 5/8] fix: removed formatting changes --- Tasks/DotNetCoreCLIV2/pushcommand.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/Tasks/DotNetCoreCLIV2/pushcommand.ts b/Tasks/DotNetCoreCLIV2/pushcommand.ts index 2de7124ab6e2..3992d6e75ded 100644 --- a/Tasks/DotNetCoreCLIV2/pushcommand.ts +++ b/Tasks/DotNetCoreCLIV2/pushcommand.ts @@ -24,11 +24,11 @@ export async function run(): Promise { let packagingLocation: pkgLocationUtils.PackagingLocation; try { const timeout: number = getRequestTimeout(); - const webApiOptions: RequestOptions = { + const webApiOptions: RequestOptions = { socketTimeout: timeout, globalAgentOptions: { - timeout: timeout - } + timeout: timeout, + } }; packagingLocation = await pkgLocationUtils.getPackagingUris(pkgLocationUtils.ProtocolType.NuGet, webApiOptions); } catch (error) { @@ -100,7 +100,7 @@ export async function run(): Promise { tl.mkdirP(tempNuGetConfigDirectory); let credCleanup = () => { if (tl.exist(tempNuGetConfigDirectory)) { - tl.rmRF(tempNuGetConfigDirectory); + tl.rmRF(tempNuGetConfigDirectory) } }; @@ -113,12 +113,11 @@ export async function run(): Promise { authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo); nuGetConfigHelper = new NuGetConfigHelper2( null, - null /* nugetConfigPath */, + null, /* nugetConfigPath */ authInfo, { credProviderFolder: null, extensionsDisabled: true }, tempNuGetPath, - false /* useNugetToModifyConfigFile */ - ); + false /* useNugetToModifyConfigFile */); const feed = getProjectAndFeedIdFromInputParam('feedPublish'); @@ -139,12 +138,11 @@ export async function run(): Promise { authInfo = new auth.NuGetExtendedAuthInfo(internalAuthInfo, externalAuthArr); nuGetConfigHelper = new NuGetConfigHelper2( null, - null /* nugetConfigPath */, + null, /* nugetConfigPath */ authInfo, { credProviderFolder: null, extensionsDisabled: true }, tempNuGetPath, - false /* useNugetToModifyConfigFile */ - ); + false /* useNugetToModifyConfigFile */); const externalAuth = externalAuthArr[0]; @@ -159,11 +157,11 @@ export async function run(): Promise { const authType: auth.ExternalAuthType = externalAuth.authType; switch (authType) { - case auth.ExternalAuthType.UsernamePassword: - case auth.ExternalAuthType.Token: + case (auth.ExternalAuthType.UsernamePassword): + case (auth.ExternalAuthType.Token): apiKey = 'RequiredApiKey'; break; - case auth.ExternalAuthType.ApiKey: + case (auth.ExternalAuthType.ApiKey): const apiKeyAuthInfo = externalAuth as auth.ApiKeyExternalAuthInfo; apiKey = apiKeyAuthInfo.apiKey; break; From 55c8072c194dd0ca2980a330a842242789aea415 Mon Sep 17 00:00:00 2001 From: Joren Thijs Date: Fri, 30 May 2025 21:21:20 +0200 Subject: [PATCH 6/8] fix: undo more formatter changes --- Tasks/DotNetCoreCLIV2/pushcommand.ts | 35 +++++++++------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/Tasks/DotNetCoreCLIV2/pushcommand.ts b/Tasks/DotNetCoreCLIV2/pushcommand.ts index 3992d6e75ded..d5c28f2fbc79 100644 --- a/Tasks/DotNetCoreCLIV2/pushcommand.ts +++ b/Tasks/DotNetCoreCLIV2/pushcommand.ts @@ -121,14 +121,7 @@ export async function run(): Promise { const feed = getProjectAndFeedIdFromInputParam('feedPublish'); - feedUri = await nutil.getNuGetFeedRegistryUrl( - packagingLocation.DefaultPackagingUri, - feed.feedId, - feed.projectId, - null, - accessToken, - /* useSession */ true - ); + feedUri = await nutil.getNuGetFeedRegistryUrl(packagingLocation.DefaultPackagingUri, feed.feedId, feed.projectId, null, accessToken, /* useSession */ true); nuGetConfigHelper.addSourcesToTempNuGetConfig([{ feedName: feed.feedId, feedUri: feedUri, isInternal: true }]); configFile = nuGetConfigHelper.tempNugetConfigPath; @@ -229,22 +222,22 @@ async function getAccessToken(isInternalFeed: boolean, uriPrefixes: any): Promis let endpoint = tl.getInput('externalEndpoint', false); if (endpoint && isInternalFeed === true) { - tl.debug('Found external endpoint, will use token for auth'); + tl.debug("Found external endpoint, will use token for auth"); let endpointAuth = tl.getEndpointAuthorization(endpoint, true); let endpointScheme = tl.getEndpointAuthorizationScheme(endpoint, true).toLowerCase(); switch (endpointScheme) { - case 'token': + case "token": accessToken = endpointAuth.parameters['apitoken']; break; default: - tl.warning(tl.loc('Warning_UnsupportedServiceConnectionAuth')); + tl.warning(tl.loc("Warning_UnsupportedServiceConnectionAuth")); break; } } if (!accessToken && isInternalFeed === true) { - tl.debug('Checking for auth from Cred Provider.'); + tl.debug("Checking for auth from Cred Provider."); const feed = getProjectAndFeedIdFromInputParam('feedPublish'); - const JsonEndpointsString = process.env['VSS_NUGET_EXTERNAL_FEED_ENDPOINTS']; + const JsonEndpointsString = process.env["VSS_NUGET_EXTERNAL_FEED_ENDPOINTS"]; if (JsonEndpointsString) { tl.debug(`Feed details ${feed.feedId} ${feed.projectId}`); @@ -286,12 +279,12 @@ async function tryServiceConnection(endpoint: EndpointCredentials, feed: any): P request.uri = endpoint.endpoint; request.method = 'GET'; request.headers = { - 'Content-Type': 'application/json', - Authorization: 'Basic ' + token64 + "Content-Type": "application/json", + Authorization: "Basic " + token64 }; const timeout: number = getRequestTimeout(); - const retriableErrorCodes = ['ETIMEDOUT', 'ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE', 'EA_AGAIN']; + const retriableErrorCodes = ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"]; const retriableStatusCodes = [408, 409, 500, 502, 503, 504]; const options: WebRequestOptions = { @@ -311,16 +304,10 @@ async function tryServiceConnection(endpoint: EndpointCredentials, feed: any): P if (response.statusCode == 200) { if (response.body) { for (const entry of response.body.resources) { - if ( - entry['@type'] === 'AzureDevOpsProjectId' && - !(entry['label'].toUpperCase() === feed.projectId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.projectId.toUpperCase())) - ) { + if (entry['@type'] === 'AzureDevOpsProjectId' && !(entry['label'].toUpperCase() === feed.projectId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.projectId.toUpperCase()))) { return false; } - if ( - entry['@type'] === 'VssFeedId' && - !(entry['label'].toUpperCase() === feed.feedId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.feedId.toUpperCase())) - ) { + if (entry['@type'] === 'VssFeedId' && !(entry['label'].toUpperCase() === feed.feedId.toUpperCase() || entry['@id'].toUpperCase().endsWith(feed.feedId.toUpperCase()))) { return false; } } From 47c25a7868904b66012828b5628d36dc6485255e Mon Sep 17 00:00:00 2001 From: Joren Thijs Date: Fri, 30 May 2025 21:24:18 +0200 Subject: [PATCH 7/8] fix: undo last formatter changes --- Tasks/DotNetCoreCLIV2/pushcommand.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Tasks/DotNetCoreCLIV2/pushcommand.ts b/Tasks/DotNetCoreCLIV2/pushcommand.ts index d5c28f2fbc79..cbfb1e820575 100644 --- a/Tasks/DotNetCoreCLIV2/pushcommand.ts +++ b/Tasks/DotNetCoreCLIV2/pushcommand.ts @@ -185,15 +185,7 @@ export async function run(): Promise { } } -function dotNetNuGetPushAsync( - dotnetPath: string, - packageFile: string, - feedUri: string, - apiKey: string, - dotnetArguments: string, - configFile: string, - workingDirectory: string -): Q.Promise { +function dotNetNuGetPushAsync(dotnetPath: string, packageFile: string, feedUri: string, apiKey: string, dotnetArguments: string, configFile: string, workingDirectory: string): Q.Promise { const dotnet = tl.tool(dotnetPath); dotnet.arg('nuget'); @@ -226,7 +218,7 @@ async function getAccessToken(isInternalFeed: boolean, uriPrefixes: any): Promis let endpointAuth = tl.getEndpointAuthorization(endpoint, true); let endpointScheme = tl.getEndpointAuthorizationScheme(endpoint, true).toLowerCase(); switch (endpointScheme) { - case "token": + case ("token"): accessToken = endpointAuth.parameters['apitoken']; break; default: @@ -280,7 +272,7 @@ async function tryServiceConnection(endpoint: EndpointCredentials, feed: any): P request.method = 'GET'; request.headers = { "Content-Type": "application/json", - Authorization: "Basic " + token64 + "Authorization": "Basic " + token64 }; const timeout: number = getRequestTimeout(); @@ -316,4 +308,4 @@ async function tryServiceConnection(endpoint: EndpointCredentials, feed: any): P } } return false; -} +} \ No newline at end of file From b8dd506b9eaaf8440eebc61a2dd51dde094114da Mon Sep 17 00:00:00 2001 From: Joren Thijs Date: Fri, 30 May 2025 21:25:21 +0200 Subject: [PATCH 8/8] fix: undo last formatter change --- Tasks/DotNetCoreCLIV2/pushcommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tasks/DotNetCoreCLIV2/pushcommand.ts b/Tasks/DotNetCoreCLIV2/pushcommand.ts index cbfb1e820575..93af522b806c 100644 --- a/Tasks/DotNetCoreCLIV2/pushcommand.ts +++ b/Tasks/DotNetCoreCLIV2/pushcommand.ts @@ -219,7 +219,7 @@ async function getAccessToken(isInternalFeed: boolean, uriPrefixes: any): Promis let endpointScheme = tl.getEndpointAuthorizationScheme(endpoint, true).toLowerCase(); switch (endpointScheme) { case ("token"): - accessToken = endpointAuth.parameters['apitoken']; + accessToken = endpointAuth.parameters["apitoken"]; break; default: tl.warning(tl.loc("Warning_UnsupportedServiceConnectionAuth"));