Skip to content

Commit

Permalink
Fix downloading build artifact in case build is triggered from forked…
Browse files Browse the repository at this point in the history
… github repos. (#10297)
  • Loading branch information
omeshp authored May 6, 2019
1 parent 08914a7 commit 5c6d88f
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"loc.messages.DownloadArtifacts": "Downloading artifact %s from: %s",
"loc.messages.DownloadingArtifactsForBuild": "Downloading artifacts for build: %s",
"loc.messages.LinkedArtifactCount": "Linked artifacts count: %s",
"loc.messages.ExtractionFailed": "Failed to extract package with error %s",
"loc.messages.FileContainerInvalidArtifactData": "Invalid file container artifact. Resource data must be in the format #/{container id}/path",
"loc.messages.UnsupportedArtifactType": "Unsupported artifact type: %s",
"loc.messages.BuildNotFound": "Build with ID %s not found",
Expand Down
122 changes: 87 additions & 35 deletions Tasks/DownloadBuildArtifactsV0/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import * as engine from 'artifact-engine/Engine';
import * as providers from 'artifact-engine/Providers';
import * as webHandlers from 'artifact-engine/Providers/typed-rest-client/Handlers';

var DecompressZip = require('decompress-zip');

var taskJson = require('./task.json');

tl.setResourcePath(path.join(__dirname, 'task.json'));
Expand Down Expand Up @@ -82,15 +84,11 @@ async function main(): Promise<void> {
var accessToken: string = tl.getEndpointAuthorizationParameter('SYSTEMVSSCONNECTION', 'AccessToken', false);
var credentialHandler: IRequestHandler = getHandlerFromToken(accessToken);
var webApi: WebApi = new WebApi(endpointUrl, credentialHandler);
var debugMode: string = tl.getVariable('System.Debug');
var isVerbose: boolean = debugMode ? debugMode.toLowerCase() != 'false' : false;
var parallelLimit: number = +tl.getInput("parallelizationLimit", false);
var retryLimit = parseInt(tl.getVariable("VSTS_HTTP_RETRY")) ? parseInt(tl.getVariable("VSTS_HTTP_RETRY")) : 4;

var templatePath: string = path.join(__dirname, 'vsts.handlebars.txt');
var buildApi: IBuildApi = await webApi.getBuildApi();
var artifacts = [];
var itemPattern: string = tl.getInput("itemPattern", false) || '**';

if (isCurrentBuild) {
projectId = tl.getVariable("System.TeamProjectId");
Expand Down Expand Up @@ -248,44 +246,62 @@ async function main(): Promise<void> {
if (artifacts) {
var downloadPromises: Array<Promise<any>> = [];
artifacts.forEach(async function (artifact, index, artifacts) {
let downloaderOptions = new engine.ArtifactEngineOptions();
downloaderOptions.itemPattern = itemPattern;
downloaderOptions.verbose = isVerbose;

if (parallelLimit) {
downloaderOptions.parallelProcessingLimit = parallelLimit;
}
let downloaderOptions = configureDownloaderOptions();

if (artifact.resource.type.toLowerCase() === "container") {
let downloader = new engine.ArtifactEngine();
var handler = new webHandlers.PersonalAccessTokenCredentialHandler(accessToken);
var isPullRequestFork = tl.getVariable("SYSTEM.PULLREQUEST.ISFORK");
var isPullRequestForkBool = isPullRequestFork ? isPullRequestFork.toLowerCase() == 'true' : false;

console.log(tl.loc("DownloadingContainerResource", artifact.resource.data));
var containerParts = artifact.resource.data.split('/');
if (isPullRequestForkBool) {
const archiveUrl: string = endpointUrl + "/" + projectId + "/_apis/build/builds/" + buildId + "/artifacts?artifactName=" + artifact.name + "&$format=zip";
console.log(tl.loc("DownloadArtifacts", artifact.name, archiveUrl));

if (containerParts.length < 3) {
throw new Error(tl.loc("FileContainerInvalidArtifactData"));
}

var containerId = parseInt(containerParts[1]);
var containerPath = containerParts.slice(2,containerParts.length).join('/');

if (containerPath == "/") {
//container REST api oddity. Passing '/' as itemPath downloads the first file instead of returning the meta data about the all the files in the root level.
//This happens only if the first item is a file.
containerPath = ""
}
var zipLocation = path.join(downloadPath, artifact.name + ".zip");
await getZipFromUrl(archiveUrl, zipLocation, handler, downloaderOptions);

var itemsUrl = endpointUrl + "/_apis/resources/Containers/" + containerId + "?itemPath=" + encodeURIComponent(containerPath) + "&isShallow=true&api-version=4.1-preview.4";
console.log(tl.loc("DownloadArtifacts", artifact.name, itemsUrl));
var unzipPromise = unzip(zipLocation, downloadPath);
unzipPromise.catch((error) => {
throw error;
});

var variables = {};
var handler = new webHandlers.PersonalAccessTokenCredentialHandler(accessToken);
var webProvider = new providers.WebProvider(itemsUrl, templatePath, variables, handler);
var fileSystemProvider = new providers.FilesystemProvider(downloadPath);
downloadPromises.push(unzipPromise);
await unzipPromise;

downloadPromises.push(downloader.processItems(webProvider, fileSystemProvider, downloaderOptions).catch((reason) => {
reject(reason);
}));
if (tl.exist(zipLocation)) {
tl.rmRF(zipLocation);
}
}
else {
let downloader = new engine.ArtifactEngine();

console.log(tl.loc("DownloadingContainerResource", artifact.resource.data));
var containerParts = artifact.resource.data.split('/');

if (containerParts.length < 3) {
throw new Error(tl.loc("FileContainerInvalidArtifactData"));
}

var containerId = parseInt(containerParts[1]);
var containerPath = containerParts.slice(2,containerParts.length).join('/');

if (containerPath == "/") {
//container REST api oddity. Passing '/' as itemPath downloads the first file instead of returning the meta data about the all the files in the root level.
//This happens only if the first item is a file.
containerPath = ""
}

var itemsUrl = endpointUrl + "/_apis/resources/Containers/" + containerId + "?itemPath=" + encodeURIComponent(containerPath) + "&isShallow=true&api-version=4.1-preview.4";
console.log(tl.loc("DownloadArtifacts", artifact.name, itemsUrl));

var variables = {};
var webProvider = new providers.WebProvider(itemsUrl, templatePath, variables, handler);
var fileSystemProvider = new providers.FilesystemProvider(downloadPath);

downloadPromises.push(downloader.processItems(webProvider, fileSystemProvider, downloaderOptions).catch((reason) => {
reject(reason);
}));
}
}
else if (artifact.resource.type.toLowerCase() === "filepath") {
let downloader = new engine.ArtifactEngine();
Expand Down Expand Up @@ -345,6 +361,42 @@ function executeWithRetriesImplementation(operationName: string, operation: () =
});
}

async function getZipFromUrl(artifactArchiveUrl: string, localPathRoot: string, handler: webHandlers.PersonalAccessTokenCredentialHandler, downloaderOptions: engine.ArtifactEngineOptions) {
var downloader = new engine.ArtifactEngine();
var zipProvider = new providers.ZipProvider(artifactArchiveUrl, handler);
var filesystemProvider = new providers.FilesystemProvider(localPathRoot);

await downloader.processItems(zipProvider, filesystemProvider, downloaderOptions)
}

function configureDownloaderOptions(): engine.ArtifactEngineOptions {
var downloaderOptions = new engine.ArtifactEngineOptions();
downloaderOptions.itemPattern = tl.getInput('itemPattern', false) || "**";
downloaderOptions.parallelProcessingLimit = +tl.getVariable("release.artifact.download.parallellimit") || 8;
var debugMode = tl.getVariable('System.Debug');
downloaderOptions.verbose = debugMode ? debugMode.toLowerCase() != 'false' : false;

return downloaderOptions;
}

export async function unzip(zipLocation: string, unzipLocation: string): Promise<void> {
await new Promise<void>(function (resolve, reject) {
tl.debug('Extracting ' + zipLocation + ' to ' + unzipLocation);

var unzipper = new DecompressZip(zipLocation);
unzipper.on('error', err => {
return reject(tl.loc("ExtractionFailed", err))
});
unzipper.on('extract', log => {
tl.debug('Extracted ' + zipLocation + ' to ' + unzipLocation + ' successfully');
return resolve();
});
unzipper.extract({
path: unzipLocation
});
});
}

main()
.then((result) => tl.setResult(tl.TaskResult.Succeeded, ""))
.catch((err) => {
Expand Down
Loading

0 comments on commit 5c6d88f

Please sign in to comment.