1414import time
1515import re
1616import urllib3
17+ import git
18+ import gitdb
19+
1720# Remove annoying warning about insecure connection (self-signed cert).
1821urllib3 .disable_warnings (urllib3 .exceptions .InsecureRequestWarning )
1922
@@ -35,7 +38,7 @@ def __call__(self, fn, *args, **kwargs):
3538
3639
3740def http_get_request (* args , ** kwargs ):
38- kwargs [' verify' ] = False
41+ kwargs [" verify" ] = False
3942 return requests .get (* args , ** kwargs )
4043
4144
@@ -48,95 +51,124 @@ def request_as_list(url, *args, **kwargs):
4851
4952 header = response .headers
5053 current_url = None
51- if ' link' in header :
54+ if " link" in header :
5255 links = re .search (
53- r'(?<=\<)([\S]*)(?=>; rel="next")' , header ['link' ], flags = re .IGNORECASE )
56+ r'(?<=\<)([\S]*)(?=>; rel="next")' , header ["link" ], flags = re .IGNORECASE
57+ )
5458 if links is not None :
5559 current_url = links .group (0 )
5660
5761 return body_json
5862
5963
6064def add_timestamp (branch ):
61- date_str = branch [' commit' ][ ' committed_date' ]
65+ date_str = branch [" commit" ][ " committed_date" ]
6266 # We ignore the TZ since Gitlab/GitHub always reports in UTC
63- branch ['dt' ] = int (
64- datetime .strptime (date_str .split ("." )[0 ],
65- '%Y-%m-%dT%H:%M:%S' ).timestamp ())
67+ branch ["dt" ] = int (datetime .strptime (date_str .split ("." )[0 ], "%Y-%m-%dT%H:%M:%S" ).timestamp ())
6668 return branch
6769
6870
71+ def is_descendant_of_root (branch , root_commit , repo ):
72+ if root_commit is None :
73+ return True
74+ try :
75+ commit_sha = branch ["commit" ]["id" ]
76+ repo .commit (commit_sha )
77+ merge_base = repo .merge_base (args .root_commit , commit_sha )
78+ if len (merge_base ) >= 1 and str (merge_base [0 ]) == args .root_commit :
79+ return True
80+ except gitdb .exc .BadObject :
81+ return False
82+
83+
6984def is_recent (branch ):
7085 deadline_sec = int (time .time ()) - (args .days * 86400 )
71- return branch ['dt' ] > deadline_sec
86+ return branch ["dt" ] > deadline_sec
7287
7388
7489def has_no_status (branch ):
75- gh_commit_sha = branch [' commit' ][ 'id' ]
90+ gh_commit_sha = branch [" commit" ][ "id" ]
7691 # Backported branches use the merge head
77- if re .fullmatch (r' ^pr\d+_.*$' , branch [' name' ]):
78- gh_commit_sha = branch [' commit' ][ ' parent_ids' ][1 ]
92+ if re .fullmatch (r" ^pr\d+_.*$" , branch [" name" ]):
93+ gh_commit_sha = branch [" commit" ][ " parent_ids" ][1 ]
7994
8095 # Query GitHub for the status of this commit
81- response = http_get_request (
82- gh_url + '/commits/' + gh_commit_sha + '/status' )
83- if int (response .headers ['x-ratelimit-remaining' ]) <= 0 :
96+ response = http_get_request (gh_url + "/commits/" + gh_commit_sha + "/status" )
97+ if int (response .headers ["x-ratelimit-remaining" ]) <= 0 :
8498 raise ConnectionError (response .json ())
8599
86100 commit = response .json ()
87- if commit is None or ' sha' not in commit :
101+ if commit is None or " sha" not in commit :
88102 return False
89103
90- for status in commit [' statuses' ]:
91- if status [' context' ] == args .gh_context :
104+ for status in commit [" statuses" ]:
105+ if status [" context" ] == args .gh_context :
92106 return False
93107
94108 return True
95109
96110
97111parser = argparse .ArgumentParser (
98- prog = 'generate_pipeline.py' ,
99- description = 'Generate Dynamic pipelines for Gitlab' )
112+ prog = "generate_pipeline.py" , description = "Generate Dynamic pipelines for Gitlab"
113+ )
114+ parser .add_argument (
115+ "-u" ,
116+ "--gl-url" ,
117+ required = True ,
118+ help = "Base URL for Gitlab remote. Ex: https://code.olcf.ornl.gov/" ,
119+ )
100120parser .add_argument (
101- '-u' , '--gl-url' , required = True ,
102- help = 'Base URL for Gitlab remote. Ex: https://code.olcf.ornl.gov/' )
121+ "-n" , "--gh-name" , required = True , help = "Full name of the GitHub project. Ex: ornladios/ADIOS2"
122+ )
103123parser .add_argument (
104- '-n' , '--gh-name' , required = True ,
105- help = 'Full name of the GitHub project. Ex: ornladios/ADIOS2' )
124+ "-p" , "--project_id" , required = True , help = "Gitlab internal project ID of the project."
125+ )
106126parser .add_argument (
107- '-p' , '--project_id' , required = True ,
108- help = 'Gitlab internal project ID of the project.' )
127+ "-c" ,
128+ "--gh-context" ,
129+ default = "OLCF Frontier" ,
130+ help = "Name of the status in GitHub (A.K.A context)" ,
131+ )
109132parser .add_argument (
110- '-c' , '--gh-context' , default = 'OLCF Crusher (Frontier)' ,
111- help = 'Name of the status in GitHub (A.K.A context)' )
133+ "-d" , "--days" , type = int , default = 1 , help = "How many days back to search for commits"
134+ )
112135parser .add_argument (
113- '-d' , '--days' , type = int , default = 1 ,
114- help = 'How many days back to search for commits' )
136+ "-m" , "--max" , type = int , default = 2 , help = "Maximum amount of pipelines computed"
137+ )
115138parser .add_argument (
116- '-m' , '--max' , type = int , default = 2 ,
117- help = 'Maximum amount of pipelines computed' )
139+ "-r" ,
140+ "--root-commit" ,
141+ default = None ,
142+ type = str ,
143+ help = "Template file of the pipeline `{branch}` will be substituted" ,
144+ )
118145parser .add_argument (
119- '-f' , '--template_file' , required = True ,
120- help = 'Template file of the pipeline `{branch}` will be substituted' )
146+ "-f" ,
147+ "--template_file" ,
148+ required = True ,
149+ help = "Template file of the pipeline `{branch}` will be substituted" ,
150+ )
121151args = parser .parse_args ()
122152
123153
124- gl_url = args .gl_url + ' /api/v4/projects/' + str (args .project_id )
125- gh_url = ' https://api.github.com/repos/' + args .gh_name
154+ gl_url = args .gl_url + " /api/v4/projects/" + str (args .project_id )
155+ gh_url = " https://api.github.com/repos/" + args .gh_name
126156
127- with open (args .template_file , 'r' ) as fd :
157+ with open (args .template_file , "r" ) as fd :
128158 template_str = fd .read ()
129159
130- branches = request_as_list (gl_url + '/repository/branches' )
160+ repo = git .Repo ("." , odbt = git .GitDB )
161+
162+ branches = request_as_list (gl_url + "/repository/branches" )
163+ branches = [b for b in branches if is_descendant_of_root (b , args .root_commit , repo )]
131164 branches = [add_timestamp (branch ) for branch in branches ]
132165 branches = [b for b in branches if is_recent (b )]
133- branches = sorted (branches , key = lambda x : x ['dt' ])
166+ branches = sorted (branches , key = lambda x : x ["dt" ])
134167
135168 # Skip running (and return true) has_no_status after returning True args.max times.
136169 # We need this not to hog the Github Rest API draconian ratelimit.
137170 run_n_times = skip_after_n_successes (default_value = False , n = args .max )
138171 branches = [b for b in branches if run_n_times (has_no_status , b )]
139172
140173 for branch in branches :
141- print (template_str .format (
142- branch = branch ['name' ], commit = branch ['commit' ]['id' ]))
174+ print (template_str .format (branch = branch ["name" ], commit = branch ["commit" ]["id" ]))
0 commit comments