diff --git a/src/sc/branching/branching.py b/src/sc/branching/branching.py index 7320428..230356d 100644 --- a/src/sc/branching/branching.py +++ b/src/sc/branching/branching.py @@ -26,10 +26,12 @@ from .commands.clean import Clean from .commands.command import Command from .commands.finish import Finish, FinishOperationError +from .commands.group import GroupShow from .commands.init import Init from .commands.list import List from .commands.pull import Pull from .commands.push import Push +from .commands.show import ShowBranch, ShowLog, ShowRepoFlowConfig from .commands.start import Start from .commands.status import Status from .commands.tag import TagCheck, TagCreate, TagList, TagPush, TagRm, TagShow @@ -198,6 +200,38 @@ def tag_check(tag: str, run_dir: Path = Path.cwd()): project_type ) + @staticmethod + def show_branch(run_dir: Path = Path.cwd()): + top_dir, project_type = detect_project(run_dir) + run_command_by_project_type( + ShowBranch(top_dir), + project_type + ) + + @staticmethod + def show_repo_flow_config(run_dir: Path = Path.cwd()): + top_dir, project_type = detect_project(run_dir) + run_command_by_project_type( + ShowRepoFlowConfig(top_dir), + project_type + ) + + @staticmethod + def show_log(run_dir: Path = Path.cwd()): + top_dir, project_type = detect_project(run_dir) + run_command_by_project_type( + ShowLog(top_dir), + project_type + ) + + @staticmethod + def group_show(group: str | None, run_dir: Path = Path.cwd()): + top_dir, project_type = detect_project(run_dir) + run_command_by_project_type( + GroupShow(top_dir, group), + project_type + ) + def detect_project(run_dir: Path) -> tuple[Path | ProjectType]: if root := RepoLibrary.get_repo_root_dir(run_dir): diff --git a/src/sc/branching/commands/common.py b/src/sc/branching/commands/common.py index 30d2686..ccf2d62 100644 --- a/src/sc/branching/commands/common.py +++ b/src/sc/branching/commands/common.py @@ -25,4 +25,4 @@ def get_alt_branch_name(branch: Branch, project: ProjectElementInterface) -> str return None def resolve_project_branch_name(branch: Branch, project: ProjectElementInterface) -> str: - return get_alt_branch_name(branch, project) or branch.name \ No newline at end of file + return get_alt_branch_name(branch, project) or branch.name diff --git a/src/sc/branching/commands/group.py b/src/sc/branching/commands/group.py new file mode 100644 index 0000000..1108ebf --- /dev/null +++ b/src/sc/branching/commands/group.py @@ -0,0 +1,94 @@ +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Module for `sc group` functionality.""" + +from dataclasses import dataclass +import logging +import subprocess + +from git import Repo +from sc_manifest_parser import ProjectElementInterface, ScManifest + +from .command import Command + +logger = logging.getLogger(__name__) + +@dataclass +class GroupShow(Command): + """List groups or show information about a particular group.""" + group: str | None + + def run_git_command(self): + logger.error("`sc show group` must be ran inside a repo project!") + + def run_repo_command(self): + if self.group: + self._show_group_info() + else: + self._list_groups() + + def _show_group_info(self): + manifest = ScManifest.from_repo_root(self.top_dir / '.repo') + group_shown = False + for proj in manifest.projects: + project_groups = proj.groups + if not project_groups or self.group not in project_groups.split(","): + continue + + group_shown = True + self._show_project(proj) + print() + logger.info("-" * 100) + + if not group_shown: + logger.warning(f"No project matching group `{self.group}` found!") + + def _list_groups(self): + manifest = ScManifest.from_repo_root(self.top_dir / '.repo') + groups = [] + for proj in manifest.projects: + project_groups = proj.groups + if project_groups: + groups.extend(project_groups.split(",")) + groups = self._remove_duplicates(groups) + for group in groups: + logger.info(f"[{group}]") + + def _remove_duplicates(self, target_list: list) -> list: + return(list(dict.fromkeys(target_list))) + + def _show_project(self, proj: ProjectElementInterface): + """Show information pertaining to a particular project.""" + proj_dir = self.top_dir / proj.path + logger.info(f"Project: {proj_dir}") + logger.info( + f"Lock Status: [bold yellow]\[{proj.lock_status or 'NORMAL'}][/]", + extra={"markup": True} + ) + + repo = Repo(proj_dir) + if repo.remotes: + remote = repo.remotes[0] + url = next(remote.urls) + remote_status = f"{remote.name} {url}" + else: + remote_status = "No remotes configured" + + logger.info(f"Remote Status: {remote_status}") + + subprocess.run( + ["git", "branch", "-vv", "--color=always"], + cwd=proj_dir, + check=False + ) diff --git a/src/sc/branching/commands/show.py b/src/sc/branching/commands/show.py new file mode 100644 index 0000000..f4d6001 --- /dev/null +++ b/src/sc/branching/commands/show.py @@ -0,0 +1,122 @@ +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Commands to display information about a repo or sc project.""" + +from dataclasses import dataclass +import logging +from pathlib import Path +import subprocess + +from git import Repo +from sc_manifest_parser import ProjectElementInterface, ScManifest + +from .command import Command + +logger = logging.getLogger(__name__) + +@dataclass +class ShowBranch(Command): + """Show branch information for all repositories.""" + def run_git_command(self): + self._show_branch(self.top_dir) + + def run_repo_command(self): + manifest = ScManifest.from_repo_root(self.top_dir / '.repo') + for proj in manifest.projects: + self._show_project(proj) + print() + logger.info("-" * 100) + + def _show_project(self, proj: ProjectElementInterface): + """Show information pertaining to a particular project.""" + proj_dir = self.top_dir / proj.path + logger.info(f"Project: {proj_dir}") + logger.info( + f"Lock Status: \[[bold yellow]{proj.lock_status or 'NORMAL'}[/]]", + extra={"markup": True} + ) + + if proj.groups: + groups = proj.groups.split(",") + group_str = " ".join([f"\[[bold yellow]{g}[/]]" for g in groups]) + else: + group_str = "\[[red bold]No Groups[/]]" + + logger.info(f"Groups: {group_str}", extra={"markup": True}) + + self._show_branch(proj_dir) + + def _show_branch(self, repo_dir: Path): + """Show remotes and branches of a git repo.""" + repo = Repo(repo_dir) + if repo.remotes: + remote = repo.remotes[0] + url = next(remote.urls) + remote_status = f"{remote.name} {url}" + else: + remote_status = "No remotes configured" + + logger.info(f"Remote Status: {remote_status}") + + subprocess.run( + ["git", "branch", "-vv", "--color=always"], + cwd=repo_dir, + check=False + ) + +@dataclass +class ShowRepoFlowConfig(Command): + """Show git flow config for all projects.""" + def run_git_command(self): + logger.error("`sc show repo_flow_config` must be ran inside a repo project!") + + def run_repo_command(self): + manifest = ScManifest.from_repo_root(self.top_dir / '.repo') + for proj in manifest.projects: + proj_dir = self.top_dir / proj.path + logger.info(f"Repo Flow Config: {proj_dir}") + result = subprocess.run( + ["git", "config", "--list"], + cwd=proj_dir, + capture_output=True, + text=True, + check=False + ) + + for line in result.stdout.splitlines(): + if "gitflow" in line: + print(line) + + print() + logger.info("-" * 100) + +@dataclass +class ShowLog(Command): + """Display most recent commit on all repositories.""" + def run_git_command(self): + self._show_log(self.top_dir) + + def run_repo_command(self): + manifest = ScManifest.from_repo_root(self.top_dir / '.repo') + for proj in manifest.projects: + logger.info(f"Project: {self.top_dir / proj.path}") + self._show_log(self.top_dir / proj.path) + logger.info("-" * 100) + + def _show_log(self, repo_dir: Path): + subprocess.run( + ["git", "log", "--pretty=oneline", "-n1"], + cwd=repo_dir, + check=False + ) diff --git a/src/sc/branching/commands/tag.py b/src/sc/branching/commands/tag.py index 8838ed1..fdf46d3 100644 --- a/src/sc/branching/commands/tag.py +++ b/src/sc/branching/commands/tag.py @@ -67,7 +67,7 @@ def run_git_command(self): self._list_tags(self.top_dir) def run_repo_command(self): - logger.info(f"Tags in mainfest: {self.top_dir / '.repo' / 'manifests'}") + logger.info(f"Tags in manifest: {self.top_dir / '.repo' / 'manifests'}") self._list_tags(self.top_dir / '.repo' / 'manifests') def _list_tags(self, repo_path: Path): diff --git a/src/sc/branching_cli.py b/src/sc/branching_cli.py index 5a85195..003c6e8 100644 --- a/src/sc/branching_cli.py +++ b/src/sc/branching_cli.py @@ -335,5 +335,41 @@ def check(tag): """Check if a tag exists on all non READ_ONLY repos.""" SCBranching.tag_check(tag) +@cli.group() +def show(): + """sc show commands.""" + pass + +@show.command() +def branch(): + """Show the current status of branching.""" + SCBranching.show_branch() + +@show.command() +def repo_flow_config(): + """Show git flow config for all projects.""" + SCBranching.show_repo_flow_config() + +@show.command() +def log(): + SCBranching.show_log() + +@show.command() +@click.argument("tag") +def tag(tag): + """Show information about a tag.""" + SCBranching.tag_show(tag) + +@show.command() +def tags(): + """List tags on the manifest.""" + SCBranching.tag_list() + +@show.command() +@click.argument("group", required=False) +def group(group): + """List groups or show information about a group.""" + SCBranching.group_show(group) + if __name__ == '__main__': cli()