Skip to content

Commit

Permalink
Merge pull request #96 from cmatsuoka/fix/local-source-filters
Browse files Browse the repository at this point in the history
many: fix source ignore patterns (CRAFT-375)
  • Loading branch information
cmatsuoka authored Jul 20, 2021
2 parents 793db26 + 39c0119 commit ab69ef7
Show file tree
Hide file tree
Showing 17 changed files with 242 additions and 52 deletions.
2 changes: 1 addition & 1 deletion craft_parts/dirs.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ProjectDirs:
"""

def __init__(self, *, work_dir: Union[Path, str] = "."):
self._work_dir = Path(work_dir).absolute()
self._work_dir = Path(work_dir).expanduser().resolve()

@property
def work_dir(self) -> Path:
Expand Down
3 changes: 3 additions & 0 deletions craft_parts/executor/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ def __init__(
project_info: ProjectInfo,
extra_build_packages: List[str] = None,
extra_build_snaps: List[str] = None,
ignore_patterns: List[str] = None,
):
self._part_list = part_list
self._project_info = project_info
self._extra_build_packages = extra_build_packages
self._extra_build_snaps = extra_build_snaps
self._handler: Dict[str, PartHandler] = {}
self._ignore_patterns = ignore_patterns

def prologue(self) -> None:
"""Prepare the execution environment.
Expand Down Expand Up @@ -139,6 +141,7 @@ def _create_part_handler(self, part: Part) -> PartHandler:
part,
part_info=PartInfo(self._project_info, part),
part_list=self._part_list,
ignore_patterns=self._ignore_patterns,
)
self._handler[part.name] = handler

Expand Down
2 changes: 2 additions & 0 deletions craft_parts/executor/part_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(
*,
part_info: PartInfo,
part_list: List[Part],
ignore_patterns: Optional[List[str]] = None,
):
self._part = part
self._part_info = part_info
Expand All @@ -71,6 +72,7 @@ def __init__(
cache_dir=part_info.cache_dir,
part=part,
project_dirs=part_info.dirs,
ignore_patterns=ignore_patterns,
)

