Coverage for src/cli/entrypoint.py: 57.34%

361 statements  

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

1#!/usr/bin/env python3 

2 

3# Standard libraries 

4from argparse import Namespace 

5from enum import Enum 

6from importlib.resources import files as resources_files 

7from pathlib import Path 

8from tempfile import _TemporaryFileWrapper as TempFile, NamedTemporaryFile 

9from time import sleep 

10from typing import List 

11 

12# Components 

13from ..features.argcomplete import Argcomplete 

14from ..features.commitizen import Commitizen 

15from ..features.gcil import Gcil 

16from ..features.git import Git 

17from ..features.mise import Mise 

18from ..features.precommit import PreCommit 

19from ..features.radiandevcore import RadianDevCore 

20from ..features.readme import Readme 

21from ..features.templates import Templates 

22from ..package.bundle import Bundle 

23from ..package.version import Version 

24from ..prints.colors import Colors 

25from ..system.commands import Commands 

26from ..system.platform import Platform 

27 

28# Entrypoint class, pylint: disable=too-few-public-methods,too-many-statements 

29class Entrypoint: 

30 

31 # Enumerations 

32 Result = Enum('Result', [ 

33 'SUCCESS', 

34 'FINALIZE', 

35 'ERROR', 

36 'CRITICAL', 

37 ]) 

38 

39 # CLI, pylint: disable=too-many-boolean-expressions,too-many-branches,too-many-locals 

40 @staticmethod 

41 def cli(options: Namespace, ) -> Result: 

42 

43 # Variables 

44 asset: str 

45 command_add: List[str] 

46 command_add_string: str 

47 command_commit: List[str] 

48 command_commit_string: str 

49 configurations_existed: bool = False 

50 configurations_revision: str = '' 

51 configurations_files: List[str] = [] 

52 data: str 

53 include_components: bool = False 

54 include_prefix: str 

55 include_remotes: bool = False 

56 include_sources: bool = False 

57 include_value: str 

58 local_ci_data: str 

59 local_template: str 

60 

61 # Configure pre-commit 

62 PreCommit.configure() 

63 

64 # List hooks 

65 if options.list: 

66 

67 # Detect hooks directory 

68 hooks_dir = Git.hooks_dir() 

69 

70 # List hooks directory 

71 if Path(hooks_dir).exists(): 

72 

73 # Detect hooks files 

74 hooks_files = Git.hooks_files() 

75 

76 # List hooks files 

77 if hooks_files: 

78 print(' ') 

79 print(f'{Colors.GREEN} ===[' 

80 f'{Colors.YELLOW} List hooks: ' 

81 f'{Colors.YELLOW_LIGHT}Hooks installed in ' 

82 f'{Colors.CYAN}"{hooks_dir}"' 

83 f'{Colors.GREEN} ]===' 

84 f'{Colors.RESET}') 

85 print(' ') 

86 for hook in hooks_files: 

87 print(f'{Colors.BOLD} - Hook: {Colors.RESET}{hook}') 

88 print(' ') 

89 Platform.flush() 

90 

91 # Missing hooks files 

92 else: 

93 print(' ') 

94 print(f'{Colors.GREEN} ===[' 

95 f'{Colors.YELLOW} List hooks: ' 

96 f'{Colors.YELLOW_LIGHT}No hooks installed in ' 

97 f'{Colors.CYAN}"{hooks_dir}"' 

98 f'{Colors.GREEN} ]===' 

99 f'{Colors.RESET}') 

100 print(' ') 

101 Platform.flush() 

102 

103 # Missing hooks directory 

104 else: 

105 print(' ') 

106 print(f'{Colors.GREEN} ===[' 

107 f'{Colors.YELLOW} List hooks: ' 

108 f'{Colors.YELLOW_LIGHT}No hooks directory found in ' 

109 f'{Colors.CYAN}"{hooks_dir}"' 

110 f'{Colors.GREEN} ]===' 

111 f'{Colors.RESET}') 

112 print(' ') 

113 Platform.flush() 

114 

115 # Prepare dependencies 

116 if options.install or options.configure or options.enable or options.autoupdate \ 

117 or options.clean or options.run: 

118 

119 # Install pre-commit dependency 

120 if options.install or not PreCommit.exists(): 

121 print(' ') 

