Пример #1
0
        private static string CreateClientClassesDocumentation(ApiMetadata api)
        {
            if (api.Type != ApiType.Grpc)
            {
                return("FIXME"); // No automatic templating for this API
            }
            var layout        = DirectoryLayout.ForApi(api.Id);
            var packageSource = Path.Combine(layout.SourceDirectory, api.Id);
            var sourceFiles   = Directory.GetFiles(packageSource, "*Client.cs");
            // TODO: Find a more robust way of detecting the clients.
            var clients = sourceFiles
                          .Where(file => File.ReadAllText(file).Contains(": ServiceSettingsBase")) // Check it contains a generated client
                          .Select(file => Path.GetFileName(file))                                  // Just the file name, not full path
                          .Select(file => file.Substring(0, file.Length - 3))                      // Trim .cs
                          .OrderBy(client => client)
                          .Select(client => $"[{client}](obj/api/{api.Id}.{client}.yml)")          // Markdown link to API doc
                          .ToList();

            switch (clients.Count)
            {
            case 0: return("FIXME");    // No automatic templating for this API

            case 1: return($"All operations are performed through {clients[0]}.");

            default:
                var list = string.Join("\r\n", clients.Select(client => $"- {client}"));
                return($"All operations are performed through the following client classes:\r\n\r\n{list}");
            }
        }
Пример #2
0
        static void GenerateProject(string package, string version, string framework, string workingArea)
        {
            if (File.Exists(workingArea) || Directory.Exists(workingArea))
            {
                throw new UserErrorException($"{workingArea} already exists");
            }
            Directory.CreateDirectory(workingArea);

            XElement targetReference;

            if (version == "project")
            {
                DirectoryLayout layout             = DirectoryLayout.ForApi(package);
                var             packageProjectFile = Path.Combine(layout.SourceDirectory, package, $"{package}.csproj");
                targetReference = new XElement("ProjectReference", new XAttribute("Include", packageProjectFile));
            }
            else
            {
                targetReference = new XElement("PackageReference", new XAttribute("Include", package), new XAttribute("Version", version));
            }
            var projectFile = new XDocument(
                new XElement("Project", new XAttribute("Sdk", "Microsoft.NET.Sdk"),
                             new XElement("PropertyGroup",
                                          new XElement("TargetFramework", framework)
                                          ),
                             new XElement("ItemGroup", targetReference)
                             )
                );

            using (var output = File.Create(Path.Combine(workingArea, "project.csproj")))
            {
                projectFile.Save(output);
            }
        }
        internal void InternalExecute(string id, string version, bool quiet)
        {
            var catalog = ApiCatalog.Load();
            var api     = catalog[id];

            string oldVersion = api.Version;

            api.Version = version;
            if (api.StructuredVersion.Patch == 0)
            {
                GenerateProjectsCommand.UpdateDependencies(catalog, api);
            }
            var layout   = DirectoryLayout.ForApi(id);
            var apiNames = catalog.CreateIdHashSet();

            // This will still write output, even if "quiet" is true, but that's probably
            // okay for batch releasing.
            GenerateProjectsCommand.GenerateMetadataFile(layout.SourceDirectory, api);
            GenerateProjectsCommand.GenerateProjects(layout.SourceDirectory, api, apiNames);
            GenerateProjectsCommand.RewriteReadme(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);
            if (!quiet)
            {
                Console.WriteLine("Updated apis.json");
                Console.WriteLine();
                Console.WriteLine(new ApiVersionPair(id, oldVersion, version));
            }
        }
