Coverage for src/somesy/cli/sync.py: 100%

27 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-11-03 13:13 +0000

1"""Sync command for somesy.""" 

2 

3import logging 

4from pathlib import Path 

5 

6import typer 

7 

8from somesy.commands import sync as sync_command 

9from somesy.commands.sync import SYNC_TARGETS 

10from somesy.core.models import SomesyInput 

11 

12from .util import ( 

13 existing_dir_arg_config, 

14 existing_file_arg_config, 

15 file_arg_config, 

16 resolved_somesy_input, 

17 wrap_exceptions, 

18) 

19 

20logger = logging.getLogger("somesy") 

21 

22app = typer.Typer() 

23 

24 

25@app.callback(invoke_without_command=True) 

26@wrap_exceptions 

27def sync( 

28 input_file: Path | None = typer.Option( 

29 None, 

30 "--input-file", 

31 "-i", 

32 help="Somesy input file path (default: discovered)", 

33 **file_arg_config, 

34 ), 

35 no_sync_pyproject: bool | None = typer.Option( 

36 None, 

37 "--no-sync-pyproject", 

38 "-P", 

39 help="Do not sync pyproject.toml file (default: False)", 

40 ), 

41 pyproject_file: list[Path] | None = typer.Option( 

42 None, 

43 "--pyproject-file", 

44 "-p", 

45 help="Existing pyproject.toml file path(s) (default: pyproject.toml)", 

46 **existing_file_arg_config, 

47 ), 

48 no_sync_package_json: bool | None = typer.Option( 

49 None, 

50 "--no-sync-package-json", 

51 "-J", 

52 help="Do not sync package.json file (default: False)", 

53 ), 

54 package_json_file: list[Path] | None = typer.Option( 

55 None, 

56 "--package-json-file", 

57 "-j", 

58 help="Existing package.json file path(s) (default: package.json)", 

59 **existing_file_arg_config, 

60 ), 

61 no_sync_julia: bool | None = typer.Option( 

62 None, 

63 "--no-sync-julia", 

64 "-L", 

65 help="Do not sync Project.toml (Julia) file (default: False)", 

66 ), 

67 julia_file: list[Path] | None = typer.Option( 

68 None, 

69 "--julia-file", 

70 "-l", 

71 help="Custom Project.toml (Julia) file path(s) (default: Project.toml)", 

72 **existing_file_arg_config, 

73 ), 

74 no_sync_fortran: bool | None = typer.Option( 

75 None, 

76 "--no-sync-fortran", 

77 "-F", 

78 help="Do not sync fpm.toml (Fortran) file (default: False)", 

79 ), 

80 fortran_file: list[Path] | None = typer.Option( 

81 None, 

82 "--fortran-file", 

83 "-f", 

84 help="Custom fpm.toml (Fortran) file path(s) (default: fpm.toml)", 

85 **existing_file_arg_config, 

86 ), 

87 no_sync_pom_xml: bool | None = typer.Option( 

88 None, 

89 "--no-sync-pomxml", 

90 "-X", 

91 help="Do not sync pom.xml (Java Maven) file (default: False)", 

92 ), 

93 pom_xml_file: list[Path] | None = typer.Option( 

94 None, 

95 "--pomxml-file", 

96 "-x", 

97 help="Custom pom.xml (Java Maven) file path(s) (default: pom.xml)", 

98 **existing_file_arg_config, 

99 ), 

100 no_sync_mkdocs: bool | None = typer.Option( 

101 None, 

102 "--no-sync-mkdocs", 

103 "-D", 

104 help="Do not sync mkdocs.yml file (default: False)", 

105 ), 

106 mkdocs_file: list[Path] | None = typer.Option( 

107 None, 

108 "--mkdocs-file", 

109 "-d", 

110 help="Custom mkdocs.yml file path(s) (default: mkdocs.yml)", 

111 **existing_file_arg_config, 

112 ), 

113 no_sync_rust: bool | None = typer.Option( 

114 None, 

115 "--no-sync-rust", 

116 "-R", 

117 help="Do not sync Cargo.toml file (default: False)", 

118 ), 

119 rust_file: list[Path] | None = typer.Option( 

120 None, 

121 "--rust-file", 

122 "-r", 

123 help="Custom Cargo.toml file path(s) (default: Cargo.toml)", 

124 **existing_file_arg_config, 

125 ), 

126 no_sync_cff: bool | None = typer.Option( 

127 None, 

128 "--no-sync-cff", 

129 "-C", 

130 help="Do not sync CITATION.cff file (default: False)", 

131 ), 

132 cff_file: list[Path] | None = typer.Option( 

133 None, 

134 "--cff-file", 

135 "-c", 

136 help="CITATION.cff file path(s) (default: CITATION.cff)", 

137 **file_arg_config, 

138 ), 

139 no_sync_codemeta: bool | None = typer.Option( 

140 None, 

141 "--no-sync-codemeta", 

142 "-M", 

143 help="Do not sync codemeta.json file (default: False)", 

144 ), 

145 codemeta_file: list[Path] | None = typer.Option( 

146 None, 

147 "--codemeta-file", 

148 "-m", 

149 help="Custom codemeta.json file path(s) (default: codemeta.json)", 

150 **file_arg_config, 

151 ), 

152 merge_codemeta: bool | None = typer.Option( 

153 False, 

154 "--merge/--overwrite", 

155 help="Merge codemeta.json with with an existing codemeta.json file (default: False)", 

156 ), 

157 pass_validation: bool | None = typer.Option( 

158 False, 

159 "--pass-validation", 

160 "-V", 

161 help="Allow incomplete input and pass output validation (default: False)", 

162 ), 

163 packages: list[Path] | None = typer.Option( 

164 None, 

165 "--packages", 

166 "-k", 

167 help="Packages (subfolders) for monorepos with their own somesy config.", 

168 **existing_dir_arg_config, 

169 ), 

170): 

