diff --git a/incorporealcms/ssg.py b/incorporealcms/ssg.py index e45b718..541f2b4 100644 --- a/incorporealcms/ssg.py +++ b/incorporealcms/ssg.py @@ -177,6 +177,10 @@ class StaticSiteGenerator(object): def symlink_to_relative_dest(self, base_dir: str, source: str) -> str: """Given a symlink, make sure it points to something inside the instance and provide its real destination. + This is made to be relative to the location of the symlink in all + circumstances, in order to avoid breaking out of the instance or output + dirs. + Args: base_dir: the full absolute path of the instance's pages dir, which the symlink destination must be in. source: the symlink to check @@ -185,8 +189,8 @@ class StaticSiteGenerator(object): """ if not os.path.realpath(source).startswith(base_dir): raise ValueError(f"symlink destination {os.path.realpath(source)} is outside the instance!") - # this symlink points to realpath inside base_dir, so relative to base_dir, the symlink dest is... - return os.path.relpath(os.path.realpath(source), base_dir) + # this symlink points to realpath inside base_dir, so relative to the source, the symlink dest is... + return os.path.relpath(os.path.realpath(source), os.path.dirname(source)) def build(): diff --git a/tests/instance/pages/subdir/relative-symlink-to-parent.md b/tests/instance/pages/subdir/relative-symlink-to-parent.md new file mode 120000 index 0000000..2bb6465 --- /dev/null +++ b/tests/instance/pages/subdir/relative-symlink-to-parent.md @@ -0,0 +1 @@ +../more-metadata.md \ No newline at end of file diff --git a/tests/test_ssg.py b/tests/test_ssg.py index a0bee09..e999264 100644 --- a/tests/test_ssg.py +++ b/tests/test_ssg.py @@ -52,6 +52,23 @@ def test_file_copy_symlink(): assert os.path.islink(os.path.join(tmpdir, 'symlink-to-foo.txt')) +def test_file_copy_subdir_symlink(): + """Test the ability to sync a symlink in a subdirectory to the output dir.""" + with tempfile.TemporaryDirectory() as tmpdir: + src_dir = os.path.join(HERE, 'instance') + generator = ssg.StaticSiteGenerator(src_dir, tmpdir) + # need to make the subdirectory as if the generator already did + os.mkdir(os.path.join(tmpdir, 'subdir')) + generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), 'subdir', + 'relative-symlink-to-parent.md', tmpdir) + # need to copy the destination for os.path.exists to be happy with this + generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'more-metadata.md', tmpdir) + logger.warning("created symlink %s", + os.readlink(os.path.join(tmpdir, 'subdir', 'relative-symlink-to-parent.md'))) + assert os.path.islink(os.path.join(tmpdir, 'subdir', 'relative-symlink-to-parent.md')) + assert os.path.exists(os.path.join(tmpdir, 'subdir', 'relative-symlink-to-parent.md')) + + def test_file_copy_symlink_of_markdown_also_has_html_symlink(): """Test the ability to sync source and generated symlinks to the output dir.""" with tempfile.TemporaryDirectory() as tmpdir: