Compare commits
	
		
			3 Commits
		
	
	
		
			e75d5c48d2
			...
			7205bb2aa5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7205bb2aa5 | |||
| 88b678931e | |||
| 8c75947088 | 
@ -2,6 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Included is a summary of changes to the project, by version. Details can be found in the commit history.
 | 
					Included is a summary of changes to the project, by version. Details can be found in the commit history.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## v2.0.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Bugfixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* With some significant refactoring, files are now handled better with respect to relative paths, which fixes an issue
 | 
				
			||||||
 | 
					  with symlink pages only properly getting resolved to their target if the symlink was in the `pages/` root rather than
 | 
				
			||||||
 | 
					  a subdir.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## v2.0.3
 | 
					## v2.0.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Bugfixes
 | 
					### Bugfixes
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,9 @@ logger = logging.getLogger(__name__)
 | 
				
			|||||||
def generate_feed(feed_type: str, instance_dir: str, dest_dir: str) -> None:
 | 
					def generate_feed(feed_type: str, instance_dir: str, dest_dir: str) -> None:
 | 
				
			||||||
    """Generate the Atom or RSS feed as requested.
 | 
					    """Generate the Atom or RSS feed as requested.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Feed entries should be symlinks to .md files in the pages/ directory, so that they
 | 
				
			||||||
 | 
					    are also linkable and can be browsed outside of the feed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Args:
 | 
					    Args:
 | 
				
			||||||
        feed_type: 'atom' or 'rss' feed
 | 
					        feed_type: 'atom' or 'rss' feed
 | 
				
			||||||
        instance_dir: the directory for the instance, containing both the feed dir and pages
 | 
					        instance_dir: the directory for the instance, containing both the feed dir and pages
 | 
				
			||||||
