Source code for gitlab_overviewer.cli.__main__

  1"""GitLab Overviewer main entry point.
  2
  3Implements multiple specifications:
  4
  5* :any:`/specs/spec_renderer_markdown` §1-4 for markdown output
  6* :any:`/specs/spec_renderer_quarto` §1-4 for quarto output
  7* :any:`/specs/spec_table_sorting` §1-2 for sort key handling
  8* :any:`/specs/spec_table_rendering_ui` §1-3 for table configuration
  9* :any:`/specs/spec_settings` §1-3 for CLI argument handling
 10"""
 11
 12from __future__ import annotations
 13
 14import dis
 15import sys
 16from enum import Enum
 17from pathlib import Path
 18from typing import Annotated, Optional
 19
 20import typer  # type: ignore
 21import yaml
 22import argparse
 23
 24from ..config.settings import Settings
 25from ..utils.logging import get_logger
 26from ..services import DataCollector
 27import gitlab_overviewer.services.sort_utils as sort_utils
 28from ..rendering.markdown import MarkdownRenderer
 29from ..rendering.quarto import QuartoRenderer
 30from ..services.sort_utils import sort_overview
 31
 32app = typer.Typer(add_completion=False, help="GitLab Overviewer – modern CLI (Typer)")
 33
 34logger = get_logger(__name__)
 35
 36
[docs] 37class OutputFormat(str, Enum): 38 markdown = "markdown" 39 quarto = "quarto"
40 41
[docs] 42@app.callback() 43def _common( 44 ctx: typer.Context, 45 sort: Annotated[ 46 Optional[str], 47 typer.Option("--sort", help="Sort definition e.g. 'priority:desc'"), 48 ] = None, 49 table_config: Annotated[ 50 str, 51 typer.Option( 52 "--table-config", metavar="PATH", help="Path to table_config.yaml" 53 ), 54 ] = "table_config.yaml", 55 log_level: Annotated[ 56 str, typer.Option("--log-level", help="Log level (DEBUG, INFO, WARNING, ERROR)") 57 ] = "INFO", 58 output: Annotated[ 59 OutputFormat, typer.Option("--output", "-o", case_sensitive=False) 60 ] = OutputFormat.markdown, 61 debug: Annotated[ 62 bool, typer.Option("--debug", is_flag=True, help="Enable debug mode") 63 ] = False, 64 display_shared: Annotated[ 65 bool, 66 typer.Option( 67 "--display-shared", 68 is_flag=True, 69 help="Includes shared projects in output when true.", 70 ), 71 ] = False, 72 gitlab_host: Annotated[ 73 Optional[str], 74 typer.Option( 75 "--gitlab-host", 76 help="Base URL of GitLab instance. Overrides ENV GITLAB_HOST.", 77 ), 78 ] = None, 79 group_api_key: Annotated[ 80 Optional[str], 81 typer.Option( 82 "--group-api-key", 83 help="API-Key(s) as JSON-string or simple string. Overrides ENV GROUP_API_KEY.", 84 ), 85 ] = None, 86): 87 """Store common CLI params into context object.""" 88 89 # Build Settings instance directly from Typer params 90 settings = Settings( 91 sort=sort, 92 table_config=table_config, 93 log_level=log_level, 94 output=str(output), 95 debug=debug, 96 display_shared=display_shared, 97 gitlab_host=gitlab_host, 98 group_api_key=group_api_key, 99 ) 100 Settings.set_singleton(settings) 101 if debug: 102 logger.setLevel("DEBUG") 103 ctx.obj = { 104 "settings": settings, 105 "format": output, 106 "sort": sort, 107 }
108 109
[docs] 110@app.command() 111def run(ctx: typer.Context): # noqa: D401 112 """Run the overview pipeline with the provided options.""" 113 settings: Settings = ctx.obj["settings"] # type: ignore[index] 114 logger.info("Running GitLab Overviewer with host %s", settings.gitlab_host) 115 116 # Table config loading (with fallback) 117 table_config_path = settings.table_config or "table_config.yaml" 118 try: 119 with open(table_config_path, "r", encoding="utf-8") as f: 120 table_config = yaml.safe_load(f) 121 except Exception as e: 122 logger.warning("Could not load table_config.yaml: %s", e) 123 table_config = None 124 125 if table_config is None or "columns" not in table_config: 126 logger.warning("No valid table column configuration found, using defaults.") 127 table_config = { 128 "columns": [ 129 {"key": "repo", "label": "Repository", "visible": True}, 130 {"key": "type", "label": "Typ", "visible": True}, 131 {"key": "priority", "label": "Wichtigkeit", "visible": True}, 132 {"key": "urgency", "label": "Dringlichkeit", "visible": True}, 133 {"key": "date", "label": "Letzte Aktivität", "visible": True}, 134 {"key": "supervisors", "label": "Betreuung/Autoren", "visible": True}, 135 {"key": "version", "label": "Version", "visible": False}, 136 {"key": "status", "label": "Status", "visible": True}, 137 {"key": "todos", "label": "Todos", "visible": True}, 138 ], 139 } 140 141 if settings.debug: 142 logger.debug("Loaded table column configuration:") 143 logger.debug(yaml.dump(table_config, allow_unicode=True)) 144 145 # Data collection 146 collector = DataCollector() 147 overview_rows = collector.collect() 148 149 # Sort the overview data 150 sort_arg = ctx.obj.get("sort") 151 sorted_overviewdata = sort_overview(overview_rows, table_config, sort_arg) 152 153 output_format = ctx.obj["format"] 154 if output_format == OutputFormat.markdown: 155 rendered = MarkdownRenderer(table_config).render(overview_rows) 156 # Write to Overview.md in the project root 157 with open("Overview.md", "w", encoding="utf-8") as f: 158 f.write(rendered) 159 logger.info("Markdown overview written to Overview.md") 160 elif output_format == OutputFormat.quarto: 161 renderer = QuartoRenderer(table_config) 162 renderer.write_files(sorted_overviewdata, base_dir="quarto") 163 logger.info("Quarto .qmd files written to 'quarto/' directory.") 164 else: 165 raise ValueError(f"Unknown output format: {output_format}")
166 167
[docs] 168@app.command() 169def overview(ctx: typer.Context): 170 """Collect and sort overview data using new pipeline.""" 171 settings: Settings = ctx.obj["settings"] 172 sort_arg = ctx.obj["sort"] 173 table_config_path = settings.table_config 174 with open(table_config_path, "r", encoding="utf-8") as f: 175 table_config = yaml.safe_load(f) 176 collector = DataCollector() 177 overview_rows = collector.collect() 178 179 # Sort the overview data 180 sort_arg = ctx.obj.get("sort") 181 sorted_overviewdata = sort_overview(overview_rows, table_config, sort_arg) 182 183 output_format = ctx.obj["format"] 184 if output_format == OutputFormat.markdown: 185 rendered = MarkdownRenderer(table_config).render(overview_rows) 186 # Write to Overview.md in the project root 187 with open("Overview.md", "w", encoding="utf-8") as f: 188 f.write(rendered) 189 logger.info("Markdown overview written to Overview.md") 190 elif output_format == OutputFormat.quarto: 191 renderer = QuartoRenderer(table_config) 192 renderer.write_files(sorted_overviewdata, base_dir="quarto") 193 logger.info("Quarto .qmd files written to 'quarto/' directory.") 194 else: 195 raise ValueError(f"Unknown output format: {output_format}")
196 197 198if __name__ == "__main__": # pragma: no cover 199 app()