1+ # *******************************************************************************
2+ # Copyright (c) 2026 Contributors to the Eclipse Foundation
3+ #
4+ # See the NOTICE file(s) distributed with this work for additional
5+ # information regarding copyright ownership.
6+ #
7+ # This program and the accompanying materials are made available under the
8+ # terms of the Apache License Version 2.0 which is available at
9+ # https://www.apache.org/licenses/LICENSE-2.0
10+ #
11+ # SPDX-License-Identifier: Apache-2.0
12+ # *******************************************************************************
13+ from __future__ import annotations
14+
115import argparse
16+ import json
17+ import logging
218import os
319import sys
420from pathlib import Path
21+ from typing import Any , Optional
22+
23+ from jinja2 import Environment , FileSystemLoader , select_autoescape
24+
25+ try :
26+ from scripts .tooling .lib .github import fetch_compare
27+ from scripts .tooling .lib .known_good import KnownGood , load_known_good
28+ except ImportError :
29+ from lib .github import fetch_compare # type: ignore[no-redef]
30+ from lib .known_good import KnownGood , load_known_good # type: ignore[no-redef]
31+
32+ _LOG = logging .getLogger (__name__ )
533
634TEMPLATE_DIR = Path (__file__ ).parent / "assets"
735
@@ -22,8 +50,69 @@ def _resolve_path_from_bazel(path: Path) -> Path:
2250 return path .resolve ()
2351
2452
53+ def _collect_entries (known_good : KnownGood ) -> list [dict [str , Any ]]:
54+ entries = []
55+ for group_name , group_modules in known_good .modules .items ():
56+ for module in group_modules .values ():
57+ try :
58+ owner_repo = module .owner_repo
59+ except ValueError :
60+ owner_repo = None
61+ entries .append (
62+ {
63+ "name" : module .name ,
64+ "group" : group_name ,
65+ "repo" : module .repo ,
66+ "owner_repo" : owner_repo ,
67+ "hash" : module .hash ,
68+ "version" : module .version ,
69+ "branch" : module .branch ,
70+ "current_hash" : None ,
71+ "behind_by" : None ,
72+ "compare_status" : None ,
73+ }
74+ )
75+ return entries
76+
77+
78+ def _enrich_with_compare_data (entries : list [dict [str , Any ]], token : str ) -> None :
79+ for entry in entries :
80+ if not entry .get ("owner_repo" ) or not entry .get ("hash" ) or entry .get ("version" ):
81+ continue
82+ result = fetch_compare (entry ["owner_repo" ], entry ["hash" ], entry ["branch" ], token )
83+ if result :
84+ entry ["current_hash" ] = result .head_sha
85+ entry ["behind_by" ] = result .ahead_by
86+ entry ["compare_status" ] = result .status
87+ else :
88+ _LOG .warning (
89+ "Could not fetch compare data for %s@%s" , entry ["owner_repo" ], entry ["branch" ]
90+ )
91+
92+
93+ def generate_report (known_good : KnownGood , token : Optional [str ] = None ) -> str :
94+ entries = _collect_entries (known_good )
95+ if token :
96+ _enrich_with_compare_data (entries , token )
97+ env = Environment (
98+ loader = FileSystemLoader (TEMPLATE_DIR ),
99+ autoescape = select_autoescape (["html" ]),
100+ )
101+ tmpl = env .get_template ("report_template.html" )
102+ return tmpl .render (
103+ modules_json = json .dumps (entries , indent = 2 ),
104+ timestamp = known_good .timestamp ,
105+ )
106+
107+
108+ def write_report (known_good : KnownGood , output_path : Path , token : Optional [str ] = None ) -> None :
109+ Path (output_path ).write_text (generate_report (known_good , token ), encoding = "utf-8" )
110+
111+
25112def register (subparsers : argparse ._SubParsersAction ) -> None :
26- parser = subparsers .add_parser ("html_report" , help = "Generate an HTML status report from known_good.json" )
113+ parser = subparsers .add_parser (
114+ "html_report" , help = "Generate an HTML status report from known_good.json"
115+ )
27116 parser .add_argument (
28117 "--known_good" ,
29118 metavar = "PATH" ,
@@ -40,9 +129,6 @@ def register(subparsers: argparse._SubParsersAction) -> None:
40129
41130
42131def _run (args : argparse .Namespace ) -> int :
43- from scripts .tooling .lib .html_report import write_report
44- from scripts .tooling .lib .known_good import load_known_good
45-
46132 known_good_path = Path (args .known_good ) / "known_good.json"
47133 try :
48134 known_good = load_known_good (known_good_path )
@@ -52,7 +138,7 @@ def _run(args: argparse.Namespace) -> int:
52138
53139 token = os .environ .get ("GITHUB_TOKEN" )
54140 output = _resolve_path_from_bazel (Path (args .output ))
55- write_report (known_good , output , TEMPLATE_DIR , token = token )
141+ write_report (known_good , output , token = token )
56142 if token :
57143 print (f"Report written to { output } (current hashes fetched from GitHub)" )
58144 else :
0 commit comments