/// <summary>
        /// Updates the dependencies in an API for known packages, but only if the default
        /// version is later than the current one, with the same major version number.
        /// </summary>
        public static void UpdateDependencies(ApiCatalog catalog, ApiMetadata api)
        {
            // Update any previously-defaulted versions to be explicit, if the new version is GA.
            // (This only affects production dependencies, so is not performed in UpdateDependencyDictionary.)
            // Implicit dependencies are always present in DefaultPackageVersions, so we don't need to worry about
            // "internal" dependencies.
            if (api.IsReleaseVersion && PackageTypeToImplicitDependencies.TryGetValue(api.Type, out var implicitDependencies))
            {
                foreach (var implicitDependency in implicitDependencies)
                {
                    if (!api.Dependencies.ContainsKey(implicitDependency))
                    {
                        api.Dependencies[implicitDependency] = DefaultPackageVersions[implicitDependency];
                    }
                }
            }

            UpdateDependencyDictionary(api.Dependencies, "dependencies");
            UpdateDependencyDictionary(api.TestDependencies, "testDependencies");

            void UpdateDependencyDictionary(SortedDictionary <string, string> dependencies, string jsonName)
            {
                if (dependencies.Count == 0)
                {
                    return;
                }

                // We want to update any dependencies to "external" packages as listed in DefaultPackageVersions,
                // but also "internal" packages such as Google.LongRunning.
                Dictionary <string, string> allDefaultPackageVersions = DefaultPackageVersions
                                                                        .Concat(catalog.Apis.Select(api => new KeyValuePair <string, string>(api.Id, api.Version)))
                                                                        .ToDictionary(pair => pair.Key, pair => pair.Value);

                foreach (var package in dependencies.Keys.ToList())
                {
                    if (allDefaultPackageVersions.TryGetValue(package, out var defaultVersion))
                    {
                        var currentVersion = dependencies[package];
                        if (currentVersion == DefaultVersionValue ||
                            currentVersion == ProjectVersionValue ||
                            defaultVersion == currentVersion)
                        {
                            continue;
                        }
                        var structuredDefaultVersion = StructuredVersion.FromString(defaultVersion);
                        var structuredCurrentVersion = StructuredVersion.FromString(currentVersion);
                        if (structuredDefaultVersion.CompareTo(structuredCurrentVersion) > 0 &&
                            structuredDefaultVersion.Major == structuredCurrentVersion.Major)
                        {
                            dependencies[package] = defaultVersion;
                        }
                    }
                }

                if (api.Json is object)
                {
                    api.Json[jsonName] = new JObject(dependencies.Select(pair => new JProperty(pair.Key, pair.Value)));
                }
            }
        }
