Skip to content

fix: Allow sync to work with dependencies in source #368

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

Merged
merged 6 commits into from
Jun 8, 2022
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
52 changes: 38 additions & 14 deletions aws_lambda_builders/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import os
import shutil
from typing import Set, Iterator, Tuple

from aws_lambda_builders.utils import copytree

Expand Down Expand Up @@ -124,14 +125,9 @@ def __init__(self, source_dir, artifact_dir, destination_dir):
self.dest_dir = destination_dir

def execute(self):
source = set(os.listdir(self.source_dir))
artifact = set(os.listdir(self.artifact_dir))
dependencies = artifact - source

for name in dependencies:
dependencies_source = os.path.join(self.artifact_dir, name)
new_destination = os.path.join(self.dest_dir, name)
deps_manager = DependencyManager(self.source_dir, self.artifact_dir, self.dest_dir)

for dependencies_source, new_destination in deps_manager.yield_source_dest():
if os.path.isdir(dependencies_source):
copytree(dependencies_source, new_destination)
else:
Expand All @@ -153,14 +149,9 @@ def __init__(self, source_dir, artifact_dir, destination_dir):
self.dest_dir = destination_dir

def execute(self):
source = set(os.listdir(self.source_dir))
artifact = set(os.listdir(self.artifact_dir))
dependencies = artifact - source

for name in dependencies:
dependencies_source = os.path.join(self.artifact_dir, name)
new_destination = os.path.join(self.dest_dir, name)
deps_manager = DependencyManager(self.source_dir, self.artifact_dir, self.dest_dir)

for dependencies_source, new_destination in deps_manager.yield_source_dest():
# shutil.move can't create subfolders if this is the first file in that folder
if os.path.isfile(dependencies_source):
os.makedirs(os.path.dirname(new_destination), exist_ok=True)
Expand Down Expand Up @@ -197,3 +188,36 @@ def execute(self):
shutil.rmtree(target_path)
else:
os.remove(target_path)


class DependencyManager:
"""
Class for handling the management of dependencies between directories
"""

# Ignore these files when comparing against which dependencies to move
# This allows for the installation of dependencies in the source directory
IGNORE_LIST = ["node_modules"]

def __init__(self, source_dir, artifact_dir, destination_dir) -> None:
self._source_dir: str = source_dir
self._artifact_dir: str = artifact_dir
self._dest_dir: str = destination_dir
self._dependencies: Set[str] = set()

def yield_source_dest(self) -> Iterator[Tuple[str, str]]:
self._set_dependencies()
for dep in self._dependencies:
yield os.path.join(self._artifact_dir, dep), os.path.join(self._dest_dir, dep)

def _set_dependencies(self) -> None:
source = self._get_source_files_exclude_deps()
artifact = set(os.listdir(self._artifact_dir))
self._dependencies = artifact - source

def _get_source_files_exclude_deps(self) -> Set[str]:
source_files = set(os.listdir(self._source_dir))
for item in self.IGNORE_LIST:
if item in source_files:
source_files.remove(item)
return source_files
50 changes: 49 additions & 1 deletion tests/unit/test_actions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from pathlib import Path
from typing import List, Tuple
from unittest import TestCase
from mock import patch, ANY
from mock import patch, ANY, Mock
from parameterized import parameterized

from aws_lambda_builders.actions import (
BaseAction,
Expand All @@ -8,6 +11,7 @@
CopyDependenciesAction,
MoveDependenciesAction,
CleanUpAction,
DependencyManager,
)


Expand Down Expand Up @@ -128,3 +132,47 @@ def test_must_copy(self, path_mock, listdir_mock, isdir_mock, rmtree_mock, rm_mo
listdir_mock.assert_any_call(target_dir)
rmtree_mock.assert_any_call("dir")
rm_mock.assert_any_call("file")


class TestDependencyManager(TestCase):
@parameterized.expand(
[
(
["app.js", "package.js", "libs", "node_modules"],
["app.js", "package.js", "libs", "node_modules"],
[("artifacts/node_modules", "dest/node_modules")],
None,
),
(
["file1, file2", "dep1", "dep2"],
["file1, file2", "dep1", "dep2"],
[("artifacts/dep1", "dest/dep1"), ("artifacts/dep2", "dest/dep2")],
["dep1", "dep2"],
),
(
["file1, file2"],
["file1, file2", "dep1", "dep2"],
[("artifacts/dep1", "dest/dep1"), ("artifacts/dep2", "dest/dep2")],
["dep1", "dep2"],
),
]
)
@patch("aws_lambda_builders.actions.os.listdir")
def test_excludes_dependencies_from_source(
self, source_files, artifact_files, expected, mock_dependencies, patched_list_dir
):
dependency_manager = DependencyManager("source", "artifacts", "dest")
dependency_manager.IGNORE_LIST = (
dependency_manager.IGNORE_LIST if mock_dependencies is None else mock_dependencies
)
patched_list_dir.side_effect = [source_files, artifact_files]
source_destinations = TestDependencyManager._convert_strings_to_paths(
list(dependency_manager.yield_source_dest())
)
expected_paths = TestDependencyManager._convert_strings_to_paths(expected)
for expected_source_dest in expected_paths:
self.assertIn(expected_source_dest, source_destinations)

@staticmethod
def _convert_strings_to_paths(source_dest_list):
return map(lambda item: (Path(item[0]), Path(item[1])), source_dest_list)