예제 #1
0
        protected override void ExecuteImpl(string[] args)
        {
            string id = args[0];

            var catalog = ApiCatalog.Load();

            if (catalog.Apis.Any(api => api.Id == id))
            {
                throw new UserErrorException($"API {id} already exists in the API catalog.");
            }
            var root       = DirectoryLayout.DetermineRootDirectory();
            var googleapis = Path.Combine(root, "googleapis");
            var apiIndex   = ApiIndex.V1.Index.LoadFromGoogleApis(googleapis);

            var targetApi = apiIndex.Apis.FirstOrDefault(api => api.DeriveCSharpNamespace() == id);

            if (targetApi is null)
            {
                var lowerWithoutCloud = id.Replace(".Cloud", "").ToLowerInvariant();
                var possibilities     = apiIndex.Apis
                                        .Select(api => api.DeriveCSharpNamespace())
                                        .Where(ns => ns.Replace(".Cloud", "").ToLowerInvariant() == lowerWithoutCloud);
                throw new UserErrorException(
                          $"No service found for '{id}'.{Environment.NewLine}Similar possibilities (check options?): {string.Join(", ", possibilities)}");
            }

            var api = new ApiMetadata
            {
                Id          = id,
                ProtoPath   = targetApi.Directory,
                ProductName = targetApi.Title.EndsWith(" API") ? targetApi.Title[..^ 4] : targetApi.Title,
예제 #2
0
        private static void SetVersion(string id, string version)
        {
            var catalog = ApiCatalog.Load();
            var api     = catalog[id];

            string oldVersion = api.Version;

            api.Version = version;
            var layout   = DirectoryLayout.ForApi(id);
            var apiNames = catalog.CreateIdHashSet();

            ProjectGenerator.Program.GenerateMetadataFile(layout.SourceDirectory, api);
            ProjectGenerator.Program.GenerateProjects(layout.SourceDirectory, api, apiNames);
            ProjectGenerator.Program.RewriteReadme(catalog);
            ProjectGenerator.Program.RewriteDocsRootIndex(catalog);

            // Update the parsed JObject associated with the ID, and write it back to apis.json.
            api.Json["version"] = version;
            string formatted = catalog.FormatJson();

            File.WriteAllText(ApiCatalog.CatalogPath, formatted);
            Console.WriteLine("Updated apis.json");
            Console.WriteLine();
            Console.WriteLine(new ApiVersionPair(id, oldVersion, version));
        }
        private static void Execute(string id)
        {
            var catalog = ApiCatalog.Load();
            var api     = catalog[id];

            if (api.NoVersionHistory)
            {
                Console.WriteLine($"Skipping version history update for {id}");
                return;
            }
            string historyFilePath = HistoryFile.GetPathForPackage(id);

            var root = DirectoryLayout.DetermineRootDirectory();

            using (var repo = new Repository(root))
            {
                var releases = LoadReleases(repo, api).ToList();
                if (!File.Exists(historyFilePath))
                {
                    File.WriteAllText(historyFilePath, "# Version history\r\n\r\n");
                }
                var historyFile = HistoryFile.Load(historyFilePath);
                historyFile.MergeReleases(releases);
                historyFile.Save(historyFilePath);
            }
            var relativePath = Path.GetRelativePath(DirectoryLayout.DetermineRootDirectory(), historyFilePath)
                               .Replace('\\', '/');

            Console.WriteLine($"Updated version history file: {relativePath}");
        }
        protected override void ExecuteImpl(string[] args)
        {
            var catalog       = ApiCatalog.Load();
            var root          = DirectoryLayout.DetermineRootDirectory();
            var googleapis    = Path.Combine(root, "googleapis");
            var apiIndex      = ApiIndex.V1.Index.LoadFromGoogleApis(googleapis);
            int modifiedCount = 0;

            foreach (var api in catalog.Apis)
            {
                var indexEntry = apiIndex.Apis.FirstOrDefault(x => x.DeriveCSharpNamespace() == api.Id);
                if (indexEntry is null)
                {
                    continue;
                }

                // Change this line when introducing a new field...
                api.Json.Last.AddAfterSelf(new JProperty("serviceConfigFile", indexEntry.ConfigFile));
                modifiedCount++;
            }

            Console.WriteLine($"Modified APIs: {modifiedCount}");
            string json = catalog.FormatJson();

            // Validate that we can still load it, before saving it to disk...
            ApiCatalog.FromJson(json);

            File.WriteAllText(ApiCatalog.CatalogPath, json);
        }
        private static void Execute(string id)
        {
            var catalog = ApiCatalog.Load();
            var api     = catalog[id];

            if (api.NoVersionHistory)
            {
                Console.WriteLine($"Skipping version history update for {id}");
                return;
            }
            string historyFilePath = HistoryFile.GetPathForPackage(id);

            var root = DirectoryLayout.DetermineRootDirectory();

            using var repo = new Repository(root);
            var releases         = Release.LoadReleases(repo, catalog, api).ToList();
            var historyFile      = HistoryFile.Load(historyFilePath);
            var sectionsInserted = historyFile.MergeReleases(releases);

            if (sectionsInserted.Count != 0)
            {
                historyFile.Save(historyFilePath);
                var relativePath = Path.GetRelativePath(DirectoryLayout.DetermineRootDirectory(), historyFilePath)
                                   .Replace('\\', '/');
                Console.WriteLine($"Updated version history file: {relativePath}");
                Console.WriteLine("New content:");
                Console.WriteLine();
                foreach (var line in sectionsInserted.SelectMany(section => section.Lines))
                {
                    Console.WriteLine(line);
                }
            }
        }
예제 #6
0
        protected override void ExecuteImpl(string[] args)
        {
            string configFile = args[0];
            var    json       = File.ReadAllText(configFile);
            var    config     = JsonConvert.DeserializeObject <BatchReleaseConfig>(json);

            var criteria = config.GetCriteria().ToList();

            if (criteria.Count != 1)
            {
                throw new UserErrorException("Batch release config must specify exactly one criterion.");
            }

            if (!config.DryRun)
            {
                var root = DirectoryLayout.DetermineRootDirectory();
                using var repo = new Repository(root);

                if (repo.RetrieveStatus().IsDirty)
                {
                    throw new UserErrorException("In non-dry-run mode, the current branch must not have changes.");
                }
            }

            var catalog   = ApiCatalog.Load();
            var criterion = criteria[0];
            var proposals = criterion.GetProposals(catalog);

            foreach (var proposal in proposals)
            {
                // Note: This takes into account the dry-run flag.
                proposal.Execute(config);
            }
        }
예제 #7
0
        private static int MainImpl(string[] args)
        {
            if (args.Length != 1)
            {
                throw new UserErrorException("Please specify the API name");
            }
            string api         = args[0];
            var    layout      = DirectoryLayout.ForApi(api);
            var    apiMetadata = ApiCatalog.Load()[api];

            string output = layout.DocsOutputDirectory;

            if (Directory.Exists(output))
            {
                Directory.Delete(output, true);
            }
            Directory.CreateDirectory(output);

            var apiDirectory = layout.SourceDirectory;
            var projects     = Project.LoadProjects(apiDirectory).ToList();

            CreateDocfxJson(api, projects, output);
            CopyAndGenerateArticles(apiMetadata, layout.DocsSourceDirectory, output);
            CreateToc(api, output);
            return(0);
        }
예제 #8
0
        protected override void ExecuteImpl(string[] args)
        {
            ValidateCommonHiddenProductionDependencies();
            var root    = DirectoryLayout.DetermineRootDirectory();
            var catalog = ApiCatalog.Load();

            ValidateApiCatalog(catalog);
            Console.WriteLine($"API catalog contains {catalog.Apis.Count} entries");
            // Now we know we can parse the API catalog, let's reformat it.
            ReformatApiCatalog(catalog);
            RewriteReadme(catalog);
            RewriteRenovate(catalog);
            HashSet <string> apiNames = catalog.CreateIdHashSet();

            foreach (var api in catalog.Apis)
            {
                var path = Path.Combine(root, "apis", api.Id);
                GenerateProjects(path, api, apiNames);
                GenerateSolutionFiles(path, api);
                GenerateDocumentationStub(path, api);
                GenerateSynthConfiguration(path, api);
                GenerateOwlBotConfiguration(path, api);
                GenerateMetadataFile(path, api);
            }
        }
        protected override void ExecuteImpl(string[] args)
        {
            string id = args[0];

            var catalog = ApiCatalog.Load();
            var api     = catalog[id];

            Console.WriteLine($"Current version of {id} in the API catalog: {api.Version}");
        }
예제 #10
0
        private Program(string apiId)
        {
            DirectoryLayout layout = DirectoryLayout.ForApi(apiId);

            _apiId       = apiId;
            _outputRoot  = layout.DocsOutputDirectory;
            _devSiteRoot = Path.Combine(_outputRoot, "devsite");
            _apiCatalog  = ApiCatalog.Load();
            _apiIds      = _apiCatalog.CreateIdHashSet();
        }
예제 #11
0
        public void Execute(string[] args)
        {
            var root    = DirectoryLayout.DetermineRootDirectory();
            var catalog = ApiCatalog.Load();
            HashSet <string> tags;

            using (var repo = new Repository(root))
            {
                tags = new HashSet <string>(repo.Tags.Select(tag => tag.FriendlyName));
            }

            List <ApiMetadata> apisToCheck = args.Length == 0
                ? catalog.Apis.Where(api => !api.Version.EndsWith("00") && !tags.Contains($"{api.Id}-{api.Version}")).ToList()
                                             // Note: this basically validates the command line arguments.
                : args.Select(arg => catalog[arg]).ToList();

            foreach (var api in apisToCheck)
            {
                Console.WriteLine($"Checking compatibility for {api.Id} version {api.Version}");
                var prefix      = api.Id + "-";
                var lastVersion = tags
                                  .Where(tag => tag.StartsWith(prefix))
                                  .Select(tag => tag.Split(new char[] { '-' }, 2)[1])
                                  .Where(v => !v.StartsWith("0")) // We can reasonably ignore old 0.x versions
                                  .Select(StructuredVersion.FromString)
                                  .OrderBy(v => v)
                                  .LastOrDefault();

                if (lastVersion is null)
                {
                    Console.WriteLine("No previous versions released; ignoring.");
                    continue;
                }

                var newVersion = api.StructuredVersion;

                // If we're releasing a new version, we should check against the previous one.
                // (For example, if this PR creates 1.2.0, then check against 1.1.0.)
                // Otherwise, just expect minor changes.
                Level requiredLevel = Level.Minor;
                if (!lastVersion.Equals(newVersion))
                {
                    requiredLevel =
                        lastVersion.Major != newVersion.Major ? Level.Major   // Major version bump: anything goes
                        : lastVersion.Minor != newVersion.Minor ? Level.Minor // Minor version bump: minor changes are okay
                        : Level.Identical;                                    // Patch version bump: API should be identical
                }

                var actualLevel = CheckCompatibility(api, lastVersion);
                if (actualLevel < requiredLevel)
                {
                    throw new UserErrorException($"Required compatibility level: {requiredLevel}. Actual compatibility level: {actualLevel}.");
                }
            }
        }
예제 #12
0

        
        protected override void ExecuteImpl(string[] args)
        {
            var catalog = ApiCatalog.Load();
            var lagging = catalog.Apis.Where(api => api.CanHaveGaRelease && api.StructuredVersion.Prerelease is string);

            Console.WriteLine($"Lagging packages:");
            foreach (var api in lagging)
            {
                Console.WriteLine($"{api.Id} ({api.Version})");
            }
        }
        protected override void ExecuteImpl(string[] args)
        {
            string     googleapisGen = args[0];
            ApiCatalog catalog       = ApiCatalog.Load();

            var apis        = catalog.Apis.Where(x => x.DetermineAutoGeneratorType() == AutoGeneratorType.OwlBot).ToList();
            var ungenerated = apis.Where(api => !IsGenerated(api, googleapisGen)).ToList();

            foreach (var api in ungenerated)
            {
                Console.WriteLine($"Ungenerated API: {api.Id}");
            }
            Console.WriteLine($"Total OwlBot APIs: {apis.Count}; ungenerated APIs: {ungenerated.Count}");
        }
예제 #16
0
        protected List <ApiVersionPair> FindChangedVersions()
        {
            var currentCatalog  = ApiCatalog.Load();
            var primaryCatalog  = LoadPrimaryCatalog();
            var currentVersions = currentCatalog.CreateRawVersionMap();
            var primaryVersions = primaryCatalog.CreateRawVersionMap();

            return(currentVersions.Keys.Concat(primaryVersions.Keys)
                   .Distinct()
                   .OrderBy(id => id)
                   .Select(id => new ApiVersionPair(id, primaryVersions.GetValueOrDefault(id), currentVersions.GetValueOrDefault(id)))
                   .Where(v => v.NewVersion != v.OldVersion)
                   .ToList());
        }
예제 #17
0
        protected override void ExecuteImpl(string[] args)
        {
            var root    = DirectoryLayout.DetermineRootDirectory();
            var catalog = ApiCatalog.Load();

            using (var repo = new Repository(root))
            {
                var allTags = repo.Tags.OrderByDescending(GitHelpers.GetDate).ToList();
                foreach (var api in catalog.Apis)
                {
                    MaybeShowStale(repo, allTags, api);
                }
            }
        }
예제 #18
0
        private static List <ApiVersionPair> FindChangedVersions()
        {
            var currentCatalog  = ApiCatalog.Load();
            var masterCatalog   = LoadMasterCatalog();
            var currentVersions = currentCatalog.CreateRawVersionMap();
            var masterVersions  = masterCatalog.CreateRawVersionMap();

            return(currentVersions.Keys.Concat(masterVersions.Keys)
                   .Distinct()
                   .OrderBy(id => id)
                   .Select(id => new ApiVersionPair(id, masterVersions.GetValueOrDefault(id), currentVersions.GetValueOrDefault(id)))
                   .Where(v => v.NewVersion != v.OldVersion)
                   .ToList());
        }
예제 #19
0
        private void CreateDocUploaderMetadata()
        {
            var apiMetadata         = ApiCatalog.Load()[_apiId];
            var docUploaderMetadata = new JObject
            {
                ["version"]      = apiMetadata.Version,
                ["language"]     = "dotnet",
                ["name"]         = apiMetadata.Id,
                ["xrefs"]        = new JArray(GetXrefUrls(apiMetadata)),
                ["xrefServices"] = new JArray("https://xref.docs.microsoft.com/query?uid={uid}")
            };
            string json = docUploaderMetadata.ToString(Formatting.Indented);

            File.WriteAllText(Path.Combine(_devSiteRoot, "docs.metadata.json"), json);
        }
예제 #20
0
        protected override void ExecuteImpl(string[] args)
        {
            Console.WriteLine($"Lagging packages (package ID, current version, date range of current version prerelease series):");
            var root    = DirectoryLayout.DetermineRootDirectory();
            var catalog = ApiCatalog.Load();

            using (var repo = new Repository(root))
            {
                var allTags = repo.Tags.OrderByDescending(GitHelpers.GetDate).ToList();
                foreach (var api in catalog.Apis)
                {
                    MaybeShowLagging(repo, allTags, api);
                }
            }
        }
예제 #21
0
        private static void IncrementVersion(string[] args)
        {
            if (args.Length != 1)
            {
                throw new UserErrorException($"{IncrementVersionCommand} requires one argument: the package ID");
            }
            string id = args[0];

            // It's slightly inefficient that we load the API catalog once here and once later on, and the code duplication
            // is annoying too, but it's insignficant really - and at least the code is simple.

            var catalog = ApiCatalog.Load();
            var api     = catalog[id];
            var version = IncrementStructuredVersion(api.StructuredVersion).ToString();

            SetVersion(id, version);

            StructuredVersion IncrementStructuredVersion(StructuredVersion originalVersion)
            {
                // Any GA version just increments the minor version.
                if (originalVersion.Prerelease is null)
                {
                    return(new StructuredVersion(originalVersion.Major, originalVersion.Minor + 1, 0, null));
                }

                // For prereleases, expect something like "beta01" which should be incremented to "beta02".
                var prereleasePattern = new Regex(@"^([^\d]*)(\d+)$");
                var match             = prereleasePattern.Match(originalVersion.Prerelease);

                if (!match.Success)
                {
                    throw new UserErrorException($"Don't know how to auto-increment version '{originalVersion}'");
                }
                var prefix = match.Groups[1].Value;
                var suffix = match.Groups[2].Value;

                if (!int.TryParse(suffix, out var counter))
                {
                    throw new UserErrorException($"Don't know how to auto-increment version '{originalVersion}'");
                }
                counter++;
                var newSuffix = counter.ToString().PadLeft(suffix.Length, '0');

                return(new StructuredVersion(originalVersion.Major, originalVersion.Minor, originalVersion.Patch, $"{prefix}{newSuffix}"));
            }
        }
예제 #22
0
        private void ValidateProjectReferences()
        {
            var catalog       = ApiCatalog.Load();
            var newReleaseIds = FindChangedVersions().Select(change => change.Id).ToList();

            foreach (var id in newReleaseIds)
            {
                var api = catalog[id];
                var projectReferences = api.Dependencies.Where(p => p.Value == "project").Select(p => p.Key);
                var badReferences     = projectReferences.Except(newReleaseIds).ToList();
                if (badReferences.Any())
                {
                    throw new UserErrorException(
                              $"Project {api.Id} contains project references to projects outside the release set: {string.Join(", ", badReferences)}");
                }
            }
        }
        protected override void ExecuteImpl(string[] args)
        {
            var catalog  = ApiCatalog.Load();
            var apiNames = catalog.CreateIdHashSet();

            foreach (var api in catalog.Apis)
            {
                GenerateProjectsCommand.UpdateDependencies(catalog, api);
                var layout = DirectoryLayout.ForApi(api.Id);
                GenerateProjectsCommand.GenerateMetadataFile(layout.SourceDirectory, api);
                GenerateProjectsCommand.GenerateProjects(layout.SourceDirectory, api, apiNames);
            }
            string formatted = catalog.FormatJson();

            File.WriteAllText(ApiCatalog.CatalogPath, formatted);
            Console.WriteLine("Updated apis.json");
        }
예제 #24
0
        private static void Main()
        {
            var directory = ServiceDirectory.LoadFromGoogleapis();

            var catalog            = ApiCatalog.Load();
            var paths              = new HashSet <string>(catalog.Apis.Select(api => api.ProtoPath));
            var serviceDirectories = directory.Services
                                     .Where(service => service.Stable)
                                     .Select(service => service.ServiceDirectory).ToList();

            var ungenerated = serviceDirectories
                              .Except(paths)
                              .Except(catalog.IgnoredPaths)
                              .OrderBy(path => path).ToList();

            ungenerated.ForEach(Console.WriteLine);
        }
예제 #25
0
        static void Main(string[] args)
        {
            bool attentionOnly = args.Contains("--attention");

            var root    = DirectoryLayout.DetermineRootDirectory();
            var catalog = ApiCatalog.Load();

            using (var repo = new Repository(root))
            {
                var diff           = repo.Diff;
                var tags           = repo.Tags;
                var shaToTimestamp = repo.Commits.ToDictionary(commit => commit.Sha, commit => commit.Committer.When);

                foreach (var api in catalog.Apis)
                {
                    DisplayApi(attentionOnly, api, tags, shaToTimestamp);
                }
            }
        }
예제 #26
0
        protected override void ExecuteImpl(string[] args)
        {
            var root    = DirectoryLayout.DetermineRootDirectory();
            var catalog = ApiCatalog.Load();

            using var repo = new Repository(root);
            var pendingChangesByApi = GitHelpers.GetPendingChangesByApi(repo, catalog);

            // TODO: Work out what to do with unreleased APIs. Do we want to show them or not?
            foreach (var api in catalog.Apis)
            {
                var release = pendingChangesByApi[api];
                var commits = release.Commits;
                if (commits.Count == 0)
                {
                    continue;
                }
                string changes = commits.Count == 1 ? "change" : "changes";
                Console.WriteLine($"{api.Id,-50} {release.ReleaseDate:yyyy-MM-dd}: {release.Version} + {commits.Count} {changes}");
            }
        }
예제 #27
0
        public void Execute(string[] args)
        {
            var root    = DirectoryLayout.DetermineRootDirectory();
            var catalog = ApiCatalog.Load();
            HashSet <string> tags;

            using (var repo = new Repository(root))
            {
                tags = new HashSet <string>(repo.Tags.Select(tag => tag.FriendlyName));
            }

            List <ApiMetadata> apisToCheck = args.Length == 0
                ? catalog.Apis.Where(api => !api.Version.EndsWith("00") && !tags.Contains($"{api.Id}-{api.Version}")).ToList()
                                             // Note: this basically validates the command line arguments.
                : args.Select(arg => catalog[arg]).ToList();

            foreach (var api in apisToCheck)
            {
                Console.WriteLine($"Checking compatibility for {api.Id} version {api.Version}");
                var prefix = api.Id + "-";

                var taggedVersions = tags
                                     .Where(tag => tag.StartsWith(prefix))
                                     .Select(tag => tag.Split(new char[] { '-' }, 2)[1])
                                     .Where(v => !v.StartsWith("0")) // We can reasonably ignore old 0.x versions
                                     .Select(StructuredVersion.FromString)
                                     .OrderBy(v => v)
                                     .ToList();

                var comparisons = GetComparisons(api.StructuredVersion, taggedVersions);
                foreach (var(oldVersion, requiredLevel) in comparisons)
                {
                    var actualLevel = CheckCompatibility(api, oldVersion);
                    if (actualLevel < requiredLevel)
                    {
                        throw new UserErrorException($"Required compatibility level: {requiredLevel}. Actual compatibility level: {actualLevel}.");
                    }
                }
            }
        }
        protected override void ExecuteImpl(string[] args)
        {
            var catalog = ApiCatalog.Load();
            // Note: for now, we could actually load it from apis/ServiceDirectory, but hey...
            var directory       = ServiceDirectory.LoadFromGoogleapis();
            var stabilityFilter = BuildStabilityFilter(args[0]);

            var ignoredOrGeneratedPaths = new HashSet <string>(catalog.IgnoredPaths.Keys);

            ignoredOrGeneratedPaths.UnionWith(catalog.Apis.Select(api => api.ProtoPath));

            var missingServices = directory.Services
                                  .Where(stabilityFilter)
                                  .Where(svc => !ignoredOrGeneratedPaths.Contains(svc.ServiceDirectory))
                                  .ToList();

            Console.WriteLine($"Missing services: {missingServices.Count}");
            foreach (var service in missingServices)
            {
                Console.WriteLine(service.ServiceDirectory);
            }
        }
        protected override void ExecuteImpl(string[] args)
        {
            var catalog         = ApiCatalog.Load();
            var root            = DirectoryLayout.DetermineRootDirectory();
            var googleapis      = Path.Combine(root, "googleapis");
            var apiIndex        = ApiIndex.V1.Index.LoadFromGoogleApis(googleapis);
            var stabilityFilter = BuildStabilityFilter(args[0]);

            var ignoredOrGeneratedPaths = new HashSet <string>(catalog.IgnoredPaths.Keys);

            ignoredOrGeneratedPaths.UnionWith(catalog.Apis.Select(api => api.ProtoPath));

            var missingApis = apiIndex.Apis
                              .Where(stabilityFilter)
                              .Where(api => !ignoredOrGeneratedPaths.Contains(api.Directory))
                              .ToList();

            Console.WriteLine($"Missing apis: {missingApis.Count}");
            foreach (var api in missingApis)
            {
                Console.WriteLine($"{api.Directory} => {api.DeriveCSharpNamespace()}");
            }
        }