Пример #4
0
        /// <summary>
        /// Checks the compatibility of the locally-built API against a version on NuGet.
        /// This assumes the local package has already been built and is up-to-date.
        /// </summary>
        private static Level CheckCompatibility(ApiMetadata api, StructuredVersion version)
        {
            Console.WriteLine($"Differences from {version}");

            // TODO: Remove this try/catch when *everything* has a previous minor version on netstandard2.0.
            AssemblyDefinition oldMetadata;

            try
            {
                oldMetadata = Assemblies.LoadPackageAsync(api.Id, version.ToString(), null, null).Result;
            }
            catch (Exception e)
            {
                Console.WriteLine($"Unable to load {api.Id} version {version} from NuGet. Some possible causes:");
                Console.WriteLine("- Package was pre-netstandard2.0");
                Console.WriteLine("- Package was never published");
                Console.WriteLine("- nuget.org failure");
                Console.WriteLine($"Exception message: {e.Message}");
                Console.WriteLine($"Returning 'identical' as the change level; please check carefully before release.");
                return(Level.Identical);
            }
            var sourceAssembly = Path.Combine(DirectoryLayout.ForApi(api.Id).SourceDirectory, api.Id, "bin", "Release", "netstandard2.0", $"{api.Id}.dll");
            var newMetadata    = Assemblies.LoadFile(sourceAssembly);

            var diff = Assemblies.Compare(oldMetadata, newMetadata, null);

            diff.PrintDifferences(Level.Major, FormatDetail.Brief);
            diff.PrintDifferences(Level.Minor, FormatDetail.Brief);
            Console.WriteLine($"Diff level: {diff.Level}");
            Console.WriteLine();
            return(diff.Level);
        }
        protected override void ExecuteImpl(string[] args)
        {
            var googleapisRoot   = GetGoogleapisRoot();
            var serviceDirectory = ServiceDirectory.LoadFromGoogleapis(googleapisRoot);
            var outputDirectory  = DirectoryLayout.ForApi("ServiceDirectory").SourceDirectory;

            string directoryJson = JsonConvert.SerializeObject(serviceDirectory, Formatting.Indented);

            File.WriteAllText(Path.Combine(outputDirectory, "directory.json"), directoryJson);

            var gitCommit     = GetCommit(googleapisRoot);
            var synthMetadata = new
            {
                sources = new[] {
                    new {
                        git = new
                        {
                            name   = "googleapis",
                            remote = "https://github.com/googleapis/googleapis.git",
                            sha    = gitCommit
                        }
                    }
                }
            };
            var synthMetadataJson = JsonConvert.SerializeObject(synthMetadata, Formatting.Indented);

            File.WriteAllText(Path.Combine(outputDirectory, "synth.metadata"), synthMetadataJson);
        }
Пример #6
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));
        }
Пример #7
0
        public static void Main(string[] args)
        {
            var layout = DirectoryLayout.ForApi("Grafeas.V1");

            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Grafeas.V1", "GrafeasClient.cs"))
            .RemoveProperty("GrafeasClient", "DefaultEndpoint")
            .RemoveProperty("GrafeasClient", "DefaultScopes")
            .RemoveProperty("GrafeasClient", "ChannelPool")
            .RemoveField("GrafeasClient", "s_channelPool")
            .RemoveMethod("GrafeasClient", "CreateAsync", "ServiceEndpoint", "GrafeasSettings")
            .RemoveMethod("GrafeasClient", "Create", "ServiceEndpoint", "GrafeasSettings")
            .RemoveMethod("GrafeasClient", "ShutdownDefaultChannelsAsync")
            .RemoveType("GrafeasClientBuilder")
            .Save();

            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Grafeas.V1", "ResourceNames.cs"))
            .RewriteTypeName("Grafeas.V1.ProjectName", "global::Grafeas.V1.ProjectName")
            .RewriteTypeName("Grafeas.V1.NoteName", "global::Grafeas.V1.NoteName")
            .RewriteTypeName("Grafeas.V1.OccurrenceName", "global::Grafeas.V1.OccurrenceName")
            .RewriteMemberAccess("Grafeas.V1.ProjectName.Parse", "global::Grafeas.V1.ProjectName.Parse")
            .RewriteMemberAccess("Grafeas.V1.NoteName.Parse", "global::Grafeas.V1.NoteName.Parse")
            .RewriteMemberAccess("Grafeas.V1.OccurrenceName.Parse", "global::Grafeas.V1.OccurrenceName.Parse")
            .Save();

            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Grafeas.V1.Snippets", "GrafeasClientSnippets.g.cs"))
            .Rewrite(new SnippetRewriter())
            .RewriteAlias("apis", "global::Grafeas.V1")
            .Save();

            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Grafeas.V1.Tests", "GrafeasClientTest.g.cs"))
            .RewriteAlias("apis", "global::Grafeas.V1")
            .Save();
        }
