22import os
33import subprocess
44import time
5+ from functools import cached_property
56
67import requests
78import yaml
@@ -47,14 +48,32 @@ def get_ignored_actions(json_string):
4748 )
4849 return set ()
4950
51+ @cached_property
52+ def get_request_headers (self ):
53+ """Get headers for GitHub API request"""
54+ headers = {
55+ 'Accept' : 'application/vnd.github.v3+json'
56+ }
57+ # if the user adds `token` add it to API Request
58+ # required for `private` repositories and creating pull requests
59+ if self .token :
60+ headers .update ({
61+ 'authorization' : 'Bearer {token}' .format (token = self .token )
62+ })
63+
64+ return headers
65+
5066 def run (self ):
5167 """Entrypoint to the GitHub Action"""
5268 workflow_paths = self .get_workflow_paths ()
53- comment = ''
69+ pull_request_body = set ()
5470
5571 if not workflow_paths :
5672 print_message (
57- f'No Workflow found in "{ self .repository } ". Skipping GitHub Actions Version Update' ,
73+ (
74+ f'No Workflow found in "{ self .repository } ". '
75+ f'Skipping GitHub Actions Version Update'
76+ ),
5877 message_type = 'warning'
5978 )
6079 return
@@ -65,7 +84,10 @@ def run(self):
6584 for workflow_path in workflow_paths :
6685 try :
6786 with open (workflow_path , 'r+' ) as file :
68- print_message (f'Checking "{ workflow_path } " for updates' , message_type = 'group' )
87+ print_message (
88+ f'Checking "{ workflow_path } " for updates' ,
89+ message_type = 'group'
90+ )
6991
7092 file_data = file .read ()
7193 updated_config = file_data
@@ -93,14 +115,18 @@ def run(self):
93115 if not latest_release :
94116 continue
95117
96- updated_action = f'{ action_repository } @{ latest_release ["tag_name" ]} '
118+ updated_action = (
119+ f'{ action_repository } @{ latest_release ["tag_name" ]} '
120+ )
97121
98122 if action != updated_action :
99123 print_message (
100124 f'Found new version for "{ action_repository } "'
101125 )
102- comment += self .generate_comment_line (
103- action_repository , latest_release
126+ pull_request_body .add (
127+ self .generate_pull_request_body_line (
128+ action_repository , latest_release
129+ )
104130 )
105131 print_message (
106132 f'Updating "{ action } " with "{ updated_action } "'
@@ -123,25 +149,7 @@ def run(self):
123149 print_message (f'Skipping "{ workflow_path } "' )
124150
125151 if self .workflow_updated :
126- # Use timestamp to ensure uniqueness of the new branch
127- new_branch = f'gh-actions-update-{ int (time .time ())} '
128-
129- print_message ('Create New Branch' , message_type = 'group' )
130-
131- subprocess .run (
132- ['git' , 'checkout' , self .base_branch ]
133- )
134- subprocess .run (
135- ['git' , 'checkout' , '-b' , new_branch ]
136- )
137- subprocess .run (['git' , 'add' , '.' ])
138- subprocess .run (
139- ['git' , 'commit' , '-m' , 'Update GitHub Action Versions' ]
140- )
141-
142- subprocess .run (['git' , 'push' , '-u' , 'origin' , new_branch ])
143-
144- print_message ('' , message_type = 'endgroup' )
152+ new_branch = self .create_new_branch ()
145153
146154 current_branch = subprocess .check_output (
147155 ['git' , 'rev-parse' , '--abbrev-ref' , 'HEAD' ]
@@ -150,9 +158,39 @@ def run(self):
150158 if new_branch in str (current_branch ):
151159 print_message ('Create Pull Request' , message_type = 'group' )
152160
153- self .create_pull_request (new_branch , comment )
161+ pull_request_body_str = (
162+ '### GitHub Actions Version Updates\n ' +
163+ '' .join (pull_request_body )
164+ )
165+ self .create_pull_request (new_branch , pull_request_body_str )
154166
155167 print_message ('' , message_type = 'endgroup' )
168+ else :
169+ print_message ('Everything is up-to-date! \U0001F389 \U0001F389 ' )
170+
171+ def create_new_branch (self ):
172+ """Create and push a new branch with the changes"""
173+ print_message ('Create New Branch' , message_type = 'group' )
174+
175+ # Use timestamp to ensure uniqueness of the new branch
176+ new_branch = f'gh-actions-update-{ int (time .time ())} '
177+
178+ subprocess .run (
179+ ['git' , 'checkout' , self .base_branch ]
180+ )
181+ subprocess .run (
182+ ['git' , 'checkout' , '-b' , new_branch ]
183+ )
184+ subprocess .run (['git' , 'add' , '.' ])
185+ subprocess .run (
186+ ['git' , 'commit' , '-m' , 'Update GitHub Action Versions' ]
187+ )
188+
189+ subprocess .run (['git' , 'push' , '-u' , 'origin' , new_branch ])
190+
191+ print_message ('' , message_type = 'endgroup' )
192+
193+ return new_branch
156194
157195 def create_pull_request (self , branch_name , body ):
158196 """Create pull request on GitHub"""
@@ -161,51 +199,37 @@ def create_pull_request(self, branch_name, body):
161199 'title' : 'Update GitHub Action Versions' ,
162200 'head' : branch_name ,
163201 'base' : self .base_branch ,
164- 'body' : '### GitHub Actions Version Updates \n ' + body ,
202+ 'body' : body ,
165203 }
166204
167205 response = requests .post (
168- url , json = payload , headers = self .get_request_headers ()
206+ url , json = payload , headers = self .get_request_headers
169207 )
170208
171209 if response .status_code == 201 :
172210 html_url = response .json ()['html_url' ]
173- print_message (f'Pull request opened at { html_url } ' )
211+ print_message (f'Pull request opened at { html_url } \U0001F389 ' )
174212 else :
175213 msg = (
176214 f'Could not create a pull request on '
177215 f'{ self .repository } , status code: { response .status_code } '
178216 )
179217 print_message (msg , message_type = 'warning' )
180218
181- def generate_comment_line (self , action_repository , latest_release ):
182- """Generate Comment line for pull request body"""
219+ def generate_pull_request_body_line (self , action_repository , latest_release ):
220+ """Generate pull request body line for pull request body"""
183221 return (
184222 f"* **[{ action_repository } ]({ self .github_url + action_repository } )** "
185223 "published a new release "
186224 f"[{ latest_release ['tag_name' ]} ]({ latest_release ['html_url' ]} ) "
187225 f"on { latest_release ['published_at' ]} \n "
188226 )
189227
190- def get_request_headers (self ):
191- """Get headers for GitHub API request"""
192- headers = {
193- 'Accept' : 'application/vnd.github.v3+json'
194- }
195- # if the user adds `GITHUB_TOKEN` add it to API Request
196- # required for `private` repositories
197- if self .token :
198- headers .update ({
199- 'authorization' : 'Bearer {token}' .format (token = self .token )
200- })
201-
202- return headers
203-
204228 def get_latest_release (self , action_repository ):
205229 """Get latest release using GitHub API """
206230 url = f'{ self .github_api_url } /repos/{ action_repository } /releases/latest'
207231
208- response = requests .get (url , headers = self .get_request_headers () )
232+ response = requests .get (url , headers = self .get_request_headers )
209233 data = {}
210234
211235 if response .status_code == 200 :
@@ -231,7 +255,7 @@ def get_workflow_paths(self):
231255 """Get all workflows of the repository using GitHub API """
232256 url = f'{ self .github_api_url } /repos/{ self .repository } /actions/workflows'
233257
234- response = requests .get (url , headers = self .get_request_headers () )
258+ response = requests .get (url , headers = self .get_request_headers )
235259 data = []
236260
237261 if response .status_code == 200 :
0 commit comments