diff --git a/mergebot_test_utils/utils.py b/mergebot_test_utils/utils.py
index e3d09349..4c50afb3 100644
--- a/mergebot_test_utils/utils.py
+++ b/mergebot_test_utils/utils.py
@@ -49,7 +49,7 @@ class re_matches:
return self._r.match(text)
def __repr__(self):
- return '~' + self._r.pattern + '~'
+ return self._r.pattern + '...'
def seen(env, pr, users):
return users['user'], f'[Pull request status dashboard]({to_pr(env, pr).url}).'
diff --git a/runbot_merge/github.py b/runbot_merge/github.py
index 8a368ed2..786f2924 100644
--- a/runbot_merge/github.py
+++ b/runbot_merge/github.py
@@ -202,10 +202,19 @@ class GH(object):
def _wait_for_update():
if not self._check_updated(branch, sha):
return
- raise exceptions.FastForwardError(self._repo)
- except requests.HTTPError:
+ raise exceptions.FastForwardError(self._repo) \
+ from Exception("timeout: never saw %s" % sha)
+ except requests.HTTPError as e:
_logger.debug('fast_forward(%s, %s, %s) -> ERROR', self._repo, branch, sha, exc_info=True)
- raise exceptions.FastForwardError(self._repo)
+ if e.response.status_code == 422:
+ try:
+ r = e.response.json()
+ except Exception:
+ pass
+ else:
+ if isinstance(r, dict) and 'message' in r:
+ e = Exception(r['message'].lower())
+ raise exceptions.FastForwardError(self._repo) from e
def set_ref(self, branch, sha):
# force-update ref
diff --git a/runbot_merge/tests/test_basic.py b/runbot_merge/tests/test_basic.py
index 99358859..085455af 100644
--- a/runbot_merge/tests/test_basic.py
+++ b/runbot_merge/tests/test_basic.py
@@ -574,7 +574,7 @@ def test_staging_ci_failure_single(env, repo, users, config, page):
assert dangerbox
assert dangerbox[0].text == 'ci/runbot'
-def test_ff_failure(env, repo, config):
+def test_ff_failure(env, repo, config, page):
""" target updated while the PR is being staged => redo staging """
with repo:
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
@@ -587,10 +587,11 @@ def test_ff_failure(env, repo, config):
repo.post_status(prx.head, 'success', 'ci/runbot')
prx.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
env.run_crons()
- assert env['runbot_merge.pull_requests'].search([
+ st = env['runbot_merge.pull_requests'].search([
('repository.name', '=', repo.name),
('number', '=', prx.number)
]).staging_id
+ assert st
with repo:
m2 = repo.make_commit('heads/master', 'cockblock', None, tree={'m': 'm', 'm2': 'm2'})
@@ -603,6 +604,14 @@ def test_ff_failure(env, repo, config):
repo.post_status(staging.id, 'success', 'ci/runbot')
env.run_crons()
+ assert st.reason == 'update is not a fast forward'
+ # check that it's added as title on the staging
+ doc = html.fromstring(page('/runbot_merge'))
+ _new, prev = doc.cssselect('li.staging')
+
+ assert 'bg-gray-lighter' in prev.classes, "ff failure is ~ cancelling"
+ assert prev.get('title') == re_matches('fast forward failed \(update is not a fast forward\)')
+
assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', repo.name),
('number', '=', prx.number)
diff --git a/runbot_merge/views/templates.xml b/runbot_merge/views/templates.xml
index fab3b1e6..c0a86866 100644
--- a/runbot_merge/views/templates.xml
+++ b/runbot_merge/views/templates.xml
@@ -135,14 +135,13 @@