-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b72e3fa
commit dafc900
Showing
65 changed files
with
3,317 additions
and
668 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import {NuGetEnvironmentSettings} from "./NuGetToolRunner"; | ||
|
||
export interface INuGetCommandOptions { | ||
/** settings used to initialize the environment NuGet.exe is invoked in */ | ||
environment: NuGetEnvironmentSettings; | ||
/** full path to NuGet.exe */ | ||
nuGetPath: string; | ||
/** path to the NuGet config file. Passed as the -ConfigFile argument. */ | ||
configFile: string; | ||
} | ||
|
||
export default INuGetCommandOptions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import * as url from "url"; | ||
import * as Q from "q"; | ||
import * as tl from "vsts-task-lib/task"; | ||
|
||
import * as auth from "./Authentication"; | ||
import { IPackageSource } from "./Authentication"; | ||
import * as ngToolRunner from "./NuGetToolRunner"; | ||
|
||
let xmlreader = require("xmlreader"); | ||
|
||
|
||
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"; | ||
public tempNugetConfigPath = undefined; | ||
|
||
constructor( | ||
private nugetPath: string, | ||
private nugetConfigPath: string, | ||
private authInfo: auth.NuGetAuthInfo, | ||
private environmentSettings: ngToolRunner.NuGetEnvironmentSettings) | ||
{ | ||
} | ||
|
||
public ensureTempConfigCreated() { | ||
// save nuget config file to agent build directory | ||
console.log("save nuget.config to temp config file"); | ||
if (!(fs.existsSync(this.tempNugetConfigDir))) { | ||
fs.mkdirSync(this.tempNugetConfigDir); | ||
} | ||
|
||
this.tempNugetConfigPath = path.join(this.tempNugetConfigDir, this.tempNugetConfigFileName); | ||
|
||
if (!fs.existsSync(this.tempNugetConfigPath)) | ||
{ | ||
if (this.nugetConfigPath) { | ||
// don't use cp as that copies the read-only flag, and tfvc sets that on files | ||
let content = fs.readFileSync(this.nugetConfigPath); | ||
fs.writeFileSync(this.tempNugetConfigPath, content); | ||
} | ||
else { | ||
// small file, use writeFileSync | ||
fs.writeFileSync(this.tempNugetConfigPath, "<configuration/>"); | ||
} | ||
} | ||
} | ||
|
||
public addSourcesToTempNuGetConfig(packageSources: IPackageSource[]): void | ||
{ | ||
tl.debug('Adding sources to nuget.config'); | ||
this.ensureTempConfigCreated(); | ||
this.addSourcesToTempNugetConfigInternal(packageSources); | ||
} | ||
|
||
public async setAuthForSourcesInTempNuGetConfigAsync(): Promise<void> | ||
{ | ||
tl.debug('Setting auth in the temp nuget.config'); | ||
|
||
let sources = await this.getSourcesFromTempNuGetConfig(); | ||
if (sources.length < 1) | ||
{ | ||
tl.debug('Not setting up auth for temp nuget.config as there are no sources'); | ||
return; | ||
} | ||
|
||
this.ensureTempConfigCreated(); | ||
sources.forEach((source) => { | ||
if (source.isInternal) | ||
{ | ||
if(this.authInfo.internalAuthInfo.useCredConfig) | ||
{ | ||
tl.debug('Setting auth for internal source ' + source.feedUri); | ||
// Removing source first | ||
this.removeSourceFromTempNugetConfig(source); | ||
// Re-adding source with creds | ||
this.addSourceWithUsernamePasswordToTempNuGetConfig(source, "VssSessionToken", this.authInfo.internalAuthInfo.accessToken); | ||
} | ||
} | ||
// Source is external | ||
else | ||
{ | ||
if (!this.authInfo.externalAuthInfo || this.authInfo.externalAuthInfo.length < 1) | ||
{ | ||
return; | ||
} | ||
|
||
let indexAuthInfo: number = this.authInfo.externalAuthInfo.findIndex(externalEndpoint => url.parse(externalEndpoint.packageSource.feedUri).href.toLowerCase() === url.parse(source.feedUri).href.toLowerCase()); | ||
if(indexAuthInfo > -1) | ||
{ | ||
let externalEndpointAuthInfo: auth.ExternalAuthInfo = this.authInfo.externalAuthInfo[indexAuthInfo]; | ||
tl.debug('Setting auth for external source ' + source.feedUri); | ||
console.log(tl.loc("Info_MatchingUrlWasFoundSettingAuth") + source.feedUri); | ||
switch(externalEndpointAuthInfo.authType) | ||
{ | ||
case (auth.ExternalAuthType.UsernamePassword): | ||
let usernamePwdAuthInfo = externalEndpointAuthInfo as auth.UsernamePasswordExternalAuthInfo; | ||
this.removeSourceFromTempNugetConfig(source); | ||
this.addSourceWithUsernamePasswordToTempNuGetConfig(source, usernamePwdAuthInfo.username, usernamePwdAuthInfo.password); | ||
break; | ||
case (auth.ExternalAuthType.Token): | ||
let tokenAuthInfo = externalEndpointAuthInfo as auth.TokenExternalAuthInfo; | ||
this.removeSourceFromTempNugetConfig(source); | ||
this.addSourceWithUsernamePasswordToTempNuGetConfig(source, "CustomToken", tokenAuthInfo.token); | ||
break; | ||
case (auth.ExternalAuthType.ApiKey): | ||
let apiKeyAuthInfo = externalEndpointAuthInfo as auth.ApiKeyExternalAuthInfo; | ||
this.setApiKeyForSourceInTempNuGetConfig(source, apiKeyAuthInfo.apiKey); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
|
||
private getSourcesFromTempNuGetConfig(): Q.Promise<IPackageSource[]> { | ||
// load content of the user's nuget.config | ||
let configPath: string = this.tempNugetConfigPath ? this.tempNugetConfigPath : this.nugetConfigPath; | ||
|
||
if (!configPath) | ||
{ | ||
return Q.resolve([]); | ||
} | ||
|
||
tl.debug('Getting sources from NuGet.config in this location: ' + configPath); | ||
|
||
let xmlString = fs.readFileSync(configPath).toString(); | ||
|
||
// strip BOM; xml parser doesn't like it | ||
if (xmlString.charCodeAt(0) === 0xFEFF) { | ||
xmlString = xmlString.substr(1); | ||
} | ||
|
||
// get package sources | ||
return Q.nfcall<any>(xmlreader.read, xmlString) | ||
.then(configXml => { | ||
let packageSources = []; | ||
let packageSource: IPackageSource; | ||
let sourceKey; | ||
let sourceValue; | ||
|
||
// give clearer errors if the user has set an invalid nuget.config | ||
if (!configXml.configuration) { | ||
if (configXml.packages) { | ||
throw new Error(tl.loc( | ||
"NGCommon_NuGetConfigIsPackagesConfig", | ||
this.nugetConfigPath, | ||
tl.getVariable("Task.DisplayName"))); | ||
} | ||
else { | ||
throw new Error(tl.loc("NGCommon_NuGetConfigIsInvalid", this.nugetConfigPath)); | ||
} | ||
} | ||
|
||
if (!configXml.configuration.packageSources || !configXml.configuration.packageSources.add) { | ||
tl.warning(tl.loc("NGCommon_NoSourcesFoundInConfig", this.nugetConfigPath)); | ||
return []; | ||
} | ||
|
||
for (let i = 0; i < configXml.configuration.packageSources.add.count(); i++) { | ||
sourceKey = configXml.configuration.packageSources.add.at(i).attributes().key; | ||
sourceValue = configXml.configuration.packageSources.add.at(i).attributes().value; | ||
if (!sourceKey || !sourceValue) { | ||
continue; | ||
} | ||
|
||
packageSource = { feedName: sourceKey, feedUri: sourceValue, isInternal: false }; | ||
let isInternalFeed: boolean = this.shouldGetCredentialsForFeed(packageSource); | ||
packageSource.isInternal = isInternalFeed; | ||
packageSources.push(packageSource); | ||
} | ||
|
||
return packageSources; | ||
}); | ||
} | ||
|
||
private removeSourceFromTempNugetConfig(packageSource: IPackageSource) { | ||
let nugetTool = ngToolRunner.createNuGetToolRunner(this.nugetPath, this.environmentSettings, this.authInfo); | ||
|
||
nugetTool.arg("sources"); | ||
nugetTool.arg("Remove"); | ||
nugetTool.arg("-NonInteractive"); | ||
nugetTool.arg("-Name"); | ||
nugetTool.arg(packageSource.feedName); | ||
nugetTool.arg("-ConfigFile"); | ||
nugetTool.arg(this.tempNugetConfigPath); | ||
|
||
// short run, use execSync | ||
nugetTool.execSync(); | ||
} | ||
|
||
|
||
private addSourcesToTempNugetConfigInternal(packageSources: IPackageSource[]) { | ||
packageSources.forEach((source) => { | ||
let nugetTool = ngToolRunner.createNuGetToolRunner(this.nugetPath, this.environmentSettings, this.authInfo); | ||
|
||
nugetTool.arg("sources"); | ||
nugetTool.arg("Add"); | ||
nugetTool.arg("-NonInteractive"); | ||
nugetTool.arg("-Name"); | ||
nugetTool.arg(source.feedName); | ||
nugetTool.arg("-Source"); | ||
nugetTool.arg(source.feedUri); | ||
nugetTool.arg("-ConfigFile"); | ||
nugetTool.arg(this.tempNugetConfigPath); | ||
|
||
// short run, use execSync | ||
nugetTool.execSync(); | ||
}); | ||
} | ||
|
||
private addSourceWithUsernamePasswordToTempNuGetConfig(source: IPackageSource, username: string, password: string) | ||
{ | ||
let nugetTool = ngToolRunner.createNuGetToolRunner(this.nugetPath, this.environmentSettings, this.authInfo); | ||
nugetTool.arg("sources"); | ||
nugetTool.arg("Add"); | ||
nugetTool.arg("-NonInteractive"); | ||
nugetTool.arg("-Name"); | ||
nugetTool.arg(source.feedName); | ||
nugetTool.arg("-Source"); | ||
nugetTool.arg(source.feedUri); | ||
nugetTool.arg("-ConfigFile"); | ||
nugetTool.arg(this.tempNugetConfigPath); | ||
nugetTool.arg("-Username"); | ||
nugetTool.arg(username); | ||
nugetTool.arg("-Password"); | ||
nugetTool.arg(password); | ||
|
||
if (tl.osType() !== 'Windows_NT') { | ||
// only Windows supports DPAPI. Older NuGets fail to add credentials at all if DPAPI fails. | ||
nugetTool.arg("-StorePasswordInClearText"); | ||
} | ||
|
||
// short run, use execSync | ||
nugetTool.execSync(); | ||
} | ||
|
||
private setApiKeyForSourceInTempNuGetConfig(source: IPackageSource, apiKey: string) | ||
{ | ||
let nugetTool = ngToolRunner.createNuGetToolRunner(this.nugetPath, this.environmentSettings, this.authInfo); | ||
nugetTool.arg("setapikey"); | ||
nugetTool.arg(apiKey); | ||
nugetTool.arg("-NonInteractive"); | ||
nugetTool.arg("-Source"); | ||
nugetTool.arg(source.feedUri); | ||
nugetTool.arg("-ConfigFile"); | ||
nugetTool.arg(this.tempNugetConfigPath); | ||
|
||
// short run, use execSync | ||
nugetTool.execSync(); | ||
} | ||
|
||
private shouldGetCredentialsForFeed(source: IPackageSource): boolean { | ||
let uppercaseUri = source.feedUri.toUpperCase(); | ||
return this.authInfo.internalAuthInfo.uriPrefixes.some(prefix => uppercaseUri.indexOf(prefix.toUpperCase()) === 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Placed as a separate file for the purpose of unit testing | ||
|
||
export function getUtcDateString(): string { | ||
let now: Date = new Date(); | ||
return `${now.getFullYear()}${now.getUTCMonth()}${now.getUTCDate()}-${now.getUTCHours()}${now.getUTCMinutes()}${now.getUTCSeconds()}`; | ||
} |
Oops, something went wrong.