122 print(f'{Colors.GREEN} ===[' 

123 f'{Colors.YELLOW} Install dependencies: ' 

124 f'{Colors.YELLOW_LIGHT}{PreCommit.TITLE}' 

125 f'{Colors.GREEN} ]===' 

126 f'{Colors.RESET}') 

127 print(' ') 

128 Platform.flush() 

129 PreCommit.dependencies() 

130 

131 # Install commitizen dependency 

132 if options.install or not Commitizen.exists() or not Commitizen.compatible(): 

133 print(' ') 

134 print(f'{Colors.GREEN} ===[' 

135 f'{Colors.YELLOW} Install dependencies: ' 

136 f'{Colors.YELLOW_LIGHT}{Commitizen.TITLE}' 

137 f'{Colors.GREEN} ]===' 

138 f'{Colors.RESET}') 

139 print(' ') 

140 Platform.flush() 

141 Commitizen.dependencies() 

142 

143 # Install argcomplete completion 

144 if options.install and not Argcomplete.configured(): 

145 print(' ') 

146 print(f'{Colors.GREEN} ===[' 

147 f'{Colors.YELLOW} Install bash completion: ' 

148 f'{Colors.YELLOW_LIGHT}argcomplete' 

149 f'{Colors.GREEN} ]===' 

150 f'{Colors.RESET}') 

151 print(' ') 

152 Platform.flush() 

153 Argcomplete.configure() 

154 if Platform.IS_TTY_STDIN: 

155 sleep(2) 

156 

157 # Install commitizen completion 

158 if options.install and not Commitizen.configured(): 

159 print(' ') 

160 print(f'{Colors.GREEN} ===[' 

161 f'{Colors.YELLOW} Install dependencies: ' 

162 f'{Colors.YELLOW_LIGHT}commitizen completion' 

163 f'{Colors.GREEN} ]===' 

164 f'{Colors.RESET}') 

165 print(' ') 

166 Platform.flush() 

167 commitizen_file: str = Commitizen.configure() 

168 print(f'commitizen completion enabled in: {commitizen_file}') 

169 if Platform.IS_TTY_STDIN: 

170 sleep(1) 

171 

172 # Bind specific configurations 

173 if options.config: 

174 Commitizen.set_configuration(options.config + Platform.PATH_SEPARATOR + 

175 Commitizen.CONFIGURATION_FILE) 

176 PreCommit.set_configuration(options.config + Platform.PATH_SEPARATOR + 

177 PreCommit.CONFIGURATION_FILE) 

178 

179 # Bind default configurations 

180 elif options.default: 

181 

182 # Bind configurations files, pylint: disable=consider-using-with 

183 commitizen_configuration: TempFile = NamedTemporaryFile( # type: ignore[type-arg] 

184 delete_on_close=True, 

185 suffix=Commitizen.CONFIGURATION_EXTENSION, 

186 ) 

187 precommit_configuration: TempFile = NamedTemporaryFile( # type: ignore[type-arg] 

188 delete_on_close=True, 

189 suffix=PreCommit.CONFIGURATION_EXTENSION, 

190 ) 

191 

192 # Bind configurations flags 

193 Commitizen.set_configuration(commitizen_configuration.name) 

194 PreCommit.set_configuration(precommit_configuration.name) 

195 

196 # Default pre-commit configuration 

197 with open( 

198 precommit_configuration.name, 

199 encoding='utf8', 

200 mode='w+t', 

201 ) as file: 

202 

203 # Acquire pre-commit configuration 

204 file.write(resources_files(Bundle.RESOURCES_ASSETS) \ 

205 .joinpath(PreCommit.CONFIGURATION_TEMPLATE) \ 

206 .read_text(encoding='utf8') \ 

207 .replace(PreCommit.REV_TEMPLATE, Version.revision())) 

208 file.flush() 

209 

210 # Default commitizen configuration 

211 with open( 

212 commitizen_configuration.name, 

213 encoding='utf8', 

214 mode='w+t', 

215 ) as file: 

216 

217 # Acquire commitizen configuration 

218 file.write(resources_files(Bundle.RESOURCES_ASSETS) \ 

219 .joinpath(Commitizen.CONFIGURATION_FILE) \ 

220 .read_text(encoding='utf8') \ 

221 .replace(PreCommit.REV_TEMPLATE, Version.revision())) 

222 file.flush() 

223 

224 # Autoupdate pre-commit hooks 

225 if not options.offline and Git.is_repository(): 

