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
5 changes: 5 additions & 0 deletions samcli/commands/build/build_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from samcli.commands._utils.template import get_template_data
from samcli.commands.exceptions import UserException
from samcli.local.lambdafn.exceptions import FunctionNotFound
from samcli.commands.build.exceptions import InvalidBuildDirException

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -83,6 +84,10 @@ def __exit__(self, *args):
def _setup_build_dir(build_dir, clean):
build_path = pathlib.Path(build_dir)

if os.path.abspath(str(build_path)) == os.path.abspath(str(pathlib.Path.cwd())):
exception_message = "Failing build: Running a build with build-dir as current working directory is extremely dangerous since the build-dir contents is first removed. This is no longer supported, please remove the '--build-dir' option from the command to allow the build artifacts to be placed in the directory your template is in."
raise InvalidBuildDirException(exception_message)

if build_path.exists() and os.listdir(build_dir) and clean:
# build folder contains something inside. Clear everything.
shutil.rmtree(build_dir)
Expand Down
2 changes: 1 addition & 1 deletion samcli/commands/build/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
@click.option('--build-dir', '-b',
default=DEFAULT_BUILD_DIR,
type=click.Path(file_okay=False, dir_okay=True, writable=True), # Must be a directory
help="Path to a folder where the built artifacts will be stored")
help="Path to a folder where the built artifacts will be stored. This directory will be first removed before starting a build.")
@click.option("--base-dir", "-s",
default=None,
type=click.Path(dir_okay=True, file_okay=False), # Must be a directory
Expand Down
10 changes: 10 additions & 0 deletions samcli/commands/build/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Build exceptions"""

from samcli.commands.exceptions import UserException


class InvalidBuildDirException(UserException):
"""
Value provided to --build-dir is invalid
"""
pass
39 changes: 39 additions & 0 deletions tests/unit/commands/buildcmd/test_build_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from mock import patch, Mock

from samcli.commands.build.build_context import BuildContext
from samcli.commands.build.exceptions import InvalidBuildDirException


class TestBuildContext__enter__(TestCase):
Expand Down Expand Up @@ -121,6 +122,8 @@ class TestBuildContext_setup_build_dir(TestCase):
def test_build_dir_exists_with_non_empty_dir(self, pathlib_patch, os_patch, shutil_patch):
path_mock = Mock()
pathlib_patch.Path.return_value = path_mock
os_patch.path.abspath.side_effect = ["/somepath", "/cwd/path"]
path_mock.cwd.return_value = "/cwd/path"
os_patch.listdir.return_value = True
path_mock.resolve.return_value = "long/full/path"
path_mock.exists.return_value = True
Expand All @@ -129,12 +132,14 @@ def test_build_dir_exists_with_non_empty_dir(self, pathlib_patch, os_patch, shut
full_build_path = BuildContext._setup_build_dir(build_dir, True)

self.assertEqual(full_build_path, "long/full/path")
self.assertEqual(os_patch.path.abspath.call_count, 2)

os_patch.listdir.assert_called_once()
path_mock.exists.assert_called_once()
path_mock.mkdir.assert_called_once_with(mode=0o755, parents=True, exist_ok=True)
pathlib_patch.Path.assert_called_once_with(build_dir)
shutil_patch.rmtree.assert_called_once_with(build_dir)
pathlib_patch.Path.cwd.assert_called_once()

@patch("samcli.commands.build.build_context.shutil")
@patch("samcli.commands.build.build_context.os")
Expand All @@ -143,46 +148,56 @@ def test_build_dir_exists_with_empty_dir(self, pathlib_patch, os_patch, shutil_p
path_mock = Mock()
pathlib_patch.Path.return_value = path_mock
os_patch.listdir.return_value = False
os_patch.path.abspath.side_effect = ["/somepath", "/cwd/path"]
path_mock.cwd.return_value = "/cwd/path"
path_mock.resolve.return_value = "long/full/path"
path_mock.exists.return_value = True
build_dir = "/somepath"

full_build_path = BuildContext._setup_build_dir(build_dir, True)

self.assertEqual(full_build_path, "long/full/path")
self.assertEqual(os_patch.path.abspath.call_count, 2)

os_patch.listdir.assert_called_once()
path_mock.exists.assert_called_once()
path_mock.mkdir.assert_called_once_with(mode=0o755, parents=True, exist_ok=True)
pathlib_patch.Path.assert_called_once_with(build_dir)
shutil_patch.rmtree.assert_not_called()
pathlib_patch.Path.cwd.assert_called_once()

@patch("samcli.commands.build.build_context.shutil")
@patch("samcli.commands.build.build_context.os")
@patch("samcli.commands.build.build_context.pathlib")
def test_build_dir_does_not_exist(self, pathlib_patch, os_patch, shutil_patch):
path_mock = Mock()
pathlib_patch.Path.return_value = path_mock
os_patch.path.abspath.side_effect = ["/somepath", "/cwd/path"]
path_mock.cwd.return_value = "/cwd/path"
path_mock.resolve.return_value = "long/full/path"
path_mock.exists.return_value = False
build_dir = "/somepath"

full_build_path = BuildContext._setup_build_dir(build_dir, True)

self.assertEqual(full_build_path, "long/full/path")
self.assertEqual(os_patch.path.abspath.call_count, 2)

os_patch.listdir.assert_not_called()
path_mock.exists.assert_called_once()
path_mock.mkdir.assert_called_once_with(mode=0o755, parents=True, exist_ok=True)
pathlib_patch.Path.assert_called_once_with(build_dir)
shutil_patch.rmtree.assert_not_called()
pathlib_patch.Path.cwd.assert_called_once()

@patch("samcli.commands.build.build_context.shutil")
@patch("samcli.commands.build.build_context.os")
@patch("samcli.commands.build.build_context.pathlib")
def test_non_clean_build_when_dir_exists_with_non_empty_dir(self, pathlib_patch, os_patch, shutil_patch):
path_mock = Mock()
pathlib_patch.Path.return_value = path_mock
os_patch.path.abspath.side_effect = ["/somepath", "/cwd/path"]
path_mock.cwd.return_value = "/cwd/path"
os_patch.listdir.return_value = True
path_mock.resolve.return_value = "long/full/path"
path_mock.exists.return_value = True
Expand All @@ -191,9 +206,33 @@ def test_non_clean_build_when_dir_exists_with_non_empty_dir(self, pathlib_patch,
full_build_path = BuildContext._setup_build_dir(build_dir, False)

self.assertEqual(full_build_path, "long/full/path")
self.assertEqual(os_patch.path.abspath.call_count, 2)

os_patch.listdir.assert_called_once()
path_mock.exists.assert_called_once()
path_mock.mkdir.assert_called_once_with(mode=0o755, parents=True, exist_ok=True)
pathlib_patch.Path.assert_called_once_with(build_dir)
shutil_patch.rmtree.assert_not_called()
pathlib_patch.Path.cwd.assert_called_once()

@patch("samcli.commands.build.build_context.shutil")
@patch("samcli.commands.build.build_context.os")
@patch("samcli.commands.build.build_context.pathlib")
def test_when_build_dir_is_cwd_raises_exception(self, pathlib_patch, os_patch, shutil_patch):
path_mock = Mock()
pathlib_patch.Path.return_value = path_mock
os_patch.path.abspath.side_effect = ["/somepath", "/somepath"]
path_mock.cwd.return_value = "/somepath"
build_dir = "/somepath"

with self.assertRaises(InvalidBuildDirException):
BuildContext._setup_build_dir(build_dir, True)

self.assertEqual(os_patch.path.abspath.call_count, 2)

os_patch.listdir.assert_not_called()
path_mock.exists.assert_not_called()
path_mock.mkdir.assert_not_called()
pathlib_patch.Path.assert_called_once_with(build_dir)
shutil_patch.rmtree.assert_not_called()
pathlib_patch.Path.cwd.assert_called_once()