half-orm-dev 0.17.3a4__tar.gz → 0.17.3a6__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 (65) hide show
  1. {half_orm_dev-0.17.3a4/half_orm_dev.egg-info → half_orm_dev-0.17.3a6}/PKG-INFO +1 -1
  2. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/release.py +103 -24
  3. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/database.py +70 -5
  4. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/patch_manager.py +1 -1
  5. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/release_manager.py +352 -56
  6. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/repo.py +94 -25
  7. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/README +36 -2
  8. half_orm_dev-0.17.3a6/half_orm_dev/version.txt +1 -0
  9. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6/half_orm_dev.egg-info}/PKG-INFO +1 -1
  10. half_orm_dev-0.17.3a4/half_orm_dev/version.txt +0 -1
  11. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/AUTHORS +0 -0
  12. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/LICENSE +0 -0
  13. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/README.md +0 -0
  14. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/__init__.py +0 -0
  15. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/__init__.py +0 -0
  16. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/__init__.py +0 -0
  17. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/apply.py +0 -0
  18. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/check.py +0 -0
  19. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/clone.py +0 -0
  20. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/init.py +0 -0
  21. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/migrate.py +0 -0
  22. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/patch.py +0 -0
  23. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/restore.py +0 -0
  24. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/sync.py +0 -0
  25. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/todo.py +0 -0
  26. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/undo.py +0 -0
  27. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/update.py +0 -0
  28. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/commands/upgrade.py +0 -0
  29. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli/main.py +0 -0
  30. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/cli_extension.py +0 -0
  31. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/decorators.py +0 -0
  32. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/hgit.py +0 -0
  33. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/migration_manager.py +0 -0
  34. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/migrations/0/17/1/00_move_to_hop.py +0 -0
  35. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/migrations/0/17/1/01_txt_to_toml.py +0 -0
  36. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/modules.py +0 -0
  37. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/patch_validator.py +0 -0
  38. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
  39. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
  40. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
  41. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/patches/log +0 -0
  42. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
  43. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/release_file.py +0 -0
  44. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/.gitignore +0 -0
  45. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/MANIFEST.in +0 -0
  46. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/Pipfile +0 -0
  47. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/conftest_template +0 -0
  48. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/git-hooks/pre-commit +0 -0
  49. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/git-hooks/prepare-commit-msg +0 -0
  50. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/init_module_template +0 -0
  51. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/module_template_1 +0 -0
  52. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/module_template_2 +0 -0
  53. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/module_template_3 +0 -0
  54. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/pyproject.toml +0 -0
  55. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/relation_test +0 -0
  56. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/sql_adapter +0 -0
  57. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/templates/warning +0 -0
  58. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev/utils.py +0 -0
  59. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev.egg-info/SOURCES.txt +0 -0
  60. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev.egg-info/dependency_links.txt +0 -0
  61. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev.egg-info/requires.txt +0 -0
  62. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/half_orm_dev.egg-info/top_level.txt +0 -0
  63. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/pyproject.toml +0 -0
  64. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/setup.cfg +0 -0
  65. {half_orm_dev-0.17.3a4 → half_orm_dev-0.17.3a6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 0.17.3a4
3
+ Version: 0.17.3a6
4
4
  Summary: half_orm development Framework.
5
5
  Author-email: Joël Maïzi <joel.maizi@collorg.org>
6
6
  License-Expression: GPL-3.0-or-later
@@ -369,39 +369,118 @@ def release_hotfix(version: Optional[str] = None) -> None:
369
369
 
370
370
 
371
371
  @release.command('apply')
372
- @click.argument('version', type=str, required=False)
373
- def release_apply(version: Optional[str] = None) -> None:
372
+ @click.option(
373
+ '--skip-tests',
374
+ is_flag=True,
375
+ help='Skip running pytest after applying patches'
376
+ )
377
+ def release_apply(skip_tests: bool) -> None:
374
378
  """
375
- Test complete release before deployment (NOT IMPLEMENTED YET).
379
+ Apply all patches for integration testing.
380
+
381
+ Restores the database from production schema and applies ALL patches
382
+ (candidates + staged) to test the complete release integration.
383
+ Optionally runs pytest to validate the release.
376
384
 
377
- Applies all patches from a release file to test the complete
378
- release workflow before production deployment.
385
+ Unlike 'patch apply' which only applies validated (staged) patches,
386
+ this command applies ALL patches including candidates that haven't
387
+ passed individual validation yet. This enables testing the complete
388
+ release before promotion.
379
389
 
380
390
  \b
381
- Args:
382
- version: Release version to test (e.g., "1.3.5-rc1")
383
- If not provided, applies latest RC
391
+ Workflow:
392
+ 1. Validate on release branch (ho-release/X.Y.Z)
393
+ 2. Restore database from model/schema.sql
394
+ 3. Apply all RC patches (if any)
395
+ 4. Apply ALL TOML patches (candidates + staged)
396
+ 5. Generate Python code
397
+ 6. Run pytest (unless --skip-tests)
398
+
399
+ \b
400
+ Requirements:
401
+ - Must be on ho-release/X.Y.Z branch
402
+ - Development release must exist
384
403
 
385
404
  \b
386
405
  Examples:
387
- Test latest RC:
406
+ Apply all patches and run tests:
388
407
  $ half_orm dev release apply
389
408
 
390
- Test specific RC:
391
- $ half_orm dev release apply 1.3.5-rc1
392
-
393
- Test stage release:
394
- $ half_orm dev release apply 1.3.5-stage
409
+ Apply patches without running tests:
410
+ $ half_orm dev release apply --skip-tests
395
411
 
396
412
  \b
397
- Status: 🚧 Not implemented - planned for future release
413
+ Output:
414
+ ✓ Applied 5 patches for version 1.3.6
415
+ ✓ Tests passed (42 tests)
416
+
417
+ Or on failure:
418
+ ✗ Tests failed
419
+ <test output>
398
420
  """
399
- click.echo("🚧 Release testing not implemented yet")
400
- click.echo()
401
- click.echo("Planned workflow:")
402
- click.echo(" 1. Restore database from model/schema.sql")
403
- click.echo(" 2. Apply all patches from release file")
404
- click.echo(" 3. Run comprehensive tests")
405
- click.echo(" 4. Validate final state")
406
- click.echo()
407
- raise NotImplementedError("Release apply not yet implemented")
421
+ try:
422
+ # Get repository instance
423
+ repo = Repo()
424
+ release_mgr = repo.release_manager
425
+
426
+ # Display context
427
+ click.echo("🔄 Applying all release patches for integration testing...")
428
+ click.echo()
429
+
430
+ # Apply release
431
+ result = release_mgr.apply_release(run_tests=not skip_tests)
432
+
433
+ # Display results
434
+ version = result['version']
435
+ patches = result['patches_applied']
436
+ candidates = result.get('candidates_merged', [])
437
+ patch_count = len(patches)
438
+
439
+ click.echo(f"✓ {utils.Color.green('Patches applied successfully!')}")
440
+ click.echo()
441
+ click.echo(f" Version: {utils.Color.bold(version)}")
442
+ click.echo(f" Patches: {utils.Color.bold(str(patch_count))}")
443
+ if candidates:
444
+ click.echo(f" Candidates merged: {utils.Color.bold(str(len(candidates)))}")
445
+ click.echo()
446
+
447
+ # Show candidates merged (simulated merges)
448
+ if candidates:
449
+ click.echo(" Candidate branches merged (simulated):")
450
+ for patch_id in candidates:
451
+ click.echo(f" • ho-patch/{patch_id}")
452
+ click.echo()
453
+
454
+ # Show patches applied
455
+ if patches:
456
+ click.echo(" Applied patches (SQL):")
457
+ for patch_id in patches:
458
+ click.echo(f" • {patch_id}")
459
+ click.echo()
460
+
461
+ # Show test results
462
+ if skip_tests:
463
+ click.echo(f"⚠️ {utils.Color.bold('Tests skipped')} (--skip-tests flag)")
464
+ elif result['tests_passed'] is None:
465
+ click.echo(f"⚠️ {utils.Color.bold('Tests not run')} (pytest not found)")
466
+ elif result['tests_passed']:
467
+ click.echo(f"✓ {utils.Color.green('Tests passed!')}")
468
+ else:
469
+ click.echo(f"✗ {utils.Color.red('Tests failed!')}")
470
+ click.echo()
471
+ click.echo("Test output:")
472
+ click.echo(result['test_output'])
473
+ sys.exit(1)
474
+
475
+ click.echo()
476
+ click.echo("📝 Next steps:")
477
+ if result['tests_passed'] or result['tests_passed'] is None:
478
+ click.echo(f" • Promote to RC: {utils.Color.bold('half_orm dev release promote rc')}")
479
+ else:
480
+ click.echo(f" • Fix failing tests")
481
+ click.echo(f" • Re-run: {utils.Color.bold('half_orm dev release apply')}")
482
+
483
+ except ReleaseManagerError as e:
484
+ click.echo(f"❌ {utils.Color.red('Release apply failed:')}", err=True)
485
+ click.echo(f" {str(e)}", err=True)
486
+ sys.exit(1)
@@ -93,6 +93,10 @@ class Database:
93
93
  if get_release and self.__repo.devel:
94
94
  self.__last_release = self.last_release
95
95
 
96
+ @property
97
+ def name(self):
98
+ return self.__name
99
+
96
100
  @property
97
101
  def last_release(self):
98
102
  "Returns the last release"
@@ -177,10 +181,22 @@ class Database:
177
181
  *command_args
178
182
  )