self.build_packages = _get_build_packages(part=self._part, plugin=self._plugin)
Expand Down
2 changes: 1 addition & 1 deletion craft_parts/infos.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __init__(
project_dirs = ProjectDirs()

self._application_name = application_name
self._cache_dir = Path(cache_dir).absolute()
self._cache_dir = Path(cache_dir).expanduser().resolve()
self._set_machine(arch)
self._base = base # TODO: infer base if not specified
self._parallel_build_count = parallel_build_count
Expand Down
9 changes: 7 additions & 2 deletions craft_parts/lifecycle_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import re
from pathlib import Path
from typing import Any, Dict, List, Sequence, Union
from typing import Any, Dict, List, Optional, Sequence, Union

from pydantic import ValidationError

Expand Down Expand Up @@ -55,6 +55,8 @@ class LifecycleManager:
to the system where Craft Parts is being executed.
:param parallel_build_count: The maximum number of concurrent jobs to be
used to build each part of this project.
:param ignore_local_sources: A list of local source patterns to ignore.
:param extra_build_packages: A list of additional build packages to install.
:param custom_args: Any additional arguments that will be passed directly
to :ref:`callbacks<callbacks>`.
"""
Expand All @@ -69,7 +71,8 @@ def __init__(
arch: str = "",
base: str = "",
parallel_build_count: int = 1,
extra_build_packages: List[str] = None,
ignore_local_sources: Optional[List[str]] = None,
extra_build_packages: Optional[List[str]] = None,
**custom_args, # custom passthrough args
):
if not re.match("^[A-Za-z][0-9A-Za-z_]*$", application_name):
Expand Down Expand Up @@ -105,10 +108,12 @@ def __init__(
self._sequencer = sequencer.Sequencer(
part_list=self._part_list,
project_info=project_info,
ignore_outdated=ignore_local_sources,
)
self._executor = executor.Executor(
part_list=self._part_list,
project_info=project_info,
ignore_patterns=ignore_local_sources,
extra_build_packages=extra_build_packages,
)
self._project_info = project_info
Expand Down
16 changes: 14 additions & 2 deletions craft_parts/sequencer.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,24 @@ class Sequencer:
:param part_list: The list of parts to process.
:param project_info: Information about this project.
:param ignore_outdated: A list of file patterns to ignore when testing for
outdated files.
"""

def __init__(self, *, part_list: List[Part], project_info: ProjectInfo):
def __init__(
self,
*,
part_list: List[Part],
project_info: ProjectInfo,
ignore_outdated: Optional[List[str]] = None,
):
self._part_list = sort_parts(part_list)
self._project_info = project_info
self._sm = StateManager(project_info=project_info, part_list=part_list)
self._sm = StateManager(
project_info=project_info,
part_list=part_list,
ignore_outdated=ignore_outdated,
)
self._actions: List[Action] = []

def plan(self, target_step: Step, part_names: Sequence[str] = None) -> List[Action]:
Expand Down
7 changes: 7 additions & 0 deletions craft_parts/sources/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ def __init__(
source_checksum: Optional[str] = None,
command: Optional[str] = None,
project_dirs: Optional[ProjectDirs] = None,
ignore_patterns: Optional[List[str]] = None,
):
if not project_dirs:
project_dirs = ProjectDirs()

if not ignore_patterns:
ignore_patterns = []

self.source = str(source)
self.part_src_dir = str(part_src_dir)
self._cache_dir = cache_dir
Expand All @@ -72,6 +76,7 @@ def __init__(
self.command = command
self._dirs = project_dirs
self._checked = False
self._ignore_patterns = ignore_patterns

# pylint: enable=too-many-arguments

Expand Down Expand Up @@ -126,6 +131,7 @@ def __init__(
source_checksum: Optional[str] = None,
command: Optional[str] = None,
project_dirs: Optional[ProjectDirs] = None,
ignore_patterns: Optional[List[str]] = None,
):
super().__init__(
source,
Expand All @@ -138,6 +144,7 @@ def __init__(
source_checksum=source_checksum,
command=command,
project_dirs=project_dirs,
ignore_patterns=ignore_patterns,
)
self._file = Path()

Expand Down
32 changes: 25 additions & 7 deletions craft_parts/sources/local_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@

"""The local source handler and helpers."""

import contextlib
import functools
import glob
import logging
import os
from pathlib import Path
from typing import List, Optional

from craft_parts.utils import file_utils

from .base import SourceHandler

logger = logging.getLogger(__name__)

# TODO: change file operations to use pathlib


Expand All @@ -36,14 +41,22 @@ def __init__(self, *args, copy_function=file_utils.link_or_copy, **kwargs):
self.source_abspath = os.path.abspath(self.source)
self.copy_function = copy_function

ignore_patterns = [
self._dirs.parts_dir.name,
self._dirs.stage_dir.name,
self._dirs.prime_dir.name,
"*.snap", # FIXME: this should be specified by the application
]
if self._dirs.work_dir.resolve() == Path(self.source_abspath):
# ignore parts/stage/dir if source dir matches workdir
self._ignore_patterns.append(self._dirs.parts_dir.name)
self._ignore_patterns.append(self._dirs.stage_dir.name)
self._ignore_patterns.append(self._dirs.prime_dir.name)
else:
# otherwise check if work_dir inside source dir
with contextlib.suppress(ValueError):
rel_work_dir = self._dirs.work_dir.relative_to(self.source_abspath)
# deep workdirs will be cut at the first component
self._ignore_patterns.append(rel_work_dir.parts[0])

logger.debug("ignore patterns: %r", self._ignore_patterns)

self._ignore = functools.partial(
_ignore, self.source_abspath, os.getcwd(), ignore_patterns
_ignore, self.source_abspath, os.getcwd(), self._ignore_patterns
)
self._updated_files = set()
self._updated_directories = set()
Expand All @@ -67,6 +80,11 @@ def check_if_outdated(
:return: Whether the sources are outdated.
"""
if not ignore_files:
ignore_files = []

ignore_files.extend(self._ignore_patterns)

try:
target_mtime = os.lstat(target).st_mtime
except FileNotFoundError:
Expand Down
4 changes: 3 additions & 1 deletion craft_parts/sources/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
import os
import re
from pathlib import Path
from typing import TYPE_CHECKING, Dict, Optional, Type
from typing import TYPE_CHECKING, Dict, List, Optional, Type

from craft_parts.dirs import ProjectDirs

Expand All @@ -101,6 +101,7 @@ def get_source_handler(
cache_dir: Path,
part: "Part",
project_dirs: ProjectDirs,
ignore_patterns: Optional[List[str]] = None,
) -> Optional[SourceHandler]:
"""Return the appropriate handler for the given source.
Expand All @@ -124,6 +125,7 @@ def get_source_handler(
source_depth=part.spec.source_depth,
source_commit=part.spec.source_commit,
project_dirs=project_dirs,
ignore_patterns=ignore_patterns,
)

return source_handler
Expand Down
4 changes: 3 additions & 1 deletion craft_parts/sources/tar_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import tarfile
import tempfile
from pathlib import Path
from typing import Optional
from typing import List, Optional

from craft_parts.dirs import ProjectDirs

Expand All @@ -46,6 +46,7 @@ def __init__(
source_depth: Optional[int] = None,
source_checksum: Optional[str] = None,
project_dirs: Optional[ProjectDirs] = None,
ignore_patterns: Optional[List[str]] = None,
):
super().__init__(
source,
Expand All @@ -57,6 +58,7 @@ def __init__(
source_depth=source_depth,
source_checksum=source_checksum,
project_dirs=project_dirs,
ignore_patterns=ignore_patterns,
)
if source_tag:
raise errors.InvalidSourceOption(source_type="tar", option="source-tag")
Expand Down
12 changes: 11 additions & 1 deletion craft_parts/state_manager/state_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,21 @@ class StateManager:
:param project_info: The project information.
:param part_list: A list of this project's parts.
:param ignore_outdated: A list of file patterns to ignore when testing for
outdated files.
"""

def __init__(self, *, project_info: ProjectInfo, part_list: List[Part]):
def __init__(
self,
*,
project_info: ProjectInfo,
part_list: List[Part],
ignore_outdated: Optional[List[str]] = None
):
self._state_db = _StateDB()
self._project_info = project_info
self._part_list = part_list
self._ignore_outdated = ignore_outdated
self._source_handler_cache: Dict[str, Optional[SourceHandler]] = {}

part_step_list = _sort_steps_by_state_timestamp(part_list)
Expand Down Expand Up @@ -277,6 +286,7 @@ def check_if_outdated(self, part: Part, step: Step) -> Optional[OutdatedReport]:
cache_dir=self._project_info.cache_dir,
part=part,
project_dirs=self._project_info.dirs,
ignore_patterns=self._ignore_outdated,
)
self._source_handler_cache[part.name] = source_handler

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ min-similarity-lines=13
max-line-length = "88"
max-attributes = 15
max-args= 6
max-locals = 16
max-locals = 18

[tool.pylint.MASTER]
extension-pkg-whitelist = [
Expand Down
Loading

0 comments on commit ab69ef7

Please sign in to comment.