Refactor monolithic CLI into modular architecture#138
Conversation
There was a problem hiding this comment.
Pull request overview
Refactors the previously monolithic gitHappens.py CLI into a githappens/ package with separated modules for configuration, Git/GitLab helpers, interactive prompts, and command workflows, while keeping gitHappens.py as a backward-compatible entrypoint (Fixes #116).
Changes:
- Introduces a modular
githappenspackage withmain.pyrouting andcommands/workflows. - Extracts GitLab API calls, config/template loading, and git utilities into dedicated modules.
- Adds a small unittest suite covering the new module boundaries and basic CLI routing.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_main_cli.py | Adds routing tests for CLI entry (open, review, report). |
| tests/test_gitlab_api.py | Adds unit tests validating GitLab command construction for issue/branch creation. |
| tests/test_git_utils.py | Adds tests for git remote parsing behavior. |
| tests/test_entrypoint.py | Verifies gitHappens.py exposes a callable main. |
| tests/test_config_templates.py | Adds tests for config loading and template selection helpers. |
| tests/init.py | Initializes test package. |
| README.md | Documents the new modular package structure. |
| githappens/init.py | Declares the new package. |
| githappens/main.py | New CLI entry/router logic (replaces monolithic script body). |
| githappens/config.py | Centralizes config loading and derived constants. |
| githappens/templates.py | Centralizes templates/reviewers/production mappings loading. |
| githappens/git_utils.py | Extracts local git helper utilities and commit summary logic. |
| githappens/gitlab_api.py | Extracts GitLab API + glab CLI interactions. |
| githappens/interactive.py | Extracts inquirer-based interactive prompting. |
| githappens/commands/init.py | Declares commands package. |
| githappens/commands/create_issue.py | Implements issue creation workflow (milestone/iteration/epic selection, MR/branch creation). |
| githappens/commands/review.py | Implements review workflow (time tracking, reviewers, optional AI review, auto-merge). |
| githappens/commands/open_mr.py | Implements “open MR in browser” workflow. |
| githappens/commands/deploy.py | Implements “last deploy” lookup workflow using pipelines/jobs. |
| githappens/commands/report.py | Implements incident report workflow and time tracking. |
| gitHappens.py | Becomes a thin compatibility wrapper delegating to githappens.main.main. |
| docs/plans/2026-05-12-modular-architecture.md | Adds an implementation plan doc for the refactor. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if template_name == config.CUSTOM_TEMPLATE: | ||
| return {} | ||
| templates = templates if templates is not None else get_templates() | ||
| return next((template for template in templates if template["name"] == template_name), None) |
| def find_merge_request_id_by_branch(branch_name): | ||
| return get_merge_request_for_branch(branch_name)["iid"] | ||
|
|
| remote_url = subprocess.check_output( | ||
| ["git", "config", "--get", "remote.origin.url"], text=True | ||
| ).strip() | ||
| url = config.BASE_URL + "/" + remote_url.split(":")[1][:-4] | ||
| webbrowser.open(f"{url}/-/merge_requests/{merge_request_id}") |
| def get_current_issue_id(): | ||
| mr = get_merge_request_for_branch(git_utils.get_current_branch()) | ||
| return mr["description"].replace('"', "").replace("#", "").split()[1] | ||
|
|
| all_projects = gitlab_api.get_all_projects(project_link) | ||
| matching_id = None | ||
| for project in all_projects: | ||
| if project.get("ssh_url_to_repo") == project_link: | ||
| matching_id = project.get("id") | ||
| break | ||
| return matching_id |
| "state_event=close", | ||
| ], | ||
| stdout=subprocess.PIPE, | ||
| stderr=subprocess.PIPE, |
| ] | ||
|
|
||
| try: | ||
| subprocess.run(time_tracking_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| cmd = ( | ||
| f'git log --since={two_weeks_ago} --format="%ad - %ae - %s" ' | ||
| '--date=short | grep -v "Merge branch"' | ||
| ) | ||
| if config.DEVELOPER_EMAIL: | ||
| cmd = f"{cmd} | grep {config.DEVELOPER_EMAIL}" | ||
| try: | ||
| output = subprocess.check_output( | ||
| cmd, shell=True, text=True, stderr=subprocess.DEVNULL, universal_newlines=True | ||
| ).strip() |
| ## Project structure | ||
|
|
||
| The executable `gitHappens.py` is a small compatibility wrapper around the | ||
| `githappens` package: | ||
|
|
||
| - `githappens/main.py` handles CLI argument parsing and command routing. | ||
| - `githappens/config.py` loads `configs/config.ini`. | ||
| - `githappens/templates.py` loads issue templates and reviewer settings. | ||
| - `githappens/gitlab_api.py` contains GitLab API and `glab` interactions. | ||
| - `githappens/git_utils.py` contains local Git helpers. | ||
| - `githappens/interactive.py` contains terminal prompts. | ||
| - `githappens/commands/` contains command-specific workflows. | ||
|
|
||
| ### Project selection |
| @@ -0,0 +1,92 @@ | |||
| # GitHappens Modular Architecture Implementation Plan | |||
|
|
|||
| > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. | |||
1a3c44e to
a02f2f1
Compare
|
Updated the branch to address the Copilot review points:\n\n- return an empty template setting for unknown templates\n- handle missing MRs/projects/active milestones/active iterations gracefully\n- parse both SSH and HTTPS Git remotes for opening MRs\n- extract issue references with a regex\n- surface subprocess failures in report/issue close flows\n- avoid shell interpolation in recent commit summary filtering\n- fixed README heading hierarchy and removed the tool-specific plan doc\n\nFresh verification:\n- |
|
Updated this PR with additional command-module regression coverage so the extracted New coverage added:
Fresh verification after the update:
|
Summary
gitHappens.pyas the backward-compatible executable wrapper.githappens/main.py.Fixes #116
Verification
python3 -m unittest discover -v(12 tests)python3 gitHappens.py --helpgit diff --check HEAD~1..HEAD