/// <summary> /// Gather the drop for a specific build. /// </summary> /// <param name="build">Build to gather drop for</param> /// <param name="rootOutputDirectory">Output directory. Must exist.</param> private async Task <DownloadedBuild> GatherDropForBuildAsync(Build build, string rootOutputDirectory) { IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger); bool success = true; // If the drop is separated, calculate the directory name based on the last element of the build // repo uri plus the build number (to disambiguate overlapping builds) string outputDirectory = rootOutputDirectory; string repoUri = build.GitHubRepository ?? build.AzureDevOpsRepository; if (_options.ReleaseLayout) { int lastSlash = repoUri.LastIndexOf("/"); if (lastSlash != -1 && lastSlash != repoUri.Length - 1) { outputDirectory = Path.Combine(rootOutputDirectory, repoUri.Substring(lastSlash + 1), build.AzureDevOpsBuildNumber); } else { // Might contain invalid path chars, this is currently unhandled. outputDirectory = Path.Combine(rootOutputDirectory, repoUri, build.AzureDevOpsBuildNumber); } Directory.CreateDirectory(outputDirectory); } List <DownloadedAsset> downloadedAssets = new List <DownloadedAsset>(); bool anyShipping = false; Console.WriteLine($"Gathering drop for build {build.AzureDevOpsBuildNumber} of {repoUri}"); using (HttpClient client = new HttpClient(new HttpClientHandler { CheckCertificateRevocationList = true })) { var assets = await remote.GetAssetsAsync(buildId : build.Id, nonShipping : (!_options.IncludeNonShipping ? (bool?)false : null)); foreach (var asset in assets) { DownloadedAsset downloadedAsset = await DownloadAssetAsync(client, build, asset, outputDirectory); if (downloadedAsset == null) { continue; } else if (!downloadedAsset.Successful) { success = false; if (!_options.ContinueOnError) { Console.WriteLine($"Aborting download."); break; } } else { anyShipping |= !asset.NonShipping; downloadedAssets.Add(downloadedAsset); } } } DownloadedBuild newBuild = new DownloadedBuild { Successful = success, Build = build, DownloadedAssets = downloadedAssets, OutputDirectory = outputDirectory, AnyShippingAssets = anyShipping }; // If separated drop, generate a manifest per build if (_options.ReleaseLayout) { await WriteDropManifest(new List <DownloadedBuild>() { newBuild }, outputDirectory); } return(newBuild); }
public override async Task <int> ExecuteAsync() { IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger); try { Channel targetChannel = null; if (!string.IsNullOrEmpty(_options.Channel)) { targetChannel = await UxHelpers.ResolveSingleChannel(remote, _options.Channel); if (targetChannel == null) { return(Constants.ErrorCode); } } // Starting with the remote, get information on the asset name + version List <Asset> matchingAssets = (await remote.GetAssetsAsync(name: _options.Name, version: _options.Version)).ToList(); string queryDescriptionString = $"name '{_options.Name}'{(!string.IsNullOrEmpty(_options.Version) ? $" and version '{_options.Version}'" : "")}" + $"{(targetChannel != null ? $" on channel '{targetChannel.Name}'" : "")} in the last {_options.MaxAgeInDays} days"; // Only print the lookup string if the output type is text. if (_options.OutputFormat == DarcOutputType.text) { Console.WriteLine($"Looking up assets with {queryDescriptionString}"); } // Walk the assets and look up the corresponding builds, potentially filtering based on channel // if there is a target channel int maxAgeInDays = _options.MaxAgeInDays; var now = DateTimeOffset.Now; int checkedAssets = 0; List <(Asset asset, Build build)> matchingAssetsAfterDate = new List <(Asset, Build)>(); foreach (Asset asset in matchingAssets) { // Get build info for asset Build buildInfo = await remote.GetBuildAsync(asset.BuildId); if (now.Subtract(buildInfo.DateProduced).TotalDays > maxAgeInDays) { break; } checkedAssets++; if (targetChannel != null && !buildInfo.Channels.Any(c => c.Id == targetChannel.Id)) { continue; } matchingAssetsAfterDate.Add((asset, buildInfo)); } if (!matchingAssetsAfterDate.Any()) { Console.WriteLine($"No assets found with {queryDescriptionString}"); int remaining = matchingAssets.Count - checkedAssets; if (remaining > 0) { Console.WriteLine($"Skipping build lookup for {remaining} assets. Consider increasing --max-age to check the rest."); } return(Constants.ErrorCode); } switch (_options.OutputFormat) { case DarcOutputType.text: foreach ((Asset asset, Build build) in matchingAssetsAfterDate) { Console.WriteLine($"{asset.Name} @ {asset.Version}"); Console.Write(UxHelpers.GetTextBuildDescription(build)); Console.WriteLine("Locations:"); if (asset.Locations.Any()) { foreach (var location in asset.Locations) { if (location.IsValid) { Console.WriteLine($"- {location.Location} ({location.Type})"); } } } else { Console.WriteLine("- None"); } Console.WriteLine(); } break; case DarcOutputType.json: var assets = matchingAssetsAfterDate.Select(assetAndBuild => { return(new { name = assetAndBuild.asset.Name, version = assetAndBuild.asset.Version, build = UxHelpers.GetJsonBuildDescription(assetAndBuild.build), locations = assetAndBuild.asset.Locations.Select(location => location.Location) }); }); Console.WriteLine(JsonConvert.SerializeObject(assets, Formatting.Indented)); break; } return(Constants.SuccessCode); } catch (AuthenticationException e) { Console.WriteLine(e.Message); return(Constants.ErrorCode); } catch (Exception e) { Logger.LogError(e, "Error: Failed to retrieve information about assets."); return(Constants.ErrorCode); } }
public override async Task <int> ExecuteAsync() { IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger); try { Channel targetChannel = null; if (!string.IsNullOrEmpty(_options.Channel)) { targetChannel = await UxHelpers.ResolveSingleChannel(remote, _options.Channel); if (targetChannel == null) { return(Constants.ErrorCode); } } // Starting with the remote, get information on the asset name + version List <Asset> matchingAssets = (await remote.GetAssetsAsync(name: _options.Name, version: _options.Version)).ToList(); string queryDescriptionString = $"name '{_options.Name}'{(!string.IsNullOrEmpty(_options.Version) ? $"and version '{_options.Version}'" : "")}" + $"{(targetChannel != null ? $" on channel '{targetChannel.Name}'" : "")} in the last {_options.MaxAgeInDays} days"; Console.WriteLine($"Looking up assets with {queryDescriptionString}"); // Walk the assets and look up the corresponding builds, potentially filtering based on channel // if there is a target channel bool foundMatching = false; int maxAgeInDays = _options.MaxAgeInDays; var now = DateTimeOffset.Now; int checkedAssets = 0; foreach (var asset in matchingAssets) { checkedAssets++; // Get build info for asset Build buildInfo = await remote.GetBuildAsync(asset.BuildId); if (targetChannel != null && !buildInfo.Channels.Any(c => c.Id == targetChannel.Id)) { continue; } if (now.Subtract(buildInfo.DateProduced).TotalDays > maxAgeInDays) { break; } foundMatching = true; Console.WriteLine($"{asset.Name} @ {asset.Version}"); Console.Write(UxHelpers.GetBuildDescription(buildInfo)); Console.WriteLine("Locations:"); if (asset.Locations.Any()) { foreach (var location in asset.Locations) { if (location.IsValid) { Console.WriteLine($"- {location.Location} ({location.Type})"); } } } else { Console.WriteLine("- None"); } Console.WriteLine(); } if (!foundMatching) { Console.WriteLine($"No assets found with {queryDescriptionString}"); int remaining = matchingAssets.Count - checkedAssets; if (remaining > 0) { Console.WriteLine($"Skipping build lookup for {remaining} assets. Consider increasing --max-age to check the rest."); } } return(Constants.SuccessCode); } catch (Exception e) { Logger.LogError(e, "Error: Failed to retrieve information about assets."); return(Constants.ErrorCode); } }