44from the generated workflow files after Dependabot updates.
55
66This script scans the generated workflow files (.github/workflows/__*.yml) to find
7- the latest action versions used, then updates:
7+ all external action versions used, then updates:
881. Hardcoded action versions in pr-checks/sync.py
992. Action version references in template files in pr-checks/checks/
10103. Action version references in regular workflow files
1111
12+ The script automatically detects all actions used in generated workflows and
13+ preserves version comments (e.g., # v1.2.3) when syncing versions.
14+
1215This ensures that when Dependabot updates action versions in generated workflows,
1316those changes are properly synced back to the source templates.
1417"""
@@ -30,31 +33,25 @@ def scan_generated_workflows(workflow_dir: str) -> Dict[str, str]:
3033 workflow_dir: Path to .github/workflows directory
3134
3235 Returns:
33- Dictionary mapping action names to their latest versions
36+ Dictionary mapping action names to their latest versions (including comments)
3437 """
3538 action_versions = {}
3639 generated_files = glob .glob (os .path .join (workflow_dir , "__*.yml" ))
3740
38- # Actions we care about syncing
39- target_actions = {
40- 'actions/setup-go' ,
41- 'actions/setup-node' ,
42- 'actions/setup-python' ,
43- 'actions/github-script'
44- }
45-
4641 for file_path in generated_files :
4742 with open (file_path , 'r' ) as f :
4843 content = f .read ()
4944
50- # Find all action uses in the file
51- pattern = r'uses:\s+(actions/[^@\s]+)@([^@\s]+)'
45+ # Find all action uses in the file, including potential comments
46+ # This pattern captures: action_name@version_with_possible_comment
47+ pattern = r'uses:\s+([^/\s]+/[^@\s]+)@([^@\n]+)'
5248 matches = re .findall (pattern , content )
5349
54- for action_name , version in matches :
55- if action_name in target_actions :
50+ for action_name , version_with_comment in matches :
51+ # Only track non-local actions (those with / but not starting with ./)
52+ if '/' in action_name and not action_name .startswith ('./' ):
5653 # Take the latest version seen (they should all be the same after Dependabot)
57- action_versions [action_name ] = version
54+ action_versions [action_name ] = version_with_comment . rstrip ()
5855
5956 return action_versions
6057
@@ -65,7 +62,7 @@ def update_sync_py(sync_py_path: str, action_versions: Dict[str, str]) -> bool:
6562
6663 Args:
6764 sync_py_path: Path to sync.py file
68- action_versions: Dictionary of action names to versions
65+ action_versions: Dictionary of action names to versions (may include comments)
6966
7067 Returns:
7168 True if file was modified, False otherwise
@@ -80,9 +77,12 @@ def update_sync_py(sync_py_path: str, action_versions: Dict[str, str]) -> bool:
8077 original_content = content
8178
8279 # Update hardcoded action versions
83- for action_name , version in action_versions .items ():
80+ for action_name , version_with_comment in action_versions .items ():
81+ # Extract just the version part (before any comment) for sync.py
82+ version = version_with_comment .split ('#' )[0 ].strip () if '#' in version_with_comment else version_with_comment .strip ()
83+
8484 # Look for patterns like 'uses': 'actions/setup-node@v4'
85- pattern = rf"('uses':\s*')(actions/{ action_name .split ('/' )[- 1 ]} )@([^']+)(')"
85+ pattern = rf"('uses':\s*')(actions/{ re . escape ( action_name .split ('/' )[- 1 ]) } )@([^']+)(')"
8686 replacement = rf"\1\2@{ version } \4"
8787 content = re .sub (pattern , replacement , content )
8888
@@ -102,7 +102,7 @@ def update_template_files(checks_dir: str, action_versions: Dict[str, str]) -> L
102102
103103 Args:
104104 checks_dir: Path to pr-checks/checks directory
105- action_versions: Dictionary of action names to versions
105+ action_versions: Dictionary of action names to versions (may include comments)
106106
107107 Returns:
108108 List of files that were modified
@@ -117,10 +117,10 @@ def update_template_files(checks_dir: str, action_versions: Dict[str, str]) -> L
117117 original_content = content
118118
119119 # Update action versions
120- for action_name , version in action_versions .items ():
121- # Look for patterns like 'uses: actions/setup-node@v4'
122- pattern = rf"(uses:\s+{ re .escape (action_name )} )@([^@\s ]+)"
123- replacement = rf"\1@{ version } "
120+ for action_name , version_with_comment in action_versions .items ():
121+ # Look for patterns like 'uses: actions/setup-node@v4' or 'uses: actions/setup-node@sha # comment'
122+ pattern = rf"(uses:\s+{ re .escape (action_name )} )@([^@\n ]+)"
123+ replacement = rf"\1@{ version_with_comment } "
124124 content = re .sub (pattern , replacement , content )
125125
126126 if content != original_content :
@@ -138,7 +138,7 @@ def update_regular_workflows(workflow_dir: str, action_versions: Dict[str, str])
138138
139139 Args:
140140 workflow_dir: Path to .github/workflows directory
141- action_versions: Dictionary of action names to versions
141+ action_versions: Dictionary of action names to versions (may include comments)
142142
143143 Returns:
144144 List of files that were modified
@@ -156,10 +156,10 @@ def update_regular_workflows(workflow_dir: str, action_versions: Dict[str, str])
156156 original_content = content
157157
158158 # Update action versions
159- for action_name , version in action_versions .items ():
160- # Look for patterns like 'uses: actions/setup-node@v4'
161- pattern = rf"(uses:\s+{ re .escape (action_name )} )@([^@\s ]+)"
162- replacement = rf"\1@{ version } "
159+ for action_name , version_with_comment in action_versions .items ():
160+ # Look for patterns like 'uses: actions/setup-node@v4' or 'uses: actions/setup-node@sha # comment'
161+ pattern = rf"(uses:\s+{ re .escape (action_name )} )@([^@\n ]+)"
162+ replacement = rf"\1@{ version_with_comment } "
163163 content = re .sub (pattern , replacement , content )
164164
165165 if content != original_content :
0 commit comments