226 PreCommit.autoupdate() 

227 

228 # Configure useless excludes 

229 PreCommit.configure_useless_excludes( 

230 default=options.default, 

231 configuration_file=precommit_configuration.name, 

232 ) 

233 

234 # Configure unused hooks 

235 PreCommit.configure_unused_hooks( 

236 default=options.default, 

237 configuration_file=precommit_configuration.name, 

238 hooks_disable=[ 

239 PreCommit.HOOK_CHECK_HOOKS_APPLY, 

240 PreCommit.HOOK_CHECK_USELESS_EXCLUDES, 

241 ], 

242 ) 

243 

244 # Configre gcil hooks 

245 Gcil.configure_hooks( 

246 configuration_file=precommit_configuration.name, 

247 autoupdate=not options.offline, 

248 ) 

249 

250 # Autoupdate pre-commit hooks 

251 if not options.offline and Git.is_repository(): 

252 PreCommit.autoupdate() 

253 

254 # Configure sources 

255 if options.configure: 

256 

257 # Install sources configurations 

258 print(' ') 

259 print(f'{Colors.GREEN} ===[' 

260 f'{Colors.YELLOW} Configure sources: ' 

261 f'{Colors.YELLOW_LIGHT}{Bundle.NAME}' 

262 f'{Colors.GREEN} ]===' 

263 f'{Colors.RESET}') 

264 print(' ') 

265 

266 # Export pre-commit assets file 

267 if Path(PreCommit.CONFIGURATION_FILE).exists(): 

268 configurations_existed = True 

269 configurations_revision = PreCommit.revision() 

270 print( 

271 f'exporting configuration in {PreCommit.CONFIGURATION_FILE} configuration' 

272 ) 

273 configurations_files += [PreCommit.CONFIGURATION_FILE] 

274 data = resources_files(Bundle.RESOURCES_ASSETS) \ 

275 .joinpath(PreCommit.CONFIGURATION_TEMPLATE) \ 

276 .read_text(encoding='utf8') \ 

277 .replace(PreCommit.REV_TEMPLATE, Version.revision()) 

278 with open( 

279 PreCommit.CONFIGURATION_FILE, 

280 encoding='utf8', 

281 mode='w', 

282 ) as f: 

283 f.write(data) 

284 if Platform.IS_TTY_STDIN: 

285 sleep(0.5) 

286 

287 # Export Commitizen assets file 

288 if Path(Commitizen.CONFIGURATION_FILE).exists(): 

289 configurations_existed = True 

290 print( 

291 f'exporting configuration in {Commitizen.CONFIGURATION_FILE} configuration' 

292 ) 

293 configurations_files += [Commitizen.CONFIGURATION_FILE] 

294 data = resources_files(Bundle.RESOURCES_ASSETS) \ 

295 .joinpath(Commitizen.CONFIGURATION_FILE) \ 

296 .read_text(encoding='utf8') \ 

297 .replace(PreCommit.REV_TEMPLATE, Version.revision()) 

298 with open( 

299 Commitizen.CONFIGURATION_FILE, 

300 encoding='utf8', 

301 mode='w', 

302 ) as f: 

303 f.write(data) 

304 if Platform.IS_TTY_STDIN: 

305 sleep(0.5) 

306 

307 # Autoupdate pre-commit hooks 

308 if not options.offline and Git.is_repository(): 

309 PreCommit.autoupdate() 

310 

311 # Configure useless excludes 

312 PreCommit.configure_useless_excludes(default=options.default) 

313 

314 # Configure unused hooks 

315 PreCommit.configure_unused_hooks( 

316 default=options.default, 

317 hooks_disable=[ 

318 PreCommit.HOOK_CHECK_HOOKS_APPLY, 

319 PreCommit.HOOK_CHECK_USELESS_EXCLUDES, 

320 ], 

321 ) 

322 

323 # Configre gcil hooks 

324 Gcil.configure_hooks( 

325 configuration_file=PreCommit.CONFIGURATION_FILE, 

326 autoupdate=not options.offline, 

327 ) 

328 

329 # Autoupdate pre-commit hooks 

330 if not options.offline and Git.is_repository(): 

331 PreCommit.autoupdate() 

332 

333 # Export components templates 

334 if options.configure and Gcil.exists(): 

335 

336 # Prepare components stage 

337 components_stage: str = options.stage if options.stage else 'test' 