Пример #8
0
        protected override void ExecuteImpl(string[] args)
        {
            var idsToCheck = new List <string>();

            foreach (var diff in FindChangedVersions())
            {
                if (diff.OldVersion is null)
                {
                    Console.WriteLine($"{diff.Id} is new; no comparison required.");
                }
                else if (diff.NewVersion is null)
                {
                    Console.WriteLine($"{diff.Id} has been deleted; no comparison required.");
                }
                else
                {
                    // Found an API to compare. Build it locally first, so we know we're up-to-date.
                    var api = diff.Id;
                    idsToCheck.Add(api);
                    Console.WriteLine($"Building {api} locally");
                    var sourceRoot = DirectoryLayout.ForApi(api).SourceDirectory;
                    Processes.RunDotnet(sourceRoot, "build", "-nologo", "-clp:NoSummary", "-v", "quiet", "-c", "Release", api);
                }
            }
            new CheckVersionCompatibilityCommand().Execute(idsToCheck.ToArray());
        }
Пример #9
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 = ApiMetadata.LoadApis().FirstOrDefault(x => x.Id == api);

            if (apiMetadata == null)
            {
                throw new UserErrorException($"Unable to load API metadata from apis.json for {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);
        }
Пример #10
0
        protected override void ExecuteImpl(string[] args)
        {
            string id      = args[0];
            string version = args[1];

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

            string oldVersion = api.Version;

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

            GenerateProjectsCommand.GenerateMetadataFile(layout.SourceDirectory, api);
            GenerateProjectsCommand.GenerateProjects(layout.SourceDirectory, api, apiNames);
            GenerateProjectsCommand.RewriteReadme(catalog);
            GenerateProjectsCommand.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));
        }
Пример #11
0
        public static void Main(string[] args)
        {
            var layout = DirectoryLayout.ForApi("Google.Cloud.Language.V1");

            // We have manually-written versions of AnalyzeEntities, AnalyzeEntitySentiment, AnalyzeSyntax and AnnotateText
            // which accept a document and automatically request UTF-16. Currently the generated methods will just omit the
            // encoding, which means no offsets are returned. At some point the service may heuristically determine that it
            // should use UTF-16, at which point we can remove the manual code - but until then, we remove the generated code.
            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Google.Cloud.Language.V1", "LanguageServiceClient.g.cs"))
            .RemoveMethod("LanguageServiceClient", "AnalyzeEntitiesAsync", "Document", "CallSettings")
            .RemoveMethod("LanguageServiceClient", "AnalyzeEntitiesAsync", "Document", "CancellationToken")
            .RemoveMethod("LanguageServiceClient", "AnalyzeEntities", "Document", "CallSettings")
            .RemoveMethod("LanguageServiceClient", "AnalyzeEntitySentimentAsync", "Document", "CallSettings")
            .RemoveMethod("LanguageServiceClient", "AnalyzeEntitySentimentAsync", "Document", "CancellationToken")
            .RemoveMethod("LanguageServiceClient", "AnalyzeEntitySentiment", "Document", "CallSettings")
            .RemoveMethod("LanguageServiceClient", "AnalyzeSyntaxAsync", "Document", "CallSettings")
            .RemoveMethod("LanguageServiceClient", "AnalyzeSyntaxAsync", "Document", "CancellationToken")
            .RemoveMethod("LanguageServiceClient", "AnalyzeSyntax", "Document", "CallSettings")
            .RemoveMethod("LanguageServiceClient", "AnnotateTextAsync", "Document", "Features", "CallSettings")
            .RemoveMethod("LanguageServiceClient", "AnnotateTextAsync", "Document", "Features", "CancellationToken")
            .RemoveMethod("LanguageServiceClient", "AnnotateText", "Document", "Features", "CallSettings")
            .Save();

            // Remove the generated tests for these methods. This is somewhat brittle, but it's unlikely that any more flattenings will be added.
            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Google.Cloud.Language.V1.Tests", "LanguageServiceClientTest.g.cs"))
            .RemoveMethod("GeneratedLanguageServiceClientTest", "AnalyzeEntities2")
            .RemoveMethod("GeneratedLanguageServiceClientTest", "AnalyzeEntities2Async")
            .RemoveMethod("GeneratedLanguageServiceClientTest", "AnalyzeEntitySentiment2")
            .RemoveMethod("GeneratedLanguageServiceClientTest", "AnalyzeEntitySentiment2Async")
            .RemoveMethod("GeneratedLanguageServiceClientTest", "AnalyzeSyntax2")
            .RemoveMethod("GeneratedLanguageServiceClientTest", "AnalyzeSyntax2Async")
            .RemoveMethod("GeneratedLanguageServiceClientTest", "AnnotateText2")
            .RemoveMethod("GeneratedLanguageServiceClientTest", "AnnotateText2Async")
            .Save();
        }
