Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

many: extract lifecycle ordering into own module #2159

Merged
merged 9 commits into from
Jun 12, 2018
11 changes: 7 additions & 4 deletions snapcraft/cli/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,16 @@ def clean(parts, step, **kwargs):
"""
project_options = get_project_options(**kwargs)
build_environment = env.BuilderEnvironmentConfig()
if build_environment.is_host:
step = step or steps.next_step(step).name

if step:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it gets confusing a bit when step can be a step_name or an actual instance of Step.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, fixed by using an identifier decl for click to make it use a different parameter name.

if step == 'strip':
echo.warning('DEPRECATED: Use `prime` instead of `strip` '
'as the step to clean')
step = steps.PRIME.name
lifecycle.clean(project_options, parts, steps.Step(step))
step = 'prime'
step = steps.get_step_by_name(step)

if build_environment.is_host:
lifecycle.clean(project_options, parts, step)
else:
config = project_loader.load_config(project_options)
lxd.Project(project_options=project_options,
Expand Down
7 changes: 7 additions & 0 deletions snapcraft/internal/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,13 @@ def __init__(self, path):
super().__init__(path=path)


class InvalidStepError(SnapcraftError):
fmt = "{step_name!r} is not a valid lifecycle step"

def __init__(self, step_name):
super().__init__(step_name=step_name)


class NoLatestStepError(SnapcraftError):
fmt = "The {part_name!r} part hasn't run any steps"

Expand Down
4 changes: 2 additions & 2 deletions snapcraft/internal/lifecycle/_clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ def _cleanup_common_directories(config, project_options):
if not max_step or step > max_step:
max_step = step

with contextlib.suppress(errors.NoNextStepError):
next_step = steps.next_step(max_step)
next_step = steps.next_step(max_step)
if next_step:
_cleanup_common_directories_for_step(next_step, project_options)


Expand Down
2 changes: 0 additions & 2 deletions snapcraft/internal/lifecycle/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os

from snapcraft.internal import steps

SNAPCRAFT_INTERNAL_DIR = os.path.join('snap', '.snapcraft')
13 changes: 4 additions & 9 deletions snapcraft/internal/lxd/_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from . import errors
from ._containerbuild import Containerbuild
from snapcraft.internal import lifecycle, steps
from snapcraft.cli import echo

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -103,7 +102,7 @@ def refresh(self):
self._container_run(['apt-get', 'upgrade', '-y'])
self._container_run(['snap', 'refresh'])

def clean(self, parts: List[str], step: str):
def clean(self, parts: List[str], step: steps.Step):
# clean with no parts deletes the container
if not step:
if not parts:
Expand All @@ -112,10 +111,6 @@ def clean(self, parts: List[str], step: str):
print('Deleting {}'.format(self._container_name))
subprocess.check_call([
'lxc', 'delete', '-f', self._container_name])
step = steps.PULL.name
# clean normally, without involving the container
if step == 'strip':
echo.warning('DEPRECATED: Use `prime` instead of `strip` '
'as the step to clean')
step = steps.PRIME.name
lifecycle.clean(self._project_options, parts, steps.Step(step))
step = steps.PULL

lifecycle.clean(self._project_options, parts, step)
2 changes: 1 addition & 1 deletion snapcraft/internal/pluginhandler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def _migrate_state_file(self):
if step:
os.remove(self.plugin.statedir)
os.makedirs(self.plugin.statedir)
self.mark_done(steps.Step(step))
self.mark_done(steps.get_step_by_name(step))

def notify_part_progress(self, progress, hint=''):
logger.info('%s %s %s', progress, self.name, hint)
Expand Down
31 changes: 30 additions & 1 deletion snapcraft/internal/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

from typing import List

from snapcraft.internal import errors


class Step:
def __init__(self, name: str, clean_if_dirty: bool) -> None:
Expand All @@ -26,7 +28,10 @@ def __init__(self, name: str, clean_if_dirty: bool) -> None:
@property
def _order(self) -> int:
if self.__order is None:
self.__order = STEPS.index(self)
try:
self.__order = STEPS.index(self)
except ValueError:
raise errors.InvalidStepError(self.name)
return self.__order

def previous_step(self) -> 'Step':
Expand Down Expand Up @@ -96,7 +101,31 @@ def __repr__(self):


def next_step(step):
"""Get the next step of the lifecycle

:param Step step: The current step. If None, the next step is the first
step in the lifecycle.
:return: The next step in the lifecycle
:rtype: Step
"""
if step:
return step.next_step()
else:
return STEPS[0]


def get_step_by_name(step_name):
"""Get the lifecycle step that has the given name.

:param str step_name: Name of the step in question.
:return: The Step in the lifecycle that has the given name.
:rtype: Step
:raises: errors.InvalidStepError if there is no step with the given name.
"""
if step_name:
for step in STEPS:
if step.name == step_name:
return step
raise errors.InvalidStepError(step_name)
else:
return STEPS[0]
4 changes: 2 additions & 2 deletions tests/unit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ def verify_state(self, part_name, state_dir, expected_step_name):
'Expected state directory for {}'.format(part_name))

# Expect every step up to and including the specified one to be run
step = steps.Step(expected_step_name)
for step in step.previous_steps + [step]:
step = steps.get_step_by_name(expected_step_name)
for step in step.previous_steps() + [step]:
self.assertTrue(os.path.exists(os.path.join(state_dir, step.name)),
'Expected {!r} to be run for {}'.format(
step.name, part_name))
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,15 @@ class ErrorFormattingTestCase(unit.TestCase):
'Unable to parse mountinfo row: [1, 2, 3]'
)
}),
('InvalidStepError', {
'exception': errors.InvalidStepError,
'kwargs': {
'step_name': 'test-step-name',
},
'expected_message': (
"'test-step-name' is not a valid lifecycle step"
)
}),
('NoLatestStepError', {
'exception': errors.NoLatestStepError,
'kwargs': {
Expand Down