Skip to content

Commit 7fe12c8

Browse files
authored
rename hook to create/drop app (#150)
* rename hooks to create/drop app * use ApplicationHooksModel * Add backward compatibility for legacy migration_hooks/pre/post config format - Enable Pydantic to accept both old and new field names via aliases - ApplicationHookModel accepts 'pre'/'post' as aliases for 'drop'/'create' - ConfigModel accepts 'migration_hooks' as alias for 'application_hooks' - Add model validators to handle legacy field name conversion - All existing tests pass + new backward compatibility test added - Both old format (.pum.yaml with migration_hooks/pre/post) and new format work seamlessly * yaml to files * reorganize
1 parent 5339b24 commit 7fe12c8

File tree

51 files changed

+232
-137
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+232
-137
lines changed

BACKWARD_COMPATIBILITY.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Backward Compatibility for Hook Configuration
2+
3+
## Overview
4+
5+
PUM now supports reading legacy `.pum.yaml` files that use the old field names `migration_hooks`, `pre`, and `post`. This ensures smooth migration for users upgrading to the new terminology.
6+
7+
## What Changed
8+
9+
The refactoring renamed hook-related fields for better clarity:
10+
- **Old**: `migration_hooks` with `pre`/`post` hooks
11+
- **New**: `application_hooks` with `drop`/`create` hooks
12+
13+
## Future Considerations
14+
15+
While backward compatibility is fully supported, we recommend updating `.pum.yaml` files to the new format at your convenience for clarity and consistency with documentation.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ PUM (PostgreSQL Upgrades Manager) is a robust database migration management tool
1616
- **Command-line and Python Integration**: Use PUM as a standalone CLI tool or integrate it into your Python project.
1717
- **Database Versioning**: Automatically manage database versioning with a metadata table.
1818
- **Changelog Management**: Apply and track SQL delta files for database upgrades.
19-
- **Migration Hooks**: Define custom hooks to execute additional SQL or Python code before or after migrations. This feature allows you to isolate data (table) code from application code (such as views and triggers), ensuring a clear separation of concerns and more maintainable database structures.
19+
- **Migration Hooks**: Define custom hooks to execute additional SQL or Python code to drop or create the application schema during migrations. This feature allows you to isolate data (table) code from application code (such as views and triggers), ensuring a clear separation of concerns and more maintainable database structures.
2020

2121
## Why PUM?
2222

docs/docs/configuration/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ In the config file `.pum.yaml`, you can define, with the YAML syntax:
66

77
* `changelogs_directory`: the directory with the changelogs files.
88
* `parameters`: the definition of parameters for the migration.
9-
* `migrations_hooks`: the `pre` and `post` migrations hooks.
9+
* `application_hooks`: the `drop` and `create` application schema hooks.
1010

1111
For example:
1212
```yaml
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
# MigrationHooksModel
1+
# ApplicationHookModel
22

3-
::: pum.config_model.MigrationHooksModel
3+
::: pum.config_model.ApplicationHookModel

docs/docs/hooks.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
# Migration hooks
22

3-
Migration hooks allow you to define actions to be executed before or after a migration. These hooks are defined in the `.pum.yaml` configuration file under the `migration_hooks` section.
3+
Migration hooks allow you to define actions to be executed during the migration process. These hooks are defined in the `.pum.yaml` configuration file under the `application_hooks` section.
44

55
There are two types of migration hooks:
66

7-
- `pre`: Executed before the migration.
8-
- `post`: Executed after the migration.
7+
- `drop`: Hooks to drop the application schema before applying migrations.
8+
- `create`: Hooks to create the application schema after applying migrations.
99

1010
## SQL hooks
1111

1212
Hooks are defined as a list of files or plain SQL code to be executed. For example:
1313

1414
```yaml
15-
migration_hooks:
16-
pre:
15+
application_hooks:
16+
drop:
1717
- code: DROP VIEW IF EXISTS pum_test_app.some_view;
1818

19-
post:
20-
- file: post/create_view.sql
19+
create:
20+
- file: create_app/create_view.sql
2121
```
2222
2323
## Python hooks
@@ -32,16 +32,16 @@ You can use `pum.utils.execute_sql` to execute the SQL code without committing.
3232
The configuration is then:
3333

3434
```yaml
35-
migration_hooks:
36-
pre:
37-
- file: pre/drop_view.sql
35+
application_hooks:
36+
drop:
37+
- file: drop_app/drop_view.sql
3838
39-
post:
40-
- file: post/create_schema.sql
41-
- file: post/create_view.py
39+
create:
40+
- file: create_app/create_schema.sql
41+
- file: create_app/create_view.py
4242
```
4343

44-
With `post/create_view.py`:
44+
With `create_app/create_view.py`:
4545

4646
```py
4747
from pirogue.utils import select_columns
@@ -85,7 +85,7 @@ class Hook(HookBase):
8585
> Local imports within the hook file are supported. The parent directory of the hook file is temporarily added to `sys.path` during execution, so you can use local imports in your hook scripts. Ensure your hook files and their dependencies are structured accordingly.
8686

8787

88-
## Data and application isoltion
88+
## Data and application isolation
8989

9090
We recommend to isolate data (tables) from business logic (e.g., views, triggers) into distinct schemas for easier upgrades.
9191
This will facilitate the migrations but also the code management: you will not have to write diff files for views and triggers.
@@ -102,7 +102,7 @@ project/
102102
│ │ ├── 01_rename_column.sql
103103
│ │ └── 02_do_something_else.sql
104104
├── app/
105-
│ ├── drop_views_and_triggers.sql
106-
│ └── create_views_and_triggers.sql
105+
│ ├── drop_app.sql
106+
│ └── create_app.sql
107107
└── .pum.yaml
108108
```

docs/mkdocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ nav:
7272
- ConfigModel: configuration/config_model.md
7373
- DependencyModel: configuration/dependency_model.md
7474
- HookModel: configuration/hook_model.md
75-
- MigrationHooksModel: configuration/migration_hooks_model.md
75+
- ApplicationHookModel: configuration/migration_hooks_model.md
7676
- ParameterDefinitionModel: configuration/parameter_definition_model.md
7777
- PermissionModel: configuration/permission_model.md
7878
- PumModel: configuration/pum_model.md

pum/cli.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,13 @@ def create_parser() -> argparse.ArgumentParser:
217217
action="store_true",
218218
)
219219
parser_install.add_argument(
220-
"--skip-pre-hooks",
221-
help="Skip pre-hook handlers during installation.",
220+
"--skip-drop-app",
221+
help="Skip drop app handlers during installation.",
222222
action="store_true",
223223
)
224224
parser_install.add_argument(
225-
"--skip-post-hooks",
226-
help="Skip post-hook handlers during installation.",
225+
"--skip-create-app",
226+
help="Skip create app handlers during installation.",
227227
action="store_true",
228228
)
229229

@@ -241,13 +241,13 @@ def create_parser() -> argparse.ArgumentParser:
241241
"--beta-testing", help="Install in beta testing mode.", action="store_true"
242242
)
243243
parser_upgrade.add_argument(
244-
"--skip-pre-hooks",
245-
help="Skip pre-hook handlers during upgrade.",
244+
"--skip-drop-app",
245+
help="Skip drop app handlers during upgrade.",
246246
action="store_true",
247247
)
248248
parser_upgrade.add_argument(
249-
"--skip-post-hooks",
250-
help="Skip post-hook handlers during upgrade.",
249+
"--skip-create-app",
250+
help="Skip create app handlers during upgrade.",
251251
action="store_true",
252252
)
253253

@@ -397,8 +397,8 @@ def cli() -> int: # noqa: PLR0912
397397
roles=args.roles,
398398
grant=args.grant,
399399
beta_testing=args.beta_testing,
400-
skip_pre_hooks=args.skip_pre_hooks,
401-
skip_post_hooks=args.skip_post_hooks,
400+
skip_drop_app=args.skip_drop_app,
401+
skip_create_app=args.skip_create_app,
402402
)
403403
conn.commit()
404404
if args.demo_data:
@@ -407,8 +407,8 @@ def cli() -> int: # noqa: PLR0912
407407
connection=conn,
408408
parameters=parameters,
409409
grant=args.grant,
410-
skip_post_hooks=args.skip_post_hooks,
411-
skip_pre_hooks=args.skip_pre_hooks,
410+
skip_create_app=args.skip_create_app,
411+
skip_drop_app=args.skip_drop_app,
412412
)
413413
elif args.command == "upgrade":
414414
upg = Upgrader(config=config)
@@ -417,8 +417,8 @@ def cli() -> int: # noqa: PLR0912
417417
parameters=parameters,
418418
max_version=args.max_version,
419419
beta_testing=args.beta_testing,
420-
skip_pre_hooks=args.skip_pre_hooks,
421-
skip_post_hooks=args.skip_post_hooks,
420+
skip_drop_app=args.skip_drop_app,
421+
skip_create_app=args.skip_create_app,
422422
)
423423
elif args.command == "role":
424424
if not args.action:

pum/config_model.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
class PumCustomBaseModel(BaseModel):
11-
model_config = ConfigDict(extra="forbid")
11+
model_config = ConfigDict(extra="forbid", populate_by_name=True)
1212

1313

1414
class ParameterDefinitionModel(PumCustomBaseModel):
@@ -54,17 +54,27 @@ def validate_args(self):
5454
return self
5555

5656

57-
class MigrationHooksModel(PumCustomBaseModel):
57+
class ApplicationHookModel(PumCustomBaseModel):
5858
"""
59-
MigrationHooksModel holds the configuration for migration hooks.
59+
ApplicationHookModel holds the configuration for application schema hooks.
6060
6161
Attributes:
62-
pre: List of pre-migration hooks.
63-
post: List of post-migration hooks.
62+
drop: Hooks to drop the application schema before applying migrations.
63+
create: Hooks to create the application schema after applying migrations.
6464
"""
6565

66-
pre: Optional[List[HookModel]] = []
67-
post: Optional[List[HookModel]] = []
66+
drop: Optional[List[HookModel]] = Field(default=[], alias="pre")
67+
create: Optional[List[HookModel]] = Field(default=[], alias="post")
68+
69+
@model_validator(mode="before")
70+
def handle_legacy_names(cls, values):
71+
"""Support legacy field names for backward compatibility."""
72+
# If new names don't exist but old names do, use old names
73+
if "drop" not in values and "pre" in values:
74+
values["drop"] = values.pop("pre")
75+
if "create" not in values and "post" in values:
76+
values["create"] = values.pop("post")
77+
return values
6878

6979

7080
class PumModel(PumCustomBaseModel):
@@ -187,15 +197,25 @@ class ConfigModel(PumCustomBaseModel):
187197
Attributes:
188198
pum: The PUM (Project Update Manager) configuration. Defaults to a new PumModel instance.
189199
parameters: List of parameter definitions. Defaults to an empty list.
190-
migration_hooks: Configuration for migration hooks. Defaults to a new MigrationHooksModel instance.
200+
application_hooks: Configuration for application schema hooks. Defaults to a new ApplicationHookModel instance.
191201
changelogs_directory: Directory path for changelogs. Defaults to "changelogs".
192202
roles: List of role definitions. Defaults to None.
193203
"""
194204

195205
pum: Optional[PumModel] = Field(default_factory=PumModel)
196206
parameters: Optional[List[ParameterDefinitionModel]] = []
197-
migration_hooks: Optional[MigrationHooksModel] = Field(default_factory=MigrationHooksModel)
207+
application_hooks: Optional[ApplicationHookModel] = Field(
208+
default_factory=ApplicationHookModel, alias="migration_hooks"
209+
)
198210
changelogs_directory: Optional[str] = "changelogs"
199211
roles: Optional[List[RoleModel]] = []
200212
demo_data: Optional[List[DemoDataModel]] = []
201213
dependencies: Optional[List[DependencyModel]] = []
214+
215+
@model_validator(mode="before")
216+
def handle_legacy_field_names(cls, values):
217+
"""Support legacy field names for backward compatibility."""
218+
# If new name doesn't exist but old name does, use old name
219+
if "application_hooks" not in values and "migration_hooks" in values:
220+
values["application_hooks"] = values.pop("migration_hooks")
221+
return values

pum/pum_config.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -245,25 +245,25 @@ def role_manager(self) -> RoleManager:
245245
return RoleManager([])
246246
return RoleManager([role.model_dump() for role in self.config.roles])
247247

248-
def pre_hook_handlers(self) -> list[HookHandler]:
249-
"""Return the list of pre-migration hook handlers."""
248+
def drop_app_handlers(self) -> list[HookHandler]:
249+
"""Return the list of drop app hook handlers."""
250250
return (
251251
[
252252
HookHandler(base_path=self._base_path, **hook.model_dump())
253-
for hook in self.config.migration_hooks.pre
253+
for hook in self.config.application_hooks.drop
254254
]
255-
if self.config.migration_hooks.pre
255+
if self.config.application_hooks.drop
256256
else []
257257
)
258258

259-
def post_hook_handlers(self) -> list[HookHandler]:
260-
"""Return the list of post-migration hook handlers."""
259+
def create_app_handlers(self) -> list[HookHandler]:
260+
"""Return the list of create app hook handlers."""
261261
return (
262262
[
263263
HookHandler(base_path=self._base_path, **hook.model_dump())
264-
for hook in self.config.migration_hooks.post
264+
for hook in self.config.application_hooks.create
265265
]
266-
if self.config.migration_hooks.post
266+
if self.config.application_hooks.create
267267
else []
268268
)
269269

@@ -311,10 +311,10 @@ def validate(self, install_dependencies: bool = False) -> None:
311311
raise PumInvalidChangelog(f"Changelog `{changelog}` is invalid.") from e
312312

313313
hook_handlers = []
314-
if self.config.migration_hooks.pre:
315-
hook_handlers.extend(self.pre_hook_handlers())
316-
if self.config.migration_hooks.post:
317-
hook_handlers.extend(self.post_hook_handlers())
314+
if self.config.application_hooks.drop:
315+
hook_handlers.extend(self.drop_app_handlers())
316+
if self.config.application_hooks.create:
317+
hook_handlers.extend(self.create_app_handlers())
318318
for hook_handler in hook_handlers:
319319
try:
320320
hook_handler.validate(parameter_defaults)

0 commit comments

Comments
 (0)