Example #2
0
        private static IEnumerable <Release> LoadReleases(Repository repo, ApiMetadata api)
        {
            var id          = api.Id;
            var pathPrefix  = $"apis/{id}/{id}/";
            var projectFile = $"apis/{id}/{id}/{id}.csproj";
            // Some versions return forward slashes, some return backslashes :(
            Func <string, bool> pathFilter = path => path.Replace('\\', '/').StartsWith(pathPrefix) && path != projectFile;

            List <Release>    releases         = new List <Release>();
            StructuredVersion currentVersion   = StructuredVersion.FromString(api.Version);
            Commit            currentTagCommit = null;

            // "Pending" as in "haven't been yielded in a release yet"
            List <GitCommit> pendingCommits = new List <GitCommit>();

            var tagPrefix        = $"{id}-";
            var versionsCommitId = repo.Tags
                                   .Where(tag => tag.FriendlyName.StartsWith(tagPrefix))
                                   .ToDictionary(tag => tag.Target.Id, tag => tag.FriendlyName.Substring(tagPrefix.Length));

            foreach (var commit in repo.Head.Commits)
            {
                if (CommitContainsApi(commit))
                {
                    pendingCommits.Add(new GitCommit(commit));
                }
                if (versionsCommitId.TryGetValue(commit.Id, out string version) && !version.StartsWith("0."))
                {
                    yield return(new Release(currentVersion, currentTagCommit, pendingCommits));

                    // Release constructor clones the list, so we're safe to clear it.
                    pendingCommits.Clear();
                    currentTagCommit = commit;
                    currentVersion   = StructuredVersion.FromString(version);
                }
            }

            if (pendingCommits.Count != 0)
            {
                yield return(new Release(currentVersion, currentTagCommit, pendingCommits));
            }

            bool CommitContainsApi(Commit commit)
            {
                if (commit.Parents.Count() != 1)
                {
                    return(false);
                }
                var tree       = commit.Tree;
                var parentTree = commit.Parents.First().Tree;
                var comparison = repo.Diff.Compare <TreeChanges>(parentTree, tree);

                return(comparison.Select(change => change.Path).Any(pathFilter));
            }
        }
        /// <summary>
        /// Returns the appropriate version to include in a package dependency.
        /// For Google.* and Grpc.*, this is major-version pinned. For other packages, we just leave it as the version
        /// specified in the string - as some packages are fine to upgrade beyond major version boundaries.
        /// </summary>
        private static string GetDependencyVersionRange(string package, string specifiedVersion)
        {
            if (!(package.StartsWith("Google.") || package.StartsWith("Grpc.")))
            {
                return(specifiedVersion);
            }
            var structuredVersion = StructuredVersion.FromString(specifiedVersion);
            var nextMajor         = StructuredVersion.FromMajorMinorPatchBuild(structuredVersion.Major + 1, 0, 0, structuredVersion.Build is null ? default(int?) : 0, null);

            return($"[{structuredVersion}, {nextMajor})");
        }
        /// <summary>
        /// Updates the dependencies in an API for known packages, but only if the default
        /// version is later than the current one, with the same major version number.
        /// </summary>
        public static void UpdateDependencies(ApiCatalog catalog, ApiMetadata api)
        {
            UpdateDependencyDictionary(api.Dependencies, "dependencies");
            UpdateDependencyDictionary(api.TestDependencies, "testDependencies");

            void UpdateDependencyDictionary(SortedDictionary <string, string> dependencies, string jsonName)
            {
                if (dependencies.Count == 0)
                {
                    return;
                }

                // We want to update any dependencies to "external" packages as listed in DefaultPackageVersions,
                // but also "internal" packages such as Google.LongRunning.
                Dictionary <string, string> allDefaultPackageVersions = DefaultPackageVersions
                                                                        .Concat(catalog.Apis.Select(api => new KeyValuePair <string, string>(api.Id, api.Version)))
                                                                        .ToDictionary(pair => pair.Key, pair => pair.Value);

                foreach (var package in dependencies.Keys.ToList())
                {
                    if (allDefaultPackageVersions.TryGetValue(package, out var defaultVersion))
                    {
                        var currentVersion = dependencies[package];
                        if (currentVersion == DefaultVersionValue ||
                            currentVersion == ProjectVersionValue ||
                            defaultVersion == currentVersion)
                        {
                            continue;
                        }
                        var structuredDefaultVersion = StructuredVersion.FromString(defaultVersion);
                        var structuredCurrentVersion = StructuredVersion.FromString(currentVersion);
                        if (structuredDefaultVersion.CompareTo(structuredCurrentVersion) > 0 &&
                            structuredDefaultVersion.Major == structuredCurrentVersion.Major)
                        {
                            dependencies[package] = defaultVersion;
                        }
                    }
                }
                if (api.Json is object)
                {
                    api.Json[jsonName] = new JObject(dependencies.Select(pair => new JProperty(pair.Key, pair.Value)));
                }
            }
        }
        private static IEnumerable <Release> LoadReleases(Repository repo, ApiCatalog catalog, ApiMetadata api)
        {
            var id = api.Id;
            var commitPredicate = GitHelpers.CreateCommitPredicate(repo, catalog, api);

            List <Release>    releases         = new List <Release>();
            StructuredVersion currentVersion   = StructuredVersion.FromString(api.Version);
            Commit            currentTagCommit = null;

            // "Pending" as in "haven't been yielded in a release yet"
            List <GitCommit> pendingCommits = new List <GitCommit>();

            var tagPrefix        = $"{id}-";
            var versionsCommitId = repo.Tags
                                   .Where(tag => tag.FriendlyName.StartsWith(tagPrefix))
                                   .ToDictionary(tag => tag.Target.Id, tag => tag.FriendlyName.Substring(tagPrefix.Length));

            foreach (var commit in repo.Head.Commits)
            {
                if (commitPredicate(commit))
                {
                    pendingCommits.Add(new GitCommit(commit));
                }
                if (versionsCommitId.TryGetValue(commit.Id, out string version) && !version.StartsWith("0."))
                {
                    yield return(new Release(currentVersion, currentTagCommit, pendingCommits));

                    // Release constructor clones the list, so we're safe to clear it.
                    pendingCommits.Clear();
                    currentTagCommit = commit;
                    currentVersion   = StructuredVersion.FromString(version);
                }
            }

            if (pendingCommits.Count != 0)
            {
                yield return(new Release(currentVersion, currentTagCommit, pendingCommits));
            }
        }