ar-infra-cli 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/PKG-INFO +5 -5
  2. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/ar_infra_cli.egg-info/PKG-INFO +5 -5
  3. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/ar_infra_cli.egg-info/SOURCES.txt +3 -0
  4. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/ar_infra_cli.egg-info/requires.txt +4 -4
  5. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/pyproject.toml +5 -5
  6. ar_infra_cli-0.2.1/src/ar_infra/cli/resources/feature-conf.yml +149 -0
  7. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/constant.py +2 -2
  8. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/entities/gradle_dependency.py +2 -2
  9. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/enums/template_feature.py +2 -2
  10. ar_infra_cli-0.2.1/src/ar_infra/infrastructure/processor/yaml_processor.py +87 -0
  11. ar_infra_cli-0.2.1/src/ar_infra/infrastructure/template/feature_config.py +107 -0
  12. ar_infra_cli-0.2.1/src/ar_infra/infrastructure/template/feature_config_schema.py +34 -0
  13. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/feature_manager.py +2 -2
  14. ar_infra_cli-0.2.0/src/ar_infra/infrastructure/template/feature_config.py +0 -202
  15. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/MANIFEST.in +0 -0
  16. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/ar_infra_cli.egg-info/dependency_links.txt +0 -0
  17. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/ar_infra_cli.egg-info/entry_points.txt +0 -0
  18. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/ar_infra_cli.egg-info/top_level.txt +0 -0
  19. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/readme.md +0 -0
  20. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/setup.cfg +0 -0
  21. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/__init__.py +0 -0
  22. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/__init__.py +0 -0
  23. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/application/__init__.py +0 -0
  24. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/application/use_cases/__init__.py +0 -0
  25. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/application/use_cases/add_dependency_use_case.py +0 -0
  26. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/application/use_cases/exception.py +0 -0
  27. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/application/use_cases/generate_project_use_case.py +0 -0
  28. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/application/use_cases/input_dto.py +0 -0
  29. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/application/use_cases/output_dto.py +0 -0
  30. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/__init__.py +0 -0
  31. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/command/__init__.py +0 -0
  32. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/command/add_dependency.py +0 -0
  33. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/command/init.py +0 -0
  34. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/main.py +0 -0
  35. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/prompt/__init__.py +0 -0
  36. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/prompt/interactive_prompt.py +0 -0
  37. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/prompt/validator.py +0 -0
  38. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/resources/__init__.py +0 -0
  39. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/resources/banner.txt +0 -0
  40. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/resources/documentation.py +0 -0
  41. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/ui/__init__.py +0 -0
  42. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/ui/banner.py +0 -0
  43. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/ui/color_properties.py +0 -0
  44. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/ui/console.py +0 -0
  45. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/ui/help.py +0 -0
  46. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/ui/message.py +0 -0
  47. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/ui/progress.py +0 -0
  48. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/cli/ui/welcome.py +0 -0
  49. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/__init__.py +0 -0
  50. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/entities/__init__.py +0 -0
  51. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/entities/path_constant.py +0 -0
  52. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/entities/path_resolver.py +0 -0
  53. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/enums/__init__.py +0 -0
  54. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/exceptions/__init__.py +0 -0
  55. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/exceptions/validation_error.py +0 -0
  56. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/value_objects/__init__.py +0 -0
  57. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/value_objects/artifact_id.py +0 -0
  58. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/value_objects/group_id.py +0 -0
  59. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/value_objects/package_name.py +0 -0
  60. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/domain/value_objects/version.py +0 -0
  61. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/health.py +0 -0
  62. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/__init__.py +0 -0
  63. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/config.py +0 -0
  64. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/fs_utilities.py +0 -0
  65. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/gradle/__init__.py +0 -0
  66. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/gradle/dependency_parser.py +0 -0
  67. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/gradle/gradle_exception.py +0 -0
  68. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/gradle/gradle_parser.py +0 -0
  69. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/gradle/gradle_writter.py +0 -0
  70. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/processor/__init__.py +0 -0
  71. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/processor/bot.py +0 -0
  72. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/processor/exception.py +0 -0
  73. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/processor/format_script_runner.py +0 -0
  74. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/processor/github_app.py +0 -0
  75. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/processor/package_renamer.py +0 -0
  76. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/__init__.py +0 -0
  77. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/development_artifact_remover.py +0 -0
  78. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/env_handler.py +0 -0
  79. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/exception.py +0 -0
  80. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/facadeit_handler.py +0 -0
  81. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/github_template_fetcher.py +0 -0
  82. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/project_signature.py +0 -0
  83. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/rest_exception_manager.py +0 -0
  84. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/infrastructure/template/swagger_handler.py +0 -0
  85. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/logger.py +0 -0
  86. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/src/ar_infra/properties.py +0 -0
  87. {ar_infra_cli-0.2.0 → ar_infra_cli-0.2.1}/tests/test_health.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ar-infra-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Production-ready Spring Boot project generator CLI
5
5
  Author-email: Abegà Razafindratelo <a.razafindratelo@gmail.com>
6
6
  License: MIT
@@ -35,7 +35,7 @@ Requires-Dist: python-dotenv>=1.2.1
35
35
  Requires-Dist: pathspec>=0.12.1
36
36
  Requires-Dist: pathvalidate>=3.3.1
