half-orm-dev 1.0.0a16__tar.gz → 1.0.0a18__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 (78) hide show
  1. {half_orm_dev-1.0.0a16/half_orm_dev.egg-info → half_orm_dev-1.0.0a18}/PKG-INFO +1 -1
  2. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migration_manager.py +43 -11
  3. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/release_manager.py +28 -1
  4. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/repo.py +25 -21
  5. half_orm_dev-1.0.0a18/half_orm_dev/version.txt +1 -0
  6. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18/half_orm_dev.egg-info}/PKG-INFO +1 -1
  7. half_orm_dev-1.0.0a16/half_orm_dev/version.txt +0 -1
  8. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/AUTHORS +0 -0
  9. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/LICENSE +0 -0
  10. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/README.md +0 -0
  11. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/__init__.py +0 -0
  12. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/bootstrap_manager.py +0 -0
  13. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/__init__.py +0 -0
  14. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/__init__.py +0 -0
  15. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/apply.py +0 -0
  16. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/bootstrap.py +0 -0
  17. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/check.py +0 -0
  18. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/clone.py +0 -0
  19. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/init.py +0 -0
  20. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/migrate.py +0 -0
  21. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/patch.py +0 -0
  22. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/release.py +0 -0
  23. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/restore.py +0 -0
  24. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/revert_migration.py +0 -0
  25. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/set_git_origin.py +0 -0
  26. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/sync.py +0 -0
  27. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/todo.py +0 -0
  28. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/undo.py +0 -0
  29. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/update.py +0 -0
  30. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/commands/upgrade.py +0 -0
  31. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli/main.py +0 -0
  32. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/cli_extension.py +0 -0
  33. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/database.py +0 -0
  34. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/decorators.py +0 -0
  35. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/file_executor.py +0 -0
  36. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/hgit.py +0 -0
  37. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/0/17/1/00_move_to_hop.py +0 -0
  38. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/0/17/1/01_txt_to_toml.py +0 -0
  39. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/0/17/4/00_toml_dict_format.py +0 -0
  40. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/0/17/4/01_add_bootstrap_table.py +0 -0
  41. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/0/17/4/02_move_patches_to_subdirs.py +0 -0
  42. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/0/17/5/01_update_pyproject_dependency.py +0 -0
  43. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/0/18/0/00_add_async_support.py +0 -0
  44. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/0/18/0/01_update_default_tests.py +0 -0
  45. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/migrations/hop/BREAKING_CHANGES-1.0.0.md +0 -0
  46. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/modules.py +0 -0
  47. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/patch_manager.py +0 -0
  48. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/patch_validator.py +0 -0
  49. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
  50. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
  51. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
  52. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/patches/log +0 -0
  53. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
  54. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/release_file.py +0 -0
  55. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/scripts/repair-metadata.py +0 -0
  56. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/.gitignore +0 -0
  57. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/MANIFEST.in +0 -0
  58. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/README +0 -0
  59. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/conftest_template +0 -0
  60. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/git-hooks/pre-commit +0 -0
  61. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/git-hooks/prepare-commit-msg +0 -0
  62. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/init_module_template +0 -0
  63. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/module_template_1 +0 -0
  64. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/module_template_2 +0 -0
  65. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/module_template_3 +0 -0
  66. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/pyproject.toml +0 -0
  67. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/relation_test +0 -0
  68. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/sql_adapter +0 -0
  69. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/templates/warning +0 -0
  70. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev/utils.py +0 -0
  71. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev.egg-info/SOURCES.txt +0 -0
  72. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev.egg-info/dependency_links.txt +0 -0
  73. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev.egg-info/entry_points.txt +0 -0
  74. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev.egg-info/requires.txt +0 -0
  75. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/half_orm_dev.egg-info/top_level.txt +0 -0
  76. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/pyproject.toml +0 -0
  77. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/setup.cfg +0 -0
  78. {half_orm_dev-1.0.0a16 → half_orm_dev-1.0.0a18}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a16
3
+ Version: 1.0.0a18
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
@@ -556,14 +556,25 @@ class MigrationManager:
556
556
  def _regenerate_modules_after_migration(
557
557
  self, from_version: str, to_version: str
558
558
  ) -> None:
559
- """Regenerate the project modules after a migration and sync to all active branches.
559
+ """Regenerate the project modules after a migration on all active branches.
560
560
 
