/// <summary> /// Gets file path for specific package. /// </summary> protected internal virtual string GetDownloadedFilePath(IPackage package, string cwd) { var uri = new Uri(package.GetDistUri()); var filename = Security.Md5($"{cwd}{package}{package.GetDistReference()}{package.GetDistShasum()}"); return(Path.Combine(GetTempDirectory(), "download", $"{filename}{Path.GetExtension(uri.AbsolutePath)}")); }
/// <summary> /// Dump the package instance to <see cref="ConfigBucket"/>. /// </summary> /// <typeparam name="T">The type of the config struct.</typeparam> /// <param name="package">The package instance.</param> /// <returns>The <see cref="ConfigBucket"/> instance.</returns> public T Dump <T>(IPackage package) where T : ConfigBucketBase, new() { var data = new T { Name = package.GetNamePretty(), Version = package.GetVersionPretty(), VersionNormalized = package.GetVersion(), ReleaseDate = package.GetReleaseDate(), PackageType = package.GetPackageType(), NotificationUri = package.GetNotificationUri(), Binaries = package.GetBinaries().ZeroAsNull(), Archive = package.GetArchives().ZeroAsNull(), Extra = package.GetExtra(), InstallationSource = package.GetInstallationSource(), }; if (!string.IsNullOrEmpty(package.GetSourceType())) { data.Source = new ConfigResource { Type = package.GetSourceType(), Uri = package.GetSourceUri(), Reference = package.GetSourceReference(), Mirrors = package.GetSourceMirrors().ZeroAsNull(), }; } if (!string.IsNullOrEmpty(package.GetDistType())) { data.Dist = new ConfigResource { Type = package.GetDistType(), Uri = package.GetDistUri(), Reference = package.GetDistReference(), Mirrors = package.GetDistMirrors().ZeroAsNull(), Shasum = package.GetDistShasum(), }; } data.Requires = LinksToDictonary(package.GetRequires()); data.RequiresDev = LinksToDictonary(package.GetRequiresDev()); data.Conflicts = LinksToDictonary(package.GetConflicts()); data.Provides = LinksToDictonary(package.GetProvides()); data.Replaces = LinksToDictonary(package.GetReplaces()); var suggests = package.GetSuggests(); if (suggests != null && suggests.Count > 0) { data.Suggests = new SortedDictionary <string, string>(suggests); } if (package is IPackageComplete packageComplete) { data.Licenses = packageComplete.GetLicenses().ZeroAsNull(); data.Authors = packageComplete.GetAuthors().ZeroAsNull(); data.Description = packageComplete.GetDescription(); data.Homepage = packageComplete.GetHomepage(); data.Keywords = packageComplete.GetKeywords().ZeroAsNull(); data.Repositories = packageComplete.GetRepositories().ZeroAsNull(); if (packageComplete.IsDeprecated) { var replacement = packageComplete.GetReplacementPackage(); if (string.IsNullOrEmpty(replacement)) { data.Deprecated = true; } else { data.Deprecated = replacement; } } data.Scripts = packageComplete.GetScripts(); var supports = packageComplete.GetSupport(); if (supports != null && supports.Count > 0) { data.Support = new SortedDictionary <string, string>(supports); } } if (package is IPackageRoot packageRoot) { data.MinimumStability = packageRoot.GetMinimumStability(); data.Platforms = packageRoot.GetPlatforms(); } if (data.Keywords != null) { Array.Sort(data.Keywords); } return(data); }
/// <inheritdoc cref="IDownloader.Download(IPackage, string)" /> public virtual Task Download(IPackage package, string cwd, bool output) { if (string.IsNullOrEmpty(package.GetDistUri())) { throw new RuntimeException("The given package is missing url information."); } var downloadedFilePath = GetDownloadedFilePath(package, cwd); fileSystem.Delete(cwd); void DoDownload(string uri) { var processedUri = ProcessUri(package, uri); if (eventDispatcher != null) { var preFileDownloadEvent = new PreFileDownloadEventArgs(PluginEvents.PreFileDownload, transport, processedUri); eventDispatcher.Dispatch(this, preFileDownloadEvent); } try { var checksum = package.GetDistShasum(); var cacheKey = GetCacheKey(package, processedUri); void DownloadFromHttp(int retries = 3) { SException processException = null; while (retries-- > 0) { try { transport.Copy( processedUri, downloadedFilePath, new ReportDownloadProgress(io, $"{GetDownloadingPrompt(package)}: ")); break; } catch (TransportException ex) { // clean up immediately. otherwise it will lead to failure forever. fileSystem.Delete(downloadedFilePath); // if we got an http response with a proper code, then // requesting again will probably not help, abort. var serverProblems = new[] { HttpStatusCode.InternalServerError, HttpStatusCode.BadGateway, HttpStatusCode.ServiceUnavailable, HttpStatusCode.GatewayTimeout, }; if (ex.HttpStatusCode != 0 && (!Array.Exists(serverProblems, (code) => code == ex.HttpStatusCode) || retries <= 0)) { throw; } processException = ex; Thread.Sleep(500); } } if (processException != null) { ExceptionDispatchInfo.Capture(processException).Throw(); } if (cache != null && cache.CopyFrom(cacheKey, downloadedFilePath)) { lastCacheWrites[package.GetName()] = cacheKey; } } // use from cache if it is present and has a valid checksum // or we have no checksum to check against if (cache != null && cache.Sha1(cacheKey, checksum) && cache.CopyTo(cacheKey, downloadedFilePath)) { if (output) { io.WriteError($" - Loading <info>{package.GetName()}</info> (<comment>{package.GetVersionPrettyFull()}</comment>) from cache", false); } } else { if (output) { io.WriteError(GetDownloadingPrompt(package), false); } DownloadFromHttp(); } // Verify the file to ensure that the file is not damaged. if (!fileSystem.Exists(downloadedFilePath, FileSystemOptions.File)) { throw new UnexpectedException($"{uri} could not be saved to {downloadedFilePath} , make sure the directory is writable and you have internet connectivity."); } if (!string.IsNullOrEmpty(checksum)) { using (var stream = fileSystem.Read(downloadedFilePath)) { if (Security.Sha1(stream) != checksum) { throw new UnexpectedException($"The checksum verification of the file failed (downloaded from {uri})"); } } } } catch { fileSystem.Delete(downloadedFilePath); ClearLastCacheWrite(package); throw; } finally { if (output) { io.OverwriteError(string.Empty, false); } } } void Start() { var uris = package.GetDistUris(); // The performance popped from the tail will be better // than popping out from the head Array.Reverse(uris); while (uris.Length > 0) { var uri = Arr.Pop(ref uris); try { DoDownload(uri); break; } catch (SException ex) { if (io.IsDebug) { io.WriteError(string.Empty); io.WriteError($"Faild: [{ex.GetType()}] {ex.Message}"); } else if (uris.Length > 0) { io.WriteError(string.Empty); io.WriteError($" Failed, trying the next URL ({ex.Message})"); } if (uris.Length <= 0) { throw; } } } } return(Task.Run(Start)); }