338 

339 # Detect if components in sources 

340 if options.components: 

341 include_components = True 

342 elif options.remotes: 

343 include_remotes = True 

344 elif options.no_components: 

345 include_sources = True 

346 elif any(Bundle.COMPONENTS_SAAS_DOMAIN in url for url in Git.urls()): 

347 include_components = True 

348 else: 

349 include_remotes = True 

350 

351 # Bind components version 

352 if include_components: 

353 

354 # Install components configurations 

355 print(' ') 

356 print(f'{Colors.GREEN} ===[' 

357 f'{Colors.YELLOW} Configure sources: ' 

358 f'{Colors.YELLOW_LIGHT}components' 

359 f'{Colors.GREEN} ]===' 

360 f'{Colors.RESET}') 

361 print(' ') 

362 

363 # Inject component in .gitlab-ci.yml 

364 include_value = Bundle.COMPONENTS_SAAS_COMMITS.replace( 

365 Bundle.COMPONENTS_PACKAGE_VERSION, 

366 Version.revision(), 

367 ) 

368 include_prefix = Bundle.COMPONENTS_SAAS_COMMITS.split( 

369 Bundle.COMPONENTS_PACKAGE_VERSION, 

370 1, 

371 )[0] 

372 local_ci_data = '' 

373 with open(Gcil.FILE, encoding='utf8') as local_ci_file: 

374 local_ci_data = local_ci_file.read() 

375 if include_value not in local_ci_data: 

376 configurations_files += [Gcil.FILE] 

377 with open( 

378 Gcil.FILE, 

379 encoding='utf8', 

380 mode='w', 

381 ) as f: 

382 if f'component: {include_prefix}' not in local_ci_data: 

383 f.write( 

384 Gcil.EOL.join([ 

385 'include:', 

386 f' - component: {include_value}', 

387 ' inputs:', 

388 f' stage: {components_stage}', 

389 ' name: commits', 

390 '', 

391 '', 

392 ])) 

393 else: 

394 local_ci_data = Gcil.EOL.join([ 

395 f'{line[0:line.find(include_prefix)]}{include_value}' # 

396 if f'component: {include_prefix}' in line # 

397 else line # 

398 for line in local_ci_data.splitlines() 

399 ]) + Gcil.EOL 

400 if 'stages:' not in local_ci_data.splitlines(): 

401 f.write( 

402 Gcil.EOL.join([ 

403 'stages:', 

404 f' - {components_stage}', 

405 '', 

406 ])) 

407 f.write(local_ci_data) 

408 

409 # Bind remotes version 

410 elif include_remotes: 

411 

412 # Install remotes configurations 

413 print(' ') 

414 print(f'{Colors.GREEN} ===[' 

415 f'{Colors.YELLOW} Configure sources: ' 

416 f'{Colors.YELLOW_LIGHT}remotes' 

417 f'{Colors.GREEN} ]===' 

418 f'{Colors.RESET}') 

419 print(' ') 

420 

421 # Inject remote in .gitlab-ci.yml 

422 include_value = Bundle.COMPONENTS_REMOTE_COMMITS.replace( 

423 Bundle.COMPONENTS_PACKAGE_VERSION, 

424 Version.revision(), 

425 ) 

426 include_prefix = Bundle.COMPONENTS_REMOTE_COMMITS.split( 

427 Bundle.COMPONENTS_PACKAGE_VERSION, 

428 1, 

429 )[0] 

430 local_ci_data = '' 

431 with open(Gcil.FILE, encoding='utf8') as local_ci_file: 

432 local_ci_data = local_ci_file.read() 

433 if include_value not in local_ci_data: 

434 configurations_files += [Gcil.FILE] 

435 with open( 

436 Gcil.FILE, 

437 encoding='utf8', 

438 mode='w', 

439 ) as f: 

440 if f'remote: {include_prefix}' not in local_ci_data: 

441 f.write( 

442 Gcil.EOL.join([ 

443 'include:', 

444 f' - remote: {include_value}', 

445 ' inputs:', 

446 f' stage: {components_stage}', 

447 ' name: commits', 

448 '', 

449 '', 

450 ])) 

451 else: 

452 local_ci_data = Gcil.EOL.join([ 

453 f'{line[0:line.find(include_prefix)]}{include_value}' # 

454 if f'remote: {include_prefix}' in line # 

455 else line # 

456 for line in local_ci_data.splitlines() 

457 ]) + Gcil.EOL 

