diff --git a/docs/specs/xref.yml b/docs/specs/xref.yml index 9f9ba034f50..4c6ae897900 100644 --- a/docs/specs/xref.yml +++ b/docs/specs/xref.yml @@ -185,31 +185,6 @@ outputs: } } --- -# Remove host from review site -# This can be removed when xref related repo migrated to v3 -repos: - https://docs.com/test-uid-conceptual#live: - - files: - docfx.yml: | - hostName: docs.microsoft.com - basePath: /base_path - xref: - - 1.xrefmap.json - docs/a.md: Link to @System.String - 1.xrefmap.json: | - { - "references":[{ - "uid": "System.String", - "name": "String", - "fullName": "System.String", - "href": "https://review.docs.microsoft.com/dotnet/api/system.string", - "nameWithType": "System.String" - }] - } -outputs: - base_path/docs/a.json: | - { "conceptual": "

Link to String

\n"} ---- # The same uid in internal and external # should resolve it from the internal one inputs: @@ -2198,3 +2173,293 @@ outputs: {"message_severity":"warning","code":"xref-not-found","message":"Cross reference not found: 'a'.","file":"c.md"} c.json: | { "conceptual": "

Link to <xref:a>

" } +--- +# [alternativeHostName][xrefmap][before] output xrefmap with main hostname +# as alternative hostname may be not available +repos: + https://docs.com/test-uid-conceptual#live: + - files: + docfx.yml: | + hostName: docs.microsoft.com + basePath: /base_path + alternativeHostName: new-docs.microsoft.com + docs/a.md: | + --- + title: Title from yaml header a + uid: a + --- + docs/b.md: | + --- + title: Title from yaml header b + uid: b + --- + docs/c.md: Link to @a +outputs: + base_path/docs/c.json: | + {"conceptual":"

Link to Title from yaml header a

\n"} + base_path/docs/a.json: + base_path/docs/b.json: + .xrefmap.json: | + { + "references":[ + { + "uid": "a", + "href": "https://docs.microsoft.com/base_path/docs/a", + "name": "Title from yaml header a" + }, + { + "uid": "b", + "href": "https://docs.microsoft.com/base_path/docs/b", + "name": "Title from yaml header b" + } + ] + } +--- +# [alternativeHostName][xrefmap][before] output xrefmap with xrefhostName if xrefhostName presents +# as alternative hostname may be not available +BuildEnvironment: Prod +repos: + https://docs.com/test-uid-conceptual#main: + - files: + docfx.yml: | + hostName: docs.microsoft.com + xrefhostName: review.docs.microsoft.com + basePath: /base_path + alternativeHostName: new-docs.microsoft.com + docs/a.md: | + --- + title: Title from yaml header a + uid: a + --- + docs/b.md: | + --- + title: Title from yaml header b + uid: b + --- + docs/c.md: Link to @a +outputs: + base_path/docs/c.json: | + {"conceptual":"

Link to Title from yaml header a

\n"} + base_path/docs/a.json: + base_path/docs/b.json: + .xrefmap.json: | + { + "references":[ + { + "uid": "a", + "href": "https://review.docs.microsoft.com/base_path/docs/a?branch=main", + "name": "Title from yaml header a" + }, + { + "uid": "b", + "href": "https://review.docs.microsoft.com/base_path/docs/b?branch=main", + "name": "Title from yaml header b" + } + ] + } +--- +# [alternativeHostName][resolve][before] resolve external xref href url and replaced with main hostname +# as alternative hostname may be not available +# remove url hostname if it matches hostname/alternativeHostname/xrefHostname/'alternativeXrefHostname' +BuildEnvironment: Prod +repos: + https://docs.com/test-alternativeHostName-before-resolve#main: + - files: + docfx.yml: | + hostName: docs.microsoft.com + xrefHostName: review.docs.microsoft.com + alternativeHostName: new-docs.microsoft.com + xref: + - 1.xrefmap.json + docs/a.md: Link to @System.String + docs/a.old.md: Link to @System.String.old + docs/b.md: Link to @System.String2 + docs/b.old.md: Link to @System.String2.old + docs/c.md: Link to @System.String3 + 1.xrefmap.json: | + { + "references":[ + { + "uid": "System.String", + "name": "String", + "href": "https://new-docs.microsoft.com/dotnet/api/system.string" + }, + { + "uid": "System.String.old", + "name": "String.old", + "href": "https://docs.microsoft.com/dotnet/api/system.string.old" + }, + { + "uid": "System.String2", + "name": "String2", + "href": "https://review.new-docs.microsoft.com/dotnet/api/system.string2" + }, + { + "uid": "System.String2.old", + "name": "String2.old", + "href": "https://review.docs.microsoft.com/dotnet/api/system.string2.old" + }, + { + "uid": "System.String3", + "name": "String3", + "href": "https://unknown-docs-3.microsoft.com/dotnet/api/system.string3" + } + ] + } +outputs: + docs/a.json: | + { "conceptual": "

