From 2485827729d6572a6339ff5bad64edd3752cf449 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:27:25 -0700 Subject: [PATCH 1/3] Fix docfx path building bug --- actions/docs-verifier/nuget.config | 3 ++- .../src/ActionRunner/ActionRunner.csproj | 1 + .../docs-verifier/src/ActionRunner/Program.cs | 27 +++---------------- .../BaseConfigurationReader.cs | 7 +++++ .../RedirectionVerifier/DocfxConfiguration.cs | 27 +++++++++++++++++-- .../DocfxConfigurationReader.cs | 4 +-- .../RedirectionVerifier/NavigationOptions.cs | 13 --------- .../OpenPublishingConfigReader.cs | 2 +- .../OpenPublishingRedirectionReader.cs | 2 +- .../RedirectionVerifier/RedirectionHelpers.cs | 7 ++--- .../RedirectionVerifier.csproj | 3 ++- .../RedirectionsVerifier.cs | 10 ++++--- .../WhatsNewConfiguration.cs | 11 -------- .../WhatsNewConfigurationReader.cs | 23 ---------------- 14 files changed, 53 insertions(+), 87 deletions(-) delete mode 100644 actions/docs-verifier/src/RedirectionVerifier/NavigationOptions.cs delete mode 100644 actions/docs-verifier/src/RedirectionVerifier/WhatsNewConfiguration.cs delete mode 100644 actions/docs-verifier/src/RedirectionVerifier/WhatsNewConfigurationReader.cs diff --git a/actions/docs-verifier/nuget.config b/actions/docs-verifier/nuget.config index fbcef101..2fec4fdd 100644 --- a/actions/docs-verifier/nuget.config +++ b/actions/docs-verifier/nuget.config @@ -1,7 +1,8 @@ - + + diff --git a/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj b/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj index 551ad3db..d6edb97a 100644 --- a/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj +++ b/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj @@ -8,6 +8,7 @@ + diff --git a/actions/docs-verifier/src/ActionRunner/Program.cs b/actions/docs-verifier/src/ActionRunner/Program.cs index 3ae4a0fb..bd966d0d 100644 --- a/actions/docs-verifier/src/ActionRunner/Program.cs +++ b/actions/docs-verifier/src/ActionRunner/Program.cs @@ -82,7 +82,7 @@ if (queryOrHeadingIndex > -1) { - newLink += linkError.Link.Substring(queryOrHeadingIndex); + newLink += linkError.Link[queryOrHeadingIndex..]; } file = file.Insert(linkError.UrlSpan.Start, newLink); @@ -106,14 +106,10 @@ IEnumerable matchers = await docfxConfigurationReader.MapConfigurationAsync(); IEnumerable pullRequestFiles = await GitHubPullRequest.GetPullRequestFilesAsync(pullRequestNumber); -WhatsNewConfigurationReader whatsNewConfigurationReader = new(); -string? whatsNewPath = await whatsNewConfigurationReader.MapConfigurationAsync(); - List files = - pullRequestFiles.Where(f => IsRedirectableFile(f, matchers, whatsNewPath)).ToList(); + [.. pullRequestFiles.Where(f => IsRedirectableFile(f, matchers))]; // We should only ever fail on MD and YML files, no other files require redirection. -// Also, filter out files that are part of the "What's new" directory - as they shouldn't require redirects. foreach (PullRequestFile file in files) { // Changing the extension from .yml to .md or the opposite doesn't require a redirection. @@ -137,7 +133,7 @@ return returnCode; static bool IsRedirectableFile( - PullRequestFile file, IEnumerable matchers, string? whatsNewPath) + PullRequestFile file, IEnumerable matchers) { string? deletedFileName = file.IsRenamed() ? file.PreviousFileName @@ -149,27 +145,12 @@ static bool IsRedirectableFile( // A deleted toc.yml doesn't need redirection. // Also, don't require a redirection for file patterns specified as "exclude"s in docfx config file. return !isDeletedToc && IsYmlOrMarkdownFile(deletedFileName) - && !IsInWhatsNewDirectory(deletedFileName, whatsNewPath) && - matchers.Any(m => m.Match(deletedFileName).HasMatches); + && matchers.Any(m => m.Match(deletedFileName).HasMatches); } static bool IsYmlOrMarkdownFile([NotNullWhen(true)] string? fileName) => Path.GetExtension(fileName) is ".yml" or ".md"; -static bool IsInWhatsNewDirectory(string fileName, string? whatsNewPath) -{ - if (whatsNewPath is { Length: > 0 }) - { - // Example: - // file.FileName: docs/whats-new/2021-03.md - // whatsNewPath: docs/whats-new - - return fileName.StartsWith(whatsNewPath, StringComparison.OrdinalIgnoreCase); - } - - return false; -} - static bool IsExtensionChangeOnly(string file1, string file2) => RemoveExtension(file1).Equals(RemoveExtension(file2), StringComparison.OrdinalIgnoreCase); diff --git a/actions/docs-verifier/src/BuildVerifier.IO.Abstractions/BaseConfigurationReader.cs b/actions/docs-verifier/src/BuildVerifier.IO.Abstractions/BaseConfigurationReader.cs index 7b4f0beb..1289ebfd 100644 --- a/actions/docs-verifier/src/BuildVerifier.IO.Abstractions/BaseConfigurationReader.cs +++ b/actions/docs-verifier/src/BuildVerifier.IO.Abstractions/BaseConfigurationReader.cs @@ -19,6 +19,12 @@ public abstract class BaseConfigurationReader /// public string? ConfigurationFileName { get; set; } + /// + /// The directory containing the configuration file, or null if at root. + /// For example, if ConfigurationFileName is "docs/docfx.json", this will be "docs". + /// + public string? ConfigurationDirectory { get; private set; } + /// /// Reads (or returns the cached) file. /// @@ -36,6 +42,7 @@ public abstract class BaseConfigurationReader { if (File.Exists($"{dir}/{ConfigurationFileName}")) { + ConfigurationDirectory = dir.TrimStart('.', '/', '\\'); ConfigurationFileName = $"{dir}/{ConfigurationFileName}"; _fileExists = true; break; diff --git a/actions/docs-verifier/src/RedirectionVerifier/DocfxConfiguration.cs b/actions/docs-verifier/src/RedirectionVerifier/DocfxConfiguration.cs index b816b416..37c4b223 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/DocfxConfiguration.cs +++ b/actions/docs-verifier/src/RedirectionVerifier/DocfxConfiguration.cs @@ -24,7 +24,7 @@ private IEnumerable GetDocfxContents() return Build.Contents.Where(content => content.Source is null or "." or "docs"); } - public IEnumerable GetMatchers() + public IEnumerable GetMatchers(string? configDirectory = null) { if (_matchers is null) { @@ -33,11 +33,15 @@ public IEnumerable GetMatchers() foreach (DocfxContent content in contents) { var matcher = new Matcher(StringComparison.OrdinalIgnoreCase); + + // Adjust the source path based on where docfx.json was found + string effectiveSource = GetEffectiveSource(content.Source, configDirectory); + if (content.Files is not null) { foreach (string includePattern in content.Files) { - matcher.AddInclude($"{content.Source}/{includePattern}"); + matcher.AddInclude($"{effectiveSource}/{includePattern}"); } } else @@ -61,6 +65,25 @@ public IEnumerable GetMatchers() return _matchers; } + + private static string GetEffectiveSource(string? source, string? configDirectory) + { + // If no config directory (docfx.json at root), use source as-is + if (string.IsNullOrEmpty(configDirectory)) + { + return source ?? "."; + } + + // If source is "." or null, it means the content is relative to where docfx.json is + // So we need to prepend the config directory + if (source is null or ".") + { + return configDirectory; + } + + // Otherwise, combine config directory with the specified source + return $"{configDirectory}/{source}"; + } } public sealed record DocfxBuild( diff --git a/actions/docs-verifier/src/RedirectionVerifier/DocfxConfigurationReader.cs b/actions/docs-verifier/src/RedirectionVerifier/DocfxConfigurationReader.cs index 33aa90e5..a3474166 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/DocfxConfigurationReader.cs +++ b/actions/docs-verifier/src/RedirectionVerifier/DocfxConfigurationReader.cs @@ -16,11 +16,11 @@ public DocfxConfigurationReader() public override async ValueTask> MapConfigurationAsync() { DocfxConfiguration? configuration = await ReadConfigurationAsync(); - return AdjustMatchers(configuration?.GetMatchers()); + return AdjustMatchers(configuration?.GetMatchers(ConfigurationDirectory)); } private static IEnumerable AdjustMatchers(IEnumerable? matchers) => (matchers is null || !matchers.Any()) - ? new[] { s_matchAllMatcher } + ? [s_matchAllMatcher] : matchers; } diff --git a/actions/docs-verifier/src/RedirectionVerifier/NavigationOptions.cs b/actions/docs-verifier/src/RedirectionVerifier/NavigationOptions.cs deleted file mode 100644 index 01d11e09..00000000 --- a/actions/docs-verifier/src/RedirectionVerifier/NavigationOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json.Serialization; - -namespace RedirectionVerifier; - -public record NavigationOptions( - [property: JsonPropertyName("repoTocFolder")] string? RepoTocFolder, - [property: JsonPropertyName("repoIndexFolder")] string? RepoIndexFolder) -{ - /// - /// The configured path for the "What's new" content. - /// - internal string? WhatsNewPath => RepoTocFolder ?? RepoIndexFolder; -} diff --git a/actions/docs-verifier/src/RedirectionVerifier/OpenPublishingConfigReader.cs b/actions/docs-verifier/src/RedirectionVerifier/OpenPublishingConfigReader.cs index f35f065b..217bb417 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/OpenPublishingConfigReader.cs +++ b/actions/docs-verifier/src/RedirectionVerifier/OpenPublishingConfigReader.cs @@ -14,7 +14,7 @@ public OpenPublishingConfigReader() public override async ValueTask?> MapConfigurationAsync() { OpenPublishingConfig? configuration = await ReadConfigurationAsync(); - if (configuration is { RedirectionFiles: { Length: > 0 } }) + if (configuration is { RedirectionFiles.Length: > 0 }) { return configuration.RedirectionFiles; } diff --git a/actions/docs-verifier/src/RedirectionVerifier/OpenPublishingRedirectionReader.cs b/actions/docs-verifier/src/RedirectionVerifier/OpenPublishingRedirectionReader.cs index 0c500c9b..56774913 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/OpenPublishingRedirectionReader.cs +++ b/actions/docs-verifier/src/RedirectionVerifier/OpenPublishingRedirectionReader.cs @@ -14,7 +14,7 @@ public OpenPublishingRedirectionReader(string configFileName) public override async ValueTask> MapConfigurationAsync() { OpenPublishingRedirections? configuration = await ReadConfigurationAsync(); - if (configuration is { Redirections: { Length: > 0 } }) + if (configuration is { Redirections.Length: > 0 }) { return configuration.Redirections; } diff --git a/actions/docs-verifier/src/RedirectionVerifier/RedirectionHelpers.cs b/actions/docs-verifier/src/RedirectionVerifier/RedirectionHelpers.cs index bc2607f2..5f649106 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/RedirectionHelpers.cs +++ b/actions/docs-verifier/src/RedirectionVerifier/RedirectionHelpers.cs @@ -12,12 +12,9 @@ public static class RedirectionHelpers public static async Task> GetRedirectionFileNames() { - ImmutableArray? redirectionFileNames = await GetRedirectionFilesAsync(); - // If no redirection files are found in the OPS config, just use the default name. - if (redirectionFileNames == null) - redirectionFileNames = ImmutableArray.Create(".openpublishing.redirection.json"); - + ImmutableArray? redirectionFileNames = await GetRedirectionFilesAsync() ?? + [".openpublishing.redirection.json"]; Console.WriteLine($"The following {redirectionFileNames.Value.Length} redirection files are registered:"); foreach (string filename in redirectionFileNames) { diff --git a/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj b/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj index 113d5aff..31025fe3 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj +++ b/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj @@ -5,7 +5,8 @@ enable - + + diff --git a/actions/docs-verifier/src/RedirectionVerifier/RedirectionsVerifier.cs b/actions/docs-verifier/src/RedirectionVerifier/RedirectionsVerifier.cs index c714ac13..511332ae 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/RedirectionsVerifier.cs +++ b/actions/docs-verifier/src/RedirectionVerifier/RedirectionsVerifier.cs @@ -3,16 +3,17 @@ public static class RedirectionsVerifier { /// - /// Verifies a redirection for the given source path, and write logs (using GitHub-specific syntax) to a text writer. + /// Verifies a redirection for the given source path, + /// and write logs (using GitHub-specific syntax) to a text writer. /// - /// Returns for a valid redirection; otherwise. + /// for a valid redirection; otherwise. public static async Task WriteResultsAsync( TextWriter writer, string sourcePath, IEnumerable redirections) { ArgumentNullException.ThrowIfNull(writer, nameof(writer)); List foundRedirections = - redirections.Where(redirection => redirection.MatchesSourcePath(sourcePath)).ToList(); + [.. redirections.Where(redirection => redirection.MatchesSourcePath(sourcePath))]; if (foundRedirections.Count == 0) { await writer.WriteLineAsync($"::error::No redirection is found for '{sourcePath}'."); @@ -32,7 +33,8 @@ public static async Task WriteResultsAsync( return false; } - // TODO: Verify file existence if it starts with "/". Will this require setting the docset as an env variable?. + // TODO: Verify file existence if it starts with "/". + // Will this require setting the docset as an env variable?. return true; } } diff --git a/actions/docs-verifier/src/RedirectionVerifier/WhatsNewConfiguration.cs b/actions/docs-verifier/src/RedirectionVerifier/WhatsNewConfiguration.cs deleted file mode 100644 index 5f4cd789..00000000 --- a/actions/docs-verifier/src/RedirectionVerifier/WhatsNewConfiguration.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace RedirectionVerifier; - -/// -/// Schema: https://whatsnewapi.azurewebsites.net/schema -/// Omitted parts of this that are not relevant to our needs. -/// -public record WhatsNewConfiguration( - [property: JsonPropertyName("docSetProductName")] string DocSetProductName, - [property: JsonPropertyName("navigationOptions")] NavigationOptions? NavigationOptions); diff --git a/actions/docs-verifier/src/RedirectionVerifier/WhatsNewConfigurationReader.cs b/actions/docs-verifier/src/RedirectionVerifier/WhatsNewConfigurationReader.cs deleted file mode 100644 index 76e7c42e..00000000 --- a/actions/docs-verifier/src/RedirectionVerifier/WhatsNewConfigurationReader.cs +++ /dev/null @@ -1,23 +0,0 @@ -using BuildVerifier.IO.Abstractions; - -namespace RedirectionVerifier; - -public class WhatsNewConfigurationReader - : BaseMappedConfigurationReader -{ - public WhatsNewConfigurationReader() - { - ConfigurationFileName = ".whatsnew.json"; - } - - public override async ValueTask MapConfigurationAsync() - { - WhatsNewConfiguration? configuration = await ReadConfigurationAsync(); - if (configuration?.NavigationOptions is not null) - { - return configuration.NavigationOptions.WhatsNewPath; - } - - return default; - } -} From 94bf65dcf2030083917af9d3082b241cf14ea7f3 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:28:43 -0700 Subject: [PATCH 2/3] undo one change --- actions/docs-verifier/src/ActionRunner/ActionRunner.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj b/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj index d6edb97a..551ad3db 100644 --- a/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj +++ b/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj @@ -8,7 +8,6 @@ - From ec468c727ec8867b6001e678ee7bfed1155b7fc6 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:30:45 -0700 Subject: [PATCH 3/3] don't use preview version --- .../src/RedirectionVerifier/RedirectionVerifier.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj b/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj index 31025fe3..43d07a7f 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj +++ b/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj @@ -5,7 +5,7 @@ enable - +