458 if 'stages:' not in local_ci_data.splitlines(): 

459 f.write( 

460 Gcil.EOL.join([ 

461 'stages:', 

462 f' - {components_stage}', 

463 '', 

464 ])) 

465 f.write(local_ci_data) 

466 

467 # Export components sources 

468 elif include_sources: 

469 

470 # Install templates configurations 

471 print(' ') 

472 print(f'{Colors.GREEN} ===[' 

473 f'{Colors.YELLOW} Configure sources: ' 

474 f'{Colors.YELLOW_LIGHT}templates' 

475 f'{Colors.GREEN} ]===' 

476 f'{Colors.RESET}') 

477 print(' ') 

478 

479 # Export templates files 

480 for asset in [ 

481 Templates.COMMITS_FILE, 

482 ]: 

483 

484 # Export template file 

485 print(f'exporting components in {asset} local template') 

486 Path(Templates.GITLAB_CI_LOCAL_FOLDER).mkdir( 

487 parents=False, 

488 exist_ok=True, 

489 ) 

490 local_template = f'{Templates.GITLAB_CI_LOCAL_FOLDER}/{asset}' 

491 configurations_files += [local_template] 

492 data = resources_files(Bundle.RESOURCES_ASSETS) \ 

493 .joinpath(asset) \ 

494 .read_text(encoding='utf8') 

495 with open( 

496 local_template, 

497 encoding='utf8', 

498 mode='w', 

499 ) as f: 

500 f.write(data) 

501 if Platform.IS_TTY_STDIN: 

502 sleep(0.5) 

503 

504 # Inject template in .gitlab-ci.yml 

505 local_ci_data = '' 

506 with open(Gcil.FILE, encoding='utf8') as local_ci_file: 

507 local_ci_data = local_ci_file.read() 

508 if asset not in local_ci_data: 

509 configurations_files += [Gcil.FILE] 

510 with open( 

511 Gcil.FILE, 

512 encoding='utf8', 

513 mode='w', 

514 ) as f: 

515 f.write( 

516 Gcil.EOL.join([ 

517 'include:', 

518 f' - local: \'/{local_template}\'', 

519 ' inputs:', 

520 f' stage: {components_stage}', 

521 f' name: {asset.rsplit("/", 1)[-1].split(".", 1)[0]}', 

522 '', 

523 '', 

524 ])) 

525 if 'stages:' not in local_ci_data.split(): 

526 f.write( 

527 Gcil.EOL.join([ 

528 'stages:', 

529 f' - {components_stage}', 

530 '', 

531 ])) 

532 f.write(local_ci_data) 

533 

534 # Register configured file 

535 configurations_files += [Gcil.FILE] 

536 

537 # Enable pre-commit hooks 

538 if not options.enable: 

539 options.enable = True 

540 

541 # Configure sources or badges 

542 if options.configure or options.badges: 

543 

544 # Inject badge to README 

545 if Readme.exists() or options.badges: 

546 

547 # Configure README sources 

548 print(' ') 

549 print(f'{Colors.GREEN} ===[' 

550 f'{Colors.YELLOW} Configure sources: ' 

551 f'{Colors.YELLOW_LIGHT}{Readme.FILE}' 

552 f'{Colors.GREEN} ]===' 

553 f'{Colors.RESET}') 

554 print(' ') 

555 

556 # Acquire README contents 

557 readme_lines: List[str] = Readme.read() 

558 

559 # Prepare empty README 

560 if not readme_lines: 

561 readme_lines = [f'# {Path.cwd().name}{Readme.EOL}'] 

562 

563 # Prepare README without empty lines 

564 if all(line.strip() for line in readme_lines): 

565 if not readme_lines[-1].endswith(Readme.EOL): 

566 readme_lines[-1] = f'{readme_lines[-1]}{Readme.EOL}' 

567 readme_lines += [f'{Readme.EOL}'] 

568 readme_lines += [''] 

569 

570 # Check badges in READE 

571 bundle_found: bool = any( 

572 line.startswith(f'[![{Bundle.NAME}]') for line in readme_lines) 

573 guidelines_found: bool = any( 

574 line.startswith(f'[![{RadianDevCore.Guidelines.BADGE_TEXT}]') 

575 for line in readme_lines) 

