Skip to content

Commit

Permalink
fix(remotebuild): do not auto clean interrupted builds (#5081)
Browse files Browse the repository at this point in the history
Fixes a bug where the remote builder would ignore the user and always
clean the launchpad project.

Also drops some magic values in favor of constants.

Fixes #4929

Signed-off-by: Callahan Kovacs <[email protected]>
  • Loading branch information
mr-cal authored Oct 2, 2024
1 parent 1df696d commit caa0757
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 11 deletions.
9 changes: 6 additions & 3 deletions snapcraft/commands/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,18 +308,21 @@ def _run( # noqa: PLR0915 [too-many-statements]
emit.progress("Remote repository already exists.", permanent=True)
emit.progress("Cleaning up")
builder.cleanup()
return 75
return os.EX_TEMPFAIL

try:
returncode = self._monitor_and_complete(build_id, builds)
except KeyboardInterrupt:
if confirm_with_user("Cancel builds?", default=True):
emit.progress("Cancelling builds.")
builder.cancel_builds()
returncode = 0
emit.progress("Cleaning up.")
builder.cleanup()
return os.EX_OK
except Exception: # noqa: BLE001 [blind-except]
returncode = 1 # General error on any other exception
if returncode != 75: # TimeoutError

if returncode != os.EX_TEMPFAIL:
emit.progress("Cleaning up")
builder.cleanup()
return returncode
Expand Down
33 changes: 25 additions & 8 deletions tests/unit/commands/test_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import sys
import time
from pathlib import Path
from unittest.mock import ANY, Mock
from unittest.mock import ANY, Mock, call

import pytest
from craft_application import launchpad
Expand Down Expand Up @@ -1019,9 +1019,13 @@ def test_monitor_build_error(mocker, emitter, snapcraft_yaml, base, fake_service


@pytest.mark.parametrize("base", const.CURRENT_BASES)
@pytest.mark.usefixtures("mock_confirm")
def test_monitor_build_interrupt(mocker, emitter, snapcraft_yaml, base, fake_services):
@pytest.mark.parametrize("cleanup", [True, False])
def test_monitor_build_interrupt(
cleanup, mock_confirm, mocker, emitter, snapcraft_yaml, base, fake_services
):
"""Test the monitor_build cleanup when a keyboard interrupt occurs."""
# first prompt is for public upload, second is to cancel builds
mock_confirm.side_effect = [True, cleanup]
mocker.patch.object(sys, "argv", ["snapcraft", "remote-build"])
snapcraft_yaml_dict = {"base": base, "build-base": "devel", "grade": "devel"}
snapcraft_yaml(**snapcraft_yaml_dict)
Expand All @@ -1043,22 +1047,35 @@ def test_monitor_build_interrupt(mocker, emitter, snapcraft_yaml, base, fake_ser
"craft_application.services.remotebuild.RemoteBuildService.cleanup"
)

mock_cancel_builds = mocker.patch(
"craft_application.services.remotebuild.RemoteBuildService.cancel_builds"
)

app = application.create_app()
app.services.remote_build._name = get_build_id(
app.services.app.name, app.project.name, app.project_dir
)
app.services.remote_build._is_setup = True
app.services.remote_build.request.download_files_with_progress = Mock()

assert app.run() == 0
assert app.run() == os.EX_OK

mock_start_builds.assert_called_once()
mock_monitor_builds.assert_called_once()
mock_fetch_logs.assert_not_called()
mock_cleanup.assert_called_once()

emitter.assert_progress("Cancelling builds.")
emitter.assert_progress("Cleaning up")
cancel_emitted = call("progress", "Cancelling builds.") in emitter.interactions
clean_emitted = call("progress", "Cleaning up.") in emitter.interactions
if cleanup:
mock_cancel_builds.assert_called_once()
assert cancel_emitted
mock_cleanup.assert_called_once()
assert clean_emitted
else:
mock_cancel_builds.assert_not_called()
assert not cancel_emitted
mock_cleanup.assert_not_called()
assert not clean_emitted


@pytest.mark.parametrize("base", const.CURRENT_BASES)
Expand Down Expand Up @@ -1091,7 +1108,7 @@ def test_monitor_build_timeout(mocker, emitter, snapcraft_yaml, base, fake_servi
app.services.app.name, app.project.name, app.project_dir
)

assert app.run() == 75
assert app.run() == os.EX_TEMPFAIL

mock_start_builds.assert_called_once()
mock_monitor_builds.assert_called_once()
Expand Down

0 comments on commit caa0757

Please sign in to comment.