half-orm-dev 1.0.0a7__tar.gz → 1.0.0a8__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 (79) hide show
  1. {half_orm_dev-1.0.0a7/half_orm_dev.egg-info → half_orm_dev-1.0.0a8}/PKG-INFO +1 -1
  2. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migration_manager.py +86 -26
  3. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/repo.py +4 -12
  4. half_orm_dev-1.0.0a8/half_orm_dev/version.txt +1 -0
  5. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8/half_orm_dev.egg-info}/PKG-INFO +1 -1
  6. half_orm_dev-1.0.0a7/half_orm_dev/version.txt +0 -1
  7. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/AUTHORS +0 -0
  8. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/LICENSE +0 -0
  9. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/README.md +0 -0
  10. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/__init__.py +0 -0
  11. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/bootstrap_manager.py +0 -0
  12. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/__init__.py +0 -0
  13. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/__init__.py +0 -0
  14. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/apply.py +0 -0
  15. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/bootstrap.py +0 -0
  16. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/check.py +0 -0
  17. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/clone.py +0 -0
  18. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/init.py +0 -0
  19. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/migrate.py +0 -0
  20. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/patch.py +0 -0
  21. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/release.py +0 -0
  22. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/restore.py +0 -0
  23. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/revert_migration.py +0 -0
  24. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/set_git_origin.py +0 -0
  25. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/sync.py +0 -0
  26. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/todo.py +0 -0
  27. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/undo.py +0 -0
  28. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/update.py +0 -0
  29. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/commands/upgrade.py +0 -0
  30. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli/main.py +0 -0
  31. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/cli_extension.py +0 -0
  32. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/database.py +0 -0
  33. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/decorators.py +0 -0
  34. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/file_executor.py +0 -0
  35. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/hgit.py +0 -0
  36. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/0/17/1/00_move_to_hop.py +0 -0
  37. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/0/17/1/01_txt_to_toml.py +0 -0
  38. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/0/17/4/00_toml_dict_format.py +0 -0
  39. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/0/17/4/01_add_bootstrap_table.py +0 -0
  40. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/0/17/4/02_move_patches_to_subdirs.py +0 -0
  41. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/0/17/5/01_update_pyproject_dependency.py +0 -0
  42. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/0/18/0/00_add_async_support.py +0 -0
  43. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/0/18/0/01_update_default_tests.py +0 -0
  44. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/migrations/hop/BREAKING_CHANGES-1.0.0.md +0 -0
  45. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/modules.py +0 -0
  46. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/patch_manager.py +0 -0
  47. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/patch_validator.py +0 -0
  48. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
  49. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
  50. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
  51. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/patches/log +0 -0
  52. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
  53. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/release_file.py +0 -0
  54. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/release_manager.py +0 -0
  55. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/scripts/repair-metadata.py +0 -0
  56. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/.gitignore +0 -0
  57. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/MANIFEST.in +0 -0
  58. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/README +0 -0
  59. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/conftest_template +0 -0
  60. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/git-hooks/pre-commit +0 -0
  61. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/git-hooks/prepare-commit-msg +0 -0
  62. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/init_module_template +0 -0
  63. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/module_stub_template +0 -0
  64. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/module_template_1 +0 -0
  65. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/module_template_2 +0 -0
  66. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/module_template_3 +0 -0
  67. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/pyproject.toml +0 -0
  68. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/relation_test +0 -0
  69. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/sql_adapter +0 -0
  70. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/templates/warning +0 -0
  71. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev/utils.py +0 -0
  72. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev.egg-info/SOURCES.txt +0 -0
  73. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev.egg-info/dependency_links.txt +0 -0
  74. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev.egg-info/entry_points.txt +0 -0
  75. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev.egg-info/requires.txt +0 -0
  76. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/half_orm_dev.egg-info/top_level.txt +0 -0
  77. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/pyproject.toml +0 -0
  78. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/setup.cfg +0 -0
  79. {half_orm_dev-1.0.0a7 → half_orm_dev-1.0.0a8}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a7