Пример #12
0
        private static void Canonicalize(string api)
        {
            if (api == "root")
            {
                return;
            }
            var layout = DirectoryLayout.ForApi(api);
            var site   = Path.Combine(layout.DocsOutputDirectory, "site");

            foreach (var page in Directory.GetFiles(site, "*.html", new EnumerationOptions {
                RecurseSubdirectories = true
            }))
            {
                var relative = Path.GetRelativePath(site, page).Replace('\\', '/');

                var canonicalLink = Canonicalizer.GetUrl(api, relative);
                // If the page isn't on CloudSite, skip it.
                if (canonicalLink is null)
                {
                    continue;
                }

                var canonicalElement = $"<link rel=\"canonical\" href=\"{canonicalLink}\" />";

                var html = File.ReadAllText(page);
                html = html.Replace("<head>", $"<head>{canonicalElement}");
                File.WriteAllText(page, html);
            }
        }
Пример #13
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    apiCatalog  = ApiCatalog.Load();
            var    apiMetadata = apiCatalog[api];

            string output = layout.DocsOutputDirectory;

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

            CreateGoogleApisDevDocfxJson(apiCatalog, apiMetadata, output);
            CreateDevsiteDocfxJson(apiCatalog, apiMetadata, output);
            CopyAndGenerateArticles(apiMetadata, layout.DocsSourceDirectory, output);
            CreateToc(api, output);
            return(0);
        }
Пример #14
0
        private static void CompareVersions(string[] args)
        {
            if (args.Length != 0)
            {
                throw new UserErrorException($"{CompareCommand} does not accept additional arguments");
            }

            var idsToCheck = new List <string>();

            foreach (var diff in FindChangedVersions())
            {
                if (diff.OldVersion is null)
                {
                    Console.WriteLine($"{diff.Id} is new; no comparison required.");
                }
                else if (diff.NewVersion is null)
                {
                    Console.WriteLine($"{diff.Id} has been deleted; no comparison required.");
                }
                else
                {
                    // Found an API to compare. Build it locally first, so we know we're up-to-date.
                    var api = diff.Id;
                    idsToCheck.Add(api);
                    Console.WriteLine($"Building {api} locally");
                    var sourceRoot = DirectoryLayout.ForApi(api).SourceDirectory;
                    Processes.RunDotnet(sourceRoot, "build", "-nologo", "-clp:NoSummary", "-v", "quiet", "-c", "Release", api);
                }
            }
            CheckVersionCompatibility.Program.Main(idsToCheck.ToArray());
        }
