in src/Agent.Plugins/BuildArtifact/BuildArtifactPluginV1.cs [84:335]
protected override async Task ProcessCommandInternalAsync(
AgentTaskPluginExecutionContext context,
CancellationToken token)
{
ArgUtil.NotNull(context, nameof(context));
string artifactName = context.GetInput(TaskProperties.ArtifactName, required: false);
string branchName = context.GetInput(TaskProperties.BranchName, required: false);
string definition = context.GetInput(TaskProperties.Definition, required: false);
string buildType = context.GetInput(TaskProperties.BuildType, required: true);
string specificBuildWithTriggering = context.GetInput(TaskProperties.SpecificBuildWithTriggering, required: false);
string buildVersionToDownload = context.GetInput(TaskProperties.BuildVersionToDownload, required: false);
string targetPath = context.GetInput(TaskProperties.DownloadPath, required: true);
string cleanDestinationFolder = context.GetInput(TaskProperties.CleanDestinationFolder, required: false);
string environmentBuildId = context.Variables.GetValueOrDefault(BuildVariables.BuildId)?.Value ?? string.Empty; // BuildID provided by environment.
string itemPattern = context.GetInput(TaskProperties.ItemPattern, required: false);
string projectName = context.GetInput(TaskProperties.Project, required: false);
string tags = context.GetInput(TaskProperties.Tags, required: false);
string allowPartiallySucceededBuilds = context.GetInput(TaskProperties.AllowPartiallySucceededBuilds, required: false);
string allowFailedBuilds = context.GetInput(TaskProperties.AllowFailedBuilds, required: false);
string allowCanceledBuilds = context.GetInput(TaskProperties.AllowCanceledBuilds, required: false);
string userSpecifiedBuildId = context.GetInput(TaskProperties.BuildId, required: false);
string defaultWorkingDirectory = context.Variables.GetValueOrDefault("system.defaultworkingdirectory").Value;
string downloadType = context.GetInput(TaskProperties.DownloadType, required: true);
// advanced
string retryDownloadCount = context.GetInput(TaskProperties.RetryDownloadCount, required: false);
string parallelizationLimit = context.GetInput(TaskProperties.ParallelizationLimit, required: false);
string checkDownloadedFiles = context.GetInput(TaskProperties.CheckDownloadedFiles, required: false);
string extractTars = context.GetInput(TaskProperties.ExtractTars, required: false);
string extractedTarsTempPath = Path.Combine(context.Variables.GetValueOrDefault("Agent.TempDirectory")?.Value, extractedTarsTempDir);
targetPath = Path.IsPathFullyQualified(targetPath) ? targetPath : Path.GetFullPath(Path.Combine(defaultWorkingDirectory, targetPath));
string[] minimatchPatterns = itemPattern.Split(
new[] { "\n" },
StringSplitOptions.RemoveEmptyEntries
);
string[] tagsInput = tags.Split(
new[] { "," },
StringSplitOptions.None
);
if (!bool.TryParse(allowPartiallySucceededBuilds, out var allowPartiallySucceededBuildsBool))
{
allowPartiallySucceededBuildsBool = false;
}
if (!bool.TryParse(allowFailedBuilds, out var allowFailedBuildsBool))
{
allowFailedBuildsBool = false;
}
if (!bool.TryParse(allowCanceledBuilds, out var allowCanceledBuildsBool))
{
allowCanceledBuildsBool = false;
}
if (!bool.TryParse(cleanDestinationFolder, out var cleanDestinationFolderBool))
{
cleanDestinationFolderBool = false;
}
var resultFilter = GetResultFilter(allowPartiallySucceededBuildsBool, allowFailedBuildsBool, allowCanceledBuildsBool);
if (!bool.TryParse(extractTars, out var extractTarsBool))
{
extractTarsBool = false;
}
if (extractTarsBool && PlatformUtil.RunningOnWindows)
{
throw new ArgumentException(StringUtil.Loc("TarExtractionNotSupportedInWindows"));
}
PipelineArtifactServer server = new PipelineArtifactServer(tracer);
ArtifactDownloadParameters downloadParameters;
if (buildType == buildTypeCurrent)
{
// TODO: use a constant for project id, which is currently defined in Microsoft.VisualStudio.Services.Agent.Constants.Variables.System.TeamProjectId (Ting)
string projectIdStr = context.Variables.GetValueOrDefault("system.teamProjectId")?.Value;
if (String.IsNullOrEmpty(projectIdStr))
{
throw new ArgumentNullException(StringUtil.Loc("CannotBeNullOrEmpty"), "Project ID");
}
Guid projectId = Guid.Parse(projectIdStr);
ArgUtil.NotEmpty(projectId, nameof(projectId));
int pipelineId = 0;
if (int.TryParse(environmentBuildId, out pipelineId) && pipelineId != 0)
{
OutputBuildInfo(context, pipelineId);
}
else
{
string hostType = context.Variables.GetValueOrDefault("system.hosttype")?.Value;
if (string.Equals(hostType, "Release", StringComparison.OrdinalIgnoreCase) ||
string.Equals(hostType, "DeploymentGroup", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(StringUtil.Loc("BuildIdIsNotAvailable", hostType ?? string.Empty, hostType ?? string.Empty));
}
else if (!string.Equals(hostType, "Build", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(StringUtil.Loc("CannotDownloadFromCurrentEnvironment", hostType ?? string.Empty));
}
else
{
// This should not happen since the build id comes from build environment. But a user may override that so we must be careful.
throw new ArgumentException(StringUtil.Loc("BuildIdIsNotValid", environmentBuildId));
}
}
downloadParameters = new ArtifactDownloadParameters
{
ProjectRetrievalOptions = BuildArtifactRetrievalOptions.RetrieveByProjectId,
ProjectId = projectId,
PipelineId = pipelineId,
ArtifactName = artifactName,
TargetDirectory = targetPath,
MinimatchFilters = minimatchPatterns,
MinimatchFilterWithArtifactName = true,
ParallelizationLimit = int.TryParse(parallelizationLimit, out var parallelLimit) ? parallelLimit : 8,
RetryDownloadCount = int.TryParse(retryDownloadCount, out var retryCount) ? retryCount : 4,
CheckDownloadedFiles = bool.TryParse(checkDownloadedFiles, out var checkDownloads) && checkDownloads,
CustomMinimatchOptions = minimatchOptions,
ExtractTars = extractTarsBool,
ExtractedTarsTempPath = extractedTarsTempPath
};
}
else if (buildType == buildTypeSpecific)
{
if (String.IsNullOrEmpty(projectName))
{
throw new ArgumentNullException(StringUtil.Loc("CannotBeNullOrEmpty"), "Project Name");
}
Guid projectId;
bool isProjGuid = Guid.TryParse(projectName, out projectId);
if (!isProjGuid)
{
projectId = await GetProjectIdAsync(context, projectName);
}
// Set the default pipelineId to 0, which is an invalid build id and it has to be reassigned to a valid build id.
int pipelineId = 0;
if (bool.TryParse(specificBuildWithTriggering, out var specificBuildWithTriggeringBool) && specificBuildWithTriggeringBool)
{
string hostType = context.Variables.GetValueOrDefault("system.hostType")?.Value;
string triggeringPipeline = null;
if (!string.IsNullOrWhiteSpace(hostType) && !hostType.Equals("build", StringComparison.OrdinalIgnoreCase)) // RM env.
{
var releaseAlias = context.Variables.GetValueOrDefault("release.triggeringartifact.alias")?.Value;
var definitionIdTriggered = context.Variables.GetValueOrDefault("release.artifacts." + releaseAlias ?? string.Empty + ".definitionId")?.Value;
if (!string.IsNullOrWhiteSpace(definitionIdTriggered) && definitionIdTriggered.Equals(definition, StringComparison.OrdinalIgnoreCase))
{
triggeringPipeline = context.Variables.GetValueOrDefault("release.artifacts." + releaseAlias ?? string.Empty + ".buildId")?.Value;
}
var triggeredProjectIdStr = context.Variables.GetValueOrDefault("release.artifacts." + releaseAlias + ".projectId")?.Value;
if (!string.IsNullOrWhiteSpace(triggeredProjectIdStr) && Guid.TryParse(triggeredProjectIdStr, out var triggeredProjectId))
{
projectId = triggeredProjectId;
}
}
else
{
var definitionIdTriggered = context.Variables.GetValueOrDefault("build.triggeredBy.definitionId")?.Value;
if (!string.IsNullOrWhiteSpace(definitionIdTriggered) && definitionIdTriggered.Equals(definition, StringComparison.OrdinalIgnoreCase))
{
triggeringPipeline = context.Variables.GetValueOrDefault("build.triggeredBy.buildId")?.Value;
}
var triggeredProjectIdStr = context.Variables.GetValueOrDefault("build.triggeredBy.projectId")?.Value;
if (!string.IsNullOrWhiteSpace(triggeredProjectIdStr) && Guid.TryParse(triggeredProjectIdStr, out var triggeredProjectId))
{
projectId = triggeredProjectId;
}
}
if (!string.IsNullOrWhiteSpace(triggeringPipeline))
{
pipelineId = int.Parse(triggeringPipeline);
}
}
if (pipelineId == 0)
{
if (buildVersionToDownload == buildVersionToDownloadLatest)
{
pipelineId = await this.GetPipelineIdAsync(context, definition, buildVersionToDownload, projectId.ToString(), tagsInput, resultFilter, null, cancellationToken: token);
}
else if (buildVersionToDownload == buildVersionToDownloadSpecific)
{
bool isPipelineIdNum = Int32.TryParse(userSpecifiedBuildId, out pipelineId);
if (!isPipelineIdNum)
{
throw new ArgumentException(StringUtil.Loc("RunIDNotValid", userSpecifiedBuildId));
}
}
else if (buildVersionToDownload == buildVersionToDownloadLatestFromBranch)
{
pipelineId = await this.GetPipelineIdAsync(context, definition, buildVersionToDownload, projectId.ToString(), tagsInput, resultFilter, branchName, cancellationToken: token);
}
else
{
throw new InvalidOperationException("Unreachable code!");
}
}
OutputBuildInfo(context, pipelineId);
downloadParameters = new ArtifactDownloadParameters
{
ProjectRetrievalOptions = BuildArtifactRetrievalOptions.RetrieveByProjectName,
ProjectName = projectName,
ProjectId = projectId,
PipelineId = pipelineId,
ArtifactName = artifactName,
TargetDirectory = targetPath,
MinimatchFilters = minimatchPatterns,
MinimatchFilterWithArtifactName = true,
ParallelizationLimit = int.TryParse(parallelizationLimit, out var parallelLimit) ? parallelLimit : 8,
RetryDownloadCount = int.TryParse(retryDownloadCount, out var retryCount) ? retryCount : 4,
CheckDownloadedFiles = bool.TryParse(checkDownloadedFiles, out var checkDownloads) && checkDownloads,
CustomMinimatchOptions = minimatchOptions,
ExtractTars = extractTarsBool,
ExtractedTarsTempPath = extractedTarsTempPath
};
}
else
{
throw new InvalidOperationException($"Build type '{buildType}' is not recognized.");
}
string fullPath = this.CreateDirectoryIfDoesntExist(targetPath);
if (cleanDestinationFolderBool)
{
CleanDirectory(context, fullPath);
}
var downloadOption = downloadType == "single" ? DownloadOptions.SingleDownload : DownloadOptions.MultiDownload;
// Build artifacts always includes the artifact in the path name
downloadParameters.IncludeArtifactNameInPath = true;
// By default, file container provider appends artifact name to target path when downloading specific files.
// This is undesirable because DownloadBuildArtifactsV0 doesn't do that.
// We also have a blob to enable appending artifact name just in case we break someone.
// By default, its value is going to be false, so we're defaulting to V0-like target path resolution.
downloadParameters.AppendArtifactNameToTargetPath =
AgentKnobs.EnableIncompatibleBuildArtifactsPathResolution.GetValue(context).AsBoolean();
context.Output(StringUtil.Loc("DownloadArtifactTo", targetPath));
await server.DownloadAsyncV2(context, downloadParameters, downloadOption, token);
context.Output(StringUtil.Loc("DownloadArtifactFinished"));
}