11#!/usr/bin/env python3
22import logging
33import os
4- from typing import Optional , List , Set , Tuple , Dict , Union
4+ from collections import namedtuple
5+ from typing import Optional , List , Set , Dict , Union
56
67import click
78import yaml
1112logging .getLogger ('github.Requester' ).setLevel (logging .WARNING )
1213logger = logging .getLogger ()
1314
15+ ActionVersion = namedtuple ('ActionVersion' , ['name' , 'current' , 'latest' ])
16+
1417
1518class GithubActionsTools (object ):
1619 workflows : dict [str , dict [str , Workflow ]] = dict () # repo_name -> [path -> workflow]
1720 actions_latest_release : dict [str , str ] = dict () # action_name@current_release -> latest_release_tag
1821
19- def __init__ (self , github_token : Optional [str ]):
20- github_token = github_token or os .getenv ('GITHUB_TOKEN' )
21- if github_token is None :
22- raise ValueError ('GITHUB_TOKEN must be set' )
22+ def __init__ (self , github_token : str ):
2323 self .client = Github (login_or_token = github_token )
2424
25- def is_local_repo (self , repo_name : str ) -> bool :
25+ @staticmethod
26+ def is_local_repo (repo_name : str ) -> bool :
2627 return os .path .exists (repo_name )
2728
2829 @staticmethod
@@ -86,7 +87,7 @@ def check_for_updates(self, action_name: str) -> Optional[str]:
8687 latest_release = repo .get_latest_release ()
8788 return latest_release .tag_name if latest_release .tag_name != current_version else None
8889
89- def get_repo_actions_latest (self , repo_name : str ) -> Dict [str , List [Tuple [ str , str , Optional [ str ]] ]]:
90+ def get_repo_actions_latest (self , repo_name : str ) -> Dict [str , List [ActionVersion ]]:
9091 workflow_paths = self .get_github_workflows (repo_name )
9192 res = dict ()
9293 for path in workflow_paths :
@@ -101,22 +102,22 @@ def get_repo_actions_latest(self, repo_name: str) -> Dict[str, List[Tuple[str, s
101102 self .actions_latest_release [action ] = latest
102103 else :
103104 latest = self .actions_latest_release [action ]
104- res [path ].append ((action_name , curr_version , latest ))
105+ res [path ].append (ActionVersion (action_name , curr_version , latest ))
105106 return res
106107
107108 def update_actions (
108109 self , repo_name : str , workflow_path : str ,
109- updates : List [Tuple [ str , str , Optional [ str ]] ],
110+ updates : List [ActionVersion ],
110111 commit_msg : str ,
111112 ) -> None :
112113 workflow_content = self ._get_workflow_content (repo_name , workflow_path )
113114 if isinstance (workflow_content , bytes ):
114115 workflow_content = workflow_content .decode ()
115116 for update in updates :
116- if update [ 2 ] is None :
117+ if update . latest is None :
117118 continue
118- current_action = f'{ update [ 0 ] } @{ update [ 1 ] } '
119- latest_action = f'{ update [ 0 ] } @{ update [ 2 ] } '
119+ current_action = f'{ update . name } @{ update . current } '
120+ latest_action = f'{ update . name } @{ update . latest } '
120121 workflow_content = workflow_content .replace (current_action , latest_action )
121122 self ._update_workflow_content (repo_name , workflow_path , workflow_content , commit_msg )
122123
@@ -141,39 +142,49 @@ def _update_workflow_content(
141142 return res
142143
143144
144- @click .group ()
145+ GITHUB_ACTION_NOT_PROVIDED_MSG = """GitHub connection token not provided.
146+ You might not be able to make the changes to remote repositories.
147+ You can provide it using GITHUB_TOKEN environment variable or --github-token option.
148+ """
149+
150+
151+ @click .group (invoke_without_command = True )
145152@click .option ('-repo' , default = '.' , help = 'Repository to analyze' )
146153@click .option ('--github-token' , default = os .getenv ('GITHUB_TOKEN' ),
147154 help = 'GitHub token to use, by default will use GITHUB_TOKEN environment variable' )
148155@click .pass_context
149- def cli (ctx , repo : str , github_token : str ):
156+ def cli (ctx , repo : str , github_token : Optional [ str ] ):
150157 ctx .ensure_object (dict )
158+ if not github_token :
159+ click .secho (GITHUB_ACTION_NOT_PROVIDED_MSG , fg = 'red' , err = True )
151160 ctx .obj ['gh' ] = GithubActionsTools (github_token )
152161 ctx .obj ['repo' ] = repo
162+ if not ctx .invoked_subcommand :
163+ ctx .invoke (update_actions )
153164
154165
155- @cli .command (help = 'List actions in a workflow ' )
156- @click .option ('--dry-run ' , is_flag = True , default = False , help = 'Do not update, list only' )
166+ @cli .command (help = 'Show actions required updates in repository workflows ' )
167+ @click .option ('-u' , '--update ' , is_flag = True , default = False , help = 'Do not update, list only' )
157168@click .option ('-commit-msg' , default = 'Update github-actions' ,
158169 help = 'Commit msg, only relevant when remote repo' )
159170@click .pass_context
160- def update_actions (ctx , dry_run : bool , commit_msg : str ):
171+ def update_actions (ctx , update : bool , commit_msg : str ):
161172 gh , repo = ctx .obj ['gh' ], ctx .obj ['repo' ]
162- action_versions = gh .get_repo_actions_latest (repo )
163- for wf in action_versions :
164- click .secho (f'{ wf } :' , fg = 'blue' )
165- for action in action_versions [ wf ]:
166- s = f'\t { action [ 0 ] :30} { action [ 1 ] :>5} '
167- if action [ 2 ] :
168- old_version = action [ 1 ] .split ('.' )
169- new_version = action [ 2 ] .split ('.' )
173+ workflow_action_versions = gh .get_repo_actions_latest (repo )
174+ for workflow in workflow_action_versions :
175+ click .secho (f'{ workflow } :' , fg = 'blue' )
176+ for action in workflow_action_versions [ workflow ]:
177+ s = f'\t { action . name :30} { action . current :>5} '
178+ if action . latest :
179+ old_version = action . current .split ('.' )
180+ new_version = action . latest .split ('.' )
170181 color = 'red' if new_version [0 ] != old_version [0 ] else 'cyan'
171- s += ' ==> ' + click .style (f'{ action [ 2 ] } ' , fg = color )
182+ s += ' ==> ' + click .style (f'{ action . latest } ' , fg = color )
172183 click .echo (s )
173- if dry_run :
184+ if not update :
174185 return
175- for wf in action_versions :
176- gh .update_actions (repo , wf , action_versions [ wf ], commit_msg )
186+ for workflow in workflow_action_versions :
187+ gh .update_actions (repo , workflow , workflow_action_versions [ workflow ], commit_msg )
177188
178189
179190@cli .command (help = 'List actions in a workflow' )
0 commit comments