Пример #15
0
        /// <summary>
        /// Remove the parts of the generated BigtableServiceApiClientBuilder that are provided manual by partial classes.
        /// </summary>
        private static void FixClientBuilder()
        {
            var layout = DirectoryLayout.ForApi("Google.Cloud.Bigtable.V2");

            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Google.Cloud.Bigtable.V2", "BigtableServiceApiClient.g.cs"))
            .RemoveMethod("BigtableServiceApiClientBuilder", "GetChannelPool")
            .Save();
        }
        private List <SmokeTest> LoadSmokeTests(string id)
        {
            var smokeTestsFile = Path.Combine(DirectoryLayout.ForApi(id).SourceDirectory, "smoketests.json");

            return(File.Exists(smokeTestsFile)
                ? JsonConvert.DeserializeObject <List <SmokeTest> >(File.ReadAllText(smokeTestsFile))
                : new List <SmokeTest>());
        }
Пример #17
0
        /// <summary>
        /// Temporary measure to remove the BigtableClientBuilder and the ChannelPool property. Bigtable uses a call invoker
        /// pool instead of a channel pool; we'll need to work out what we want the builder to look like for this.
        /// See https://github.com/googleapis/google-cloud-dotnet/issues/3117 for status.
        /// </summary>
        private static void RemoveClientBuilder()
        {
            var layout = DirectoryLayout.ForApi("Google.Cloud.Bigtable.V2");

            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Google.Cloud.Bigtable.V2", "BigtableServiceApiClient.cs"))
            .RemoveProperty("BigtableServiceApiClient", "ChannelPool")
            .RemoveType("BigtableServiceApiClientBuilder")
            .Save();
        }
Пример #18
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();
        }
Пример #19
0
        private Assembly PublishUnitTestsAndLoadAssembly(string id)
        {
            // We publish the *test* project, as that's executable - so the appropriate gRPC native
            // libraries are published as well. (They're not published within the library project.)
            Console.WriteLine($"Publishing release version of unit test project");
            var sourceRoot  = DirectoryLayout.ForApi(id).SourceDirectory;
            var testProject = $"{id}.Tests";

            Processes.RunDotnet(sourceRoot, "publish", "-nologo", "-clp:NoSummary", "-v", "quiet", "-c", "Release", testProject, "-f", TestTargetFramework);

            var assemblyFile = Path.Combine(sourceRoot, testProject, "bin", "Release", TestTargetFramework, "publish", $"{id}.dll");

            return(Assembly.LoadFrom(assemblyFile));
        }
Пример #20
0
        // TODO: Find a more robust way of detecting the clients.
        private static List <string> GetClientClasses(ApiMetadata api)
        {
            if (api.Type != ApiType.Grpc)
            {
                return(new List <string>());
            }
            var layout        = DirectoryLayout.ForApi(api.Id);
            var packageSource = Path.Combine(layout.SourceDirectory, api.Id);
            var sourceFiles   = Directory.GetFiles(packageSource, "*Client.cs").Concat(Directory.GetFiles(packageSource, "*Client.g.cs"));

            return(sourceFiles
                   .Where(file => File.ReadAllText(file).Contains(": gaxgrpc::ServiceSettingsBase")) // Check it contains a generated client
                   .Select(file => Path.GetFileName(file))                                           // Just the file name, not full path
                   .Select(file => file.Split('.')[0])                                               // Trim .cs or .g.cs
                   .OrderBy(client => client)
                   .ToList());
        }
        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");
        }
Пример #22
0
        public static void Main(string[] args)
        {
            var layout = DirectoryLayout.ForApi("Grafeas.V1");

            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Grafeas.V1", "GrafeasClient.g.cs"))
            .RemoveProperty("GrafeasClient", "DefaultEndpoint")
            .RemoveProperty("GrafeasClient", "ChannelPool")
            .RemoveMethod("GrafeasClient", "CreateAsync", "CancellationToken")
            .RemoveMethod("GrafeasClient", "Create")
            .RemoveMethod("GrafeasClient", "ShutdownDefaultChannelsAsync")
            .RemoveMethod("GrafeasClientBuilder", "GetDefaultEndpoint")
            .RemoveMethod("GrafeasClientBuilder", "GetChannelPool")
            .Save();

            SourceFile.Load(Path.Combine(layout.SourceDirectory, "Grafeas.V1.Snippets", "GrafeasClientSnippets.g.cs"))
            .Rewrite(new SnippetRewriter())
            .Save();
        }
