diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 02662626..6c528320 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -34,6 +34,8 @@ jobs: run: | python -m pip install --upgrade pip pip install -r src/scripts/requirements.txt + python src/scripts/make_requirements_hooks_file.py + pip install -r src/scripts/requirements_hooks.txt - name: Run python build script run: | python src/scripts/build_site.py --delete-scheduled-posts diff --git a/src/scripts/hooks.py b/src/extras/hooks.py similarity index 60% rename from src/scripts/hooks.py rename to src/extras/hooks.py index 8706924d..f8cc2e9a 100644 --- a/src/scripts/hooks.py +++ b/src/extras/hooks.py @@ -1,6 +1,17 @@ import utils +def preprocess(comic_info): + """ + Runs immediately after the main comic's comic_info.ini file is loaded. Can be used to do any miscellaneous setup + you might want before the comic starts to build. + + :param comic_info: The main comic's comic_info.ini file parsed into a RawConfigParser object. + :return: None + """ + pass + + def extra_global_values(comic_folder, comic_info, comic_data_dicts): """ Returns a dictionary of values that will be added to the global values sent to all templates when they're built. @@ -8,9 +19,9 @@ def extra_global_values(comic_folder, comic_info, comic_data_dicts): :param comic_folder: If the main comic is being built, this will be blank. Otherwise, it's the name of the extra comic that's currently being built. Use this value if you want to return different global values depending on what comic is being built. - :param comic_info: The comic_info.ini file parsed into a RawConfigParser object. + :param comic_info: The current comic's comic_info.ini file parsed into a RawConfigParser object. :param comic_data_dicts: List of comic data dicts that were built during the previous processing steps. - :return: + :return: dict of additional global template variables """ return {} @@ -23,12 +34,22 @@ def build_other_pages(comic_folder, comic_info, comic_data_dicts): :param comic_folder: If the main comic is being built, this will be blank. Otherwise, it's the name of the extra comic that's currently being built. Use this value if you want to return different global values depending on what comic is being built. - :param comic_info: The comic_info.ini file parsed into a RawConfigParser object. + :param comic_info: The current comic's comic_info.ini file parsed into a RawConfigParser object. :param comic_data_dicts: List of comic data dicts that were built during the previous processing steps. Each data dict also contains the global values passed in to all templates when they're built. - :return: + :return: None """ # You can use comic_data_dicts[-1] to pass the last comic_data_dict to the template so it can have access to all - # the global template variables. + # the global template variables, as well as the information of the most recent comic page. # utils.write_to_template("infinite_scroll", "path/to/html/index.html", comic_data_dicts[-1]) + + +def postprocess(comic_info): + """ + Runs at the very end of the comic_git build process. Can be used to do any miscellaneous cleanup you might need. + + :param comic_info: The main comic's comic_info.ini file parsed into a RawConfigParser object. + :return: None + """ + pass diff --git a/src/scripts/build_site.py b/src/scripts/build_site.py index e8d7593c..df8b18bb 100644 --- a/src/scripts/build_site.py +++ b/src/scripts/build_site.py @@ -3,6 +3,7 @@ import os import re import shutil +import sys from collections import OrderedDict, defaultdict from configparser import RawConfigParser from copy import deepcopy @@ -39,15 +40,6 @@ def web_path(rel_path: str): return rel_path -def find_project_root(): - while not os.path.exists("your_content"): - last_cwd = os.getcwd() - os.chdir("..") - if os.getcwd() == last_cwd: - raise FileNotFoundError("Couldn't find a folder in the path matching 'your_content'. Make sure you're " - "running this script from within the comic_git repository.") - - def delete_output_file_space(comic_info: RawConfigParser = None): shutil.rmtree("comic", ignore_errors=True) if os.path.isfile("feed.xml"): @@ -138,6 +130,10 @@ def run_hook(theme: str, func: str, args: List[Any]) -> Any: :return: The return value of the function called, if one was found. Otherwise, None. """ if os.path.exists(f"your_content/themes/{theme}/scripts/hooks.py"): + current_path = os.path.abspath(".") + if current_path not in sys.path: + sys.path.append(current_path) + print(f"Path updated: {sys.path}") hooks = import_module(f"your_content.themes.{theme}.scripts.hooks") if hasattr(hooks, func): method = getattr(hooks, func) @@ -515,12 +511,17 @@ def main(delete_scheduled_posts=False, publish_all_comics=False): processing_times = [("Start", time())] # Get site-wide settings for this comic - find_project_root() + utils.find_project_root() comic_info = read_info("your_content/comic_info.ini") comic_url, BASE_DIRECTORY = utils.get_comic_url(comic_info) + theme = get_option(comic_info, "Comic Settings", "Theme", default="default") processing_times.append(("Get comic settings", time())) + run_hook(theme, "preprocess", [comic_info]) + + processing_times.append(("Preprocessing hook", time())) + # Setup output file space setup_output_file_space(comic_info) processing_times.append(("Setup output file space", time())) @@ -545,6 +546,10 @@ def main(delete_scheduled_posts=False, publish_all_comics=False): processing_times ) + run_hook(theme, "postprocess", [comic_info]) + + processing_times.append(("Postprocessing hook", time())) + print_processing_times(processing_times) diff --git a/src/scripts/delete_autogenerated_files.py b/src/scripts/delete_autogenerated_files.py index ecd31259..1d07a108 100644 --- a/src/scripts/delete_autogenerated_files.py +++ b/src/scripts/delete_autogenerated_files.py @@ -1,4 +1,5 @@ -from src.scripts.build_site import find_project_root, delete_output_file_space +from src.scripts.build_site import delete_output_file_space +from utils import find_project_root find_project_root() delete_output_file_space() diff --git a/src/scripts/make_requirements_hooks_file.py b/src/scripts/make_requirements_hooks_file.py new file mode 100644 index 00000000..97f8735b --- /dev/null +++ b/src/scripts/make_requirements_hooks_file.py @@ -0,0 +1,35 @@ +import os +from typing import Set + +from build_site import read_info, get_extra_comics_list, get_extra_comic_info, get_option +from utils import find_project_root, str_to_list + + +def get_requirements(theme: str) -> Set[str]: + requirements_path = f"your_content/themes/{theme}/scripts/requirements.txt" + if os.path.exists(requirements_path): + with open(requirements_path) as f: + return set(str_to_list(f.read().replace("\r", ""), delimiter="\n")) + return set() + + +def main(): + find_project_root() + comic_info = read_info("your_content/comic_info.ini") + theme = get_option(comic_info, "Comic Settings", "Theme", default="default") + requirements = get_requirements(theme) + print(requirements) + # Build any extra comics that may be needed + for extra_comic in get_extra_comics_list(comic_info): + print(extra_comic) + extra_comic_info = get_extra_comic_info(extra_comic, comic_info) + theme = get_option(extra_comic_info, "Comic Settings", "Theme") + if theme: + requirements.update(get_requirements(theme)) + print(requirements) + with open("src/scripts/requirements_hooks.txt", "w") as f: + f.write("\n".join(requirements)) + + +if __name__ == "__main__": + main() diff --git a/src/scripts/utils.py b/src/scripts/utils.py index f0dee5ea..b9906094 100644 --- a/src/scripts/utils.py +++ b/src/scripts/utils.py @@ -50,6 +50,15 @@ def str_to_list(s: str, delimiter: str=",") -> List[str]: return [item.strip(" ") for item in s.strip(delimiter + " ").split(delimiter)] +def find_project_root(): + while not os.path.exists("your_content"): + last_cwd = os.getcwd() + os.chdir("..") + if os.getcwd() == last_cwd: + raise FileNotFoundError("Couldn't find a folder in the path matching 'your_content'. Make sure you're " + "running this script from within the comic_git repository.") + + def write_to_template(template_name: str, html_path: str, data_dict: Dict=None) -> None: """ Searches for either an HTML or a TPL file named in first the "templates" folder of your @@ -63,6 +72,8 @@ def write_to_template(template_name: str, html_path: str, data_dict: Dict=None) :param data_dict: The dictionary of values to pass to the template when it's rendered. :return: None """ + if jinja_environment is None: + raise RuntimeError("Jinja environment was not initialized before write_to_template was called.") try: file_contents = jinja_environment.get_template(template_name + ".html").render() except TemplateNotFound: