Skip to content

Commit 5b54339

Browse files
committed
improve simulation of the github merge button
Now, we check out the target, add the fork as a remote, fetch it, and merge the SHA under test. Added a helper function that does all this in the `task` module; tests should detect whether they are doing a straightforward clone or a PR merge based on the presence of a base repo URL and ref. See `build` for an example.
1 parent cc9b13b commit 5b54339

File tree

5 files changed

+145
-52
lines changed

5 files changed

+145
-52
lines changed

cog/task.py

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def system_output(cmd, work_dir=None):
124124
return subprocess.check_output([cmd], stderr=subprocess.STDOUT, executable='/bin/bash', shell=True)
125125

126126

127-
def git_clone(url, sha, target=None, work_dir=None):
127+
def git_clone(url, sha, target=None, work_dir=None, log=False):
128128
'''Clone a git repository.
129129
130130
The arguments are parsed as::
@@ -137,7 +137,8 @@ def git_clone(url, sha, target=None, work_dir=None):
137137
:param sha: The SHA of the revision to check out
138138
:param target: Name of directory to clone into
139139
:param work_dir: Working directory in which to perform clone
140-
:returns: Return code of "git clone"
140+
:param include_log: Return console output also
141+
:returns: Return code of "git clone", optionally console output
141142
'''
142143
if target is None:
143144
target = sha
@@ -149,11 +150,79 @@ def git_clone(url, sha, target=None, work_dir=None):
149150

150151
if not os.path.exists(target):
151152
cmd = ' '.join(['git clone', url, target, '&& cd %s && ' % target,
152-
'git checkout', sha, '&> /dev/null'])
153-
return system(cmd)
153+
'git checkout', sha, '&> clone.log'])
154+
rc = system(cmd)
155+
156+
if log:
157+
with open(os.path.join(target, 'clone.log')) as f:
158+
clone_log = f.read()
159+
return rc, clone_log
160+
161+
return rc
154162
else:
163+
if log:
164+
return None, None
155165
return None
156166

167+
168+
def simulate_pr(base_url, base_ref, fork_url, sha, target=None, work_dir=None,
169+
log=False):
170+
'''Simulate the merge button on GitHub.
171+
172+
The arguments are parsed as::
173+
174+
git clone [base_url] [work_dir/target]
175+
cd [work_dir/target]
176+
git checkout [base_ref]
177+
git remote add fork [fork_url]
178+
git fetch fork
179+
git merge --no-edit --no-ff [sha]
180+
181+
You may need to set up SSH keys if authentication is needed.
182+
183+
:param base_url: The URL to git clone
184+
:param base_ref: The ref to check out
185+
:param fork_url: The URL for the fork under test
186+
:param sha: The SHA of the revision (in the fork) to check out
187+
:param target: Name of directory to clone into
188+
:param work_dir: Working directory in which to perform clone
189+
:param include_log: Return console output also
190+
:returns: Last return code, optionally console output
191+
'''
192+
if target is None:
193+
target = sha
194+
195+
if work_dir:
196+
target = os.path.join(work_dir, target)
197+
198+
target = os.path.abspath(target)
199+
logfile = os.path.join(work_dir, 'clone.log')
200+
201+
if not os.path.exists(target):
202+
cmd = ' '.join(['git clone', base_url, target, '&&',
203+
'cd', target, '&&',
204+
'git checkout', base_ref, '&&',
205+
'git remote add fork', fork_url, '&&',
206+
'git fetch fork', '&&',
207+
'git merge --no-edit --no-ff', sha, '&&',
208+
'>>', logfile, '2>&1'])
209+
rc = system(cmd)
210+
211+
if log:
212+
try:
213+
with open(logfile) as f:
214+
clone_log = f.read()
215+
except Exception:
216+
clone_log = cmd
217+
return rc, clone_log
218+
219+
return rc
220+
else:
221+
if log:
222+
return None, None
223+
return None
224+
225+
157226
def git_fetch(url,repo_dir):
158227
'''Fetch a remote, commands executed in repository directory (usually not = work_dir)
159228
:param url: The URL to git fetch

cog/tasks/build.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,26 @@ def run(self, document, work_dir):
3131
return {'success': False,
3232
'reason': 'incomplete base specification for merge'}
3333

34-
# get the code
35-
code = cog.task.git_clone(git_url, sha, sha, work_dir=work_dir)
36-
if code is None or code != 0:
37-
return {'success': False, 'reason': 'git clone failed',
38-
'code': str(code)}
39-
40-
checkout_path = os.path.join(work_dir, sha)
41-
42-
if base_repo_url is not None:
43-
code = cog.task.git_merge(base_repo_url, base_repo_ref,
44-
checkout_path)
34+
# Get the code
35+
# Case 1: Just check out a repo and run
36+
if base_repo_ref is None:
37+
code, log = cog.task.git_clone(git_url, sha, sha,
38+
work_dir=work_dir, log=True)
39+
if code is None or code != 0:
40+
return {'success': False, 'reason': 'git clone failed',
41+
'code': str(code), 'log': str(log)}
42+
43+
# Case 2: Simulate a GitHub Pull request merge
44+
else:
45+
code, log = cog.task.simulate_pr(base_repo_url, base_repo_ref,
46+
git_url, sha, sha,
47+
work_dir=work_dir,
48+
log=True)
4549
if code is None or code != 0:
4650
return {'success': False, 'reason': 'git merge failed',
47-
'code': str(code)}
51+
'code': str(code), 'log': str(log)}
52+
53+
checkout_path = os.path.join(work_dir, sha)
4854

4955
# build
5056
results = {'success': True, 'attachments': []}

cog/tasks/cppcheck.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,26 @@ def run(self, document, work_dir):
4141
return {'success': False,
4242
'reason': 'incomplete base specification for merge'}
4343

44-
# get the code
45-
code = cog.task.git_clone(git_url, sha, sha, work_dir=work_dir)
46-
if code is None or (code != 0 and code != 1):
47-
return {'success': False, 'reason': 'git clone failed',
48-
'code': str(code)}
49-
50-
checkout_path = os.path.join(work_dir, sha)
51-
52-
if base_repo_url is not None:
53-
code = cog.task.git_merge(base_repo_url, base_repo_ref,
54-
checkout_path)
44+
# Get the code
45+
# Case 1: Just check out a repo and run
46+
if base_repo_ref is None:
47+
code, log = cog.task.git_clone(git_url, sha, sha,
48+
work_dir=work_dir, log=True)
49+
if code is None or code != 0:
50+
return {'success': False, 'reason': 'git clone failed',
51+
'code': str(code), 'log': str(log)}
52+
53+
# Case 2: Simulate a GitHub Pull request merge
54+
else:
55+
code, log = cog.task.simulate_pr(base_repo_url, base_repo_ref,
56+
git_url, sha, sha,
57+
work_dir=work_dir,
58+
log=True)
5559
if code is None or code != 0:
5660
return {'success': False, 'reason': 'git merge failed',
57-
'code': str(code)}
61+
'code': str(code), 'log': str(log)}
62+
63+
checkout_path = os.path.join(work_dir, sha)
5864

5965
# run cppcheck
6066
results = {'success': True, 'attachments': []}

cog/tasks/fixme.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,26 @@ def run(self, document, work_dir):
3030
return {'success': False,
3131
'reason': 'incomplete base specification for merge'}
3232

33-
# get the code
34-
code = cog.task.git_clone(git_url, sha, sha, work_dir=work_dir)
35-
if code is None or (code != 0 and code != 1):
36-
return {'success': False, 'reason': 'git clone failed',
37-
'code': str(code)}
38-
39-
checkout_path = os.path.join(work_dir, sha)
40-
41-
if base_repo_url is not None:
42-
code = cog.task.git_merge(base_repo_url, base_repo_ref,
43-
checkout_path)
33+
# Get the code
34+
# Case 1: Just check out a repo and run
35+
if base_repo_ref is None:
36+
code, log = cog.task.git_clone(git_url, sha, sha,
37+
work_dir=work_dir, log=True)
38+
if code is None or code != 0:
39+
return {'success': False, 'reason': 'git clone failed',
40+
'code': str(code), 'log': str(log)}
41+
42+
# Case 2: Simulate a GitHub Pull request merge
43+
else:
44+
code, log = cog.task.simulate_pr(base_repo_url, base_repo_ref,
45+
git_url, sha, sha,
46+
work_dir=work_dir,
47+
log=True)
4448
if code is None or code != 0:
4549
return {'success': False, 'reason': 'git merge failed',
46-
'code': str(code)}
50+
'code': str(code), 'log': str(log)}
51+
52+
checkout_path = os.path.join(work_dir, sha)
4753

4854
# find instances of fixme
4955
results = {'success': True, 'attachments': []}

cog/tasks/rattest.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,26 @@ def run(self, document, work_dir):
3232
return {'success': False,
3333
'reason': 'incomplete base specification for merge'}
3434

35-
# get the code
36-
code = cog.task.git_clone(git_url, sha, sha, work_dir=work_dir)
37-
if code is None or code != 0:
38-
return {'success': False, 'reason': 'git clone failed',
39-
'code': str(code)}
40-
41-
checkout_path = os.path.join(work_dir, sha)
42-
43-
if base_repo_url is not None:
44-
code = cog.task.git_merge(base_repo_url, base_repo_ref,
45-
checkout_path)
35+
# Get the code
36+
# Case 1: Just check out a repo and run
37+
if base_repo_ref is None:
38+
code, log = cog.task.git_clone(git_url, sha, sha,
39+
work_dir=work_dir, log=True)
40+
if code is None or code != 0:
41+
return {'success': False, 'reason': 'git clone failed',
42+
'code': str(code), 'log': str(log)}
43+
44+
# Case 2: Simulate a GitHub Pull request merge
45+
else:
46+
code, log = cog.task.simulate_pr(base_repo_url, base_repo_ref,
47+
git_url, sha, sha,
48+
work_dir=work_dir,
49+
log=True)
4650
if code is None or code != 0:
4751
return {'success': False, 'reason': 'git merge failed',
48-
'code': str(code)}
52+
'code': str(code), 'log': str(log)}
53+
54+
checkout_path = os.path.join(work_dir, sha)
4955

5056
# build
5157
results = {'success': True, 'attachments': []}

0 commit comments

Comments
 (0)