Coverage for  / home / runner / work / bijux-cli / bijux-cli / src / bijux_cli / cli / commands / config / export.py: 100%

33 statements  

« prev     ^ index     » next       coverage.py v7.13.2, created at 2026-01-26 17:59 +0000

1# SPDX-License-Identifier: Apache-2.0 

2# Copyright © 2025 Bijan Mousavi 

3 

4"""Implements the `config export` subcommand for the Bijux CLI. 

5 

6This module contains the logic for exporting the application's entire current 

7configuration to a specified destination, which can be a file or standard 

8output. The output format can be explicitly set to 'env', 'json', or 'yaml', 

9or it can be inferred from the destination file's extension. 

10 

11Output Contract: 

12 * Success (to file): `{"status": "exported", "file": str, "format": str}` 

13 * Success (to stdout): The raw exported configuration data is printed directly. 

14 * Error: `{"error": str, "code": int}` 

15 

16Exit Codes: 

17 * `0`: Success. 

18 * `1` or `2`: An error occurred during the export process, such as a file 

19 write error or invalid format request. 

20""" 

21 

22from __future__ import annotations 

23 

24import platform 

25 

26import typer 

27 

28from bijux_cli.cli.core.command import ( 

29 ascii_safe, 

30 new_run_command, 

31 validate_common_flags, 

32) 

33from bijux_cli.cli.core.constants import ( 

34 OPT_FORMAT, 

35 OPT_LOG_LEVEL, 

36 OPT_PRETTY, 

37 OPT_QUIET, 

38) 

39from bijux_cli.cli.core.help_text import ( 

40 HELP_FORMAT, 

41 HELP_LOG_LEVEL, 

42 HELP_NO_PRETTY, 

43 HELP_QUIET, 

44) 

45from bijux_cli.core.di import DIContainer 

46from bijux_cli.core.enums import ErrorType 

47from bijux_cli.core.errors import ConfigError 

48from bijux_cli.core.exit_policy import ExitIntentError 

49from bijux_cli.core.precedence import current_execution_policy, resolve_exit_intent 

50from bijux_cli.services.config.contracts import ConfigProtocol 

51 

52 

53def export_config( 

54 ctx: typer.Context, 

55 path: str = typer.Argument( 

56 ..., help="Destination file – use “-” to write to STDOUT" 

57 ), 

58 out_fmt: str = typer.Option( 

59 None, "--out-format", help="Force output format: env | json | yaml" 

60 ), 

61 quiet: bool = typer.Option(False, *OPT_QUIET, help=HELP_QUIET), 

62 fmt: str = typer.Option("json", *OPT_FORMAT, help=HELP_FORMAT), 

63 pretty: bool = typer.Option(True, OPT_PRETTY, help=HELP_NO_PRETTY), 

64 log_level: str = typer.Option("info", *OPT_LOG_LEVEL, help=HELP_LOG_LEVEL), 

65) -> None: 

66 """Exports the current configuration to a file or standard output. 

67 

68 This function writes all configuration key-value pairs to a specified 

69 destination. If the destination is a file path, a structured JSON/YAML 

70 confirmation message is printed to stdout upon success. If the destination 

71 is "-", the raw exported configuration is printed directly to stdout. 

72 

73 Args: 

74 ctx (typer.Context): The Typer context for the CLI. 

75 path (str): The destination file path, or "-" for standard output. 

76 out_fmt (str): The desired output format ('env', 'json', 'yaml'). If 

77 unspecified, it is inferred from the file extension. 

78 quiet (bool): If True, suppresses all output except for errors. 

79 confirmation payload (file export only). 

80 fmt (str): The format for the confirmation payload ("json" or "yaml"). 

81 pretty (bool): If True, pretty-prints the confirmation payload. log_level (str): Logging level for diagnostics. 

82 

83 Returns: 

84 None: 

85 

86 Raises: 

87 SystemExit: Always exits with a contract-compliant status code and 

88 payload, indicating success or detailing the error. 

89 """ 

90 command = "config export" 

91 effective = current_execution_policy() 

92 fmt_lower = validate_common_flags( 

93 fmt, 

94 command, 

95 effective.quiet, 

96 include_runtime=effective.include_runtime, 

97 log_level=effective.log_level, 

98 ) 

99 quiet = effective.quiet 

100 include_runtime = effective.include_runtime 

101 pretty = effective.pretty 

102 

103 config_svc = DIContainer.current().resolve(ConfigProtocol) 

104 

105 try: 

106 config_svc.export(path, out_fmt) 

107 except ConfigError as exc: 

108 code = 2 if getattr(exc, "http_status", 0) == 400 else 1 

109 intent = resolve_exit_intent( 

110 message=f"Failed to export config: {exc}", 

111 code=code, 

112 failure="export_failed", 

113 command=command, 

114 fmt=fmt_lower, 

115 quiet=quiet, 

116 include_runtime=include_runtime, 

117 error_type=ErrorType.INTERNAL, 

118 log_level=effective.log_level, 

119 ) 

120 raise ExitIntentError(intent) from exc 

121 

122 if path != "-": 

123 

124 def payload_builder(include_runtime: bool) -> dict[str, object]: 

125 """Builds the payload confirming a successful export to a file. 

126 

127 Args: 

128 include_runtime (bool): If True, includes Python and platform info. 

129 

130 Returns: 

131 ConfigExportPayload: The structured payload. 

132 """ 

133 payload: dict[str, object] = { 

134 "status": "exported", 

135 "file": path, 

136 "format": out_fmt or "auto", 

137 } 

138 if include_runtime: 

139 payload.update( 

140 { 

141 "python": ascii_safe( 

142 platform.python_version(), "python_version" 

143 ), 

144 "platform": ascii_safe(platform.platform(), "platform"), 

145 } 

146 ) 

147 return payload 

148 

149 new_run_command( 

150 command_name=command, 

151 payload_builder=payload_builder, 

152 quiet=quiet, 

153 fmt=fmt_lower, 

154 pretty=pretty, 

155 log_level=log_level, 

156 )