blob: 16dfc21b966cca900b48e264958c924acd1c9d7e [file] [log] [blame]
#!/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()