3
+ Version: 1.0.0a8
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
@@ -313,6 +313,9 @@ class MigrationManager:
313
313
  f"Continuing with migration attempt."
314
314
  )
315
315
 
316
+ # Ensure all active branches are in sync with origin before touching anything
317
+ self._ensure_active_branches_synced()
318
+
316
319
  # Get pending migrations
317
320
  pending = self.get_pending_migrations(current_version, target_version)
318
321
 
@@ -452,6 +455,72 @@ class MigrationManager:
452
455
  except Exception:
453
456
  pass # remote tag may already be gone
454
457
 
458
+ def _ensure_active_branches_synced(self) -> None:
459
+ """Verify all active branches are in sync with origin before migration.
460
+
461
+ Branches that are behind are fast-forwarded automatically (no local commits
462
+ at risk). Branches that are ahead or diverged block the migration — the
463
+ developer must push or resolve before proceeding.
464
+
465
+ Raises:
466
+ MigrationManagerError: if any active branch is ahead or diverged.
467
+ """
468
+ repo = self._repo
469
+ git_repo = repo.hgit._HGit__git_repo
470
+ current_branch = git_repo.active_branch.name
471
+
472
+ try:
473
+ branches_status = repo.hgit.get_active_branches_status()
474
+ except Exception:
475
+ return # can't determine status, proceed cautiously
476
+
477
+ patch_branches = [b['name'] for b in branches_status.get('patch_branches', [])]
478
+ release_branches = [b['name'] for b in branches_status.get('release_branches', [])]
479
+ staged_branches = [b['name'] for b in branches_status.get('staged_branches', [])]
480
+ active_branches = release_branches + patch_branches + staged_branches
481
+
482
+ blocked = []
483
+ for branch in active_branches:
484
+ try:
485
+ synced, status = repo.hgit.is_branch_synced(branch)
486
+ if synced:
487
+ continue
488
+ if status == 'behind':
489
+ # Fast-forward: no local commits at risk
490
+ repo.hgit.checkout(branch)
491
+ git_repo.git.merge('--ff-only', f'origin/{branch}')
492
+ elif status in ('ahead', 'diverged'):
493
+ blocked.append((branch, status))
494
+ except Exception:
495
+ pass # branch may not exist locally, skip
496
+
497
+ # Return to original branch
498
+ try:
499
+ repo.hgit.checkout(current_branch)
500
+ except Exception:
501
+ pass
502
+
503
+ if blocked:
504
+ ahead = [(b, s) for b, s in blocked if s == 'ahead']
505
+ diverged = [(b, s) for b, s in blocked if s == 'diverged']
506
+ parts = []
507
+ if ahead:
508
+ branch_list = ', '.join(b for b, _ in ahead)
509
+ parts.append(
510
+ f" Branches ahead of origin (unpushed commits) — push first:\n"
511
+ + '\n'.join(f" git push origin {b}" for b, _ in ahead)
512
+ )
513
+ if diverged:
514
+ parts.append(
515
+ f" Branches diverged from origin (local and remote have diverged) "
516
+ f"— rebase or merge to resolve:\n"
517
+ + '\n'.join(f" {b}" for b, _ in diverged)
518
+ )
519
+ raise MigrationManagerError(
520
+ f"Migration blocked: active branches are not in sync with origin.\n"
521
+ + '\n'.join(parts)
522
+ )
523
+
455
524
  def _regenerate_modules_after_migration(
456
525
  self, from_version: str, to_version: str
457
526
  ) -> None:
@@ -468,50 +537,41 @@ class MigrationManager:
468
537
  package_name = repo.name
469
538
  package_dir = str(Path(repo.base_dir) / package_name)
470
539
 