576 gcil_found: bool = any( 

577 line.startswith(f'[![{Gcil.BADGE_TEXT}]') for line in readme_lines) 

578 

579 # Detect gcil usage 

580 gcil_exists: bool = Gcil.exists() 

581 gcil_configured: bool = Gcil.configured() 

582 gcil_installed: bool = Gcil.installed() 

583 gcil_needed: bool = gcil_exists and (gcil_configured or gcil_installed) 

584 

585 # Validate badge in README 

586 if not bundle_found \ 

587 or not guidelines_found \ 

588 or (not gcil_found and gcil_needed): 

589 

590 # Inject badge lines 

591 print(f'injecting badge in {Readme.FILE} documentation') 

592 for i, line in enumerate(readme_lines): 

593 

594 # Ignore non-empty lines 

595 if line.strip(): 

596 continue 

597 

598 # Handle empty line 

599 if not line: 

600 line = Readme.EOL 

601 

602 # Inject badges separator 

603 readme_lines.insert( 

604 i + 1, 

605 line, 

606 ) 

607 

608 # Inject RadianDevCore guidelines badge 

609 if not guidelines_found: 

610 readme_lines.insert( 

611 i + 1, 

612 f'[![{RadianDevCore.Guidelines.BADGE_TEXT}]' 

613 f'({RadianDevCore.Guidelines.BADGE_IMAGE})]' 

614 f'({RadianDevCore.Guidelines.BADGE_URL}){line}', 

615 ) 

616 

617 # Inject pre-commit-crocodile badge 

618 if not bundle_found: 

619 readme_lines.insert( 

620 i + 1, 

621 f'[![{Bundle.NAME}]' 

622 f'({Bundle.BADGE})]' 

623 f'({Bundle.DOCUMENTATION}){line}', 

624 ) 

625 

626 # Inject gcil badge 

627 if not gcil_found and gcil_needed: 

628 readme_lines.insert( 

629 i + 1, 

630 f'[![{Gcil.BADGE_TEXT}]' 

631 f'({Gcil.BADGE_IMAGE})]' 

632 f'({Gcil.BADGE_URL}){line}', 

633 ) 

634 

635 # Preserve README documentation 

636 break 

637 

638 # Export README contents 

639 Readme.write(readme_lines) 

640 

641 # Register configured file 

642 configurations_files += [Readme.FILE] 

643 

644 # Keep existing README 

645 else: 

646 print(f'keeping original {Readme.FILE} documentation') 

647 

648 # Register mise configuration file 

649 if options.configure and Path(Mise.CONFIG_LOCAL).exists(): 

650 configurations_files += [Mise.CONFIG_LOCAL] 

651 

652 # Show sources status 

653 if Git.is_repository(): 

654 print(' ') 

655 print(f'{Colors.GREEN} ===[' 

656 f'{Colors.YELLOW} Sources status: ' 

657 f'{Colors.YELLOW_LIGHT}git status' 

658 f'{Colors.GREEN} ]===' 

659 f'{Colors.RESET}') 

660 print(' ') 

661 Git.status(untracked=True) 

662 

663 # Configure sources 

664 if options.configure \ 

665 and Git.is_repository() \ 

666 and Git.intend(configurations_files) \ 

667 and Git.diff(configurations_files): 

668 

669 # Show commit hints 

670 print(' ') 

671 print(f'{Colors.GREEN} ===[' 

672 f'{Colors.YELLOW} Update sources: ' 

673 f'{Colors.YELLOW_LIGHT}git' 

674 f'{Colors.GREEN} ]===' 

675 f'{Colors.RESET}') 

676 print(' ') 

677 command_add = [ 

678 Git.BINARY, 

679 'add', 

680 '--patch' if options.patch else '-v', 

681 ] + [f'./{asset}' for asset in configurations_files] 

682 if configurations_existed and configurations_revision == Version.revision(): 

683 command_commit = [ 

684 Git.BINARY, 

685 'commit', 

686 '-m', 

687 f'chore(pre-commit): refresh with \'{Bundle.NAME}\' {Version.get()}', 

688 '-s', 

689 ] 

690 elif configurations_existed: 

691 command_commit = [ 

692 Git.BINARY, 

693 'commit', 

694 '-m', 

695 f'chore(pre-commit): migrate to \'{Bundle.NAME}\' {Version.get()}', 

696 '-s', 

697 ] 

698 else: 