Пример #23
0
        private static Task <AssemblyDefinition> LoadAsync(CommandLineOptions options, string file, string version)
        {
            if (version == "local")
            {
                var sourceRoot = DirectoryLayout.ForApi(options.Package).SourceDirectory;
                file = Path.Combine(sourceRoot, options.Package, "bin", "Release", options.Framework, $"{options.Package}.dll");
            }
            if (file != null)
            {
                return(Task.FromResult(Assemblies.LoadFile(file)));
            }

            // Okay, definitely loading from NuGet.
            if (version == "latest")
            {
                version = null; // This is how LoadPackageAsync loads the latest
            }
            return(Assemblies.LoadPackageAsync(options.Package, version, options.Framework, assemblyName: null));
        }
Пример #24
0
        private static void Execute(string id)
        {
            var    catalog         = ApiMetadata.LoadApis();
            var    api             = catalog.FirstOrDefault(x => x.Id == id) ?? throw new UserErrorException($"Unknown API: {id}");
            string historyFilePath = Path.Combine(DirectoryLayout.ForApi(id).DocsSourceDirectory, MarkdownFile);

            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);
            }
        }
Пример #25
0
        private static void SetVersion(string[] args)
        {
            if (args.Length != 2)
            {
                throw new UserErrorException($"{SetVersionCommand} requires two arguments: the package ID and the new version");
            }
            string id      = args[0];
            string version = args[1];
            var    catalog = ApiMetadata.LoadApis();

            var api = catalog.FirstOrDefault(x => x.Id == id);

            if (api == null)
            {
                throw new UserErrorException($"API '{id}' not found in API catalog.");
            }
            string oldVersion = api.Version;

            api.Version = version;
            var layout   = DirectoryLayout.ForApi(id);
            var apiNames = new HashSet <string>(catalog.Select(x => x.Id));

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

            // This is somewhat annoying and ugly, but never mind.
            // If we need similar code in other places, we should put it in ApiMetadata.
            JToken parsed    = JToken.Parse(File.ReadAllText(ApiMetadata.CatalogPath));
            var    apiObject = parsed
                               .Children()
                               .OfType <JObject>()
                               .FirstOrDefault(obj => obj.TryGetValue("id", out var idToken) && idToken.Value <string>() == id);

            apiObject["version"] = version;
            string formatted = parsed.ToString(Formatting.Indented);

            File.WriteAllText(ApiMetadata.CatalogPath, formatted);
            Console.WriteLine("Updated apis.json");
            Console.WriteLine();
            Console.WriteLine(new ApiVersionPair(id, oldVersion, version));
        }
Пример #26
0
        protected override void ExecuteImpl(string[] args)
        {
            string id       = args[0];
            var    assembly = PublishUnitTestsAndLoadAssembly(id);
            var    clients  = FindClients(assembly);

            if (clients.Count == 0)
            {
                throw new UserErrorException($"{id} appears to have no client types. Are you sure this is an API client library?");
            }
            Console.WriteLine($"Detect client types: {string.Join(", ", clients.Select(t => t.Name))}");
            var tests = clients.SelectMany(c => SuggestSmokeTests(c)).ToList();

            Console.WriteLine($"Number of smoke tests suggested: {tests.Count}");
            if (tests.Any())
            {
                var serializerSettings = new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore,
                    Converters        = { new StringEnumConverter(new CamelCaseNamingStrategy()) },
                    ContractResolver  = new DefaultContractResolver {
                        NamingStrategy = new CamelCaseNamingStrategy()
                    }
                };
                string testJson = JsonConvert.SerializeObject(tests, Formatting.Indented, serializerSettings);
                Console.WriteLine(testJson);
                Console.WriteLine();
                var smokeTestsFile = Path.Combine(DirectoryLayout.ForApi(id).SourceDirectory, "smoketests.json");
                if (File.Exists(smokeTestsFile))
                {
                    Console.WriteLine("smoketests.json already exists, so it has been left alone.");
                }
                else
                {
                    File.WriteAllText(smokeTestsFile, testJson);
                    Console.WriteLine("Written suggested smoke tests to smoketests.json. Please review the tests carefully before committing.");
                }
            }
        }
