Skip to content

Commit b975c93

Browse files
committed
action: Overhaul the action
Remove the use of gitlint, instead just iterating over the commits manually. Use the PR SHAs to calculate the list of commits to check using the following reference: https://github.com/orgs/community/discussions/59677#discussioncomment-10782359 Clean up the action's inputs to simplify it. Signed-off-by: Carles Cufi <[email protected]>
1 parent f67aabd commit b975c93

File tree

5 files changed

+134
-170
lines changed

5 files changed

+134
-170
lines changed

.gitlint

Lines changed: 0 additions & 60 deletions
This file was deleted.

action.py

Lines changed: 133 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import argparse
1010
import json
1111
import os
12+
import re
1213
import shlex
1314
import shutil
1415
import subprocess
@@ -37,8 +38,6 @@
3738
help='Target local repo path to run the check on')
3839
PARSER.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')
4241
PARSER.add_argument('--pr', default=None, required=True,
4342
help='<org>/<repo>/<pr num>')
4443
PARSER.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

8086
def 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

116207
def 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

147253
if __name__ == '__main__':
148254
main()

action.yml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,9 @@ description: 'Check commit (sauce) tags in an OSS based NCS repository'
33
inputs:
44
target:
55
description: 'path to the target repository'
6-
required: true
7-
baserev:
8-
description: 'base revision to use. Will compute a rev-list --first-parent to HEAD'
9-
default: 'none'
10-
revrange:
11-
description: 'revision range in gitlint format (comma-separated SHAs)'
126
default: 'none'
137
upstream:
14-
description: 'Upstream org/repo'
8+
description: 'Upstream <org>/<repo>/<branch>'
159
default: 'none'
1610
github-token:
1711
description: 'The token to authenticate with'
@@ -32,8 +26,6 @@ runs:
3226
--target "${{ inputs.target }}" \
3327
--upstream "${{ inputs.upstream }}" \
3428
--pr "${{ github.repository }}/${{ github.event.pull_request.number }}" \
35-
--baserev "${{ inputs.baserev }}" \
36-
--revrange "${{ inputs.revrange }}"
3729
3830
shell: bash
3931
env:

gitlint/ncs.py

Lines changed: 0 additions & 73 deletions
This file was deleted.

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
gitlint
21
pygithub

0 commit comments

Comments
 (0)