@ -37,16 +40,18 @@ def generate_feed(feed_type: str, instance_dir: str, dest_dir: str) -> None:
 | 
				
			|||||||
    fg.link(href=f'https://{Config.DOMAIN_NAME}', rel='alternate')
 | 
					    fg.link(href=f'https://{Config.DOMAIN_NAME}', rel='alternate')
 | 
				
			||||||
    fg.subtitle(f"Blog posts and other interesting materials from {Config.TITLE_SUFFIX}")
 | 
					    fg.subtitle(f"Blog posts and other interesting materials from {Config.TITLE_SUFFIX}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # feed symlinks should all be within the core content subdirectory
 | 
				
			||||||
 | 
					    pages_dir = os.path.join(instance_dir, 'pages')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # get recent feeds
 | 
					    # get recent feeds
 | 
				
			||||||
    feed_path = os.path.join(instance_dir, 'feed')
 | 
					    feed_path = os.path.join(instance_dir, 'feed')
 | 
				
			||||||
    feed_entry_paths = [os.path.join(dirpath, filename) for dirpath, _, filenames in os.walk(feed_path)
 | 
					    feed_entry_paths = [os.path.join(dirpath, filename) for dirpath, _, filenames in os.walk(feed_path)
 | 
				
			||||||
                        for filename in filenames if os.path.islink(os.path.join(dirpath, filename))]
 | 
					                        for filename in filenames if os.path.islink(os.path.join(dirpath, filename))]
 | 
				
			||||||
    for feed_entry_path in sorted(feed_entry_paths):
 | 
					    for feed_entry_path in sorted(feed_entry_paths):
 | 
				
			||||||
        # get the actual file to parse it
 | 
					        # get the actual file to parse it
 | 
				
			||||||
        os.chdir(os.path.abspath(os.path.join(instance_dir, 'pages')))
 | 
					        resolved_path = os.path.relpath(os.path.realpath(feed_entry_path), pages_dir)
 | 
				
			||||||
        resolved_path = os.path.relpath(os.path.realpath(feed_entry_path), os.path.join(instance_dir, 'pages'))
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            content, md, page_name, page_title, mtime = parse_md(resolved_path)
 | 
					            content, md, page_name, page_title, mtime = parse_md(os.path.join(pages_dir, resolved_path), pages_dir)
 | 
				
			||||||
            link = f'https://{Config.DOMAIN_NAME}{instance_resource_path_to_request_path(resolved_path)}'
 | 
					            link = f'https://{Config.DOMAIN_NAME}{instance_resource_path_to_request_path(resolved_path)}'
 | 
				
			||||||
        except (OSError, ValueError, TypeError):
 | 
					        except (OSError, ValueError, TypeError):
 | 
				
			||||||
            logger.exception("error loading/rendering markdown!")
 | 
					            logger.exception("error loading/rendering markdown!")
 | 
				
			||||||
 | 
				
			|||||||
@ -44,18 +44,21 @@ def instance_resource_path_to_request_path(path):
 | 
				
			|||||||
    return '/' + re.sub(r'.md$', '', re.sub(r'index.md$', '', path))
 | 
					    return '/' + re.sub(r'.md$', '', re.sub(r'index.md$', '', path))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_md(path: str):
 | 
					def parse_md(path: str, pages_root: str):
 | 
				
			||||||
    """Given a file to parse, return file content and other derived data along with the md object.
 | 
					    """Given a file to parse, return file content and other derived data along with the md object.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Args:
 | 
					    Args:
 | 
				
			||||||
        path: the path to the file to render
 | 
					        path: the path to the file to render
 | 
				
			||||||
 | 
					        pages_root: the absolute path to the pages/ dir, which the path should be within. necessary for
 | 
				
			||||||
 | 
					                    proper resolution of resolving parent pages (which needs to know when to stop)
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        logger.debug("opening path '%s'", path)
 | 
					        absolute_path = os.path.join(pages_root, path)
 | 
				
			||||||
        with open(path, 'r') as input_file:
 | 
					        logger.debug("opening path '%s'", absolute_path)
 | 
				
			||||||
 | 
					        with open(absolute_path, 'r') as input_file:
 | 
				
			||||||
            mtime = datetime.datetime.fromtimestamp(os.path.getmtime(input_file.name), tz=datetime.timezone.utc)
 | 
					            mtime = datetime.datetime.fromtimestamp(os.path.getmtime(input_file.name), tz=datetime.timezone.utc)
 | 
				
			||||||
            entry = input_file.read()
 | 
					            entry = input_file.read()
 | 
				
			||||||
        logger.debug("path '%s' read", path)
 | 
					        logger.debug("path '%s' read", absolute_path)
 | 
				
			||||||
        md = init_md()
 | 
					        md = init_md()
 | 
				
			||||||
        content = Markup(md.convert(entry))     # nosec B704
 | 
					        content = Markup(md.convert(entry))     # nosec B704
 | 
				
			||||||
    except (OSError, FileNotFoundError):
 | 
					    except (OSError, FileNotFoundError):
 | 
				
			||||||
@ -67,17 +70,25 @@ def parse_md(path: str):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    logger.debug("file metadata: %s", md.Meta)
 | 
					    logger.debug("file metadata: %s", md.Meta)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    page_name = get_meta_str(md, 'title') if md.Meta.get('title') else instance_resource_path_to_request_path(path)
 | 
					    rel_path = os.path.relpath(path, pages_root)
 | 
				
			||||||
 | 
					    page_name = get_meta_str(md, 'title') if md.Meta.get('title') else instance_resource_path_to_request_path(rel_path)
 | 
				
			||||||
    page_title = f'{page_name} - {Config.TITLE_SUFFIX}' if page_name else Config.TITLE_SUFFIX
 | 
					    page_title = f'{page_name} - {Config.TITLE_SUFFIX}' if page_name else Config.TITLE_SUFFIX
 | 
				
			||||||
    logger.debug("title (potentially derived): %s", page_title)
 | 
					    logger.debug("title (potentially derived): %s", page_title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return content, md, page_name, page_title, mtime
 | 
					    return content, md, page_name, page_title, mtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def handle_markdown_file_path(path: str) -> str:
 | 
					def handle_markdown_file_path(path: str, pages_root: str) -> str:
 | 
				
			||||||
    """Given a location on disk, attempt to open it and render the markdown within."""
 | 
					    """Given a location on disk, attempt to open it and render the markdown within.
 | 
				
			||||||
    content, md, page_name, page_title, mtime = parse_md(path)
 | 
					
 | 
				
			||||||
    parent_navs = generate_parent_navs(path)
 | 
					    Args:
 | 
				
			||||||
 | 
					        path: the path to the file to parse and produce metadata for
 | 
				
			||||||
 | 
					        pages_root: the absolute path to the pages/ dir, which the path should be within. necessary for
 | 
				
			||||||
 | 
					                    proper resolution of resolving parent pages (which needs to know when to stop)
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    content, md, page_name, page_title, mtime = parse_md(path, pages_root)
 | 
				
			||||||
 | 
					    relative_path = os.path.relpath(path, pages_root)
 | 
				
			||||||
 | 
					    parent_navs = generate_parent_navs(relative_path, pages_root)
 | 
				
			||||||
    extra_footer = get_meta_str(md, 'footer') if md.Meta.get('footer') else None
 | 
					    extra_footer = get_meta_str(md, 'footer') if md.Meta.get('footer') else None
 | 
				
			||||||
    template_name = get_meta_str(md, 'template') if md.Meta.get('template') else 'base.html'
 | 
					    template_name = get_meta_str(md, 'template') if md.Meta.get('template') else 'base.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,14 +103,21 @@ def handle_markdown_file_path(path: str) -> str:
 | 
				
			|||||||
                           description=get_meta_str(md, 'description'),
 | 
					                           description=get_meta_str(md, 'description'),
 | 
				
			||||||
                           image=Config.BASE_HOST + get_meta_str(md, 'image'),
 | 
					                           image=Config.BASE_HOST + get_meta_str(md, 'image'),
 | 
				
			||||||
                           content=content,
 | 
					                           content=content,
 | 
				
			||||||
                           base_url=Config.BASE_HOST + instance_resource_path_to_request_path(path),
 | 
					                           base_url=Config.BASE_HOST + instance_resource_path_to_request_path(relative_path),
 | 
				
			||||||
                           navs=parent_navs,
 | 
					                           navs=parent_navs,
 | 
				
			||||||
                           mtime=mtime.strftime('%Y-%m-%d %H:%M:%S %Z'),
 | 
					                           mtime=mtime.strftime('%Y-%m-%d %H:%M:%S %Z'),
 | 
				
			||||||
                           extra_footer=extra_footer)
 | 
					                           extra_footer=extra_footer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def generate_parent_navs(path):
 | 
					def generate_parent_navs(path, pages_root: str):
 | 
				
			||||||
    """Create a series of paths/links to navigate up from the given resource path."""
 | 
					    """Create a series of paths/links to navigate up from the given resource path.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        path: the path to parse and generate parent metadata nav links for
 | 
				
			||||||
 | 
					        pages_root: the absolute path to the pages/ dir, which the path should be within. path is relative,
 | 
				
			||||||
 | 
					                    but opening parents requires the full path
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    logger.debug("path to generate navs for: %s", path)
 | 
				
			||||||
    if path == 'index.md':
 | 
					    if path == 'index.md':
 | 
				
			||||||
        # bail and return the domain name as a terminal case
 | 
					        # bail and return the domain name as a terminal case
 | 
				
			||||||
        return [(Config.DOMAIN_NAME, '/')]
 | 
					        return [(Config.DOMAIN_NAME, '/')]
 | 
				
			||||||
@ -124,14 +142,14 @@ def generate_parent_navs(path):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # read the resource
 | 
					        # read the resource
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            with open(path, 'r') as entry_file:
 | 
					            with open(os.path.join(pages_root, path), 'r') as entry_file:
 | 
				
			||||||
                entry = entry_file.read()
 | 
					                entry = entry_file.read()
 | 
				
			||||||
            _ = Markup(md.convert(entry))       # nosec B704
 | 
					            _ = Markup(md.convert(entry))       # nosec B704
 | 
				
			||||||
            page_name = (" ".join(md.Meta.get('title')) if md.Meta.get('title')
 | 
					            page_name = (" ".join(md.Meta.get('title')) if md.Meta.get('title')
 | 
				
			||||||
                         else request_path_to_breadcrumb_display(request_path))
 | 
					                         else request_path_to_breadcrumb_display(request_path))
 | 
				
			||||||
            return generate_parent_navs(parent_resource_path) + [(page_name, request_path)]
 | 
					            return generate_parent_navs(parent_resource_path, pages_root) + [(page_name, request_path)]
 | 
				
			||||||
        except FileNotFoundError:
 | 
					        except FileNotFoundError:
 | 
				
			||||||
            return generate_parent_navs(parent_resource_path) + [(request_path, request_path)]
 | 
					            return generate_parent_navs(parent_resource_path, pages_root) + [(request_path, request_path)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def request_path_to_breadcrumb_display(path):
 | 
					def request_path_to_breadcrumb_display(path):
 | 
				
			||||||
 | 
				
			|||||||
@ -86,7 +86,6 @@ class StaticSiteGenerator(object):
 | 
				
			|||||||
            convert_markdown: whether or not to convert Markdown files (or simply copy them)
 | 
					            convert_markdown: whether or not to convert Markdown files (or simply copy them)
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        cprint(f"copying files from '{source_dir}' to '{dest_dir}'", 'green')
 | 
					        cprint(f"copying files from '{source_dir}' to '{dest_dir}'", 'green')
 | 
				
			||||||
        os.chdir(source_dir)
 | 
					 | 
				
			||||||
        for base_dir, subdirs, files in os.walk(source_dir):
 | 
					        for base_dir, subdirs, files in os.walk(source_dir):
 | 
				
			||||||
            logger.debug("starting to build against %s || %s || %s", base_dir, subdirs, files)
 | 
					            logger.debug("starting to build against %s || %s || %s", base_dir, subdirs, files)
 | 
				
			||||||
            # remove the absolute path of the directory from the base_dir
 | 
					            # remove the absolute path of the directory from the base_dir
 | 
				
			||||||
@ -113,9 +112,12 @@ class StaticSiteGenerator(object):
 | 
				
			|||||||
            dest_dir: the output directory to place the subdir in
 | 
					            dest_dir: the output directory to place the subdir in
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        dst = os.path.join(dest_dir, base_dir, subdir)
 | 
					        dst = os.path.join(dest_dir, base_dir, subdir)
 | 
				
			||||||
        if os.path.islink(os.path.join(base_dir, subdir)):
 | 
					        absolute_dir = os.path.join(source_dir, base_dir, subdir)
 | 
				
			||||||
 | 
					        logger.debug("checking if %s is a symlink or not", absolute_dir)
 | 
				
			||||||
 | 
					        if os.path.islink(absolute_dir):
 | 
				
			||||||
 | 
					            logger.debug("symlink; raw destination is %s", os.path.realpath(absolute_dir))
 | 
				
			||||||
            # keep the link relative to the output directory
 | 
					            # keep the link relative to the output directory
 | 
				
			||||||
            src = self.symlink_to_relative_dest(source_dir, os.path.join(base_dir, subdir))
 | 
					            src = self.symlink_to_relative_dest(source_dir, absolute_dir)
 | 
				
			||||||
            print(f"creating directory symlink '{dst}' -> '{src}'")
 | 
					            print(f"creating directory symlink '{dst}' -> '{src}'")
 | 
				
			||||||
            os.symlink(src, dst, target_is_directory=True)
 | 
					            os.symlink(src, dst, target_is_directory=True)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
@ -137,9 +139,12 @@ class StaticSiteGenerator(object):
 | 
				
			|||||||
            dest_dir: the output directory to place the subdir in
 | 
					            dest_dir: the output directory to place the subdir in
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        dst = os.path.join(dest_dir, base_dir, file_)
 | 
					        dst = os.path.join(dest_dir, base_dir, file_)
 | 
				
			||||||
        if os.path.islink(os.path.join(base_dir, file_)):
 | 
					        absolute_file = os.path.join(source_dir, base_dir, file_)
 | 
				
			||||||
 | 
					        logger.debug("checking if %s is a symlink or not", absolute_file)
 | 
				
			||||||
 | 
					        if os.path.islink(absolute_file):
 | 
				
			||||||
 | 
					            logger.debug("symlink; raw destination is %s", os.path.realpath(absolute_file))
 | 
				
			||||||
            # keep the link relative to the output directory
 | 
					            # keep the link relative to the output directory
 | 
				
			||||||
            src = self.symlink_to_relative_dest(source_dir, os.path.join(base_dir, file_))
 | 
					            src = self.symlink_to_relative_dest(source_dir, absolute_file)
 | 
				
			||||||
            print(f"creating symlink '{dst}' -> '{src}'")
 | 
					            print(f"creating symlink '{dst}' -> '{src}'")
 | 
				
			||||||
            os.symlink(src, dst, target_is_directory=False)
 | 
					            os.symlink(src, dst, target_is_directory=False)
 | 
				
			||||||
            if src.endswith('.md') and convert_markdown:
 | 
					            if src.endswith('.md') and convert_markdown:
 | 
				
			||||||
@ -151,7 +156,7 @@ class StaticSiteGenerator(object):
 | 
				
			|||||||
                os.symlink(second_src, second_dst, target_is_directory=False)
 | 
					                os.symlink(second_src, second_dst, target_is_directory=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            src = os.path.join(base_dir, file_)
 | 
					            src = os.path.join(source_dir, base_dir, file_)
 | 
				
			||||||
            print(f"copying file '{src}' -> '{dst}'")
 | 
					            print(f"copying file '{src}' -> '{dst}'")
 | 
				
			||||||
            shutil.copy2(src, dst)
 | 
					            shutil.copy2(src, dst)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -160,7 +165,7 @@ class StaticSiteGenerator(object):
 | 
				
			|||||||
                rendered_file = dst.removesuffix('.md') + '.html'
 | 
					                rendered_file = dst.removesuffix('.md') + '.html'
 | 
				
			||||||
                print(f"rendering file '{src}' -> '{rendered_file}'")
 | 
					                print(f"rendering file '{src}' -> '{rendered_file}'")
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    content = handle_markdown_file_path(src)
 | 
					                    content = handle_markdown_file_path(src, source_dir)
 | 
				
			||||||
                except UnicodeDecodeError:
 | 
					                except UnicodeDecodeError:
 | 
				
			||||||
                    # perhaps this isn't a markdown file at all for some reason; we
 | 
					                    # perhaps this isn't a markdown file at all for some reason; we
 | 
				
			||||||
                    # copied it above so stick with tha
 | 
					                    # copied it above so stick with tha
 | 
				
			||||||
@ -172,6 +177,10 @@ class StaticSiteGenerator(object):
 | 
				
			|||||||
    def symlink_to_relative_dest(self, base_dir: str, source: str) -> str:
 | 
					    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.
 | 
					        """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:
 | 
					        Args:
 | 
				
			||||||
            base_dir: the full absolute path of the instance's pages dir, which the symlink destination must be in.
 | 
					            base_dir: the full absolute path of the instance's pages dir, which the symlink destination must be in.
 | 
				
			||||||
            source: the symlink to check
 | 
					            source: the symlink to check
 | 
				
			||||||
@ -180,8 +189,8 @@ class StaticSiteGenerator(object):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        if not os.path.realpath(source).startswith(base_dir):
 | 
					        if not os.path.realpath(source).startswith(base_dir):
 | 
				
			||||||
            raise ValueError(f"symlink destination {os.path.realpath(source)} is outside the instance!")
 | 
					            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...
 | 
					        # 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), base_dir)
 | 
					        return os.path.relpath(os.path.realpath(source), os.path.dirname(source))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def build():
 | 
					def build():
 | 
				
			||||||
 | 
				
			|||||||
@ -23,13 +23,11 @@ def test_graphviz_is_rendered():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
					        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(src_dir, 'pages'))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'test-graphviz.md', tmpdir, True)
 | 
					        ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'test-graphviz.md', tmpdir, True)
 | 
				
			||||||
        with open(os.path.join(tmpdir, 'test-graphviz.html'), 'r') as graphviz_output:
 | 
					        with open(os.path.join(tmpdir, 'test-graphviz.html'), 'r') as graphviz_output:
 | 
				
			||||||
            data = graphviz_output.read()
 | 
					            data = graphviz_output.read()
 | 
				
			||||||
            assert 'data:image/png;base64' in data
 | 
					            assert 'data:image/png;base64' in data
 | 
				
			||||||
    os.chdir(HERE)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_invalid_graphviz_is_not_rendered():
 | 
					def test_invalid_graphviz_is_not_rendered():
 | 
				
			||||||
@ -37,12 +35,10 @@ def test_invalid_graphviz_is_not_rendered():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
					        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(src_dir, 'broken'))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with pytest.raises(ValueError):
 | 
					        with pytest.raises(ValueError):
 | 
				
			||||||
            ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'broken'), '', 'test-invalid-graphviz.md',
 | 
					            ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'broken'), '', 'test-invalid-graphviz.md',
 | 
				
			||||||
                                          tmpdir, True)
 | 
					                                          tmpdir, True)
 | 
				
			||||||
    os.chdir(HERE)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_figures_are_rendered():
 | 
					def test_figures_are_rendered():
 | 
				
			||||||
@ -50,7 +46,6 @@ def test_figures_are_rendered():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
					        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(src_dir, 'pages'))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'figures.md', tmpdir, True)
 | 
					        ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'figures.md', tmpdir, True)
 | 
				
			||||||
        with open(os.path.join(tmpdir, 'figures.html'), 'r') as graphviz_output:
 | 
					        with open(os.path.join(tmpdir, 'figures.html'), 'r') as graphviz_output:
 | 
				
			||||||
@ -62,7 +57,6 @@ def test_figures_are_rendered():
 | 
				
			|||||||
            assert ('<figure class="left"><img alt="fancy logo" src="bss-square-no-bg.png" />'
 | 
					            assert ('<figure class="left"><img alt="fancy logo" src="bss-square-no-bg.png" />'
 | 
				
			||||||
                    '<span></span></figure>') in data
 | 
					                    '<span></span></figure>') in data
 | 
				
			||||||
            assert '<figure><img alt="just a logo" src="bss-square-no-bg.png" /></figure>' in data
 | 
					            assert '<figure><img alt="just a logo" src="bss-square-no-bg.png" /></figure>' in data
 | 
				
			||||||
    os.chdir(HERE)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_og_image():
 | 
					def test_og_image():
 | 
				
			||||||
@ -70,7 +64,6 @@ def test_og_image():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
					        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(src_dir, 'pages'))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'more-metadata.md', tmpdir, True)
 | 
					        ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'more-metadata.md', tmpdir, True)
 | 
				
			||||||
        with open(os.path.join(tmpdir, 'more-metadata.html'), 'r') as markdown_output:
 | 
					        with open(os.path.join(tmpdir, 'more-metadata.html'), 'r') as markdown_output:
 | 
				
			||||||
@ -83,7 +76,6 @@ def test_og_url():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
					        ssg = StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(src_dir, 'pages'))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # testing a whole build run because of bugs in how I handle pathing adding a "./" in
 | 
					        # testing a whole build run because of bugs in how I handle pathing adding a "./" in
 | 
				
			||||||
        # the generated URLs for content in the pages/ root
 | 
					        # the generated URLs for content in the pages/ root
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,10 @@
 | 
				
			|||||||
				"level": "DEBUG",
 | 
									"level": "DEBUG",
 | 
				
			||||||
				"handlers": ["console"]
 | 
									"handlers": ["console"]
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								"incorporealcms.markdown": {
 | 
				
			||||||
 | 
									"level": "DEBUG",
 | 
				
			||||||
 | 
									"handlers": ["console"]
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
			"incorporealcms.ssg": {
 | 
								"incorporealcms.ssg": {
 | 
				
			||||||
				"level": "DEBUG",
 | 
									"level": "DEBUG",
 | 
				
			||||||
				"handlers": ["console"]
 | 
									"handlers": ["console"]
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								tests/instance/pages/subdir/relative-symlink-to-parent.md
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								tests/instance/pages/subdir/relative-symlink-to-parent.md
									
									
									
									
									
										Symbolic link
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					../more-metadata.md
 | 
				
			||||||
@ -8,33 +8,38 @@ from unittest.mock import patch
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from incorporealcms import init_instance
 | 
				
			||||||
from incorporealcms.markdown import (generate_parent_navs, handle_markdown_file_path,
 | 
					from incorporealcms.markdown import (generate_parent_navs, handle_markdown_file_path,
 | 
				
			||||||
                                     instance_resource_path_to_request_path, parse_md,
 | 
					                                     instance_resource_path_to_request_path, parse_md,
 | 
				
			||||||
                                     request_path_to_breadcrumb_display)
 | 
					                                     request_path_to_breadcrumb_display)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HERE = os.path.dirname(os.path.abspath(__file__))
 | 
					HERE = os.path.dirname(os.path.abspath(__file__))
 | 
				
			||||||
os.chdir(os.path.join(HERE, 'instance/', 'pages/'))
 | 
					INSTANCE_DIR = os.path.join(HERE, 'instance')
 | 
				
			||||||
 | 
					PAGES_DIR = os.path.join(INSTANCE_DIR, 'pages/')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# initialize in order to configure debug logging
 | 
				
			||||||
 | 
					init_instance(INSTANCE_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_generate_page_navs_index():
 | 
					def test_generate_page_navs_index():
 | 
				
			||||||
    """Test that the index page has navs to the root (itself)."""
 | 
					    """Test that the index page has navs to the root (itself)."""
 | 
				
			||||||
    assert generate_parent_navs('index.md') == [('example.org', '/')]
 | 
					    assert generate_parent_navs('index.md', PAGES_DIR) == [('example.org', '/')]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_generate_page_navs_subdir_index():
 | 
					def test_generate_page_navs_subdir_index():
 | 
				
			||||||
    """Test that dir pages have navs to the root and themselves."""
 | 
					    """Test that dir pages have navs to the root and themselves."""
 | 
				
			||||||
    assert generate_parent_navs('subdir/index.md') == [('example.org', '/'), ('subdir', '/subdir/')]
 | 
					    assert generate_parent_navs('subdir/index.md', PAGES_DIR) == [('example.org', '/'), ('subdir', '/subdir/')]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_generate_page_navs_subdir_real_page():
 | 
					def test_generate_page_navs_subdir_real_page():
 | 
				
			||||||
    """Test that real pages have navs to the root, their parent, and themselves."""
 | 
					    """Test that real pages have navs to the root, their parent, and themselves."""
 | 
				
			||||||
    assert generate_parent_navs('subdir/page.md') == [('example.org', '/'), ('subdir', '/subdir/'),
 | 
					    assert generate_parent_navs('subdir/page.md', PAGES_DIR) == [('example.org', '/'), ('subdir', '/subdir/'),
 | 
				
			||||||
                                                                 ('Page', '/subdir/page')]
 | 
					                                                                 ('Page', '/subdir/page')]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_generate_page_navs_subdir_with_title_parsing_real_page():
 | 
					def test_generate_page_navs_subdir_with_title_parsing_real_page():
 | 
				
			||||||
    """Test that title metadata is used in the nav text."""
 | 
					    """Test that title metadata is used in the nav text."""
 | 
				
			||||||
    assert generate_parent_navs('subdir-with-title/page.md') == [
 | 
					    assert generate_parent_navs('subdir-with-title/page.md', PAGES_DIR) == [
 | 
				
			||||||
        ('example.org', '/'),
 | 
					        ('example.org', '/'),
 | 
				
			||||||
        ('SUB!', '/subdir-with-title/'),
 | 
					        ('SUB!', '/subdir-with-title/'),
 | 
				
			||||||
        ('page', '/subdir-with-title/page')
 | 
					        ('page', '/subdir-with-title/page')
 | 
				
			||||||
@ -43,7 +48,7 @@ def test_generate_page_navs_subdir_with_title_parsing_real_page():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def test_generate_page_navs_subdir_with_no_index():
 | 
					def test_generate_page_navs_subdir_with_no_index():
 | 
				
			||||||
    """Test that breadcrumbs still generate even if a subdir doesn't have an index.md."""
 | 
					    """Test that breadcrumbs still generate even if a subdir doesn't have an index.md."""
 | 
				
			||||||
    assert generate_parent_navs('no-index-dir/page.md') == [
 | 
					    assert generate_parent_navs('no-index-dir/page.md', PAGES_DIR) == [
 | 
				
			||||||
        ('example.org', '/'),
 | 
					        ('example.org', '/'),
 | 
				
			||||||
        ('/no-index-dir/', '/no-index-dir/'),
 | 
					        ('/no-index-dir/', '/no-index-dir/'),
 | 
				
			||||||
        ('page', '/no-index-dir/page')
 | 
					        ('page', '/no-index-dir/page')
 | 
				
			||||||
@ -53,26 +58,26 @@ def test_generate_page_navs_subdir_with_no_index():
 | 
				
			|||||||
def test_page_includes_themes_with_default():
 | 
					def test_page_includes_themes_with_default():
 | 
				
			||||||
    """Test that a request contains the configured themes and sets the default as appropriate."""
 | 
					    """Test that a request contains the configured themes and sets the default as appropriate."""
 | 
				
			||||||
    assert '<link rel="stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
 | 
					    assert '<link rel="stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
 | 
				
			||||||
        in handle_markdown_file_path('index.md')
 | 
					        in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
    assert '<link rel="alternate stylesheet" type="text/css" title="dark" href="/static/css/dark.css">'\
 | 
					    assert '<link rel="alternate stylesheet" type="text/css" title="dark" href="/static/css/dark.css">'\
 | 
				
			||||||
        in handle_markdown_file_path('index.md')
 | 
					        in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
    assert '<a href="" onclick="setStyle(\'light\'); return false;">[light]</a>'\
 | 
					    assert '<a href="" onclick="setStyle(\'light\'); return false;">[light]</a>'\
 | 
				
			||||||
        in handle_markdown_file_path('index.md')
 | 
					        in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
    assert '<a href="" onclick="setStyle(\'dark\'); return false;">[dark]</a>'\
 | 
					    assert '<a href="" onclick="setStyle(\'dark\'); return false;">[dark]</a>'\
 | 
				
			||||||
        in handle_markdown_file_path('index.md')
 | 
					        in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_render_with_style_overrides():
 | 
					def test_render_with_style_overrides():
 | 
				
			||||||
    """Test that the default can be changed."""
 | 
					    """Test that the default can be changed."""
 | 
				
			||||||
    with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'dark'):
 | 
					    with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'dark'):
 | 
				
			||||||
        assert '<link rel="stylesheet" type="text/css" title="dark" href="/static/css/dark.css">'\
 | 
					        assert '<link rel="stylesheet" type="text/css" title="dark" href="/static/css/dark.css">'\
 | 
				
			||||||
            in handle_markdown_file_path('index.md')
 | 
					            in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
        assert '<link rel="alternate stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
 | 
					        assert '<link rel="alternate stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
 | 
				
			||||||
            in handle_markdown_file_path('index.md')
 | 
					            in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
    assert '<a href="" onclick="setStyle(\'light\'); return false;">[light]</a>'\
 | 
					    assert '<a href="" onclick="setStyle(\'light\'); return false;">[light]</a>'\
 | 
				
			||||||
        in handle_markdown_file_path('index.md')
 | 
					        in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
    assert '<a href="" onclick="setStyle(\'dark\'); return false;">[dark]</a>'\
 | 
					    assert '<a href="" onclick="setStyle(\'dark\'); return false;">[dark]</a>'\
 | 
				
			||||||
        in handle_markdown_file_path('index.md')
 | 
					        in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_render_with_default_style_override():
 | 
					def test_render_with_default_style_override():
 | 
				
			||||||
@ -81,23 +86,21 @@ def test_render_with_default_style_override():
 | 
				
			|||||||
                                                     'warm': '/static/css/warm.css'}):
 | 
					                                                     'warm': '/static/css/warm.css'}):
 | 
				
			||||||
        with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'warm'):
 | 
					        with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'warm'):
 | 
				
			||||||
            assert '<link rel="stylesheet" type="text/css" title="warm" href="/static/css/warm.css">'\
 | 
					            assert '<link rel="stylesheet" type="text/css" title="warm" href="/static/css/warm.css">'\
 | 
				
			||||||
                in handle_markdown_file_path('index.md')
 | 
					                in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
            assert '<link rel="alternate stylesheet" type="text/css" title="cool" href="/static/css/cool.css">'\
 | 
					            assert '<link rel="alternate stylesheet" type="text/css" title="cool" href="/static/css/cool.css">'\
 | 
				
			||||||
                in handle_markdown_file_path('index.md')
 | 
					                in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
            assert '<link rel="alternate stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
 | 
					            assert '<link rel="alternate stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
 | 
				
			||||||
                not in handle_markdown_file_path('index.md')
 | 
					                not in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
            assert '<a href="" onclick="setStyle(\'warm\'); return false;">[warm]</a>'\
 | 
					            assert '<a href="" onclick="setStyle(\'warm\'); return false;">[warm]</a>'\
 | 
				
			||||||
                in handle_markdown_file_path('index.md')
 | 
					                in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
            assert '<a href="" onclick="setStyle(\'cool\'); return false;">[cool]</a>'\
 | 
					            assert '<a href="" onclick="setStyle(\'cool\'); return false;">[cool]</a>'\
 | 
				
			||||||
                in handle_markdown_file_path('index.md')
 | 
					                in handle_markdown_file_path('index.md', PAGES_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_redirects_error_unsupported():
 | 
					def test_redirects_error_unsupported():
 | 
				
			||||||
    """Test that we throw a warning about the barely-used Markdown redirect tag, which we can't support via SSG."""
 | 
					    """Test that we throw a warning about the barely-used Markdown redirect tag, which we can't support via SSG."""
 | 
				
			||||||
    os.chdir(os.path.join(HERE, 'instance/', 'broken/'))
 | 
					 | 
				
			||||||
    with pytest.raises(NotImplementedError):
 | 
					    with pytest.raises(NotImplementedError):
 | 
				
			||||||
        handle_markdown_file_path('redirect.md')
 | 
					        handle_markdown_file_path('redirect.md', os.path.join(INSTANCE_DIR, 'broken'))
 | 
				
			||||||
    os.chdir(os.path.join(HERE, 'instance/', 'pages/'))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_instance_resource_path_to_request_path_on_index():
 | 
					def test_instance_resource_path_to_request_path_on_index():
 | 
				
			||||||
@ -131,21 +134,21 @@ def test_request_path_to_breadcrumb_display_patterns():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def test_parse_md_metadata():
 | 
					def test_parse_md_metadata():
 | 
				
			||||||
    """Test the direct results of parsing a markdown file."""
 | 
					    """Test the direct results of parsing a markdown file."""
 | 
				
			||||||
    content, md, page_name, page_title, mtime = parse_md('more-metadata.md')
 | 
					    content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'more-metadata.md'), PAGES_DIR)
 | 
				
			||||||
    assert page_name == 'title for the page'
 | 
					    assert page_name == 'title for the page'
 | 
				
			||||||
    assert page_title == 'title for the page - example.org'
 | 
					    assert page_title == 'title for the page - example.org'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_parse_md_metadata_forced_no_title():
 | 
					def test_parse_md_metadata_forced_no_title():
 | 
				
			||||||
    """Test the direct results of parsing a markdown file."""
 | 
					    """Test the direct results of parsing a markdown file."""
 | 
				
			||||||
    content, md, page_name, page_title, mtime = parse_md('forced-no-title.md')
 | 
					    content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'forced-no-title.md'), PAGES_DIR)
 | 
				
			||||||
    assert page_name == ''
 | 
					    assert page_name == ''
 | 
				
			||||||
    assert page_title == 'example.org'
 | 
					    assert page_title == 'example.org'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_parse_md_metadata_no_title_so_path():
 | 
					def test_parse_md_metadata_no_title_so_path():
 | 
				
			||||||
    """Test the direct results of parsing a markdown file."""
 | 
					    """Test the direct results of parsing a markdown file."""
 | 
				
			||||||
    content, md, page_name, page_title, mtime = parse_md('subdir/index.md')
 | 
					    content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'subdir/index.md'), PAGES_DIR)
 | 
				
			||||||
    assert page_name == '/subdir/'
 | 
					    assert page_name == '/subdir/'
 | 
				
			||||||
    assert page_title == '/subdir/ - example.org'
 | 
					    assert page_title == '/subdir/ - example.org'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -153,10 +156,10 @@ def test_parse_md_metadata_no_title_so_path():
 | 
				
			|||||||
