Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1a80c9b
Bump the actions group with 4 updates
dependabot[bot] Sep 8, 2025
436471d
Initial plan
Copilot Sep 9, 2025
8d31b53
Add sync-back automation for Dependabot action version updates
Copilot Sep 10, 2025
f77ed60
Improve sync-back automation with automatic action detection, comment…
Copilot Sep 10, 2025
5d79536
Remove regular workflow file updates from sync-back script
Copilot Sep 10, 2025
f537110
Add sync-back script execution to rebuild workflow
Copilot Sep 10, 2025
d9bc711
Rename script for consistency
henrymercer Sep 10, 2025
d08f929
Run test script in CI
henrymercer Sep 10, 2025
cde0d79
Run sync back script separately
henrymercer Sep 10, 2025
1343eba
Remove unused imports
henrymercer Sep 10, 2025
d0f02ad
Simplify import
henrymercer Sep 10, 2025
c9d2739
Use more generic regexp for `sync.py` changes
henrymercer Sep 10, 2025
5df1d6e
Remove redundant check
henrymercer Sep 22, 2025
ee37081
Remove docs about sync back workflow
henrymercer Sep 22, 2025
5065ea8
Improve comment
henrymercer Sep 22, 2025
86ed211
Note limitation of looking for `uses: ` in pattern
henrymercer Sep 22, 2025
bb07e07
Remove trailing whitespace
henrymercer Sep 22, 2025
8df0043
Remove half baked dry run functionality
henrymercer Sep 22, 2025
fbe415d
Remove misleading test case
henrymercer Sep 22, 2025
d9ad6a3
Error if `sync.py` not found
henrymercer Sep 22, 2025
e9d7b2d
Remove unnecessary test cases
henrymercer Sep 22, 2025
2885255
Only sync back versions on Dependabot update PRs
henrymercer Sep 23, 2025
c656a25
Merge pull request #3094 from github/copilot/stack-pr-3088
henrymercer Sep 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improve sync-back automation with automatic action detection, comment…
… preservation, and tests

Co-authored-by: henrymercer <14129055+henrymercer@users.noreply.github.com>
  • Loading branch information
Copilot and henrymercer committed Sep 10, 2025
commit f77ed607fd888340dd3b035d106b102f5033d792
2 changes: 2 additions & 0 deletions pr-checks/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
env
__pycache__/
*.pyc
17 changes: 14 additions & 3 deletions pr-checks/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,32 @@ Manually run each step in the `justfile`.
When Dependabot updates action versions in the generated workflow files (`.github/workflows/__*.yml`),
the sync-back automation ensures those changes are properly reflected in the source templates.

The sync-back script automatically detects all actions used in generated workflows and preserves
version comments (e.g., `# v1.2.3`) when syncing versions between files.

### Running sync-back manually

To sync action versions from generated workflows back to source templates:

```bash
# Dry run to see what would be changed
./pr-checks/sync-back.sh --dry-run --verbose
python3 pr-checks/sync-back.py --dry-run --verbose

# Actually apply the changes
./pr-checks/sync-back.sh
python3 pr-checks/sync-back.py
```

The sync-back script (`sync-back.py`) automatically updates:
The sync-back script automatically updates:
- Hardcoded action versions in `pr-checks/sync.py`
- Action version references in template files in `pr-checks/checks/`
- Action version references in regular workflow files

This ensures that the `verify-pr-checks.sh` test always passes after Dependabot PRs.

### Testing

The sync-back script includes comprehensive tests that can be run with:

```bash
python3 pr-checks/test_sync_back.py -v
```
56 changes: 28 additions & 28 deletions pr-checks/sync-back.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
from the generated workflow files after Dependabot updates.

This script scans the generated workflow files (.github/workflows/__*.yml) to find
the latest action versions used, then updates:
all external action versions used, then updates:
1. Hardcoded action versions in pr-checks/sync.py
2. Action version references in template files in pr-checks/checks/
3. Action version references in regular workflow files