171 """Sync project metadata input with metadata files.""" 

172 somesy_input = resolved_somesy_input( 

173 input_file=input_file, 

174 no_sync_cff=no_sync_cff, 

175 cff_file=cff_file, 

176 no_sync_pyproject=no_sync_pyproject, 

177 pyproject_file=pyproject_file, 

178 no_sync_package_json=no_sync_package_json, 

179 package_json_file=package_json_file, 

180 no_sync_codemeta=no_sync_codemeta, 

181 codemeta_file=codemeta_file, 

182 no_sync_julia=no_sync_julia, 

183 julia_file=julia_file, 

184 no_sync_fortran=no_sync_fortran, 

185 fortran_file=fortran_file, 

186 no_sync_pom_xml=no_sync_pom_xml, 

187 pom_xml_file=pom_xml_file, 

188 no_sync_mkdocs=no_sync_mkdocs, 

189 mkdocs_file=mkdocs_file, 

190 no_sync_rust=no_sync_rust, 

191 rust_file=rust_file, 

192 merge_codemeta=merge_codemeta, 

193 pass_validation=pass_validation, 

194 packages=packages, 

195 ) 

196 

197 run_sync(somesy_input) 

198 

199 

200def run_sync(somesy_input: SomesyInput): 

201 """Write log messages and run synchronization based on passed config.""" 

202 conf = somesy_input.config 

203 logger.info("[bold green]Synchronizing project metadata...[/bold green]") 

204 logger.info("Files to sync:") 

205 for target in SYNC_TARGETS: 

206 if not getattr(conf, f"no_sync_{target.key}"): 

207 files = getattr(conf, f"{target.key}_file") 

208 gap = "\n" if target.log_gap else "" 

209 logger.info( 

210 f" - [italic]{target.label}[/italic]:\t[grey]{files}[/grey]{gap}" 

211 ) 

212 # ---- 

213 if conf.pass_validation: 

214 logger.info("[bold yellow]Passing validation of metadata files.[/bold yellow]") 

215 sync_command(somesy_input) 

216 # ---- 

217 logger.info("[bold green]Metadata synchronization completed.[/bold green]")