remove os.chdir usage, rely on absolute and relative paths more
os.chdir was getting confusing and hurting the log output, and potentially the cause of a couple bugs left to fix, so this removes it, but it means we need to pass around the pages/ absolute path into the markdown parser, because it relies on knowing both the absolute path now (to open files), and also the path relative to the pages dir in order to know where to stop reading parent files/how to generate proper URL-like references to other files. probably this should be refactored at some point to inherit the pages/ path from the SSG somehow, rather than passing it through a bunch of methods, but this seems to work for now fixes #22 Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
This commit is contained in:
@@ -24,6 +24,9 @@ logger = logging.getLogger(__name__)
|
||||
def generate_feed(feed_type: str, instance_dir: str, dest_dir: str) -> None:
|
||||
"""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:
|
||||
feed_type: 'atom' or 'rss' feed
|
||||
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.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
|
||||
feed_path = os.path.join(instance_dir, 'feed')
|
||||
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 feed_entry_path in sorted(feed_entry_paths):
|
||||
# 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), os.path.join(instance_dir, 'pages'))
|
||||
resolved_path = os.path.relpath(os.path.realpath(feed_entry_path), pages_dir)
|
||||
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)}'
|
||||
except (OSError, ValueError, TypeError):
|
||||
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))
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
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:
|
||||
logger.debug("opening path '%s'", path)
|
||||
with open(path, 'r') as input_file:
|
||||
absolute_path = os.path.join(pages_root, path)
|
||||
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)
|
||||
entry = input_file.read()
|
||||
logger.debug("path '%s' read", path)
|
||||
logger.debug("path '%s' read", absolute_path)
|
||||
md = init_md()
|
||||
content = Markup(md.convert(entry)) # nosec B704
|
||||
except (OSError, FileNotFoundError):
|
||||
@@ -67,17 +70,25 @@ def parse_md(path: str):
|
||||
|
||||
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
|
||||
logger.debug("title (potentially derived): %s", page_title)
|
||||
|
||||
return content, md, page_name, page_title, mtime
|
||||
|
||||
|
||||
def handle_markdown_file_path(path: str) -> str:
|
||||
"""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)
|
||||
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.
|
||||
|
||||
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
|
||||
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'),
|
||||
image=Config.BASE_HOST + get_meta_str(md, 'image'),
|
||||
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,
|
||||
mtime=mtime.strftime('%Y-%m-%d %H:%M:%S %Z'),
|
||||
extra_footer=extra_footer)
|
||||
|
||||
|
||||
def generate_parent_navs(path):
|
||||
"""Create a series of paths/links to navigate up from the given resource path."""
|
||||
def generate_parent_navs(path, pages_root: str):
|
||||
"""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':
|
||||
# bail and return the domain name as a terminal case
|
||||
return [(Config.DOMAIN_NAME, '/')]
|
||||
@@ -124,14 +142,14 @@ def generate_parent_navs(path):
|
||||
|
||||
# read the resource
|
||||
try:
|
||||
with open(path, 'r') as entry_file:
|
||||
with open(os.path.join(pages_root, path), 'r') as entry_file:
|
||||
entry = entry_file.read()
|
||||
_ = Markup(md.convert(entry)) # nosec B704
|
||||
page_name = (" ".join(md.Meta.get('title')) if md.Meta.get('title')
|
||||
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:
|
||||
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):
|
||||
|
||||
@@ -86,7 +86,6 @@ class StaticSiteGenerator(object):
|
||||
convert_markdown: whether or not to convert Markdown files (or simply copy them)
|
||||
"""
|
||||
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):
|
||||
logger.debug("starting to build against %s || %s || %s", base_dir, subdirs, files)
|
||||
# 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
|
||||
"""
|
||||
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
|
||||
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}'")
|
||||
os.symlink(src, dst, target_is_directory=True)
|
||||
else:
|
||||
@@ -137,9 +139,12 @@ class StaticSiteGenerator(object):
|
||||
dest_dir: the output directory to place the subdir in
|
||||
"""
|
||||
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
|
||||
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}'")
|
||||
os.symlink(src, dst, target_is_directory=False)
|
||||
if src.endswith('.md') and convert_markdown:
|
||||
@@ -151,7 +156,7 @@ class StaticSiteGenerator(object):
|
||||
os.symlink(second_src, second_dst, target_is_directory=False)
|
||||
|
||||
else:
|
||||
src = os.path.join(base_dir, file_)
|
||||
src = os.path.join(source_dir, base_dir, file_)
|
||||
print(f"copying file '{src}' -> '{dst}'")
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
@@ -160,7 +165,7 @@ class StaticSiteGenerator(object):
|
||||
rendered_file = dst.removesuffix('.md') + '.html'
|
||||
print(f"rendering file '{src}' -> '{rendered_file}'")
|
||||
try:
|
||||
content = handle_markdown_file_path(src)
|
||||
content = handle_markdown_file_path(src, source_dir)
|
||||
except UnicodeDecodeError:
|
||||
# perhaps this isn't a markdown file at all for some reason; we
|
||||
# copied it above so stick with tha
|
||||
|
||||
Reference in New Issue
Block a user