/// <summary>
        ///
        /// </summary>
        /// <param name="job"></param>
        /// <param name="build"></param>
        /// <param name="artifactRegex"></param>
        /// <returns></returns>
        protected Artifact FindDefaultArtifactForBuild(
            Job job,
            Build build,
            string artifactRegex)
        {
            var artifacts = this.GetArtifactListFromJob(job);

            if (!artifacts.Any())
            {
                throw new Exception($"Requested job '{job.jobId}' from version '{build.version}' has no artifacts.");
            }

            // Try to find a suitable artifact...
            Artifact artifact = null;

            // Search by regex.
            if (!string.IsNullOrWhiteSpace(artifactRegex))
            {
                foreach (var arf in artifacts)
                {
                    string arfname = (string)arf.fileName;

                    if (Regex.IsMatch(arfname, artifactRegex))
                    {
                        if (artifact != null)
                        {
                            throw new Exception($"Ambiguous artifact match for regex: '{artifactRegex}'.");
                        }

                        artifact = arf;
                    }
                }
            }

            if (artifact != null)
            {
                return(artifact);
            }

            // If there is only one artifact use that one.
            if (artifacts.Count() == 1)
            {
                return(artifacts.First());
            }

            // Use an artifact whose name defaults to the project's name
            artifact = (from p in artifacts
                        where string.Equals(p.name, build.project.name, StringComparison.InvariantCultureIgnoreCase)
                        select p).FirstOrDefault();

            if (artifact != null)
            {
                return(artifact);
            }

            // Now use the file name
            artifact = (from p in artifacts
                        where string.Equals(p.fileName, build.project.name + ".zip", StringComparison.InvariantCultureIgnoreCase)
                        select p).FirstOrDefault();

            if (artifact != null)
            {
                return(artifact);
            }

            // Our best bet is to find the artifact with the biggest size...
            artifact = artifacts.OrderByDescending((i) => i.size).Where((i) => string.Equals(i.type, "zip", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();

            if (artifact == null)
            {
                throw new Exception($"Could not find suitable artifact. Regex: '{artifactRegex}'.");
            }

            return(artifact);
        }
        /// <summary>
        /// Downloads (And extracts) single artifacts from jobs.
        /// </summary>
        /// <param name="applicationId"></param>
        /// <param name="build"></param>
        /// <param name="artifactRegex"></param>
        /// <param name="destinationPath"></param>
        /// <param name="logger"></param>
        public void DownloadSingleArtifactFromBuild(
            string applicationId,
            Build build,
            string artifactRegex,
            string destinationPath,
            ILoggerInterface logger)
        {
            UtilsSystem.EnsureDirectoryExists(destinationPath, true);

            // Use the first job in the build...
            var job      = build.jobs.First();
            var artifact = this.FindDefaultArtifactForBuild(job, build, artifactRegex);

            var filename  = artifact.fileName;
            var extension = Path.GetExtension(filename);

            string downloadTemporaryDir =
                UtilsSystem.EnsureDirectoryExists(UtilsSystem.CombinePaths(this.TempDir, "_appveyor", "dld", applicationId), true);

            int artifactRetentionNum     = 5;
            int artifactAgeHoursForStale = 24;

            // Do not touch the latest artifactRetentionNum artifacts or artifacts that are not older than artifactAgeHoursForStale hours
            var staleFiles = Directory.EnumerateFiles(downloadTemporaryDir)
                             .Select((i) => new FileInfo(i))
                             .Where((i) => i.Extension.Equals(".zip", StringComparison.CurrentCultureIgnoreCase))
                             .OrderByDescending((i) => i.CreationTimeUtc)
                             .Skip(artifactRetentionNum)
                             .Where((i) => (DateTime.UtcNow - i.LastWriteTime).TotalHours > artifactAgeHoursForStale)
                             .ToList();

            foreach (var f in staleFiles)
            {
                // Make this fail proof, it's just a cleanup.
                try
                {
                    this.Logger.LogInfo(true, "Removing stale artifact cache file {0}", f.FullName);
                    f.Delete();
                }
                catch
                {
                    // ignored
                }
            }

            // Use a short hash as the temporary file name, because long paths can have issues...
            var tmpFile = UtilsSystem.CombinePaths(downloadTemporaryDir, UtilsEncryption.GetShortHash(JsonConvert.SerializeObject(build) + filename) + extension);

            if (Path.GetExtension(tmpFile)?.ToLower() != ".zip")
            {
                throw new NotImplementedException("AppVeyor artifacts should only be Zip Files.");
            }

            if (!File.Exists(tmpFile))
            {
                // Use an intermediate .tmp file just in case the files does not finish to download,
                // if it exists, clear it.
                string tmpFileDownload = tmpFile + ".tmp";
                if (File.Exists(tmpFileDownload))
                {
                    UtilsSystem.RetryWhile(() => File.Delete(tmpFileDownload), (e) => true, 4000, this.Logger);
                }

                var url = $"/api/buildjobs/{job.jobId}/artifacts/{filename}";
                logger.LogInfo(true, "Downloading artifact from: '{0}' to '{1}'", url, tmpFileDownload);
                this.ExecuteApiCallToFile(url, tmpFileDownload);

                // Rename to the final cached artifact file
                logger.LogInfo(true, "Download succesful, moving to '{0}'", tmpFile);
                UtilsSystem.RetryWhile(() => File.Move(tmpFileDownload, tmpFile), (e) => true, 4000, this.Logger);
            }
            else
            {
                logger.LogInfo(true, "Skipping artifact download, already in local cache: {0}", tmpFile);
            }

            logger.LogInfo(true, "Unzipping {1} file to '{0}'...", destinationPath, UtilsSystem.BytesToString(new FileInfo(tmpFile).Length));

            ZipFile.ExtractToDirectory(tmpFile, destinationPath);

            logger.LogInfo(true, "Unzipping finished.");
        }