179
183
 
180
- def register_release(self, major, minor, patch, changelog=None):
181
- "Register the release into half_orm_meta.hop_release"
184
+ def register_release(self, major, minor, patch, pre_release='', pre_release_num='', changelog=None):
185
+ """
186
+ Register the release into half_orm_meta.hop_release.
187
+
188
+ Args:
189
+ major: Major version number
190
+ minor: Minor version number
191
+ patch: Patch version number
192
+ pre_release: Pre-release type ('alpha', 'beta', 'rc', or '' for production)
193
+ pre_release_num: Pre-release number (e.g., '1' for rc1)
194
+ changelog: Optional changelog text
195
+ """
182
196
  return self.__model.get_relation_class('half_orm_meta.hop_release')(
183
- major=major, minor=minor, patch=patch, changelog=changelog
197
+ major=major, minor=minor, patch=patch,
198
+ pre_release=pre_release, pre_release_num=pre_release_num,
199
+ changelog=changelog
184
200
  ).ho_insert()
185
201
 
186
202
  def _generate_schema_sql(self, version: str, model_dir: Path) -> Path:
@@ -271,6 +287,7 @@ class Database:
271
287
 
272
288
  # Construct versioned schema file path
273
289
  schema_file = model_dir / f"schema-{version}.sql"