def test_parse_md_no_file():
 | 
					def test_parse_md_no_file():
 | 
				
			||||||
    """Test the direct results of parsing a markdown file."""
 | 
					    """Test the direct results of parsing a markdown file."""
 | 
				
			||||||
    with pytest.raises(FileNotFoundError):
 | 
					    with pytest.raises(FileNotFoundError):
 | 
				
			||||||
        content, md, page_name, page_title, mtime = parse_md('nope.md')
 | 
					        content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'nope.md'), PAGES_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_parse_md_bad_file():
 | 
					def test_parse_md_bad_file():
 | 
				
			||||||
    """Test the direct results of parsing a markdown file."""
 | 
					    """Test the direct results of parsing a markdown file."""
 | 
				
			||||||
    with pytest.raises(ValueError):
 | 
					    with pytest.raises(ValueError):
 | 
				
			||||||
        content, md, page_name, page_title, mtime = parse_md('actually-a-png.md')
 | 
					        content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'actually-a-png.md'), PAGES_DIR)
 | 
				
			||||||
 | 
				
			|||||||
@ -3,12 +3,15 @@
 | 
				
			|||||||
SPDX-FileCopyrightText: © 2025 Brian S. Stephan <bss@incorporeal.org>
 | 
					SPDX-FileCopyrightText: © 2025 Brian S. Stephan <bss@incorporeal.org>
 | 
				
			||||||
SPDX-License-Identifier: GPL-3.0-or-later
 | 
					SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import tempfile
 | 
					import tempfile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import incorporealcms.ssg as ssg
 | 
					import incorporealcms.ssg as ssg
 | 
				
			||||||
from incorporealcms import init_instance
 | 
					from incorporealcms import init_instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger(__file__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HERE = os.path.dirname(os.path.abspath(__file__))
 | 
					HERE = os.path.dirname(os.path.abspath(__file__))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
instance_dir = os.path.join(HERE, 'instance')
 | 
					instance_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
@ -20,7 +23,6 @@ def test_file_copy():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
					        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(instance_dir, 'pages'))
 | 
					 | 
				
			||||||
        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'no-title.md', tmpdir,
 | 
					        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'no-title.md', tmpdir,
 | 
				
			||||||
                                            True)
 | 
					                                            True)
 | 
				
			||||||
        assert os.path.exists(os.path.join(tmpdir, 'no-title.md'))
 | 
					        assert os.path.exists(os.path.join(tmpdir, 'no-title.md'))
 | 
				
			||||||
@ -32,7 +34,6 @@ def test_file_copy_no_markdown():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
					        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(instance_dir, 'pages'))
 | 
					 | 
				
			||||||
        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'no-title.md', tmpdir,
 | 
					        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'no-title.md', tmpdir,
 | 
				
			||||||
                                            False)
 | 
					                                            False)
 | 
				
			||||||
        assert os.path.exists(os.path.join(tmpdir, 'no-title.md'))
 | 
					        assert os.path.exists(os.path.join(tmpdir, 'no-title.md'))
 | 
				
			||||||
