From b781670e69b5308e138798506a332fac54f31b78 Mon Sep 17 00:00:00 2001 From: "Reza J. Bavaghoush" Date: Mon, 6 Nov 2023 23:38:13 +0100 Subject: [PATCH] feat: add show-diff option --- README.md | 48 ++++++++++++------------- github_actions_docs/__init__.py | 4 +++ github_actions_docs/cli.py | 9 +++-- github_actions_docs/lib/generator.py | 53 +++++++++++++++++++++++++--- pyproject.toml | 5 +-- 5 files changed, 87 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 0cd8ca7..13fb983 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,15 @@ of comment tags per item as it's in the default inline mode. pip install github-actions-docs ``` -Options: +## Usage + +```bash +github-actions-docs .github/actions/example/action.yaml --verbose +# Creates or updates .github/actions/example/README.md +github-actions-docs .github/actions/example/action.yaml --verbose --dry-run --show-diff +# Does not save anything on the disk and shows the diff between what would have +# been generated if and existing .github/actions/example/README.md +``` ```bash github-actions-docs --help @@ -44,18 +52,14 @@ github-actions-docs --help # -h, --help show this help message and exit # --version show program's version number and exit # --verbose More verbosity in logging. (default: False) -# --ignore Continue on inputs file not being a valid github action or workflow. (default: False) -# --tag-prefix TAG_PREFIX -# Prefix used for the tags in the output. (default: GH_DOCS) -# --output-mode [{replace,inject}] -# Method of output to file. (default: inject) -# --generation-mode [{inline,block}] -# Whether to create tags inline (more flexibility but more noise). (default: inline) -# --docs-filename DOCS_FILENAME -# Creates or updates output on the same path as the input. (default: README.md) -# --usage-ref-override USAGE_REF_OVERRIDE -# Override the uses reference in usage section. By default latest tag or current branch name will be used. (default: ) - +# --dry-run Show content of the generated docs instead of writing it. (default: False) +# --show-diff Show diff between existing file and the newly generated one. (default: False) +# --ignore Silently continue on invalid files. (default: False) +# --tag-prefix Prefix used for the tags in the output. (default: GH_DOCS) +# --output-mode Method of output to file. (default: inject) Possible values: [replace, inject] +# --generation-mode Whether to create tags inline or only a pair of tags. (default: inline) Possible values: [inline, block] +# --docs-filename Creates or updates output on the same path as the input. (default: README.md) +# --usage-ref-override Override the uses reference in usage section. By default latest tag or current branch name will be used. ``` ## As a pre-commit hook @@ -71,19 +75,15 @@ Sample `.pre-commit-config.yaml` - id: generate-gh-actions-docs ``` -## Quick start - -Following command creates or updates `.github/actions/example/README.md`. - -```bash -github-actions-docs .github/actions/example/action.yaml --verbose -``` +## Inline generation mode -If the output file (determined by `--docs-filename`) does not exist, it would be -created based on a default template. If not it would check content of the existing -file for the [tags](#full-list-of-tags) and updates them. +If the output file (determined by `--docs-filename`) does not exist on the same +path as the input file, it would be generated based on a default template. Otherwise +it would check content of the existing file for the [tags](#full-list-of-tags) and +tries to update them by putting the desired value in a `BEGIN` and `END` pair of tags +with the same name. -## Full list of tags (for inline generation mode) +### Full list of tags (inline generation mode) | tag name | corresponding yaml path | description | type | | --------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------ | diff --git a/github_actions_docs/__init__.py b/github_actions_docs/__init__.py index bc8527a..53306b4 100644 --- a/github_actions_docs/__init__.py +++ b/github_actions_docs/__init__.py @@ -17,6 +17,7 @@ def main(): args = build_args_parser(description=description, version=version).parse_args() if args.verbose: logging.getLogger().setLevel(logging.DEBUG) + logging.getLogger().handlers = [logging.StreamHandler(sys.stderr)] elif args.ignore: logging.getLogger().setLevel(logging.WARNING) exit_code = generate_docs( @@ -26,6 +27,9 @@ def main(): usage_ref_override=args.usage_ref_override, tag_prefix=args.tag_prefix, ignore=args.ignore, + dry_run=args.dry_run, + show_diff=args.show_diff, + generation_mode=args.generation_mode, ) sys.exit(exit_code) diff --git a/github_actions_docs/cli.py b/github_actions_docs/cli.py index 550ff6b..940cb3e 100644 --- a/github_actions_docs/cli.py +++ b/github_actions_docs/cli.py @@ -24,12 +24,17 @@ def build_args_parser(description: str, version: str) -> argparse.ArgumentParser parser.add_argument( "--dry-run", action="store_true", - help="Don't write anything to the destination files", + help="Show content of the generated docs instead of writing it.", + ) + parser.add_argument( + "--show-diff", + action="store_true", + help="Show diff between existing file and the newly generated one.", ) parser.add_argument( "--ignore", action="store_true", - help="Continue on inputs file not being a valid github action or workflow.", + help="Silently continue on invalid files.", ) parser.add_argument( "--tag-prefix", diff --git a/github_actions_docs/lib/generator.py b/github_actions_docs/lib/generator.py index f192e87..697c1fc 100644 --- a/github_actions_docs/lib/generator.py +++ b/github_actions_docs/lib/generator.py @@ -1,5 +1,8 @@ +import difflib import logging +import pathlib import re +import tempfile from github_actions_docs.config import DOCS_TEMPLATES from github_actions_docs.errors import ( @@ -8,6 +11,9 @@ ) from github_actions_docs.lib.parser import GithubActions from github_actions_docs.lib.styler import UpdateDocsStyle +from pygments import highlight +from pygments.formatters import Terminal256Formatter +from pygments.lexers import DiffLexer, MarkdownLexer def generate_docs( @@ -17,6 +23,9 @@ def generate_docs( usage_ref_override: str = "", tag_prefix: str = "GH_DOCS", ignore: bool = False, + dry_run: bool = False, + show_diff: bool = False, + generation_mode="inline", ) -> int: """ Args: @@ -29,12 +38,14 @@ def generate_docs( branch name. tag_prefix: sections are designated by comments in markdown file. This parameter controls the prefix of those comments. - ignore: continue if any one of the input file is not a valid github - action or workflow. + ignore: continue if any one of the input files are not a valid github + action or a workflow. Returns: exit code, 1 if any of input files has been changed, 0 if no change. """ + if dry_run: + docs_path = pathlib.Path(tempfile.mkdtemp()) changed_files = [] for path in file_paths: logging.debug(f"evaluating: {path}") @@ -51,17 +62,51 @@ def generate_docs( UpdateDocsStyle(parsed_yaml, github_actions.yaml_path, usage_ref_override) + # Setup output + existing_docs_path = github_actions.yaml_path.parent.joinpath(docs_filename) + existing_file_content = "" + if existing_docs_path.is_file(): + with open(existing_docs_path, "r") as f: + existing_file_content = f.read() + if dry_run: + docs_path = docs_path.joinpath(f"{hash(path)}_{docs_filename}") + if existing_docs_path.is_file(): + with open(docs_path, "w") as f: + f.write(existing_file_content) + else: + docs_path = github_actions.yaml_path.parent.joinpath(docs_filename) + # Generate changed file changed_file = create_or_update_docs_file( parsed_yaml, github_actions.yaml_path, docs_filename, output_mode, action_type, + docs_path, tag_prefix, ) changed_files.append(changed_file) + # Generate output + with open(docs_path, "r") as f: + new_file_content = f.read() + if dry_run: + if not show_diff: + print(new_file_content) + logging.info(f"file would have been written in: {existing_docs_path}") + if show_diff: + diff = "".join( + difflib.unified_diff( + existing_file_content.splitlines(keepends=True), + new_file_content.splitlines(keepends=True), + n=10, + ) + ) + if not diff: + print("No changes to the existing file!") + else: + print(highlight(diff, DiffLexer(), Terminal256Formatter())) if changed_file: - logging.info(f"changed file: {github_actions.yaml_path}") + logging.info(f"changed for file: {github_actions.yaml_path}") else: logging.info(f"no change: {github_actions.yaml_path}") logging.debug(f"number of changed files: {sum(changed_files)}/{len(file_paths)}") @@ -74,6 +119,7 @@ def create_or_update_docs_file( docs_filename: str, output_mode: str, action_type: str, + docs_path: pathlib.Path, tag_prefix: str = "GH_DOCS", ) -> bool: """ @@ -81,7 +127,6 @@ def create_or_update_docs_file( True if the file has been updated """ file_changed = False - docs_path = yaml_path.parent.joinpath(docs_filename) if action_type not in DOCS_TEMPLATES: template = DOCS_TEMPLATES["generic"].format(prefix=tag_prefix) else: diff --git a/pyproject.toml b/pyproject.toml index bd28314..711927b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "github_actions_docs" description = "Generate github actions documentation in markdown format." -version = "0.3.0" +version = "0.3.1" readme = "README.md" requires-python = ">=3.10" authors = [ @@ -20,7 +20,8 @@ classifiers = [ ] dependencies = [ 'importlib-metadata>=6.8.0', - 'ruamel.yaml<=0.18.0' + 'ruamel.yaml<=0.18.0', + 'pygments', ] [project.urls]