| #!/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() |