@ -44,7 +45,6 @@ def test_file_copy_symlink():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
					        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(instance_dir, 'pages'))
 | 
					 | 
				
			||||||
        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-foo.txt', tmpdir)
 | 
					        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-foo.txt', tmpdir)
 | 
				
			||||||
        # need to copy the destination for os.path.exists to be happy with this
 | 
					        # 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'), '', 'foo.txt', tmpdir)
 | 
					        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'foo.txt', tmpdir)
 | 
				
			||||||
@ -52,12 +52,28 @@ def test_file_copy_symlink():
 | 
				
			|||||||
        assert os.path.islink(os.path.join(tmpdir, 'symlink-to-foo.txt'))
 | 
					        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():
 | 
					def test_file_copy_symlink_of_markdown_also_has_html_symlink():
 | 
				
			||||||
    """Test the ability to sync source and generated symlinks to the output dir."""
 | 
					    """Test the ability to sync source and generated symlinks to the output dir."""
 | 
				
			||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
					        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(instance_dir, 'pages'))
 | 
					 | 
				
			||||||
        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-no-title.md', tmpdir,
 | 
					        generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-no-title.md', tmpdir,
 | 
				
			||||||
                                            True)
 | 
					                                            True)
 | 
				
			||||||
        # need to copy the destination for os.path.exists to be happy with this
 | 
					        # need to copy the destination for os.path.exists to be happy with this
 | 
				
			||||||
