/// <summary> /// Assert whether the plugin api version matches the current bucket. /// </summary> protected virtual bool AssertPluginApiVersion(IPackage package) { if (package.GetPackageType() != PluginType) { return(false); } IConstraint requireConstraint = null; foreach (var link in package.GetRequires()) { if (link.GetTarget() == PluginRequire) { requireConstraint = link.GetConstraint(); break; } } if (requireConstraint == null) { throw new RuntimeException( $"Plugin \"{package.GetName()}\" is missing a require statement for a version of the \"{PluginRequire}\" package."); } var currentPluginApiVersion = GetPluginApiVersion(); var currentPluginApiConstraint = new Constraint("==", versionParser.Normalize(currentPluginApiVersion)); if (!requireConstraint.Matches(currentPluginApiConstraint)) { io.WriteError($"<warning>The \"{package.GetName()}\" plugin was skipped because it requires a Plugin API version (\"{requireConstraint.GetPrettyString()}\") that does not match your Bucket installation (\"{currentPluginApiVersion}\"). You may need to run bucket update with the \"--no-plugins\" option.</warning>"); return(false); } return(true); }
private void ExtractAliases(IDictionary <string, string> requires, IList <ConfigAlias> collection) { foreach (var item in requires) { var require = item.Key; var requireVersion = item.Value; var match = Regex.Match(requireVersion, @"^(?<version>[^,\s#]+)(?:#[^ ]+)? +as +(?<alias>[^,\s]+)$"); if (!match.Success) { continue; } collection.Add(new ConfigAlias() { Package = require.ToLower(), Version = versionParser.Normalize(match.Groups["version"].Value, requireVersion), Alias = match.Groups["alias"].Value, AliasNormalized = versionParser.Normalize(match.Groups["alias"].Value, requireVersion), }); } }
private Package CreatePackage(ConfigBucketBase config, Type type) { if (string.IsNullOrEmpty(config.VersionNormalized)) { config.VersionNormalized = versionParser.Normalize(config.Version); } if (!packageCreaters.TryGetValue(type, out PackageCreater creater)) { throw new RuntimeException($"Can not found creater with \"{type}\" type."); } return(creater(config.Name, config.VersionNormalized, config.Version)); }
/// <inheritdoc /> protected override void Initialize() { var driver = GetDriver(); if (driver == null) { throw new InvalidArgumentException($"No driver found to handle VCS repository {uri}"); } if (loader == null) { loader = new LoaderPackage(); } var rootIdentifier = driver.GetRootIdentifier(); try { if (driver.HasBucketFile(rootIdentifier)) { var bucketInformation = driver.GetBucketInformation(rootIdentifier); packageName = bucketInformation.Name; } } #pragma warning disable CA1031 catch (System.Exception ex) #pragma warning restore CA1031 { io.WriteError($"<error>Skipped parsing {rootIdentifier}, {ex.Message}</error>", verbosity: Verbosities.VeryVerbose); } foreach (var item in driver.GetTags()) { var originTag = item.Key; var identifier = item.Value; var message = $"Reading {Factory.DefaultBucketFile} of <info>({packageName ?? uri.ToString()})</info> (tag:<comment>{originTag}</comment>)"; if (isVeryVerbose) { io.WriteError(message); } else if (isVerbose) { io.OverwriteError(message, false); } // strip the release- prefix from tags if present. var tag = originTag.Trim().IndexOf("release-", StringComparison.OrdinalIgnoreCase) != 0 ? originTag : originTag.Substring(8); var cachedPackage = GetCachedPackageVersion(tag, identifier, out bool skipped); if (cachedPackage != null) { AddPackage(cachedPackage); continue; } else if (skipped) { emptyReferences.AddLast(identifier); continue; } var parsedTag = ValidateTag(tag); if (string.IsNullOrEmpty(parsedTag)) { io.WriteError($"<warning>Skipped tag {tag}, invalid tag name.</warning>", verbosity: Verbosities.VeryVerbose); continue; } try { var bucketInformation = driver.GetBucketInformation(identifier); if (bucketInformation == null) { io.WriteError($"<warning>Skipped tag {tag}, no {Factory.DefaultBucketFile} file.</warning>", verbosity: Verbosities.VeryVerbose); emptyReferences.AddLast(identifier); continue; } var version = bucketInformation.Version; string versionNormalized; if (!string.IsNullOrEmpty(version)) { versionNormalized = versionParser.Normalize(bucketInformation.Version); } else { version = tag; versionNormalized = parsedTag; } // make sure tag packages have no -dev flag version = Regex.Replace(version, "[.-]?dev$", string.Empty, RegexOptions.IgnoreCase); versionNormalized = Regex.Replace(versionNormalized, "(^dev-|[.-]?dev$)", string.Empty, RegexOptions.IgnoreCase); // broken package, version doesn't match tag if (versionNormalized != parsedTag) { io.WriteError($"<warning>Skipped tag {tag}, tag ({parsedTag}) does not match version ({versionNormalized}) in {Factory.DefaultBucketFile}</warning>", verbosity: Verbosities.VeryVerbose); continue; } var tagPackageName = string.IsNullOrEmpty(bucketInformation.Name) ? bucketInformation.Name : packageName; var existingPackage = this.FindPackage(tagPackageName, versionNormalized); if (existingPackage != null) { io.WriteError( $"<warning>Skipped tag {tag}, it conflicts with an another tag ({existingPackage.GetVersionPretty()}) as both resolve to {versionNormalized} internally</warning>", verbosity: Verbosities.VeryVerbose); continue; } io.WriteError($"Importing tag {tag} ({versionNormalized})", verbosity: Verbosities.VeryVerbose); bucketInformation.Version = version; bucketInformation.VersionNormalized = versionNormalized; AddPackage(loader.Load(PreProcess(driver, bucketInformation, identifier))); } #pragma warning disable CA1031 catch (System.Exception ex) #pragma warning restore CA1031 { string errorMessage; if (ex is TransportException transportException && transportException.HttpStatusCode == HttpStatusCode.NotFound) { emptyReferences.AddLast(identifier); errorMessage = $"no {Factory.DefaultBucketFile} file was found"; }
/// <inheritdoc /> public IPackage[] WhatProvides(string name, PredicatePackageAcceptable predicatePackageAcceptable = null) { // skip platform packages, root package and bucket-plugin-api if (Regex.IsMatch(name, RepositoryPlatform.RegexPlatform) || name == ConfigBucketBase.RootPackage || name == PluginManager.PluginRequire) { return(Array.Empty <IPackage>()); } AssertLoadProviderMapping(); if (string.IsNullOrEmpty(providersUri)) { return(Array.Empty <IPackage>()); } // package does not exist in this repo if (!providers.TryGetValue(name, out ConfigMetadata metadata)) { return(Array.Empty <IPackage>()); } var hash = metadata["sha256"]; var requestUri = providersUri.Replace("%package%", name).Replace("%hash%", hash); var cacheKey = $"provider-{name.Replace('/', '$')}.json"; ConfigPackages data = null; if (!string.IsNullOrEmpty(hash) && cache.TryReadSha256(cacheKey, out string content, hash)) { data = JsonFile.Parse <ConfigPackages>(content); } if (!data) { data = FetchFile <ConfigPackages>(requestUri, cacheKey, hash); } var result = new Dictionary <string, IPackage>(); var versionsToLoad = new Dictionary <int, ConfigPackageBucket>(); foreach (var versions in data.Packages) { foreach (var version in versions.Value) { var packageConfig = version.Value; var uid = packageConfig.Uid; var normalizedName = packageConfig.Name.ToLower(); // only load the actual named package, not other packages // that might find themselves in the same file if (normalizedName != name) { continue; } if (versionsToLoad.ContainsKey(uid)) { continue; } if (string.IsNullOrEmpty(packageConfig.VersionNormalized)) { packageConfig.VersionNormalized = versionParser.Normalize(packageConfig.Version); } if (IsVersionAcceptable(predicatePackageAcceptable, null, normalizedName, packageConfig)) { versionsToLoad[uid] = packageConfig; } } } // load acceptable packages in the providers var loadedPackages = CreatePackages(versionsToLoad.Values, typeof(IPackageComplete)); var uids = versionsToLoad.Keys.ToArray(); var index = 0; foreach (var package in loadedPackages) { package.SetRepository(this); var uid = uids[index++]; if (package is PackageAlias packageAlias) { var aliased = packageAlias.GetAliasOf(); aliased.SetRepository(this); result[uid.ToString()] = aliased; result[$"{uid}-alias"] = package; } else { result[uid.ToString()] = package; } } return(result.Values.ToArray()); }
/// <inheritdoc /> /// <exception cref="InvalidPackageException">Triggered when validation fails.</exception> public IPackage Load(ConfigBucketBase config, Type expectedClass) { warnings.Clear(); errors.Clear(); // valid package name. if (string.IsNullOrEmpty(config.Name)) { errors.Add("The \"name\" property not allowed to be empty."); } else { var warning = GetPackageNamingDeprecationWarnings(config.Name); if (!string.IsNullOrEmpty(warning)) { warnings.Add(warning); } } // valid version. if (string.IsNullOrEmpty(config.Version)) { errors.Add("The \"version\" property not allowed to be empty."); } else { try { versionParser.Normalize(config.Version); } #pragma warning disable CA1031 catch (System.Exception ex) #pragma warning restore CA1031 { errors.Add($"Property \"version\" is invalid value ({config.Version}): {ex.Message}."); config.Version = null; config.VersionNormalized = null; } } // valid type. if (!string.IsNullOrEmpty(config.PackageType) && !ValidateRegex(config.PackageType, "type", "[A-Za-z0-9-]+")) { config.PackageType = null; } // valid authors. config.Authors = Arr.Filter(config.Authors ?? Array.Empty <ConfigAuthor>(), (author) => { if (!string.IsNullOrEmpty(author.Email) && !ValidateEmail(author.Email, $"Authors {author.Name}")) { author.Email = null; } return(!string.IsNullOrEmpty(author.Name)); }); // valid support. config.Support = Arr.Filter(config.Support, (support) => { var legal = new[] { "email", "issues", "forum", "source", "docs", "wiki" }; var channel = support.Key; if (!Array.Exists(legal, (item) => item == channel)) { warnings.Add($"Property \"{channel}\" is invalid, please use: {string.Join(", ", legal)}"); return(false); } if (channel == "email" && !ValidateEmail(support.Value, "Support")) { return(false); } return(true); }).ToDictionary(item => item.Key, item => item.Value); // valid link. IDictionary <string, string> ValidateLinks(IDictionary <string, string> collection, string linkType) { return(Arr.Filter(collection, (require) => { return ValidateLink(require.Key, require.Value, linkType); }).ToDictionary(item => item.Key, item => item.Value)); } config.Requires = ValidateLinks(config.Requires, "require"); config.RequiresDev = ValidateLinks(config.RequiresDev, "require-dev"); config.Replaces = ValidateLinks(config.Replaces, "replace"); config.Provides = ValidateLinks(config.Provides, "provide"); config.Conflicts = ValidateLinks(config.Conflicts, "conflict"); if (errors.Count > 0) { throw new InvalidPackageException(errors.ToArray(), warnings.ToArray(), config); } return(loader.Load(config, expectedClass)); }