0
# Hook System
1
2
Pre-commit, post-commit, and setup hook system for extending bump-my-version functionality with custom automation workflows. Provides shell command execution at key points in the version bump process with full context access and error handling.
3
4
## Capabilities
5
6
### Hook Execution Functions
7
8
Functions for executing hooks at different stages of the version bump workflow.
9
10
```python { .api }
11
def run_setup_hooks(
12
config: Config,
13
current_version: Version,
14
dry_run: bool = False
15
) -> None:
16
"""
17
Execute setup hooks before version processing begins.
18
19
Runs configured setup hooks in sequence before any version analysis
20
or file modifications. Useful for environment preparation or validation.
21
22
Args:
23
config: Configuration containing setup_hooks list
24
current_version: Current Version object for context
25
dry_run: Preview operations without executing
26
27
Raises:
28
HookError: Hook execution failed
29
"""
30
31
def run_pre_commit_hooks(
32
config: Config,
33
current_version: Version,
34
new_version: Version,
35
dry_run: bool = False
36
) -> None:
37
"""
38
Execute pre-commit hooks before SCM operations.
39
40
Runs configured pre-commit hooks after file modifications but before
41
creating commits or tags. Ideal for formatting, linting, or testing.
42
43
Args:
44
config: Configuration containing pre_commit_hooks list
45
current_version: Current Version object
46
new_version: New Version object
47
dry_run: Preview operations without executing
48
49
Raises:
50
HookError: Hook execution failed
51
"""
52
53
def run_post_commit_hooks(
54
config: Config,
55
current_version: Version,
56
new_version: Version,
57
dry_run: bool = False
58
) -> None:
59
"""
60
Execute post-commit hooks after SCM operations complete.
61
62
Runs configured post-commit hooks after all version bump operations
63
including commits and tags. Perfect for deployment, notifications, or cleanup.
64
65
Args:
66
config: Configuration containing post_commit_hooks list
67
current_version: Current Version object
68
new_version: New Version object
69
dry_run: Preview operations without executing
70
71
Raises:
72
HookError: Hook execution failed
73
"""
74
```
75
76
### Hook Configuration
77
78
Hook settings are configured in the main bump-my-version configuration:
79
80
```python { .api }
81
class Config(BaseSettings):
82
# Hook configuration lists
83
setup_hooks: List[str] = []
84
pre_commit_hooks: List[str] = []
85
post_commit_hooks: List[str] = []
86
```
87
88
## Hook Types and Timing
89
90
### Setup Hooks
91
92
Execute before any version processing begins:
93
94
```toml
95
[tool.bumpversion]
96
setup_hooks = [
97
"echo 'Starting version bump process'",
98
"uv sync --upgrade",
99
"python -m pytest --no-cov -x",
100
"pre-commit run --all-files"
101
]
102
```
103
104
**Use Cases:**
105
- Environment validation and setup
106
- Dependency updates
107
- Pre-flight testing
108
- Code formatting and linting
109
- Documentation generation
110
111
### Pre-commit Hooks
112
113
Execute after file modifications but before SCM operations:
114
115
```toml
116
[tool.bumpversion]
117
pre_commit_hooks = [
118
"python setup.py check",
119
"uv build",
120
"twine check dist/*",
121
"git add dist/",
122
"git add CHANGELOG.md"
123
]
124
```
125
126
**Use Cases:**
127
- Package building and validation
128
- Additional file modifications
129
- Staging extra files for commit
130
- Running final tests
131
- Documentation updates
132
133
### Post-commit Hooks
134
135
Execute after all SCM operations complete:
136
137
```toml
138
[tool.bumpversion]
139
post_commit_hooks = [
140
"git push origin main",
141
"git push --tags",
142
"gh release create {new_version} --generate-notes",
143
"uv publish",
144
"slack-notify 'Released {new_version}'"
145
]
146
```
147
148
**Use Cases:**
149
- Pushing changes to remote repositories
150
- Creating GitHub/GitLab releases
151
- Publishing packages to registries
152
- Sending notifications
153
- Triggering CI/CD pipelines
154
- Deployment automation
155
156
## Hook Context and Templating
157
158
Hooks have access to the full template context including version information, timestamps, and SCM details:
159
160
### Available Context Variables
161
162
```python
163
context = {
164
# Version information
165
"current_version": "1.0.0",
166
"new_version": "1.0.1",
167
"major": "1",
168
"minor": "0",
169
"patch": "1",
170
171
# SCM information
172
"commit_sha": "abc123...",
173
"branch_name": "main",
174
"tag_name": "v1.0.1",
175
176
# Timestamps
177
"now": datetime.now(),
178
"utcnow": datetime.utcnow(),
179
180
# Environment variables (BMP_* prefixed)
181
"BMP_CUSTOM_VAR": "value"
182
}
183
```
184
185
### Template Usage in Hooks
186
187
```toml
188
[tool.bumpversion]
189
post_commit_hooks = [
190
"echo 'Released version {new_version} on {now:%Y-%m-%d}'",
191
"git tag -a archive-{current_version} -m 'Archive old version {current_version}'",
192
"curl -X POST https://api.example.com/releases -d version={new_version}",
193
"docker build -t myapp:{new_version} .",
194
"docker push myapp:{new_version}"
195
]
196
```
197
198
## Configuration Examples
199
200
### Basic Hook Configuration
201
202
```toml
203
[tool.bumpversion]
204
current_version = "1.0.0"
205
commit = true
206
tag = true
207
208
# Simple hooks
209
setup_hooks = ["python -m pytest"]
210
pre_commit_hooks = ["python setup.py check"]
211
post_commit_hooks = ["git push --follow-tags"]
212
```
213
214
### Advanced Hook Workflows
215
216
```toml
217
[tool.bumpversion]
218
current_version = "1.0.0"
219
commit = true
220
tag = true
221
222
# Comprehensive development workflow
223
setup_hooks = [
224
"echo 'Starting release process for {current_version} → {new_version}'",
225
"uv sync --upgrade",
226
"python -m pytest --cov=src --cov-report=html",
227
"pre-commit run --all-files",
228
"python -c 'import sys; sys.exit(0 if \"{new_version}\" > \"{current_version}\" else 1)'"
229
]
230
231
pre_commit_hooks = [
232
"uv build",
233
"twine check dist/*",
234
"python scripts/update_changelog.py {current_version} {new_version}",
235
"git add CHANGELOG.md",
236
"python scripts/update_docs.py {new_version}",
237
"git add docs/"
238
]
239
240
post_commit_hooks = [
241
"git push origin {branch_name}",
242
"git push --tags",
243
"gh release create {new_version} dist/* --generate-notes",
244
"uv publish --token $PYPI_TOKEN",
245
"python scripts/notify_slack.py 'Released {new_version}'",
246
"python scripts/update_dependencies.py {new_version}"
247
]
248
```
249
250
### CI/CD Integration Hooks
251
252
```toml
253
[tool.bumpversion]
254
# Hooks for CI/CD pipeline integration
255
setup_hooks = [
256
"echo 'CI Release Process Started'",
257
"echo 'RELEASE_VERSION={new_version}' >> $GITHUB_ENV"
258
]
259
260
pre_commit_hooks = [
261
"docker build -t myapp:{new_version} .",
262
"docker run --rm myapp:{new_version} python -m pytest",
263
"echo 'DOCKER_IMAGE=myapp:{new_version}' >> $GITHUB_ENV"
264
]
265
266
post_commit_hooks = [
267
"docker push myapp:{new_version}",
268
"docker tag myapp:{new_version} myapp:latest",
269
"docker push myapp:latest",
270
"kubectl set image deployment/myapp myapp=myapp:{new_version}"
271
]
272
```
273
274
## Usage Examples
275
276
### Programmatic Hook Execution
277
278
```python
279
from bumpversion.hooks import run_setup_hooks, run_pre_commit_hooks, run_post_commit_hooks
280
from bumpversion.config import get_configuration
281
from bumpversion.context import get_context
282
283
# Load configuration
284
config = get_configuration()
285
286
# Create context
287
context = get_context(config)
288
context.update({
289
"current_version": "1.0.0",
290
"new_version": "1.0.1"
291
})
292
293
# Create version objects
294
from bumpversion.versioning.models import Version
295
current_version = Version({"major": "1", "minor": "0", "patch": "0"}, None)
296
new_version = Version({"major": "1", "minor": "0", "patch": "1"}, None)
297
298
try:
299
# Execute hooks in sequence
300
run_setup_hooks(config, current_version, dry_run=False)
301
print("Setup hooks completed successfully")
302
303
# ... perform version bump operations ...
304
305
run_pre_commit_hooks(config, current_version, new_version, dry_run=False)
306
print("Pre-commit hooks completed successfully")
307
308
# ... perform SCM operations ...
309
310
run_post_commit_hooks(config, current_version, new_version, dry_run=False)
311
print("Post-commit hooks completed successfully")
312
313
except HookError as e:
314
print(f"Hook execution failed: {e}")
315
print(f"Failed command: {e.command}")
316
print(f"Exit code: {e.exit_code}")
317
print(f"Output: {e.output}")
318
```
319
320
### Custom Hook Runner
321
322
```python
323
import subprocess
324
from typing import List, Dict, Any
325
from bumpversion.exceptions import HookError
326
327
def run_custom_hooks(
328
hooks: List[str],
329
context: Dict[str, Any],
330
cwd: Optional[Path] = None
331
) -> None:
332
"""
333
Run custom hooks with enhanced error handling.
334
335
Args:
336
hooks: List of shell commands to execute
337
context: Template context for command formatting
338
cwd: Working directory for command execution
339
"""
340
for hook in hooks:
341
try:
342
# Format command with context
343
formatted_command = hook.format(**context)
344
print(f"Executing hook: {formatted_command}")
345
346
# Execute command
347
result = subprocess.run(
348
formatted_command,
349
shell=True,
350
cwd=cwd,
351
capture_output=True,
352
text=True,
353
check=True
354
)
355
356
print(f"Hook output: {result.stdout}")
357
358
except subprocess.CalledProcessError as e:
359
raise HookError(
360
f"Hook failed: {formatted_command}",
361
command=formatted_command,
362
exit_code=e.returncode,
363
output=e.stderr
364
)
365
```
366
367
### Conditional Hook Execution
368
369
```python
370
import os
371
from bumpversion.hooks import run_post_commit_hooks
372
373
# Only run deployment hooks in production environment
374
if os.getenv("ENVIRONMENT") == "production":
375
config.post_commit_hooks.extend([
376
"python scripts/deploy_production.py {new_version}",
377
"python scripts/notify_team.py 'Production deployment of {new_version} complete'"
378
])
379
380
# Run different hooks based on version type
381
if context.get("patch") != "0":
382
# Patch release
383
config.post_commit_hooks.append("python scripts/hotfix_notification.py")
384
elif context.get("minor") != "0":
385
# Minor release
386
config.post_commit_hooks.append("python scripts/feature_announcement.py")
387
else:
388
# Major release
389
config.post_commit_hooks.extend([
390
"python scripts/major_release_blog.py {new_version}",
391
"python scripts/social_media_announcement.py {new_version}"
392
])
393
```
394
395
### Hook Error Handling
396
397
```python
398
from bumpversion.hooks import run_pre_commit_hooks
399
from bumpversion.exceptions import HookError
400
401
try:
402
run_pre_commit_hooks(config, current_version, new_version)
403
except HookError as e:
404
print(f"Pre-commit hook failed: {e}")
405
406
# Check if it's a specific type of failure
407
if "pytest" in e.command:
408
print("Tests failed - aborting release")
409
sys.exit(1)
410
elif "build" in e.command:
411
print("Build failed - checking for dependency issues")
412
# Could implement recovery logic here
413
else:
414
print(f"Unknown hook failure: {e.command}")
415
print(f"Exit code: {e.exit_code}")
416
print(f"Error output: {e.output}")
417
```
418
419
### Environment-Specific Hooks
420
421
```toml
422
[tool.bumpversion]
423
# Base configuration
424
current_version = "1.0.0"
425
426
# Development environment hooks
427
setup_hooks = [
428
"python -c 'import os; print(f\"Environment: {os.getenv(\"ENVIRONMENT\", \"development\")}\")'",
429
"if [ \"$ENVIRONMENT\" = \"development\" ]; then echo 'Dev mode - skipping some checks'; fi"
430
]
431
432
post_commit_hooks = [
433
# Always push to origin
434
"git push origin {branch_name}",
435
"git push --tags",
436
437
# Conditional publishing
438
"if [ \"$ENVIRONMENT\" = \"production\" ]; then uv publish; fi",
439
"if [ \"$ENVIRONMENT\" = \"staging\" ]; then uv publish --repository testpypi; fi"
440
]
441
```
442
443
## Hook Best Practices
444
445
### Error Handling
446
447
- Always use proper exit codes in hook scripts
448
- Provide meaningful error messages
449
- Consider implementing rollback mechanisms
450
- Log hook execution for debugging
451
452
### Performance
453
454
- Keep hooks lightweight and fast
455
- Use parallel execution where possible
456
- Cache dependencies and build artifacts
457
- Avoid redundant operations
458
459
### Security
460
461
- Validate all inputs and context variables
462
- Use secure credential management
463
- Audit hook commands for security issues
464
- Limit hook permissions appropriately
465
466
### Testing
467
468
- Test hooks in isolation
469
- Use dry-run modes where available
470
- Implement hook validation scripts
471
- Test error conditions and recovery