From 2d3cf524178e49863a2442a79f5b1b95bf4ddf43 Mon Sep 17 00:00:00 2001 From: Konstantinos Lampridis Date: Wed, 14 May 2025 23:17:13 +0300 Subject: [PATCH 1/7] chore: remove comment from gen_api script --- scripts/gen_api_refs_pages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gen_api_refs_pages.py b/scripts/gen_api_refs_pages.py index e1b16be2..1aa3d602 100644 --- a/scripts/gen_api_refs_pages.py +++ b/scripts/gen_api_refs_pages.py @@ -22,6 +22,7 @@ for path in sorted( x for x in src.rglob("*.py") if '{{ cookiecutter.project_slug }}' not in x.parts ): + # print(f"Processing {path}") ## 1. extract Relative path from Python File and remove suffix (.py) # EG src/biskotaki/cli.py --> biskotaki/cli _module_path = path.relative_to(src).with_suffix("") @@ -35,7 +36,6 @@ # Skip __main__ Files if parts[-1] == "__main__": - print(f"Skip Gen API Refs for file: {path}") continue # For __init__ Files dedicate an index.md file, instead of __init__.md From efb92a17b499af172a8c021804824a263c4f8ef8 Mon Sep 17 00:00:00 2001 From: Konstantinos Lampridis Date: Wed, 14 May 2025 23:19:30 +0300 Subject: [PATCH 2/7] docs: retire section-index plugin in favor of material built-in ! --- mkdocs.yml | 10 ++++++++-- pyproject.toml | 1 - uv.lock | 14 -------------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 66dadc81..f777f589 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,14 +18,20 @@ theme: default: material/tag features: + # render a breadcrumb navigation above the title of each page, which might make orientation easier for users visiting your documentation on devices with smaller screens. - navigation.path - - navigation.top + - navigation.top # back-to-top button shown when user, after scrolling down, starts to scroll up again. - navigation.footer # enables switching multiple tabs with the same name - content.tabs.link # renders copy button on code blocks - content.code.copy + - navigation.instant + # URL in the address bar is automatically updated with the active anchor as highlighted in the table of contents (right sidebar) + - navigation.tracking + - navigation.indexes + plugins: # Enable jinja inside your markdown files # - allows {% include %} statements @@ -58,7 +64,7 @@ plugins: # programmatically generate Nav Item: ie 'reference/' entry in Navigation - literate-nav: nav_file: SUMMARY.md - - section-index + # - section-index # Enable displaying at the bottom of pages the last date of modification! # uv add --optional docs 'mkdocs-git-revision-date-localized-plugin' diff --git a/pyproject.toml b/pyproject.toml index d92ebd8a..ccd4efae 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,7 +126,6 @@ docs = [ "mkdocs-macros-plugin>=1.3.7, <2.0.0", "mkdocs-material>=9.6.12, <10.0.0", "mkdocs-mermaid2-plugin>=1.2.1, <2.0.0", - "mkdocs-section-index==0.3.9", # mkdocstrings 0.26.1 is the last to support python3.8! "mkdocstrings==0.26.1", "mkdocstrings-python==1.11.1", diff --git a/uv.lock b/uv.lock index 02bf5d5a..7e49097d 100644 --- a/uv.lock +++ b/uv.lock @@ -319,7 +319,6 @@ docs = [ { name = "mkdocs-macros-plugin" }, { name = "mkdocs-material" }, { name = "mkdocs-mermaid2-plugin" }, - { name = "mkdocs-section-index" }, { name = "mkdocstrings" }, { name = "mkdocstrings-python" }, { name = "pymdown-extensions" }, @@ -371,7 +370,6 @@ requires-dist = [ { name = "mkdocs-macros-plugin", marker = "extra == 'docs'", specifier = ">=1.3.7,<2.0.0" }, { name = "mkdocs-material", marker = "extra == 'docs'", specifier = ">=9.6.12,<10.0.0" }, { name = "mkdocs-mermaid2-plugin", marker = "extra == 'docs'", specifier = ">=1.2.1,<2.0.0" }, - { name = "mkdocs-section-index", marker = "extra == 'docs'", specifier = "==0.3.9" }, { name = "mkdocstrings", marker = "extra == 'docs'", specifier = "==0.26.1" }, { name = "mkdocstrings-python", marker = "extra == 'docs'", specifier = "==1.11.1" }, { name = "mypy", marker = "extra == 'typing'", specifier = "==1.2.0" }, @@ -1107,18 +1105,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/24/ce/c8a41cb0f3044990c8afbdc20c853845a9e940995d4e0cffecafbb5e927b/mkdocs_mermaid2_plugin-1.2.1-py3-none-any.whl", hash = "sha256:22d2cf2c6867d4959a5e0903da2dde78d74581fc0b107b791bc4c7ceb9ce9741", size = 17260 }, ] -[[package]] -name = "mkdocs-section-index" -version = "0.3.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mkdocs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/86/09/3cfcfec56740fba157991cd098c76dd08ef9c211db292c7c7d820d16c351/mkdocs_section_index-0.3.9.tar.gz", hash = "sha256:b66128d19108beceb08b226ee1ba0981840d14baf8a652b6c59e650f3f92e4f8", size = 13941 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/19/16f6368f69949ea2d0086197a86beda4d4f27f09bb8c59130922f03d335d/mkdocs_section_index-0.3.9-py3-none-any.whl", hash = "sha256:5e5eb288e8d7984d36c11ead5533f376fdf23498f44e903929d72845b24dfe34", size = 8728 }, -] - [[package]] name = "mkdocstrings" version = "0.26.1" From 766744436d1f9bcb9bdc45b85ab07996b1c448aa Mon Sep 17 00:00:00 2001 From: Konstantinos Lampridis Date: Wed, 14 May 2025 23:41:32 +0300 Subject: [PATCH 3/7] feat: generate better Mkdocs documentation Now, when the Generator is called with 'mkdocs' as Docs Builder, the out-of-the-box markdown files generated in the docs folder are much richer in content and better in structure (also mkdocs.yml) is updated. --- .../mkdocs.yml | 109 ++++++++++++++++-- .../assets/docker_off.png" | Bin 33086 -> 0 bytes .../build-process_DAG.md" | 17 --- .../cicd.md" | 14 --- .../cicd_mermaid.md" | 24 ---- .../dev_guides/docker.md" | 40 ------- .../dev_guides/index.md" | 38 ------ .../index.md" | 66 +++++++++++ .../topics/arch.md" | 51 ++++++++ .../topics/development/build_process_DAG.md" | 20 ++++ .../topics/development/cicd.md" | 28 +++++ .../topics/development/cicd_mermaid.md" | 0 .../topics/development/dockerfile_mermaid.md" | 0 .../topics/development/index.md" | 35 ++++++ .../docs/assets/docker_off.png | Bin 33086 -> 0 bytes .../docs/build-process_DAG.md | 17 --- .../biskotaki-gold-standard/docs/cicd.md | 14 --- .../docs/dev_guides/docker.md | 40 ------- .../docs/dev_guides/index.md | 38 ------ .../biskotaki-gold-standard/docs/index.md | 66 +++++++++++ .../docs/topics/arch.md | 51 ++++++++ .../topics/development/build_process_DAG.md | 20 ++++ .../docs/topics/development/cicd.md | 28 +++++ .../docs/topics/development/cicd_mermaid.md | 12 ++ .../development}/dockerfile_mermaid.md | 0 .../docs/topics/development/index.md | 35 ++++++ .../biskotaki-gold-standard/mkdocs.yml | 109 ++++++++++++++++-- tests/test_gold_standard.py | 17 +-- 28 files changed, 624 insertions(+), 265 deletions(-) delete mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/assets/docker_off.png" delete mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/build-process_DAG.md" delete mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd.md" delete mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd_mermaid.md" delete mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/docker.md" delete mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/index.md" create mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/arch.md" create mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/build_process_DAG.md" create mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd.md" rename tests/data/snapshots/biskotaki-gold-standard/docs/cicd_mermaid.md => "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd_mermaid.md" (100%) rename "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dockerfile_mermaid.md" => "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/dockerfile_mermaid.md" (100%) create mode 100644 "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/index.md" delete mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/assets/docker_off.png delete mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/build-process_DAG.md delete mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/cicd.md delete mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/docker.md delete mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/index.md create mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/topics/arch.md create mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/build_process_DAG.md create mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd.md create mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd_mermaid.md rename tests/data/snapshots/biskotaki-gold-standard/docs/{ => topics/development}/dockerfile_mermaid.md (100%) create mode 100644 tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/index.md diff --git a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/mkdocs.yml b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/mkdocs.yml index 69a5a609..f9311b1a 100644 --- a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/mkdocs.yml +++ b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/mkdocs.yml @@ -17,25 +17,43 @@ theme: default: material/tag features: + # render a breadcrumb navigation above the title of each page, which might make orientation easier for users visiting your documentation on devices with smaller screens. - navigation.path - - navigation.top + - navigation.top # back-to-top button shown when user, after scrolling down, starts to scroll - navigation.footer + # enables switching multiple tabs with the same name + - content.tabs.link + # renders copy button on code blocks + - content.code.copy + # make index.md landing page to collapsible sections (no need for sections-index 3rd-party plugin) + - navigation.indexes plugins: # Enable jinja inside your markdown files + # - allows {% raw %}{% include %}{% endraw %} statements + # - requires careful escape of double-braces in markdown + # - requires wrapping content between {% raw %}{%{% endraw %} raw {% raw %}%}{% endraw %} and {% raw %}{%{% endraw %} endraw {% raw %}%}{% endraw %} # https://github.com/fralau/mkdocs_macros_plugin - macros + # include_dir: docs/includes + # Authors need installation # - git-authors + + # Automated Tags - Page Generation - tags: tags_file: tags.md + # BASIC SEARCH PLUGIN - search + # MERMAID Render Support - mermaid2 + # Directives Provider for docstrings parsing - mkdocstrings - # gain the ability to tap-in to the build process + + # Tap-in to the build process and generate files with mkdocstrings directives - gen-files: # scripts allowing to programmatically generate .md content scripts: @@ -44,19 +62,94 @@ plugins: # programmatically generate Nav Item: ie 'reference/' entry in Navigation - literate-nav: nav_file: SUMMARY.md - - section-index markdown_extensions: - mkdocs-click - pymdownx.highlight - + + ### ADMONITIONS ### + # enabled block kelements such as example, note, info, warning, tip, etc + # https://squidfunk.github.io/mkdocs-material/reference/admonitions/ + - admonition + + ### SUPERFENCES ### + # arbitrary nesting of code and content blocks inside each other, + # including admonitions, tabs, lists and all other elements + # https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#superfences + # this custom fence defined below with name 'mermaid' is required + # to prevent superfences from breaking mermaid default syntax (3 backticks)! + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + + ### TABBED ### + # usage of content tabs, a simple way to group related content and + # code blocks under accessible tabs. + # https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#tabbed + - pymdownx.tabbed: + alternate_style: true + + ### GRIDS with Grid Cards or Generic Cards ### + - attr_list + - md_in_html + + ### ICONS/EMOJIS ### + # https://squidfunk.github.io/mkdocs-material/reference/icons-emojis/#with-colors-docsstylesheetsextracss + # The following icon sets are bundled with Material for MkDocs: + # – Material Design: https://pictogrammers.com/library/mdi/ + # – FontAwesome : https://fontawesome.com/search?ic=free + # – Octicons : https://primer.style/octicons/ + # – Simple Icons : https://simpleicons.org/ + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + + ### On-HOVER TOOLTIP ### + # abbr by default supports tooltips on url links + - abbr + # if attr_list is also added, then tooltips on other html elements are supported as below: + # ie :material-information-outline:{ title="Important information" } + + # The Details extension supercharges the Admonition extension, making the resulting call-outs collapsible, allowing them to be opened and closed by the user. + # https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#details + # example: ??? blahblah + # expanded example: ???+ blahblah + # if element is insise non-trivial content wrap in div with class markdown + - pymdownx.details + +###### SITE NAVIGATION ###### nav: - - Home: - - "Quick Start": index.md + ## Landing Page ## + - Home: index.md + + ## Motivation ## + # - Motivation: topics/why_this_package.md + + ## TUTORIALS ## + # - Tutorials: + # Landing Page + # - "tutorials/index.md" + + ## REFERENCES ## - API References: reference/ + + ## TOPICS/EXPLANATIONS ## - Topics: - - "Docker": build-process_DAG.md - - "CI/CD": cicd.md + + # Project Architecture + - "Architecture": "topics/arch.md" + + # Development (nested) topics: build process, CI/CD, etc. + - Development: + # Landing Page + - "topics/development/index.md" + # Nested Pages + - "Docker": "topics/development/build_process_DAG.md" + - "CI/CD": "topics/development/cicd.md" + + ## Automated Tags - Page Generation ## - tags: tags.md diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/assets/docker_off.png" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/assets/docker_off.png" deleted file mode 100644 index e709048f5f2f1e74eec049734868e8cc16e5a933..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33086 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 G$A1If$!_5Q diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/build-process_DAG.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/build-process_DAG.md" deleted file mode 100644 index 30d2ae1a..00000000 --- "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/build-process_DAG.md" +++ /dev/null @@ -1,17 +0,0 @@ -## Docker Build Process DAG - -`docker build` possible execution paths. - -Flow Chart, of how exection navigates docker stages (see --target of docker build). - -If you run `docker build .` the `target` used by default is the `default_with_demo` Stage in the Graph. - -**Dockerfile: ./Dockerfile** - -{# we have include 'dockerfile_mermaid.md' statment below #} -{# intention is to leverage markdown imports, on docs build time #} -{# it should not affect dynamically the Generator behaviour #} - -{# so we must enusre that jinja does, treats below as literal, and not try to interpret #} - -{% raw %}{% include 'dockerfile_mermaid.md' %}{% endraw %} diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd.md" deleted file mode 100644 index 5a610a0d..00000000 --- "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd.md" +++ /dev/null @@ -1,14 +0,0 @@ ---- -tags: - - CICD ---- - -## CICD Pipeline, as Github Action Workflow - -### Variables to provide for `var` context - -Flow Chart, of Jobs Dependencies in the Pipeline. - -**config: ./.github/workflows/test.yaml** - -{% raw %}{% include 'cicd_mermaid.md' %}{% endraw %} diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd_mermaid.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd_mermaid.md" deleted file mode 100644 index 974b6b01..00000000 --- "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd_mermaid.md" +++ /dev/null @@ -1,24 +0,0 @@ -{% if cookiecutter.cicd == "stable" %}```mermaid -graph LR; - set_github_outputs --> test_suite - test_suite --> codecov_coverage_host - set_github_outputs --> docker_build - test_suite --> docker_build - set_github_outputs --> check_which_git_branch_we_are_on - test_suite --> pypi_publish - check_which_git_branch_we_are_on --> pypi_publish - set_github_outputs --> lint - set_github_outputs --> docs - set_github_outputs --> code_visualization -```{% elif cookiecutter.cicd == "experimental" %}```mermaid -graph LR; - test_n_build - test_n_build --> codecov_coverage_host - test_n_build --> docker_build - lint - docs - code_visualization - test_n_build --> signal_deploy - signal_deploy --> pypi_publish - signal_deploy --> gh_release -```{% endif %} diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/docker.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/docker.md" deleted file mode 100644 index 19731b3d..00000000 --- "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/docker.md" +++ /dev/null @@ -1,40 +0,0 @@ -# Do Docker-related stuff - -
- Docker on CI - - ## How to prevent any Image from being published to Dockerhub - - 1. Open your `.github/workflows/test.yaml`, and look for the **Worfklow Variables** - - **Worfklow Variables** are defined in the `env` *section* - - 2. Check the *value* of the `DOCKER_JOB_ON` **Worfklow Variable** - - [this is line is not rendered; markdown comment]: # - - ![Docker OFF](../assets/docker_off.png) - - 3. If *value* is **false**, then we are OK. - - 4. If not, set value* to **false** - - ```shell - git add .github/workflows/test.yaml - git commit "ci: emphemerally prevent any Image Build and Dockerhub Publish" - ``` - - Now, it is **guaranteed**, that **NO** Dockerhub Publish will happen, - by any Pipeline subsequent `trigger`, aka `git events` (ie `git push`) fired. - - **Info**: the `DOCKER_JOB_ON` is a top-level Gate to all Docker-related in CI. - Only, if `DOCKER_JOB_ON` is **true**, any image build and publish can be ever considered. - - -
- - -### References - -- [https://automated-workflows.readthedocs.io/en/main/guide_setup_cicd](https://automated-workflows.readthedocs.io/en/main/guide_setup_cicd/) -- [https://automated-workflows.readthedocs.io/en/main/ref_docker](https://automated-workflows.readthedocs.io/en/main/ref_docker/) diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/index.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/index.md" deleted file mode 100644 index 7949cf41..00000000 --- "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/index.md" +++ /dev/null @@ -1,38 +0,0 @@ -# How-To Guides for Development - -This section includes practical, *how-to* guides, for a **developer** to achieve something. -Guides on how to **run tests** against your code, how to **publish to PyPI**, how to build -a Docker Image and **publish it to Dockerhub**, how to do `Static Code Analysis`, etc. - - -## How to prevent any Image from being published to Dockerhub - -1. Open your `.github/workflows/test.yaml`, and look for the **Worfklow Variables** - - **Worfklow Variables** are defined in the `env` *section* - -2. Check the *value* of the `DOCKER_JOB_ON` **Worfklow Variable** - - [this is line is not rendered; markdown comment]: # - - ![Docker OFF](../assets/docker_off.png) - -3. If *value* is **false**, then we are OK. - -4. If not, set value* to **false** - - ```shell - git add .github/workflows/test.yaml - git commit "ci: emphemerally prevent any Image Build and Dockerhub Publish" - ``` - -Now, it is **guaranteed**, that **NO** Dockerhub Publish will happen, -by any Pipeline subsequent `trigger`, aka `git events` (ie `git push`) fired. - -**Info**: the `DOCKER_JOB_ON` is a top-level Gate to all Docker-related in CI. -Only, if `DOCKER_JOB_ON` is **true**, any image build and publish can be ever considered. - -### References - -- https://automated-workflows.readthedocs.io/en/main/guide_setup_cicd/ -- https://automated-workflows.readthedocs.io/en/main/ref_docker/ diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/index.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/index.md" index 950f5372..ec53d19f 100644 --- "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/index.md" +++ "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/index.md" @@ -1,7 +1,73 @@ # Welcome to {{ cookiecutter.project_name }} Documentation! +[//]: # (Render a few important badges: CI/CD Status, RTD, Coverage, Latest Tag/Sem Ver) + {{ cookiecutter.project_name }} is an open source TODO +[//]: # (Same a few words about what this does) + +[//]: # (Maybe state Goal and/or small motivation note) + +[//]: # (Leverage Mermaid to show high of what happens) + +[//]: # (Ideally record video with demo and embed here) + ## Quick-start TODO + +## :material-book-open: Documentation + +Read about how to use the `{{ cookiecutter.pkg_name }}` package, understand its features +and capabilities. + +Learn how to use the `{{ cookiecutter.pkg_name }}` package, achieve goals leverating understand its features +and capabilities. + + +
+ + +- :fontawesome-regular-circle-play:{ .lg .middle } __`How-to` Guides__ + + --- + + Step-by-step `Guides` that leverage **{{ cookiecutter.pkg_name }}** to achieve `Goals`, such as: + + - TODO 1 + - TODO 2 + + [:octicons-arrow-right-24: :material-rocket-launch: `Install`, `Run`, `Use`](./guides/index.md) + + +- :material-application-brackets-outline:{ .lg .middle } __API References__ + + --- + [//]: # (link ./reference/CLI.md does not exist yet, it is generate at docs build-time) + [:octicons-arrow-right-24: :material-console:{ .lg .middle } {{ cookiecutter.pkg_name|replace('_', '-') }} CLI](./reference/CLI.md) + + [//]: # (link ./reference/{{ cookiecutter.pkg_name }}.md does not exist yet, it is generate at docs build-time) + [:octicons-arrow-right-24: :material-language-python: API Refs](./reference/{{ cookiecutter.pkg_name }}) + + +- :fontawesome-solid-book-open:{ .lg .middle } __Topics__ + + --- + + **Explanations / Topics** + + [:octicons-arrow-right-24: :material-language-python: Architecture ](./topics/arch.md) + + [//]: # (Add important Topics here) + + +- :fontawesome-solid-book-open:{ .lg .middle } __Development Topics__ + + --- + + **Topics / Explanations** on Development + + [:octicons-arrow-right-24: :material-hammer-screwdriver: Development Topics ](./topics/development/index.md) + + +
diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/arch.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/arch.md" new file mode 100644 index 00000000..1b90979c --- /dev/null +++ "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/arch.md" @@ -0,0 +1,51 @@ +# Software Architecture + +[//]: # (this is a comment) +[//]: # (Description of what is this Page) + +Here you can find the software architecture of the project. + +## Module Dependencies + +[//]: # (Description of what is this Section) + +Here you can find the dependencies between the modules of the project. + +The dependencies are Visualized as a Graph, where Nodes are the modules and the Edges are python ``import`` statements. + +The dependencies are visualized, after running the following command: + +```sh +tox -e pydeps +``` + +!!! Tip + + Right-click and open image in new Tab for better inspection + +### First-party Dependencies + +[//]: # (Inner Python Imports SVG Graph) + +![First-party Dependencies](../assets/deps_inner.svg) + + +### First and Third party Dependencies + +[//]: # (First-Party with 3rd-party having all incoming edges to our individual Modules) + +![All Dependencies - C](../assets/deps_all.svg) + + +### 1st+3rd party Deps - 1st as Cluster + +[//]: # ("Boxed" First-Party with 3rd-party having all incoming edges to our Box) + +![All Dependencies - B](../assets/deps_ktc.svg) + + +### 1st+3rd party Deps - 1st+3rd as Cluster + +[//]: # ("Boxed" First-Party with 3rd-party having 1 incoming edge to our Box) + +![All Dependencies - A](../assets/deps_ktc-mcs_2.svg) diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/build_process_DAG.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/build_process_DAG.md" new file mode 100644 index 00000000..709dc654 --- /dev/null +++ "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/build_process_DAG.md" @@ -0,0 +1,20 @@ +## Docker Build Process DAG + +> Understand how we leverage `Docker` in the build process. + +The project features a `Dockerfile`, designed for + +- multi-stage builds +- parallel stage building (assuming appropriate build backend) +- size minimization of the produced `Docker` image +- minimization of vulerabilities + +## Dockerfile visualized as Directed Acyclic Graph (DAG) + +> Understand the execution path of `docker build`, via **DAG visualization** + +{% raw %}{% include 'topics/development/dockerfile_mermaid.md' %}{% endraw %} + +- `solid boxes` represent distinct docker **stages** and their *aliases* +- `solid arrows` represent **stage dependencies**; `FROM a AS b` type of instructions +- `dotted arrows` represent **stage COPY**: `COPY --from=a /path /path` type of instructions diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd.md" new file mode 100644 index 00000000..5341e037 --- /dev/null +++ "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd.md" @@ -0,0 +1,28 @@ +--- +tags: + - CICD +--- + +## CI/CD Pipeline + +> Understand what Jobs are part of the CI/CD Pipeline + +**CI/CD Pipeline** is implemented as `Github Actions Workflow` in a YAML file format. + +### Workflow of Jobs: visualized as a Directed Acyclic Graph (DAG) + +> Understand the Job Dependencies at "compile time" + +**YAML Workflow: ./.github/workflows/cicd.yml** + +{% raw %}{% include 'topics/development/cicd_mermaid.md' %}{% endraw %} + +- `solid boxes` represent **Jobs** declared in the `jobs` array of the YAML Workflow +- `solid arrows` represent **Job Dependencies**; `job_A.needs: [job_b, job_c]` type of yaml objects + + +[//]: # (TODO add section to EXPLAIN the CI/CD Pipeline at runtime) + +[//]: # (TODO make screenshot of CI Server run and paste here) + +[//]: # (TODO add link to live CI server Pipeline RUNS) diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/cicd_mermaid.md "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd_mermaid.md" similarity index 100% rename from tests/data/snapshots/biskotaki-gold-standard/docs/cicd_mermaid.md rename to "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd_mermaid.md" diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dockerfile_mermaid.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/dockerfile_mermaid.md" similarity index 100% rename from "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dockerfile_mermaid.md" rename to "src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/dockerfile_mermaid.md" diff --git "a/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/index.md" "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/index.md" new file mode 100644 index 00000000..5a026902 --- /dev/null +++ "b/src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == \"mkdocs\" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/index.md" @@ -0,0 +1,35 @@ +# Development +Here you will find topics related to `Development`, the `build`, and the +`CI/CD` Pipeline design of the **Biskotaki** open-source project. + + +
+ + +- :material-docker:{ .lg .middle } __Docker__ + + --- + + Dockerfile design, Build Process + + [:octicons-arrow-right-24: Topic ](./build_process_DAG.md) + + +- :simple-githubactions:{ .lg .middle } __CI/CD Pipeline__ + + --- + + Github Actions Workflow of Jobs, visualized as a DAG + + [:octicons-arrow-right-24: Topic ](./cicd.md) + + +- :material-state-machine:{ .lg .middle } __Git Ops Processes__ + + --- + + Step-by-step Processes, leveraging `git` and `CI` for **Releasing changes** + + [:octicons-arrow-right-24: Docs ](./gitops/) + +
diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/assets/docker_off.png b/tests/data/snapshots/biskotaki-gold-standard/docs/assets/docker_off.png deleted file mode 100644 index e709048f5f2f1e74eec049734868e8cc16e5a933..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33086 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 G$A1If$!_5Q diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/build-process_DAG.md b/tests/data/snapshots/biskotaki-gold-standard/docs/build-process_DAG.md deleted file mode 100644 index 2b0d4bdf..00000000 --- a/tests/data/snapshots/biskotaki-gold-standard/docs/build-process_DAG.md +++ /dev/null @@ -1,17 +0,0 @@ -## Docker Build Process DAG - -`docker build` possible execution paths. - -Flow Chart, of how exection navigates docker stages (see --target of docker build). - -If you run `docker build .` the `target` used by default is the `default_with_demo` Stage in the Graph. - -**Dockerfile: ./Dockerfile** - - - - - - - -{% include 'dockerfile_mermaid.md' %} diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/cicd.md b/tests/data/snapshots/biskotaki-gold-standard/docs/cicd.md deleted file mode 100644 index c3a10be7..00000000 --- a/tests/data/snapshots/biskotaki-gold-standard/docs/cicd.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -tags: - - CICD ---- - -## CICD Pipeline, as Github Action Workflow - -### Variables to provide for `var` context - -Flow Chart, of Jobs Dependencies in the Pipeline. - -**config: ./.github/workflows/test.yaml** - -{% include 'cicd_mermaid.md' %} diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/docker.md b/tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/docker.md deleted file mode 100644 index 19731b3d..00000000 --- a/tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/docker.md +++ /dev/null @@ -1,40 +0,0 @@ -# Do Docker-related stuff - -
- Docker on CI - - ## How to prevent any Image from being published to Dockerhub - - 1. Open your `.github/workflows/test.yaml`, and look for the **Worfklow Variables** - - **Worfklow Variables** are defined in the `env` *section* - - 2. Check the *value* of the `DOCKER_JOB_ON` **Worfklow Variable** - - [this is line is not rendered; markdown comment]: # - - ![Docker OFF](../assets/docker_off.png) - - 3. If *value* is **false**, then we are OK. - - 4. If not, set value* to **false** - - ```shell - git add .github/workflows/test.yaml - git commit "ci: emphemerally prevent any Image Build and Dockerhub Publish" - ``` - - Now, it is **guaranteed**, that **NO** Dockerhub Publish will happen, - by any Pipeline subsequent `trigger`, aka `git events` (ie `git push`) fired. - - **Info**: the `DOCKER_JOB_ON` is a top-level Gate to all Docker-related in CI. - Only, if `DOCKER_JOB_ON` is **true**, any image build and publish can be ever considered. - - -
- - -### References - -- [https://automated-workflows.readthedocs.io/en/main/guide_setup_cicd](https://automated-workflows.readthedocs.io/en/main/guide_setup_cicd/) -- [https://automated-workflows.readthedocs.io/en/main/ref_docker](https://automated-workflows.readthedocs.io/en/main/ref_docker/) diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/index.md b/tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/index.md deleted file mode 100644 index 7949cf41..00000000 --- a/tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/index.md +++ /dev/null @@ -1,38 +0,0 @@ -# How-To Guides for Development - -This section includes practical, *how-to* guides, for a **developer** to achieve something. -Guides on how to **run tests** against your code, how to **publish to PyPI**, how to build -a Docker Image and **publish it to Dockerhub**, how to do `Static Code Analysis`, etc. - - -## How to prevent any Image from being published to Dockerhub - -1. Open your `.github/workflows/test.yaml`, and look for the **Worfklow Variables** - - **Worfklow Variables** are defined in the `env` *section* - -2. Check the *value* of the `DOCKER_JOB_ON` **Worfklow Variable** - - [this is line is not rendered; markdown comment]: # - - ![Docker OFF](../assets/docker_off.png) - -3. If *value* is **false**, then we are OK. - -4. If not, set value* to **false** - - ```shell - git add .github/workflows/test.yaml - git commit "ci: emphemerally prevent any Image Build and Dockerhub Publish" - ``` - -Now, it is **guaranteed**, that **NO** Dockerhub Publish will happen, -by any Pipeline subsequent `trigger`, aka `git events` (ie `git push`) fired. - -**Info**: the `DOCKER_JOB_ON` is a top-level Gate to all Docker-related in CI. -Only, if `DOCKER_JOB_ON` is **true**, any image build and publish can be ever considered. - -### References - -- https://automated-workflows.readthedocs.io/en/main/guide_setup_cicd/ -- https://automated-workflows.readthedocs.io/en/main/ref_docker/ diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/index.md b/tests/data/snapshots/biskotaki-gold-standard/docs/index.md index a1792b64..0259da0b 100644 --- a/tests/data/snapshots/biskotaki-gold-standard/docs/index.md +++ b/tests/data/snapshots/biskotaki-gold-standard/docs/index.md @@ -1,7 +1,73 @@ # Welcome to Biskotaki Gold Standard Documentation! +[//]: # (Render a few important badges: CI/CD Status, RTD, Coverage, Latest Tag/Sem Ver) + Biskotaki Gold Standard is an open source TODO +[//]: # (Same a few words about what this does) + +[//]: # (Maybe state Goal and/or small motivation note) + +[//]: # (Leverage Mermaid to show high of what happens) + +[//]: # (Ideally record video with demo and embed here) + ## Quick-start TODO + +## :material-book-open: Documentation + +Read about how to use the `biskotakigold` package, understand its features +and capabilities. + +Learn how to use the `biskotakigold` package, achieve goals leverating understand its features +and capabilities. + + +
+ + +- :fontawesome-regular-circle-play:{ .lg .middle } __`How-to` Guides__ + + --- + + Step-by-step `Guides` that leverage **biskotakigold** to achieve `Goals`, such as: + + - TODO 1 + - TODO 2 + + [:octicons-arrow-right-24: :material-rocket-launch: `Install`, `Run`, `Use`](./guides/index.md) + + +- :material-application-brackets-outline:{ .lg .middle } __API References__ + + --- + [//]: # (link ./reference/CLI.md does not exist yet, it is generate at docs build-time) + [:octicons-arrow-right-24: :material-console:{ .lg .middle } biskotakigold CLI](./reference/CLI.md) + + [//]: # (link ./reference/biskotakigold.md does not exist yet, it is generate at docs build-time) + [:octicons-arrow-right-24: :material-language-python: API Refs](./reference/biskotakigold) + + +- :fontawesome-solid-book-open:{ .lg .middle } __Topics__ + + --- + + **Explanations / Topics** + + [:octicons-arrow-right-24: :material-language-python: Architecture ](./topics/arch.md) + + [//]: # (Add important Topics here) + + +- :fontawesome-solid-book-open:{ .lg .middle } __Development Topics__ + + --- + + **Topics / Explanations** on Development + + [:octicons-arrow-right-24: :material-hammer-screwdriver: Development Topics ](./topics/development/index.md) + + +
diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/topics/arch.md b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/arch.md new file mode 100644 index 00000000..1b90979c --- /dev/null +++ b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/arch.md @@ -0,0 +1,51 @@ +# Software Architecture + +[//]: # (this is a comment) +[//]: # (Description of what is this Page) + +Here you can find the software architecture of the project. + +## Module Dependencies + +[//]: # (Description of what is this Section) + +Here you can find the dependencies between the modules of the project. + +The dependencies are Visualized as a Graph, where Nodes are the modules and the Edges are python ``import`` statements. + +The dependencies are visualized, after running the following command: + +```sh +tox -e pydeps +``` + +!!! Tip + + Right-click and open image in new Tab for better inspection + +### First-party Dependencies + +[//]: # (Inner Python Imports SVG Graph) + +![First-party Dependencies](../assets/deps_inner.svg) + + +### First and Third party Dependencies + +[//]: # (First-Party with 3rd-party having all incoming edges to our individual Modules) + +![All Dependencies - C](../assets/deps_all.svg) + + +### 1st+3rd party Deps - 1st as Cluster + +[//]: # ("Boxed" First-Party with 3rd-party having all incoming edges to our Box) + +![All Dependencies - B](../assets/deps_ktc.svg) + + +### 1st+3rd party Deps - 1st+3rd as Cluster + +[//]: # ("Boxed" First-Party with 3rd-party having 1 incoming edge to our Box) + +![All Dependencies - A](../assets/deps_ktc-mcs_2.svg) diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/build_process_DAG.md b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/build_process_DAG.md new file mode 100644 index 00000000..e283c268 --- /dev/null +++ b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/build_process_DAG.md @@ -0,0 +1,20 @@ +## Docker Build Process DAG + +> Understand how we leverage `Docker` in the build process. + +The project features a `Dockerfile`, designed for + +- multi-stage builds +- parallel stage building (assuming appropriate build backend) +- size minimization of the produced `Docker` image +- minimization of vulerabilities + +## Dockerfile visualized as Directed Acyclic Graph (DAG) + +> Understand the execution path of `docker build`, via **DAG visualization** + +{% include 'topics/development/dockerfile_mermaid.md' %} + +- `solid boxes` represent distinct docker **stages** and their *aliases* +- `solid arrows` represent **stage dependencies**; `FROM a AS b` type of instructions +- `dotted arrows` represent **stage COPY**: `COPY --from=a /path /path` type of instructions diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd.md b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd.md new file mode 100644 index 00000000..359a8a16 --- /dev/null +++ b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd.md @@ -0,0 +1,28 @@ +--- +tags: + - CICD +--- + +## CI/CD Pipeline + +> Understand what Jobs are part of the CI/CD Pipeline + +**CI/CD Pipeline** is implemented as `Github Actions Workflow` in a YAML file format. + +### Workflow of Jobs: visualized as a Directed Acyclic Graph (DAG) + +> Understand the Job Dependencies at "compile time" + +**YAML Workflow: ./.github/workflows/cicd.yml** + +{% include 'topics/development/cicd_mermaid.md' %} + +- `solid boxes` represent **Jobs** declared in the `jobs` array of the YAML Workflow +- `solid arrows` represent **Job Dependencies**; `job_A.needs: [job_b, job_c]` type of yaml objects + + +[//]: # (TODO add section to EXPLAIN the CI/CD Pipeline at runtime) + +[//]: # (TODO make screenshot of CI Server run and paste here) + +[//]: # (TODO add link to live CI server Pipeline RUNS) diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd_mermaid.md b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd_mermaid.md new file mode 100644 index 00000000..309a4305 --- /dev/null +++ b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd_mermaid.md @@ -0,0 +1,12 @@ +```mermaid +graph LR; + test_n_build + test_n_build --> codecov_coverage_host + test_n_build --> docker_build + lint + docs + code_visualization + test_n_build --> signal_deploy + signal_deploy --> pypi_publish + signal_deploy --> gh_release +``` diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/dockerfile_mermaid.md b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/dockerfile_mermaid.md similarity index 100% rename from tests/data/snapshots/biskotaki-gold-standard/docs/dockerfile_mermaid.md rename to tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/dockerfile_mermaid.md diff --git a/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/index.md b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/index.md new file mode 100644 index 00000000..5a026902 --- /dev/null +++ b/tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/index.md @@ -0,0 +1,35 @@ +# Development +Here you will find topics related to `Development`, the `build`, and the +`CI/CD` Pipeline design of the **Biskotaki** open-source project. + + +
+ + +- :material-docker:{ .lg .middle } __Docker__ + + --- + + Dockerfile design, Build Process + + [:octicons-arrow-right-24: Topic ](./build_process_DAG.md) + + +- :simple-githubactions:{ .lg .middle } __CI/CD Pipeline__ + + --- + + Github Actions Workflow of Jobs, visualized as a DAG + + [:octicons-arrow-right-24: Topic ](./cicd.md) + + +- :material-state-machine:{ .lg .middle } __Git Ops Processes__ + + --- + + Step-by-step Processes, leveraging `git` and `CI` for **Releasing changes** + + [:octicons-arrow-right-24: Docs ](./gitops/) + +
diff --git a/tests/data/snapshots/biskotaki-gold-standard/mkdocs.yml b/tests/data/snapshots/biskotaki-gold-standard/mkdocs.yml index d26fb136..dc581b42 100644 --- a/tests/data/snapshots/biskotaki-gold-standard/mkdocs.yml +++ b/tests/data/snapshots/biskotaki-gold-standard/mkdocs.yml @@ -17,25 +17,43 @@ theme: default: material/tag features: + # render a breadcrumb navigation above the title of each page, which might make orientation easier for users visiting your documentation on devices with smaller screens. - navigation.path - - navigation.top + - navigation.top # back-to-top button shown when user, after scrolling down, starts to scroll - navigation.footer + # enables switching multiple tabs with the same name + - content.tabs.link + # renders copy button on code blocks + - content.code.copy + # make index.md landing page to collapsible sections (no need for sections-index 3rd-party plugin) + - navigation.indexes plugins: # Enable jinja inside your markdown files + # - allows {% include %} statements + # - requires careful escape of double-braces in markdown + # - requires wrapping content between {% raw %} and {% endraw %} # https://github.com/fralau/mkdocs_macros_plugin - macros + # include_dir: docs/includes + # Authors need installation # - git-authors + + # Automated Tags - Page Generation - tags: tags_file: tags.md + # BASIC SEARCH PLUGIN - search + # MERMAID Render Support - mermaid2 + # Directives Provider for docstrings parsing - mkdocstrings - # gain the ability to tap-in to the build process + + # Tap-in to the build process and generate files with mkdocstrings directives - gen-files: # scripts allowing to programmatically generate .md content scripts: @@ -44,19 +62,94 @@ plugins: # programmatically generate Nav Item: ie 'reference/' entry in Navigation - literate-nav: nav_file: SUMMARY.md - - section-index markdown_extensions: - mkdocs-click - pymdownx.highlight - + + ### ADMONITIONS ### + # enabled block kelements such as example, note, info, warning, tip, etc + # https://squidfunk.github.io/mkdocs-material/reference/admonitions/ + - admonition + + ### SUPERFENCES ### + # arbitrary nesting of code and content blocks inside each other, + # including admonitions, tabs, lists and all other elements + # https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#superfences + # this custom fence defined below with name 'mermaid' is required + # to prevent superfences from breaking mermaid default syntax (3 backticks)! + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + + ### TABBED ### + # usage of content tabs, a simple way to group related content and + # code blocks under accessible tabs. + # https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#tabbed + - pymdownx.tabbed: + alternate_style: true + + ### GRIDS with Grid Cards or Generic Cards ### + - attr_list + - md_in_html + + ### ICONS/EMOJIS ### + # https://squidfunk.github.io/mkdocs-material/reference/icons-emojis/#with-colors-docsstylesheetsextracss + # The following icon sets are bundled with Material for MkDocs: + # – Material Design: https://pictogrammers.com/library/mdi/ + # – FontAwesome : https://fontawesome.com/search?ic=free + # – Octicons : https://primer.style/octicons/ + # – Simple Icons : https://simpleicons.org/ + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + + ### On-HOVER TOOLTIP ### + # abbr by default supports tooltips on url links + - abbr + # if attr_list is also added, then tooltips on other html elements are supported as below: + # ie :material-information-outline:{ title="Important information" } + + # The Details extension supercharges the Admonition extension, making the resulting call-outs collapsible, allowing them to be opened and closed by the user. + # https://squidfunk.github.io/mkdocs-material/setup/extensions/python-markdown-extensions/#details + # example: ??? blahblah + # expanded example: ???+ blahblah + # if element is insise non-trivial content wrap in div with class markdown + - pymdownx.details + +###### SITE NAVIGATION ###### nav: - - Home: - - "Quick Start": index.md + ## Landing Page ## + - Home: index.md + + ## Motivation ## + # - Motivation: topics/why_this_package.md + + ## TUTORIALS ## + # - Tutorials: + # Landing Page + # - "tutorials/index.md" + + ## REFERENCES ## - API References: reference/ + + ## TOPICS/EXPLANATIONS ## - Topics: - - "Docker": build-process_DAG.md - - "CI/CD": cicd.md + + # Project Architecture + - "Architecture": "topics/arch.md" + + # Development (nested) topics: build process, CI/CD, etc. + - Development: + # Landing Page + - "topics/development/index.md" + # Nested Pages + - "Docker": "topics/development/build_process_DAG.md" + - "CI/CD": "topics/development/cicd.md" + + ## Automated Tags - Page Generation ## - tags: tags.md diff --git a/tests/test_gold_standard.py b/tests/test_gold_standard.py index 8cd93fc5..a099a335 100644 --- a/tests/test_gold_standard.py +++ b/tests/test_gold_standard.py @@ -53,9 +53,6 @@ def gen_gs_project( assert (gen_project_dir / 'docs').is_dir() assert (gen_project_dir / 'docs' / 'assets').exists() assert (gen_project_dir / 'docs' / 'assets').is_dir() - assert (gen_project_dir / 'docs' / 'dev_guides').exists() - assert (gen_project_dir / 'docs' / 'dev_guides' / 'docker.md').exists() - assert (gen_project_dir / 'docs' / 'dev_guides' / 'docker.md').is_file() ## Logging file created - Assertions ## from cookiecutter_python._logging_config import FILE_TARGET_LOGS @@ -139,11 +136,18 @@ def _compare_file_content(runtime_file: Path, snap_file: Path): runtime_file_content = runtime_file.read_text().splitlines() snap_file_content = snap_file.read_text().splitlines() + # find common parts of a above Path objects + common_parts = set(runtime_file.parts).intersection(set(snap_file.parts)) + + # use existing part to order the set + ordered_parts: t.Tuple = tuple([x for x in snap_file.parts if x in common_parts]) + for line_index, line_pair in enumerate(zip(runtime_file_content, snap_file_content)): assert line_pair[0] == line_pair[1], ( f"File: {runtime_file.relative_to(runtime_file.parent)} has different content at Runtime than in Snapshot\n" + f"Relative (sub) path: {Path(*ordered_parts).resolve()}\n" f"Line Index: {line_index}\n" - f"Line Runtime: {line_pair[0]}\n" + f"Line Runtime : {line_pair[0]}\n" f"Line Snapshot: {line_pair[1]}\n" ) assert len(runtime_file_content) == len(snap_file_content) @@ -225,6 +229,7 @@ def test_gs_matches_runtime( 'settings.json', '.tox', '.pytest_cache', + 'del-env', } for p in x.parts ] @@ -234,9 +239,7 @@ def test_gs_matches_runtime( ) snap_relative_paths_set = set([x for x in snap_relative_paths_set if x.name != 'reqs.txt']) - if has_developer_fixed_windows_mishap: - assert runtime_relative_paths_set == snap_relative_paths_set - elif running_on_windows: # there is a log mishappening that we exists on windows + if running_on_windows: # there is a log mishappening that we exists on windows from cookiecutter_python._logging_config import ( FILE_TARGET_LOGS as LOG_FILE_NAME, ) From a61c218d72f70e77e647b9c5d7b090fadc27f6c1 Mon Sep 17 00:00:00 2001 From: Konstantinos Lampridis Date: Wed, 14 May 2025 23:57:20 +0300 Subject: [PATCH 4/7] refactor(black): apply black --- tests/test_gold_standard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_gold_standard.py b/tests/test_gold_standard.py index a099a335..0436049b 100644 --- a/tests/test_gold_standard.py +++ b/tests/test_gold_standard.py @@ -138,7 +138,7 @@ def _compare_file_content(runtime_file: Path, snap_file: Path): # find common parts of a above Path objects common_parts = set(runtime_file.parts).intersection(set(snap_file.parts)) - + # use existing part to order the set ordered_parts: t.Tuple = tuple([x for x in snap_file.parts if x in common_parts]) From 8d5d8b1d2659d5d46ef25c6816b8d826c8508c5b Mon Sep 17 00:00:00 2001 From: Konstantinos Lampridis Date: Wed, 14 May 2025 23:58:26 +0300 Subject: [PATCH 5/7] test: simplify code --- tests/test_gold_standard.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/test_gold_standard.py b/tests/test_gold_standard.py index 0436049b..73e5ea12 100644 --- a/tests/test_gold_standard.py +++ b/tests/test_gold_standard.py @@ -207,12 +207,6 @@ def test_gs_matches_runtime( import sys running_on_windows: bool = sys.platform.startswith("win") - # exception misbehaviour fixed on Windows? - import os - - has_developer_fixed_windows_mishap: bool = ( - os.environ.get("BUG_LOG_DEL_WIN") != "permission_error" - ) # This is useful for local development, to make the tests more reliable snap_relative_paths_set = set( From 07dfc13caa357d1faca520e4cf3a254f32c3e33e Mon Sep 17 00:00:00 2001 From: Konstantinos Lampridis Date: Thu, 15 May 2025 00:03:04 +0300 Subject: [PATCH 6/7] test: fix integration tests --- tests/test_build_backend_sdist.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_build_backend_sdist.py b/tests/test_build_backend_sdist.py index ca59d4a1..dcf3baa6 100644 --- a/tests/test_build_backend_sdist.py +++ b/tests/test_build_backend_sdist.py @@ -49,14 +49,14 @@ def sdist_expected_correct_file_structure(): 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/.readthedocs.yml', 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/Dockerfile', 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/MANIFEST.in', - 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/assets/docker_off.png', - 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/build-process_DAG.md', - 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd.md', - 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/cicd_mermaid.md', - 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/docker.md', - 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dev_guides/index.md', - 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/dockerfile_mermaid.md', + # MKDOCS 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/index.md', + 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/arch.md', + 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/build_process_DAG.md', + 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd.md', + 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/cicd_mermaid.md', + 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/dockerfile_mermaid.md', + 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/topics/development/index.md', 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "mkdocs" %}docs{% else %}PyGen_TO_DELETE{% endif %}/tags.md', 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "sphinx" %}docs{% else %}PyGen_TO_DELETE{% endif %}/Makefile', 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/{% if cookiecutter.docs_builder == "sphinx" %}docs{% else %}PyGen_TO_DELETE{% endif %}/conf.py', @@ -170,14 +170,14 @@ def sdist_expected_correct_file_structure(): 'tests/data/snapshots/biskotaki-gold-standard/CHANGELOG.rst', 'tests/data/snapshots/biskotaki-gold-standard/CONTRIBUTING.md', 'tests/data/snapshots/biskotaki-gold-standard/Dockerfile', - 'tests/data/snapshots/biskotaki-gold-standard/docs/assets/docker_off.png', - 'tests/data/snapshots/biskotaki-gold-standard/docs/build-process_DAG.md', - 'tests/data/snapshots/biskotaki-gold-standard/docs/cicd.md', - 'tests/data/snapshots/biskotaki-gold-standard/docs/cicd_mermaid.md', - 'tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/docker.md', - 'tests/data/snapshots/biskotaki-gold-standard/docs/dev_guides/index.md', - 'tests/data/snapshots/biskotaki-gold-standard/docs/dockerfile_mermaid.md', + # MKDOCS Docs dir 'tests/data/snapshots/biskotaki-gold-standard/docs/index.md', + 'tests/data/snapshots/biskotaki-gold-standard/docs/topics/arch.md', + 'tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/build_process_DAG.md', + 'tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd.md', + 'tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/cicd_mermaid.md', + 'tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/dockerfile_mermaid.md', + 'tests/data/snapshots/biskotaki-gold-standard/docs/topics/development/index.md', 'tests/data/snapshots/biskotaki-gold-standard/docs/tags.md', 'tests/data/snapshots/biskotaki-gold-standard/LICENSE', 'tests/data/snapshots/biskotaki-gold-standard/mkdocs.yml', From 01015471b0f3dcc6e7a3481266991a7291f60684 Mon Sep 17 00:00:00 2001 From: Konstantinos Lampridis Date: Thu, 15 May 2025 00:17:47 +0300 Subject: [PATCH 7/7] test: fix tests --- tests/test_gold_standard.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_gold_standard.py b/tests/test_gold_standard.py index 73e5ea12..17a16b2f 100644 --- a/tests/test_gold_standard.py +++ b/tests/test_gold_standard.py @@ -46,13 +46,14 @@ def gen_gs_project( assert gen_project_dir.exists() assert gen_project_dir.is_dir() assert (gen_project_dir / 'src').exists() and (gen_project_dir / 'src').is_dir() - # Ad-hoc sanity checks that docs folder contains all sub-dirs and nested files + + # sanity checks on docs folder assert (gen_project_dir / 'docs').exists() + assert (gen_project_dir / 'docs').is_dir() + + # sanity check that cleanup of docs folder is done assert not (gen_project_dir / 'docs-mkdocs').exists() assert not (gen_project_dir / 'docs-sphinx').exists() - assert (gen_project_dir / 'docs').is_dir() - assert (gen_project_dir / 'docs' / 'assets').exists() - assert (gen_project_dir / 'docs' / 'assets').is_dir() ## Logging file created - Assertions ## from cookiecutter_python._logging_config import FILE_TARGET_LOGS