@ -74,7 +90,6 @@ def test_dir_copy():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
					        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(instance_dir, 'pages'))
 | 
					 | 
				
			||||||
        generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'media', tmpdir)
 | 
					        generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'media', tmpdir)
 | 
				
			||||||
        assert os.path.exists(os.path.join(tmpdir, 'media'))
 | 
					        assert os.path.exists(os.path.join(tmpdir, 'media'))
 | 
				
			||||||
        assert os.path.isdir(os.path.join(tmpdir, 'media'))
 | 
					        assert os.path.isdir(os.path.join(tmpdir, 'media'))
 | 
				
			||||||
@ -85,7 +100,6 @@ def test_dir_copy_symlink():
 | 
				
			|||||||
    with tempfile.TemporaryDirectory() as tmpdir:
 | 
					    with tempfile.TemporaryDirectory() as tmpdir:
 | 
				
			||||||
        src_dir = os.path.join(HERE, 'instance')
 | 
					        src_dir = os.path.join(HERE, 'instance')
 | 
				
			||||||
        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
					        generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
 | 
				
			||||||
        os.chdir(os.path.join(instance_dir, 'pages'))
 | 
					 | 
				
			||||||
        generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-subdir', tmpdir)
 | 
					        generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-subdir', tmpdir)
 | 
				
			||||||
        # need to copy the destination for os.path.exists to be happy with this
 | 
					        # need to copy the destination for os.path.exists to be happy with this
 | 
				
			||||||
        generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'subdir', tmpdir)
 | 
					        generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'subdir', tmpdir)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user