diff --git a/azure-pipelines-v3.yml b/azure-pipelines-v3.yml index cc3349e3180..f874a5da20e 100644 --- a/azure-pipelines-v3.yml +++ b/azure-pipelines-v3.yml @@ -211,8 +211,8 @@ parameters: params: https://github.com/MicrosoftDocs/sql-docs-pr --profile --timeout 120 --regression-rules docs: params: https://github.com/dotnet/docs --timeout 60 --regression-rules - # learn-pr: - # params: https://github.com/MicrosoftDocs/learn-pr --timeout 105 --no-dry-sync --regression-rules + learn-pr: + params: https://github.com/MicrosoftDocs/learn-pr --timeout 105 --no-dry-sync --regression-rules windowsserverdocs-pr: params: https://github.com/MicrosoftDocs/windowsserverdocs-pr --timeout 45 --regression-rules VBA-Docs: @@ -231,8 +231,8 @@ parameters: params: https://github.com/Azure/azure-docs-rest-apis --timeout 130 --error-level Warning powerapps-docs-web-api-ref-pr: params: https://github.com/MicrosoftDocs/powerapps-docs-web-api-ref-pr --timeout 25 - mc-docs-pr: - params: https://github.com/MicrosoftDocs/mc-docs-pr --timeout 70 + # mc-docs-pr: + # params: https://github.com/MicrosoftDocs/mc-docs-pr --timeout 70 dynamics365smb-devitpro: params: https://github.com/MicrosoftDocs/dynamics365smb-devitpro --timeout 15 test: diff --git a/docs/specs/markdown.yml b/docs/specs/markdown.yml index b26e6c32574..c06a3850813 100644 --- a/docs/specs/markdown.yml +++ b/docs/specs/markdown.yml @@ -857,3 +857,15 @@ outputs: "conceptual": "

Check Find for more details.\nThis method can be ran on Windows, and you can also used it in Azure SDK.

\n", "no-loc": ["method", "SDK"] } +--- +# remove domain for alternative hostname for absolute links +inputs: + docfx.yml: | + alternativeHostname: new-docs.microsoft.com + docs/a.md: | + [A link](https://new-docs.microsoft.com/en-us/azure) +outputs: + docs/a.json: | + { + "conceptual": "

A link

