half-orm-dev 0.16.0a9__py3-none-any.whl

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 (58) hide show
  1. half_orm_dev/__init__.py +1 -0
  2. half_orm_dev/cli/__init__.py +9 -0
  3. half_orm_dev/cli/commands/__init__.py +56 -0
  4. half_orm_dev/cli/commands/apply.py +13 -0
  5. half_orm_dev/cli/commands/clone.py +102 -0
  6. half_orm_dev/cli/commands/init.py +331 -0
  7. half_orm_dev/cli/commands/new.py +15 -0
  8. half_orm_dev/cli/commands/patch.py +317 -0
  9. half_orm_dev/cli/commands/prepare.py +21 -0
  10. half_orm_dev/cli/commands/prepare_release.py +119 -0
  11. half_orm_dev/cli/commands/promote_to.py +127 -0
  12. half_orm_dev/cli/commands/release.py +344 -0
  13. half_orm_dev/cli/commands/restore.py +14 -0
  14. half_orm_dev/cli/commands/sync.py +13 -0
  15. half_orm_dev/cli/commands/todo.py +73 -0
  16. half_orm_dev/cli/commands/undo.py +17 -0
  17. half_orm_dev/cli/commands/update.py +73 -0
  18. half_orm_dev/cli/commands/upgrade.py +191 -0
  19. half_orm_dev/cli/main.py +103 -0
  20. half_orm_dev/cli_extension.py +38 -0
  21. half_orm_dev/database.py +1389 -0
  22. half_orm_dev/hgit.py +1025 -0
  23. half_orm_dev/hop.py +167 -0
  24. half_orm_dev/manifest.py +43 -0
  25. half_orm_dev/modules.py +456 -0
  26. half_orm_dev/patch.py +281 -0
  27. half_orm_dev/patch_manager.py +1694 -0
  28. half_orm_dev/patch_validator.py +335 -0
  29. half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +34 -0
  30. half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +2 -0
  31. half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +3 -0
  32. half_orm_dev/patches/log +2 -0
  33. half_orm_dev/patches/sql/half_orm_meta.sql +208 -0
  34. half_orm_dev/release_manager.py +2841 -0
  35. half_orm_dev/repo.py +1562 -0
  36. half_orm_dev/templates/.gitignore +15 -0
  37. half_orm_dev/templates/MANIFEST.in +1 -0
  38. half_orm_dev/templates/Pipfile +13 -0
  39. half_orm_dev/templates/README +25 -0
  40. half_orm_dev/templates/conftest_template +42 -0
  41. half_orm_dev/templates/init_module_template +10 -0
  42. half_orm_dev/templates/module_template_1 +12 -0
  43. half_orm_dev/templates/module_template_2 +6 -0
  44. half_orm_dev/templates/module_template_3 +3 -0
  45. half_orm_dev/templates/relation_test +23 -0
  46. half_orm_dev/templates/setup.py +81 -0
  47. half_orm_dev/templates/sql_adapter +9 -0
  48. half_orm_dev/templates/warning +12 -0
  49. half_orm_dev/utils.py +49 -0
  50. half_orm_dev/version.txt +1 -0
  51. half_orm_dev-0.16.0a9.dist-info/METADATA +935 -0
  52. half_orm_dev-0.16.0a9.dist-info/RECORD +58 -0
  53. half_orm_dev-0.16.0a9.dist-info/WHEEL +5 -0
  54. half_orm_dev-0.16.0a9.dist-info/licenses/AUTHORS +3 -0
  55. half_orm_dev-0.16.0a9.dist-info/licenses/LICENSE +14 -0
  56. half_orm_dev-0.16.0a9.dist-info/top_level.txt +2 -0
  57. tests/__init__.py +0 -0
  58. tests/conftest.py +329 -0
