Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions miss_islington/tasks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import logging
import os
import ssl
import subprocess
Expand All @@ -15,6 +16,7 @@

from . import util

logger = logging.getLogger(__name__)

app = celery.Celery("backport_cpython")

Expand Down Expand Up @@ -116,16 +118,39 @@ async def backport_task_asyncio(
# Ensure that we don't have any changes lying around
subprocess.check_output(['git', 'reset', '--hard'])
subprocess.check_output(['git', 'clean', '-fxd'])

cp = cherry_picker.CherryPicker(
"origin",
commit_hash,
[branch],
config=CHERRY_PICKER_CONFIG,
prefix_commit=False,
# Clear any leftover cherry-picker state from a previously interrupted
# run; otherwise CherryPicker() init raises InvalidRepoException and
# every subsequent backport on this worker silently fails.
subprocess.run(
['git', 'config', '--local', '--remove-section', 'cherry-picker'],
stderr=subprocess.DEVNULL,
check=False,
)

try:
cp = cherry_picker.CherryPicker(
"origin",
commit_hash,
[branch],
config=CHERRY_PICKER_CONFIG,
prefix_commit=False,
)
cp.backport()
except cherry_picker.InvalidRepoException as ire:
await util.comment_on_pr(
gh,
issue_number,
f"""\
Sorry {util.get_participants(created_by, merged_by)}, I had trouble backporting to `{branch}`.
Please retry by removing and re-adding the "needs backport to {branch}" label.
Alternatively, you can backport using [cherry_picker](https://pypi.org/project/cherry-picker/) on the command line.
```
cherry_picker {commit_hash} {branch}
```
""",
)
await util.assign_pr_to_core_dev(gh, issue_number, merged_by)
logger.exception("InvalidRepoException while backporting to %s", branch)
except cherry_picker.BranchCheckoutException as bce:
await util.comment_on_pr(
gh,
Expand Down
60 changes: 60 additions & 0 deletions tests/test_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import os
import subprocess
from unittest import mock

from cherry_picker import cherry_picker

os.environ.setdefault("HEROKU_REDIS_MAROON_URL", "someurl")

from miss_islington import tasks


async def test_invalid_repo_exception_posts_comment_and_clears_state():
"""A stuck cherry-picker.state in git config used to wedge every
subsequent backport: CherryPicker() init raised InvalidRepoException
and the task crashed with no comment posted. The task now wipes
stale state pre-flight and posts an error comment if init still fails.
"""
with (
mock.patch("miss_islington.tasks.aiohttp.ClientSession"),
mock.patch(
"miss_islington.tasks.apps.get_installation_access_token",
new=mock.AsyncMock(return_value={"token": "test-token"}),
),
mock.patch("miss_islington.tasks.util.is_cpython_repo", return_value=True),
mock.patch("miss_islington.tasks.subprocess.check_output"),
mock.patch("miss_islington.tasks.subprocess.run") as run_mock,
mock.patch(
"miss_islington.tasks.cherry_picker.CherryPicker",
side_effect=cherry_picker.InvalidRepoException("stuck state"),
),
mock.patch(
"miss_islington.tasks.util.comment_on_pr", new=mock.AsyncMock()
) as comment_mock,
mock.patch(
"miss_islington.tasks.util.assign_pr_to_core_dev", new=mock.AsyncMock()
) as assign_mock,
):
await tasks.backport_task_asyncio(
"7a4c6dfb8839eb05fb87baf70364680e45001dd4",
"3.15",
issue_number=130749,
created_by="medmunds",
merged_by="bitdancer",
installation_id=42958231,
)

run_mock.assert_any_call(
["git", "config", "--local", "--remove-section", "cherry-picker"],
stderr=subprocess.DEVNULL,
check=False,
)

comment_mock.assert_awaited_once()
posted_message = comment_mock.await_args.args[2]
assert "3.15" in posted_message
assert "@bitdancer" in posted_message

assign_mock.assert_awaited_once()
assert assign_mock.await_args.args[1] == 130749
assert assign_mock.await_args.args[2] == "bitdancer"
Loading