From e9c2c9a95c3b9073e4ce63a15bfdc46304161fbd Mon Sep 17 00:00:00 2001 From: Peter Spillman Date: Tue, 30 May 2017 18:13:38 -0400 Subject: [PATCH] DotNetCoreCLI, restore auth, pack, nuget push --- .../nuget-task-common/Authentication.ts | 69 ++++ .../nuget-task-common/CommandHelper.ts} | 33 +- .../nuget-task-common/NuGetConfigHelper2.ts} | 41 +- .../nuget-task-common/NuGetToolRunner2.ts} | 28 +- .../resources.resjson/en-US/resources.resjson | 2 + Tasks/Common/nuget-task-common/Utility.ts | 39 +- Tasks/Common/nuget-task-common/module.json | 2 + Tasks/DotNetCoreCLI/Common/utility.ts | 22 ++ .../resources.resjson/en-US/resources.resjson | 82 +++- Tasks/DotNetCoreCLI/Tests/DotnetMockHelper.ts | 138 +++++++ Tasks/DotNetCoreCLI/Tests/L0.ts | 195 ++++++++-- .../Tests/NuGetConfigHelper-mock.ts | 19 + .../Tests/PackTests/packBuildNumber.ts | 53 +++ .../Tests/PackTests/packEnvVar.ts | 54 +++ .../Tests/PackTests/packPrerelease.ts | 55 +++ .../Tests/PushTests/internalFeed.ts | 55 +++ .../RestoreTests/selectSourceNuGetOrg.ts | 56 +++ .../Tests/RestoreTests/selectSourceVsts.ts | 57 +++ .../Tests/RestoreTests/singleProject.ts | 66 ++++ .../RestoreTests/singleProjectConfigFile.ts | 52 +++ Tasks/DotNetCoreCLI/dotnetcore.ts | 32 ++ Tasks/DotNetCoreCLI/make.json | 17 + Tasks/DotNetCoreCLI/npm-shrinkwrap.json | 228 ----------- Tasks/DotNetCoreCLI/package.json | 8 +- Tasks/DotNetCoreCLI/packcommand.ts | 162 ++++++++ Tasks/DotNetCoreCLI/pushcommand.ts | 170 +++++++++ Tasks/DotNetCoreCLI/restorecommand.ts | 168 +++++++++ Tasks/DotNetCoreCLI/task.json | 356 +++++++++++++++++- Tasks/DotNetCoreCLI/task.loc.json | 354 ++++++++++++++++- Tasks/NuGetCommand/Common/Authentication.ts | 83 ---- .../Common/INuGetCommandOptions.ts | 2 +- .../Common/VstsNuGetPushToolRunner.ts | 3 +- .../Common/VstsNuGetPushToolUtilities.ts | 7 +- .../resources.resjson/en-US/resources.resjson | 6 +- Tasks/NuGetCommand/Tests/L0.ts | 8 +- .../Tests/NuGetConfigHelper-mock.ts | 2 +- Tasks/NuGetCommand/Tests/NugetMockHelper.ts | 21 +- .../Tests/PublishTests/internalFeedNuGet.ts | 2 +- .../PublishTests/internalFeedVstsNuGetPush.ts | 2 +- .../internalFeedVstsNuGetPushAllowConflict.ts | 2 +- ...ternalFeedVstsNuGetPushDisallowConflict.ts | 2 +- Tasks/NuGetCommand/nugetcustom.ts | 8 +- Tasks/NuGetCommand/nugetpack.ts | 2 +- Tasks/NuGetCommand/nugetpublisher.ts | 29 +- Tasks/NuGetCommand/nugetrestore.ts | 23 +- Tasks/NuGetCommand/task.json | 6 +- Tasks/NuGetCommand/task.loc.json | 4 +- Tasks/NuGetPublisher/task.loc.json | 2 +- 48 files changed, 2336 insertions(+), 491 deletions(-) rename Tasks/{NuGetCommand/Common/utilities.ts => Common/nuget-task-common/CommandHelper.ts} (74%) rename Tasks/{NuGetCommand/Common/NuGetConfigHelper.ts => Common/nuget-task-common/NuGetConfigHelper2.ts} (90%) rename Tasks/{NuGetCommand/Common/NuGetToolRunner.ts => Common/nuget-task-common/NuGetToolRunner2.ts} (89%) create mode 100644 Tasks/DotNetCoreCLI/Common/utility.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/DotnetMockHelper.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/NuGetConfigHelper-mock.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/PackTests/packBuildNumber.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/PackTests/packEnvVar.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/PackTests/packPrerelease.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/PushTests/internalFeed.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/RestoreTests/selectSourceNuGetOrg.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/RestoreTests/selectSourceVsts.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/RestoreTests/singleProject.ts create mode 100644 Tasks/DotNetCoreCLI/Tests/RestoreTests/singleProjectConfigFile.ts create mode 100644 Tasks/DotNetCoreCLI/make.json delete mode 100644 Tasks/DotNetCoreCLI/npm-shrinkwrap.json create mode 100644 Tasks/DotNetCoreCLI/packcommand.ts create mode 100644 Tasks/DotNetCoreCLI/pushcommand.ts create mode 100644 Tasks/DotNetCoreCLI/restorecommand.ts delete mode 100644 Tasks/NuGetCommand/Common/Authentication.ts diff --git a/Tasks/Common/nuget-task-common/Authentication.ts b/Tasks/Common/nuget-task-common/Authentication.ts index 5045f0488658..74c87c7dcb38 100644 --- a/Tasks/Common/nuget-task-common/Authentication.ts +++ b/Tasks/Common/nuget-task-common/Authentication.ts @@ -1,5 +1,11 @@ import * as tl from "vsts-task-lib/task"; +export interface IPackageSource { + feedName: string; + feedUri: string; + isInternal: boolean; +} + export class NuGetAuthInfo { constructor( public uriPrefixes: string[], @@ -7,6 +13,69 @@ export class NuGetAuthInfo { } } +export class NuGetExtendedAuthInfo { + constructor( + public internalAuthInfo: InternalAuthInfo, + public externalAuthInfo?: ExternalAuthInfo[]) { + } +} + +export class InternalAuthInfo +{ + constructor( + public uriPrefixes: string[], + public accessToken: string, + public useCredProvider: string, + public useCredConfig: boolean) { + } +} + +export class ExternalAuthInfo +{ + constructor( + public packageSource: IPackageSource, + public authType: ExternalAuthType) { + } +} + +export class TokenExternalAuthInfo extends ExternalAuthInfo +{ + constructor( + public packageSource: IPackageSource, + public token: string) + { + super(packageSource, ExternalAuthType.Token); + } +} + +export class UsernamePasswordExternalAuthInfo extends ExternalAuthInfo +{ + constructor( + public packageSource: IPackageSource, + public username: string, + public password: string) + { + super(packageSource, ExternalAuthType.UsernamePassword); + } +} + +export class ApiKeyExternalAuthInfo extends ExternalAuthInfo +{ + constructor( + public packageSource: IPackageSource, + public apiKey: string) + { + super(packageSource, ExternalAuthType.ApiKey); + } +} + +export enum ExternalAuthType +{ + Token, + UsernamePassword, + ApiKey +} + export function getSystemAccessToken(): string { tl.debug("Getting credentials for local feeds"); let auth = tl.getEndpointAuthorization("SYSTEMVSSCONNECTION", false); diff --git a/Tasks/NuGetCommand/Common/utilities.ts b/Tasks/Common/nuget-task-common/CommandHelper.ts similarity index 74% rename from Tasks/NuGetCommand/Common/utilities.ts rename to Tasks/Common/nuget-task-common/CommandHelper.ts index f9d9f035e617..560490ef1adc 100644 --- a/Tasks/NuGetCommand/Common/utilities.ts +++ b/Tasks/Common/nuget-task-common/CommandHelper.ts @@ -1,42 +1,13 @@ import * as path from "path"; import * as vsts from "vso-node-api/WebApi"; -import locationHelpers = require("nuget-task-common/LocationHelpers"); -import {VersionInfo} from "nuget-task-common/pe-parser/VersionResource"; +import locationHelpers = require("./LocationHelpers"); +import {VersionInfo} from "./pe-parser/VersionResource"; import * as tl from "vsts-task-lib/task"; import * as auth from "./Authentication"; import { IPackageSource } from "./Authentication"; import * as url from "url"; -export async function getNuGetFeedRegistryUrl(accessToken:string, feedId: string, nuGetVersion: VersionInfo): Promise -{ - const ApiVersion = "3.0-preview.1"; - let PackagingAreaName: string = "nuget"; - // If no version is received, V3 is assumed - let PackageAreaId: string = nuGetVersion && nuGetVersion.productVersion.a < 3 ? "5D6FC3B3-EF78-4342-9B6E-B3799C866CFA" : "9D3A4E8E-2F8F-4AE1-ABC2-B461A51CB3B3"; - - let credentialHandler = vsts.getBearerHandler(accessToken); - let collectionUrl = tl.getVariable("System.TeamFoundationCollectionUri"); - // The second element contains the transformed packaging URL - let packagingCollectionUrl = (await locationHelpers.assumeNuGetUriPrefixes(collectionUrl))[1]; - - if (!packagingCollectionUrl) - { - packagingCollectionUrl = collectionUrl; - } - const overwritePackagingCollectionUrl = tl.getVariable("NuGet.OverwritePackagingCollectionUrl"); - if (overwritePackagingCollectionUrl) { - tl.debug("Overwriting packaging collection URL"); - packagingCollectionUrl = overwritePackagingCollectionUrl; - } - - let vssConnection = new vsts.WebApi(packagingCollectionUrl, credentialHandler); - let coreApi = vssConnection.getCoreApi(); - - let data = await coreApi.vsoClient.getVersioningData(ApiVersion, PackagingAreaName, PackageAreaId, { feedId: feedId }); - - return data.requestUrl; -} export function GetExternalAuthInfoArray(inputKey: string): auth.ExternalAuthInfo[] { diff --git a/Tasks/NuGetCommand/Common/NuGetConfigHelper.ts b/Tasks/Common/nuget-task-common/NuGetConfigHelper2.ts similarity index 90% rename from Tasks/NuGetCommand/Common/NuGetConfigHelper.ts rename to Tasks/Common/nuget-task-common/NuGetConfigHelper2.ts index db6896e5816e..27bc3785eb11 100644 --- a/Tasks/NuGetCommand/Common/NuGetConfigHelper.ts +++ b/Tasks/Common/nuget-task-common/NuGetConfigHelper2.ts @@ -6,38 +6,41 @@ import * as tl from "vsts-task-lib/task"; import * as auth from "./Authentication"; import { IPackageSource } from "./Authentication"; -import * as ngToolRunner from "./NuGetToolRunner"; +import * as ngToolRunner from "./NuGetToolRunner2"; let xmlreader = require("xmlreader"); +// NuGetConfigHelper2 handles authenticated scenarios where the user selects a source from the UI or from a service connection. +// It is used by the NuGetCommand >= v2.0.0 and DotNetCoreCLI >= v2.0.0 -export class NuGetConfigHelper { - private tempNugetConfigBaseDir - = tl.getVariable("Agent.BuildDirectory") - || tl.getVariable("Agent.ReleaseDirectory") - || process.cwd(); - private tempNugetConfigDir = path.join(this.tempNugetConfigBaseDir, "Nuget"); - private tempNugetConfigFileName = "tempNuGet_" + tl.getVariable("build.buildId") + ".config"; +export class NuGetConfigHelper2 { public tempNugetConfigPath = undefined; constructor( private nugetPath: string, private nugetConfigPath: string, - private authInfo: auth.NuGetAuthInfo, - private environmentSettings: ngToolRunner.NuGetEnvironmentSettings) + private authInfo: auth.NuGetExtendedAuthInfo, + private environmentSettings: ngToolRunner.NuGetEnvironmentSettings, + private tempConfigPath: string /*optional*/) { + this.tempNugetConfigPath = tempConfigPath || this.getTempNuGetConfigPath(); } + public static getTempNuGetConfigBasePath() { + return tl.getVariable("Agent.BuildDirectory") + || tl.getVariable("Agent.ReleaseDirectory") + || process.cwd(); + } + public ensureTempConfigCreated() { // save nuget config file to agent build directory console.log(tl.loc("Info_SavingTempConfig")); - if (!tl.exist(this.tempNugetConfigDir)) { - tl.mkdirP(this.tempNugetConfigDir); + let tempNuGetConfigDir = path.dirname(this.tempNugetConfigPath); + if (!tl.exist(tempNuGetConfigDir)) { + tl.mkdirP(tempNuGetConfigDir); } - this.tempNugetConfigPath = path.join(this.tempNugetConfigDir, this.tempNugetConfigFileName); - if (!tl.exist(this.tempNugetConfigPath)) { if (this.nugetConfigPath) { @@ -62,7 +65,8 @@ export class NuGetConfigHelper { public async setAuthForSourcesInTempNuGetConfigAsync(): Promise { tl.debug('Setting auth in the temp nuget.config'); - + this.ensureTempConfigCreated(); + let sources = await this.getSourcesFromTempNuGetConfig(); if (sources.length < 1) { @@ -70,7 +74,6 @@ export class NuGetConfigHelper { return; } - this.ensureTempConfigCreated(); sources.forEach((source) => { if (source.isInternal) { @@ -121,6 +124,12 @@ export class NuGetConfigHelper { }); } + private getTempNuGetConfigPath(): string { + const tempNuGetConfigBaseDir = NuGetConfigHelper2.getTempNuGetConfigBasePath(); + const tempNuGetConfigFileName = "tempNuGet_" + tl.getVariable("build.buildId") + ".config"; + return path.join(tempNuGetConfigBaseDir, "Nuget", tempNuGetConfigFileName); + } + private getSourcesFromTempNuGetConfig(): Q.Promise { // load content of the user's nuget.config let configPath: string = this.tempNugetConfigPath ? this.tempNugetConfigPath : this.nugetConfigPath; diff --git a/Tasks/NuGetCommand/Common/NuGetToolRunner.ts b/Tasks/Common/nuget-task-common/NuGetToolRunner2.ts similarity index 89% rename from Tasks/NuGetCommand/Common/NuGetToolRunner.ts rename to Tasks/Common/nuget-task-common/NuGetToolRunner2.ts index 787aac91f894..012dc4c47345 100644 --- a/Tasks/NuGetCommand/Common/NuGetToolRunner.ts +++ b/Tasks/Common/nuget-task-common/NuGetToolRunner2.ts @@ -3,10 +3,14 @@ import * as tl from "vsts-task-lib/task"; import {IExecOptions, IExecSyncResult, ToolRunner} from "vsts-task-lib/toolrunner"; import * as auth from "./Authentication"; -import {NuGetQuirkName, NuGetQuirks, defaultQuirks} from "nuget-task-common/NuGetQuirks"; -import * as ngutil from "nuget-task-common/Utility"; -import * as util from "./utilities"; -import * as peParser from "nuget-task-common/pe-parser"; +import {NuGetQuirkName, NuGetQuirks, defaultQuirks} from "./NuGetQuirks"; +import * as ngutil from "./Utility"; +import * as peParser from "./pe-parser"; +import * as commandHelper from "./CommandHelper"; + +// NuGetToolRunner2 can handle environment setup for new authentication scenarios where +// we are accessing internal or external package sources. +// It is used by the NuGetCommand >= v2.0.0 and DotNetCoreCLI >= v2.0.0 interface EnvironmentDictionary { [key: string]: string; } @@ -18,7 +22,7 @@ export interface NuGetEnvironmentSettings { function prepareNuGetExeEnvironment( input: EnvironmentDictionary, settings: NuGetEnvironmentSettings, - authInfo: auth.NuGetAuthInfo): EnvironmentDictionary { + authInfo: auth.NuGetExtendedAuthInfo): EnvironmentDictionary { let env: EnvironmentDictionary = {}; let originalCredProviderPath: string; @@ -73,11 +77,11 @@ function prepareNuGetExeEnvironment( return env; } -export class NuGetToolRunner extends ToolRunner { +export class NuGetToolRunner2 extends ToolRunner { private settings: NuGetEnvironmentSettings; - private authInfo: auth.NuGetAuthInfo; + private authInfo: auth.NuGetExtendedAuthInfo; - constructor(nuGetExePath: string, settings: NuGetEnvironmentSettings, authInfo: auth.NuGetAuthInfo) { + constructor(nuGetExePath: string, settings: NuGetEnvironmentSettings, authInfo: auth.NuGetExtendedAuthInfo) { if (tl.osType() === 'Windows_NT' || !nuGetExePath.trim().toLowerCase().endsWith(".exe")) { super(nuGetExePath); } @@ -104,8 +108,8 @@ export class NuGetToolRunner extends ToolRunner { } } -export function createNuGetToolRunner(nuGetExePath: string, settings: NuGetEnvironmentSettings, authInfo: auth.NuGetAuthInfo): NuGetToolRunner { - let runner = new NuGetToolRunner(nuGetExePath, settings, authInfo); +export function createNuGetToolRunner(nuGetExePath: string, settings: NuGetEnvironmentSettings, authInfo: auth.NuGetExtendedAuthInfo): NuGetToolRunner2 { + let runner = new NuGetToolRunner2(nuGetExePath, settings, authInfo); runner.on("debug", message => tl.debug(message)); return runner; } @@ -167,7 +171,7 @@ export function isCredentialProviderEnabled(quirks: NuGetQuirks): boolean { return false; } - if (util.isOnPremisesTfs() && ( + if (commandHelper.isOnPremisesTfs() && ( quirks.hasQuirk(NuGetQuirkName.NoTfsOnPremAuthCredentialProvider))) { tl.debug("Credential provider is disabled due to on-prem quirks."); return false; @@ -192,7 +196,7 @@ export function isCredentialConfigEnabled(quirks: NuGetQuirks): boolean { return false; } - if (util.isOnPremisesTfs() && ( + if (commandHelper.isOnPremisesTfs() && ( quirks.hasQuirk(NuGetQuirkName.NoTfsOnPremAuthConfig))) { tl.debug("Credential config is disabled due to on-prem quirks."); return false; diff --git a/Tasks/Common/nuget-task-common/Strings/resources.resjson/en-US/resources.resjson b/Tasks/Common/nuget-task-common/Strings/resources.resjson/en-US/resources.resjson index 1886e3e6307d..a5e1e8b0bac9 100644 --- a/Tasks/Common/nuget-task-common/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/Common/nuget-task-common/Strings/resources.resjson/en-US/resources.resjson @@ -3,7 +3,9 @@ "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", "loc.messages.Info_AvailableVersions": "The available versions are: %s", + "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", + "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", "loc.messages.Info_UsingVersion": "Using version: %s", "loc.messages.Info_UsingToolPath": "Using tool path: %s", "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", diff --git a/Tasks/Common/nuget-task-common/Utility.ts b/Tasks/Common/nuget-task-common/Utility.ts index e1aa05f01f8c..089d39af6e74 100644 --- a/Tasks/Common/nuget-task-common/Utility.ts +++ b/Tasks/Common/nuget-task-common/Utility.ts @@ -1,8 +1,10 @@ import * as path from "path"; - import * as tl from "vsts-task-lib/task"; - import * as ngToolRunner from "./NuGetToolRunner"; +import * as vsts from "vso-node-api/WebApi"; +import {VersionInfo} from "./pe-parser/VersionResource"; +import locationHelpers = require("./LocationHelpers"); +import * as url from "url"; // Attempts to resolve paths the same way the legacy PowerShell's Find-Files worked export function resolveFilterSpec(filterSpec: string, basePath?: string, allowEmptyMatch?: boolean, includeFolders?: boolean): string[] { @@ -184,4 +186,35 @@ export function setConsoleCodePage() { if (tl.osType() === 'Windows_NT') { tl.execSync(path.resolve(process.env.windir, "system32", "chcp.com"), ["65001"]); } -} \ No newline at end of file +} + +export async function getNuGetFeedRegistryUrl(accessToken:string, feedId: string, nuGetVersion: VersionInfo): Promise +{ + const ApiVersion = "3.0-preview.1"; + let PackagingAreaName: string = "nuget"; + // If no version is received, V3 is assumed + let PackageAreaId: string = nuGetVersion && nuGetVersion.productVersion.a < 3 ? "5D6FC3B3-EF78-4342-9B6E-B3799C866CFA" : "9D3A4E8E-2F8F-4AE1-ABC2-B461A51CB3B3"; + + let credentialHandler = vsts.getBearerHandler(accessToken); + let collectionUrl = tl.getVariable("System.TeamFoundationCollectionUri"); + // The second element contains the transformed packaging URL + let packagingCollectionUrl = (await locationHelpers.assumeNuGetUriPrefixes(collectionUrl))[1]; + + if (!packagingCollectionUrl) + { + packagingCollectionUrl = collectionUrl; + } + + const overwritePackagingCollectionUrl = tl.getVariable("NuGet.OverwritePackagingCollectionUrl"); + if (overwritePackagingCollectionUrl) { + tl.debug("Overwriting packaging collection URL"); + packagingCollectionUrl = overwritePackagingCollectionUrl; + } + + let vssConnection = new vsts.WebApi(packagingCollectionUrl, credentialHandler); + let coreApi = vssConnection.getCoreApi(); + + let data = await coreApi.vsoClient.getVersioningData(ApiVersion, PackagingAreaName, PackageAreaId, { feedId: feedId }); + + return data.requestUrl; +} diff --git a/Tasks/Common/nuget-task-common/module.json b/Tasks/Common/nuget-task-common/module.json index 5c78b1270d11..93a9bbcf6cbe 100644 --- a/Tasks/Common/nuget-task-common/module.json +++ b/Tasks/Common/nuget-task-common/module.json @@ -4,7 +4,9 @@ "NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", "NGCommon_UnableToFindTool": "Unable to find tool %s", "Info_AvailableVersions": "The available versions are: %s", + "Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", "Info_ResolvedToolFromCache": "Resolved from tool cache: %s", + "Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", "Info_UsingVersion": "Using version: %s", "Info_UsingToolPath": "Using tool path: %s", "Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", diff --git a/Tasks/DotNetCoreCLI/Common/utility.ts b/Tasks/DotNetCoreCLI/Common/utility.ts new file mode 100644 index 000000000000..501ecf3e6e89 --- /dev/null +++ b/Tasks/DotNetCoreCLI/Common/utility.ts @@ -0,0 +1,22 @@ +import * as tl from "vsts-task-lib/task"; +import nuGetGetter = require("nuget-task-common/NuGetToolGetter"); + +export function getProjectFiles(projectPattern: string[]): string[] { + var projectFiles = tl.findMatch(tl.getVariable("System.DefaultWorkingDirectory") || process.cwd(), projectPattern); + if (!projectFiles || !projectFiles.length) { + tl.warning(tl.loc("noProjectFilesFound")); + return []; + } + + return projectFiles; +} + +export async function getNuGetPath(): Promise { + tl.debug('Getting NuGet'); + return process.env[nuGetGetter.NUGET_EXE_TOOL_PATH_ENV_VAR] || await nuGetGetter.getNuGet("4.0.0"); +} + +export function getUtcDateString(): string { + let now: Date = new Date(); + return `${now.getFullYear()}${now.getUTCMonth()}${now.getUTCDate()}-${now.getUTCHours()}${now.getUTCMinutes()}${now.getUTCSeconds()}`; +} diff --git a/Tasks/DotNetCoreCLI/Strings/resources.resjson/en-US/resources.resjson b/Tasks/DotNetCoreCLI/Strings/resources.resjson/en-US/resources.resjson index 3d66b0070997..3c15f6be37b1 100644 --- a/Tasks/DotNetCoreCLI/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/DotNetCoreCLI/Strings/resources.resjson/en-US/resources.resjson @@ -3,19 +3,93 @@ "loc.helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?linkid=832194)", "loc.description": "Build, test and publish using dotnet core command-line.", "loc.instanceNameFormat": "dotnet $(command)", + "loc.group.displayName.restoreAuth": "Feeds and authentication", + "loc.group.displayName.restoreAdvanced": "Advanced", + "loc.group.displayName.pushAuth": "Destination feed and authentication", + "loc.group.displayName.pushAdvanced": "Advanced", + "loc.group.displayName.packOptions": "Pack options", + "loc.group.displayName.packAdvanced": "Advanced", "loc.input.label.command": "Command", - "loc.input.help.command": "Select or type a dotnet command", + "loc.input.help.command": "Select a dotnet command. Select 'Custom' to enter any dotnet command with arguments.", "loc.input.label.publishWebProjects": "Publish Web Projects", "loc.input.help.publishWebProjects": "If true, the task will try to find the web projects in the repository and run the publish command on them. Web projects are identified by presence of either a web.config file or wwwroot folder in the directory.", "loc.input.label.projects": "Project(s)", "loc.input.help.projects": "Relative path of the .csproj file(s) from repo root. Wildcards can be used. For example, **/*.csproj for all .csproj files in all the sub folders.", + "loc.input.label.custom": "Custom command", + "loc.input.help.custom": "The command to pass to dotnet.exe for execution.", "loc.input.label.arguments": "Arguments", "loc.input.help.arguments": "Arguments to the selected command. For example, build configuration, output folder, rutime. The arguments depend on the command selected.", "loc.input.label.zipAfterPublish": "Zip Published Projects", "loc.input.help.zipAfterPublish": "If true, folder created by the publish command will be zipped.", + "loc.input.label.selectOrConfig": "Feeds to use", + "loc.input.help.selectOrConfig": "You can either select a feed from VSTS and/or NuGet.org here, or commit a nuget.config file to your source code repository and set its path here.", + "loc.input.label.feedRestore": "Use packages from this VSTS/TFS feed", + "loc.input.help.feedRestore": "Include the selected feed in the generated NuGet.config. You must have Package Management installed and licensed to select a feed here.", + "loc.input.label.includeNuGetOrg": "Use packages from NuGet.org", + "loc.input.help.includeNuGetOrg": "Include NuGet.org in the generated NuGet.config.", + "loc.input.label.nugetConfigPath": "Path to NuGet.config", + "loc.input.help.nugetConfigPath": "The NuGet.config in your repository that specifies the feeds from which to restore packages.", + "loc.input.label.externalEndpoints": "Credentials for feeds outside this account/collection", + "loc.input.help.externalEndpoints": "Credentials to use for external registries located in the selected NuGet.config. For feeds in this account/collection, leave this blank; the build’s credentials are used automatically.", + "loc.input.label.noCache": "Disable local cache", + "loc.input.help.noCache": "Prevents NuGet from using packages from local machine caches.", + "loc.input.label.packagesDirectory": "Destination directory", + "loc.input.help.packagesDirectory": "Specifies the folder in which packages are installed. If no folder is specified, packages are restored into a packages/ folder alongside the selected solution, packages.config, or project.json.", + "loc.input.label.verbosityRestore": "Verbosity", + "loc.input.help.verbosityRestore": "Specifies the amount of detail displayed in the output.", + "loc.input.label.searchPatternPush": "Path to NuGet package(s) to publish", + "loc.input.help.searchPatternPush": "The pattern to match or path to nupkg files to be uploaded. Multiple patterns can be separated by a semicolon.", + "loc.input.label.nuGetFeedType": "Target feed location", + "loc.input.label.feedPublish": "Target feed", + "loc.input.help.feedPublish": "Select a feed hosted in this account. You must have Package Management installed and licensed to select a feed here.", + "loc.input.label.externalEndpoint": "NuGet server", + "loc.input.help.externalEndpoint": "The NuGet service endpoint that contains the external NuGet server’s credentials.", + "loc.input.label.searchPatternPack": "Path to csproj or nuspec file(s) to pack", + "loc.input.help.searchPatternPack": "Pattern to search for csproj or nuspec files to pack.\n\nYou can separate multiple patterns with a semicolon, and you can make a pattern negative by prefixing it with '-:'. Example: `**\\*.csproj;-:**\\*.Tests.csproj`", + "loc.input.label.configurationToPack": "Configuration to Package", + "loc.input.help.configurationToPack": "When using a csproj file this specifies the configuration to package", + "loc.input.label.outputDir": "Package Folder", + "loc.input.help.outputDir": "Folder where packages will be created. If empty, packages will be created alongside the csproj file.", + "loc.input.label.nobuild": "Do not build", + "loc.input.help.nobuild": "Don't build the project before packing. Corresponds to the --no-build command line parameter.", + "loc.input.label.versioningScheme": "Automatic package versioning", + "loc.input.help.versioningScheme": "Cannot be used with include referenced projects. If you choose 'Use the date and time', this will generate a [SemVer](http://semver.org/spec/v1.0.0.html)-compliant version formatted as `X.Y.Z-ci-datetime` where you choose X, Y, and Z.\n\nIf you choose 'Use an environment variable', you must select an environment variable and ensure it contains the version number you want to use.\n\nIf you choose 'Use the build number', this will use the build number to version your package. **Note:** Under Options set the build number format to be '[$(BuildDefinitionName)_$(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)](https://go.microsoft.com/fwlink/?LinkID=627416)'.", + "loc.input.label.versionEnvVar": "Environment variable", + "loc.input.help.versionEnvVar": "Enter the variable name without $, $env, or %.", + "loc.input.label.requestedMajorVersion": "Major", + "loc.input.help.requestedMajorVersion": "The 'X' in version [X.Y.Z](http://semver.org/spec/v1.0.0.html)", + "loc.input.label.requestedMinorVersion": "Minor", + "loc.input.help.requestedMinorVersion": "The 'Y' in version [X.Y.Z](http://semver.org/spec/v1.0.0.html)", + "loc.input.label.requestedPatchVersion": "Patch", + "loc.input.help.requestedPatchVersion": "The 'Z' in version [X.Y.Z](http://semver.org/spec/v1.0.0.html)", + "loc.input.label.buildProperties": "Additional build properties", + "loc.input.help.buildProperties": "Specifies a list of token=value pairs, separated by semicolons, where each occurrence of $token$ in the .nuspec file will be replaced with the given value. Values can be strings in quotation marks.", + "loc.input.label.verbosityPack": "Verbosity", + "loc.input.help.verbosityPack": "Specifies the amount of detail displayed in the output.", + "loc.messages.BuildIdentityPermissionsHint": "For internal feeds, make sure the build service identity '%s' [%s] has access to the feed.", + "loc.messages.Error_AutomaticallyVersionReleases": "Autoversion: Getting version number from build option is not supported in releases", + "loc.messages.Error_CommandNotRecognized": "The command %s was not recognized.", + "loc.messages.Error_NoSourceSpecifiedForPush": "No source was specified for push", + "loc.messages.Error_NoValueFoundForEnvVar": "No value was found for the provided environment variable.", + "loc.messages.Error_NoVersionFoundInBuildNumber": "Could not find version number data in the following environment variable: BUILD_BUILDNUMBER. The value of the variable should contain a substring with the following formats: X.Y.Z or X.Y.Z.A where A, X, Y, and Z are positive integers.", + "loc.messages.Error_PackageFailure": "An error ocurred while trying to pack the files.", + "loc.messages.Error_PushNotARegularFile": "%s is not a file. Check the 'Path/Pattern to nupkg' property of the task.", + "loc.messages.Info_AttemptingToPackFile": "Attempting to pack file: ", + "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", + "loc.messages.Info_NoPackagesMatchedTheSearchPattern": "No packages matched the search pattern.", + "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", + "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", + "loc.messages.Info_UsingVersion": "Using version: %s", + "loc.messages.PackagesFailedToInstall": "Packages failed to restore", + "loc.messages.PackagesFailedToPublish": "Packages failed to publish", + "loc.messages.PackagesInstalledSuccessfully": "Packages were restored successfully", + "loc.messages.PackagesPublishedSuccessfully": "Packages were published successfully", + "loc.messages.UnknownFeedType": "Unknown feed type '%s'", + "loc.messages.Warning_AutomaticallyVersionReferencedProjects": "The automatic package versioning and include referenced projects options do not work together. Referenced projects will not inherit the custom version provided by the automatic versioning settings.", + "loc.messages.Warning_MoreThanOneVersionInBuildNumber": "Found more than one instance of version data in BUILD_BUILDNUMBER.Assuming first instance is version.", + "loc.messages.dotnetCommandFailed": "Dotnet command failed with non-zero exit code on the following projects : %s", "loc.messages.noProjectFilesFound": "Project file(s) matching the specified pattern were not found.", + "loc.messages.noPublishFolderFoundToZip": "A publish folder could not be found to zip for project file: %s.", "loc.messages.noWebProjctFound": "No web project was found in the repository. Web projects are identified by presence of either a web.config file or wwwroot folder in the directory.", - "loc.messages.dotnetCommandFailed": "Dotnet command failed with non-zero exit code on the following projects : %s", - "loc.messages.zipFailed": "Zip failed with error: %s", - "loc.messages.noPublishFolderFoundToZip": "A publish folder could not be found to zip for project file: %s." + "loc.messages.zipFailed": "Zip failed with error: %s" } \ No newline at end of file diff --git a/Tasks/DotNetCoreCLI/Tests/DotnetMockHelper.ts b/Tasks/DotNetCoreCLI/Tests/DotnetMockHelper.ts new file mode 100644 index 000000000000..885d9b062105 --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/DotnetMockHelper.ts @@ -0,0 +1,138 @@ +import tmrm = require('vsts-task-lib/mock-run'); +import VersionInfoVersion from 'nuget-task-common/pe-parser/VersionInfoVersion' +import {VersionInfo, VersionStrings} from 'nuget-task-common/pe-parser/VersionResource' +import * as auth from 'nuget-task-common/Authentication' + +export class DotnetMockHelper { + private defaultNugetVersion = '4.0.0'; + private defaultNugetVersionInfo = [4,0,0,0]; + + constructor( + private tmr: tmrm.TaskMockRunner) { + process.env['AGENT_HOMEDIRECTORY'] = "c:\\agent\\home\\directory"; + process.env['BUILD_SOURCESDIRECTORY'] = "c:\\agent\\home\\directory\\sources", + process.env['ENDPOINT_AUTH_SYSTEMVSSCONNECTION'] = "{\"parameters\":{\"AccessToken\":\"token\"},\"scheme\":\"OAuth\"}"; + process.env['ENDPOINT_URL_SYSTEMVSSCONNECTION'] = "https://example.visualstudio.com/defaultcollection"; + process.env['SYSTEM_DEFAULTWORKINGDIRECTORY'] = "c:\\agent\\home\\directory"; + process.env['SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'] = "https://example.visualstudio.com/defaultcollection"; + process.env['BUILD_BUILDID'] = "1"; + } + + public setNugetVersionInputDefault() { + } + + public registerDefaultNugetVersionMock() { + this.registerNugetVersionMock(this.defaultNugetVersion, this.defaultNugetVersionInfo); + this.registerNugetToolGetterMock(); + } + + public registerNugetToolGetterMock() { + this.tmr.registerMock('nuget-task-common/NuGetToolGetter', { + getNuGet: function(versionSpec) { + return "c:\\from\\tool\\installer\\nuget.exe"; + }, + } ) + } + + public registerNugetVersionMock(productVersion: string, versionInfoVersion: number[]) { + this.registerNugetVersionMockInternal(productVersion, versionInfoVersion); + this.registerMockWithMultiplePaths(['nuget-task-common/pe-parser', './pe-parser'], { + getFileVersionInfoAsync: function(nuGetExePath) { + let result: VersionInfo = { strings: {} }; + result.fileVersion = new VersionInfoVersion(versionInfoVersion[0], versionInfoVersion[1], versionInfoVersion[2], versionInfoVersion[3]); + result.strings['ProductVersion'] = productVersion; + return result; + } + }) + } + + private registerNugetVersionMockInternal(productVersion: string, versionInfoVersion: number[]) { + this.registerMockWithMultiplePaths(['nuget-task-common/pe-parser/index', './pe-parser/index'], { + getFileVersionInfoAsync: function(nuGetExePath) { + let result: VersionInfo = { strings: {} }; + result.fileVersion = new VersionInfoVersion(versionInfoVersion[0], versionInfoVersion[1], versionInfoVersion[2], versionInfoVersion[3]); + result.productVersion = new VersionInfoVersion(versionInfoVersion[0], versionInfoVersion[1], versionInfoVersion[2], versionInfoVersion[3]); + result.strings['ProductVersion'] = productVersion; + return result; + } + }) + } + + public registerNugetUtilityMock(projectFile: string[]) { + this.tmr.registerMock('nuget-task-common/Utility', { + resolveFilterSpec: function(filterSpec, basePath?, allowEmptyMatch?) { + return projectFile; + }, + getBundledNuGetLocation: function(version) { + return 'c:\\agent\\home\\directory\\externals\\nuget\\nuget.exe'; + }, + stripLeadingAndTrailingQuotes: function(path) { + return path; + }, + getNuGetFeedRegistryUrl(accessToken, feedId, nuGetVersion) { + return 'https://vsts/packagesource'; + } + }); + } + + public registerVstsNuGetPushRunnerMock() { + this.tmr.registerMock('./Common/VstsNuGetPushToolUtilities', { + getBundledVstsNuGetPushLocation: function() { + return 'c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe'; + } + }); + } + + public registerNuGetPackUtilsMock() { + this.tmr.registerMock('./Common/utility', { + getUtcDateString: function() { + return 'YYYYMMDD-HHMMSS'; + } + }); + } + + public registerNugetConfigMock() { + var nchm = require('./NuGetConfigHelper-mock'); + this.tmr.registerMock('nuget-task-common/NuGetConfigHelper2', nchm); + } + + public registerToolRunnerMock() { + var mtt = require('vsts-task-lib/mock-toolrunner'); + this.tmr.registerMock('vsts-task-lib/toolrunner', mtt); + } + + public RegisterLocationServiceMocks() { + this.tmr.registerMock('vso-node-api/WebApi', { + getBearerHandler: function(token){ + return {}; + }, + WebApi: function(url, handler){ + return { + getCoreApi: function() { + return { + vsoClient: { + getVersioningData: function (ApiVersion, PackagingAreaName, PackageAreaId, Obj) { + return { requestUrl:"foobar" } + } + } + }; + } + }; + } + }) + } + + public setAnswers(a) { + a.osType["osType"] = "Windows_NT"; + a.exist["c:\\agent\\home\\directory\\externals\\nuget\\nuget.exe"] = true; + a.exist["c:\\from\\tool\\installer\\nuget.exe"] = true; + a.exist["c:\\agent\\home\\directory\\externals\\nuget\\CredentialProvider\\CredentialProvider.TeamBuild.exe"] = true; + this.tmr.setAnswers(a); + } + + private registerMockWithMultiplePaths(paths: string[], mock: any) { + for(let i = 0; i < paths.length; i++) { + this.tmr.registerMock(paths[i], mock); + } + } +} \ No newline at end of file diff --git a/Tasks/DotNetCoreCLI/Tests/L0.ts b/Tasks/DotNetCoreCLI/Tests/L0.ts index e2ec27f94c3b..103b35762acb 100644 --- a/Tasks/DotNetCoreCLI/Tests/L0.ts +++ b/Tasks/DotNetCoreCLI/Tests/L0.ts @@ -20,43 +20,120 @@ describe('DotNetCoreExe Suite', function () { assert(tr.failed, 'task should have failed'); assert(tr.errorIssues.length > 0, "error reason should have been recorded"); done(); - }), + }); - it('restore works with explicit project files', (done: MochaDone) => { + it('restore single solution', (done: MochaDone) => { + this.timeout(1000); - process.env["__projects__"] = '**/project.json'; - let tp = path.join(__dirname, 'validInputs.js') - let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); - tr.run(); + let tp = path.join(__dirname, './RestoreTests/singleProject.js') + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); - assert(tr.invokedToolCount == 4, 'should have invoked tool.'); - assert(tr.succeeded, 'task should have succeeded'); - done(); - }), + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + done(); + }); - it('restore works with no project file is specified', (done: MochaDone) => { + it('restore nocache', (done: MochaDone) => { + this.timeout(1000); - process.env["__projects__"] = ""; - let tp = path.join(__dirname, 'validInputs.js') - let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); - tr.run(); + let tp = path.join(__dirname, './RestoreTests/singleProject.js') + process.env["__nocache__"] = "true"; + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); - assert(tr.invokedToolCount == 1, 'should have invoked tool once'); - assert(tr.succeeded, 'task should have succeeded'); - done(); - }), + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config --no-cache'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output, no-cache'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + process.env["__nocache__"] = undefined; + done(); + }); - it('restore fails with when the dotnet restore fails', (done: MochaDone) => { + it('restore verbosity Detailed', (done: MochaDone) => { + this.timeout(1000); - process.env["__projects__"] = "dummy/project.json"; - let tp = path.join(__dirname, 'validInputs.js') - let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); - tr.run(); + let tp = path.join(__dirname, './RestoreTests/singleProject.js') + process.env["__verbosity__"] = "Detailed"; + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); - assert(tr.invokedToolCount == 1, 'should have invoked tool once'); - assert(tr.failed, 'task should have failed'); - done(); - }) + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config --verbosity Detailed'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output, verbosity Detailed'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + process.env["__verbosity__"] = undefined; + done(); + }); + + it('restore verbosity - omits switch', (done: MochaDone) => { + this.timeout(1000); + + let tp = path.join(__dirname, './RestoreTests/singleProject.js') + process.env["__verbosity__"] = "-"; + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + process.env["__verbosity__"] = undefined; + done(); + }); + + it('restore with config file', (done: MochaDone) => { + this.timeout(1000); + + let tp = path.join(__dirname, './RestoreTests/singleProjectConfigFile.js') + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + done(); + }); + + it('restore with vsts feed', (done: MochaDone) => { + this.timeout(1000); + + let tp = path.join(__dirname, './RestoreTests/selectSourceVsts.js') + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config'), 'it should have run dotnet'); + assert(tr.stdOutContained('adding package source uri: https://vsts/packagesource'), 'it should have added vsts source to config'); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + done(); + }); + + it('restore select nuget.org source', (done: MochaDone) => { + this.timeout(1000); + + let tp = path.join(__dirname, './RestoreTests/selectSourceNuGetOrg.js') + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config'), 'it should have run dotnet'); + assert(tr.stdOutContained('adding package source uri: https://api.nuget.org/v3/index.json'), "should have added nuget.org source to config"); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + done(); + }); it('publish works with explicit project files', (done: MochaDone) => { @@ -151,5 +228,65 @@ describe('DotNetCoreExe Suite', function () { assert(tr.invokedToolCount == 1, 'should have invoked tool two times'); assert(tr.succeeded, 'task should have succeeded'); done(); - }) + }); + + it('packs with prerelease', (done: MochaDone) => { + this.timeout(1000); + + let tp = path.join(__dirname, './PackTests/packPrerelease.js') + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe pack c:\\agent\\home\\directory\\foo.nuspec --output C:\\out\\dir /p:PackageVersion=x.y.z-CI-YYYYMMDD-HHMMSS'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + done(); + }); + + it('packs with env var', (done: MochaDone) => { + this.timeout(1000); + + let tp = path.join(__dirname, './PackTests/packEnvVar.js') + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe pack c:\\agent\\home\\directory\\foo.nuspec --output C:\\out\\dir /p:PackageVersion=XX.YY.ZZ'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + done(); + }); + + it('packs with build number', (done: MochaDone) => { + this.timeout(1000); + + let tp = path.join(__dirname, './PackTests/packBuildNumber.js') + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe pack c:\\agent\\home\\directory\\single.csproj --output C:\\out\\dir /p:PackageVersion=1.2.3'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + done(); + }); + + it('pushes successfully to internal feed using NuGet.exe', (done: MochaDone) => { + this.timeout(1000); + + let tp = path.join(__dirname, './PushTests/internalFeed.js') + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run() + assert(tr.invokedToolCount == 1, 'should have run dotnet once'); + assert(tr.ran('c:\\path\\dotnet.exe nuget push c:\\agent\\home\\directory\\foo.nupkg --source https://vsts/packagesource --api-key VSTS'), 'it should have run dotnet'); + assert(tr.stdOutContained('dotnet output'), "should have dotnet output"); + assert(tr.succeeded, 'should have succeeded'); + assert.equal(tr.errorIssues.length, 0, "should have no errors"); + done(); + }); }); \ No newline at end of file diff --git a/Tasks/DotNetCoreCLI/Tests/NuGetConfigHelper-mock.ts b/Tasks/DotNetCoreCLI/Tests/NuGetConfigHelper-mock.ts new file mode 100644 index 000000000000..6ec52eb8b92a --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/NuGetConfigHelper-mock.ts @@ -0,0 +1,19 @@ +import * as tl from "vsts-task-lib/task"; + +export class NuGetConfigHelper2 { + tempNugetConfigPath = NuGetConfigHelper2.getTempNuGetConfigBasePath() + "\\NuGet\\tempNuGet_.config"; + + setAuthForSourcesInTempNuGetConfigAsync() { + tl.debug("setting up auth for the sources configured in the helper"); + } + + addSourcesToTempNuGetConfig(packageSources) { + packageSources.forEach((source) => { + tl.debug(`adding package source uri: ${source.feedUri}`); + }); + } + + static getTempNuGetConfigBasePath() { + return tl.getVariable("Agent.HomeDirectory"); + } +} \ No newline at end of file diff --git a/Tasks/DotNetCoreCLI/Tests/PackTests/packBuildNumber.ts b/Tasks/DotNetCoreCLI/Tests/PackTests/packBuildNumber.ts new file mode 100644 index 000000000000..e71ae0547f7a --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/PackTests/packBuildNumber.ts @@ -0,0 +1,53 @@ +import ma = require('vsts-task-lib/mock-answer'); +import tmrm = require('vsts-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', 'pack'); +tmr.setInput('searchPatternPack', 'single.csproj'); +tmr.setInput('outputDir', 'C:\\out\\dir'); +tmr.setInput('versioningScheme', 'byBuildNumber'); + +let a: ma.TaskLibAnswers = { + "osType": {}, + "checkPath": { + "c:\\agent\\home\\directory\\single.csproj": true, + "c:\\path\\dotnet.exe": true + }, + "which": { + "dotnet": "c:\\path\\dotnet.exe" + }, + "exec": { + "c:\\path\\dotnet.exe pack c:\\agent\\home\\directory\\single.csproj --output C:\\out\\dir /p:PackageVersion=1.2.3": { + "code": 0, + "stdout": "dotnet output", + "stderr": "" + } + }, + "exist": { + "C:\\out\\dir": true + }, + "stats": { + "c:\\agent\\home\\directory\\single.csproj": { + "isFile": true + } + }, + "findMatch": { + "single.csproj": ["c:\\agent\\home\\directory\\single.csproj"] + } +}; +nmh.setAnswers(a); + +process.env['BUILD_BUILDNUMBER'] = '1.2.3' +nmh.registerNugetUtilityMock(["c:\\agent\\home\\directory\\single.csproj"]); +nmh.registerDefaultNugetVersionMock(); +nmh.registerToolRunnerMock(); +nmh.registerNugetConfigMock(); +nmh.registerNuGetPackUtilsMock(); + +tmr.run(); diff --git a/Tasks/DotNetCoreCLI/Tests/PackTests/packEnvVar.ts b/Tasks/DotNetCoreCLI/Tests/PackTests/packEnvVar.ts new file mode 100644 index 000000000000..4d2ce7cacec2 --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/PackTests/packEnvVar.ts @@ -0,0 +1,54 @@ +import ma = require('vsts-task-lib/mock-answer'); +import tmrm = require('vsts-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', 'pack'); +tmr.setInput('searchPatternPack', 'foo.nuspec'); +tmr.setInput('outputDir', 'C:\\out\\dir'); +tmr.setInput('versioningScheme', 'byEnvVar'); +tmr.setInput('versionEnvVar', 'foobarVersionEnvVar'); + +let a: ma.TaskLibAnswers = { + "osType": {}, + "checkPath": { + "c:\\agent\\home\\directory\\foo.nuspec": true, + "c:\\path\\dotnet.exe": true + }, + "which": { + "dotnet": "c:\\path\\dotnet.exe" + }, + "exec": { + "c:\\path\\dotnet.exe pack c:\\agent\\home\\directory\\foo.nuspec --output C:\\out\\dir /p:PackageVersion=XX.YY.ZZ": { + "code": 0, + "stdout": "dotnet output", + "stderr": "" + } + }, + "exist": { + "C:\\out\\dir": true + }, + "stats": { + "c:\\agent\\home\\directory\\foo.nuspec": { + "isFile": true + } + }, + "findMatch": { + "foo.nuspec" : ["c:\\agent\\home\\directory\\foo.nuspec"] + } +}; +nmh.setAnswers(a); + +process.env['foobarVersionEnvVar'] = 'XX.YY.ZZ' +nmh.registerNugetUtilityMock(["c:\\agent\\home\\directory\\foo.nuspec"]); +nmh.registerDefaultNugetVersionMock(); +nmh.registerToolRunnerMock(); +nmh.registerNugetConfigMock(); +nmh.registerNuGetPackUtilsMock(); + +tmr.run(); diff --git a/Tasks/DotNetCoreCLI/Tests/PackTests/packPrerelease.ts b/Tasks/DotNetCoreCLI/Tests/PackTests/packPrerelease.ts new file mode 100644 index 000000000000..fb140294214c --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/PackTests/packPrerelease.ts @@ -0,0 +1,55 @@ +import ma = require('vsts-task-lib/mock-answer'); +import tmrm = require('vsts-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', 'pack'); +tmr.setInput('searchPatternPack', 'foo.nuspec'); +tmr.setInput('outputDir', 'C:\\out\\dir'); +tmr.setInput('versioningScheme', 'byPrereleaseNumber'); +tmr.setInput('requestedMajorVersion', 'x'); +tmr.setInput('requestedMinorVersion', 'y'); +tmr.setInput('requestedPatchVersion', 'z'); + +let a: ma.TaskLibAnswers = { + "osType": {}, + "checkPath": { + "c:\\agent\\home\\directory\\foo.nuspec": true, + "c:\\path\\dotnet.exe": true + }, + "which": { + "dotnet": "c:\\path\\dotnet.exe" + }, + "exec": { + "c:\\path\\dotnet.exe pack c:\\agent\\home\\directory\\foo.nuspec --output C:\\out\\dir /p:PackageVersion=x.y.z-CI-YYYYMMDD-HHMMSS": { + "code": 0, + "stdout": "dotnet output", + "stderr": "" + } + }, + "exist": { + "C:\\out\\dir": true + }, + "stats": { + "c:\\agent\\home\\directory\\foo.nuspec": { + "isFile": true + } + }, + "findMatch": { + "foo.nuspec" : ["c:\\agent\\home\\directory\\foo.nuspec"] + } +}; +nmh.setAnswers(a); + +nmh.registerNugetUtilityMock(["c:\\agent\\home\\directory\\foo.nuspec"]); +nmh.registerDefaultNugetVersionMock(); +nmh.registerToolRunnerMock(); +nmh.registerNugetConfigMock(); +nmh.registerNuGetPackUtilsMock(); + +tmr.run(); diff --git a/Tasks/DotNetCoreCLI/Tests/PushTests/internalFeed.ts b/Tasks/DotNetCoreCLI/Tests/PushTests/internalFeed.ts new file mode 100644 index 000000000000..b5563d6efd7c --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/PushTests/internalFeed.ts @@ -0,0 +1,55 @@ +import ma = require('vsts-task-lib/mock-answer'); +import tmrm = require('vsts-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', 'FeedFooId'); + +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": { + "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": "" + } + }, + "exist": {}, + "stats": { + "c:\\agent\\home\\directory\\foo.nupkg": { + "isFile": true + } + }, + "rmRF": { + "c:\\agent\\home\\directory\\NuGet_1": { + "success": true + } + }, + "findMatch": { + "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(); + +tmr.run(); diff --git a/Tasks/DotNetCoreCLI/Tests/RestoreTests/selectSourceNuGetOrg.ts b/Tasks/DotNetCoreCLI/Tests/RestoreTests/selectSourceNuGetOrg.ts new file mode 100644 index 000000000000..862a894e90c4 --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/RestoreTests/selectSourceNuGetOrg.ts @@ -0,0 +1,56 @@ +import ma = require('vsts-task-lib/mock-answer'); +import tmrm = require('vsts-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', 'restore'); +tmr.setInput('projects', 'single.csproj'); +tmr.setInput('selectOrConfig', 'select'); +tmr.setInput('includeNuGetOrg', 'True'); + +let a: ma.TaskLibAnswers = { + "osType": {}, + "checkPath": { + "c:\\agent\\home\\directory\\single.csproj": true, + "c:\\path\\dotnet.exe": true + }, + "which": { + "dotnet": "c:\\path\\dotnet.exe" + }, + "exec": { + "c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config": { + "code": 0, + "stdout": "dotnet output", + "stderr": "" + } + }, + "exist": { + "D:\\src\\github\\vsts-tasks\\Tests\\Nuget" : true + }, + "stats": { + "c:\\agent\\home\\directory\\single.csproj": { + "isFile": true + } + }, + "rmRF": { + "c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config": { + "success": true + } + }, + "findMatch": { + "single.csproj" : ["c:\\agent\\home\\directory\\single.csproj"] + } +}; +nmh.setAnswers(a); + +nmh.registerNugetUtilityMock(["c:\\agent\\home\\directory\\single.csproj"]); +nmh.registerDefaultNugetVersionMock(); +nmh.registerToolRunnerMock(); +nmh.registerNugetConfigMock(); + +tmr.run(); diff --git a/Tasks/DotNetCoreCLI/Tests/RestoreTests/selectSourceVsts.ts b/Tasks/DotNetCoreCLI/Tests/RestoreTests/selectSourceVsts.ts new file mode 100644 index 000000000000..30ff471e5ca9 --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/RestoreTests/selectSourceVsts.ts @@ -0,0 +1,57 @@ +import ma = require('vsts-task-lib/mock-answer'); +import tmrm = require('vsts-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', 'restore'); +tmr.setInput('projects', 'single.csproj'); +tmr.setInput('selectOrConfig', 'select'); +tmr.setInput('feedRestore', 'b7fb1a0d-637d-4dc0-978c-a5e43d8e0a98'); + +let a: ma.TaskLibAnswers = { + "osType": {}, + "checkPath": { + "c:\\agent\\home\\directory\\single.csproj": true, + "c:\\path\\dotnet.exe": true + }, + "which": { + "dotnet": "c:\\path\\dotnet.exe" + }, + "exec": { + "c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config": { + "code": 0, + "stdout": "dotnet output", + "stderr": "" + } + }, + "exist": { + "D:\\src\\github\\vsts-tasks\\Tests\\Nuget" : true + }, + "stats": { + "c:\\agent\\home\\directory\\single.csproj": { + "isFile": true + } + }, + "rmRF": { + "c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config": { + "success": true + } + }, + "findMatch": { + "single.csproj" : ["c:\\agent\\home\\directory\\single.csproj"] + } +}; +nmh.setAnswers(a); + +process.env["NuGet_ForceEnableCredentialConfig"] = "false"; +nmh.registerNugetUtilityMock(["c:\\agent\\home\\directory\\single.csproj"]); +nmh.registerDefaultNugetVersionMock(); +nmh.registerToolRunnerMock(); +nmh.registerNugetConfigMock(); + +tmr.run(); diff --git a/Tasks/DotNetCoreCLI/Tests/RestoreTests/singleProject.ts b/Tasks/DotNetCoreCLI/Tests/RestoreTests/singleProject.ts new file mode 100644 index 000000000000..7a40ba5b6bd3 --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/RestoreTests/singleProject.ts @@ -0,0 +1,66 @@ +import ma = require('vsts-task-lib/mock-answer'); +import tmrm = require('vsts-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', 'restore'); +tmr.setInput('projects', 'single.csproj'); +tmr.setInput('selectOrConfig', 'select'); +if(process.env["__nocache__"]) { + tmr.setInput('noCache', process.env["__nocache__"]); + } +if(process.env["__verbosity__"]) { + tmr.setInput('verbosityRestore', process.env["__verbosity__"]) +} + +let a: ma.TaskLibAnswers = { + "osType": {}, + "checkPath": { + "c:\\agent\\home\\directory\\single.csproj": true, + "c:\\path\\dotnet.exe": true + }, + "which": { + "dotnet": "c:\\path\\dotnet.exe" + }, + "exec": { + "c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config": { + "code": 0, + "stdout": "dotnet output", + "stderr": "" + }, + "c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config --no-cache": { + "code": 0, + "stdout": "dotnet output, no-cache", + "stderr": "" + }, + "c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config --verbosity Detailed": { + "code": 0, + "stdout": "dotnet output, verbosity Detailed", + "stderr": "" + } + }, + "exist": { + "D:\\src\\github\\vsts-tasks\\Tests\\Nuget" : true + }, + "stats": { + "c:\\agent\\home\\directory\\single.csproj": { + "isFile": true + } + }, + "findMatch": { + "single.csproj" : ["c:\\agent\\home\\directory\\single.csproj"] + } +}; +nmh.setAnswers(a); + +nmh.registerNugetUtilityMock(["c:\\agent\\home\\directory\\single.csproj"]); +nmh.registerDefaultNugetVersionMock(); +nmh.registerToolRunnerMock(); +nmh.registerNugetConfigMock(); + +tmr.run(); diff --git a/Tasks/DotNetCoreCLI/Tests/RestoreTests/singleProjectConfigFile.ts b/Tasks/DotNetCoreCLI/Tests/RestoreTests/singleProjectConfigFile.ts new file mode 100644 index 000000000000..8a1da7de0fef --- /dev/null +++ b/Tasks/DotNetCoreCLI/Tests/RestoreTests/singleProjectConfigFile.ts @@ -0,0 +1,52 @@ +import ma = require('vsts-task-lib/mock-answer'); +import tmrm = require('vsts-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', 'restore'); +tmr.setInput('projects', 'single.csproj'); +tmr.setInput('selectOrConfig', 'config'); +tmr.setInput('nugetConfigPath', 'c:\\agent\\home\\directory\\nuget.config'); + +let a: ma.TaskLibAnswers = { + "osType": {}, + "checkPath": { + "c:\\agent\\home\\directory\\nuget.config": true, + "c:\\agent\\home\\directory\\single.csproj": true, + "c:\\path\\dotnet.exe": true + }, + "which": { + "dotnet": "c:\\path\\dotnet.exe" + }, + "exec": { + "c:\\path\\dotnet.exe restore c:\\agent\\home\\directory\\single.csproj --configfile c:\\agent\\home\\directory\\NuGet\\tempNuGet_.config": { + "code": 0, + "stdout": "dotnet output", + "stderr": "" + } + }, + "exist": { + "D:\\src\\github\\vsts-tasks\\Tests\\Nuget" : true + }, + "stats": { + "c:\\agent\\home\\directory\\single.csproj": { + "isFile": true + } + }, + "findMatch": { + "single.csproj" : ["c:\\agent\\home\\directory\\single.csproj"] + } +}; +nmh.setAnswers(a); + +nmh.registerNugetUtilityMock(["c:\\agent\\home\\directory\\single.csproj"]); +nmh.registerDefaultNugetVersionMock(); +nmh.registerToolRunnerMock(); +nmh.registerNugetConfigMock(); + +tmr.run(); diff --git a/Tasks/DotNetCoreCLI/dotnetcore.ts b/Tasks/DotNetCoreCLI/dotnetcore.ts index 6bbea9e049c0..772c8dfd0b4e 100644 --- a/Tasks/DotNetCoreCLI/dotnetcore.ts +++ b/Tasks/DotNetCoreCLI/dotnetcore.ts @@ -3,6 +3,11 @@ import path = require("path"); import fs = require("fs"); var archiver = require('archiver'); +import * as restoreCommand from './restorecommand'; +import * as packCommand from './packcommand'; +import * as pushCommand from './pushcommand'; + + export class dotNetExe { private command: string; private projects: string[]; @@ -22,6 +27,33 @@ export class dotNetExe { public async execute() { tl.setResourcePath(path.join(__dirname, "task.json")); + + switch(this.command) { + case "build": + case "publish": + case "run": + case "test": + await this.executeBasicCommand(this.command); + break; + case "custom": + const custom = tl.getInput("custom", true); + await this.executeBasicCommand(custom); + break; + case "restore": + await restoreCommand.run(); + break; + case "pack": + await packCommand.run(); + break; + case "push": + await pushCommand.run(); + break; + default: + tl.setResult(tl.TaskResult.Failed, tl.loc("Error_CommandNotRecognized", this.command)); + } + } + + private async executeBasicCommand(command: string) { var dotnetPath = tl.which("dotnet", true); this.extractOutputArgument(); diff --git a/Tasks/DotNetCoreCLI/make.json b/Tasks/DotNetCoreCLI/make.json new file mode 100644 index 000000000000..0bfa15383e84 --- /dev/null +++ b/Tasks/DotNetCoreCLI/make.json @@ -0,0 +1,17 @@ +{ + "common": [ + { + "module": "../Common/nuget-task-common", + "type": "node", + "compile": true + } + ], + "rm": [ + { + "items": [ + "node_modules/nuget-task-common/node_modules/vsts-task-lib" + ], + "options": "-Rf" + } + ] +} diff --git a/Tasks/DotNetCoreCLI/npm-shrinkwrap.json b/Tasks/DotNetCoreCLI/npm-shrinkwrap.json deleted file mode 100644 index 0ea5364b0861..000000000000 --- a/Tasks/DotNetCoreCLI/npm-shrinkwrap.json +++ /dev/null @@ -1,228 +0,0 @@ -{ - "name": "vsts-tasks-dotnetcoreexe", - "version": "0.1.0", - "dependencies": { - "archiver": { - "version": "1.2.0", - "from": "archiver@1.2.0", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.2.0.tgz" - }, - "archiver-utils": { - "version": "1.3.0", - "from": "archiver-utils@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz" - }, - "async": { - "version": "2.1.4", - "from": "async@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz" - }, - "balanced-match": { - "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" - }, - "bl": { - "version": "1.1.2", - "from": "bl@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.0.tgz" - }, - "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz" - }, - "buffer-crc32": { - "version": "0.2.13", - "from": "buffer-crc32@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" - }, - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "compress-commons": { - "version": "1.1.0", - "from": "compress-commons@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.1.0.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "crc32-stream": { - "version": "1.0.0", - "from": "crc32-stream@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-1.0.0.tgz" - }, - "end-of-stream": { - "version": "1.1.0", - "from": "end-of-stream@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", - "dependencies": { - "once": { - "version": "1.3.3", - "from": "once@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz" - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - }, - "glob": { - "version": "7.1.1", - "from": "glob@>=7.0.0 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz" - }, - "graceful-fs": { - "version": "4.1.11", - "from": "graceful-fs@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" - }, - "inflight": { - "version": "1.0.6", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "lazystream": { - "version": "1.0.0", - "from": "lazystream@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz" - }, - "lodash": { - "version": "4.17.4", - "from": "lodash@>=4.8.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" - }, - "minimatch": { - "version": "3.0.3", - "from": "minimatch@>=3.0.2 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz" - }, - "mockery": { - "version": "1.7.0", - "from": "mockery@>=1.7.0 <2.0.0", - "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz" - }, - "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.7 <2.0.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" - }, - "normalize-path": { - "version": "2.0.1", - "from": "normalize-path@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz" - }, - "nuget-task-common": { - "version": "1.0.0", - "from": "..\\Common\\nuget-task-common", - "resolved": "file:..\\Common\\nuget-task-common" - }, - "once": { - "version": "1.4.0", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "q": { - "version": "1.4.1", - "from": "q@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz" - }, - "readable-stream": { - "version": "2.2.2", - "from": "readable-stream@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz" - }, - "semver": { - "version": "5.3.0", - "from": "semver@>=5.1.0 <6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" - }, - "shelljs": { - "version": "0.3.0", - "from": "shelljs@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "tar-stream": { - "version": "1.5.2", - "from": "tar-stream@>=1.5.0 <2.0.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.2.tgz" - }, - "tunnel": { - "version": "0.0.4", - "from": "tunnel@0.0.4", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz" - }, - "underscore": { - "version": "1.8.3", - "from": "underscore@>=1.8.3 <2.0.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - }, - "vsts-task-lib": { - "version": "2.0.2-preview", - "from": "vsts-task-lib@>=2.0.2-preview <3.0.0", - "resolved": "https://registry.npmjs.org/vsts-task-lib/-/vsts-task-lib-2.0.2-preview.tgz" - }, - "uuid": { - "version": "2.0.1", - "from": "uuid@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz" - }, - "wrappy": { - "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - }, - "zip-stream": { - "version": "1.1.0", - "from": "zip-stream@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.1.0.tgz" - } - } -} \ No newline at end of file diff --git a/Tasks/DotNetCoreCLI/package.json b/Tasks/DotNetCoreCLI/package.json index 474e27694f85..a5bc442b1bb3 100644 --- a/Tasks/DotNetCoreCLI/package.json +++ b/Tasks/DotNetCoreCLI/package.json @@ -2,7 +2,7 @@ "name": "vsts-tasks-dotnetcoreexe", "version": "0.1.0", "description": "Dotnet core exe", - "main": "dotnetexe.js", + "main": "dotnetcore.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -17,8 +17,10 @@ }, "homepage": "https://github.com/Microsoft/vsts-tasks#readme", "dependencies": { - "vsts-task-lib": "^2.0.2-preview", + "vsts-task-lib": "2.0.5", "q": "^1.4.1", - "archiver": "1.2.0" + "archiver": "1.2.0", + "xmlreader": "^0.2.3", + "vso-node-api": "6.0.1-preview" } } diff --git a/Tasks/DotNetCoreCLI/packcommand.ts b/Tasks/DotNetCoreCLI/packcommand.ts new file mode 100644 index 000000000000..5b70676ce003 --- /dev/null +++ b/Tasks/DotNetCoreCLI/packcommand.ts @@ -0,0 +1,162 @@ +import * as tl from "vsts-task-lib/task"; +import * as nutil from "nuget-task-common/Utility"; +import nuGetGetter = require("nuget-task-common/NuGetToolGetter"); +import * as path from "path"; +import * as ngToolRunner from "nuget-task-common/NuGetToolRunner2"; +import { IExecOptions } from "vsts-task-lib/toolrunner"; +import * as utility from './Common/utility'; + +export async function run(): Promise { + + let searchPattern = tl.getPathInput("searchPatternPack", true); + let configuration = tl.getInput("configurationToPack"); + let versioningScheme = tl.getInput("versioningScheme"); + let versionEnvVar = tl.getInput("versionEnvVar"); + let majorVersion = tl.getInput("requestedMajorVersion"); + let minorVersion = tl.getInput("requestedMinorVersion"); + let patchVersion = tl.getInput("requestedPatchVersion"); + let propertiesInput = tl.getInput("buildProperties"); + let verbosity = tl.getInput("verbosityPack"); + let nobuild = tl.getBoolInput("nobuild"); + let outputDir = undefined; + + try + { + // If outputDir is not provided then the root working directory is set by default. + // By requiring it, it will throw an error if it is not provided and we can set it to undefined. + outputDir = tl.getPathInput("outputDir", true); + } + catch(error) + { + outputDir = undefined; + } + + try{ + let version: string = undefined; + switch(versioningScheme) + { + case "off": + break; + case "byPrereleaseNumber": + tl.debug(`Getting prerelease number`); + + let nowUtcString = utility.getUtcDateString(); + version = `${majorVersion}.${minorVersion}.${patchVersion}-CI-${nowUtcString}`; + break; + case "byEnvVar": + tl.debug(`Getting version from env var: ${versionEnvVar}`); + version = tl.getVariable(versionEnvVar); + if(!version) + { + tl.setResult(tl.TaskResult.Failed, tl.loc("Error_NoValueFoundForEnvVar")); + break; + } + break; + case "byBuildNumber": + tl.debug("Getting version number from build number") + + if(tl.getVariable("SYSTEM_HOSTTYPE") === "release") + { + tl.setResult(tl.TaskResult.Failed, tl.loc("Error_AutomaticallyVersionReleases")); + return; + } + + let buildNumber: string = tl.getVariable("BUILD_BUILDNUMBER"); + tl.debug(`Build number: ${buildNumber}`); + + let versionRegex = /\d+\.\d+\.\d+(?:\.\d+)?/; + let versionMatches = buildNumber.match(versionRegex); + if (!versionMatches) + { + tl.setResult(tl.TaskResult.Failed, tl.loc("Error_NoVersionFoundInBuildNumber")); + return; + } + + if (versionMatches.length > 1) + { + tl.warning(tl.loc("Warning_MoreThanOneVersionInBuildNumber")) + } + + version = versionMatches[0]; + break; + } + + tl.debug(`Version to use: ${version}`); + + if(outputDir && !tl.exist(outputDir)) + { + tl.debug(`Creating output directory: ${outputDir}`); + tl.mkdirP(outputDir); + } + + let useLegacyFind: boolean = tl.getVariable("NuGet.UseLegacyFindFiles") === "true"; + let filesList: string[] = []; + if (!useLegacyFind) { + let findOptions: tl.FindOptions = {}; + let matchOptions: tl.MatchOptions = {}; + filesList = tl.findMatch(undefined, searchPattern, findOptions, matchOptions); + } + else { + filesList = nutil.resolveFilterSpec(searchPattern); + } + + tl.debug(`Found ${filesList.length} files`); + filesList.forEach(file => { + tl.debug(`--File: ${file}`); + }); + + let props: string[] = []; + if(configuration && configuration !== "$(BuildConfiguration)") + { + props.push(`Configuration=${configuration}`); + } + if(propertiesInput) + { + props = props.concat(propertiesInput.split(";")); + } + + let environmentSettings: ngToolRunner.NuGetEnvironmentSettings = { + credProviderFolder: null, + extensionsDisabled: true + }; + + const dotnetPath = tl.which("dotnet", true); + for (const file of filesList) { + await dotnetPackAsync(dotnetPath, file, outputDir, nobuild, version, props, verbosity); + } + } catch (err) { + tl.error(err); + tl.setResult(tl.TaskResult.Failed, tl.loc("Error_PackageFailure")); + } +} + +function dotnetPackAsync(dotnetPath: string, packageFile: string, outputDir: string, nobuild: boolean, version: string, properties: string[], verbosity: string) { + let dotnet = tl.tool(dotnetPath); + + dotnet.arg("pack"); + dotnet.arg(packageFile); + + if (outputDir) { + dotnet.arg("--output"); + dotnet.arg(outputDir); + } + + if (nobuild){ + dotnet.arg("--no-build"); + } + + if (properties && properties.length > 0) { + dotnet.arg("/p:"+properties.join(";")); + } + + if(version){ + dotnet.arg("/p:PackageVersion="+version); + } + + if(verbosity && verbosity !== "-") { + dotnet.arg("--verbosity"); + dotnet.arg(verbosity); + } + + return dotnet.exec({ cwd: path.dirname(packageFile) } as IExecOptions); +} diff --git a/Tasks/DotNetCoreCLI/pushcommand.ts b/Tasks/DotNetCoreCLI/pushcommand.ts new file mode 100644 index 000000000000..8fa031040ad9 --- /dev/null +++ b/Tasks/DotNetCoreCLI/pushcommand.ts @@ -0,0 +1,170 @@ +import * as path from "path"; +import * as Q from "q"; +import * as tl from "vsts-task-lib/task"; + +import locationHelpers = require("nuget-task-common/LocationHelpers"); +import {NuGetConfigHelper2} from "nuget-task-common/NuGetConfigHelper2"; +import * as nutil from "nuget-task-common/Utility"; +import * as vsts from "vso-node-api/WebApi"; +import * as vsom from 'vso-node-api/VsoClient'; +import * as auth from "nuget-task-common/Authentication"; +import { IPackageSource } from "nuget-task-common/Authentication"; +import * as utility from './Common/utility'; +import {IExecOptions} from "vsts-task-lib/toolrunner"; +import * as commandHelper from "nuget-task-common/CommandHelper"; + + +export async function run(): Promise { + let buildIdentityDisplayName: string = null; + let buildIdentityAccount: string = null; + try { + // Get list of files to publish + const searchPattern = tl.getPathInput("searchPatternPush", true, false); + + let findOptions: tl.FindOptions = {}; + let matchOptions: tl.MatchOptions = {}; + const filesList = tl.findMatch(undefined, searchPattern, findOptions, matchOptions); + + filesList.forEach(packageFile => { + if (!tl.stats(packageFile).isFile()) { + throw new Error(tl.loc("Error_PushNotARegularFile", packageFile)); + } + }); + + if (filesList && filesList.length < 1) + { + tl.setResult(tl.TaskResult.Succeeded, tl.loc("Info_NoPackagesMatchedTheSearchPattern")); + return; + } + + // Get the info the type of feed + let nugetFeedType = tl.getInput("nuGetFeedType") || "internal"; + + // Make sure the feed type is an expected one + let normalizedNuGetFeedType = ["internal", "external"].find(x => nugetFeedType.toUpperCase() === x.toUpperCase()); + if (!normalizedNuGetFeedType) { + throw new Error(tl.loc("UnknownFeedType", nugetFeedType)); + } + nugetFeedType = normalizedNuGetFeedType; + + let serviceUri = tl.getEndpointUrl("SYSTEMVSSCONNECTION", false); + let urlPrefixes = await locationHelpers.assumeNuGetUriPrefixes(serviceUri); + 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 + let testPrefixes = tl.getVariable("DotNetCoreCLITask.ExtraUrlPrefixesForTesting"); + if (testPrefixes) { + urlPrefixes = urlPrefixes.concat(testPrefixes.split(";")); + tl.debug(`all URL prefixes: ${urlPrefixes}`); + } + + // Setting up auth info + let externalAuthArr = commandHelper.GetExternalAuthInfoArray("externalEndpoint"); + let accessToken = auth.getSystemAccessToken(); + + let authInfo = new auth.NuGetExtendedAuthInfo(new auth.InternalAuthInfo(urlPrefixes, accessToken, /*useCredProvider*/ null, /*useCredConfig*/ true), externalAuthArr); + + let configFile = null; + let apiKey: string; + let credCleanup = () => { return }; + + const nuGetPath = await utility.getNuGetPath(); + + // 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); + + const nuGetConfigHelper = new NuGetConfigHelper2(nuGetPath, null, authInfo, {credProviderFolder: null, extensionsDisabled: true}, tempNuGetPath); + let feedUri: string = undefined; + const isInternalFeed: boolean = nugetFeedType === "internal"; + + if (isInternalFeed) + { + const internalFeedId = tl.getInput("feedPublish"); + feedUri = await nutil.getNuGetFeedRegistryUrl(accessToken, internalFeedId, null); + nuGetConfigHelper.addSourcesToTempNuGetConfig([{ feedName: internalFeedId, feedUri: feedUri, isInternal: true }]); + configFile = nuGetConfigHelper.tempNugetConfigPath; + credCleanup = () => {tl.rmRF(tempNuGetConfigDirectory)}; + + apiKey = "VSTS"; + } + else { + let 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; + credCleanup = () => {tl.rmRF(tempNuGetConfigDirectory)}; + + let authType: auth.ExternalAuthType = externalAuth.authType; + switch(authType) { + case (auth.ExternalAuthType.UsernamePassword): + case (auth.ExternalAuthType.Token): + apiKey = "RequiredApiKey"; + break; + case (auth.ExternalAuthType.ApiKey): + let apiKeyAuthInfo = externalAuth as auth.ApiKeyExternalAuthInfo; + apiKey = apiKeyAuthInfo.apiKey; + break; + default: + break; + } + } + + await nuGetConfigHelper.setAuthForSourcesInTempNuGetConfigAsync(); + + 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")); + + } catch (err) { + tl.error(err); + + 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 { + let dotnet = tl.tool(dotnetPath); + + dotnet.arg("nuget"); + dotnet.arg("push"); + + dotnet.arg(packageFile); + + dotnet.arg("--source"); + dotnet.arg(feedUri); + + dotnet.arg("--api-key"); + dotnet.arg(apiKey); + + // dotnet.exe v1 and v2 do not accept the --verbosity parameter for the "nuget push"" command, although it does for other commands + + return dotnet.exec({ cwd: workingDirectory } as IExecOptions); +} diff --git a/Tasks/DotNetCoreCLI/restorecommand.ts b/Tasks/DotNetCoreCLI/restorecommand.ts new file mode 100644 index 000000000000..6ca5124ab2f3 --- /dev/null +++ b/Tasks/DotNetCoreCLI/restorecommand.ts @@ -0,0 +1,168 @@ +import * as tl from "vsts-task-lib/task"; +import * as Q from "q"; +import * as utility from './Common/utility'; +import locationHelpers = require("nuget-task-common/LocationHelpers"); +import * as auth from "nuget-task-common/Authentication"; +import {NuGetConfigHelper2} from "nuget-task-common/NuGetConfigHelper2"; +import peParser = require('nuget-task-common/pe-parser/index'); +import * as path from "path"; +import {VersionInfo} from "nuget-task-common/pe-parser/VersionResource"; +import { IPackageSource } from "nuget-task-common/Authentication"; +import {IExecOptions} from "vsts-task-lib/toolrunner"; +import * as nutil from "nuget-task-common/Utility"; +import * as commandHelper from "nuget-task-common/CommandHelper"; + + +const NUGET_ORG_V3_URL: string = "https://api.nuget.org/v3/index.json"; + +export async function run(): Promise { + let buildIdentityDisplayName: string = null; + let buildIdentityAccount: string = null; + + try { + const projectSearch = tl.getDelimitedInput("projects", "\n", false); + + // if no projectSearch strings are given, use "" to operate on the current directory + const projectFiles = projectSearch ? utility.getProjectFiles(projectSearch) : [""]; + + const noCache = tl.getBoolInput("noCache"); + const verbosity = tl.getInput("verbosityRestore"); + let packagesDirectory = tl.getPathInput("packagesDirectory"); + if (!tl.filePathSupplied("packagesDirectory")) { + packagesDirectory = null; + } + + // Setting up auth-related variables + tl.debug('Setting up auth'); + const serviceUri = tl.getEndpointUrl("SYSTEMVSSCONNECTION", false); + let urlPrefixes = await locationHelpers.assumeNuGetUriPrefixes(serviceUri); + 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 + let testPrefixes = tl.getVariable("DotNetCoreCLITask.ExtraUrlPrefixesForTesting"); + if (testPrefixes) { + urlPrefixes = urlPrefixes.concat(testPrefixes.split(";")); + tl.debug(`All URL prefixes: ${urlPrefixes}`); + } + + let accessToken = auth.getSystemAccessToken(); + + let externalAuthArr: auth.ExternalAuthInfo[] = commandHelper.GetExternalAuthInfoArray("externalEndpoints"); + const authInfo = new auth.NuGetExtendedAuthInfo(new auth.InternalAuthInfo(urlPrefixes, accessToken, /*useCredProvider*/ null, /*useCredConfig*/ true), externalAuthArr); + + // Setting up sources, either from provided config file or from feed selection + tl.debug('Setting up sources'); + let nuGetConfigPath : string = undefined; + let selectOrConfig = tl.getInput("selectOrConfig"); + + // This IF is here in order to provide a value to nuGetConfigPath (if option selected, if user provided it) + // and then pass it into the config helper + if (selectOrConfig === "config" ) { + nuGetConfigPath = tl.getPathInput("nugetConfigPath", false, true); + if (!tl.filePathSupplied("nugetConfigPath")) { + nuGetConfigPath = undefined; + } + } + + const nuGetPath = await utility.getNuGetPath(); + + // If there was no nuGetConfigPath, NuGetConfigHelper will create one + let nuGetConfigHelper = new NuGetConfigHelper2( + nuGetPath, + nuGetConfigPath, + authInfo, + {credProviderFolder: null, extensionsDisabled: true}, + null); + + let credCleanup = () => { return; }; + + // Now that the NuGetConfigHelper was initialized with all the known information we can proceed + // and check if the user picked the 'select' option to fill out the config file if needed + if (selectOrConfig === "select" ) { + let sources: Array = new Array(); + let feed = tl.getInput("feedRestore"); + if (feed) { + let feedUrl:string = await nutil.getNuGetFeedRegistryUrl(accessToken, feed, null); + sources.push( + { + feedName: feed, + feedUri: feedUrl, + isInternal: true + }) + } + + let includeNuGetOrg = tl.getBoolInput("includeNuGetOrg", false); + if (includeNuGetOrg) { + sources.push( + { + feedName: "NuGetOrg", + feedUri: NUGET_ORG_V3_URL, + isInternal: false + }) + } + + // Creating NuGet.config for the user + if (sources.length > 0) + { + tl.debug(`Adding the following sources to the config file: ${sources.map(x => x.feedName).join(';')}`) + nuGetConfigHelper.addSourcesToTempNuGetConfig(sources); + credCleanup = () => { tl.rmRF(nuGetConfigHelper.tempNugetConfigPath); } + nuGetConfigPath = nuGetConfigHelper.tempNugetConfigPath; + } + else { + tl.debug('No sources were added to the temp NuGet.config file'); + } + } + + // Setting creds in the temp NuGet.config if needed + await nuGetConfigHelper.setAuthForSourcesInTempNuGetConfigAsync(); + + const configFile = nuGetConfigHelper.tempNugetConfigPath; + const dotnetPath = tl.which("dotnet", true); + + try { + for (const projectFile of projectFiles) { + await dotNetRestoreAsync(dotnetPath, projectFile, configFile, noCache, verbosity); + } + } finally { + credCleanup(); + } + + tl.setResult(tl.TaskResult.Succeeded, tl.loc("PackagesInstalledSuccessfully")); + + } catch (err) { + + tl.error(err); + + if (buildIdentityDisplayName || buildIdentityAccount) { + tl.warning(tl.loc("BuildIdentityPermissionsHint", buildIdentityDisplayName, buildIdentityAccount)); + } + + tl.setResult(tl.TaskResult.Failed, tl.loc("PackagesFailedToInstall")); + } +} + +function dotNetRestoreAsync(dotnetPath: string, projectFile: string, configFile: string, noCache: boolean, verbosity: string): Q.Promise { + let dotnet = tl.tool(dotnetPath); + dotnet.arg("restore"); + + if(projectFile) { + dotnet.arg(projectFile); + } + + dotnet.arg("--configfile"); + dotnet.arg(configFile); + + if (noCache) { + dotnet.arg("--no-cache"); + } + + if (verbosity && verbosity !== "-") { + dotnet.arg("--verbosity"); + dotnet.arg(verbosity); + } + + return dotnet.exec({ cwd: path.dirname(projectFile) } as IExecOptions); +} + diff --git a/Tasks/DotNetCoreCLI/task.json b/Tasks/DotNetCoreCLI/task.json index f17b51903a6c..e38b09e6e5e9 100644 --- a/Tasks/DotNetCoreCLI/task.json +++ b/Tasks/DotNetCoreCLI/task.json @@ -15,13 +15,50 @@ ], "demands": [], "version": { - "Major": 1, + "Major": 2, "Minor": 0, - "Patch": 2 + "Patch": 0 }, "minimumAgentVersion": "2.0.0", "instanceNameFormat": "dotnet $(command)", - "groups": [], + "groups": [ + { + "name": "restoreAuth", + "displayName": "Feeds and authentication", + "isExpanded": true, + "visibleRule": "command = restore" + }, + { + "name": "restoreAdvanced", + "displayName": "Advanced", + "isExpanded": false, + "visibleRule": "command = restore" + }, + { + "name": "pushAuth", + "displayName": "Destination feed and authentication", + "isExpanded": true, + "visibleRule": "command = push" + }, + { + "name": "pushAdvanced", + "displayName": "Advanced", + "isExpanded": false, + "visibleRule": "command = push" + }, + { + "name": "packOptions", + "displayName": "Pack options", + "isExpanded": false, + "visibleRule": "command = pack" + }, + { + "name": "packAdvanced", + "displayName": "Advanced", + "isExpanded": false, + "visibleRule": "command = pack" + } + ], "inputs": [ { "name": "command", @@ -29,16 +66,19 @@ "label": "Command", "defaultValue": "build", "required": true, - "helpMarkDown": "Select or type a dotnet command", + "helpMarkDown": "Select a dotnet command. Select 'Custom' to enter any dotnet command with arguments.", "options": { "build": "build", + "push": "nuget push", + "pack": "pack", "publish": "publish", "restore": "restore", + "run": "run", "test": "test", - "run": "run" + "custom": "custom" }, "properties": { - "EditableOptions": "True" + "EditableOptions": "False" } }, { @@ -55,15 +95,25 @@ "type": "multiLine", "label": "Project(s)", "defaultValue": "", - "visibleRule": "command != publish || publishWebProjects = false", + "visibleRule": "command = build || command = restore || command = run || command = test || command = custom || command = publish", "required": false, "helpMarkDown": "Relative path of the .csproj file(s) from repo root. Wildcards can be used. For example, **/*.csproj for all .csproj files in all the sub folders." }, + { + "name": "custom", + "type": "string", + "label": "Custom command", + "defaultValue": "", + "helpMarkDown": "The command to pass to dotnet.exe for execution.", + "required": true, + "visibleRule": "command = custom" + }, { "name": "arguments", "type": "string", "label": "Arguments", "defaultValue": "", + "visibleRule": "command = build || command = publish || command = run || command = test", "required": false, "helpMarkDown": "Arguments to the selected command. For example, build configuration, output folder, rutime. The arguments depend on the command selected." }, @@ -75,6 +125,271 @@ "defaultValue": "true", "required": false, "helpMarkDown": "If true, folder created by the publish command will be zipped." + }, + { + "name": "selectOrConfig", + "type": "radio", + "label": "Feeds to use", + "defaultValue": "config", + "helpMarkDown": "You can either select a feed from VSTS and/or NuGet.org here, or commit a nuget.config file to your source code repository and set its path here.", + "required": "true", + "options": { + "select": "Feed(s) I select here", + "config": "Feeds in my NuGet.config" + }, + "groupName": "restoreAuth" + }, + { + "name": "feedRestore", + "type": "pickList", + "label": "Use packages from this VSTS/TFS feed", + "defaultValue": "", + "helpMarkDown": "Include the selected feed in the generated NuGet.config. You must have Package Management installed and licensed to select a feed here.", + "required": "false", + "groupName": "restoreAuth", + "visibleRule": "selectOrConfig = select", + "properties": { + "EditableOptions": "True" + } + }, + { + "name": "includeNuGetOrg", + "type": "boolean", + "label": "Use packages from NuGet.org", + "defaultValue": "true", + "helpMarkDown": "Include NuGet.org in the generated NuGet.config.", + "required": "false", + "groupName": "restoreAuth", + "visibleRule": "selectOrConfig = select" + }, + { + "name": "nugetConfigPath", + "type": "filePath", + "label": "Path to NuGet.config", + "defaultValue": "", + "helpMarkDown": "The NuGet.config in your repository that specifies the feeds from which to restore packages.", + "required": "false", + "groupName": "restoreAuth", + "visibleRule": "selectOrConfig = config" + }, + { + "name": "externalEndpoints", + "type": "connectedService:ExternalNuGetFeed", + "label": "Credentials for feeds outside this account/collection", + "required": false, + "helpMarkDown": "Credentials to use for external registries located in the selected NuGet.config. For feeds in this account/collection, leave this blank; the build’s credentials are used automatically.", + "properties": { + "EditableOptions": "False", + "MultiSelect": "True" + }, + "groupName": "restoreAuth", + "visibleRule": "selectOrConfig = config" + }, + { + "name": "noCache", + "type": "boolean", + "label": "Disable local cache", + "defaultValue": "false", + "helpMarkDown": "Prevents NuGet from using packages from local machine caches.", + "required": "false", + "groupName": "restoreAdvanced" + }, + { + "name": "packagesDirectory", + "type": "string", + "label": "Destination directory", + "defaultValue": "", + "helpMarkDown": "Specifies the folder in which packages are installed. If no folder is specified, packages are restored into a packages/ folder alongside the selected solution, packages.config, or project.json.", + "required": "false", + "groupName": "restoreAdvanced" + }, + { + "name": "verbosityRestore", + "type": "pickList", + "label": "Verbosity", + "defaultValue": "Detailed", + "helpMarkDown": "Specifies the amount of detail displayed in the output.", + "required": "false", + "groupName": "restoreAdvanced", + "options": { + "-": "-", + "Quiet": "Quiet", + "Minimal": "Minimal", + "Normal": "Normal", + "Detailed": "Detailed", + "Diagnostic": "Diagnostic" + } + }, + { + "name": "searchPatternPush", + "type": "filePath", + "label": "Path to NuGet package(s) to publish", + "defaultValue": "$(Build.ArtifactStagingDirectory)/*.nupkg", + "helpMarkDown": "The pattern to match or path to nupkg files to be uploaded. Multiple patterns can be separated by a semicolon.", + "required": true, + "visibleRule": "command = push" + }, + { + "name": "nuGetFeedType", + "type": "radio", + "label": "Target feed location", + "required": true, + "defaultValue": "internal", + "options": { + "internal": "This account/collection", + "external": "External NuGet server (including other accounts/collections)" + }, + "visibleRule": "command = push" + }, + { + "name": "feedPublish", + "type": "pickList", + "label": "Target feed", + "defaultValue": "", + "required": true, + "helpMarkDown": "Select a feed hosted in this account. You must have Package Management installed and licensed to select a feed here.", + "visibleRule": "command = push && nuGetFeedType = internal" + }, + { + "name": "externalEndpoint", + "type": "connectedService:ExternalNuGetFeed", + "label": "NuGet server", + "required": true, + "helpMarkDown": "The NuGet service endpoint that contains the external NuGet server’s credentials.", + "visibleRule": "command = push && nuGetFeedType = external" + }, + { + "name": "searchPatternPack", + "type": "filePath", + "label": "Path to csproj or nuspec file(s) to pack", + "defaultValue": "**/*.csproj", + "helpMarkDown": "Pattern to search for csproj or nuspec files to pack.\n\nYou can separate multiple patterns with a semicolon, and you can make a pattern negative by prefixing it with '-:'. Example: `**\\*.csproj;-:**\\*.Tests.csproj`", + "required": true, + "visibleRule": "command = pack" + }, + { + "name": "configurationToPack", + "type": "string", + "label": "Configuration to Package", + "defaultValue": "$(BuildConfiguration)", + "helpMarkDown": "When using a csproj file this specifies the configuration to package", + "required": false, + "visibleRule": "command = pack" + }, + { + "name": "outputDir", + "type": "filePath", + "label": "Package Folder", + "defaultValue": "$(Build.ArtifactStagingDirectory)", + "helpMarkDown": "Folder where packages will be created. If empty, packages will be created alongside the csproj file.", + "required": false, + "visibleRule": "command = pack" + }, + { + "name": "nobuild", + "type": "boolean", + "label": "Do not build", + "defaultValue": "false", + "helpMarkDown": "Don't build the project before packing. Corresponds to the --no-build command line parameter.", + "required": false, + "visibleRule": "command = pack" + }, + { + "name": "versioningScheme", + "type": "pickList", + "label": "Automatic package versioning", + "defaultValue": "off", + "helpMarkDown": "Cannot be used with include referenced projects. If you choose 'Use the date and time', this will generate a [SemVer](http://semver.org/spec/v1.0.0.html)-compliant version formatted as `X.Y.Z-ci-datetime` where you choose X, Y, and Z.\n\nIf you choose 'Use an environment variable', you must select an environment variable and ensure it contains the version number you want to use.\n\nIf you choose 'Use the build number', this will use the build number to version your package. **Note:** Under Options set the build number format to be '[$(BuildDefinitionName)_$(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)](https://go.microsoft.com/fwlink/?LinkID=627416)'.", + "required": true, + "groupName": "packOptions", + "options": { + "off": "Off", + "byPrereleaseNumber": "Use the date and time", + "byEnvVar": "Use an environment variable", + "byBuildNumber": "Use the build number" + } + }, + { + "name": "versionEnvVar", + "type": "string", + "label": "Environment variable", + "defaultValue": "", + "helpMarkDown": "Enter the variable name without $, $env, or %.", + "required": true, + "groupName": "packOptions", + "visibleRule": "versioningScheme = byEnvVar" + }, + { + "name": "requestedMajorVersion", + "type": "string", + "label": "Major", + "defaultValue": "1", + "helpMarkDown": "The 'X' in version [X.Y.Z](http://semver.org/spec/v1.0.0.html)", + "required": true, + "groupName": "packOptions", + "visibleRule": "versioningScheme = byPrereleaseNumber" + }, + { + "name": "requestedMinorVersion", + "type": "string", + "label": "Minor", + "defaultValue": "0", + "helpMarkDown": "The 'Y' in version [X.Y.Z](http://semver.org/spec/v1.0.0.html)", + "required": true, + "groupName": "packOptions", + "visibleRule": "versioningScheme = byPrereleaseNumber" + }, + { + "name": "requestedPatchVersion", + "type": "string", + "label": "Patch", + "defaultValue": "0", + "helpMarkDown": "The 'Z' in version [X.Y.Z](http://semver.org/spec/v1.0.0.html)", + "required": true, + "groupName": "packOptions", + "visibleRule": "versioningScheme = byPrereleaseNumber" + }, + { + "name": "buildProperties", + "type": "string", + "label": "Additional build properties", + "defaultValue": "", + "required": false, + "helpMarkDown": "Specifies a list of token=value pairs, separated by semicolons, where each occurrence of $token$ in the .nuspec file will be replaced with the given value. Values can be strings in quotation marks.", + "groupName": "packAdvanced" + }, + { + "name": "verbosityPack", + "type": "pickList", + "label": "Verbosity", + "defaultValue": "Detailed", + "helpMarkDown": "Specifies the amount of detail displayed in the output.", + "required": "false", + "groupName": "packAdvanced", + "options": { + "-": "-", + "Quiet": "Quiet", + "Minimal": "Minimal", + "Normal": "Normal", + "Detailed": "Detailed", + "Diagnostic": "Diagnostic" + } + } + ], + "dataSourceBindings": [ + { + "target": "feedRestore", + "endpointId": "tfs:feed", + "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", + "resultSelector": "jsonpath:$.value[*]", + "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" + }, + { + "target": "feedPublish", + "endpointId": "tfs:feed", + "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", + "resultSelector": "jsonpath:$.value[*]", + "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" } ], "execution": { @@ -84,10 +399,31 @@ } }, "messages": { + "BuildIdentityPermissionsHint": "For internal feeds, make sure the build service identity '%s' [%s] has access to the feed.", + "Error_AutomaticallyVersionReleases": "Autoversion: Getting version number from build option is not supported in releases", + "Error_CommandNotRecognized": "The command %s was not recognized.", + "Error_NoSourceSpecifiedForPush": "No source was specified for push", + "Error_NoValueFoundForEnvVar": "No value was found for the provided environment variable.", + "Error_NoVersionFoundInBuildNumber": "Could not find version number data in the following environment variable: BUILD_BUILDNUMBER. The value of the variable should contain a substring with the following formats: X.Y.Z or X.Y.Z.A where A, X, Y, and Z are positive integers.", + "Error_PackageFailure": "An error ocurred while trying to pack the files.", + "Error_PushNotARegularFile": "%s is not a file. Check the 'Path/Pattern to nupkg' property of the task.", + "Info_AttemptingToPackFile": "Attempting to pack file: ", + "Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", + "Info_NoPackagesMatchedTheSearchPattern": "No packages matched the search pattern.", + "Info_ResolvedToolFromCache": "Resolved from tool cache: %s", + "Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", + "Info_UsingVersion": "Using version: %s", + "PackagesFailedToInstall": "Packages failed to restore", + "PackagesFailedToPublish": "Packages failed to publish", + "PackagesInstalledSuccessfully": "Packages were restored successfully", + "PackagesPublishedSuccessfully": "Packages were published successfully", + "UnknownFeedType": "Unknown feed type '%s'", + "Warning_AutomaticallyVersionReferencedProjects" : "The automatic package versioning and include referenced projects options do not work together. Referenced projects will not inherit the custom version provided by the automatic versioning settings.", + "Warning_MoreThanOneVersionInBuildNumber": "Found more than one instance of version data in BUILD_BUILDNUMBER.Assuming first instance is version.", + "dotnetCommandFailed": "Dotnet command failed with non-zero exit code on the following projects : %s", "noProjectFilesFound": "Project file(s) matching the specified pattern were not found.", + "noPublishFolderFoundToZip": "A publish folder could not be found to zip for project file: %s.", "noWebProjctFound": "No web project was found in the repository. Web projects are identified by presence of either a web.config file or wwwroot folder in the directory.", - "dotnetCommandFailed": "Dotnet command failed with non-zero exit code on the following projects : %s", - "zipFailed": "Zip failed with error: %s", - "noPublishFolderFoundToZip": "A publish folder could not be found to zip for project file: %s." + "zipFailed": "Zip failed with error: %s" } } \ No newline at end of file diff --git a/Tasks/DotNetCoreCLI/task.loc.json b/Tasks/DotNetCoreCLI/task.loc.json index 37144a608eb3..8c5866b4cea7 100644 --- a/Tasks/DotNetCoreCLI/task.loc.json +++ b/Tasks/DotNetCoreCLI/task.loc.json @@ -15,13 +15,50 @@ ], "demands": [], "version": { - "Major": 1, + "Major": 2, "Minor": 0, - "Patch": 2 + "Patch": 0 }, "minimumAgentVersion": "2.0.0", "instanceNameFormat": "ms-resource:loc.instanceNameFormat", - "groups": [], + "groups": [ + { + "name": "restoreAuth", + "displayName": "ms-resource:loc.group.displayName.restoreAuth", + "isExpanded": true, + "visibleRule": "command = restore" + }, + { + "name": "restoreAdvanced", + "displayName": "ms-resource:loc.group.displayName.restoreAdvanced", + "isExpanded": false, + "visibleRule": "command = restore" + }, + { + "name": "pushAuth", + "displayName": "ms-resource:loc.group.displayName.pushAuth", + "isExpanded": true, + "visibleRule": "command = push" + }, + { + "name": "pushAdvanced", + "displayName": "ms-resource:loc.group.displayName.pushAdvanced", + "isExpanded": false, + "visibleRule": "command = push" + }, + { + "name": "packOptions", + "displayName": "ms-resource:loc.group.displayName.packOptions", + "isExpanded": false, + "visibleRule": "command = pack" + }, + { + "name": "packAdvanced", + "displayName": "ms-resource:loc.group.displayName.packAdvanced", + "isExpanded": false, + "visibleRule": "command = pack" + } + ], "inputs": [ { "name": "command", @@ -32,13 +69,16 @@ "helpMarkDown": "ms-resource:loc.input.help.command", "options": { "build": "build", + "push": "nuget push", + "pack": "pack", "publish": "publish", "restore": "restore", + "run": "run", "test": "test", - "run": "run" + "custom": "custom" }, "properties": { - "EditableOptions": "True" + "EditableOptions": "False" } }, { @@ -55,15 +95,25 @@ "type": "multiLine", "label": "ms-resource:loc.input.label.projects", "defaultValue": "", - "visibleRule": "command != publish || publishWebProjects = false", + "visibleRule": "command = build || command = restore || command = run || command = test || command = custom || command = publish", "required": false, "helpMarkDown": "ms-resource:loc.input.help.projects" }, + { + "name": "custom", + "type": "string", + "label": "ms-resource:loc.input.label.custom", + "defaultValue": "", + "helpMarkDown": "ms-resource:loc.input.help.custom", + "required": true, + "visibleRule": "command = custom" + }, { "name": "arguments", "type": "string", "label": "ms-resource:loc.input.label.arguments", "defaultValue": "", + "visibleRule": "command = build || command = publish || command = run || command = test", "required": false, "helpMarkDown": "ms-resource:loc.input.help.arguments" }, @@ -75,6 +125,271 @@ "defaultValue": "true", "required": false, "helpMarkDown": "ms-resource:loc.input.help.zipAfterPublish" + }, + { + "name": "selectOrConfig", + "type": "radio", + "label": "ms-resource:loc.input.label.selectOrConfig", + "defaultValue": "config", + "helpMarkDown": "ms-resource:loc.input.help.selectOrConfig", + "required": "true", + "options": { + "select": "Feed(s) I select here", + "config": "Feeds in my NuGet.config" + }, + "groupName": "restoreAuth" + }, + { + "name": "feedRestore", + "type": "pickList", + "label": "ms-resource:loc.input.label.feedRestore", + "defaultValue": "", + "helpMarkDown": "ms-resource:loc.input.help.feedRestore", + "required": "false", + "groupName": "restoreAuth", + "visibleRule": "selectOrConfig = select", + "properties": { + "EditableOptions": "True" + } + }, + { + "name": "includeNuGetOrg", + "type": "boolean", + "label": "ms-resource:loc.input.label.includeNuGetOrg", + "defaultValue": "true", + "helpMarkDown": "ms-resource:loc.input.help.includeNuGetOrg", + "required": "false", + "groupName": "restoreAuth", + "visibleRule": "selectOrConfig = select" + }, + { + "name": "nugetConfigPath", + "type": "filePath", + "label": "ms-resource:loc.input.label.nugetConfigPath", + "defaultValue": "", + "helpMarkDown": "ms-resource:loc.input.help.nugetConfigPath", + "required": "false", + "groupName": "restoreAuth", + "visibleRule": "selectOrConfig = config" + }, + { + "name": "externalEndpoints", + "type": "connectedService:ExternalNuGetFeed", + "label": "ms-resource:loc.input.label.externalEndpoints", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.externalEndpoints", + "properties": { + "EditableOptions": "False", + "MultiSelect": "True" + }, + "groupName": "restoreAuth", + "visibleRule": "selectOrConfig = config" + }, + { + "name": "noCache", + "type": "boolean", + "label": "ms-resource:loc.input.label.noCache", + "defaultValue": "false", + "helpMarkDown": "ms-resource:loc.input.help.noCache", + "required": "false", + "groupName": "restoreAdvanced" + }, + { + "name": "packagesDirectory", + "type": "string", + "label": "ms-resource:loc.input.label.packagesDirectory", + "defaultValue": "", + "helpMarkDown": "ms-resource:loc.input.help.packagesDirectory", + "required": "false", + "groupName": "restoreAdvanced" + }, + { + "name": "verbosityRestore", + "type": "pickList", + "label": "ms-resource:loc.input.label.verbosityRestore", + "defaultValue": "Detailed", + "helpMarkDown": "ms-resource:loc.input.help.verbosityRestore", + "required": "false", + "groupName": "restoreAdvanced", + "options": { + "-": "-", + "Quiet": "Quiet", + "Minimal": "Minimal", + "Normal": "Normal", + "Detailed": "Detailed", + "Diagnostic": "Diagnostic" + } + }, + { + "name": "searchPatternPush", + "type": "filePath", + "label": "ms-resource:loc.input.label.searchPatternPush", + "defaultValue": "$(Build.ArtifactStagingDirectory)/*.nupkg", + "helpMarkDown": "ms-resource:loc.input.help.searchPatternPush", + "required": true, + "visibleRule": "command = push" + }, + { + "name": "nuGetFeedType", + "type": "radio", + "label": "ms-resource:loc.input.label.nuGetFeedType", + "required": true, + "defaultValue": "internal", + "options": { + "internal": "This account/collection", + "external": "External NuGet server (including other accounts/collections)" + }, + "visibleRule": "command = push" + }, + { + "name": "feedPublish", + "type": "pickList", + "label": "ms-resource:loc.input.label.feedPublish", + "defaultValue": "", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.feedPublish", + "visibleRule": "command = push && nuGetFeedType = internal" + }, + { + "name": "externalEndpoint", + "type": "connectedService:ExternalNuGetFeed", + "label": "ms-resource:loc.input.label.externalEndpoint", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.externalEndpoint", + "visibleRule": "command = push && nuGetFeedType = external" + }, + { + "name": "searchPatternPack", + "type": "filePath", + "label": "ms-resource:loc.input.label.searchPatternPack", + "defaultValue": "**/*.csproj", + "helpMarkDown": "ms-resource:loc.input.help.searchPatternPack", + "required": true, + "visibleRule": "command = pack" + }, + { + "name": "configurationToPack", + "type": "string", + "label": "ms-resource:loc.input.label.configurationToPack", + "defaultValue": "$(BuildConfiguration)", + "helpMarkDown": "ms-resource:loc.input.help.configurationToPack", + "required": false, + "visibleRule": "command = pack" + }, + { + "name": "outputDir", + "type": "filePath", + "label": "ms-resource:loc.input.label.outputDir", + "defaultValue": "$(Build.ArtifactStagingDirectory)", + "helpMarkDown": "ms-resource:loc.input.help.outputDir", + "required": false, + "visibleRule": "command = pack" + }, + { + "name": "nobuild", + "type": "boolean", + "label": "ms-resource:loc.input.label.nobuild", + "defaultValue": "false", + "helpMarkDown": "ms-resource:loc.input.help.nobuild", + "required": false, + "visibleRule": "command = pack" + }, + { + "name": "versioningScheme", + "type": "pickList", + "label": "ms-resource:loc.input.label.versioningScheme", + "defaultValue": "off", + "helpMarkDown": "ms-resource:loc.input.help.versioningScheme", + "required": true, + "groupName": "packOptions", + "options": { + "off": "Off", + "byPrereleaseNumber": "Use the date and time", + "byEnvVar": "Use an environment variable", + "byBuildNumber": "Use the build number" + } + }, + { + "name": "versionEnvVar", + "type": "string", + "label": "ms-resource:loc.input.label.versionEnvVar", + "defaultValue": "", + "helpMarkDown": "ms-resource:loc.input.help.versionEnvVar", + "required": true, + "groupName": "packOptions", + "visibleRule": "versioningScheme = byEnvVar" + }, + { + "name": "requestedMajorVersion", + "type": "string", + "label": "ms-resource:loc.input.label.requestedMajorVersion", + "defaultValue": "1", + "helpMarkDown": "ms-resource:loc.input.help.requestedMajorVersion", + "required": true, + "groupName": "packOptions", + "visibleRule": "versioningScheme = byPrereleaseNumber" + }, + { + "name": "requestedMinorVersion", + "type": "string", + "label": "ms-resource:loc.input.label.requestedMinorVersion", + "defaultValue": "0", + "helpMarkDown": "ms-resource:loc.input.help.requestedMinorVersion", + "required": true, + "groupName": "packOptions", + "visibleRule": "versioningScheme = byPrereleaseNumber" + }, + { + "name": "requestedPatchVersion", + "type": "string", + "label": "ms-resource:loc.input.label.requestedPatchVersion", + "defaultValue": "0", + "helpMarkDown": "ms-resource:loc.input.help.requestedPatchVersion", + "required": true, + "groupName": "packOptions", + "visibleRule": "versioningScheme = byPrereleaseNumber" + }, + { + "name": "buildProperties", + "type": "string", + "label": "ms-resource:loc.input.label.buildProperties", + "defaultValue": "", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.buildProperties", + "groupName": "packAdvanced" + }, + { + "name": "verbosityPack", + "type": "pickList", + "label": "ms-resource:loc.input.label.verbosityPack", + "defaultValue": "Detailed", + "helpMarkDown": "ms-resource:loc.input.help.verbosityPack", + "required": "false", + "groupName": "packAdvanced", + "options": { + "-": "-", + "Quiet": "Quiet", + "Minimal": "Minimal", + "Normal": "Normal", + "Detailed": "Detailed", + "Diagnostic": "Diagnostic" + } + } + ], + "dataSourceBindings": [ + { + "target": "feedRestore", + "endpointId": "tfs:feed", + "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", + "resultSelector": "jsonpath:$.value[*]", + "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" + }, + { + "target": "feedPublish", + "endpointId": "tfs:feed", + "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", + "resultSelector": "jsonpath:$.value[*]", + "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" } ], "execution": { @@ -84,10 +399,31 @@ } }, "messages": { + "BuildIdentityPermissionsHint": "ms-resource:loc.messages.BuildIdentityPermissionsHint", + "Error_AutomaticallyVersionReleases": "ms-resource:loc.messages.Error_AutomaticallyVersionReleases", + "Error_CommandNotRecognized": "ms-resource:loc.messages.Error_CommandNotRecognized", + "Error_NoSourceSpecifiedForPush": "ms-resource:loc.messages.Error_NoSourceSpecifiedForPush", + "Error_NoValueFoundForEnvVar": "ms-resource:loc.messages.Error_NoValueFoundForEnvVar", + "Error_NoVersionFoundInBuildNumber": "ms-resource:loc.messages.Error_NoVersionFoundInBuildNumber", + "Error_PackageFailure": "ms-resource:loc.messages.Error_PackageFailure", + "Error_PushNotARegularFile": "ms-resource:loc.messages.Error_PushNotARegularFile", + "Info_AttemptingToPackFile": "ms-resource:loc.messages.Info_AttemptingToPackFile", + "Info_MatchingUrlWasFoundSettingAuth": "ms-resource:loc.messages.Info_MatchingUrlWasFoundSettingAuth", + "Info_NoPackagesMatchedTheSearchPattern": "ms-resource:loc.messages.Info_NoPackagesMatchedTheSearchPattern", + "Info_ResolvedToolFromCache": "ms-resource:loc.messages.Info_ResolvedToolFromCache", + "Info_SavingTempConfig": "ms-resource:loc.messages.Info_SavingTempConfig", + "Info_UsingVersion": "ms-resource:loc.messages.Info_UsingVersion", + "PackagesFailedToInstall": "ms-resource:loc.messages.PackagesFailedToInstall", + "PackagesFailedToPublish": "ms-resource:loc.messages.PackagesFailedToPublish", + "PackagesInstalledSuccessfully": "ms-resource:loc.messages.PackagesInstalledSuccessfully", + "PackagesPublishedSuccessfully": "ms-resource:loc.messages.PackagesPublishedSuccessfully", + "UnknownFeedType": "ms-resource:loc.messages.UnknownFeedType", + "Warning_AutomaticallyVersionReferencedProjects": "ms-resource:loc.messages.Warning_AutomaticallyVersionReferencedProjects", + "Warning_MoreThanOneVersionInBuildNumber": "ms-resource:loc.messages.Warning_MoreThanOneVersionInBuildNumber", + "dotnetCommandFailed": "ms-resource:loc.messages.dotnetCommandFailed", "noProjectFilesFound": "ms-resource:loc.messages.noProjectFilesFound", + "noPublishFolderFoundToZip": "ms-resource:loc.messages.noPublishFolderFoundToZip", "noWebProjctFound": "ms-resource:loc.messages.noWebProjctFound", - "dotnetCommandFailed": "ms-resource:loc.messages.dotnetCommandFailed", - "zipFailed": "ms-resource:loc.messages.zipFailed", - "noPublishFolderFoundToZip": "ms-resource:loc.messages.noPublishFolderFoundToZip" + "zipFailed": "ms-resource:loc.messages.zipFailed" } } \ No newline at end of file diff --git a/Tasks/NuGetCommand/Common/Authentication.ts b/Tasks/NuGetCommand/Common/Authentication.ts deleted file mode 100644 index 2d77d2c6bca4..000000000000 --- a/Tasks/NuGetCommand/Common/Authentication.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as tl from "vsts-task-lib/task"; - - -export interface IPackageSource { - feedName: string; - feedUri: string; - isInternal: boolean; -} - -export class NuGetAuthInfo { - constructor( - public internalAuthInfo: InternalAuthInfo, - public externalAuthInfo?: ExternalAuthInfo[]) { - } -} - -export class InternalAuthInfo -{ - constructor( - public uriPrefixes: string[], - public accessToken: string, - public useCredProvider: string, - public useCredConfig: boolean) { - } -} - -export class ExternalAuthInfo -{ - constructor( - public packageSource: IPackageSource, - public authType: ExternalAuthType) { - } -} - -export class TokenExternalAuthInfo extends ExternalAuthInfo -{ - constructor( - public packageSource: IPackageSource, - public token: string) - { - super(packageSource, ExternalAuthType.Token); - } -} - -export class UsernamePasswordExternalAuthInfo extends ExternalAuthInfo -{ - constructor( - public packageSource: IPackageSource, - public username: string, - public password: string) - { - super(packageSource, ExternalAuthType.UsernamePassword); - } -} - -export class ApiKeyExternalAuthInfo extends ExternalAuthInfo -{ - constructor( - public packageSource: IPackageSource, - public apiKey: string) - { - super(packageSource, ExternalAuthType.ApiKey); - } -} - -export enum ExternalAuthType -{ - Token, - UsernamePassword, - ApiKey -} - -export function getSystemAccessToken(): string { - tl.debug("Getting credentials for local feeds"); - let auth = tl.getEndpointAuthorization("SYSTEMVSSCONNECTION", false); - if (auth.scheme === "OAuth") { - tl.debug("Got auth token"); - return auth.parameters["AccessToken"]; - } - else { - tl.warning("Could not determine credentials to use for NuGet"); - } -} diff --git a/Tasks/NuGetCommand/Common/INuGetCommandOptions.ts b/Tasks/NuGetCommand/Common/INuGetCommandOptions.ts index e65c84310126..296fdf2278af 100644 --- a/Tasks/NuGetCommand/Common/INuGetCommandOptions.ts +++ b/Tasks/NuGetCommand/Common/INuGetCommandOptions.ts @@ -1,4 +1,4 @@ -import { NuGetEnvironmentSettings } from "./NuGetToolRunner"; +import { NuGetEnvironmentSettings } from "nuget-task-common/NuGetToolRunner2"; export interface INuGetCommandOptions { /** settings used to initialize the environment NuGet.exe is invoked in */ diff --git a/Tasks/NuGetCommand/Common/VstsNuGetPushToolRunner.ts b/Tasks/NuGetCommand/Common/VstsNuGetPushToolRunner.ts index 186622542f08..7fb940badba3 100644 --- a/Tasks/NuGetCommand/Common/VstsNuGetPushToolRunner.ts +++ b/Tasks/NuGetCommand/Common/VstsNuGetPushToolRunner.ts @@ -1,7 +1,6 @@ import {IExecOptions, IExecSyncResult, ToolRunner} from "vsts-task-lib/toolrunner"; -import * as auth from "./Authentication"; +import * as auth from "nuget-task-common/Authentication"; import * as tl from "vsts-task-lib/task"; -import * as util from "./utilities"; import * as path from "path"; export interface VstsNuGetPushSettings { diff --git a/Tasks/NuGetCommand/Common/VstsNuGetPushToolUtilities.ts b/Tasks/NuGetCommand/Common/VstsNuGetPushToolUtilities.ts index 8e180e019eb9..b1165b976d9b 100644 --- a/Tasks/NuGetCommand/Common/VstsNuGetPushToolUtilities.ts +++ b/Tasks/NuGetCommand/Common/VstsNuGetPushToolUtilities.ts @@ -1,13 +1,12 @@ // Placed as a separate file for the purpose of unit testing - -import * as util from "./utilities"; import * as path from "path"; +import * as commandHelper from "nuget-task-common/CommandHelper"; export function getBundledVstsNuGetPushLocation(): string { const vstsNuGetPushPaths: string[] = ["VstsNuGetPush/0.13.0"]; - const toolPath = util.locateTool("VstsNuGetPush", - { + const toolPath = commandHelper.locateTool("VstsNuGetPush", + { root: path.dirname(__dirname), searchPath: vstsNuGetPushPaths, toolFilenames: ["VstsNuGetPush.exe"], diff --git a/Tasks/NuGetCommand/Strings/resources.resjson/en-US/resources.resjson b/Tasks/NuGetCommand/Strings/resources.resjson/en-US/resources.resjson index df6410f00f89..714c3ccacd94 100644 --- a/Tasks/NuGetCommand/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/NuGetCommand/Strings/resources.resjson/en-US/resources.resjson @@ -40,7 +40,7 @@ "loc.input.label.verbosityPush": "Verbosity", "loc.input.help.verbosityPush": "Specifies the amount of detail displayed in the output.", "loc.input.label.searchPatternPack": "Path to csproj or nuspec file(s) to pack", - "loc.input.help.searchPatternPack": "Pattern to search for csproj or nuspec files to pack.\n\nYou can separate multiple patterns with a semicolon, and you can make a pattern negative by prefixing it with '-:'. Example: `**\\*.csproj;-:**\\*.Tests.csproj`", + "loc.input.help.searchPatternPack": "Pattern to search for csproj directories to pack.\n\nYou can separate multiple patterns with a semicolon, and you can make a pattern negative by prefixing it with '-:'. Example: `**\\*.csproj;-:**\\*.Tests.csproj`", "loc.input.label.configurationToPack": "Configuration to Package", "loc.input.help.configurationToPack": "When using a csproj file this specifies the configuration to package", "loc.input.label.outputDir": "Package Folder", @@ -87,7 +87,9 @@ "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", "loc.messages.Info_AvailableVersions": "The available versions are: %s", + "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", + "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", "loc.messages.Info_UsingVersion": "Using version: %s", "loc.messages.Info_UsingToolPath": "Using tool path: %s", "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", @@ -111,9 +113,7 @@ "loc.messages.Warning_FallBackToNuGet": "VstsNuGetPush.exe was not found, falling back to NuGet.exe.", "loc.messages.Info_NoPackagesMatchedTheSearchPattern": "No packages matched the search pattern.", "loc.messages.Warning_ForceNuGetCannotSkipConflicts": "NuGet.exe was forced to be used for publish, if push conflicts occur it will result in a failure.", - "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", "loc.messages.Error_PushNotARegularFile": "%s is not a file. Check the 'Path/Pattern to nupkg' property of the task.", "loc.messages.Warning_AllowDuplicatesOnlyAvailableHosted": "The 'Allow duplicates to be skipped' option is currently only available on Visual Studio Team Services. If NuGet.exe encounters a conflict, the task will fail.", - "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", "loc.messages.Error_CommandNotRecognized": "The command %s was not recognized." } \ No newline at end of file diff --git a/Tasks/NuGetCommand/Tests/L0.ts b/Tasks/NuGetCommand/Tests/L0.ts index f00265d817c7..68d3b2091de3 100644 --- a/Tasks/NuGetCommand/Tests/L0.ts +++ b/Tasks/NuGetCommand/Tests/L0.ts @@ -177,7 +177,7 @@ describe('NuGetCommand Suite', function () { tr.run() assert(tr.invokedToolCount == 1, 'should have run NuGet once'); - assert(tr.ran('c:\\from\\tool\\installer\\nuget.exe push c:\\agent\\home\\directory\\foo.nupkg -NonInteractive -Source foobar -ApiKey VSTS'), 'it should have run NuGet'); + assert(tr.ran('c:\\from\\tool\\installer\\nuget.exe push c:\\agent\\home\\directory\\foo.nupkg -NonInteractive -Source https://vsts/packagesource -ApiKey VSTS'), 'it should have run NuGet'); assert(tr.stdOutContained('setting console code page'), 'it should have run chcp'); assert(tr.stdOutContained('NuGet output here'), "should have nuget output"); assert(tr.succeeded, 'should have succeeded'); @@ -193,7 +193,7 @@ describe('NuGetCommand Suite', function () { tr.run() assert(tr.invokedToolCount == 1, 'should have run VstsNuGetPush once'); - assert(tr.ran('c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source foobar -AccessToken token -NonInteractive'), 'it should have run NuGet'); + assert(tr.ran('c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source https://vsts/packagesource -AccessToken token -NonInteractive'), 'it should have run NuGet'); assert(tr.stdOutContained('setting console code page'), 'it should have run chcp'); assert(tr.stdOutContained('VstsNuGetPush output here'), "should have VstsNuGetPush output"); assert(tr.succeeded, 'should have succeeded'); @@ -209,7 +209,7 @@ describe('NuGetCommand Suite', function () { tr.run() assert(tr.invokedToolCount == 1, 'should have run VstsNuGetPush once'); - assert(tr.ran('c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source foobar -AccessToken token -NonInteractive'), 'it should have run NuGet'); + assert(tr.ran('c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source https://vsts/packagesource -AccessToken token -NonInteractive'), 'it should have run NuGet'); assert(tr.stdOutContained('setting console code page'), 'it should have run chcp'); assert(tr.stdOutContained('VstsNuGetPush output here'), "should have VstsNuGetPush output"); assert(tr.succeeded, 'should have succeeded'); @@ -225,7 +225,7 @@ describe('NuGetCommand Suite', function () { tr.run() assert(tr.invokedToolCount == 1, 'should have run VstsNuGetPush once'); - assert(tr.ran('c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source foobar -AccessToken token -NonInteractive'), 'it should have run NuGet'); + assert(tr.ran('c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source https://vsts/packagesource -AccessToken token -NonInteractive'), 'it should have run NuGet'); assert(tr.stdOutContained('setting console code page'), 'it should have run chcp'); assert(tr.stdOutContained('VstsNuGetPush output here'), "should have VstsNuGetPush output"); assert(tr.failed, 'should have failed'); diff --git a/Tasks/NuGetCommand/Tests/NuGetConfigHelper-mock.ts b/Tasks/NuGetCommand/Tests/NuGetConfigHelper-mock.ts index 4787b8d46f61..652670cf494f 100644 --- a/Tasks/NuGetCommand/Tests/NuGetConfigHelper-mock.ts +++ b/Tasks/NuGetCommand/Tests/NuGetConfigHelper-mock.ts @@ -1,6 +1,6 @@ import * as tl from "vsts-task-lib/task"; -export class NuGetConfigHelper { +export class NuGetConfigHelper2 { tempNugetConfigPath = tl.getVariable("Agent.HomeDirectory") + "\\tempNuGet_.config"; setAuthForSourcesInTempNuGetConfigAsync() { diff --git a/Tasks/NuGetCommand/Tests/NugetMockHelper.ts b/Tasks/NuGetCommand/Tests/NugetMockHelper.ts index a1c881979740..ec5267dca035 100644 --- a/Tasks/NuGetCommand/Tests/NugetMockHelper.ts +++ b/Tasks/NuGetCommand/Tests/NugetMockHelper.ts @@ -1,7 +1,7 @@ import tmrm = require('vsts-task-lib/mock-run'); import VersionInfoVersion from 'nuget-task-common/pe-parser/VersionInfoVersion' import {VersionInfo, VersionStrings} from 'nuget-task-common/pe-parser/VersionResource' -import * as auth from './../Common/Authentication' +import * as auth from 'nuget-task-common/Authentication' export class NugetMockHelper { private defaultNugetVersion = '4.0.0'; @@ -35,7 +35,7 @@ export class NugetMockHelper { public registerNugetVersionMock(productVersion: string, versionInfoVersion: number[]) { this.registerNugetVersionMockInternal(productVersion, versionInfoVersion); - this.tmr.registerMock('nuget-task-common/pe-parser', { + this.registerMockWithMultiplePaths(['nuget-task-common/pe-parser', './pe-parser'], { getFileVersionInfoAsync: function(nuGetExePath) { let result: VersionInfo = { strings: {} }; result.fileVersion = new VersionInfoVersion(versionInfoVersion[0], versionInfoVersion[1], versionInfoVersion[2], versionInfoVersion[3]); @@ -46,7 +46,7 @@ export class NugetMockHelper { } private registerNugetVersionMockInternal(productVersion: string, versionInfoVersion: number[]) { - this.tmr.registerMock('nuget-task-common/pe-parser/index', { + this.registerMockWithMultiplePaths(['nuget-task-common/pe-parser/index', './pe-parser/index'], { getFileVersionInfoAsync: function(nuGetExePath) { let result: VersionInfo = { strings: {} }; result.fileVersion = new VersionInfoVersion(versionInfoVersion[0], versionInfoVersion[1], versionInfoVersion[2], versionInfoVersion[3]); @@ -74,8 +74,11 @@ export class NugetMockHelper { setConsoleCodePage: function() { var tlm = require('vsts-task-lib/mock-task'); tlm.debug(`setting console code page`); + }, + getNuGetFeedRegistryUrl(accessToken, feedId, nuGetVersion) { + return 'https://vsts/packagesource'; } - } ) + }); } public registerVstsNuGetPushRunnerMock() { @@ -96,9 +99,9 @@ export class NugetMockHelper { public registerNugetConfigMock() { var nchm = require('./NuGetConfigHelper-mock'); - this.tmr.registerMock('./Common/NuGetConfigHelper', nchm); + this.tmr.registerMock('nuget-task-common/NuGetConfigHelper2', nchm); } - + public registerToolRunnerMock() { var mtt = require('vsts-task-lib/mock-toolrunner'); this.tmr.registerMock('vsts-task-lib/toolrunner', mtt); @@ -132,4 +135,10 @@ export class NugetMockHelper { a.exist["c:\\agent\\home\\directory\\externals\\nuget\\CredentialProvider\\CredentialProvider.TeamBuild.exe"] = true; this.tmr.setAnswers(a); } + + private registerMockWithMultiplePaths(paths: string[], mock: any) { + for(let i = 0; i < paths.length; i++) { + this.tmr.registerMock(paths[i], mock); + } + } } \ No newline at end of file diff --git a/Tasks/NuGetCommand/Tests/PublishTests/internalFeedNuGet.ts b/Tasks/NuGetCommand/Tests/PublishTests/internalFeedNuGet.ts index 45bd1c961588..a3cf6c67f478 100644 --- a/Tasks/NuGetCommand/Tests/PublishTests/internalFeedNuGet.ts +++ b/Tasks/NuGetCommand/Tests/PublishTests/internalFeedNuGet.ts @@ -21,7 +21,7 @@ let a: ma.TaskLibAnswers = { }, "which": {}, "exec": { - "c:\\from\\tool\\installer\\nuget.exe push c:\\agent\\home\\directory\\foo.nupkg -NonInteractive -Source foobar -ApiKey VSTS": { + "c:\\from\\tool\\installer\\nuget.exe push c:\\agent\\home\\directory\\foo.nupkg -NonInteractive -Source https://vsts/packagesource -ApiKey VSTS": { "code": 0, "stdout": "NuGet output here", "stderr": "" diff --git a/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPush.ts b/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPush.ts index b88570451e9c..0ec501ae49b1 100644 --- a/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPush.ts +++ b/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPush.ts @@ -21,7 +21,7 @@ let a: ma.TaskLibAnswers = { }, "which": {}, "exec": { - "c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source foobar -AccessToken token -NonInteractive": { + "c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source https://vsts/packagesource -AccessToken token -NonInteractive": { "code": 0, "stdout": "VstsNuGetPush output here", "stderr": "" diff --git a/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPushAllowConflict.ts b/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPushAllowConflict.ts index 51d6b0bb2300..ad848fd74289 100644 --- a/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPushAllowConflict.ts +++ b/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPushAllowConflict.ts @@ -21,7 +21,7 @@ let a: ma.TaskLibAnswers = { }, "which": {}, "exec": { - "c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source foobar -AccessToken token -NonInteractive": { + "c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source https://vsts/packagesource -AccessToken token -NonInteractive": { "code": 2, "stdout": "VstsNuGetPush output here", "stderr": "" diff --git a/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPushDisallowConflict.ts b/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPushDisallowConflict.ts index 910d2e421a6e..84c6e5a955cc 100644 --- a/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPushDisallowConflict.ts +++ b/Tasks/NuGetCommand/Tests/PublishTests/internalFeedVstsNuGetPushDisallowConflict.ts @@ -21,7 +21,7 @@ let a: ma.TaskLibAnswers = { }, "which": {}, "exec": { - "c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source foobar -AccessToken token -NonInteractive": { + "c:\\agent\\home\\directory\\externals\\nuget\\VstsNuGetPush.exe c:\\agent\\home\\directory\\foo.nupkg -Source https://vsts/packagesource -AccessToken token -NonInteractive": { "code": 2, "stdout": "VstsNuGetPush output here", "stderr": "" diff --git a/Tasks/NuGetCommand/nugetcustom.ts b/Tasks/NuGetCommand/nugetcustom.ts index 64b25c1139e9..1e9c0e38894a 100644 --- a/Tasks/NuGetCommand/nugetcustom.ts +++ b/Tasks/NuGetCommand/nugetcustom.ts @@ -1,8 +1,8 @@ import * as tl from "vsts-task-lib/task"; -import * as ngToolRunner from "./Common/NuGetToolRunner"; +import * as ngToolRunner from "nuget-task-common/NuGetToolRunner2"; import * as nutil from "nuget-task-common/Utility"; import * as path from "path"; -import * as auth from "./Common/Authentication"; +import * as auth from "nuget-task-common/Authentication"; import locationHelpers = require("nuget-task-common/LocationHelpers"); import nuGetGetter = require("nuget-task-common/NuGetToolGetter"); @@ -13,7 +13,7 @@ class NuGetExecutionOptions { public nuGetPath: string, public environment: ngToolRunner.NuGetEnvironmentSettings, public args: string, - public authInfo: auth.NuGetAuthInfo + public authInfo: auth.NuGetExtendedAuthInfo ) { } } @@ -55,7 +55,7 @@ export async function run(nuGetPath: string): Promise { urlPrefixes = urlPrefixes.concat(testPrefixes.split(";")); tl.debug(`All URL prefixes: ${urlPrefixes}`); } - let authInfo = new auth.NuGetAuthInfo(new auth.InternalAuthInfo(urlPrefixes, accessToken, useCredProvider, false), []); + let authInfo = new auth.NuGetExtendedAuthInfo(new auth.InternalAuthInfo(urlPrefixes, accessToken, useCredProvider, false), []); let environmentSettings: ngToolRunner.NuGetEnvironmentSettings = { credProviderFolder: useCredProvider ? path.dirname(credProviderPath) : null, extensionsDisabled: true diff --git a/Tasks/NuGetCommand/nugetpack.ts b/Tasks/NuGetCommand/nugetpack.ts index 921987943eea..f0c4cebd7f4c 100644 --- a/Tasks/NuGetCommand/nugetpack.ts +++ b/Tasks/NuGetCommand/nugetpack.ts @@ -2,7 +2,7 @@ import * as tl from "vsts-task-lib/task"; import * as nutil from "nuget-task-common/Utility"; import nuGetGetter = require("nuget-task-common/NuGetToolGetter"); import * as path from "path"; -import * as ngToolRunner from "./Common/NuGetToolRunner"; +import * as ngToolRunner from "nuget-task-common/NuGetToolRunner2"; import * as packUtils from "./Common/NuGetPackUtilities"; import INuGetCommandOptions from "./Common/INuGetCommandOptions"; diff --git a/Tasks/NuGetCommand/nugetpublisher.ts b/Tasks/NuGetCommand/nugetpublisher.ts index 20cb6837fcc1..266e59cc2de7 100644 --- a/Tasks/NuGetCommand/nugetpublisher.ts +++ b/Tasks/NuGetCommand/nugetpublisher.ts @@ -4,8 +4,8 @@ import * as tl from "vsts-task-lib/task"; import INuGetCommandOptions from "./Common/INuGetCommandOptions"; import locationHelpers = require("nuget-task-common/LocationHelpers"); -import {NuGetConfigHelper} from "./Common/NuGetConfigHelper"; -import * as ngToolRunner from "./Common/NuGetToolRunner"; +import {NuGetConfigHelper2} from "nuget-task-common/NuGetConfigHelper2"; +import * as ngToolRunner from "nuget-task-common/NuGetToolRunner2"; import * as vstsNuGetPushToolRunner from "./Common/VstsNuGetPushToolRunner"; import * as vstsNuGetPushToolUtilities from "./Common/VstsNuGetPushToolUtilities"; import * as nutil from "nuget-task-common/Utility"; @@ -14,12 +14,10 @@ import * as vsts from "vso-node-api/WebApi"; import * as vsom from 'vso-node-api/VsoClient'; import {VersionInfo} from "nuget-task-common/pe-parser/VersionResource"; import {VersionInfoVersion} from "nuget-task-common/pe-parser/VersionInfoVersion"; -import * as nugetCommonUtilities from "./Common/utilities" -import * as auth from "./Common/Authentication"; -import { IPackageSource } from "./Common/Authentication"; -import * as utilities from "./Common/utilities"; +import * as auth from "nuget-task-common/Authentication"; +import { IPackageSource } from "nuget-task-common/Authentication"; import peParser = require('nuget-task-common/pe-parser/index'); -import * as util from "./Common/utilities"; +import * as commandHelper from "nuget-task-common/CommandHelper"; class PublishOptions implements INuGetCommandOptions { constructor( @@ -28,7 +26,7 @@ class PublishOptions implements INuGetCommandOptions { public apiKey: string, public configFile: string, public verbosity: string, - public authInfo: auth.NuGetAuthInfo, + public authInfo: auth.NuGetExtendedAuthInfo, public environment: ngToolRunner.NuGetEnvironmentSettings ) { } } @@ -95,7 +93,7 @@ export async function run(nuGetPath: string): Promise { } // Setting up auth info - let externalAuthArr = utilities.GetExternalAuthInfoArray("externalEndpoint"); + let externalAuthArr = commandHelper.GetExternalAuthInfoArray("externalEndpoint"); let accessToken = auth.getSystemAccessToken(); const quirks = await ngToolRunner.getNuGetQuirksAsync(nuGetPath); let credProviderPath = nutil.locateCredentialProvider(); @@ -103,7 +101,7 @@ export async function run(nuGetPath: string): Promise { // is unconditionally displayed let useCredProvider = ngToolRunner.isCredentialProviderEnabled(quirks) && credProviderPath; let useCredConfig = ngToolRunner.isCredentialConfigEnabled(quirks) && !useCredProvider; - let authInfo = new auth.NuGetAuthInfo(new auth.InternalAuthInfo(urlPrefixes, accessToken, useCredProvider, useCredConfig), externalAuthArr); + let authInfo = new auth.NuGetExtendedAuthInfo(new auth.InternalAuthInfo(urlPrefixes, accessToken, useCredProvider, useCredConfig), externalAuthArr); let environmentSettings: ngToolRunner.NuGetEnvironmentSettings = { credProviderFolder: useCredProvider ? path.dirname(credProviderPath) : null, @@ -112,7 +110,7 @@ export async function run(nuGetPath: string): Promise { let configFile = null; let apiKey: string; let credCleanup = () => { return }; - let nuGetConfigHelper = new NuGetConfigHelper(nuGetPath, null, authInfo, environmentSettings); + let nuGetConfigHelper = new NuGetConfigHelper2(nuGetPath, null, authInfo, environmentSettings, null); let feedUri: string = undefined; let isInternalFeed: boolean = nugetFeedType === "internal"; @@ -128,7 +126,7 @@ export async function run(nuGetPath: string): Promise { apiKey = "VSTS"; const nuGetVersion: VersionInfo = await peParser.getFileVersionInfoAsync(nuGetPath); - feedUri = await utilities.getNuGetFeedRegistryUrl(accessToken, internalFeedId, nuGetVersion); + feedUri = await nutil.getNuGetFeedRegistryUrl(accessToken, internalFeedId, nuGetVersion); } else { let externalAuth = externalAuthArr[0]; @@ -164,7 +162,7 @@ export async function run(nuGetPath: string): Promise { let verbosity = tl.getInput("verbosityPush"); let continueOnConflict: boolean = tl.getBoolInput("allowPackageConflicts"); - if (continueOnConflict && util.isOnPremisesTfs()) + if (continueOnConflict && commandHelper.isOnPremisesTfs()) { tl.warning(tl.loc("Warning_AllowDuplicatesOnlyAvailableHosted")); } @@ -234,7 +232,7 @@ export async function run(nuGetPath: string): Promise { } } -function publishPackageNuGetAsync(packageFile: string, options: PublishOptions, authInfo: auth.NuGetAuthInfo): Q.Promise { +function publishPackageNuGetAsync(packageFile: string, options: PublishOptions, authInfo: auth.NuGetExtendedAuthInfo): Q.Promise { let nugetTool = ngToolRunner.createNuGetToolRunner(options.nuGetPath, options.environment, authInfo); nugetTool.arg("push"); @@ -293,7 +291,7 @@ function shouldUseVstsNuGetPush(isInternalFeed: boolean, conflictsAllowed: boole return false; } - if (util.isOnPremisesTfs()) + if (commandHelper.isOnPremisesTfs()) { tl.debug('Pushing to an onPrem environment, only NuGet.exe is supported.'); return false; @@ -332,4 +330,3 @@ function shouldUseVstsNuGetPush(isInternalFeed: boolean, conflictsAllowed: boole // NOTE: This should return true once VstsNuGetPush is packaged within the task return false; } - diff --git a/Tasks/NuGetCommand/nugetrestore.ts b/Tasks/NuGetCommand/nugetrestore.ts index cf2dc172494f..c95aad733ed7 100644 --- a/Tasks/NuGetCommand/nugetrestore.ts +++ b/Tasks/NuGetCommand/nugetrestore.ts @@ -3,19 +3,19 @@ import * as path from "path"; import * as Q from "q"; import {IExecOptions} from "vsts-task-lib/toolrunner"; -import * as auth from "./Common/Authentication"; -import { IPackageSource } from "./Common/Authentication"; +import * as auth from "nuget-task-common/Authentication"; +import { IPackageSource } from "nuget-task-common/Authentication"; import INuGetCommandOptions from "./Common/INuGetCommandOptions"; import locationHelpers = require("nuget-task-common/LocationHelpers"); -import {NuGetConfigHelper} from "./Common/NuGetConfigHelper"; +import {NuGetConfigHelper2} from "nuget-task-common/NuGetConfigHelper2"; import nuGetGetter = require("nuget-task-common/NuGetToolGetter"); -import * as ngToolRunner from "./Common/NuGetToolRunner"; +import * as ngToolRunner from "nuget-task-common/NuGetToolRunner2"; import * as nutil from "nuget-task-common/Utility"; import * as vsts from "vso-node-api/WebApi"; import * as vsom from 'vso-node-api/VsoClient'; import peParser = require('nuget-task-common/pe-parser/index'); import {VersionInfo} from "nuget-task-common/pe-parser/VersionResource"; -import * as utilities from "./Common/utilities"; +import * as commandHelper from "nuget-task-common/CommandHelper"; const NUGET_ORG_V2_URL: string = "https://www.nuget.org/api/v2/"; const NUGET_ORG_V3_URL: string = "https://api.nuget.org/v3/index.json"; @@ -28,7 +28,7 @@ class RestoreOptions implements INuGetCommandOptions { public verbosity: string, public packagesDirectory: string, public environment: ngToolRunner.NuGetEnvironmentSettings, - public authInfo: auth.NuGetAuthInfo + public authInfo: auth.NuGetExtendedAuthInfo ) { } } @@ -87,8 +87,8 @@ export async function run(nuGetPath: string): Promise { tl.debug(`All URL prefixes: ${urlPrefixes}`); } let accessToken = auth.getSystemAccessToken(); - let externalAuthArr: auth.ExternalAuthInfo[] = utilities.GetExternalAuthInfoArray("externalEndpoints"); - const authInfo = new auth.NuGetAuthInfo(new auth.InternalAuthInfo(urlPrefixes, accessToken, useCredProvider, useCredConfig), externalAuthArr); + let externalAuthArr: auth.ExternalAuthInfo[] = commandHelper.GetExternalAuthInfoArray("externalEndpoints"); + const authInfo = new auth.NuGetExtendedAuthInfo(new auth.InternalAuthInfo(urlPrefixes, accessToken, useCredProvider, useCredConfig), externalAuthArr); let environmentSettings: ngToolRunner.NuGetEnvironmentSettings = { credProviderFolder: useCredProvider ? path.dirname(credProviderPath) : null, extensionsDisabled: true @@ -108,11 +108,12 @@ export async function run(nuGetPath: string): Promise { } // If there was no nuGetConfigPath, NuGetConfigHelper will create one - let nuGetConfigHelper = new NuGetConfigHelper( + let nuGetConfigHelper = new NuGetConfigHelper2( nuGetPath, nuGetConfigPath, authInfo, - environmentSettings); + environmentSettings, + null); let credCleanup = () => { return; }; @@ -122,7 +123,7 @@ export async function run(nuGetPath: string): Promise { let sources: Array = new Array(); let feed = tl.getInput("feedRestore"); if (feed) { - let feedUrl:string = await utilities.getNuGetFeedRegistryUrl(accessToken, feed, nuGetVersion); + let feedUrl:string = await nutil.getNuGetFeedRegistryUrl(accessToken, feed, nuGetVersion); sources.push( { feedName: feed, diff --git a/Tasks/NuGetCommand/task.json b/Tasks/NuGetCommand/task.json index 8759c2a1ed61..27e22107a2ea 100644 --- a/Tasks/NuGetCommand/task.json +++ b/Tasks/NuGetCommand/task.json @@ -238,7 +238,7 @@ "type": "filePath", "label": "Path to csproj or nuspec file(s) to pack", "defaultValue": "**/*.csproj", - "helpMarkDown": "Pattern to search for csproj or nuspec files to pack.\n\nYou can separate multiple patterns with a semicolon, and you can make a pattern negative by prefixing it with '-:'. Example: `**\\*.csproj;-:**\\*.Tests.csproj`", + "helpMarkDown": "Pattern to search for csproj directories to pack.\n\nYou can separate multiple patterns with a semicolon, and you can make a pattern negative by prefixing it with '-:'. Example: `**\\*.csproj;-:**\\*.Tests.csproj`", "required": true, "visibleRule": "command = pack" }, @@ -408,7 +408,9 @@ "NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", "Info_AvailableVersions": "The available versions are: %s", + "Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", "Info_ResolvedToolFromCache": "Resolved from tool cache: %s", + "Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", "Info_UsingVersion": "Using version: %s", "Info_UsingToolPath": "Using tool path: %s", "Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", @@ -434,10 +436,8 @@ "Warning_FallBackToNuGet": "VstsNuGetPush.exe was not found, falling back to NuGet.exe.", "Info_NoPackagesMatchedTheSearchPattern": "No packages matched the search pattern.", "Warning_ForceNuGetCannotSkipConflicts": "NuGet.exe was forced to be used for publish, if push conflicts occur it will result in a failure.", - "Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", "Error_PushNotARegularFile": "%s is not a file. Check the 'Path/Pattern to nupkg' property of the task.", "Warning_AllowDuplicatesOnlyAvailableHosted": "The 'Allow duplicates to be skipped' option is currently only available on Visual Studio Team Services. If NuGet.exe encounters a conflict, the task will fail.", - "Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", "Error_CommandNotRecognized": "The command %s was not recognized." } diff --git a/Tasks/NuGetCommand/task.loc.json b/Tasks/NuGetCommand/task.loc.json index 72c42b55523c..dfeb78c1b324 100644 --- a/Tasks/NuGetCommand/task.loc.json +++ b/Tasks/NuGetCommand/task.loc.json @@ -402,7 +402,9 @@ "NGCommon_NoSourcesFoundInConfig": "ms-resource:loc.messages.NGCommon_NoSourcesFoundInConfig", "NGCommon_UnabletoDetectNuGetVersion": "ms-resource:loc.messages.NGCommon_UnabletoDetectNuGetVersion", "Info_AvailableVersions": "ms-resource:loc.messages.Info_AvailableVersions", + "Info_MatchingUrlWasFoundSettingAuth": "ms-resource:loc.messages.Info_MatchingUrlWasFoundSettingAuth", "Info_ResolvedToolFromCache": "ms-resource:loc.messages.Info_ResolvedToolFromCache", + "Info_SavingTempConfig": "ms-resource:loc.messages.Info_SavingTempConfig", "Info_UsingVersion": "ms-resource:loc.messages.Info_UsingVersion", "Info_UsingToolPath": "ms-resource:loc.messages.Info_UsingToolPath", "Info_ExpectBehaviorChangeWhenUsingVersionQuery": "ms-resource:loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery", @@ -426,10 +428,8 @@ "Warning_FallBackToNuGet": "ms-resource:loc.messages.Warning_FallBackToNuGet", "Info_NoPackagesMatchedTheSearchPattern": "ms-resource:loc.messages.Info_NoPackagesMatchedTheSearchPattern", "Warning_ForceNuGetCannotSkipConflicts": "ms-resource:loc.messages.Warning_ForceNuGetCannotSkipConflicts", - "Info_MatchingUrlWasFoundSettingAuth": "ms-resource:loc.messages.Info_MatchingUrlWasFoundSettingAuth", "Error_PushNotARegularFile": "ms-resource:loc.messages.Error_PushNotARegularFile", "Warning_AllowDuplicatesOnlyAvailableHosted": "ms-resource:loc.messages.Warning_AllowDuplicatesOnlyAvailableHosted", - "Info_SavingTempConfig": "ms-resource:loc.messages.Info_SavingTempConfig", "Error_CommandNotRecognized": "ms-resource:loc.messages.Error_CommandNotRecognized" } } \ No newline at end of file diff --git a/Tasks/NuGetPublisher/task.loc.json b/Tasks/NuGetPublisher/task.loc.json index d3e917363364..717e79d8cd85 100644 --- a/Tasks/NuGetPublisher/task.loc.json +++ b/Tasks/NuGetPublisher/task.loc.json @@ -154,4 +154,4 @@ "NGCommon_NoSourcesFoundInConfig": "ms-resource:loc.messages.NGCommon_NoSourcesFoundInConfig", "NGCommon_UnabletoDetectNuGetVersion": "ms-resource:loc.messages.NGCommon_UnabletoDetectNuGetVersion" } -} +} \ No newline at end of file