Coverage for src/taipanstack/config/generators.py: 100%

41 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-06 15:01 +0000

1""" 

2Configuration file generators. 

3 

4This module generates configuration files (pyproject.toml, pre-commit, etc.) 

5with proper validation and templating. 

6""" 

7 

8from taipanstack.config.models import StackConfig 

9 

10 

11def _generate_ruff_config(target_version: str) -> str: 

12 """Generate Ruff configuration. 

13 

14 Args: 

15 target_version: The target Python version. 

16 

17 Returns: 

18 Ruff configuration string. 

19 

20 """ 

21 return f"""[tool.ruff] 

22line-length = 88 

23target-version = "{target_version}" 

24 

25[tool.ruff.lint] 

26select = [ 

27 "F", # Pyflakes 

28 "E", # pycodestyle errors 

29 "W", # pycodestyle warnings 

30 "I", # isort 

31 "N", # pep8-naming 

32 "D", # pydocstyle 

33 "Q", # flake8-quotes 

34 "S", # flake8-bandit 

35 "B", # flake8-bugbear 

36 "A", # flake8-builtins 

37 "C4", # flake8-comprehensions 

38 "T20", # flake8-print 

39 "SIM", # flake8-simplify 

40 "PTH", # flake8-use-pathlib 

41 "TID", # flake8-tidy-imports 

42 "ARG", # flake8-unused-arguments 

43 "PIE", # flake8-pie 

44 "PLC", # Pylint Convention 

45 "PLE", # Pylint Error 

46 "PLR", # Pylint Refactor 

47 "PLW", # Pylint Warning 

48 "RUF", # Ruff-specific 

49 "UP", # pyupgrade 

50 "ERA", # eradicate 

51 "TRY", # tryceratops 

52] 

53ignore = ["D203", "D212", "D213", "D416", "D417"] 

54 

55[tool.ruff.lint.mccabe] 

56max-complexity = 10 

57 

58[tool.ruff.lint.per-file-ignores] 

59"tests/**/*.py" = ["S101", "D"] 

60 

61[tool.ruff.format] 

62quote-style = "double" 

63indent-style = "space" 

64""" 

65 

66 

67def _generate_mypy_config(python_version: str) -> str: 

68 """Generate Mypy configuration. 

69 

70 Args: 

71 python_version: The target Python version. 

72 

73 Returns: 

74 Mypy configuration string. 

75 

76 """ 

77 return f"""[tool.mypy] 

78python_version = "{python_version}" 

79warn_return_any = true 

80warn_unused_configs = true 

81disallow_untyped_defs = true 

82disallow_any_unimported = false 

83no_implicit_optional = true 

84check_untyped_defs = true 

85strict_optional = true 

86strict_equality = true 

87ignore_missing_imports = true 

88show_error_codes = true 

89enable_error_code = ["ignore-without-code", "redundant-cast", "truthy-bool"] 

90""" 

91 

92 

93def _generate_pytest_config() -> str: 

94 """Generate Pytest configuration. 

95 

96 Returns: 

97 Pytest configuration string. 

98 

99 """ 

100 return """[tool.pytest.ini_options] 

101testpaths = ["tests"] 

102addopts = "-v --cov=src --cov-report=html --cov-report=term-missing --cov-fail-under=80 --strict-markers" 

103markers = [ 

104 "slow: marks tests as slow (deselect with '-m \"not slow\"')", 

105 "security: marks tests as security-related", 

106] 

107""" 

108 

109 

110def _generate_coverage_config() -> str: 

111 """Generate Coverage configuration. 

112 

113 Returns: 

114 Coverage configuration string. 

115 

116 """ 

117 return """[tool.coverage.run] 

118branch = true 

119source = ["src"] 

120omit = ["*/tests/*", "*/__pycache__/*"] 

121 

122[tool.coverage.report] 

123exclude_lines = [ 

124 "def __repr__", 

125 "raise NotImplementedError", 

126 "if TYPE_CHECKING:", 

127 "if __name__ == .__main__.:", 

128] 

129""" 

130 

131 

132def generate_pyproject_config(config: StackConfig) -> str: 

133 """Generate Ruff, Mypy, and Pytest configuration for pyproject.toml. 

134 

135 Args: 

136 config: The Stack configuration. 

137 

138 Returns: 

139 Configuration string to append to pyproject.toml. 

140 

141 """ 

142 target_version = config.to_target_version() 

143 python_version = config.python_version 

144 

145 return f""" 

146# --- Stack v2.0 Quality Configuration --- 

147{_generate_ruff_config(target_version)} 

148{_generate_mypy_config(python_version)} 

149{_generate_pytest_config()} 

150{_generate_coverage_config()}""" 

151 

152 

153def _generate_bandit_hook(severity: str) -> str: 

154 """Generate Bandit pre-commit hook. 

155 

156 Args: 

157 severity: Bandit severity level. 

158 

159 Returns: 

160 Bandit hook YAML string. 

161 

162 """ 

163 sev_char = severity[0].upper() 

164 return f""" 

165 - repo: https://github.com/PyCQA/bandit 

166 rev: '1.8.0' 

167 hooks: 

168 - id: bandit 

169 args: ["-r", ".", "-l{sev_char}"] 

170""" 

171 

172 

173def _generate_pip_audit_hook() -> str: 

174 """Generate pip-audit pre-commit hook. 

175 

176 Returns: 

177 pip-audit hook YAML string. 

178 

179 """ 

