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}"); } }
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)); } }
/// <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); }
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)); }
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(); }
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()); }
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); }
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)); }
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(); }
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); } }
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); }
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()); }
/// <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>()); }
/// <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(); }
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(); }
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)); }
// 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"); }
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(); }
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)); }
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); } }
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)); }
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."); } } }
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); }