", + } diff --git a/docs/specs/ops.yml b/docs/specs/ops.yml index 130a2aac359..540d1893c33 100644 --- a/docs/specs/ops.yml +++ b/docs/specs/ops.yml @@ -191,7 +191,7 @@ outputs: "files": [ { "url": "/admin/integrate-with-microsoft-teams", - "redirect_url": "basics/teams-integration", + "redirect_url": "/admin/basics/teams-integration", "document_id": "b1c6c9d3-40a8-a21d-98e8-a3cbf54eaeb4", "document_version_independent_id": "b1c6c9d3-40a8-a21d-98e8-a3cbf54eaeb4", "canonical_url": "https://docs.com/en-us/admin/integrate-with-microsoft-teams" diff --git a/docs/specs/redirection.yml b/docs/specs/redirection.yml index 36fb1a727c3..7eee501dd05 100644 --- a/docs/specs/redirection.yml +++ b/docs/specs/redirection.yml @@ -976,3 +976,180 @@ outputs: .errors.log: | {"message_severity":"warning","code":"path-invalid","message":"Path /*invalid.md contains invalid chars '<', '>', '*'."} {"message_severity":"warning","code":"missing-attribute","message":"Missing required attribute: '(source_path or source_path_from_root) or redirect_url'."} +--- +# Remove redirect_url hostname when it is the same as hostname or alternativeHostName in despite of redirect_document_id +repos: + https://docs.com/ops/redirect: + - files: + redirections.json: | + { + "redirections": [ + { + "source_path": "original.md", + "redirect_url": "https://docs.com/folder/a", + "redirect_document_id": "true" + }, + { + "source_path": "original2.md", + "redirect_url": "https://docs.com/folder/b", + "redirect_document_id": "false" + }, + { + "source_path": "original3.md", + "redirect_url": "https://new-docs.com/folder/c", + "redirect_document_id": "true" + }, + { + "source_path": "original4.md", + "redirect_url": "https://new-docs.com/folder/d", + "redirect_document_id": "false" + }, + { + "source_path": "original5.md", + "redirect_url": "https://docs-diff.com/folder/c", + "redirect_document_id": "true" + }, + { + "source_path": "original6.md", + "redirect_url": "https://docs-diff.com/folder/d", + "redirect_document_id": "false" + }, + ] + } + docfx.yml: | + hostname: docs.com + alternativeHostName: new-docs.com + name: docset + product: product + folder/index.md: +outputs: + folder/index.json: + .publish.json: | + { + "name": "docset", + "product": "product", + "base_path": "/", + "files": [ + { + "url": "/original", + "redirect_url": "/folder/a", + "document_id": "6172869a-1d42-80e4-5d0c-707167a0d4c1", + "document_version_independent_id": "6172869a-1d42-80e4-5d0c-707167a0d4c1", + "canonical_url": "https://docs.com/en-us/original" + }, + { + "url": "/original2", + "redirect_url": "/folder/b", + "document_id": "0e929cdb-46ae-915a-031f-4c6f8e8f6a1a", + "document_version_independent_id": "0e929cdb-46ae-915a-031f-4c6f8e8f6a1a", + "canonical_url": "https://docs.com/en-us/original2" + }, + { + "url": "/original3", + "redirect_url": "/folder/c", + "document_id": "01bf1826-aa28-49c6-d258-4c3837762c95", + "document_version_independent_id": "01bf1826-aa28-49c6-d258-4c3837762c95", + "canonical_url": "https://docs.com/en-us/original3" + }, + { + "url": "/original4", + "redirect_url": "/folder/d", + "document_id": "168b8137-5da2-ede9-224a-b295bb82ee6a", + "document_version_independent_id": "168b8137-5da2-ede9-224a-b295bb82ee6a", + "canonical_url": "https://docs.com/en-us/original4" + }, + { + "url": "/original5", + "redirect_url": "https://docs-diff.com/folder/c", + "document_id": "5269a2f9-8e29-8fed-7da3-4b08cbcb5972", + "document_version_independent_id": "5269a2f9-8e29-8fed-7da3-4b08cbcb5972", + "canonical_url": "https://docs.com/en-us/original5" + }, + { + "url": "/original6", + "redirect_url": "https://docs-diff.com/folder/d", + "document_id": "3c570421-59fc-1dc7-d86a-43dbdf5d391b", + "document_version_independent_id": "3c570421-59fc-1dc7-d86a-43dbdf5d391b", + "canonical_url": "https://docs.com/en-us/original6" + }, + { + "url": "/folder/", + "path": "folder/index.json", + "source_path": "folder/index.md", + "canonical_url": "https://docs.com/en-us/folder/", + "document_id": "87407f22-7c8e-317e-430d-96980c2fd1a6", + "document_version_independent_id": "87407f22-7c8e-317e-430d-96980c2fd1a6", + "site_name": "Docs", + "depot_name": "product.docset", + } + ] + } + .errors.log: | + {"message_severity":"suggestion","code":"redirect-url-invalid","message":"Can't redirect document ID for redirected file 'original.md' because redirect URL 'https://docs.com/folder/a' is invalid or is in a different docset. Specify a redirect_url in the same docset, or set redirect_document_id to false in .openpublishing.redirection.json.","file":"redirections.json"} + {"message_severity":"suggestion","code":"redirect-url-invalid","message":"Can't redirect document ID for redirected file 'original3.md' because redirect URL 'https://new-docs.com/folder/c' is invalid or is in a different docset. Specify a redirect_url in the same docset, or set redirect_document_id to false in .openpublishing.redirection.json.","file":"redirections.json"} + {"message_severity":"suggestion","code":"redirect-url-invalid","message":"Can't redirect document ID for redirected file 'original5.md' because redirect URL 'https://docs-diff.com/folder/c' is invalid or is in a different docset. Specify a redirect_url in the same docset, or set redirect_document_id to false in .openpublishing.redirection.json.","file":"redirections.json"} +--- +# convert relative redirect url to absolute path in despite of redirect_document_id config +repos: + https://docs.com/ops/redirect: + - files: + redirections.json: | + { + "redirections": [ + { + "source_path": "original.md", + "redirect_url": "folder/a", + "redirect_document_id": "true" + }, + { + "source_path": "original2.md", + "redirect_url": "folder/b", + "redirect_document_id": "false" + } + ] + } + docfx.yml: | + hostname: docs.com + name: docset + product: product + folder/index.md: +outputs: + folder/index.json: + .publish.json: | + { + "name": "docset", + "product": "product", + "base_path": "/", + "files": [ + { + "url": "/original", + "locale": "en-us", + "redirect_url": "/folder/a", + "document_id": "6172869a-1d42-80e4-5d0c-707167a0d4c1", + "document_version_independent_id": "6172869a-1d42-80e4-5d0c-707167a0d4c1", + "canonical_url": "https://docs.com/en-us/original" + }, + { + "url": "/original2", + "locale": "en-us", + "redirect_url": "/folder/b", + "document_id": "0e929cdb-46ae-915a-031f-4c6f8e8f6a1a", + "document_version_independent_id": "0e929cdb-46ae-915a-031f-4c6f8e8f6a1a", + "canonical_url": "https://docs.com/en-us/original2" + }, + { + "url": "/folder/", + "path": "folder/index.json", + "source_path": "folder/index.md", + "locale": "en-us", + "canonical_url": "https://docs.com/en-us/folder/", + "document_id": "87407f22-7c8e-317e-430d-96980c2fd1a6", + "document_version_independent_id": "87407f22-7c8e-317e-430d-96980c2fd1a6", + "site_name": "Docs", + "depot_name": "product.docset", + } + ] + } + .errors.log: | + {"message_severity":"suggestion","code":"redirect-url-invalid","message":"Can't redirect document ID for redirected file 'original.md' because redirect URL 'folder/a' is invalid or is in a different docset. Specify a redirect_url in the same docset, or set redirect_document_id to false in .openpublishing.redirection.json.","file":"redirections.json"} +--- 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/link/LinkResolver.cs b/src/docfx/build/link/LinkResolver.cs index b91b5908172..285de94e427 100644 --- a/src/docfx/build/link/LinkResolver.cs +++ b/src/docfx/build/link/LinkResolver.cs @@ -142,7 +142,6 @@ public LinkResolver( ValidateLink(inclusionRoot, linkNode); if (linkType == LinkType.External) { - var resolvedHref = _config.RemoveHostName ? UrlUtility.RemoveLeadingHostName(href, _config.HostName) : href; if (_config.TrustedDomains.TryGetValue(tagName, out var domains) && !domains.IsTrusted(href, out var untrustedDomain)) { if (tagName == "img") @@ -155,6 +154,8 @@ public LinkResolver( } return (errors, "", fragment, LinkType.AbsolutePath, null, false); } + var resolvedHref = _config.RemoveHostName ? UrlUtility.RemoveLeadingHostName(href, _config.HostName) : href; + resolvedHref = UrlUtility.RemoveLeadingHostName(resolvedHref, _config.AlternativeHostName, true); return (errors, resolvedHref, fragment, LinkType.AbsolutePath, null, false); } diff --git a/src/docfx/build/redirection/RedirectionProvider.cs b/src/docfx/build/redirection/RedirectionProvider.cs index 877acfc19a2..15cd85a786e 100644 --- a/src/docfx/build/redirection/RedirectionProvider.cs +++ b/src/docfx/build/redirection/RedirectionProvider.cs @@ -95,14 +95,14 @@ public FilePath GetOriginalFile(FilePath file) using (Progress.Start("Loading redirections")) { var redirections = LoadRedirectionModel(); - var redirectUrls = GetRedirectUrls(redirections, _config.HostName); + var redirectUrls = GetRedirectUrls(redirections); var redirectPaths = redirectUrls.Keys.Select(x => x.Path).ToHashSet(); return (redirectUrls, redirectPaths, redirections); } } - private Dictionary GetRedirectUrls(RedirectionItem[] redirections, string hostName) + private Dictionary GetRedirectUrls(RedirectionItem[] redirections) { var redirectUrls = new Dictionary(); @@ -127,23 +127,21 @@ private Dictionary GetRedirectUrls(RedirectionItem[] redirecti var monikers = item.Monikers is null ? default : _monikerProvider.Validate(_errors, item.Monikers); var filePath = FilePath.Redirection(path, monikers); - if (item.RedirectDocumentId) + switch (UrlUtility.GetLinkType(absoluteRedirectUrl)) { - switch (UrlUtility.GetLinkType(absoluteRedirectUrl)) - { - case LinkType.RelativePath: - var siteUrl = _documentProvider.GetSiteUrl(filePath); - absoluteRedirectUrl = PathUtility.Normalize(Path.Combine(Path.GetDirectoryName(siteUrl) ?? "", absoluteRedirectUrl)); - break; - case LinkType.AbsolutePath: - break; - case LinkType.External: - absoluteRedirectUrl = UrlUtility.RemoveLeadingHostName(absoluteRedirectUrl, hostName, removeLocale: true); - break; - default: - _errors.Add(Errors.Redirection.RedirectUrlInvalid(path, redirectUrl)); - break; - } + case LinkType.RelativePath: + var siteUrl = _documentProvider.GetSiteUrl(filePath); + absoluteRedirectUrl = PathUtility.Normalize(Path.Combine(Path.GetDirectoryName(siteUrl) ?? "", absoluteRedirectUrl)); + break; + case LinkType.AbsolutePath: + break; + case LinkType.External: + absoluteRedirectUrl = UrlUtility.RemoveLeadingHostName(absoluteRedirectUrl, _config.HostName, removeLocale: true); + absoluteRedirectUrl = UrlUtility.RemoveLeadingHostName(absoluteRedirectUrl, _config.AlternativeHostName, removeLocale: true); + break; + default: + _errors.Add(Errors.Redirection.RedirectUrlInvalid(path, redirectUrl)); + break; } if (!redirectUrls.TryAdd(filePath, absoluteRedirectUrl)) 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 RedirectionFiles { get; init; } = new(); + public string AlternativeHostName { get; init; } = string.Empty; + public IEnumerable> GetFileReferences() { foreach (var url in Xref) diff --git a/src/docfx/config/EnvironmentVariable.cs b/src/docfx/config/EnvironmentVariable.cs index 3985daa2999..a1173a9847c 100644 --- a/src/docfx/config/EnvironmentVariable.cs +++ b/src/docfx/config/EnvironmentVariable.cs @@ -27,6 +27,9 @@ public static class EnvironmentVariable public static string? SessionId => GetValue("DOCFX_SESSION_ID"); + // TODO: remove after switch complete + public static string? PPEDefaultDomainHostName => GetValue("DOCFX_PPE_DEFAULT_DOMAIN_HOST_NAME"); + private static string? GetValue(string name) { var value = Environment.GetEnvironmentVariable(name); diff --git a/src/docfx/config/TestQuirks.cs b/src/docfx/config/TestQuirks.cs index a267a6e46c7..263cbe08f5e 100644 --- a/src/docfx/config/TestQuirks.cs +++ b/src/docfx/config/TestQuirks.cs @@ -7,6 +7,8 @@ internal static class TestQuirks { public static Func? 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 a01858c7655..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); @@ -223,13 +230,13 @@ private static string GetHostName(string siteName) _ => OpsAccessor.DocsEnvironment switch { DocsEnvironment.Prod => "docs.microsoft.com", - _ => "ppe.docs.microsoft.com", + _ => EnvironmentVariable.PPEDefaultDomainHostName ?? "ppe.docs.microsoft.com", }, }; } - 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/src/docfx/docfx.csproj b/src/docfx/docfx.csproj index 8ee836fc3f3..df818f21011 100644 --- a/src/docfx/docfx.csproj +++ b/src/docfx/docfx.csproj @@ -22,7 +22,7 @@ - + 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();