diff options
| author | Leitwolf | 2026-10-15 18:43:48 +0300 |
|---|---|---|
| committer | GitHub | 2026-10-15 18:43:48 +0300 |
| commit | 0b4634261957c81626cf134d3126ae3d3a04735a (patch) | |
| tree | 4690238a1af85bfb71a3ef237ed1ac8bae32983c /Documents/api | |
| parent | c525fa05bbe85f3e7337d4a43c21ee6c864c839a (diff) | |
| parent | 607a15b1e856097ff4f8e3892368217a4672f16b (diff) | |
| download | website-0b4634261957c81626cf134d3126ae3d3a04735a.tar.gz | |
Merge pull request #34 from Progmasoft/feature/tala-run
Serve tala.run, document talac and Tazel, and move the site API to ASP.NET Core 10
Diffstat (limited to 'Documents/api')
| -rw-r--r-- | Documents/api/CheckDocumentation.cs | 118 | ||||
| -rw-r--r-- | Documents/api/docfx.json | 39 | ||||
| -rw-r--r-- | Documents/api/index.md | 31 | ||||
| -rw-r--r-- | Documents/api/namespaces/Progmasoft.Tala.Website.Api.md | 10 | ||||
| -rw-r--r-- | Documents/api/packages.lock.json | 35 | ||||
| -rw-r--r-- | Documents/api/toc.yml | 7 |
6 files changed, 240 insertions, 0 deletions
diff --git a/Documents/api/CheckDocumentation.cs b/Documents/api/CheckDocumentation.cs new file mode 100644 index 0000000..f6b2da8 --- /dev/null +++ b/Documents/api/CheckDocumentation.cs @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +// SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 + +// Documentation coverage gate of the API reference. +// +// DocFX builds a reference from whatever comments exist and does not report a declaration that has none. This +// program reads the metadata DocFX generated and fails when a namespace, type or member is undocumented, so the +// reference cannot contain an entry without a description. +// +// Usage, after `dotnet docfx metadata Documents/api/docfx.json`: +// +// dotnet run Documents/api/CheckDocumentation.cs -- Documents/api +// +// The exit code is 0 when everything is documented, 1 when something is not, and 2 when the metadata is missing. + +using System.Text.RegularExpressions; + +string root = args.Length > 0 ? args[0] : "Documents/api"; +string referenceDirectory = Path.Combine(root, "reference"); +string namespaceDirectory = Path.Combine(root, "namespaces"); + +if (!Directory.Exists(referenceDirectory)) +{ + Console.Error.WriteLine($"No metadata in '{referenceDirectory}'. Run 'dotnet docfx metadata' first."); + return 2; +} + +// A namespace has no declaration that could carry a comment. Its description is an overwrite file named after it. +HashSet<string> describedNamespaces = Directory.Exists(namespaceDirectory) + ? Directory.EnumerateFiles(namespaceDirectory, "*.md") + .Where(HasDescription) + .Select(path => Path.GetFileNameWithoutExtension(path)!) + .ToHashSet(StringComparer.Ordinal) + : []; + +List<string> undocumented = []; +int checkedItems = 0; + +foreach (string file in Directory.EnumerateFiles(referenceDirectory, "*.yml").Order(StringComparer.Ordinal)) +{ + if (Path.GetFileName(file) == "toc.yml") + { + continue; + } + + // The items a file defines come first; the `references` section after them repeats names of other files. + string text = File.ReadAllText(file).ReplaceLineEndings("\n"); + int references = text.IndexOf("\nreferences:", StringComparison.Ordinal); + string definitions = references < 0 ? text : text[..references]; + + foreach (string item in ItemStart().Split(definitions).Skip(1)) + { + string uid = item[..item.IndexOf('\n')].Trim(); + string type = ItemType().Match(item) is { Success: true } match ? match.Groups[1].Value : "Item"; + checkedItems++; + + bool documented = type == "Namespace" + ? describedNamespaces.Contains(uid) + : ItemSummary().IsMatch(item); + if (!documented) + { + undocumented.Add($"{type} {uid}"); + } + } +} + +if (checkedItems == 0) +{ + Console.Error.WriteLine($"The metadata in '{referenceDirectory}' defines nothing; the gate would pass vacuously."); + return 2; +} + +if (undocumented.Count != 0) +{ + Console.Error.WriteLine($"{undocumented.Count} of {checkedItems} API items have no documentation:"); + foreach (string entry in undocumented) + { + Console.Error.WriteLine($" {entry}"); + } + + Console.Error.WriteLine("Add a <summary> to each declaration, or a file in Documents/api/namespaces for a namespace."); + return 1; +} + +Console.WriteLine($"All {checkedItems} API items are documented."); +return 0; + +// An overwrite file describes its namespace when it has text after its front matter and comment header. +static bool HasDescription(string path) +{ + string text = File.ReadAllText(path).ReplaceLineEndings("\n"); + int frontMatterEnd = text.IndexOf("\n---", 3, StringComparison.Ordinal); + if (!text.StartsWith("---\n", StringComparison.Ordinal) || frontMatterEnd < 0) + { + return false; + } + + string body = HtmlComment().Replace(text[(frontMatterEnd + 4)..], string.Empty); + return text[..frontMatterEnd].Contains($"uid: {Path.GetFileNameWithoutExtension(path)}", StringComparison.Ordinal) + && !string.IsNullOrWhiteSpace(body); +} + +internal static partial class Program +{ + // Every defined item starts with its identifier at the left margin. + [GeneratedRegex(@"^- uid: ", RegexOptions.Multiline)] + private static partial Regex ItemStart(); + + [GeneratedRegex(@"^ type: (\w+)", RegexOptions.Multiline)] + private static partial Regex ItemType(); + + // A summary with content: text on the same line, or a block scalar followed by an indented line. + [GeneratedRegex(@"^ summary: (?:[^\s>|].*|[>|][-+]?\n \S)", RegexOptions.Multiline)] + private static partial Regex ItemSummary(); + + [GeneratedRegex(@"<!--.*?-->", RegexOptions.Singleline)] + private static partial Regex HtmlComment(); +} diff --git a/Documents/api/docfx.json b/Documents/api/docfx.json new file mode 100644 index 0000000..58bc6c9 --- /dev/null +++ b/Documents/api/docfx.json @@ -0,0 +1,39 @@ +{ + "metadata": [ + { + "src": [ + { + "src": "../../backend/Progmasoft.Tala.Website.Api", + "files": [ + "Progmasoft.Tala.Website.Api.csproj" + ] + } + ], + "dest": "reference", + "includePrivateMembers": true + } + ], + "build": { + "content": [ + { + "files": [ + "index.md", + "toc.yml", + "reference/**/*.yml" + ] + } + ], + "dest": "_site", + "globalMetadata": { + "_appTitle": "Tala website API", + "_enableSearch": false + }, + "overwrite": [ + { + "files": [ + "namespaces/*.md" + ] + } + ] + } +} diff --git a/Documents/api/index.md b/Documents/api/index.md new file mode 100644 index 0000000..8cbed8a --- /dev/null +++ b/Documents/api/index.md @@ -0,0 +1,31 @@ +<!-- +SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 +--> + +# Tala website API + +Reference documentation of the website's ASP.NET Core API, generated by DocFX from the XML documentation comments +of `backend/Progmasoft.Tala.Website.Api`. The reference includes internal and private members: it documents the code +for the people who maintain it. + +Build and check it from the repository root: + +```sh +dotnet tool restore +dotnet docfx Documents/api/docfx.json --warningsAsErrors +dotnet run Documents/api/CheckDocumentation.cs -- Documents/api +``` + +## What the gate requires + +Three things fail the check, locally and in CI: + +- **A broken link or an unresolved reference.** DocFX runs with every warning treated as an error. +- **An undocumented public declaration or a wrong `cref`.** The API project compiles its XML documentation, and the + build treats the compiler's documentation warnings as errors. +- **Any undocumented item of the reference.** `CheckDocumentation.cs` reads the metadata DocFX generated and fails + when a namespace, type or member has no summary, internal and private members included. + +A namespace has no declaration that could hold a comment. Its description is a file in `namespaces/`, named after +the namespace. diff --git a/Documents/api/namespaces/Progmasoft.Tala.Website.Api.md b/Documents/api/namespaces/Progmasoft.Tala.Website.Api.md new file mode 100644 index 0000000..de90b81 --- /dev/null +++ b/Documents/api/namespaces/Progmasoft.Tala.Website.Api.md @@ -0,0 +1,10 @@ +--- +uid: Progmasoft.Tala.Website.Api +summary: *content +--- + +<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> --> +<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 --> + +The API of the language website: the site's own public state at `/api/v1/site` and a liveness probe at `/health`. +It has no account, package, publication or registry endpoint. diff --git a/Documents/api/packages.lock.json b/Documents/api/packages.lock.json new file mode 100644 index 0000000..0f57a57 --- /dev/null +++ b/Documents/api/packages.lock.json @@ -0,0 +1,35 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "Microsoft.DotNet.ILCompiler": { + "type": "Direct", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "tnG8ntt/Bk6odvHREnGLMo3PEiihy5iSlIFVp0JbIo00GKtNRt2k73eKZbPqR5yaJNIa3z8R86YLwbxfqpb17g==" + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + } + }, + "net10.0/win-x64": { + "Microsoft.DotNet.ILCompiler": { + "type": "Direct", + "requested": "[10.0.10, )", + "resolved": "10.0.10", + "contentHash": "tnG8ntt/Bk6odvHREnGLMo3PEiihy5iSlIFVp0JbIo00GKtNRt2k73eKZbPqR5yaJNIa3z8R86YLwbxfqpb17g==", + "dependencies": { + "runtime.win-x64.Microsoft.DotNet.ILCompiler": "10.0.10" + } + }, + "runtime.win-x64.Microsoft.DotNet.ILCompiler": { + "type": "Transitive", + "resolved": "10.0.10", + "contentHash": "e5P00ObvdmAl49KkvGvqDLE5+EXu/IdXCDsLghgKFXKrNybyxMmH12HuGrsO7LTIGjXI58tyN3Hwxwr/MR0d8A==" + } + } + } +}
\ No newline at end of file diff --git a/Documents/api/toc.yml b/Documents/api/toc.yml new file mode 100644 index 0000000..cf49dc1 --- /dev/null +++ b/Documents/api/toc.yml @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> +# SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 + +- name: Overview + href: index.md +- name: Reference + href: reference/ |