471
- # Regenerate all modules (idempotent thanks to global reset in generate())
472
- _modules.generate(repo)
473
-
474
- # Stage the entire package directory
475
- repo.hgit.add(package_dir)
476
-
477
- # Nothing changed — nothing to do
478
- staged = git_repo.git.diff('--cached', '--name-only')
479
- if not staged.strip():
480
- return
481
-
482
- # Commit on ho-prod
483
540
  commit_msg = (
484
541
  f"[HOP] Regenerate modules (migration {from_version} → {to_version})"
485
542
  )
486
- repo.hgit.commit('-m', commit_msg)
487
- repo.hgit.push_branch('ho-prod')
488
543
 
489
- # Sync to all active branches
544
+ # Collect active branches before moving around
490
545
  try:
491
546
  branches_status = repo.hgit.get_active_branches_status()
492
547
  except Exception:
493
- return
548
+ branches_status = {}
494
549
 
495
550
  patch_branches = [b['name'] for b in branches_status.get('patch_branches', [])]
496
551
  release_branches = [b['name'] for b in branches_status.get('release_branches', [])]
497
552
  staged_branches = [b['name'] for b in branches_status.get('staged_branches', [])]
498
- target_branches = release_branches + patch_branches + staged_branches
553
+ # ho-prod first, then active branches
554
+ all_branches = ['ho-prod'] + release_branches + patch_branches + staged_branches
499
555
 
500
- sync_msg = (
501
- f"[HOP] Sync modules from ho-prod (migration {from_version} → {to_version})"
502
- )
503
- for branch in target_branches:
556
+ for branch in all_branches:
504
557
  try:
505
558
  repo.hgit.checkout(branch)
506
- git_repo.git.checkout('ho-prod', '--', package_name)
559
+ # generate() reads existing files and preserves developer code sections
560
+ _modules.generate(repo)
507
561
  repo.hgit.add(package_dir)
508
- if not git_repo.git.status('--porcelain').strip():
562
+ if not git_repo.git.diff('--cached', '--name-only').strip():
509
563
  continue
510
- repo.hgit.commit('-m', sync_msg)
511
- repo.hgit.push_branch(branch)
564
+ repo.hgit.commit('-m', commit_msg)
565
+ try:
566
+ repo.hgit.push_branch(branch)
567
+ except Exception as push_err:
568
+ sys.stderr.write(
569
+ f"Warning: could not push {branch} after module regeneration "
570
+ f"(diverged branch?): {push_err}\n"
571
+ )
512
572
  except Exception as e:
513
573
  sys.stderr.write(
514
- f"Warning: could not sync modules to {branch}: {e}\n"
574
+ f"Warning: could not regenerate modules on {branch}: {e}\n"
515
575
  )
516
576
 
517
577
  repo.hgit.checkout('ho-prod')
@@ -690,18 +690,10 @@ class Repo:
690
690
  remote_ref = f"origin/{branch}"
691
691
  try:
692
692
  synced, status = self.hgit.is_branch_synced(branch)
693
- if not synced and status == "diverged":
694
- print(
695
- f"Warning: branch {branch} has diverged from origin. "
696
- f"Resetting to origin (source of truth).",
697
- file=sys.stderr
698
- )
699
- # Only reset when the remote is ahead of local ("behind") or
700
- # branches have diverged. Never reset an "ahead" branch —
701
- # that would orphan local commits that have not been pushed
702
- # yet (e.g. the "Create patch directory" commit from
703
- # `hop patch create` before the first push).
704
- if not synced and status in ("behind", "diverged"):
693
+ # Only fast-forward when origin is strictly ahead (no local commits
694
+ # at risk). Never reset on diverged branches — that would destroy
695
+ # unmerged local work.
696
+ if not synced and status == "behind":
705
697
  self.hgit._HGit__git_repo.git.reset('--hard', remote_ref)
706
698
  except GitCommandError:
707
699
  # Remote branch may not exist yet, continue without reset
@@ -0,0 +1 @@
1
+ 1.0.0-a8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a7
3
+ Version: 1.0.0a8
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-a7
File without changes
File without changes
File without changes
File without changes
File without changes