Skip to content
Open
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
18 changes: 13 additions & 5 deletions samcli/commands/_utils/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,19 @@ def _resolve_relative_to(path, original_root, new_root):
return None

# Value is definitely a relative path. Change it relative to the destination directory
return os.path.relpath(
# Resolve the paths to take care of symlinks
os.path.normpath(os.path.join(pathlib.Path(original_root).resolve(), path)),
pathlib.Path(new_root).resolve(), # Absolute original path w.r.t ``original_root``
) # Resolve the original path with respect to ``new_root``
# Resolve the paths to take care of symlinks
absolute_path = os.path.normpath(os.path.join(pathlib.Path(original_root).resolve(), path))
try:
return os.path.relpath(
absolute_path,
pathlib.Path(new_root).resolve(), # Absolute original path w.r.t ``original_root``
) # Resolve the original path with respect to ``new_root``
except ValueError:
# os.path.relpath raises ValueError when the two paths are on different drives or
# UNC mounts, which happens on Windows when the template and the build directory
# are not on the same drive. A relative path cannot express that, so fall back to
# the absolute path rather than letting the exception escape.
return absolute_path


def get_template_parameters(template_file):
Expand Down
17 changes: 16 additions & 1 deletion tests/unit/commands/_utils/test_template.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import copy
import os
import pathlib
import platform
import tempfile
from unittest import TestCase
from unittest import TestCase, skipIf
from unittest.mock import patch, mock_open, MagicMock
import shutil

Expand Down Expand Up @@ -803,6 +805,19 @@ def test_must_resolve_relative_to_symlinked_original_root_and_new_root(self):

self.assertEqual(result, expected_result)

@skipIf(platform.system() != "Windows", "Different drives only exist on Windows")
def test_must_resolve_relative_to_across_different_drives(self):
# os.path.relpath raises ValueError when the two paths are on different drives,
# so a template on one drive and a --build-dir on another cannot be expressed
# relatively. Fall back to the absolute path rather than crashing.
original_root = os.path.join("C:" + os.sep, "src")
new_root = os.path.join("D:" + os.sep, "destination")
expected_result = os.path.normpath(os.path.join(pathlib.Path(original_root).resolve(), self.curpath))

result = _resolve_relative_to(self.curpath, original_root, new_root)

self.assertEqual(result, expected_result)

def create_symlink(self, src, dest):
os.makedirs(src)
os.symlink(src, dest)
Expand Down