699 command_commit = [ 

700 Git.BINARY, 

701 'commit', 

702 '-m', 

703 f'chore(pre-commit): import \'{Bundle.NAME}\' {Version.get()}', 

704 '-s', 

705 ] 

706 command_add_string = ' '.join([ 

707 f'{argument}' if "'" in argument else argument # 

708 for argument in command_add 

709 ]) 

710 command_commit_string = ' '.join([ 

711 f'"{argument}"' if "'" in argument else argument 

712 for argument in command_commit 

713 ]) 

714 print(f'{Colors.BOLD} - Add configurations: ' 

715 f'{Colors.CYAN}{command_add_string}' 

716 f'{Colors.RESET}') 

717 print(f'{Colors.BOLD} - Commit changes: ' 

718 f'{Colors.CYAN}{command_commit_string}' 

719 f'{Colors.RESET}') 

720 Platform.flush() 

721 

722 # Commit changes automatically 

723 if options.commit: 

724 print(' ') 

725 Commands.run(Git.BINARY, command_add[1:]) 

726 Platform.flush() 

727 

728 # Discard rejected pre-commit configuration changes 

729 if options.patch \ 

730 and PreCommit.CONFIGURATION_FILE in configurations_files \ 

731 and Git.diff([PreCommit.CONFIGURATION_FILE]) \ 

732 and Git.commit_exists(f'HEAD:{PreCommit.CONFIGURATION_FILE}'): 

733 print(' ') 

734 print(f'discarding unstaged {PreCommit.CONFIGURATION_FILE} changes') 

735 Git.restore([PreCommit.CONFIGURATION_FILE]) 

736 Platform.flush() 

737 

738 if not Git.staging_empty(): 

739 print(' ') 

740 Commands.run(Git.BINARY, command_commit[1:]) 

741 Platform.flush() 

742 

743 # Delay user interactions 

744 if Platform.IS_TTY_STDIN: 

745 if options.commit: 

746 sleep(1) 

747 else: 

748 sleep(3) 

749 

750 # Configure badges 

751 elif options.badges and configurations_files and Git.is_repository(): 

752 

753 # Show commit hints 

754 print(' ') 

755 print(f'{Colors.GREEN} ===[' 

756 f'{Colors.YELLOW} Update sources: ' 

757 f'{Colors.YELLOW_LIGHT}git' 

758 f'{Colors.GREEN} ]===' 

759 f'{Colors.RESET}') 

760 print(' ') 

761 command_add = [ 

762 Git.BINARY, 

763 'add', 

764 '--patch' if options.patch else '-v', 

765 ] + [f'./{asset}' for asset in configurations_files] 

766 command_commit = [ 

767 Git.BINARY, 

768 'commit', 

769 '-m', 

770 'docs(readme): add applicable badges to the documentation', 

771 '-s', 

772 ] 

773 command_add_string = ' '.join([ 

774 f'{argument}' if "'" in argument else argument # 

775 for argument in command_add 

776 ]) 

777 command_commit_string = ' '.join([ 

778 f'"{argument}"' if "'" in argument else argument 

779 for argument in command_commit 

780 ]) 

781 print(f'{Colors.BOLD} - Add configurations: ' 

782 f'{Colors.CYAN}{command_add_string}' 

783 f'{Colors.RESET}') 

784 print(f'{Colors.BOLD} - Commit changes: ' 

785 f'{Colors.CYAN}{command_commit_string}' 

786 f'{Colors.RESET}') 

787 Platform.flush() 

788 

789 # Commit changes automatically 

790 if options.commit: 

791 print(' ') 

792 Commands.run(Git.BINARY, command_add[1:]) 

793 Platform.flush() 

794 if not Git.staging_empty(): 

795 print(' ') 

796 Commands.run(Git.BINARY, command_commit[1:]) 

797 Platform.flush() 

798 

799 # Delay user interactions 

800 if Platform.IS_TTY_STDIN: 

801 if options.commit: 

802 sleep(1) 

803 else: 

804 sleep(3) 

805 

806 # Enable hooks 

807 if options.enable and Git.is_repository(): 

808 

809 # Detect Git remotes 

810 git_remotes: List[str] = Git.remotes() 

811 

812 # Enable Git hooks 

813 print(' ') 

814 print(f'{Colors.GREEN} ===[' 

815 f'{Colors.YELLOW} Enable hooks: ' 

816 f'{Colors.YELLOW_LIGHT}Git remote' 

817 f'{Colors.GREEN} ]===' 

818 f'{Colors.RESET}') 

