Nuovi doc: tutorial-primo-giro, risoluzione-problemi, sviluppo, rilascio, glossario e registro delle decisioni (docs/decisioni, 6 ADR). docs/README.md e' l'indice per categoria e la tabella delle fonti uniche; il README di root rimanda a sviluppo/rilascio invece di duplicarli. scripts/CheckDocs.cs verifica link e ancore, <Version> contro CHANGELOG, copertura di architettura.md e presenza di ogni doc nell'indice; .markdownlint-cli2.jsonc configura il lint Markdown. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
60 lines
3.4 KiB
C#
60 lines
3.4 KiB
C#
// dotnet run scripts/CheckDocs.cs (dalla root del repo; exit 1 se i doc sono fuori allineamento)
|
|
// Controlla anche che ogni doc di docs/ e docs/decisioni/ sia nell'indice.
|
|
// Controlla: link relativi (file e #ancora), <Version> del csproj == ultima voce del CHANGELOG,
|
|
// ogni .cs di PrepStep/ e scripts/ citato in docs/architettura.md.
|
|
using System.Text.RegularExpressions;
|
|
|
|
var errori = new List<string>();
|
|
var mdFiles = Directory.EnumerateFiles(".", "*.md", SearchOption.AllDirectories)
|
|
.Where(f => !f.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}")
|
|
&& !f.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}")
|
|
&& !f.Contains($"{Path.DirectorySeparatorChar}raw{Path.DirectorySeparatorChar}")
|
|
&& !f.Contains($"{Path.DirectorySeparatorChar}step{Path.DirectorySeparatorChar}")).ToList();
|
|
|
|
// slug delle intestazioni come lo calcola GitHub/Gitea
|
|
static HashSet<string> Ancore(string path) =>
|
|
File.ReadLines(path).Where(l => l.StartsWith('#'))
|
|
.Select(l => Regex.Replace(Regex.Replace(l.TrimStart('#').Trim().ToLowerInvariant(), @"[`*]", ""), @"[^\p{L}\p{N}\- _]", "").Replace(' ', '-'))
|
|
.ToHashSet();
|
|
|
|
// 1. link relativi
|
|
foreach (var f in mdFiles)
|
|
{
|
|
var testo = Regex.Replace(File.ReadAllText(f), @"```.*?```", "", RegexOptions.Singleline);
|
|
foreach (Match m in Regex.Matches(testo, @"\]\(([^)\s]+)\)"))
|
|
{
|
|
var url = m.Groups[1].Value;
|
|
if (Regex.IsMatch(url, @"^[a-z]+:")) continue; // http:, mailto:, ...
|
|
var parti = url.Split('#', 2);
|
|
var target = parti[0] == "" ? f : Path.GetFullPath(Path.Combine(Path.GetDirectoryName(f)!, parti[0]));
|
|
if (!File.Exists(target) && !Directory.Exists(target)) { errori.Add($"{f}: link rotto -> {url}"); continue; }
|
|
if (parti.Length == 2 && target.EndsWith(".md") && !Ancore(target).Contains(parti[1].ToLowerInvariant()))
|
|
errori.Add($"{f}: ancora inesistente -> {url}");
|
|
}
|
|
}
|
|
|
|
// 2. versione
|
|
var version = Regex.Match(File.ReadAllText("PrepStep/PrepStep.csproj"), @"<Version>(.+?)</Version>").Groups[1].Value;
|
|
var ultima = Regex.Match(File.ReadAllText("CHANGELOG.md"), @"^## \[(\d[^\]]*)\]", RegexOptions.Multiline).Groups[1].Value;
|
|
if (version != ultima) errori.Add($"CHANGELOG.md: ultima voce {ultima} != <Version> {version} del csproj");
|
|
|
|
// 3. ogni sorgente citato nella mappa file-per-file
|
|
var arch = File.ReadAllText("docs/architettura.md");
|
|
foreach (var cs in Directory.EnumerateFiles("PrepStep", "*.cs").Concat(Directory.EnumerateFiles("scripts", "*.cs")))
|
|
{
|
|
var nome = Path.GetFileName(cs);
|
|
if (!arch.Contains(nome) && !nome.EndsWith(".Designer.cs")) errori.Add($"docs/architettura.md: {cs} non è descritto");
|
|
}
|
|
|
|
// 4. ogni doc in docs/ è raggiungibile dall'indice della sua cartella
|
|
foreach (var dir in new[] { "docs", Path.Combine("docs", "decisioni") })
|
|
{
|
|
var indice = File.ReadAllText(Path.Combine(dir, "README.md")) + (dir == "docs" ? File.ReadAllText(Path.Combine(dir, "decisioni", "README.md")) : "");
|
|
foreach (var md in Directory.EnumerateFiles(dir, "*.md").Where(f => Path.GetFileName(f) != "README.md"))
|
|
if (!indice.Contains(Path.GetFileName(md))) errori.Add($"{dir}/README.md: {Path.GetFileName(md)} non è nell'indice");
|
|
}
|
|
|
|
errori.ForEach(Console.Error.WriteLine);
|
|
Console.WriteLine(errori.Count == 0 ? $"Doc OK ({mdFiles.Count} file md)" : $"{errori.Count} problemi");
|
|
return errori.Count == 0 ? 0 : 1;
|