Skip to content

Commit 90e27c3

Browse files
Antony BaileyAntony Bailey
authored andcommitted
might give up on this soon
1 parent 5ab15af commit 90e27c3

File tree

1 file changed

+163
-162
lines changed

1 file changed

+163
-162
lines changed

.github/workflows/sha-pins.yml

Lines changed: 163 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,168 @@
11
name: Check and update workflow action references
22

33
on:
4-
schedule:
5-
- cron: '0 0 * * 1' # Run weekly on Mondays
6-
workflow_dispatch: # Allow manual trigger
4+
schedule:
5+
- cron: '0 0 * * 1' # Run weekly on Mondays
6+
workflow_dispatch: # Allow manual trigger
77

88
jobs:
9-
check-and-update-workflows:
10-
runs-on: ubuntu-latest
11-
permissions:
12-
contents: write
13-
pull-requests: write
14-
actions: write
15-
16-
steps:
17-
- name: Checkout repository
18-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
19-
20-
- name: Setup Python
21-
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5
22-
with:
23-
python-version: '3.10'
24-
25-
- name: Install dependencies
26-
run: |
27-
python -m pip install --upgrade pip
28-
pip install PyGithub
29-
30-
- name: Check and update action references
31-
env:
32-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33-
run: |
34-
python << 'EOF'
35-
import os
36-
import re
37-
import glob
38-
from github import Github
39-
40-
# Initialize GitHub client
41-
g = Github(os.environ['GITHUB_TOKEN'])
42-
repo = g.get_repo(os.environ['GITHUB_REPOSITORY'])
43-
44-
# Find all workflow files
45-
workflow_files = glob.glob('.github/workflows/*.yml')
46-
47-
# Initialize counts
48-
updated_files = 0
49-
updates = 0
50-
51-
for workflow_file in workflow_files:
52-
with open(workflow_file, 'r') as f:
53-
content = f.read()
54-
55-
# Find action references: uses: owner/repo@ref
56-
action_pattern = r'uses:\s+([a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+)@([a-zA-Z0-9_.-]+)(\s+#.*)?'
57-
matches = re.findall(action_pattern, content)
58-
59-
updated_content = content
60-
file_updated = False
61-
62-
for match in matches:
63-
action, version, comment = match
64-
# Skip if already pinned to a SHA
65-
if len(version) == 40 and all(c in '0123456789abcdef' for c in version.lower()):
66-
continue
67-
68-
try:
69-
# Get the SHA for the tag/branch
70-
action_repo = g.get_repo(action)
71-
if version.startswith('v'):
72-
# It's likely a tag
73-
try:
74-
ref = action_repo.get_git_ref(f"tags/{version}")
75-
sha = ref.object.sha
76-
77-
# For annotated tags, we need to get the commit SHA
78-
if ref.object.type == 'tag':
79-
tag = action_repo.get_git_tag(sha)
80-
sha = tag.object.sha
81-
except Exception as e:
82-
print(f"Error resolving tag {version} for {action}: {str(e)}")
83-
continue
84-
else:
85-
# It's likely a branch
86-
try:
87-
ref = action_repo.get_git_ref(f"heads/{version}")
88-
sha = ref.object.sha
89-
except Exception as e:
90-
print(f"Error resolving branch {version} for {action}: {str(e)}")
91-
continue
92-
93-
# Create updated reference with SHA
94-
old_ref = f"uses: {action}@{version}"
95-
new_ref = f"uses: {action}@{sha} # {version}"
96-
97-
# Update the content
98-
updated_content = updated_content.replace(old_ref, new_ref)
99-
print(f"Updating {workflow_file}: {old_ref} -> {new_ref}")
100-
file_updated = True
101-
updates += 1
102-
103-
except Exception as e:
104-
print(f"Error processing {action}@{version}: {str(e)}")
105-
106-
# Write updated content back to the file
107-
if file_updated:
108-
with open(workflow_file, 'w') as f:
109-
f.write(updated_content)
110-
updated_files += 1
111-
112-
# Create a PR if there were updates
113-
if updated_files > 0:
114-
try:
115-
branch_name = "action-sha-updates"
116-
base_branch = repo.default_branch
117-
118-
# Check if branch exists and delete if it does
119-
try:
120-
repo.get_git_ref(f"heads/{branch_name}")
121-
repo.get_git_ref(f"heads/{branch_name}").delete()
122-
except:
123-
pass
124-
125-
# Create branch
126-
sb = repo.get_branch(base_branch)
127-
repo.create_git_ref(ref=f"refs/heads/{branch_name}", sha=sb.commit.sha)
128-
129-
# Commit changes
130-
commit_message = f"Update action references with SHA pins\n\nUpdated {updates} references in {updated_files} workflow files"
131-
132-
# Create a commit with all changes
133-
element_list = []
134-
for workflow_file in workflow_files:
135-
with open(workflow_file, 'r') as f:
136-
content = f.read()
137-
138-
element = {
139-
"path": workflow_file,
140-
"mode": "100644",
141-
"type": "blob",
142-
"content": content
143-
}
144-
element_list.append(element)
145-
146-
commit = repo.create_git_tree(element_list, base_tree=sb.commit.tree)
147-
parent = repo.get_git_commit(sb.commit.sha)
148-
commit = repo.create_git_commit(commit_message, commit, [parent])
149-
ref = repo.get_git_ref(f"heads/{branch_name}")
150-
ref.edit(commit.sha)
151-
152-
# Create a PR
153-
pr = repo.create_pull(
154-
title="Update GitHub Action references with SHA pins",
155-
body="This PR updates GitHub Action references to use SHA pins for better security.",
156-
base=base_branch,
157-
head=branch_name
158-
)
159-
print(f"Created PR #{pr.number}")
160-
161-
except Exception as e:
162-
print(f"Error creating PR: {str(e)}")
163-
exit(1)
164-
165-
else:
166-
print("No updates needed.")
167-
EOF
9+
check-and-update-workflows:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
actions: write
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
19+
20+
- name: Setup Python
21+
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5
22+
with:
23+
python-version: '3.10'
24+
25+
- name: Install dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
pip install PyGithub
29+
30+
- name: Check and update action references
31+
env:
32+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
run: |
34+
python << 'EOF'
35+
import os
36+
import re
37+
import glob
38+
from github import Github
39+
40+
# Initialize GitHub client
41+
g = Github(os.environ['GITHUB_TOKEN'])
42+
repo = g.get_repo(os.environ['GITHUB_REPOSITORY'])
43+
44+
# Find all workflow files
45+
workflow_files = glob.glob('.github/workflows/*.yml')
46+
47+
# Initialize counts
48+
updated_files = 0
49+
updates = 0
50+
51+
for workflow_file in workflow_files:
52+
with open(workflow_file, 'r') as f:
53+
content = f.read()
54+
55+
# Find action references: uses: owner/repo@ref
56+
action_pattern = r'uses:\s+([a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+)@([a-zA-Z0-9_.-]+)(\s+#.*)?'
57+
matches = re.findall(action_pattern, content)
58+
59+
updated_content = content
60+
file_updated = False
61+
62+
for match in matches:
63+
action, version, comment = match
64+
# Skip if already pinned to a SHA
65+
if len(version) == 40 and all(c in '0123456789abcdef' for c in version.lower()):
66+
continue
67+
68+
try:
69+
# Get the SHA for the tag/branch
70+
action_repo = g.get_repo(action)
71+
if version.startswith('v'):
72+
# It's likely a tag
73+
try:
74+
ref = action_repo.get_git_ref(f"tags/{version}")
75+
sha = ref.object.sha
76+
77+
# For annotated tags, we need to get the commit SHA
78+
if ref.object.type == 'tag':
79+
tag = action_repo.get_git_tag(sha)
80+
sha = tag.object.sha
81+
except Exception as e:
82+
print(f"Error resolving tag {version} for {action}: {str(e)}")
83+
continue
84+
else:
85+
# It's likely a branch
86+
try:
87+
ref = action_repo.get_git_ref(f"heads/{version}")
88+
sha = ref.object.sha
89+
except Exception as e:
90+
print(f"Error resolving branch {version} for {action}: {str(e)}")
91+
continue
92+
93+
# Create updated reference with SHA
94+
old_ref = f"uses: {action}@{version}"
95+
new_ref = f"uses: {action}@{sha} # {version}"
96+
97+
# Update the content
98+
updated_content = updated_content.replace(old_ref, new_ref)
99+
print(f"Updating {workflow_file}: {old_ref} -> {new_ref}")
100+
file_updated = True
101+
updates += 1
102+
103+
except Exception as e:
104+
print(f"Error processing {action}@{version}: {str(e)}")
105+
106+
# Write updated content back to the file
107+
if file_updated:
108+
with open(workflow_file, 'w') as f:
109+
f.write(updated_content)
110+
updated_files += 1
111+
112+
# Create a PR if there were updates
113+
if updated_files > 0:
114+
try:
115+
branch_name = "action-sha-updates"
116+
base_branch = repo.default_branch
117+
118+
# Check if branch exists and delete if it does
119+
try:
120+
repo.get_git_ref(f"heads/{branch_name}")
121+
repo.get_git_ref(f"heads/{branch_name}").delete()
122+
except:
123+
pass
124+
125+
# Create branch
126+
sb = repo.get_branch(base_branch)
127+
repo.create_git_ref(ref=f"refs/heads/{branch_name}", sha=sb.commit.sha)
128+
129+
# Commit changes
130+
commit_message = f"Update action references with SHA pins\n\nUpdated {updates} references in {updated_files} workflow files"
131+
132+
# Create a commit with all changes
133+
element_list = []
134+
for workflow_file in workflow_files:
135+
with open(workflow_file, 'r') as f:
136+
content = f.read()
137+
138+
element = {
139+
"path": workflow_file,
140+
"mode": "100644",
141+
"type": "blob",
142+
"content": content
143+
}
144+
element_list.append(element)
145+
146+
base_tree = repo.get_git_tree(sb.commit.sha)
147+
tree = repo.create_git_tree(element_list, base_tree)
148+
parent = repo.get_git_commit(sb.commit.sha)
149+
commit = repo.create_git_commit(commit_message, tree, [parent])
150+
ref = repo.get_git_ref(f"heads/{branch_name}")
151+
ref.edit(commit.sha)
152+
153+
# Create a PR
154+
pr = repo.create_pull(
155+
title="Update GitHub Action references with SHA pins",
156+
body="This PR updates GitHub Action references to use SHA pins for better security.",
157+
base=base_branch,
158+
head=branch_name
159+
)
160+
print(f"Created PR #{pr.number}")
161+
162+
except Exception as e:
163+
print(f"Error creating PR: {str(e)}")
164+
exit(1)
165+
166+
else:
167+
print("No updates needed.")
168+
EOF

0 commit comments

Comments
 (0)