561
- Runs generate(), commits the changed files on ho-prod, then replays those
562
- changes on every active branch by checking out the package directory from ho-prod.
563
- Skips silently if generate() produced no changes.
561
+ Each branch is regenerated against the DB schema that matches its state:
562
+ - ho-prod → production schema (schema.sql)
563
+ - ho-release/X.Y.Z release schema (release-X.Y.Z.sql), no bootstrap
564
+ - ho-patch/* → production schema (schema.sql)
565
+
566
+ Bootstrap scripts are NOT run during this restore: the modules may be in
567
+ an inconsistent state (that is precisely what we are fixing), and running
568
+ bootstrap would fail trying to import them.
569
+
570
+ Stale local branches (no longer on remote) are skipped to avoid pre-commit
571
+ hook failures.
564
572
  """
573
+ import re as _re
565
574
  from half_orm_dev import modules as _modules
566
575
 
576
+ _RELEASE_RE = _re.compile(r'^ho-release/(.+)$')
577
+
567
578
  repo = self._repo
568
579
  git_repo = repo.hgit._HGit__git_repo
569
580
  package_name = repo.name
@@ -573,21 +584,42 @@ class MigrationManager:
573
584
  f"[HOP] Regenerate modules (migration {from_version} → {to_version})"
574
585
  )
575
586
 
576
- # Collect active branches before moving around
587
+ # Collect active branches, filtering out stale ones (no remote counterpart)
577
588
  try:
578
589
  branches_status = repo.hgit.get_active_branches_status()
579
590
  except Exception:
580
591
  branches_status = {}
581
592
 
582
- patch_branches = [b['name'] for b in branches_status.get('patch_branches', [])]
583
- release_branches = [b['name'] for b in branches_status.get('release_branches', [])]
584
- # ho-staged/* branches are frozen after merge — excluded from module regeneration
593
+ patch_branches = [
594
+ b['name'] for b in branches_status.get('patch_branches', [])
595
+ if b.get('exists_on_remote', True)
596
+ ]
597
+ release_branches = [
598
+ b['name'] for b in branches_status.get('release_branches', [])
599
+ if b.get('exists_on_remote', True)
600
+ ]
585
601
  all_branches = ['ho-prod'] + release_branches + patch_branches
586
602
 
587
603
  for branch in all_branches:
588
604
  try:
589
605
  repo.hgit.checkout(branch)
590
- # generate() reads existing files and preserves developer code sections
606
+
607
+ # Restore the DB to the schema appropriate for this branch so
608
+ # generate() introspects the right set of relations.
609
+ m = _RELEASE_RE.match(branch)
610
+ if m:
611
+ release_version = m.group(1)
612
+ release_schema = repo.get_release_schema_path(release_version)
613
+ if release_schema.exists():
614
+ repo.restore_database_from_release_schema(
615
+ release_version, skip_bootstrap=True
616
+ )
617
+ else:
618
+ repo.restore_database_from_schema(skip_bootstrap=True)
619
+ else:
620
+ # ho-prod and ho-patch/*: use production schema
621
+ repo.restore_database_from_schema(skip_bootstrap=True)
622
+
591
623
  _modules.generate(repo)
592
624
  repo.hgit.add(package_dir)
593
625
  if not git_repo.git.diff('--cached', '--name-only').strip():
@@ -597,8 +629,8 @@ class MigrationManager:
597
629
  repo.hgit.push_branch(branch)
598
630
  except Exception as push_err:
599
631
  sys.stderr.write(
600
- f"Warning: could not push {branch} after module regeneration "
601
- f"(diverged branch?): {push_err}\n"
632
+ f"Warning: could not push {branch} after module regeneration: "
633
+ f"{push_err}\n"
602
634
  )
603
635
  except Exception as e:
604
636
  sys.stderr.write(
@@ -2586,7 +2586,34 @@ class ReleaseManager:
2586
2586
  # snapshot changes (which delete the TOML), we commit everything as
2587
2587
  # a single merge commit with no conflict.
2588
2588
  self._repo.hgit.checkout("ho-prod")
2589
- self._repo.hgit.merge(release_branch, no_commit=True)
2589
+ try:
2590
+ self._repo.hgit.merge(release_branch, no_commit=True)
2591
+ except GitCommandError:
2592
+ # ho_baseclasses.py is auto-generated and may conflict when the
2593
+ # file was introduced by a half_orm_dev migration AFTER the release
2594
+ # branch was cut (CONFLICT add/add). The release branch always
2595
+ # holds the correct post-patch version, so we take --theirs.
2596
+ git_repo = self._repo.hgit._HGit__git_repo
2597
+ conflicted = git_repo.git.diff(
2598
+ '--name-only', '--diff-filter=U'
2599
+ ).splitlines()
2600
+ package = self._repo.name
2601
+ auto_generated = {f'{package}/ho_baseclasses.py'}
2602
+ unexpected = set(conflicted) - auto_generated
2603
+ if unexpected:
2604
+ raise ReleaseManagerError(
2605
+ f"Merge conflict(s) in non-auto-generated file(s): "
2606
+ + ', '.join(sorted(unexpected))
2607
+ )
2608
+ if not conflicted:
2609
+ raise # no conflicted files listed — unexpected state
2610
+ for path in conflicted:
2611
+ git_repo.git.checkout('--theirs', path)
2612
+ git_repo.git.add(path)
2613
+ click.echo(
2614
+ f" ℹ Resolved add/add conflict in {', '.join(conflicted)} "
2615
+ f"(took release branch version)"
2616
+ )
2590
2617
 
2591
2618
  # 10. Generate schema dump and apply snapshot changes in merge window
2592
2619
  self._repo.database._generate_schema_sql(version, model_dir)
@@ -32,6 +32,7 @@ from half_orm_dev.patch_manager import PatchManager, PatchManagerError
32
32
  from half_orm_dev.release_manager import ReleaseManager, ReleaseManagerError
33
33
  from half_orm_dev.migration_manager import MigrationManager, MigrationManagerError
34
34
  from half_orm_dev.release_file import ReleaseFile, ReleaseFileError
35
+ from half_orm_dev.bootstrap_manager import BootstrapManager
35
36
 
36
37
  from .utils import TEMPLATE_DIRS, hop_version
37
38
 
@@ -2394,7 +2395,7 @@ Each script is executed only once unless `--force` is used.
2394
2395
  self.model.execute_query('CREATE SCHEMA public')
2395
2396
  self.model.execute_query('GRANT ALL ON SCHEMA public TO public')
2396
2397
 
2397
- def restore_database_from_schema(self, exclude_bootstrap_patch_id: Optional[str] = None, exclude_bootstrap_version: Optional[str] = None) -> None:
2398
+ def restore_database_from_schema(self, exclude_bootstrap_patch_id: Optional[str] = None, exclude_bootstrap_version: Optional[str] = None, skip_bootstrap: bool = False) -> None:
2398
2399
  """
2399
2400
  Restore database from model/schema.sql, metadata, and data files.
2400
2401
 
@@ -2511,16 +2512,18 @@ Each script is executed only once unless `--force` is used.
2511
2512
  # In promote context: exclude_bootstrap_version skips the version being
2512
2513
  # promoted (its bootstraps don't run during promote, they run via upgrade).
2513
2514
  # up_to_version prevents bootstraps from future versions from running.
2514
- from half_orm_dev.bootstrap_manager import BootstrapManager
2515
- bootstrap_mgr = BootstrapManager(self)
2516
- result = bootstrap_mgr.run_bootstrap(
2517
- exclude_patch_id=exclude_bootstrap_patch_id,
2518
- exclude_version=exclude_bootstrap_version,
2519
- up_to_version=exclude_bootstrap_version
2520
- )
2521
- if result['errors']:
2522
- error_msg = "\n".join([f" • {f}: {e}" for f, e in result['errors']])
2523
- raise RepoError(f"Bootstrap execution failed:\n{error_msg}")
2515
+ # skip_bootstrap=True is used when regenerating modules after migration
2516
+ # (bootstrap validation belongs only in patch merge / release promote prod).
2517
+ if not skip_bootstrap:
2518
+ bootstrap_mgr = BootstrapManager(self)
2519
+ result = bootstrap_mgr.run_bootstrap(
2520
+ exclude_patch_id=exclude_bootstrap_patch_id,
2521
+ exclude_version=exclude_bootstrap_version,
2522
+ up_to_version=exclude_bootstrap_version
2523
+ )
2524
+ if result['errors']:
2525
+ error_msg = "\n".join([f" • {f}: {e}" for f, e in result['errors']])
2526
+ raise RepoError(f"Bootstrap execution failed:\n{error_msg}")
2524
2527
 
2525
2528
  except RepoError:
2526
2529
  # Re-raise RepoError as-is
@@ -2660,7 +2663,8 @@ Each script is executed only once unless `--force` is used.
2660
2663
  self,
2661
2664
  version: str,
2662
2665
  exclude_bootstrap_patch_id: Optional[str] = None,
2663
- exclude_bootstrap_version: Optional[str] = None
2666
+ exclude_bootstrap_version: Optional[str] = None,
2667
+ skip_bootstrap: bool = False
2664
2668
  ) -> None:
2665
2669
  """
2666
2670
  Restore database from release schema file.
@@ -2708,15 +2712,15 @@ Each script is executed only once unless `--force` is used.
2708
2712
  # Reload half_orm metadata cache
2709
2713
  self.model.reconnect(reload=True)
2710
2714
 
2711
- # Execute bootstrap scripts
2712
- # up_to_version prevents bootstraps from future versions from running.
2713
- from half_orm_dev.bootstrap_manager import BootstrapManager
2714
- bootstrap_mgr = BootstrapManager(self)
2715
- bootstrap_mgr.run_bootstrap(
2716
- exclude_patch_id=exclude_bootstrap_patch_id,
2717
- exclude_version=exclude_bootstrap_version,
2718
- up_to_version=exclude_bootstrap_version
2719
- )
2715
+ # Execute bootstrap scripts (skip during module regeneration — the
2716
+ # modules may be in an inconsistent state at that point)
2717
+ if not skip_bootstrap:
2718
+ bootstrap_mgr = BootstrapManager(self)
2719
+ bootstrap_mgr.run_bootstrap(
2720
+ exclude_patch_id=exclude_bootstrap_patch_id,
2721
+ exclude_version=exclude_bootstrap_version,
2722
+ up_to_version=exclude_bootstrap_version
2723
+ )
2720
2724
 
2721
2725
  except Exception as e:
2722
2726
  raise RepoError(f"Failed to restore from release schema: {e}") from e
@@ -0,0 +1 @@
1
+ 1.0.0-a18
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a16
3
+ Version: 1.0.0a18
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
@@ -1 +0,0 @@
1
- 1.0.0-a16
File without changes
File without changes