Coverage for src / mysingle / cli / protos / __main__.py: 0%
95 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-02 00:58 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-02 00:58 +0900
1"""
2Proto CLI - gRPC Proto 파일 관리 도구.
4사용법:
5 mysingle-proto init # 저장소 초기화 및 submodule 구성
6 mysingle-proto status # 서비스별 proto 파일 현황
7 mysingle-proto generate # 코드 생성
8 mysingle-proto validate # Proto 파일 검증
9 mysingle-proto info # 패키지 버전 및 상태 정보
10 mysingle-proto # 대화형 모드
11 mysingle-proto --help # 도움말
12"""
14from __future__ import annotations
16import argparse
17import sys
18from pathlib import Path
20from ..utils import console, print_error, print_header, print_info
21from .commands import generate, info, init, status, validate
22from .models import ProtoConfig
23from .utils import LogLevel, log
26def get_repo_root() -> Path:
27 """저장소 루트 디렉터리 찾기"""
28 # CLI가 패키지로 설치된 경우
29 current = Path.cwd()
31 # grpc-protos 디렉터리 찾기
32 for parent in [current, *current.parents]:
33 if (parent / "protos").exists() and (parent / "buf.yaml").exists():
34 return parent
36 # 찾지 못한 경우 현재 디렉터리 사용
37 return current
40def show_interactive_menu(config: ProtoConfig) -> int:
41 """대화형 메뉴를 표시하고 사용자 선택을 처리합니다."""
42 from rich.prompt import Prompt
44 print_header("🔧 MySingle Proto CLI")
46 console.print("[cyan]사용 가능한 명령:[/cyan]\n")
47 console.print(" [green]1.[/green] init - 저장소 초기화 및 환경 확인")
48 console.print(" [green]2.[/green] status - 서비스별 proto 파일 현황")
49 console.print(" [green]3.[/green] generate - Python gRPC 스텁 생성")
50 console.print(" [green]4.[/green] validate - Proto 파일 검증")
51 console.print(" [green]5.[/green] info - 패키지 버전 및 상태 정보")
52 console.print(" [green]h.[/green] help - 도움말 표시")
53 console.print(" [green]q.[/green] quit - 종료\n")
55 choice = Prompt.ask(
56 "명령을 선택하세요", choices=["1", "2", "3", "4", "5", "h", "q"], default="q"
57 )
59 if choice == "q":
60 print_info("종료합니다.")
61 return 0
62 elif choice == "h":
63 # Show help
64 build_parser().print_help()
65 return 0
66 elif choice == "1":
67 # init command
68 return init.execute_interactive(config)
69 elif choice == "2":
70 # status command
71 return status.execute_interactive(config)
72 elif choice == "3":
73 # generate command
74 return generate.execute_interactive(config)
75 elif choice == "4":
76 # validate command
77 return validate.execute_interactive(config)
78 elif choice == "5":
79 # info command
80 import argparse
82 args = argparse.Namespace()
83 return info.execute(args, config)
85 return 0
88def build_parser() -> argparse.ArgumentParser:
89 """CLI 파서 생성"""
90 parser = argparse.ArgumentParser(
91 prog="proto-cli",
92 description="🔧 MySingle Quant - gRPC Proto 파일 관리 도구",
93 formatter_class=argparse.RawDescriptionHelpFormatter,
94 epilog="""
95사용 예시:
96 %(prog)s init # 저장소 환경 확인 / Submodule 구성
97 %(prog)s status # 서비스별 proto 현황
98 %(prog)s status -v # 상세 파일 목록 포함
99 %(prog)s validate --fix # Proto 검증 및 자동 수정
100 %(prog)s generate # 코드 생성
102더 자세한 정보:
103 GitHub: https://github.com/Br0therDan/grpc-protos
104 """,
105 )
107 parser.add_argument(
108 "--services-root",
109 type=Path,
110 help="서비스 루트 디렉터리 경로 (기본값: ../services)",
111 )
113 # 서브커맨드
114 subparsers = parser.add_subparsers(dest="command", help="사용 가능한 명령")
116 # init 명령
117 init_parser = subparsers.add_parser(
118 "init",
119 help="저장소 초기화 및 환경 확인",
120 description="grpc-protos 저장소를 초기화하고 필수 도구(Git, Buf) 설치를 확인합니다.",
121 )
122 init.setup_parser(init_parser)
124 # status 명령
125 status_parser = subparsers.add_parser(
126 "status",
127 help="서비스별 proto 파일 현황 확인",
128 description="각 서비스의 proto 파일 개수와 경로를 테이블 형식으로 출력합니다.",
129 )
130 status.setup_parser(status_parser)
132 # generate 명령
133 generate_parser = subparsers.add_parser(
134 "generate",
135 help="Buf를 사용하여 Python gRPC 스텁 생성",
136 description="proto 파일로부터 Python 코드를 생성하고 import 경로를 수정합니다.",
137 )
138 generate.setup_parser(generate_parser)
140 # validate 명령
141 validate_parser = subparsers.add_parser(
142 "validate",
143 help="Proto 파일 검증 (lint, format, breaking)",
144 description="Buf를 사용하여 proto 파일의 린트, 포맷, Breaking change를 검사합니다.",
145 )
146 validate.setup_parser(validate_parser)
148 # info 명령
149 info_parser = subparsers.add_parser(
150 "info",
151 help="패키지 버전 및 상태 정보 확인",
152 description="현재 mysingle 패키지 버전과 Git 상태를 확인합니다.",
153 )
154 info.setup_parser(info_parser)
156 # TODO: 추가 명령어 구현 예정
157 # - pr: Pull Request 생성 자동화
158 # - diff: Proto 변경사항 시각화
160 return parser
163def main(argv: list[str] | None = None) -> int:
164 """CLI 메인 함수"""
165 parser = build_parser()
166 args = parser.parse_args(argv)
168 # 저장소 설정
169 try:
170 repo_root = get_repo_root()
171 config = ProtoConfig.from_repo_root(repo_root, services_root=args.services_root)
172 except Exception as e:
173 log(f"설정 로드 실패: {e}", LogLevel.ERROR)
174 return 1
176 # 명령이 지정되지 않은 경우 대화형 모드
177 if not args.command:
178 return show_interactive_menu(config)
180 # 명령 실행
181 try:
182 if args.command == "init":
183 return init.execute(args, config)
184 elif args.command == "status":
185 return status.execute(args, config)
186 elif args.command == "generate":
187 return generate.execute(args, config)
188 elif args.command == "validate":
189 return validate.execute(args, config)
190 elif args.command == "info":
191 return info.execute(args, config)
192 else:
193 log(f"알 수 없는 명령: {args.command}", LogLevel.ERROR)
194 parser.print_help()
195 return 1
196 except KeyboardInterrupt:
197 print_error("\n작업이 사용자에 의해 중단되었습니다.")
198 return 130
199 except Exception as e:
200 print_error(f"오류 발생: {e}")
201 if "--debug" in sys.argv:
202 raise
203 return 1
206if __name__ == "__main__":
207 sys.exit(main())