2828def scan_generated_workflows (workflow_dir : str ) -> Dict [str , str ]:
2929 """
3030 Scan generated workflow files to extract the latest action versions.
31-
31+
3232 Args:
3333 workflow_dir: Path to .github/workflows directory
34-
34+
3535 Returns:
3636 Dictionary mapping action names to their latest versions (including comments)
3737 """
3838 action_versions = {}
3939 generated_files = glob .glob (os .path .join (workflow_dir , "__*.yml" ))
40-
40+
4141 for file_path in generated_files :
4242 with open (file_path , 'r' ) as f :
4343 content = f .read ()
44-
44+
4545 # Find all action uses in the file, including potential comments
4646 # This pattern captures: action_name@version_with_possible_comment
4747 pattern = r'uses:\s+([^/\s]+/[^@\s]+)@([^@\n]+)'
4848 matches = re .findall (pattern , content )
49-
49+
5050 for action_name , version_with_comment in matches :
5151 # Only track non-local actions (those with / but not starting with ./)
5252 if not action_name .startswith ('./' ):
5353 # Assume that version numbers are consistent (this should be the case on a Dependabot update PR)
5454 action_versions [action_name ] = version_with_comment .rstrip ()
55-
55+
5656 return action_versions
5757
5858
5959def update_sync_py (sync_py_path : str , action_versions : Dict [str , str ]) -> bool :
6060 """
6161 Update hardcoded action versions in pr-checks/sync.py
62-
62+
6363 Args:
6464 sync_py_path: Path to sync.py file
6565 action_versions: Dictionary of action names to versions (may include comments)
66-
66+
6767 Returns:
6868 True if file was modified, False otherwise
6969 """
7070 if not os .path .exists (sync_py_path ):
7171 print (f"Warning: { sync_py_path } not found" )
7272 return False
73-
73+
7474 with open (sync_py_path , 'r' ) as f :
7575 content = f .read ()
76-
76+
7777 original_content = content
78-
79- # Update hardcoded action versions
78+
79+ # Update hardcoded action versions
8080 for action_name , version_with_comment in action_versions .items ():
8181 # Extract just the version part (before any comment) for sync.py
8282 version = version_with_comment .split ('#' )[0 ].strip () if '#' in version_with_comment else version_with_comment .strip ()
83-
83+
8484 # Look for patterns like 'uses': 'actions/setup-node@v4'
8585 # Note that this will break if we store an Action uses reference in a
8686 # variable - that's a risk we're happy to take since in that case the
8787 # PR checks will just fail.
8888 pattern = rf"('uses':\s*'){ re .escape (action_name )} @(?:[^']+)(')"
8989 replacement = rf"\1{ action_name } @{ version } \2"
9090 content = re .sub (pattern , replacement , content )
91-
91+
9292 if content != original_content :
9393 with open (sync_py_path , 'w' ) as f :
9494 f .write (content )
@@ -102,36 +102,36 @@ def update_sync_py(sync_py_path: str, action_versions: Dict[str, str]) -> bool:
102102def update_template_files (checks_dir : str , action_versions : Dict [str , str ]) -> List [str ]:
103103 """
104104 Update action versions in template files in pr-checks/checks/
105-
105+
106106 Args:
107107 checks_dir: Path to pr-checks/checks directory
108108 action_versions: Dictionary of action names to versions (may include comments)
109-
109+
110110 Returns:
111111 List of files that were modified
112112 """
113113 modified_files = []
114114 template_files = glob .glob (os .path .join (checks_dir , "*.yml" ))
115-
115+
116116 for file_path in template_files :
117117 with open (file_path , 'r' ) as f :
118118 content = f .read ()
119-
119+
120120 original_content = content
121-
121+
122122 # Update action versions
123123 for action_name , version_with_comment in action_versions .items ():
124124 # Look for patterns like 'uses: actions/setup-node@v4' or 'uses: actions/setup-node@sha # comment'
125125 pattern = rf"(uses:\s+{ re .escape (action_name )} )@(?:[^@\n]+)"
126126 replacement = rf"\1@{ version_with_comment } "
127127 content = re .sub (pattern , replacement , content )
128-
128+
129129 if content != original_content :
130130 with open (file_path , 'w' ) as f :
131131 f .write (content )
132132 modified_files .append (file_path )
133133 print (f"Updated { file_path } " )
134-
134+
135135 return modified_files
136136
137137
@@ -140,51 +140,51 @@ def main():
140140 parser .add_argument ("--dry-run" , action = "store_true" , help = "Show what would be changed without making changes" )
141141 parser .add_argument ("--verbose" , "-v" , action = "store_true" , help = "Enable verbose output" )
142142 args = parser .parse_args ()
143-
143+
144144 # Get the repository root (assuming script is in pr-checks/)
145145 script_dir = Path (__file__ ).parent
146146 repo_root = script_dir .parent
147-
147+
148148 workflow_dir = repo_root / ".github" / "workflows"
149149 checks_dir = script_dir / "checks"
150150 sync_py_path = script_dir / "sync.py"
151-
151+
152152 print ("Scanning generated workflows for latest action versions..." )
153153 action_versions = scan_generated_workflows (str (workflow_dir ))
154-
154+
155155 if args .verbose :
156156 print ("Found action versions:" )
157157 for action , version in action_versions .items ():
158158 print (f" { action } @{ version } " )
159-
159+
160160 if not action_versions :
161161 print ("No action versions found in generated workflows" )
162162 return 1
163-
163+
164164 if args .dry_run :
165165 print ("\n DRY RUN - Would make the following changes:" )
166166 print (f"Action versions to sync: { action_versions } " )
167167 return 0
168-
168+
169169 # Update files
170170 print ("\n Updating source files..." )
171171 modified_files = []
172-
172+
173173 # Update sync.py
174174 if update_sync_py (str (sync_py_path ), action_versions ):
175175 modified_files .append (str (sync_py_path ))
176-
177- # Update template files
176+
177+ # Update template files
178178 template_modified = update_template_files (str (checks_dir ), action_versions )
179179 modified_files .extend (template_modified )
180-
180+
181181 if modified_files :
182182 print (f"\n Sync completed. Modified { len (modified_files )} files:" )
183183 for file_path in modified_files :
184184 print (f" { file_path } " )
185185 else :
186186 print ("\n No files needed updating - all action versions are already in sync" )
187-
187+
188188 return 0
189189
190190
0 commit comments