private (string workingDirectory, bool isWorkspaceContained) GetTarWorkingDirectory(string[] segments, string workspaceRoot) { // If path segment is single directory outside of Pipeline.Workspace extract tarball directly to this path if (segments.Count() == 1) { var workingDirectory = segments[0]; if (FingerprintCreator.IsPathySegment(workingDirectory) && !workingDirectory.StartsWith(workspaceRoot)) { return(workingDirectory, false); } } // All other scenarios means that paths must within and relative to Pipeline.Workspace return(workspaceRoot.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), true); }
public async virtual Task RunAsync(AgentTaskPluginExecutionContext context, CancellationToken token) { ArgUtil.NotNull(context, nameof(context)); VariableValue saltValue = context.Variables.GetValueOrDefault(SaltVariableName); string salt = saltValue?.Value ?? string.Empty; VariableValue workspaceRootValue = context.Variables.GetValueOrDefault("pipeline.workspace"); string workspaceRoot = workspaceRootValue?.Value; string key = context.GetInput(PipelineCacheTaskPluginConstants.Key, required: true); string restoreKeysBlock = context.GetInput(PipelineCacheTaskPluginConstants.RestoreKeys, required: false); // TODO: Translate path from container to host (Ting) string path = context.GetInput(PipelineCacheTaskPluginConstants.Path, required: true); (bool isOldFormat, string[] keySegments, IEnumerable <string[]> restoreKeys, string[] pathSegments) = ParseIntoSegments(salt, key, restoreKeysBlock, path); if (isOldFormat) { context.Warning(OldKeyFormatMessage); } context.Output("Resolving key:"); Fingerprint keyFp = FingerprintCreator.EvaluateToFingerprint(context, workspaceRoot, keySegments, FingerprintType.Key); context.Output($"Resolved to: {keyFp}"); Func <Fingerprint[]> restoreKeysGenerator = () => restoreKeys.Select(restoreKey => { context.Output("Resolving restore key:"); Fingerprint f = FingerprintCreator.EvaluateToFingerprint(context, workspaceRoot, restoreKey, FingerprintType.Key); f.Segments = f.Segments.Concat(new[] { Fingerprint.Wildcard }).ToArray(); context.Output($"Resolved to: {f}"); return(f); }).ToArray(); await ProcessCommandInternalAsync( context, keyFp, restoreKeysGenerator, pathSegments, workspaceRoot, token); }
internal async Task UploadAsync( AgentTaskPluginExecutionContext context, Fingerprint keyFingerprint, string[] pathSegments, string workspaceRoot, CancellationToken cancellationToken, ContentFormat contentFormat) { VssConnection connection = context.VssConnection; BlobStoreClientTelemetry clientTelemetry; DedupManifestArtifactClient dedupManifestClient = DedupManifestArtifactClientFactory.Instance.CreateDedupManifestClient(context, connection, cancellationToken, out clientTelemetry); PipelineCacheClient pipelineCacheClient = this.CreateClient(clientTelemetry, context, connection); using (clientTelemetry) { // Check if the key exists. PipelineCacheActionRecord cacheRecordGet = clientTelemetry.CreateRecord <PipelineCacheActionRecord>((level, uri, type) => new PipelineCacheActionRecord(level, uri, type, PipelineArtifactConstants.RestoreCache, context)); PipelineCacheArtifact getResult = await pipelineCacheClient.GetPipelineCacheArtifactAsync(new[] { keyFingerprint }, cancellationToken, cacheRecordGet); // Send results to CustomerIntelligence context.PublishTelemetry(area: PipelineArtifactConstants.AzurePipelinesAgent, feature: PipelineArtifactConstants.PipelineCache, record: cacheRecordGet); //If cache exists, return. if (getResult != null) { context.Output($"Cache with fingerprint `{getResult.Fingerprint}` already exists."); return; } context.Output("Resolving path:"); Fingerprint pathFp = FingerprintCreator.EvaluateToFingerprint(context, workspaceRoot, pathSegments, FingerprintType.Path); context.Output($"Resolved to: {pathFp}"); string uploadPath = await this.GetUploadPathAsync(contentFormat, context, pathFp, pathSegments, workspaceRoot, cancellationToken); //Upload the pipeline artifact. PipelineCacheActionRecord uploadRecord = clientTelemetry.CreateRecord <PipelineCacheActionRecord>((level, uri, type) => new PipelineCacheActionRecord(level, uri, type, nameof(dedupManifestClient.PublishAsync), context)); PublishResult result = await clientTelemetry.MeasureActionAsync( record : uploadRecord, actionAsync : async() => await AsyncHttpRetryHelper.InvokeAsync( async() => { return(await dedupManifestClient.PublishAsync(uploadPath, cancellationToken)); }, maxRetries: 3, tracer: tracer, canRetryDelegate: e => true, // this isn't great, but failing on upload stinks, so just try a couple of times cancellationToken: cancellationToken, continueOnCapturedContext: false) ); CreatePipelineCacheArtifactContract options = new CreatePipelineCacheArtifactContract { Fingerprint = keyFingerprint, RootId = result.RootId, ManifestId = result.ManifestId, ProofNodes = result.ProofNodes.ToArray(), ContentFormat = contentFormat.ToString(), }; // delete archive file if it's tar. if (contentFormat == ContentFormat.SingleTar) { try { if (File.Exists(uploadPath)) { File.Delete(uploadPath); } } catch { } } // Cache the artifact PipelineCacheActionRecord cacheRecord = clientTelemetry.CreateRecord <PipelineCacheActionRecord>((level, uri, type) => new PipelineCacheActionRecord(level, uri, type, PipelineArtifactConstants.SaveCache, context)); CreateStatus status = await pipelineCacheClient.CreatePipelineCacheArtifactAsync(options, cancellationToken, cacheRecord); // Send results to CustomerIntelligence context.PublishTelemetry(area: PipelineArtifactConstants.AzurePipelinesAgent, feature: PipelineArtifactConstants.PipelineCache, record: uploadRecord); context.PublishTelemetry(area: PipelineArtifactConstants.AzurePipelinesAgent, feature: PipelineArtifactConstants.PipelineCache, record: cacheRecord); context.Output("Saved item."); } }