99import argparse
1010import json
1111import os
12+ import re
1213import shlex
1314import shutil
1415import subprocess
3738 help = 'Target local repo path to run the check on' )
3839PARSER .add_argument ('-b' , '--baserev' , default = None , required = True ,
3940 help = 'Base revision to use, all the way to HEAD' )
40- PARSER .add_argument ('-r' , '--revrange' , default = None , required = True ,
41- help = 'Revision range to use in gitlint format' )
4241PARSER .add_argument ('--pr' , default = None , required = True ,
4342 help = '<org>/<repo>/<pr num>' )
4443PARSER .add_argument ('--upstream' , default = None , required = True ,
@@ -69,21 +68,28 @@ def parse_args():
6968
7069 ARGS = PARSER .parse_args ()
7170
72- stdout (f'target: { ARGS .target } baserev: { ARGS .baserev } revrange: { ARGS . revrange } ' )
71+ stdout (f'target: { ARGS .target } baserev: { ARGS .baserev } ' )
7372 stdout (f'pr: { ARGS .pr } upstream: { ARGS .upstream } ' )
7473
75- if ARGS .baserev == 'none' and ARGS .revrange == 'none' :
76- sys .exit ('Must specify either baserev or revrange' )
77- elif ARGS .baserev != 'none' and ARGS .revrange != 'none' :
78- sys .exit ('Must specify only one of baserev or revrange' )
74+ if ARGS .target == 'none' :
75+ sys .exit ('--target is required' )
76+
77+ if ARGS .upstream == 'none' :
78+ sys .exit ('--upstream is required' )
79+
80+ if ARGS .baserev == 'none' and ARGS .pr == 'none' :
81+ sys .exit ('--baserev or --pr is required' )
82+
83+ if ARGS .baserev != 'none' and ARGS .pr != 'none' :
84+ sys .exit ('--baserev and --pr are mutually exclusive' )
7985
8086def ssplit (cmd ):
8187 if isinstance (cmd , str ):
8288 return shlex .split (cmd )
8389
8490 return cmd
8591
86- def runc (cmd , ** kwargs ):
92+ def runc (cmd , exit_on_cpe = True , ** kwargs ):
8793 # A shorthand for running a simple shell command.
8894
8995 cwd = os .fspath (kwargs .get ('cwd' , os .getcwd ()))
@@ -95,9 +101,17 @@ def runc(cmd, **kwargs):
95101 stdout (f'running "{ cmd } " in "{ cwd } "' )
96102
97103 kwargs ['check' ] = True
98- return subprocess .run (ssplit (cmd ), ** kwargs )
104+ try :
105+ ret = subprocess .run (ssplit (cmd ), ** kwargs )
106+ except subprocess .CalledProcessError as e :
107+ if exit_on_cpe :
108+ sys .exit (f'Execution of { cmd } failed with { e .returncode } ' )
109+ else :
110+ raise
99111
100- def runc_out (cmd , ** kwargs ):
112+ return ret
113+
114+ def runc_out (cmd , exit_on_cpe = True , ** kwargs ):
101115 # A shorthand for running a simple shell command and getting its output.
102116
103117 cwd = kwargs .get ('cwd' , os .getcwd ())
@@ -110,8 +124,85 @@ def runc_out(cmd, **kwargs):
110124 kwargs ['check' ] = True
111125 kwargs ['universal_newlines' ] = True
112126 kwargs ['stdout' ] = subprocess .PIPE
113- cp = subprocess .run (ssplit (cmd ), ** kwargs )
114- return cp .stdout
127+
128+ try :
129+ cp = subprocess .run (ssplit (cmd ), ** kwargs )
130+ except subprocess .CalledProcessError as e :
131+ if exit_on_cpe :
132+ sys .exit (f'Execution of { cmd } failed with { e .returncode } ' )
133+ else :
134+ raise
135+
136+ return cp .stdout .rstrip ()
137+
138+ def fetch_upstream (gh , upstream ):
139+ pass
140+
141+ def fetch_pr (gh , pr ):
142+ pass
143+
144+ def merge_base (target , base , head ):
145+ mb = runc_out (f'git -C { target } merge-base { base } { head } ' )
146+ stdout (f'merge base { mb } ' )
147+ return mb
148+
149+ def check_commit (urepo , target , sha ):
150+ stdout (f'Checking commit { sha } ' )
151+ cm = runc_out (f'git -C { target } show -s --format=%B { sha } ' ).split ('\n ' )
152+ title = cm [0 ]
153+ body = '\n ' .join (cm [1 :])
154+ # stdout(f'{title}')
155+ # stdout(f'{body}')
156+ m = re .match (r'^(Revert\s+\")?\[nrf (mergeup|fromtree|fromlist|noup)\]\s+' ,
157+ title )
158+ if not m :
159+ sys_exit (f'{ sha } : Title does not contain a sauce tag' )
160+ revert = m .group (1 )
161+ tag = m .group (2 )
162+ if not tag :
163+ sys_exit (f'{ sha } : Title does not contain a sauce tag' )
164+
165+ #stdout(tag)
166+
167+ # Skip the rest of checks if the commit is a revert
168+ if revert :
169+ if tag == 'mergeup' :
170+ sys .exit ('Mergeup cannot be reverted' )
171+ stdout (f'Revert commit, skipping additional checks' )
172+ return
173+
174+ if tag == 'mergeup' :
175+ # Count the merges in this commit range (sha^! is a range for sha
176+ # itself)
177+ count = runc_out ('git rev-list --merges --count {sha}^!' )
178+ if count != '1' :
179+ sys .exit ('mergeup used in a non-merge commit' )
180+ if not re .match (r'^\[nrf mergeup\] Merge upstream up to commit \b([a-f0-9]{40})\b' ,
181+ title ):
182+ sys .exit ('{sha}: Invalid mergeup commit title' )
183+ elif tag == 'fromlist' :
184+ regex = r'^Upstream PR: (' \
185+ r'https://github\.com/.*/pull/(\d+)|' \
186+ r'http://lists.infradead.org/pipermail/hostap/.*\.html' \
187+ r')'
188+
189+ match = re .search (regex , body , re .MULTILINE )
190+ if not match :
191+ sys_exit (f'{ sha } : fromlist commit missing an Upstream PR reference' )
192+
193+ #stdout(f'fromlist: {match.group(1)}')
194+ if urepo :
195+ upr = match .group (2 )
196+ stdout (f'fromlist: { upr } ' )
197+ elif tag == 'fromtree' :
198+ regex = r'^\(cherry picked from commit \b([a-f0-9]{40})\b\)'
199+ match = re .search (regex , body , re .MULTILINE )
200+ if not match :
201+ sys_exit (f'{ sha } : fromtree commit missing cherry-pick reference' )
202+ #stdout(f'fromtree: {match.group(0)}')
203+ if urepo :
204+ usha = match .group (1 )
205+ stdout (f'fromtree: { usha } ' )
115206
116207def main ():
117208 parse_args ()
@@ -121,28 +212,43 @@ def main():
121212
122213 gh = Github (token or None )
123214
124- org_str , repo_str , pr_num = gh_pr_split (ARGS .pr )
215+ dcommits = []
216+ dshas = []
125217
126218 target = Path (ARGS .target ).absolute ()
127-
128219 if not target .is_dir ():
129220 sys .exit (f'target repo { target } does not exist; check path' )
130221
131- if ARGS .revrange == 'none' :
132- try :
133- revs = runc_out (f'git -C { target } rev-list --first-parent { ARGS .baserev } ..HEAD' )
134- revs = revs .replace ('\n ' , ',' )
135- except subprocess .CalledProcessError :
136- sys .exit ('git execution exited with error' )
222+ org_str , repo_str , br_str = gh_pr_split (ARGS .upstream )
223+ urepo = gh .get_repo (f'{ org_str } /{ repo_str } ' )
224+
225+ if ARGS .pr != 'none' :
226+ org_str , repo_str , pr_str = gh_pr_split (ARGS .pr )
227+ drepo = gh .get_repo (f'{ org_str } /{ repo_str } ' )
228+ dpr = drepo .get_pull (int (pr_str ))
229+ baserev = merge_base (target , dpr .base .sha , dpr .head .sha )
230+ headrev = dpr .head .sha
231+ dcommits = [c for c in dpr .get_commits ()]
232+ dshas = [c .sha for c in dcommits ]
233+ stdout (f'SHAs found in PR: { dshas } ' )
137234 else :
138- revs = ARGS .revrange
235+ baserev = ARGS .baserev
236+ headrev = 'HEAD'
139237
140- stdout (f'Running { PROG } checks on target { target } for range { revs } ' )
141- try :
142- runc (f'gitlint --target { target } -c ncs-sauce-tags.enable=true ' \
143- f'--commits { revs } ' , cwd = os .path .dirname (__file__ ))
144- except subprocess .CalledProcessError :
145- sys .exit ('gitlint execution exited with error' )
238+ stdout (f'baserev: { baserev } ' )
239+ stdout (f'headrev: { headrev } ' )
240+ revs = runc_out (f'git -C { target } rev-list --first-parent { baserev } ..{ headrev } ' )
241+ revs = revs .split ('\n ' )
242+ revs .reverse ()
243+ stdout (f'revs: { revs } ' )
244+
245+ rev_str = f"{ ',' .join (revs )} ,"
246+
247+ for r in revs :
248+ check_commit (urepo , target , r )
249+
250+ if dshas and dshas != revs :
251+ sys .exit (f'{ dshas } is different from { revs } ' )
146252
147253if __name__ == '__main__' :
148254 main ()
0 commit comments