37
37
  Provides-Extra: dev
38
- Requires-Dist: pytest>=9.0.2; extra == "dev"
38
+ Requires-Dist: pytest>=9.0.3; extra == "dev"
39
39
  Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
40
40
  Requires-Dist: pytest-asyncio>=1.3.0; extra == "dev"
41
41
  Requires-Dist: pytest-mock>=3.15.1; extra == "dev"
@@ -55,14 +55,14 @@ Requires-Dist: safety>=3.7.0; extra == "dev"
55
55
  Requires-Dist: pip-audit>=2.10.0; extra == "dev"
56
56
  Requires-Dist: coverage[toml]>=7.13.0; extra == "dev"
57
57
  Requires-Dist: pre-commit>=4.5.1; extra == "dev"
58
- Requires-Dist: sphinx>=8.2.3; extra == "dev"
58
+ Requires-Dist: sphinx>=9.1.0; extra == "dev"
59
59
  Requires-Dist: sphinx-rtd-theme; extra == "dev"
60
60
  Requires-Dist: sphinx-autodoc-typehints>=3.6.0; extra == "dev"
61
61
  Requires-Dist: build>=1.4.0; extra == "dev"
62
62
  Requires-Dist: setuptools>=80.9.0; extra == "dev"
63
- Requires-Dist: wheel>=0.45.1; extra == "dev"
63
+ Requires-Dist: wheel>=0.47.0; extra == "dev"
64
64
  Requires-Dist: twine>=6.2.0; extra == "dev"
65
- Requires-Dist: ipython>=9.9.0; extra == "dev"
65
+ Requires-Dist: ipython>=9.10.0; extra == "dev"
66
66
  Requires-Dist: lxml>=5.3.0; extra == "dev"
67
67
  Requires-Dist: typing-extensions>=4.12.0; extra == "dev"
68
68
  Requires-Dist: ipdb>=0.13.13; extra == "dev"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ar-infra-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Production-ready Spring Boot project generator CLI
5
5
  Author-email: Abegà Razafindratelo <a.razafindratelo@gmail.com>
6
6
  License: MIT
@@ -35,7 +35,7 @@ Requires-Dist: python-dotenv>=1.2.1
35
35
  Requires-Dist: pathspec>=0.12.1
36
36
  Requires-Dist: pathvalidate>=3.3.1
37
37
  Provides-Extra: dev
38
- Requires-Dist: pytest>=9.0.2; extra == "dev"
38
+ Requires-Dist: pytest>=9.0.3; extra == "dev"
39
39
  Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
40
40
  Requires-Dist: pytest-asyncio>=1.3.0; extra == "dev"
41
41
  Requires-Dist: pytest-mock>=3.15.1; extra == "dev"
@@ -55,14 +55,14 @@ Requires-Dist: safety>=3.7.0; extra == "dev"
55
55
  Requires-Dist: pip-audit>=2.10.0; extra == "dev"
56
56
  Requires-Dist: coverage[toml]>=7.13.0; extra == "dev"
57
57
  Requires-Dist: pre-commit>=4.5.1; extra == "dev"
58
- Requires-Dist: sphinx>=8.2.3; extra == "dev"
58
+ Requires-Dist: sphinx>=9.1.0; extra == "dev"
59
59
  Requires-Dist: sphinx-rtd-theme; extra == "dev"
60
60
  Requires-Dist: sphinx-autodoc-typehints>=3.6.0; extra == "dev"
61
61
  Requires-Dist: build>=1.4.0; extra == "dev"
62
62
  Requires-Dist: setuptools>=80.9.0; extra == "dev"
63
- Requires-Dist: wheel>=0.45.1; extra == "dev"
63
+ Requires-Dist: wheel>=0.47.0; extra == "dev"
64
64
  Requires-Dist: twine>=6.2.0; extra == "dev"
65
- Requires-Dist: ipython>=9.9.0; extra == "dev"
65
+ Requires-Dist: ipython>=9.10.0; extra == "dev"
66
66
  Requires-Dist: lxml>=5.3.0; extra == "dev"
67
67
  Requires-Dist: typing-extensions>=4.12.0; extra == "dev"
68
68
  Requires-Dist: ipdb>=0.13.13; extra == "dev"
@@ -30,6 +30,7 @@ src/ar_infra/cli/prompt/validator.py
30
30
  src/ar_infra/cli/resources/__init__.py
31
31
  src/ar_infra/cli/resources/banner.txt
32
32
  src/ar_infra/cli/resources/documentation.py
33
+ src/ar_infra/cli/resources/feature-conf.yml
33
34
  src/ar_infra/cli/ui/__init__.py
34
35
  src/ar_infra/cli/ui/banner.py
35
36
  src/ar_infra/cli/ui/color_properties.py
@@ -67,12 +68,14 @@ src/ar_infra/infrastructure/processor/exception.py
67
68
  src/ar_infra/infrastructure/processor/format_script_runner.py
68
69
  src/ar_infra/infrastructure/processor/github_app.py
69
70
  src/ar_infra/infrastructure/processor/package_renamer.py
71
+ src/ar_infra/infrastructure/processor/yaml_processor.py
70
72
  src/ar_infra/infrastructure/template/__init__.py
71
73
  src/ar_infra/infrastructure/template/development_artifact_remover.py
