diff --git a/dotnet/private/docfx_repo.bzl b/dotnet/private/docfx_repo.bzl index 0e4e5e8e8362e..48db4742478a7 100644 --- a/dotnet/private/docfx_repo.bzl +++ b/dotnet/private/docfx_repo.bzl @@ -25,8 +25,8 @@ docfx_repo = repository_rule( def _docfx_extension_impl(module_ctx): docfx_repo( name = "docfx", - version = "2.78.2", - sha256 = "68b70b5e3e3f0df0dd858b228131fec40ca45493bb5b93f77b9ab3a38b21f7fb", + version = "2.78.4", + sha256 = "56faca4233a743b446a7584ff8195d8bb09a33aaa97e9e089c1e6b112212a848", ) return module_ctx.extension_metadata(reproducible = True) diff --git a/dotnet/update-deps.sh b/dotnet/update-deps.sh index b7fa1f7fd74f2..b6e6b4dcf9571 100755 --- a/dotnet/update-deps.sh +++ b/dotnet/update-deps.sh @@ -21,3 +21,5 @@ DOTNET="${DOTNET:-dotnet}" ("$DOTNET" tool restore && "$DOTNET" tool run paket install) bazel run @rules_dotnet//tools/paket2bazel:paket2bazel -- --dependencies-file "$(pwd)/paket.dependencies" --output-folder "$(pwd)" ) + +bazel run //scripts:update_docfx diff --git a/scripts/BUILD.bazel b/scripts/BUILD.bazel index bdce92f78c3b0..4da240564d112 100644 --- a/scripts/BUILD.bazel +++ b/scripts/BUILD.bazel @@ -38,6 +38,15 @@ py_binary( srcs = ["update_multitool_binaries.py"], ) +py_binary( + name = "update_docfx", + srcs = ["update_docfx.py"], + deps = [ + requirement("packaging"), + requirement("urllib3"), + ], +) + java_binary( name = "google-java-format", jvm_flags = [ diff --git a/scripts/update_docfx.py b/scripts/update_docfx.py new file mode 100644 index 0000000000000..16dfc21b966cc --- /dev/null +++ b/scripts/update_docfx.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +"""Update docfx_repo.bzl to the latest DocFX package on NuGet. + +This script fetches the latest stable DocFX version (or a user-specified one), +computes the nupkg sha256, and rewrites dotnet/private/docfx_repo.bzl. +""" + +import argparse +import hashlib +import json +import os +from pathlib import Path + +import urllib3 +from packaging.version import InvalidVersion, Version + +NUGET_INDEX_URL = "https://api.nuget.org/v3-flatcontainer/docfx/index.json" +NUGET_NUPKG_URL = "https://api.nuget.org/v3-flatcontainer/docfx/{version}/docfx.{version}.nupkg" + +http = urllib3.PoolManager() + + +def fetch_json(url): + r = http.request("GET", url) + return json.loads(r.data) + + +def choose_version(versions, allow_prerelease, explicit_version=None): + if explicit_version: + if explicit_version not in versions: + raise ValueError(f"Requested DocFX version {explicit_version!r} not found in NuGet index") + return explicit_version + + parsed = [] + for v in versions: + try: + pv = Version(v) + except InvalidVersion: + continue + if not allow_prerelease and pv.is_prerelease: + continue + parsed.append((pv, v)) + + if not parsed: + if allow_prerelease: + raise ValueError("No parseable DocFX versions found in NuGet index") + else: + raise ValueError("No stable DocFX versions found. Use --allow-prerelease to include prereleases.") + + return max(parsed, key=lambda item: item[0])[1] + + +def sha256_of_url(url): + digest = hashlib.sha256() + r = http.request("GET", url, preload_content=False) + for chunk in r.stream(1024 * 1024): + digest.update(chunk) + r.release_conn() + return digest.hexdigest() + + +def render_docfx_repo(version, sha256): + return f'''\ +"""Repository rule to download the docfx NuGet package.""" + +_BUILD = """ +package(default_visibility = ["//visibility:public"]) +exports_files(glob(["**/*"])) +filegroup(name = "docfx_dll", srcs = ["tools/net8.0/any/docfx.dll"]) +""" + +def _docfx_repo_impl(ctx): + ctx.download_and_extract( + url = "https://api.nuget.org/v3-flatcontainer/docfx/{{0}}/docfx.{{0}}.nupkg".format(ctx.attr.version), + sha256 = ctx.attr.sha256, + type = "zip", + ) + ctx.file("BUILD.bazel", _BUILD) + +docfx_repo = repository_rule( + implementation = _docfx_repo_impl, + attrs = {{ + "version": attr.string(mandatory = True), + "sha256": attr.string(mandatory = True), + }}, +) + +def _docfx_extension_impl(module_ctx): + docfx_repo( + name = "docfx", + version = "{version}", + sha256 = "{sha256}", + ) + return module_ctx.extension_metadata(reproducible = True) + +docfx_extension = module_extension(implementation = _docfx_extension_impl) +''' + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--version", + help="Use this DocFX version instead of the latest stable.", + ) + parser.add_argument( + "--allow-prerelease", + action="store_true", + help="Allow prerelease versions when selecting latest.", + ) + parser.add_argument( + "--output", + default="dotnet/private/docfx_repo.bzl", + help="Output file path (default: dotnet/private/docfx_repo.bzl)", + ) + args = parser.parse_args() + + index = fetch_json(NUGET_INDEX_URL) + versions = index.get("versions", []) + if not versions: + raise ValueError("NuGet index returned no versions for DocFX") + + version = choose_version(versions, args.allow_prerelease, args.version) + nupkg_url = NUGET_NUPKG_URL.format(version=version) + sha256 = sha256_of_url(nupkg_url) + + output_path = Path(args.output) + if not output_path.is_absolute(): + workspace_dir = os.environ.get("BUILD_WORKSPACE_DIRECTORY") + if workspace_dir: + output_path = Path(workspace_dir) / output_path + output_path.write_text(render_docfx_repo(version, sha256)) + + print(f"Updated {output_path} to DocFX {version}") + + +if __name__ == "__main__": + main()