290
+ temp_schema_file = model_dir / f".schema-{version}.sql.tmp"
274
291
 
275
292
  # Generate schema dump using pg_dump
276
293
  try:
@@ -278,16 +295,45 @@ class Database:
278
295
  'pg_dump',
279
296
  self.__name,
280
297
  '--schema-only',
298
+ '--no-owner',
281
299
  '-f',
282
- str(schema_file)
300
+ str(temp_schema_file)
301
+ )
302
+
303
+ # Filter out version-specific lines for cross-version compatibility
304
+ content = temp_schema_file.read_text()
305
+ filtered_lines = []
306
+ # SET commands that are version-specific and should be removed
307
+ version_specific_sets = (
308
+ 'SET transaction_timeout', # PG17+
283
309
  )
310
+ for line in content.split('\n'):
311
+ # Skip \restrict and \unrestrict lines
312
+ if line.startswith('\\restrict') or line.startswith('\\unrestrict'):
313
+ continue
314
+ # Skip "-- Dumped from/by" comments (version-specific)
315
+ if line.startswith('-- Dumped from') or line.startswith('-- Dumped by'):
316
+ continue
317
+ # Skip version-specific SET commands
318
+ if any(line.startswith(s) for s in version_specific_sets):
319
+ continue
320
+ filtered_lines.append(line)
321
+
322
+ schema_file.write_text('\n'.join(filtered_lines))
284
323
  except Exception as e:
285
324
  raise Exception(f"Failed to generate schema SQL: {e}") from e
325
+ finally:
326
+ # Clean up temporary file
327
+ if temp_schema_file.exists():
328
+ temp_schema_file.unlink()
286
329
 
287
330
  # Generate metadata dump (half_orm_meta data only)
331
+ # Keep only COPY statements to avoid version-specific SET commands
288
332
  metadata_file = model_dir / f"metadata-{version}.sql"
333
+ temp_file = model_dir / f".metadata-{version}.sql.tmp"
289
334
 
290
335
  try:
336
+ # Dump to temporary file
291
337
  self.execute_pg_command(
292
338
  'pg_dump',
293
339
  self.__name,
@@ -296,10 +342,29 @@ class Database:
296
342
  '--table=half_orm_meta.hop_release',
297
343
  '--table=half_orm_meta.hop_release_issue',
298
344
  '-f',
299
- str(metadata_file)
345
+ str(temp_file)
300
346
  )
347
+
348
+ # Filter to keep only COPY blocks (COPY ... FROM stdin; ... \.)
349
+ content = temp_file.read_text()
350
+ filtered_lines = []
351
+ in_copy_block = False
352
+ for line in content.split('\n'):
353
+ if line.startswith('COPY '):
354
+ in_copy_block = True
355
+ if in_copy_block:
356
+ filtered_lines.append(line)
357
+ if line == '\\.':
358
+ in_copy_block = False
359
+ filtered_lines.append('') # Empty line between blocks
360
+
361
+ metadata_file.write_text('\n'.join(filtered_lines))
301
362
  except Exception as e:
302
363
  raise Exception(f"Failed to generate metadata SQL: {e}") from e
364
+ finally:
365
+ # Clean up temporary file
366
+ if temp_file.exists():
367
+ temp_file.unlink()
303
368
 
304
369
  # Create or update symlink
305
370
  symlink_path = model_dir / "schema.sql"
@@ -1916,7 +1916,7 @@ class PatchManager:
1916
1916
  # This also syncs .hop/ to all active branches automatically via decorator
1917
1917
  try:
1918
1918
  self._repo.commit_and_sync_to_active_branches(
1919
- message=f"[HOP] move patch #{patch_id} from candidate to stage %{version}"
1919
+ message=f"[HOP] move patch #{patch_id} from candidate to stage %{version}\nFixes #{patch_id}."
1920
1920
  )
1921
1921
  except Exception as e:
1922
1922
  raise PatchManagerError(f"Failed to commit/push changes: {e}")