72
74
  src/ar_infra/infrastructure/template/env_handler.py
73
75
  src/ar_infra/infrastructure/template/exception.py
74
76
  src/ar_infra/infrastructure/template/facadeit_handler.py
75
77
  src/ar_infra/infrastructure/template/feature_config.py
78
+ src/ar_infra/infrastructure/template/feature_config_schema.py
76
79
  src/ar_infra/infrastructure/template/feature_manager.py
77
80
  src/ar_infra/infrastructure/template/github_template_fetcher.py
78
81
  src/ar_infra/infrastructure/template/project_signature.py
@@ -15,7 +15,7 @@ pathspec>=0.12.1
15
15
  pathvalidate>=3.3.1
16
16
 
17
17
  [dev]
18
- pytest>=9.0.2
18
+ pytest>=9.0.3
19
19
  pytest-cov>=7.0.0
20
20
  pytest-asyncio>=1.3.0
21
21
  pytest-mock>=3.15.1
@@ -35,14 +35,14 @@ safety>=3.7.0
35
35
  pip-audit>=2.10.0
36
36
  coverage[toml]>=7.13.0
37
37
  pre-commit>=4.5.1
38
- sphinx>=8.2.3
38
+ sphinx>=9.1.0
39
39
  sphinx-rtd-theme
40
40
  sphinx-autodoc-typehints>=3.6.0
41
41
  build>=1.4.0
42
42
  setuptools>=80.9.0
43
- wheel>=0.45.1
43
+ wheel>=0.47.0
44
44
  twine>=6.2.0
45
- ipython>=9.9.0
45
+ ipython>=9.10.0
46
46
  lxml>=5.3.0
47
47
  typing-extensions>=4.12.0