Link to String

\n"} + docs/a.old.json: | + { "conceptual": "

Link to String.old

\n"} + docs/b.json: | + { "conceptual": "

Link to String2

\n"} + docs/b.old.json: | + { "conceptual": "

Link to String2.old

\n"} + docs/c.json: | + { "conceptual": "

Link to String3

\n"} +--- + +# [alternativeHostName][xrefmap][after] output xrefmap with main hostname +# as alternative hostname may be not available +repos: + https://docs.com/test-alternativeHostName-xrefmap-after#live: + - files: + docfx.yml: | + hostName: new-docs.microsoft.com + basePath: /base_path + alternativeHostName: docs.microsoft.com + docs/a.md: | + --- + title: Title from yaml header a + uid: a + --- + docs/b.md: | + --- + title: Title from yaml header b + uid: b + --- + docs/c.md: Link to @a +outputs: + base_path/docs/c.json: | + {"conceptual":"

Link to Title from yaml header a

\n"} + base_path/docs/a.json: + base_path/docs/b.json: + .xrefmap.json: | + { + "references":[ + { + "uid": "a", + "href": "https://new-docs.microsoft.com/base_path/docs/a", + "name": "Title from yaml header a" + }, + { + "uid": "b", + "href": "https://new-docs.microsoft.com/base_path/docs/b", + "name": "Title from yaml header b" + } + ] + } +--- +# [alternativeHostName][xrefmap][after] output xrefmap with xrefhostName if xrefhostName presents +# as alternative hostname may be not available +BuildEnvironment: Prod +repos: + https://docs.com/test-alternativeHostName-after0#main: + - files: + docfx.yml: | + hostName: new-docs.microsoft.com + xrefhostName: review.new-docs.microsoft.com + basePath: /base_path + alternativeHostName: docs.microsoft.com + docs/a.md: | + --- + title: Title from yaml header a + uid: a + --- + docs/b.md: | + --- + title: Title from yaml header b + uid: b + --- + docs/c.md: Link to @a +outputs: + base_path/docs/c.json: | + {"conceptual":"

Link to Title from yaml header a

\n"} + base_path/docs/a.json: + base_path/docs/b.json: + .xrefmap.json: | + { + "references":[ + { + "uid": "a", + "href": "https://review.new-docs.microsoft.com/base_path/docs/a?branch=main", + "name": "Title from yaml header a" + }, + { + "uid": "b", + "href": "https://review.new-docs.microsoft.com/base_path/docs/b?branch=main", + "name": "Title from yaml header b" + } + ] + } +--- +# [alternativeHostName][resolve][after] resolve external xref href url and replaced with main hostname +# as alternative hostname may be not available +# remove url hostname if it matches hostname/alternativeHostname/xrefHostname/'alternativeXrefHostname' +BuildEnvironment: Prod +repos: + https://docs.com/test-alternativeHostName-after2#main: + - files: + docfx.yml: | + hostName: new-docs.microsoft.com + xrefHostName: review.new-docs.microsoft.com + alternativeHostName: docs.microsoft.com + xref: + - 1.xrefmap.json + docs/a.md: Link to @System.String + docs/a.old.md: Link to @System.String.old + docs/b.md: Link to @System.String2 + docs/b.old.md: Link to @System.String2.old + docs/c.md: Link to @System.String3 + 1.xrefmap.json: | + { + "references":[ + { + "uid": "System.String", + "name": "String", + "href": "https://new-docs.microsoft.com/dotnet/api/system.string" + }, + { + "uid": "System.String.old", + "name": "String.old", + "href": "https://docs.microsoft.com/dotnet/api/system.string.old" + }, + { + "uid": "System.String2", + "name": "String2", + "href": "https://review.new-docs.microsoft.com/dotnet/api/system.string2" + }, + { + "uid": "System.String2.old", + "name": "String2.old", + "href": "https://review.docs.microsoft.com/dotnet/api/system.string2.old" + }, + { + "uid": "System.String3", + "name": "String3", + "href": "https://unknown-docs-3.microsoft.com/dotnet/api/system.string3" + } + ] + } +outputs: + docs/a.json: | + { "conceptual": "

Link to String

\n"} + docs/a.old.json: | + { "conceptual": "

Link to String.old

\n"} + docs/b.json: | + { "conceptual": "

Link to String2

\n"} + docs/b.old.json: | + { "conceptual": "

Link to String2.old

\n"} + docs/c.json: | + { "conceptual": "

Link to String3

