11#!/usr/bin/env python3
2- """Build the ordered list of instrumentation modules for this review run.
2+ """Pick the next instrumentation module for this cleanup run.
33
4- Reads module list from settings.gradle.kts, filters out already-reviewed
5- modules (read from the otelbot/module-cleanup-progress branch by the workflow
6- and passed via REVIEW_PROGRESS), respects the open-PR cap, and writes a
7- `modules` JSON array + `has_work` flag to $GITHUB_OUTPUT.
4+ Reads the module list from settings.gradle.kts, filters out already-processed
5+ modules (passed via REVIEW_PROGRESS), and emits a single module to walk this
6+ run plus a count of how many unprocessed modules remain after it.
87
9- The review job processes modules sequentially on a single branch, stopping
10- after it accumulates at least `FILE_THRESHOLD` modified files, so the list
11- emitted here is an upper-bound slice the job is allowed to walk through .
8+ The workflow chains itself one module at a time. The finalize step uses
9+ `queue_remaining` to decide whether to self-dispatch or flush the pending
10+ queue into a PR .
1211
1312Environment variables:
14- GITHUB_OUTPUT - path to the GitHub Actions output file
15- GH_TOKEN - token for `gh` CLI (set automatically by the workflow)
16- REVIEW_PROGRESS - newline-separated list of reviewed module names
17- (contents of reviewed.txt on the progress branch)
13+ GITHUB_OUTPUT - path to the GitHub Actions output file
14+ GH_TOKEN - token for `gh` CLI (set automatically by the workflow)
15+ REVIEW_PROGRESS - newline-separated list of processed module names
16+ (contents of processed.txt on the memory branch, plus
17+ shorts already in inflight module-cleanup PR bodies)
18+
19+ Outputs (to $GITHUB_OUTPUT):
20+ has_work - "true" if a module was picked, "false" otherwise
21+ short_name - picked module's gradle short name (e.g. "akka-actor:javaagent")
22+ module_dir - picked module's repo-relative directory
23+ queue_remaining - count of unprocessed modules left AFTER this one
1824"""
1925
20- import json
2126import os
2227import re
2328import subprocess
2429from pathlib import Path
2530
2631SETTINGS_FILE = "settings.gradle.kts"
27- # Skip the run entirely if at least this many automated review PRs are already open.
32+ # Skip the run entirely if at least this many module-cleanup PRs are already open.
2833MAX_OPEN_PRS = 5
29- # Upper bound on modules the review job will walk through in a single run,
30- # even if the file-count threshold is never reached. Keeps one run bounded.
31- MODULE_LIMIT_PER_RUN = 50
3234
3335
3436def parse_modules () -> list [tuple [str , str ]]:
3537 """Return list of (gradle_name, module_dir) from settings.gradle.kts."""
3638 text = Path (SETTINGS_FILE ).read_text (encoding = "utf-8" )
37- # Match include(":instrumentation:activej-http:6.0:javaagent")
3839 raw = re .findall (r'include\(":instrumentation:([^"]+)"\)' , text )
3940 pairs = []
4041 for entry in sorted (raw ):
4142 parts = entry .split (":" )
42- # Skip shared/helper modules (e.g. "cdi-testing") that don't follow the
43- # <library>:<variant> layout used for real instrumentation modules.
4443 if len (parts ) < 2 :
4544 continue
4645 module_dir = "instrumentation/" + "/" .join (parts )
47- # Gradle module name: second-to-last:last
4846 gradle_name = f"{ parts [- 2 ]} :{ parts [- 1 ]} "
4947 pairs .append ((gradle_name , module_dir ))
5048 return pairs
5149
5250
53- def load_reviewed () -> set [str ]:
54- """Load already-reviewed module names from the REVIEW_PROGRESS env var."""
51+ def load_processed () -> set [str ]:
52+ """Load already-processed module names from the REVIEW_PROGRESS env var."""
5553 progress = os .environ .get ("REVIEW_PROGRESS" , "" )
5654 return {line .strip () for line in progress .splitlines () if line .strip ()}
5755
5856
5957def count_open_prs () -> int :
60- """Count open PRs with the module cleanup label."""
6158 result = subprocess .run (
6259 ["gh" , "pr" , "list" , "--label" , "module cleanup" ,
6360 "--state" , "open" , "--json" , "number" , "--jq" , "length" ],
@@ -67,46 +64,49 @@ def count_open_prs() -> int:
6764
6865
6966def write_output (key : str , value : str ) -> None :
70- """Append a key=value to $GITHUB_OUTPUT. Values must not contain newlines."""
7167 assert "\n " not in value , f"multi-line $GITHUB_OUTPUT value not supported: { value !r} "
7268 with open (os .environ ["GITHUB_OUTPUT" ], "a" , encoding = "utf-8" ) as f :
7369 f .write (f"{ key } ={ value } \n " )
7470
7571
72+ def emit_no_work () -> None :
73+ write_output ("has_work" , "false" )
74+ write_output ("short_name" , "" )
75+ write_output ("module_dir" , "" )
76+ write_output ("queue_remaining" , "0" )
77+
78+
7679def main () -> None :
7780 all_modules = parse_modules ()
7881 print (f"Total instrumentation modules: { len (all_modules )} " )
7982
80- reviewed = load_reviewed ()
81- print (f"Already reviewed : { len (reviewed )} " )
83+ processed = load_processed ()
84+ print (f"Already processed : { len (processed )} " )
8285
83- remaining = [(name , d ) for name , d in all_modules if name not in reviewed ]
86+ remaining = [(n , d ) for n , d in all_modules if n not in processed ]
8487 print (f"Remaining modules: { len (remaining )} " )
8588
8689 if not remaining :
87- print ("All modules have been reviewed!" )
88- write_output ("has_work" , "false" )
89- write_output ("modules" , "[]" )
90+ print ("All modules have been processed!" )
91+ emit_no_work ()
9092 return
9193
9294 open_prs = count_open_prs ()
93- print (f"Open review PRs: { open_prs } " )
94-
95+ print (f"Open module-cleanup PRs: { open_prs } " )
9596 if open_prs >= MAX_OPEN_PRS :
9697 print (f"PR cap reached ({ open_prs } open >= { MAX_OPEN_PRS } ). Skipping this cycle." )
97- write_output ("has_work" , "false" )
98- write_output ("modules" , "[]" )
98+ emit_no_work ()
9999 return
100100
101- batch = remaining [:MODULE_LIMIT_PER_RUN ]
102- print (f"Dispatching { len (batch )} modules (upper bound for this run)" )
103-
104- modules = [{"short_name" : name , "module_dir" : d } for name , d in batch ]
105- modules_json = json .dumps (modules )
106- print (json .dumps (modules , indent = 2 ))
101+ short_name , module_dir = remaining [0 ]
102+ queue_remaining = len (remaining ) - 1
103+ print (f"Picked: { short_name } ({ module_dir } )" )
104+ print (f"Queue remaining after this run: { queue_remaining } " )
107105
108106 write_output ("has_work" , "true" )
109- write_output ("modules" , modules_json )
107+ write_output ("short_name" , short_name )
108+ write_output ("module_dir" , module_dir )
109+ write_output ("queue_remaining" , str (queue_remaining ))
110110
111111
112112if __name__ == "__main__" :
0 commit comments