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
-
+