\n"} +--- diff --git a/src/docfx/build/xref/XrefResolver.cs b/src/docfx/build/xref/XrefResolver.cs index a3d539447ab..e42eaffdd6d 100644 --- a/src/docfx/build/xref/XrefResolver.cs +++ b/src/docfx/build/xref/XrefResolver.cs @@ -15,10 +15,12 @@ internal class XrefResolver private readonly DependencyMapBuilder _dependencyMapBuilder; private readonly FileLinkMapBuilder _fileLinkMapBuilder; private readonly Repository? _repository; + + // the hostname used in output xrefmap private readonly string _xrefHostName; + private readonly InternalXrefMapBuilder _internalXrefMapBuilder; private readonly Func _jsonSchemaTransformer; - private readonly Watch _externalXrefMap; private readonly Watch<(IReadOnlyDictionary xrefsByUid, IReadOnlyDictionary xrefsByFilePath)> _internalXrefMap; @@ -48,6 +50,7 @@ public XrefResolver( _dependencyMapBuilder = dependencyMapBuilder; _fileLinkMapBuilder = fileLinkMapBuilder; _xrefHostName = string.IsNullOrEmpty(config.XrefHostName) ? config.HostName : config.XrefHostName; + _internalXrefMapBuilder = new( config, errorLog, @@ -281,21 +284,32 @@ private void ValidateExternalXref(IReadOnlyDictionary? AppDataPath { get; set; } + public static Func? BuildEnvironment { get; set; } + public static Func? GitRemoteProxy { get; set; } public static Func? HttpProxy { get; set; } diff --git a/src/docfx/config/ops/OpsConfigAdapter.cs b/src/docfx/config/ops/OpsConfigAdapter.cs index 1dc1297e308..12f8703f7dd 100644 --- a/src/docfx/config/ops/OpsConfigAdapter.cs +++ b/src/docfx/config/ops/OpsConfigAdapter.cs @@ -72,6 +72,13 @@ public OpsConfigAdapter(OpsAccessor opsAccessor) return null; } + public static string GetXrefHostNameByHostName(string hostName, string? branch = null, DocsEnvironment? env = null) + { + return (branch != "live" && (env ?? OpsAccessor.DocsEnvironment) == DocsEnvironment.Prod) + ? $"review.{hostName}" + : hostName; + } + private async Task GetBuildConfig(Uri url) { var queries = HttpUtility.ParseQueryString(url.Query); @@ -119,7 +126,7 @@ private async Task GetBuildConfig(Uri url) xrefMaps.AddRange(links); } - var xrefHostName = GetXrefHostName(docset.site_name, branch); + var xrefHostName = GetXrefHostNameBySiteName(docset.site_name, branch); var documentUrls = JsonConvert.DeserializeAnonymousType( await _opsAccessor.GetDocumentUrls(), new[] { new { log_code = "", document_url = "" } }) ?.ToDictionary(item => item.log_code, item => item.document_url); @@ -228,8 +235,8 @@ private static string GetHostName(string siteName) }; } - private static string GetXrefHostName(string siteName, string branch) + private static string GetXrefHostNameBySiteName(string siteName, string branch) { - return branch != "live" && OpsAccessor.DocsEnvironment == DocsEnvironment.Prod ? $"review.{GetHostName(siteName)}" : GetHostName(siteName); + return GetXrefHostNameByHostName(GetHostName(siteName), branch); } } diff --git a/test/docfx.SpecTest/DocfxTest.cs b/test/docfx.SpecTest/DocfxTest.cs index 372e9543b4e..9655e4a91ce 100644 --- a/test/docfx.SpecTest/DocfxTest.cs +++ b/test/docfx.SpecTest/DocfxTest.cs @@ -18,9 +18,12 @@ public static class DocfxTest private static readonly AsyncLocal> s_repos = new(); private static readonly AsyncLocal> s_remoteFiles = new(); private static readonly AsyncLocal s_appDataPath = new(); + private static readonly AsyncLocal s_buildEnvironment = new(); static DocfxTest() { + TestQuirks.BuildEnvironment = () => s_buildEnvironment.Value; + TestQuirks.AppDataPath = () => s_appDataPath.Value; TestQuirks.GitRemoteProxy = remote => @@ -80,6 +83,7 @@ public static void Run(TestData test, DocfxTestSpec spec) try { + s_buildEnvironment.Value = Enum.TryParse(spec.BuildEnvironment, out DocsEnvironment env) ? env : DocsEnvironment.PPE; s_repos.Value = repos; s_remoteFiles.Value = spec.Http; s_appDataPath.Value = appDataPath; @@ -95,6 +99,7 @@ public static void Run(TestData test, DocfxTestSpec spec) } finally { + s_buildEnvironment.Value = null; s_repos.Value = null; s_remoteFiles.Value = null; s_appDataPath.Value = null; diff --git a/test/docfx.SpecTest/DocfxTestSpec.cs b/test/docfx.SpecTest/DocfxTestSpec.cs index e74cc158d6f..c3ebca9036a 100644 --- a/test/docfx.SpecTest/DocfxTestSpec.cs +++ b/test/docfx.SpecTest/DocfxTestSpec.cs @@ -31,6 +31,8 @@ public class DocfxTestSpec public string Locale { get; set; } + public string BuildEnvironment { get; set; } = "PPE"; + [JsonConverter(typeof(OneOrManyConverter))] public string[] BuildFiles { get; set; } = Array.Empty();