@@ -0,0 +1,344 @@
1
+ """
2
+ Release command group - Unified release management.
3
+
4
+ Groups all release-related commands under 'half_orm dev release':
5
+ - release new: Prepare next release stage file
6
+ - release promote: Promote stage to rc or production
7
+
8
+ Replaces legacy commands:
9
+ - prepare-release → release new
10
+ - promote-to → release promote
11
+ """
12
+
13
+ import click
14
+ import sys
15
+ from typing import Optional
16
+
17
+ from half_orm_dev.repo import Repo
18
+ from half_orm_dev.release_manager import (
19
+ ReleaseManagerError,
20
+ ReleaseFileError,
21
+ ReleaseVersionError
22
+ )
23
+ from half_orm import utils
24
+
25
+
26
+ @click.group()
27
+ def release():
28
+ """
29
+ Release management commands.
30
+
31
+ Prepare, promote, and deploy releases with this unified command group.
32
+
33
+ \b
34
+ Common workflow:
35
+ 1. half_orm dev release new <level>
36
+ 2. half_orm dev patch add <patch_id>
37
+ 3. half_orm dev release promote rc
38
+ 4. half_orm dev release promote prod
39
+ """
40
+ pass
41
+
42
+
43
+ @release.command('new')
44
+ @click.argument(
45
+ 'level',
46
+ type=click.Choice(['patch', 'minor', 'major'], case_sensitive=False)
47
+ )
48
+ def release_new(level: str) -> None:
49
+ """
50
+ Prepare next release stage file.
51
+
52
+ Creates releases/X.Y.Z-stage.txt based on production version and
53
+ semantic versioning increment level.
54
+
55
+ \b
56
+ LEVEL: Version increment type (patch, minor, or major)
57
+
58
+ \b
59
+ Semantic versioning rules:
60
+ • patch: Bug fixes, minor changes (1.3.5 → 1.3.6)
61
+ • minor: New features, backward compatible (1.3.5 → 1.4.0)
62
+ • major: Breaking changes (1.3.5 → 2.0.0)
63
+
64
+ \b
65
+ Workflow:
66
+ 1. Read production version from model/schema.sql
67
+ 2. Calculate next version (patch/minor/major)
68
+ 3. Create releases/X.Y.Z-stage.txt
69
+ 4. Commit and push to reserve version globally
70
+
71
+ \b
72
+ Requirements:
73
+ • Must be on ho-prod branch
74
+ • Repository must be clean (no uncommitted changes)
75
+ • Must be synced with origin/ho-prod
76
+
77
+ \b
78
+ Examples:
79
+ Prepare patch release (production 1.3.5 → 1.3.6):
80
+ $ half_orm dev release new patch
81
+
82
+ Prepare minor release (production 1.3.5 → 1.4.0):
83
+ $ half_orm dev release new minor
84
+
85
+ Prepare major release (production 1.3.5 → 2.0.0):
86
+ $ half_orm dev release new major
87
+
88
+ \b
89
+ Next steps after release new:
90
+ • Create patches: half_orm dev patch new <patch_id>
91
+ • Add to release: half_orm dev patch add <patch_id>
92
+ • Promote to RC: half_orm dev release promote rc
93
+ """
94
+ # Normalize level to lowercase
95
+ level = level.lower()
96
+
97
+ try:
98
+ # Get Repo singleton
99
+ repo = Repo()
100
+
101
+ # Get ReleaseManager
102
+ release_mgr = repo.release_manager
103
+
104
+ click.echo(f"Preparing {level} release...")
105
+ click.echo()
106
+
107
+ # Prepare release
108
+ result = release_mgr.prepare_release(level)
109
+
110
+ # Extract result info
111
+ version = result['version']
112
+ stage_file = result['file']
113
+ previous_version = result['previous_version']
114
+
115
+ # Success message
116
+ click.echo(f"✅ {utils.Color.bold('Release prepared successfully!')}")
117
+ click.echo()
118
+ click.echo(f" Previous version: {utils.Color.bold(previous_version)}")
119
+ click.echo(f" New version: {utils.Color.bold(version)}")
120
+ click.echo(f" Stage file: {utils.Color.bold(stage_file)}")
121
+ click.echo()
122
+ click.echo(f"📝 Next steps:")
123
+ click.echo(f" 1. Create patches: {utils.Color.bold(f'half_orm dev patch new <patch_id>')}")
124
+ click.echo(f" 2. Add to release: {utils.Color.bold(f'half_orm dev patch add <patch_id>')}")
125
+ click.echo(f" 3. Promote to RC: {utils.Color.bold('half_orm dev release promote rc')}")
126
+ click.echo()
127
+
128
+ except ReleaseManagerError as e:
129
+ # Handle validation errors (branch, clean, sync, etc.)
130
+ click.echo(f"❌ {utils.Color.red('Release preparation failed:')}", err=True)
131
+ click.echo(f" {str(e)}", err=True)
132
+ sys.exit(1)
133
+
134
+ except ReleaseFileError as e:
135
+ # Handle file errors (missing schema, stage exists, etc.)
136
+ click.echo(f"❌ {utils.Color.red('File error:')}", err=True)
137
+ click.echo(f" {str(e)}", err=True)
138
+ sys.exit(1)
139
+
140
+ except ReleaseVersionError as e:
141
+ # Handle version errors (invalid format, calculation, etc.)
142
+ click.echo(f"❌ {utils.Color.red('Version error:')}", err=True)
143
+ click.echo(f" {str(e)}", err=True)
144
+ sys.exit(1)
145
+
146
+
147
+ @release.command('promote')
148
+ @click.argument('target', type=click.Choice(['rc', 'prod'], case_sensitive=False))
149
+ def release_promote(target: str) -> None:
150
+ """
151
+ Promote stage release to RC or production.
152
+
153
+ Promotes the smallest stage release to RC (rc1, rc2, etc.) or promotes
154
+ an RC to production. Merges archived patch code into ho-prod and
155
+ manages branch cleanup. Must be run from ho-prod branch.
156
+
157
+ \b
158
+ TARGET: Either 'rc' or 'prod'
159
+ • rc: Promotes stage to release candidate (with branch cleanup)
160
+ • prod: Promotes RC to production release (generates schema dumps)
161
+
162
+ \b
163
+ Complete workflow for RC:
164
+ 1. Detect smallest stage release (sequential promotion)
165
+ 2. Validate single active RC rule
166
+ 3. Acquire distributed lock on ho-prod
167
+ 4. Merge archived patches code into ho-prod
168
+ 5. Rename stage file to RC file (git mv)
169
+ 6. Commit and push promotion
170
+ 7. Send rebase notifications to active branches
171
+ 8. Cleanup patch branches
172
+ 9. Release lock
173
+
174
+ \b
175
+ Complete workflow for Production:
176
+ 1. Detect latest RC file
177
+ 2. Validate sequential version rule
178
+ 3. Acquire distributed lock on ho-prod
179
+ 4. Restore database and apply all patches
180
+ 5. Generate schema-X.Y.Z.sql and metadata-X.Y.Z.sql
181
+ 6. Update schema.sql symlink
182
+ 7. Rename RC file to production file (git mv)
183
+ 8. Commit and push promotion
184
+ 9. Release lock
185
+
186
+ \b
187
+ Examples:
188
+ Promote smallest stage release to RC:
189
+ $ half_orm dev release promote rc
190
+
191
+ Output:
192
+ ✓ Promoted 1.3.5-stage → 1.3.5-rc1
193
+ ✓ Merged 3 patches into ho-prod
194
+ ✓ Deleted 3 patch branches
195
+ ✓ Notified 2 active branches
196
+
197
+ Promote RC to production:
198
+ $ half_orm dev release promote prod
199
+
200
+ Output:
201
+ ✓ Promoted 1.3.5-rc1 → 1.3.5
202
+ ✓ Generated schema-1.3.5.sql
203
+ ✓ Generated metadata-1.3.5.sql
204
+ ✓ Updated schema.sql → schema-1.3.5.sql
205
+
206
+ \b
207
+ Next steps after promote rc:
208
+ • Test RC: Run integration tests
209
+ • Fix issues: Create patches, add to new stage, promote again
210
+ • Deploy: half_orm dev release promote prod
211
+
212
+ \b
213
+ Next steps after promote prod:
214
+ • Tag release: git tag v1.3.5
215
+ • Deploy to production: Use db upgrade on production servers
216
+ • Start next cycle: half_orm dev release new patch
217
+
218
+ \b
219
+ Raises:
220
+ click.ClickException: If validations fail or workflow errors occur
221
+ """
222
+ try:
223
+ # Get repository instance
224
+ repo = Repo()
225
+
226
+ # Delegate to ReleaseManager
227
+ click.echo(f"Promoting release to {target.upper()}...")
228
+ click.echo()
229
+
230
+ result = repo.release_manager.promote_to(target.lower())
231
+
232
+ # Display success message
233
+ click.echo(f"✓ {utils.Color.green('Success!')}")
234
+ click.echo()
235
+
236
+ # Target-specific output
237
+ if target.lower() == 'rc':
238
+ # RC promotion output
239
+ click.echo(f" Promoted: {utils.Color.bold(result['from_file'])} → {utils.Color.bold(result['to_file'])}")
240
+ patches = result.get('patches_merged')
241
+ if patches:
242
+ click.echo(f" Patches merged: {utils.Color.bold(str(len(patches)))} patch(es)")
243
+ click.echo(f" Branches cleaned: {utils.Color.bold(str(len(result['branches_deleted'])))} branch(es)")
244
+
245
+ if result.get('notified_branches'):
246
+ click.echo(f" Notified: {len(result['notified_branches'])} active branch(es)")
247
+
248
+ click.echo()
249
+ click.echo("📝 Next steps:")
250
+ click.echo(f" • Test RC thoroughly")
251
+ click.echo(f" • Fix issues: Create patch, add to new stage, promote again")
252
+ click.echo(f" • Deploy to production: {utils.Color.bold('half_orm dev release promote prod')}")
253
+
254
+ else:
255
+ # Production promotion output
256
+ click.echo(f" Promoted: {utils.Color.bold(result['from_file'])} → {utils.Color.bold(result['to_file'])}")
257
+ click.echo(f" Version: {utils.Color.bold(result['version'])}")
258
+
259
+ if result.get('schema_file'):
260
+ click.echo(f" Schema: {utils.Color.bold(result['schema_file'])}")
261
+ if result.get('metadata_file'):
262
+ click.echo(f" Metadata: {utils.Color.bold(result['metadata_file'])}")
263
+ if result.get('symlink_updated'):
264
+ click.echo(f" Symlink: schema.sql → {utils.Color.bold(result['schema_file'])}")
265
+
266
+ click.echo()
267
+ click.echo("📝 Next steps:")
268
+ click.echo(f""" • Tag release: {utils.Color.bold(f'git tag v{result["version"]}')}""")
269
+ click.echo(f" • Deploy to production servers: {utils.Color.bold('half_orm dev db upgrade')}")
270
+ click.echo(f" • Start next cycle: {utils.Color.bold('half_orm dev release new patch')}")
271
+
272
+ click.echo()
273
+
274
+ except ReleaseManagerError as e:
275
+ raise click.ClickException(str(e))
276
+
277
+
278
+ @release.command('hotfix')
279
+ @click.argument('patch_id', type=str)
280
+ def release_hotfix(patch_id: str) -> None:
281
+ """
282
+ Create emergency hotfix release (NOT IMPLEMENTED YET).
283
+
284
+ Creates a hotfix release that bypasses the normal stage → rc → prod
285
+ workflow for critical production issues.
286
+
287
+ \b
288
+ Args:
289
+ patch_id: Patch identifier for the hotfix
290
+
291
+ \b
292
+ Example:
293
+ $ half_orm dev release hotfix critical-security-fix
294
+
295
+ \b
296
+ Status: 🚧 Not implemented - planned for future release
297
+ """
298
+ click.echo("🚧 Hotfix release creation not implemented yet")
299
+ click.echo()
300
+ click.echo("Planned workflow:")
301
+ click.echo(" 1. Create ho-patch/PATCH_ID from ho-prod")
302
+ click.echo(" 2. Create releases/X.Y.Z-hotfixN.txt")
303
+ click.echo(" 3. Emergency deployment workflow")
304
+ click.echo()
305
+ raise NotImplementedError("Hotfix release creation not yet implemented")
306
+
307
+
308
+ @release.command('apply')
309
+ @click.argument('version', type=str, required=False)
310
+ def release_apply(version: Optional[str] = None) -> None:
311
+ """
312
+ Test complete release before deployment (NOT IMPLEMENTED YET).
313
+
314
+ Applies all patches from a release file to test the complete
315
+ release workflow before production deployment.
316
+
317
+ \b
318
+ Args:
319
+ version: Release version to test (e.g., "1.3.5-rc1")
320
+ If not provided, applies latest RC
321
+
322
+ \b
323
+ Examples:
324
+ Test latest RC:
325
+ $ half_orm dev release apply
326
+
327
+ Test specific RC:
328
+ $ half_orm dev release apply 1.3.5-rc1
329
+
330
+ Test stage release:
331
+ $ half_orm dev release apply 1.3.5-stage
332
+
333
+ \b
334
+ Status: 🚧 Not implemented - planned for future release
335
+ """
336
+ click.echo("🚧 Release testing not implemented yet")
337
+ click.echo()
338
+ click.echo("Planned workflow:")
339
+ click.echo(" 1. Restore database from model/schema.sql")
340
+ click.echo(" 2. Apply all patches from release file")
341
+ click.echo(" 3. Run comprehensive tests")
342
+ click.echo(" 4. Validate final state")
343
+ click.echo()
344
+ raise NotImplementedError("Release apply not yet implemented")
@@ -0,0 +1,14 @@
1
+ """
2
+ Restore command - Restore to release
3
+ """
4
+
5
+ import click
6
+ from half_orm_dev.repo import Repo
7
+
8
+
9
+ @click.command()
10
+ @click.argument('release')
11
+ def restore(release):
12
+ """Restore to release."""
13
+ repo = Repo()
14
+ repo.restore(release)
@@ -0,0 +1,13 @@
1
+ """
2
+ Sync command - Synchronize the Python package with the database model
3
+ """
4
+
5
+ import click
6
+ from half_orm_dev.repo import Repo
7
+
8
+
9
+ @click.command()
10
+ def sync_package():
11
+ """Synchronize the Python package with the database model."""
12
+ repo = Repo()
13
+ repo.sync_package()
@@ -0,0 +1,73 @@
1
+ """
2
+ TODO command - Placeholder for unimplemented Git-centric commands
3
+
4
+ Single function with multiple aliases for all commands to implement.
5
+ """
6
+
7
+ import click
8
+
9
+
10
+ @click.command()
11
+ @click.pass_context
12
+ def todo(ctx):
13
+ """
14
+ Placeholder for unimplemented Git-centric commands.
15
+
16
+ All legacy commands (prepare, undo, release, new) removed in v0.16.0.
17
+ New patch-centric workflow commands not yet implemented.
18
+
19
+ Target Git-centric architecture:
20
+ - ho-prod + ho-patch/patch-name branches
21
+ - Patches/patch-name/ directory structure
22
+ - releases/X.Y.Z-stage.txt → rc → production workflow
23
+ - PatchManager integration via repo.patch_manager
24
+ - Single active development rule (one RC at a time)
25
+ - Developer responsibility for conflict management
26
+ """
27
+ command_name = ctx.info_name
28
+
29
+ # Map of command → description for helpful error messages
30
+ command_descriptions = {
31
+ # 🚧 New Git-centric commands
32
+ 'init-project': 'Initialize new project with ho-prod branch and Patches/ structure',
33
+ 'create-patch': 'Create ho-patch/patch-name branch with Patches/patch-name/ directory',
34
+ 'apply-patch': 'Apply current patch files using PatchManager.apply_patch_files()',
35
+ 'add-to-release': 'Add patch to releases/X.Y.Z-stage.txt and merge to ho-prod',
36
+ 'prepare-release': 'Create next releases/X.Y.Z-stage.txt file',
37
+ 'promote-to': "Promote stage → target ('rc', 'prod') with automatic branch cleanup",
38
+ 'update': 'Apply patches in production (adapt for Git-centric)',
39
+ 'upgrade': 'Apply patches in production (adapt for Git-centric)',
40
+
41
+ # ♻️ Commands to implement
42
+ 'create-hotfix': 'Create emergency hotfix bypassing normal workflow',
43
+ 'rollback': 'Rollback database to previous version using backups/',
44
+ 'sync-package': 'Synchronize Python package with database model',
45
+ 'restore': 'Restore database to specific version (adapt for new backups)',
46
+ 'list-patches': 'List all patches in Patches/ directory',
47
+ 'status': 'Show development status with patch/release information',
48
+ 'apply-release': 'Apply next release',
49
+ }
50
+
51
+ description = command_descriptions.get(command_name, 'Git-centric command')
52
+
53
+ raise NotImplementedError(
54
+ f"Command '{command_name}' not implemented in v0.16.0\n"
55
+ f"Description: {description}\n\n"
56
+ f"Legacy commands removed - use new patch-centric workflow:\n"
57
+ f"See docs/half_orm_dev.md for architecture details.\n"
58
+ f"Current working: PatchManager (102 tests), HGit, Repo integration."
59
+ )
60
+
61
+
62
+ # Create aliases for ALL commands (new + adapted)
63
+ # 🚧 New Git-centric commands
64
+ add_to_release = todo
65
+ apply_release = todo
66
+ create_hotfix = todo
67
+ rollback = todo
68
+ list_patches = todo
69
+ status = todo
70
+
71
+ # ♻️ Commands to adapt (also in todo for now)
72
+ sync_package = todo # Keep functionality, adapt to new architecture
73
+ restore = todo # Adapt for new backup/restore logic
@@ -0,0 +1,17 @@
1
+ """
2
+ Undo command - Undo the last release
3
+ """
4
+
5
+ import click
6
+ from half_orm_dev.repo import Repo
7
+
8
+
9
+ @click.command()
10
+ @click.option(
11
+ '-d', '--database-only', is_flag=True,
12
+ help='Restore the database to the previous release.'
13
+ )
14
+ def undo(database_only):
15
+ """Undo the last release."""
16
+ repo = Repo()
17
+ repo.undo_release(database_only)
@@ -0,0 +1,73 @@
1
+ """
2
+ Update command - Fetch and list available production releases.
3
+
4
+ Equivalent to 'apt update' - read-only operation that shows
5
+ available releases without making any changes.
6
+ """
7
+
8
+ import click
9
+ from half_orm_dev.repo import Repo
10
+
11
+
12
+ @click.command()
13
+ def update():
14
+ """
15
+ Fetch and list available production releases.
16
+
17
+ Synchronizes with origin (git fetch --tags) and displays available
18
+ releases for production upgrade. Makes NO modifications to database
19
+ or repository.
20
+
21
+ By default, shows only production releases (v1.3.6, v1.4.0).
22
+ Use --allow-rc to include release candidates (v1.3.6-rc1).
23
+
24
+ Examples:
25
+ # List production releases only
26
+ half_orm dev update
27
+
28
+ # Include RC releases
29
+ half_orm dev update --allow-rc
30
+ """
31
+ repo = Repo()
32
+
33
+ # Direct access to ReleaseManager (KISS principle)
34
+ result = repo.release_manager.update_production()
35
+
36
+ # Format and display results
37
+ _display_update_results(result)
38
+
39
+
40
+ def _display_update_results(result):
41
+ """
42
+ Format and display update results to user.
43
+
44
+ Args:
45
+ result: Dict from ReleaseManager.update_production()
46
+ """
47
+ click.echo("\nFetching releases from origin... ✓\n")
48
+
49
+ current = result['current_version']
50
+ click.echo(f"Current production version: {current}")
51
+
52
+ if not result['has_updates']:
53
+ click.echo("\n✓ Production is up to date. No upgrades available.")
54
+ return
55
+
56
+ click.echo("\nAvailable releases for upgrade:")
57
+ for rel in result['available_releases']:
58
+ rel_type = rel['type'].upper() if rel['type'] != 'production' else ''
59
+ type_label = f" ({rel_type})" if rel_type else ""
60
+ patch_count = len(rel['patches'])
61
+ click.echo(f" → {rel['version']}{type_label} - {patch_count} patches")
62
+
63
+ if result['upgrade_path']:
64
+ click.echo("\nUpgrade path (sequential):")
65
+ path_str = " → ".join([current] + result['upgrade_path'])
66
+ click.echo(f" {path_str}")
67
+
68
+ click.echo("\nTo upgrade:")
69
+ click.echo(" half_orm dev upgrade (apply all)")
70
+ if result['upgrade_path']:
71
+ first_version = result['upgrade_path'][0]
72
+ click.echo(f" half_orm dev upgrade --to-release={first_version} (apply specific)")
73
+ click.echo(" half_orm dev upgrade --dry-run (simulate)")