Пример #27
0
        private static int MainImpl(string[] args)
        {
            if (args.Length == 0 || args.Length > 2)
            {
                ThrowUsageError();
            }
            string api            = args[0];
            bool   generateReport = false;
            string reportFile     = null;

            if (args.Length == 2)
            {
                generateReport = true;
                string reportArg = args[1];
                if (!reportArg.StartsWith("--report"))
                {
                    ThrowUsageError();
                }
                if (reportArg != "--report")
                {
                    if (!reportArg.StartsWith("--report="))
                    {
                        ThrowUsageError();
                    }
                    reportFile = reportArg.Substring("--report=".Length);
                }
            }

            DirectoryLayout layout         = api == "root" ? DirectoryLayout.ForRootDocs() : DirectoryLayout.ForApi(api);
            string          snippetsSource = Directory.GetDirectories(layout.SourceDirectory, "*.Snippets").FirstOrDefault();

            if (snippetsSource == null)
            {
                Console.WriteLine($"Unable to find snippets within API {api}. Ignoring this API.");
                return(0);
            }

            string output = layout.SnippetOutputDirectory;

            if (!Directory.Exists(output))
            {
                Directory.CreateDirectory(output);
            }
            else
            {
                foreach (var file in Directory.GetFiles(output))
                {
                    File.Delete(file);
                }
            }

            var memberLookup = LoadMembersByType(layout.DocfxMetadataDirectory);

            Console.WriteLine($"Loaded {memberLookup.Count} types with {memberLookup.Sum(x => x.Count())} members");
            List <string> errors   = new List <string>();
            var           snippets = LoadAllSnippets(snippetsSource, errors);

            Console.WriteLine($"Loaded {snippets.Sum(x => x.Count())} snippets");

            foreach (var entry in snippets)
            {
                if (entry.Key.EndsWith(".cs"))
                {
                    errors.Add($"Snippet file {entry.Key} does not end with one of the accepted snippet file suffixes: {string.Join(", ", SnippetFileSuffixes)}");
                    continue;
                }

                string snippetFile = entry.Key + ".txt";
                GenerateSnippetText(Path.Combine(output, snippetFile), entry);
                MapSnippetMetadataUids(entry, memberLookup[entry.Key], errors);
                GenerateSnippetMarkdown(Path.Combine(output, entry.Key + ".md"), snippetFile, entry);
            }

            var seeAlsos = LoadAllSeeAlsos(snippetsSource, errors);

            Console.WriteLine($"Loaded {seeAlsos.Sum(x => x.Count())} see-alsos");
            foreach (var entry in seeAlsos)
            {
                MapSeeAlsoMetadataUids(entry, memberLookup, entry.Key, errors);
                GenerateSeeAlsoMarkdown(Path.Combine(output, entry.Key + ".md"), entry);
            }

            ValidateSeeAlsos(seeAlsos.SelectMany(x => x), snippets.SelectMany(x => x), errors);

            if (errors.Any())
            {
                foreach (var error in errors)
                {
                    Console.Error.WriteLine(error);
                }
                return(1);
            }

            if (generateReport)
            {
                using (var writer = reportFile == null ? Console.Out : File.CreateText(reportFile))
                {
                    GenerateReport(memberLookup, snippets, seeAlsos, writer);
                }
            }
            return(0);
        }