48
48
  ipdb>=0.13.13
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ar-infra-cli"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Production-ready Spring Boot project generator CLI"
9
9
  authors = [
10
10
  {name = "Abegà Razafindratelo", email = "a.razafindratelo@gmail.com"}
@@ -45,7 +45,7 @@ dependencies = [
45
45
 
46
46
  [project.optional-dependencies]
47
47
  dev = [
48
- "pytest>=9.0.2",
48
+ "pytest>=9.0.3",
49
49
  "pytest-cov>=7.0.0",
50
50
  "pytest-asyncio>=1.3.0",
51
51
  "pytest-mock>=3.15.1",
@@ -65,14 +65,14 @@ dev = [
65
65
  "pip-audit>=2.10.0",
66
66
  "coverage[toml]>=7.13.0",
67
67
  "pre-commit>=4.5.1",
68
- "sphinx>=8.2.3",
68
+ "sphinx>=9.1.0",
69
69
  "sphinx-rtd-theme",
70
70
  "sphinx-autodoc-typehints>=3.6.0",
71
71
  "build>=1.4.0",
72
72
  "setuptools>=80.9.0",
73
- "wheel>=0.45.1",
73
+ "wheel>=0.47.0",
74
74
  "twine>=6.2.0",
75
- "ipython>=9.9.0",
75
+ "ipython>=9.10.0",
76
76
  "lxml>=5.3.0",
77
77
  "typing-extensions>=4.12.0",
78
78
  "ipdb>=0.13.13",
@@ -0,0 +1,149 @@
1
+ src_package: "src/main/java/com/example/arinfra/"
2
+ test_package: "src/test/java/com/example/arinfra/"
3
+
4
+ database_shared:
5
+ directories:
6
+ - "{src_package}repository"
7
+ - "src/main/resources/db"
8
+ files:
9
+ - "{src_package}endpoint/rest/controller/health/HealthRepositoryController.java"
10
+ - "{src_package}service/health/HealthRepositoryService.java"
11
+ - "{test_package}endpoint/rest/controller/health/HealthRepositoryControllerIT.java"
12
+ env_variables:
13
+ - SPRING_DATASOURCE_URL
14
+ - SPRING_DATASOURCE_USERNAME
15
+ - SPRING_DATASOURCE_PASSWORD
16
+ dependencies:
17
+ - "org.springframework.boot:spring-boot-starter-data-jpa"
18
+ - "org.flywaydb:flyway-core"
19
+
20
+ features:
21
+ POSTGRESQL:
22
+ files:
23
+ shared_directories:
24
+ - "{src_package}repository"
25
+ - "src/main/resources/db"
26
+ - "{test_package}conf/db"
27
+ specific_files:
28
+ - "{test_package}conf/db/PostgresConf.java"
29
+ shared_files:
30
+ - "{src_package}endpoint/rest/controller/health/HealthRepositoryController.java"
31
+ - "{src_package}service/health/HealthRepositoryService.java"
32
+ - "{test_package}endpoint/rest/controller/health/HealthRepositoryControllerIT.java"
33
+ shared_env_variables:
34
+ - SPRING_DATASOURCE_URL
35
+ - SPRING_DATASOURCE_USERNAME
36
+ - SPRING_DATASOURCE_PASSWORD
37
+ dependencies:
38
+ shared:
39
+ - "org.springframework.boot:spring-boot-starter-data-jpa"
40
+ - "org.flywaydb:flyway-core"
41
+ specific:
42
+ - "org.postgresql:postgresql"
43
+ - "org.flywaydb:flyway-database-postgresql"
44
+ - "org.testcontainers:postgresql"
45
+
46
+ MYSQL:
47
+ files:
48
+ shared_directories:
49
+ - "{src_package}repository"
50
+ - "src/main/resources/db"
51
+ - "{test_package}conf/db"
52
+ shared_files:
53
+ - "{src_package}endpoint/rest/controller/health/HealthRepositoryController.java"
54
+ - "{src_package}service/health/HealthRepositoryService.java"
55
+ - "{test_package}endpoint/rest/controller/health/HealthRepositoryControllerIT.java"
56
+ specific_files:
57
+ - "{test_package}conf/db/MysqlConf.java"
58
+ shared_env_variables:
59
+ - SPRING_DATASOURCE_URL
60
+ - SPRING_DATASOURCE_USERNAME
61
+ - SPRING_DATASOURCE_PASSWORD
62
+ dependencies:
63
+ shared:
64
+ - "org.springframework.boot:spring-boot-starter-data-jpa"
65
+ - "org.flywaydb:flyway-core"
66
+ specific:
67
+ - "com.mysql:mysql-connector-j"
68
+ - "org.flywaydb:flyway-mysql"
69
+ - "org.testcontainers:mysql"
70
+
71
+ RABBITMQ:
72
+ files:
73
+ specific_directories:
74
+ - "{src_package}event"
75
+ - "{src_package}datastructure"
76
+ specific_files:
77
+ - "{src_package}config/RabbitConf.java"
78
+ - "{src_package}datastructure/ListGrouper.java"
79
+ - "{src_package}service/health/HealthEventService.java"
80
+ - "{src_package}endpoint/rest/controller/health/HealthEventController.java"
81
+ - "{test_package}conf/RabbitMQConf.java"
82
+ - "{test_package}service/health/HealthEventServiceIT.java"
83
+ - "{test_package}endpoint/rest/controller/health/HealthEventControllerIT.java"
84
+ specific_env_variables:
85
+ - SPRING_RABBITMQ_HOST
86
+ - SPRING_RABBITMQ_USERNAME
87
+ - SPRING_RABBITMQ_PASSWORD
88
+ - SPRING_RABBITMQ_VHOST
89
+ - SPRING_RABBITMQ_QUEUE
90
+ - SPRING_RABBITMQ_EXCHANGE
91
+ - SPRING_RABBITMQ_ROUTING_KEY
92
+ - SPRING_RABBITMQ_PORT
93
+ - APP_RABBITMQ_SSL
94
+ dependencies:
95
+ specific:
96
+ - "org.springframework.boot:spring-boot-starter-amqp"
97
+ - "org.springframework.amqp:spring-rabbit-test"
98
+ - "org.testcontainers:rabbitmq"
99
+
100
+ S3_BUCKET:
101
+ files:
102
+ specific_directories:
103
+ - "{src_package}exception/bucket"
104
+ specific_files:
105
+ - "{src_package}config/BucketConf.java"
106
+ - "{src_package}file/BucketComponent.java"
107
+ - "{src_package}endpoint/rest/controller/health/HealthBucketController.java"
108
+ - "{src_package}service/health/HealthBucketService.java"
109
+ - "{test_package}conf/BucketConf.java"
110
+ - "{test_package}file/BucketComponentIT.java"
111
+ - "{test_package}service/health/HealthBucketServiceIT.java"
112
+ - "{test_package}endpoint/rest/controller/health/HealthBucketControllerIT.java"
113
+ specific_env_variables:
114
+ - CLOUD_STORAGE_KEY_ID
115
+ - CLOUD_STORAGE_APPLICATION_KEY
116
+ - CLOUD_STORAGE_BUCKET_NAME
117
+ - CLOUD_STORAGE_FULL_ENDPOINT
118
+ - CLOUD_STORAGE_REGION
119
+ dependencies:
120
+ specific:
121
+ - "software.amazon.awssdk:s3"
122
+ - "software.amazon.awssdk:s3-transfer-manager"
123
+ - "org.testcontainers:localstack"
124
+
125
+ EMAIL:
126
+ files:
127
+ specific_directories:
128
+ - "{src_package}mail"
129
+ - "{test_package}mail"
130
+ specific_files:
131
+ - "{src_package}config/EmailConf.java"
132
+ - "{src_package}service/health/HealthEmailService.java"
133
+ - "{src_package}exception/EmailSendException.java"
134
+ - "{src_package}exception/health/EmailHealthCheckException.java"
135
+ - "{src_package}endpoint/rest/controller/health/HealthEmailController.java"
136
+ - "{test_package}conf/EmailConf.java"
137
+ - "{test_package}service/health/HealthEmailServiceIT.java"
138
+ - "{test_package}endpoint/rest/controller/health/HealthEmailControllerIT.java"
139
+ specific_env_variables:
140
+ - SPRING_MAIL_HOST
141
+ - SPRING_MAIL_USERNAME
142
+ - SPRING_MAIL_PASSWORD
143
+ - SPRING_MAIL_FROM_EMAIL
144
+ - SPRING_MAIL_PORT
145
+ dependencies:
146
+ specific:
147
+ - "org.springframework.boot:spring-boot-starter-mail"
148
+ - "com.icegreen:greenmail"
149
+ - "com.icegreen:greenmail-junit5"
@@ -83,10 +83,10 @@ MALICIOUS_PATTERNS: Final[list[re.Pattern[str]]] = [
83
83
  re.compile(r"System\.exit\("),
84
84
  re.compile(r"Runtime\.getRuntime\(\)"),
85
85
  re.compile(r"ProcessBuilder"),
86
- re.compile(r"\.\.\/\.\.\/"),
86
+ re.compile(r"\.\./\.\./"),
87
87
  re.compile(r"exec\("),
88
88
  re.compile(r"`[^`]+`"),
89
- re.compile(r"\$\([^\)]+\)"),
89
+ re.compile(r"\$\([^)]+\)"),
90
90
  ]
91
91
 
92
92
  DEPENDENCY_PATTERN: Final[re.Pattern[str]] = re.compile(
@@ -1,11 +1,11 @@
1
1
  """Gradle dependency entity and configuration enum."""
2
2
 
3
3
  from dataclasses import dataclass
4
- from enum import Enum
4
+ from enum import StrEnum
5
5
  from typing import final
6
6
 
7
7
 
8
- class GradleConfiguration(str, Enum):
8
+ class GradleConfiguration(StrEnum):
9
9
  IMPLEMENTATION = "implementation"
10
10
  TEST_IMPLEMENTATION = "testImplementation"
11
11
  RUNTIME_ONLY = "runtimeOnly"
@@ -1,7 +1,7 @@
1
- from enum import Enum
1
+ from enum import StrEnum
2
2
 
3
3
 
4
- class TemplateFeature(str, Enum):
4
+ class TemplateFeature(StrEnum):
5
5
  """Available features in the template."""
6
6
 
7
7
  POSTGRESQL = "postgresql"
@@ -0,0 +1,87 @@
1
+ from importlib.resources.abc import (
2
+ Traversable, # nosemgrep: python.lang.compatibility.python37.python37-compatibility-importlib2
3
+ )
4
+ from pathlib import Path
5
+ from typing import Any, TypeVar
6
+
7
+ import yaml
8
+ from pydantic import BaseModel
9
+
10
+ from src.ar_infra.domain.entities.path_resolver import PathSecurityValidator
11
+
12
+
13
+ T = TypeVar("T", bound=BaseModel)
14
+
15
+ _ALLOWED_EXTENSIONS: frozenset[str] = frozenset({".yml", ".yaml"})
16
+ _MAX_FILE_SIZE_BYTES: int = 10 * 1024 * 1024 # 10 MB
17
+
18
+
19
+ class YamlFileProcessor:
20
+ def __init__(self, security_validator: PathSecurityValidator | None = None) -> None:
21
+ self._security_validator = security_validator or PathSecurityValidator()
22
+
23
+ def load(self, path: Traversable | Path, model: type[T]) -> T:
24
+ """
25
+ Load a YAML file and validate its content against a Pydantic model.
26
+
27
+ :param path: Traversable (package resource) or Path (filesystem).
28
+ :param model: Pydantic BaseModel subclass to validate against.
29
+ :return: Validated instance of model.
30
+ :raises ValueError: On security constraint violations.
31
+ :raises yaml.YAMLError: On malformed YAML.
32
+ :raises pydantic.ValidationError: On schema mismatch.
33
+ """
34
+ safe_path = self._validate_path(path)
35
+ raw: dict[str, Any] = self._read(safe_path)
36
+ return self._parse(raw, model)
37
+
38
+ def _validate_path(self, path: Traversable | Path) -> Traversable | Path:
39
+ self._check_extension(path.name)
40
+ if isinstance(path, Path):
41
+ return self._validate_filesystem_path(path)
42
+ return path
43
+
44
+ def _check_extension(self, name: str) -> None:
45
+ suffix = Path(name).suffix.lower()
46
+ if suffix not in _ALLOWED_EXTENSIONS:
47
+ raise ValueError(
48
+ f"Rejected file '{name}': extension '{suffix}' is not allowed. "
49
+ f"Accepted: {_ALLOWED_EXTENSIONS}"
50
+ )
51
+
52
+ def _validate_filesystem_path(self, path: Path) -> Path:
53
+ resolved = self._security_validator.validate_destination_path(str(path))
54
+
55
+ size = resolved.stat().st_size
56
+ if size > _MAX_FILE_SIZE_BYTES:
57
+ raise ValueError(
58
+ f"Rejected file '{path.name}': size {size} bytes exceeds "
59
+ f"the limit of {_MAX_FILE_SIZE_BYTES} bytes."
60
+ )
61
+
62
+ return resolved
63
+
64
+ def _read(self, path: Traversable | Path) -> dict[str, Any]:
65
+ # Python objects, enabling RCE.
66
+ # Reference: https://pyyaml.org/wiki/PyYAMLDocumentation#loading-yaml
67
+ with path.open("r", encoding="utf-8") as f:
68
+ # Secondary size guard for Traversable (no stat() available).
69
+ content = f.read(_MAX_FILE_SIZE_BYTES + 1)
70
+ if len(content) > _MAX_FILE_SIZE_BYTES:
71
+ raise ValueError(
72
+ f"Rejected file '{path.name}': content exceeds "
73
+ f"the limit of {_MAX_FILE_SIZE_BYTES} bytes."
74
+ )
75
+ data = yaml.safe_load(content)
76
+
77
+ if not isinstance(data, dict):
78
+ raise TypeError(
79
+ f"Expected a YAML mapping at the root of '{path.name}', got {type(data).__name__}."
80
+ )
81
+
82
+ return data
83
+
84
+ def _parse(self, raw: dict[str, Any], model: type[T]) -> T:
85
+ result = model.model_validate(raw)
86
+ assert isinstance(result, model)
87
+ return result
@@ -0,0 +1,107 @@
1
+ import sys
2
+ from dataclasses import dataclass
3
+ from functools import cache
4
+ from pathlib import Path
5
+ from typing import Final
6
+
7
+ from src.ar_infra.domain.enums.template_feature import TemplateFeature
8
+ from src.ar_infra.infrastructure.processor.yaml_processor import YamlFileProcessor
9
+ from src.ar_infra.infrastructure.template.feature_config_schema import (
10
+ FeatureConfigSchema,
11
+ FeatureSchema,
12
+ )
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class FeatureFiles:
17
+ shared_directories: list[str]
18
+ specific_directories: list[str]
19
+ shared_files: list[str]
20
+ specific_files: list[str]
21
+ shared_env_variables: list[str]
22
+ specific_env_variables: list[str]
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class FeatureDependencies:
27
+ shared: list[str]
28
+ specific: list[str]
29
+
30
+
31
+ _RESOURCES_DIR = Path(__file__).resolve().parents[2] / "cli" / "resources"
32
+
33
+
34
+ @cache
35
+ def _load_config() -> FeatureConfigSchema:
36
+ if getattr(sys, "frozen", False):
37
+ base = Path(sys._MEIPASS) # type: ignore[attr-defined] # pylint: disable=protected-access
38
+ conf = base / "ar_infra" / "cli" / "resources" / "feature-conf.yml"
39
+ else:
40
+ conf = _RESOURCES_DIR / "feature-conf.yml"
41
+ return YamlFileProcessor().load(conf, FeatureConfigSchema)
42
+
43
+
44
+ def _resolve(template: str, src: str, test: str) -> str:
45
+ return template.format(src_package=src, test_package=test)
46
+
47
+
48
+ def _resolve_list(items: list[str], src: str, test: str) -> list[str]:
49
+ return [_resolve(item, src, test) for item in items]
50
+
51
+
52
+ def _to_feature_files(schema: FeatureSchema, src: str, test: str) -> FeatureFiles:
53
+ f = schema.files
54
+
55
+ def r(items: list[str]) -> list[str]:
56
+ return _resolve_list(items, src, test)
57
+
58
+ return FeatureFiles(
59
+ shared_directories=r(f.shared_directories),
60
+ specific_directories=r(f.specific_directories),
61
+ shared_files=r(f.shared_files),
62
+ specific_files=r(f.specific_files),
63
+ shared_env_variables=f.shared_env_variables,
64
+ specific_env_variables=f.specific_env_variables,
65
+ )
66
+
67
+
68
+ def _to_feature_dependencies(schema: FeatureSchema) -> FeatureDependencies:
69
+ return FeatureDependencies(
70
+ shared=schema.dependencies.shared,
71
+ specific=schema.dependencies.specific,
72
+ )
73
+
74
+
75
+ def _to_enum(key: str) -> TemplateFeature:
76
+ return TemplateFeature[key]
77
+
78
+
79
+ def _build_feature_files() -> dict[TemplateFeature, FeatureFiles]:
80
+ cfg = _load_config()
81
+ return {
82
+ _to_enum(key): _to_feature_files(schema, cfg.src_package, cfg.test_package)
83
+ for key, schema in cfg.features.items()
84
+ }
85
+
86
+
87
+ def _build_feature_dependencies() -> dict[TemplateFeature, FeatureDependencies]:
88
+ cfg = _load_config()
89
+ return {_to_enum(key): _to_feature_dependencies(schema) for key, schema in cfg.features.items()}
90
+
91
+
92
+ FEATURE_FILES: Final[dict[TemplateFeature, FeatureFiles]] = _build_feature_files()
93
+ FEATURE_DEPENDENCIES: Final[dict[TemplateFeature, FeatureDependencies]] = (
94
+ _build_feature_dependencies()
95
+ )
96
+
97
+
98
+ def get_all_dependencies_for_features(enabled_features: set[TemplateFeature]) -> list[str]:
99
+ all_deps = set()
100
+
101
+ for feature in enabled_features:
102
+ feature_deps = FEATURE_DEPENDENCIES.get(feature)
103
+ if feature_deps:
104
+ all_deps.update(feature_deps.shared)
105
+ all_deps.update(feature_deps.specific)
106
+
107
+ return sorted(all_deps)
@@ -0,0 +1,34 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class FeatureFilesSchema(BaseModel):
5
+ shared_directories: list[str] = []
6
+ specific_directories: list[str] = []
7
+ shared_files: list[str] = []
8
+ specific_files: list[str] = []
9
+ shared_env_variables: list[str] = []
10
+ specific_env_variables: list[str] = []
11
+
12
+
13
+ class FeatureDependenciesSchema(BaseModel):
14
+ shared: list[str] = []
15
+ specific: list[str] = []
16
+
17
+
18
+ class FeatureSchema(BaseModel):
19
+ files: FeatureFilesSchema = FeatureFilesSchema()
20
+ dependencies: FeatureDependenciesSchema = FeatureDependenciesSchema()
21
+
22
+
23
+ class DatabaseSharedSchema(BaseModel):
24
+ directories: list[str] = []
25
+ files: list[str] = []
26
+ env_variables: list[str] = []
27
+ dependencies: list[str] = []
28
+
29
+
30
+ class FeatureConfigSchema(BaseModel):
31
+ src_package: str
32
+ test_package: str
33
+ database_shared: DatabaseSharedSchema
34
+ features: dict[str, FeatureSchema]
@@ -39,8 +39,8 @@ class FeatureManager:
39
39
  template_dir: Path,
40
40
  enabled_features: set[TemplateFeature],
41
41
  ) -> None:
42
- all_features = set(TemplateFeature)
43
- features_to_remove = all_features - enabled_features
42
+ all_features: set[TemplateFeature] = set(TemplateFeature)
43
+ features_to_remove: set[TemplateFeature] = all_features - enabled_features
44
44
 
45
45
  # Remove specific files/directories for disabled features
46
46
  for feature in features_to_remove:
@@ -1,202 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Final
3
-
4
- from src.ar_infra.domain.enums.template_feature import TemplateFeature
5
-
6
-
7
- SRC_PACKAGE = "src/main/java/com/example/arinfra/"
8
- TEST_PACKAGE = "src/test/java/com/example/arinfra/"
9
-
10
-
11
- @dataclass(frozen=True)
12
- class FeatureFiles:
13
- shared_directories: list[str]
14
- specific_directories: list[str]
15
- shared_files: list[str]
16
- specific_files: list[str]
17
- shared_env_variables: list[str]
18
- specific_env_variables: list[str]
19
-
20
-
21
- @dataclass(frozen=True)
22
- class FeatureDependencies:
23
- shared: list[str]
24
- specific: list[str]
25
-
26
-
27
- # Database shared resources (common to all database features)
28
- DATABASE_SHARED_DIRECTORIES: list[str] = [
29
- SRC_PACKAGE + "repository",
30
- "src/main/resources/db",
31
- ]
32
-
33
- DATABASE_SHARED_FILES: list[str] = [
34
- SRC_PACKAGE + "endpoint/rest/controller/health/HealthRepositoryController.java",
35
- SRC_PACKAGE + "service/health/HealthRepositoryService.java",
36
- TEST_PACKAGE + "endpoint/rest/controller/health/HealthRepositoryControllerIT.java",
37
- ]
38
-
39
- DATABASE_SHARED_ENV_VARIABLES: list[str] = [
40
- "SPRING_DATASOURCE_URL",
41
- "SPRING_DATASOURCE_USERNAME",
42
- "SPRING_DATASOURCE_PASSWORD",
43
- ]
44
-
45
- DATABASE_SHARED_DEPENDENCIES: list[str] = [
46
- "org.springframework.boot:spring-boot-starter-data-jpa",
47
- "org.flywaydb:flyway-core",
48
- ]
49
-
50
- FEATURE_FILES: Final[dict[TemplateFeature, FeatureFiles]] = {
51
- TemplateFeature.POSTGRESQL: FeatureFiles(
52
- shared_directories=[*DATABASE_SHARED_DIRECTORIES, TEST_PACKAGE + "conf/db"],
53
- specific_directories=[],
54
- shared_files=DATABASE_SHARED_FILES,
55
- specific_files=[TEST_PACKAGE + "conf/db/PostgresConf.java"],
56
- shared_env_variables=DATABASE_SHARED_ENV_VARIABLES,
57
- specific_env_variables=[],
58
- ),
59
- TemplateFeature.MYSQL: FeatureFiles(
60
- shared_directories=[*DATABASE_SHARED_DIRECTORIES, TEST_PACKAGE + "conf/db"],
61
- specific_directories=[],
62
- shared_files=DATABASE_SHARED_FILES,
63
- specific_files=[TEST_PACKAGE + "conf/db/MysqlConf.java"],
64
- shared_env_variables=DATABASE_SHARED_ENV_VARIABLES,
65
- specific_env_variables=[],
66
- ),
67
- TemplateFeature.RABBITMQ: FeatureFiles(
68
- shared_directories=[],
69
- specific_directories=[
70
- SRC_PACKAGE + "event",
71
- SRC_PACKAGE + "datastructure",
72
- ],
73
- shared_files=[],
74
- specific_files=[
75
- SRC_PACKAGE + "config/RabbitConfig.java",
76
- SRC_PACKAGE + "datastructure/ListGrouper.java",
77
- SRC_PACKAGE + "service/health/HealthEventService.java",
78
- SRC_PACKAGE + "endpoint/rest/controller/health/HealthEventController.java",
79
- TEST_PACKAGE + "conf/RabbitMQConf.java",
80
- TEST_PACKAGE + "service/health/HealthEventServiceIT.java",
81
- TEST_PACKAGE + "endpoint/rest/controller/health/HealthEventControllerIT.java",
82
- ],
83
- shared_env_variables=[],
84
- specific_env_variables=[
85
- "SPRING_RABBITMQ_HOST",
86
- "SPRING_RABBITMQ_USERNAME",
87
- "SPRING_RABBITMQ_PASSWORD",
88
- "SPRING_RABBITMQ_VHOST",
89
- "SPRING_RABBITMQ_QUEUE",
90
- "SPRING_RABBITMQ_EXCHANGE",
91
- "SPRING_RABBITMQ_ROUTING_KEY",
92
- "SPRING_RABBITMQ_PORT",
93
- "APP_RABBITMQ_SSL",
94
- ],
95
- ),
96
- TemplateFeature.S3_BUCKET: FeatureFiles(
97
- shared_directories=[],
98
- specific_directories=[
99
- SRC_PACKAGE + "exception/bucket",
100
- ],
101
- shared_files=[],
102
- specific_files=[
103
- SRC_PACKAGE + "config/BucketConf.java",
104
- SRC_PACKAGE + "file/BucketComponent.java",
105
- SRC_PACKAGE + "endpoint/rest/controller/health/HealthBucketController.java",
106
- SRC_PACKAGE + "service/health/HealthBucketService.java",
107
- TEST_PACKAGE + "conf/BucketConf.java",
108
- TEST_PACKAGE + "file/BucketComponentIT.java",
109
- TEST_PACKAGE + "service/health/HealthBucketServiceIT.java",
110
- TEST_PACKAGE + "endpoint/rest/controller/health/HealthBucketControllerIT.java",
111
- ],
112
- shared_env_variables=[],
113
- specific_env_variables=[
114
- "CLOUD_STORAGE_KEY_ID",
115
- "CLOUD_STORAGE_APPLICATION_KEY",
116
- "CLOUD_STORAGE_BUCKET_NAME",
117
- "CLOUD_STORAGE_FULL_ENDPOINT",
118
- "CLOUD_STORAGE_REGION",
119
- ],
120
- ),
121
- TemplateFeature.EMAIL: FeatureFiles(
122
- shared_directories=[],
123
- specific_directories=[
124
- SRC_PACKAGE + "mail",
125
- TEST_PACKAGE + "mail",
126
- ],
127
- shared_files=[],
128
- specific_files=[
129
- SRC_PACKAGE + "config/EmailConf.java",
130
- SRC_PACKAGE + "service/health/HealthEmailService.java",
131
- SRC_PACKAGE + "exception/EmailSendException.java",
132
- SRC_PACKAGE + "exception/health/EmailHealthCheckException.java",
133
- SRC_PACKAGE + "endpoint/rest/controller/health/HealthEmailController.java",
134
- TEST_PACKAGE + "conf/EmailConf.java",
135
- TEST_PACKAGE + "service/health/HealthEmailServiceIT.java",
136
- TEST_PACKAGE + "endpoint/rest/controller/health/HealthEmailControllerIT.java",
137
- ],
138
- shared_env_variables=[],
139
- specific_env_variables=[
140
- "SPRING_MAIL_HOST",
141
- "SPRING_MAIL_USERNAME",
142
- "SPRING_MAIL_PASSWORD",
143
- "SPRING_MAIL_FROM_EMAIL",
144
- "SPRING_MAIL_PORT",
145
- ],
146
- ),
147
- }
148
-
149
- FEATURE_DEPENDENCIES: Final[dict[TemplateFeature, FeatureDependencies]] = {
150
- TemplateFeature.POSTGRESQL: FeatureDependencies(
151
- shared=DATABASE_SHARED_DEPENDENCIES,
152
- specific=[
153
- "org.postgresql:postgresql",
154
- "org.flywaydb:flyway-database-postgresql",
155
- "org.testcontainers:postgresql",
156
- ],
157
- ),
158
- TemplateFeature.MYSQL: FeatureDependencies(
159
- shared=DATABASE_SHARED_DEPENDENCIES,
160
- specific=[
161
- "com.mysql:mysql-connector-j",
162
- "org.flywaydb:flyway-mysql",
163
- "org.testcontainers:mysql",
164
- ],
165
- ),
166
- TemplateFeature.RABBITMQ: FeatureDependencies(
167
- shared=[],
168
- specific=[
169
- "org.springframework.boot:spring-boot-starter-amqp",
170
- "org.springframework.amqp:spring-rabbit-test",
171
- "org.testcontainers:rabbitmq",
172
- ],
173
- ),
174
- TemplateFeature.S3_BUCKET: FeatureDependencies(
175
- shared=[],
176
- specific=[
177
- "software.amazon.awssdk:s3",
178
- "software.amazon.awssdk:s3-transfer-manager",
179
- "org.testcontainers:localstack",
180
- ],
181
- ),
182
- TemplateFeature.EMAIL: FeatureDependencies(
183
- shared=[],
184
- specific=[
185
- "org.springframework.boot:spring-boot-starter-mail",
186
- "com.icegreen:greenmail",
187
- "com.icegreen:greenmail-junit5",
188
- ],
189
- ),
190
- }
191
-
192
-
193
- def get_all_dependencies_for_features(enabled_features: set[TemplateFeature]) -> list[str]:
194
- all_deps = set()
195
-
196
- for feature in enabled_features:
197
- feature_deps = FEATURE_DEPENDENCIES.get(feature)
198
- if feature_deps:
199
- all_deps.update(feature_deps.shared)
200
- all_deps.update(feature_deps.specific)
201
-
202
- return sorted(all_deps)
File without changes
File without changes
File without changes