77from typing import Any
88
99from .build_pipeline import DEFAULT_FEED_TITLE , DEFAULT_SITE_URL , copy_if_different
10- from .publisher import publish_reports , unique_report_paths_by_content
10+ from .publisher import preflight_publish_destinations , publish_reports , require_publish_candidates
1111
1212
1313REPORT_JSON_PATTERN = re .compile (r"advisory_report_\d{4}-\d{2}-\d{2}\.json" )
@@ -24,29 +24,35 @@ def load_report(path: Path) -> dict[str, Any] | None:
2424
2525
2626def discover_report_paths (artifact_roots : list [str | Path ], explicit_reports : list [str | Path ]) -> list [Path ]:
27- candidates : list [Path ] = []
27+ explicit_candidates : list [Path ] = []
2828 for report in explicit_reports :
2929 path = Path (report )
3030 if path .exists () and path .name .startswith ("advisory_report_" ) and path .suffix == ".json" :
31- candidates .append (path )
31+ explicit_candidates .append (path )
32+ root_candidates : list [Path ] = []
3233 for root in artifact_roots :
3334 root_path = Path (root )
3435 if not root_path .exists ():
3536 continue
36- candidates .extend (
37+ root_candidates .extend (
3738 path
3839 for path in root_path .rglob ("advisory_report_*.json" )
3940 if path .is_file () and REPORT_JSON_PATTERN .fullmatch (path .name )
4041 )
4142
42- by_as_of : dict [str , Path ] = {}
43- for path in sorted (candidates , key = lambda item : (item .name , str (item ))):
44- payload = load_report (path )
45- if not payload :
43+ unique_paths : list [Path ] = []
44+ seen : set [Path ] = set ()
45+ all_candidates = explicit_candidates + root_candidates
46+ all_candidates .sort (key = lambda item : str (item ))
47+ all_candidates .sort (key = lambda item : item .name , reverse = True )
48+ candidates = all_candidates
49+ for path in candidates :
50+ resolved = path .resolve ()
51+ if resolved in seen :
4652 continue
47- as_of = str ( payload . get ( "as_of" , "" ) )
48- by_as_of [ as_of ] = path
49- return [ by_as_of [ key ] for key in sorted ( by_as_of , reverse = True )]
53+ seen . add ( resolved )
54+ unique_paths . append ( path )
55+ return unique_paths
5056
5157
5258def backfill_site_archive (
@@ -55,13 +61,23 @@ def backfill_site_archive(
5561 output_dir : str | Path ,
5662 site_url : str ,
5763 feed_title : str ,
64+ current_report : str | Path | None = None ,
5865) -> list [Path ]:
59- if not report_paths :
66+ if not report_paths and current_report is None :
6067 raise ValueError ("No advisory_report_YYYY-MM-DD.json files found for backfill." )
61- report_paths = unique_report_paths_by_content (report_paths )
68+ selection = require_publish_candidates (current_report , report_paths )
69+ report_paths = list (selection .selected_paths )
70+ preflight_publish_destinations (report_paths )
6271 output = Path (output_dir )
6372 output .mkdir (parents = True , exist_ok = True )
64- written = publish_reports (report_paths , output , site_url = site_url , feed_title = feed_title )
73+ written = publish_reports (
74+ report_paths ,
75+ output ,
76+ site_url = site_url ,
77+ feed_title = feed_title ,
78+ mandatory_current = current_report ,
79+ recovered_history = report_paths if current_report is not None else None ,
80+ )
6581 for report_path in report_paths :
6682 copy_if_different (report_path , output / report_path .name )
6783 markdown_path = report_path .with_suffix (".md" )
@@ -77,6 +93,7 @@ def build_arg_parser() -> argparse.ArgumentParser:
7793 parser = argparse .ArgumentParser (description = "Backfill advisory site archive from local workflow artifacts." )
7894 parser .add_argument ("--artifact-root" , action = "append" , default = [], help = "Directory to recursively scan for advisory reports." )
7995 parser .add_argument ("--report" , action = "append" , default = [], help = "Explicit advisory report JSON path." )
96+ parser .add_argument ("--current-report" , help = "Optional explicit mandatory current report; --report remains history-only." )
8097 parser .add_argument ("--output-dir" , required = True , help = "Static site output directory." )
8198 parser .add_argument ("--site-url" , default = DEFAULT_SITE_URL )
8299 parser .add_argument ("--feed-title" , default = DEFAULT_FEED_TITLE )
@@ -86,7 +103,13 @@ def build_arg_parser() -> argparse.ArgumentParser:
86103def main (argv : list [str ] | None = None ) -> None :
87104 args = build_arg_parser ().parse_args (argv )
88105 reports = discover_report_paths (args .artifact_root , args .report )
89- backfill_site_archive (report_paths = reports , output_dir = args .output_dir , site_url = args .site_url , feed_title = args .feed_title )
106+ backfill_site_archive (
107+ report_paths = reports ,
108+ current_report = args .current_report ,
109+ output_dir = args .output_dir ,
110+ site_url = args .site_url ,
111+ feed_title = args .feed_title ,
112+ )
90113 print (f"archive_backfill_reports={ len (reports )} output={ args .output_dir } " )
91114
92115
0 commit comments