819 print(' ') 

820 print(f'detected Git remotes: {", ".join(git_remotes)}') 

821 Platform.flush() 

822 for remote in git_remotes: 

823 print(f'updating git remote for {remote}') 

824 Git.update_remote_head(remote) 

825 

826 # Enable pre-commit hooks 

827 print(' ') 

828 print(f'{Colors.GREEN} ===[' 

829 f'{Colors.YELLOW} Enable hooks: ' 

830 f'{Colors.YELLOW_LIGHT}{PreCommit.TITLE}' 

831 f'{Colors.GREEN} ]===' 

832 f'{Colors.RESET}') 

833 print(' ') 

834 Platform.flush() 

835 PreCommit.install() 

836 

837 # Run pre-commit hooks 

838 if not options.run and Path(PreCommit.CONFIGURATION_FILE).exists(): 

839 options.run = True 

840 

841 # Ignore GitLab CI stage from --configure 

842 options.stage = None 

843 

844 # Disable hooks 

845 if options.disable: 

846 

847 # Disable pre-commit hooks 

848 print(' ') 

849 print(f'{Colors.GREEN} ===[' 

850 f'{Colors.YELLOW} Disable hooks: ' 

851 f'{Colors.YELLOW_LIGHT}{PreCommit.TITLE}' 

852 f'{Colors.GREEN} ]===' 

853 f'{Colors.RESET}') 

854 print(' ') 

855 Platform.flush() 

856 PreCommit.uninstall() 

857 

858 # Autoupdate hooks 

859 if options.autoupdate: 

860 

861 # Autoupdate pre-commit hooks 

862 print(' ') 

863 print(f'{Colors.GREEN} ===[' 

864 f'{Colors.YELLOW} Autoupdate hooks: ' 

865 f'{Colors.YELLOW_LIGHT}{PreCommit.TITLE}' 

866 f'{Colors.GREEN} ]===' 

867 f'{Colors.RESET}') 

868 print(' ') 

869 Platform.flush() 

870 PreCommit.autoupdate() 

871 

872 # Cleanup hooks 

873 if options.clean: 

874 

875 # Disable pre-commit hooks 

876 print(' ') 

877 print(f'{Colors.GREEN} ===[' 

878 f'{Colors.YELLOW} Cleanup hooks: ' 

879 f'{Colors.YELLOW_LIGHT}{PreCommit.TITLE}' 

880 f'{Colors.GREEN} ]===' 

881 f'{Colors.RESET}') 

882 print(' ') 

883 Platform.flush() 

884 PreCommit.clean() 

885 

886 # Run hooks 

887 if options.run: 

888 

889 # Run pre-commit hooks 

890 print(' ') 

891 print(f'{Colors.GREEN} ===[' 

892 f'{Colors.YELLOW} Run hooks: ' 

893 f'{Colors.YELLOW_LIGHT}{PreCommit.TITLE}' 

894 f'{Colors.CYAN}{f" (stage: {options.stage})" if options.stage else ""}' 

895 f'{Colors.GREEN} ]===' 

896 f'{Colors.RESET}') 

897 print(' ') 

898 Platform.flush() 

899 if options.stage == 'list': 

900 hooks_stages: List[str] = PreCommit.stages() 

901 print(f'{Colors.BOLD}Supported {PreCommit.TITLE} hooks stages:' 

902 f'{Colors.RESET} {", ".join(hooks_stages)}') 

903 else: 

904 PreCommit.run( 

905 all_files=True, 

906 stage=options.stage, 

907 ) 

908 

909 # Run commitizen hooks 

910 if Git.is_repository() and Git.commit_exists('HEAD'): 

911 print(' ') 

912 print(f'{Colors.GREEN} ===[' 

913 f'{Colors.YELLOW} Run hooks: ' 

914 f'{Colors.YELLOW_LIGHT}commitizen' 

915 f'{Colors.GREEN} ]===' 

916 f'{Colors.RESET}') 

917 print(' ') 

918 Platform.flush() 

919 if Git.commit_exists('HEAD~1'): 

920 Commitizen.check('HEAD~1..HEAD') 

921 else: 

922 Commitizen.check('HEAD') 

923 

924 # Result 

925 return Entrypoint.Result.SUCCESS