diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c2ecef6e023..82f921c982c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,9 +16,9 @@ jobs: - name: dotnet publish run: | - dotnet publish src/docfx -f net8.0 -c Release /p:Version=${GITHUB_REF_NAME#v} --self-contained -r win-x64 -o drop/publish/win-x64 /p:PlaywrightPlatform=win - dotnet publish src/docfx -f net8.0 -c Release /p:Version=${GITHUB_REF_NAME#v} --self-contained -r linux-x64 -o drop/publish/linux-x64 /p:PlaywrightPlatform=linux - dotnet publish src/docfx -f net8.0 -c Release /p:Version=${GITHUB_REF_NAME#v} --self-contained -r osx-x64 -o drop/publish/osx-x64 /p:PlaywrightPlatform=osx + dotnet publish src/docfx -f net8.0 -c Release /p:Version=${GITHUB_REF_NAME#v} --self-contained -r win-x64 -o drop/publish/win-x64 + dotnet publish src/docfx -f net8.0 -c Release /p:Version=${GITHUB_REF_NAME#v} --self-contained -r linux-x64 -o drop/publish/linux-x64 + dotnet publish src/docfx -f net8.0 -c Release /p:Version=${GITHUB_REF_NAME#v} --self-contained -r osx-x64 -o drop/publish/osx-x64 mkdir -p drop/bin - run: zip -r ../../bin/docfx-win-x64-${GITHUB_REF_NAME}.zip . diff --git a/Directory.Build.props b/Directory.Build.props index cddf0d2c372..46f3e4a1dc1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -59,4 +59,12 @@ + + + + + + + + diff --git a/docs/reference/docfx-environment-variables-reference.md b/docs/reference/docfx-environment-variables-reference.md index e48301347d1..ad8ed1945bc 100644 --- a/docs/reference/docfx-environment-variables-reference.md +++ b/docs/reference/docfx-environment-variables-reference.md @@ -26,4 +26,9 @@ This setting is intended to be used on offline environment. ## `DOCFX_PDF_TIMEOUT` -Maximum time in milliseconds to override the default [Playwright timeout](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) for PDF generation. \ No newline at end of file +Maximum time in milliseconds to override the default [Playwright timeout](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) for PDF generation. + +## `PLAYWRIGHT_NODEJS_PATH` + +Custom Node.js executable path that will be used by the `docfx pdf' command. +By default, docfx automatically detect installed Node.js from `PATH`. diff --git a/src/Docfx.App/Docfx.App.csproj b/src/Docfx.App/Docfx.App.csproj index 8a0ae58e539..609196c0de2 100644 --- a/src/Docfx.App/Docfx.App.csproj +++ b/src/Docfx.App/Docfx.App.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Docfx.App/Helpers/PlaywrightHelper.cs b/src/Docfx.App/Helpers/PlaywrightHelper.cs new file mode 100644 index 00000000000..57e08f25112 --- /dev/null +++ b/src/Docfx.App/Helpers/PlaywrightHelper.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Docfx.Common; +using Docfx.Exceptions; + +#nullable enable + +namespace Docfx; + +internal static class PlaywrightHelper +{ + public static void EnsurePlaywrightNodeJsPath() + { + // Skip if playwright environment variable exists. + if (Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_SEARCH_PATH") != null) + return; + + if (Environment.GetEnvironmentVariable("PLAYWRIGHT_NODEJS_PATH") != null) + return; + + if (!TryFindNodeExecutable(out var exePath, out var nodeVersion)) + { + throw new DocfxException("Node.js executable is not found. Try to install Node.js or set the `PLAYWRIGHT_NODEJS_PATH` environment variable."); + } + + Logger.LogInfo($"Using Node.js {nodeVersion} executable."); + Logger.LogVerbose($"Path: {exePath}"); + + Environment.SetEnvironmentVariable("PLAYWRIGHT_NODEJS_PATH", exePath, EnvironmentVariableTarget.Process); + } + + private static bool TryFindNodeExecutable(out string exePath, out string nodeVersion) + { + // Find Node.js executable installation path from PATHs. + string exeName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "node.exe" : "node"; + + var pathEnv = Environment.GetEnvironmentVariable("PATH"); + if (pathEnv == null) + throw new DocfxException("Failed to get `PATH` environment variable."); + + var paths = pathEnv.Split(Path.PathSeparator); + foreach (var path in paths) + { + string fullPath = Path.GetFullPath(Path.Combine(path, exeName)); + + if (File.Exists(fullPath)) + { + exePath = fullPath; + nodeVersion = GetNodeVersion(exePath); + return true; + } + } + + exePath = ""; + nodeVersion = ""; + return false; + } + + /// + /// Returns `node --version` command result + /// + private static string GetNodeVersion(string exePath) + { + using var memoryStream = new MemoryStream(); + using var stdoutWriter = new StreamWriter(memoryStream); + + var exitCode = CommandUtility.RunCommand(new CommandInfo { Name = exePath, Arguments = "--version" }, stdoutWriter); + + if (exitCode != 0) + return ""; + + stdoutWriter.Flush(); + memoryStream.Position = 0; + + using var streamReader = new StreamReader(memoryStream); + return streamReader.ReadLine() ?? ""; + } +} diff --git a/src/Docfx.App/PdfBuilder.cs b/src/Docfx.App/PdfBuilder.cs index 25b7e225467..0b8240a2ef3 100644 --- a/src/Docfx.App/PdfBuilder.cs +++ b/src/Docfx.App/PdfBuilder.cs @@ -47,6 +47,11 @@ class Outline public string? pdfFooterTemplate { get; init; } } + static PdfBuilder() + { + PlaywrightHelper.EnsurePlaywrightNodeJsPath(); + } + public static Task Run(BuildJsonConfig config, string configDirectory, string? outputDirectory = null) { var outputFolder = Path.GetFullPath(Path.Combine( diff --git a/test/docfx.Snapshot.Tests/PercyTest.cs b/test/docfx.Snapshot.Tests/PercyTest.cs index 59a2b58261c..7f28fc88e8e 100644 --- a/test/docfx.Snapshot.Tests/PercyTest.cs +++ b/test/docfx.Snapshot.Tests/PercyTest.cs @@ -38,6 +38,7 @@ public PercyFactAttribute() static PercyTest() { + PlaywrightHelper.EnsurePlaywrightNodeJsPath(); Microsoft.Playwright.Program.Main(["install", "chromium"]); }