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

1""" 

2Proto CLI - gRPC Proto 파일 관리 도구. 

3 

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""" 

13 

14from __future__ import annotations 

15 

16import argparse 

17import sys 

18from pathlib import Path 

19 

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 

24 

25 

26def get_repo_root() -> Path: 

27 """저장소 루트 디렉터리 찾기""" 

28 # CLI가 패키지로 설치된 경우 

29 current = Path.cwd() 

30 

31 # grpc-protos 디렉터리 찾기 

32 for parent in [current, *current.parents]: 

33 if (parent / "protos").exists() and (parent / "buf.yaml").exists(): 

34 return parent 

35 

36 # 찾지 못한 경우 현재 디렉터리 사용 

37 return current 

38 

39 

40def show_interactive_menu(config: ProtoConfig) -> int: 

41 """대화형 메뉴를 표시하고 사용자 선택을 처리합니다.""" 

42 from rich.prompt import Prompt 

43 

44 print_header("🔧 MySingle Proto CLI") 

45 

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") 

54 

55 choice = Prompt.ask( 

56 "명령을 선택하세요", choices=["1", "2", "3", "4", "5", "h", "q"], default="q" 

57 ) 

58 

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 

81 

82 args = argparse.Namespace() 

83 return info.execute(args, config) 

84 

85 return 0 

86 

87 

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 # 코드 생성 

101 

102더 자세한 정보: 

103 GitHub: https://github.com/Br0therDan/grpc-protos 

104 """, 

105 ) 

106 

107 parser.add_argument( 

108 "--services-root", 

109 type=Path, 

110 help="서비스 루트 디렉터리 경로 (기본값: ../services)", 

111 ) 

112 

113 # 서브커맨드 

114 subparsers = parser.add_subparsers(dest="command", help="사용 가능한 명령") 

115 

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) 

123 

124 # status 명령 

125 status_parser = subparsers.add_parser( 

126 "status", 

127 help="서비스별 proto 파일 현황 확인", 

128 description="각 서비스의 proto 파일 개수와 경로를 테이블 형식으로 출력합니다.", 

129 ) 

130 status.setup_parser(status_parser) 

131 

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) 

139 

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) 

147 

148 # info 명령 

149 info_parser = subparsers.add_parser( 

150 "info", 

151 help="패키지 버전 및 상태 정보 확인", 

152 description="현재 mysingle 패키지 버전과 Git 상태를 확인합니다.", 

153 ) 

154 info.setup_parser(info_parser) 

155 

156 # TODO: 추가 명령어 구현 예정 

157 # - pr: Pull Request 생성 자동화 

158 # - diff: Proto 변경사항 시각화 

159 

160 return parser 

161 

162 

163def main(argv: list[str] | None = None) -> int: 

164 """CLI 메인 함수""" 

165 parser = build_parser() 

166 args = parser.parse_args(argv) 

167 

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 

175 

176 # 명령이 지정되지 않은 경우 대화형 모드 

177 if not args.command: 

178 return show_interactive_menu(config) 

179 

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 

204 

205 

206if __name__ == "__main__": 

207 sys.exit(main())