The script automatically detects all actions used in generated workflows and
preserves version comments (e.g., # v1.2.3) when syncing versions.

This ensures that when Dependabot updates action versions in generated workflows,
those changes are properly synced back to the source templates.
"""
Expand All @@ -30,31 +33,25 @@ def scan_generated_workflows(workflow_dir: str) -> Dict[str, str]:
workflow_dir: Path to .github/workflows directory

Returns:
Dictionary mapping action names to their latest versions
Dictionary mapping action names to their latest versions (including comments)
"""
action_versions = {}
generated_files = glob.glob(os.path.join(workflow_dir, "__*.yml"))

# Actions we care about syncing
target_actions = {
'actions/setup-go',
'actions/setup-node',
'actions/setup-python',
'actions/github-script'
}

for file_path in generated_files:
with open(file_path, 'r') as f:
content = f.read()

# Find all action uses in the file
pattern = r'uses:\s+(actions/[^@\s]+)@([^@\s]+)'
# Find all action uses in the file, including potential comments
# This pattern captures: action_name@version_with_possible_comment
pattern = r'uses:\s+([^/\s]+/[^@\s]+)@([^@\n]+)'
matches = re.findall(pattern, content)

for action_name, version in matches:
if action_name in target_actions:
for action_name, version_with_comment in matches:
# Only track non-local actions (those with / but not starting with ./)
if '/' in action_name and not action_name.startswith('./'):
# Take the latest version seen (they should all be the same after Dependabot)
action_versions[action_name] = version
action_versions[action_name] = version_with_comment.rstrip()

return action_versions

Expand All @@ -65,7 +62,7 @@ def update_sync_py(sync_py_path: str, action_versions: Dict[str, str]) -> bool:

Args:
sync_py_path: Path to sync.py file
action_versions: Dictionary of action names to versions
action_versions: Dictionary of action names to versions (may include comments)

Returns:
True if file was modified, False otherwise
Expand All @@ -80,9 +77,12 @@ def update_sync_py(sync_py_path: str, action_versions: Dict[str, str]) -> bool:
original_content = content

# Update hardcoded action versions
for action_name, version in action_versions.items():
for action_name, version_with_comment in action_versions.items():
# Extract just the version part (before any comment) for sync.py
version = version_with_comment.split('#')[0].strip() if '#' in version_with_comment else version_with_comment.strip()

# Look for patterns like 'uses': 'actions/setup-node@v4'
pattern = rf"('uses':\s*')(actions/{action_name.split('/')[-1]})@([^']+)(')"
pattern = rf"('uses':\s*')(actions/{re.escape(action_name.split('/')[-1])})@([^']+)(')"
replacement = rf"\1\2@{version}\4"
content = re.sub(pattern, replacement, content)

Expand All @@ -102,7 +102,7 @@ def update_template_files(checks_dir: str, action_versions: Dict[str, str]) -> L

Args:
checks_dir: Path to pr-checks/checks directory
action_versions: Dictionary of action names to versions
action_versions: Dictionary of action names to versions (may include comments)

Returns:
List of files that were modified
Expand All @@ -117,10 +117,10 @@ def update_template_files(checks_dir: str, action_versions: Dict[str, str]) -> L
original_content = content

# Update action versions
for action_name, version in action_versions.items():
# Look for patterns like 'uses: actions/setup-node@v4'
pattern = rf"(uses:\s+{re.escape(action_name)})@([^@\s]+)"
replacement = rf"\1@{version}"
for action_name, version_with_comment in action_versions.items():
# Look for patterns like 'uses: actions/setup-node@v4' or 'uses: actions/setup-node@sha # comment'
pattern = rf"(uses:\s+{re.escape(action_name)})@([^@\n]+)"
replacement = rf"\1@{version_with_comment}"
content = re.sub(pattern, replacement, content)

if content != original_content:
Expand All @@ -138,7 +138,7 @@ def update_regular_workflows(workflow_dir: str, action_versions: Dict[str, str])

Args:
workflow_dir: Path to .github/workflows directory
action_versions: Dictionary of action names to versions
action_versions: Dictionary of action names to versions (may include comments)

Returns:
List of files that were modified
Expand All @@ -156,10 +156,10 @@ def update_regular_workflows(workflow_dir: str, action_versions: Dict[str, str])
original_content = content

# Update action versions
for action_name, version in action_versions.items():
# Look for patterns like 'uses: actions/setup-node@v4'
pattern = rf"(uses:\s+{re.escape(action_name)})@([^@\s]+)"
replacement = rf"\1@{version}"
for action_name, version_with_comment in action_versions.items():
# Look for patterns like 'uses: actions/setup-node@v4' or 'uses: actions/setup-node@sha # comment'
pattern = rf"(uses:\s+{re.escape(action_name)})@([^@\n]+)"
replacement = rf"\1@{version_with_comment}"
content = re.sub(pattern, replacement, content)

if content != original_content:
Expand Down
25 changes: 0 additions & 25 deletions pr-checks/sync-back.sh

This file was deleted.

Loading