Exemple #1
0
        /// <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),
                });
            }
        }
Exemple #3
0
        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));
        }
Exemple #4
0
        /// <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";
                    }
Exemple #5
0
        /// <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());
        }
Exemple #6
0
        /// <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));
        }