180 return """ 

181 - repo: https://github.com/pypa/pip-audit 

182 rev: 'v2.8.0' 

183 hooks: 

184 - id: pip-audit 

185""" 

186 

187 

188def _generate_semgrep_hook() -> str: 

189 """Generate Semgrep pre-commit hook. 

190 

191 Returns: 

192 Semgrep hook YAML string. 

193 

194 """ 

195 return """ 

196 - repo: https://github.com/semgrep/pre-commit 

197 rev: 'v1.99.0' 

198 hooks: 

199 - id: semgrep 

200 args: ['--config=auto'] 

201""" 

202 

203 

204def _generate_detect_secrets_hook() -> str: 

205 """Generate detect-secrets pre-commit hook. 

206 

207 Returns: 

208 Detect-secrets hook YAML string. 

209 

210 """ 

211 return """ 

212 - repo: https://github.com/Yelp/detect-secrets 

213 rev: 'v1.5.0' 

214 hooks: 

215 - id: detect-secrets 

216 args: ['--baseline', '.secrets.baseline'] 

217""" 

218 

219 

220def _generate_paranoid_hooks() -> str: 

221 """Generate extra security hooks for paranoid mode. 

222 

223 Returns: 

224 Paranoid hooks YAML string. 

225 

226 """ 

227 return """ 

228 - repo: https://github.com/trailofbits/pip-audit 

229 rev: 'v2.7.3' 

230 hooks: 

231 - id: pip-audit 

232 

233 - repo: https://github.com/jendrikseipp/vulture 

234 rev: 'v2.11' 

235 hooks: 

236 - id: vulture 

237 

238 - repo: https://github.com/guilatrova/tryceratops 

239 rev: 'v2.3.3' 

240 hooks: 

241 - id: tryceratops 

242""" 

243 

244 

245def _collect_security_hooks(config: StackConfig) -> list[str]: 

246 """Collect security hooks based on configuration. 

247 

248 Args: 

249 config: The Stack configuration. 

250 

251 Returns: 

252 A list of security hook YAML strings. 

253 

254 """ 

255 security_hooks: list[str] = [] 

256 

257 if config.security.enable_bandit: 

258 security_hooks.append(_generate_bandit_hook(config.security.bandit_severity)) 

259 

260 if config.security.enable_pip_audit: 

261 security_hooks.append(_generate_pip_audit_hook()) 

262 

263 if config.security.enable_semgrep: 

264 security_hooks.append(_generate_semgrep_hook()) 

265 

266 if config.security.enable_detect_secrets: 

267 security_hooks.append(_generate_detect_secrets_hook()) 

268 

269 # Add extra hooks for paranoid mode 

270 if config.security.level == "paranoid": 

271 security_hooks.append(_generate_paranoid_hooks()) 

272 

273 return security_hooks 

274 

275 

276def generate_pre_commit_config(config: StackConfig) -> str: 

277 """Generate .pre-commit-config.yaml content. 

278 

279 Args: 

280 config: The Stack configuration. 

281 

282 Returns: 

283 Pre-commit configuration YAML string. 

284 

285 """ 

286 security_hooks = _collect_security_hooks(config) 

287 

288 return f"""# Stack v2.0 Pre-commit Configuration 

289# Security Level: {config.security.level} 

290repos: 

291 - repo: https://github.com/pre-commit/pre-commit-hooks 

292 rev: v5.0.0 

293 hooks: 

294 - id: trailing-whitespace 

295 - id: end-of-file-fixer 

296 - id: check-yaml 

297 - id: check-added-large-files 

298 - id: check-merge-conflict 

299 - id: check-case-conflict 

300 - id: detect-private-key 

301 

302 - repo: https://github.com/astral-sh/ruff-pre-commit 

303 rev: 'v0.8.4' 

304 hooks: 

305 - id: ruff 

306 args: [--fix, --exit-non-zero-on-fix] 

307 - id: ruff-format 

308 

309 - repo: https://github.com/pre-commit/mirrors-mypy 

310 rev: 'v1.13.0' 

311 hooks: 

312 - id: mypy 

313 additional_dependencies: [types-all, pydantic] 

314{"".join(security_hooks)}""" 

315 

316 

317def generate_security_policy() -> str: 

318 """Generate SECURITY.md content. 

319 

320 Returns: 

321 Security policy markdown string. 

322 

323 """ 

324 return """# Security Policy 

325 

326## Supported Versions 

327 

328We prioritize security fixes for the latest version (Rolling Release). 

329 

330| Version | Supported | 

331| ------- | ------------------ | 

332| Latest | :white_check_mark: | 

333| Older | :x: | 

334 

335## Security Features 

336 

337This project includes multiple layers of security: 

338 

339- **SAST**: Bandit for static security analysis 

340- **SCA**: pip-audit for dependency vulnerabilities 

341- **Secrets**: detect-secrets for preventing credential leaks 

342- **Type Safety**: Mypy + Pydantic for runtime validation 

343- **Runtime Guards**: Protection against path traversal and injection 

344 

345## Reporting a Vulnerability 

346 

3471. **DO NOT** create a public issue for security vulnerabilities 

3482. Report via the [Security tab](../../security/advisories/new) 

3493. Or email the maintainer directly 

3504. Include: 

351 - Description of the vulnerability 

352 - Steps to reproduce 

353 - Potential impact 

354 - Suggested fix (if any) 

355 

356## Response Timeline 

357 

358- **Acknowledgment**: Within 48 hours 

359- **Initial Assessment**: Within 1 week 

360- **Fix Release**: Depends on severity (critical: ASAP, others: next release) 

361"""