/// <summary>
 ///     Given a downloaded build, determine what the product name should be
 ///     for the release json
 /// </summary>
 /// <param name="build">Downloaded build</param>
 /// <returns>Product name</returns>
 private string GetProductNameForReleaseJson(DownloadedBuild build)
 {
     // Preference the github repo name over the azure devops repo name.
     if (!string.IsNullOrEmpty(build.Build.GitHubRepository))
     {
         // Split off the github.com+org name and just use the repo name, all lower case.
         (string owner, string repo) = GitHubClient.ParseRepoUri(build.Build.GitHubRepository);
         return(repo.ToLowerInvariant());
     }
     else if (!string.IsNullOrEmpty(build.Build.AzureDevOpsRepository))
     {
         // Use the full repo name without project/account
         (string accountName, string projectName, string repoName) = AzureDevOpsClient.ParseRepoUri(build.Build.AzureDevOpsRepository);
         return(repoName.ToLowerInvariant());
     }
     else
     {
         throw new NotImplementedException("Unknown repository name.");
     }
 }
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                // Formalize the directory path.
                string rootOutputPath = Path.GetFullPath(_options.OutputDirectory);
                bool   success        = true;

                // Gather the list of builds that need to be downloaded.
                InputBuilds buildsToDownload = await GatherBuildsToDownloadAsync();

                if (!buildsToDownload.Successful)
                {
                    success = false;
                    if (!_options.ContinueOnError)
                    {
                        return(Constants.ErrorCode);
                    }
                }

                Console.WriteLine();

                List <DownloadedBuild> downloadedBuilds = new List <DownloadedBuild>();

                foreach (var build in buildsToDownload.Builds)
                {
                    DownloadedBuild downloadedBuild = await GatherDropForBuildAsync(build, rootOutputPath);

                    if (!downloadedBuild.Successful)
                    {
                        success = false;
                        Console.WriteLine($"Failed to download build with id {build.Id}");
                        if (!_options.ContinueOnError)
                        {
                            return(Constants.ErrorCode);
                        }
                    }
                    downloadedBuilds.Add(downloadedBuild);
                }

                // Write the unified drop manifest
                await WriteDropManifest(downloadedBuilds, _options.OutputDirectory);

                // Write the release json
                await WriteReleaseJson(downloadedBuilds, _options.OutputDirectory);

                Console.WriteLine();
                if (!success)
                {
                    Console.WriteLine("One or more failures attempting to download the drop, please see output.");
                    return(Constants.ErrorCode);
                }
                else
                {
                    Console.WriteLine("Download successful.");
                    return(Constants.SuccessCode);
                }
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to gather drop.");
                return(Constants.ErrorCode);
            }
        }
        /// <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);
        }
 /// <summary>
 ///     Given a downloaded build, determine what the fileshare location should be.
 /// </summary>
 /// <param name="build">Downloaded build</param>
 /// <returns>File share location</returns>
 public string GetFileShareLocationForReleaseJson(DownloadedBuild build)
 {
     // We only want to have shipping assets in the release json, so append that path
     return(Path.Combine(build.OutputDirectory, shippingSubPath));
 }