half-orm-dev 1.0.0a19__tar.gz → 1.0.0a20__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 (80) hide show
  1. {half_orm_dev-1.0.0a19/half_orm_dev.egg-info → half_orm_dev-1.0.0a20}/PKG-INFO +1 -1
  2. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/hgit.py +12 -2
  3. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/release_manager.py +48 -24
  4. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/repo.py +29 -2
  5. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/.gitignore +3 -1
  6. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/git-hooks/pre-commit +12 -0
  7. half_orm_dev-1.0.0a20/half_orm_dev/templates/git-hooks/pre-push +18 -0
  8. half_orm_dev-1.0.0a20/half_orm_dev/templates/git-hooks/reference-transaction +34 -0
  9. half_orm_dev-1.0.0a20/half_orm_dev/version.txt +1 -0
  10. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20/half_orm_dev.egg-info}/PKG-INFO +1 -1
  11. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev.egg-info/SOURCES.txt +3 -1
  12. half_orm_dev-1.0.0a19/half_orm_dev/version.txt +0 -1
  13. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/AUTHORS +0 -0
  14. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/LICENSE +0 -0
  15. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/README.md +0 -0
  16. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/__init__.py +0 -0
  17. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/bootstrap_manager.py +0 -0
  18. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/__init__.py +0 -0
  19. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/__init__.py +0 -0
  20. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/apply.py +0 -0
  21. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/bootstrap.py +0 -0
  22. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/check.py +0 -0
  23. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/clone.py +0 -0
  24. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/init.py +0 -0
  25. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/migrate.py +0 -0
  26. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/patch.py +0 -0
  27. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/release.py +0 -0
  28. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/restore.py +0 -0
  29. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/revert_migration.py +0 -0
  30. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/set_git_origin.py +0 -0
  31. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/sync.py +0 -0
  32. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/todo.py +0 -0
  33. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/undo.py +0 -0
  34. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/update.py +0 -0
  35. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/commands/upgrade.py +0 -0
  36. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli/main.py +0 -0
  37. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/cli_extension.py +0 -0
  38. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/database.py +0 -0
  39. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/decorators.py +0 -0
  40. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/file_executor.py +0 -0
  41. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migration_manager.py +0 -0
  42. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/0/17/1/00_move_to_hop.py +0 -0
  43. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/0/17/1/01_txt_to_toml.py +0 -0
  44. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/0/17/4/00_toml_dict_format.py +0 -0
  45. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/0/17/4/01_add_bootstrap_table.py +0 -0
  46. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/0/17/4/02_move_patches_to_subdirs.py +0 -0
  47. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/0/17/5/01_update_pyproject_dependency.py +0 -0
  48. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/0/18/0/00_add_async_support.py +0 -0
  49. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/0/18/0/01_update_default_tests.py +0 -0
  50. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/migrations/hop/BREAKING_CHANGES-1.0.0.md +0 -0
  51. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/modules.py +0 -0
  52. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/patch_manager.py +0 -0
  53. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/patch_validator.py +0 -0
  54. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
  55. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
  56. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
  57. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/patches/log +0 -0
  58. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
  59. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/release_file.py +0 -0
  60. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/scripts/repair-metadata.py +0 -0
  61. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/MANIFEST.in +0 -0
  62. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/README +0 -0
  63. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/conftest_template +0 -0
  64. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/git-hooks/prepare-commit-msg +0 -0
  65. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/init_module_template +0 -0
  66. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/module_template_1 +0 -0
  67. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/module_template_2 +0 -0
  68. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/module_template_3 +0 -0
  69. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/pyproject.toml +0 -0
  70. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/relation_test +0 -0
  71. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/sql_adapter +0 -0
  72. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/templates/warning +0 -0
  73. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev/utils.py +0 -0
  74. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev.egg-info/dependency_links.txt +0 -0
  75. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev.egg-info/entry_points.txt +0 -0
  76. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev.egg-info/requires.txt +0 -0
  77. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/half_orm_dev.egg-info/top_level.txt +0 -0
  78. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/pyproject.toml +0 -0
  79. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/setup.cfg +0 -0
  80. {half_orm_dev-1.0.0a19 → half_orm_dev-1.0.0a20}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a19
3
+ Version: 1.0.0a20
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
@@ -367,7 +367,12 @@ class HGit:
367
367
  # Local git now knows about all remote tags
368
368
  """
369
369
  origin = self.__git_repo.remote('origin')
370
- origin.fetch(tags=True)
370
+ marker = Path(self.__git_repo.working_dir) / '.hop' / '.fetching'
371
+ try:
372
+ marker.touch()
373
+ origin.fetch(tags=True)
374
+ finally:
375
+ marker.unlink(missing_ok=True)
371
376
 
372
377
  def tag_exists(self, tag_name: str) -> bool:
373
378
  """
@@ -443,7 +448,12 @@ class HGit:
443
448
  # Stale remote refs (deleted branches on remote) are removed
444
449
  """
445
450
  origin = self.__git_repo.remote('origin')
446
- origin.fetch(prune=True)
451
+ marker = Path(self.__git_repo.working_dir) / '.hop' / '.fetching'
452
+ try:
453
+ marker.touch()
454
+ origin.fetch(prune=True)
455
+ finally:
456
+ marker.unlink(missing_ok=True)
447
457
 
448
458
  def delete_local_branch(self, branch_name: str) -> None:
449
459
  """
@@ -1232,6 +1232,52 @@ class ReleaseManager:
1232
1232
  # Best effort - don't fail if checkout back fails
1233
1233
  pass
1234
1234
 
1235
+ def ensure_ho_current(self) -> bool:
1236
+ """
1237
+ Set up ho-current branch for production servers if not already done.
1238
+
1239
+ Creates ho-current from the current production version's immutable tag
1240
+ and checks it out. Local-only, never pushed to origin.
1241
+
1242
+ Returns:
1243
+ True if ho-current was created and checked out, False otherwise.
1244
+
1245
+ Raises:
1246
+ ReleaseManagerError: If the required version tag is not found locally.
1247
+ """
1248
+ if not (
1249
+ self._repo.production
1250
+ and self._repo.hgit.branch == 'ho-prod'
1251
+ and not self._repo.hgit.branch_exists('ho-current')
1252
+ ):
1253
+ return False
1254
+
1255
+ try:
1256
+ current_version = self._repo.database.last_release_s
1257
+ except Exception as e:
1258
+ raise ReleaseManagerError(
1259
+ f"Cannot read current production version from database: {e}"
1260
+ )
1261
+
1262
+ current_tag = f'v{current_version}'
1263
+ tag_exists = any(
1264
+ t.name == current_tag
1265
+ for t in self._repo.hgit._HGit__git_repo.tags
1266
+ )
1267
+ if not tag_exists:
1268
+ raise ReleaseManagerError(
1269
+ f"Cannot set up ho-current: tag {current_tag} not found locally.\n"
1270
+ f"Run 'hop update' to fetch tags first."
1271
+ )
1272
+
1273
+ self._repo.hgit.create_branch_from_tag('ho-current', current_tag)
1274
+ self._repo.hgit._HGit__git_repo.heads['ho-current'].checkout()
1275
+ click.echo(
1276
+ f" ℹ ho-current branch created from {current_tag} (local only).\n"
1277
+ f" Production servers use ho-current instead of ho-prod."
1278
+ )
1279
+ return True
1280
+
1235
1281
  def update_production(self) -> dict:
1236
1282
  """
1237
1283
  Fetch tags and list available releases for production upgrade (read-only).
@@ -1290,30 +1336,8 @@ class ReleaseManager:
1290
1336
  f"Cannot read current production version from database: {e}"
1291
1337
  )
1292
1338
 
1293
- # Migration: production servers that still track ho-prod instead of
1294
- # ho-current. Create ho-current from the current version's immutable tag
1295
- # and switch to it — local-only, no push to origin.
1296
- if (
1297
- self._repo.production
1298
- and self._repo.hgit.branch == 'ho-prod'
1299
- and not self._repo.hgit.branch_exists('ho-current')
1300
- ):
1301
- current_tag = f'v{current_version}'
1302
- tag_exists = any(
1303
- t.name == current_tag
1304
- for t in self._repo.hgit._HGit__git_repo.tags
1305
- )
1306
- if not tag_exists:
1307
- raise ReleaseManagerError(
1308
- f"Cannot migrate to ho-current: tag {current_tag} not found locally.\n"
1309
- f"Run 'hop update' again after ensuring tags are fetched."
1310
- )
1311
- self._repo.hgit.create_branch_from_tag('ho-current', current_tag)
1312
- self._repo.hgit._HGit__git_repo.heads['ho-current'].checkout()
1313
- click.echo(
1314
- f" ℹ Migrated to ho-current (created from {current_tag}, local only).\n"
1315
- f" Production servers should now use ho-current instead of ho-prod."
1316
- )
1339
+ # Migration: production servers that still track ho-prod instead of ho-current.
1340
+ self.ensure_ho_current()
1317
1341
 
1318
1342
  # 3. Build list of available releases with details
1319
1343
  available_releases = []
@@ -1457,6 +1457,17 @@ class Repo:
1457
1457
  if action == 'installed' or overall_action == 'skipped':
1458
1458
  overall_action = action
1459
1459
 
1460
+ # Ensure production-specific entries are in .gitignore (idempotent).
1461
+ gitignore_path = Path(self.__base_dir) / '.gitignore'
1462
+ if gitignore_path.exists():
1463
+ content = gitignore_path.read_text()
1464
+ lines = content.splitlines()
1465
+ missing = [e for e in ('.hop/production', '.hop/.fetching')
1466
+ if e not in lines]
1467
+ if missing:
1468
+ with gitignore_path.open('a') as f:
1469
+ f.write('\n' + '\n'.join(missing) + '\n')
1470
+
1460
1471
  return {
1461
1472
  'installed': any_installed,
1462
1473
  'action': overall_action
@@ -2941,9 +2952,14 @@ Each script is executed only once unless `--force` is used.
2941
2952
  )
2942
2953
 
2943
2954
  # Step 3: Clone repository
2955
+ # Production clones fetch ho-prod only — dev branches are irrelevant on production.
2956
+ clone_cmd = ["git", "clone"]
2957
+ if connection_options.get('production'):
2958
+ clone_cmd += ["--single-branch", "--branch", "ho-prod"]
2959
+ clone_cmd += [git_origin, str(dest_path)]
2944
2960
  try:
2945
2961
  result = subprocess.run(
2946
- ["git", "clone", git_origin, str(dest_path)],
2962
+ clone_cmd,
2947
2963
  capture_output=True,
2948
2964
  text=True,
2949
2965
  check=True,
@@ -3016,5 +3032,16 @@ Each script is executed only once unless `--force` is used.
3016
3032
  f"Failed to restore database from schema: {e}"
3017
3033
  ) from e
3018
3034
 
3019
- # Step 9: Install Git hooks
3035
+ # Step 9: Set up ho-current for production servers
3036
+ if connection_options.get('production'):
3037
+ # Mark this clone as production (read-only): blocks git commit/push via hooks.
3038
+ production_marker = Path(dest_path) / '.hop' / 'production'
3039
+ production_marker.parent.mkdir(parents=True, exist_ok=True)
3040
+ production_marker.touch()
3041
+ try:
3042
+ repo.release_manager.ensure_ho_current()
3043
+ except Exception as e:
3044
+ print(f" ⚠️ Warning: Could not set up ho-current: {e}", file=sys.stderr)
3045
+
3046
+ # Step 10: Install Git hooks
3020
3047
  repo.install_git_hooks()
@@ -14,4 +14,6 @@ Backups
14
14
  __pycache__
15
15
  .hop/alt_config
16
16
  .hop/local_config
17
- .hop/backups/
17
+ .hop/backups/
18
+ .hop/production
19
+ .hop/.fetching
@@ -1,11 +1,23 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
3
  # Half-ORM pre-commit hook
4
+ # 0. Blocks commits on production servers (read-only)
4
5
  # 1. Checks if current ho-* branch exists on remote origin
5
6
  # 2. Protects ho-prod branch from direct commits
6
7
  # 3. Optionally calls pre-commit-custom if it exists
7
8
  # Generated by half_orm_dev
8
9
 
10
+ # =============================================================================
11
+ # PRODUCTION SERVER GUARD
12
+ # =============================================================================
13
+ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
14
+ if [ -f "${REPO_ROOT}/.hop/production" ]; then
15
+ echo "ERROR: git commit is not allowed on a production server." >&2
16
+ echo " This repository is read-only (production mode)." >&2
17
+ echo " Changes are deployed via 'hop upgrade', never committed directly." >&2
18
+ exit 1
19
+ fi
20
+
9
21
  # Get current branch
10
22
  CURRENT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
11
23
 
@@ -0,0 +1,18 @@
1
+ #!/bin/sh
2
+
3
+ # Half-ORM pre-push hook
4
+ # Blocks git push on production servers (read-only mode).
5
+ # Generated by half_orm_dev
6
+
7
+ # =============================================================================
8
+ # PRODUCTION SERVER GUARD
9
+ # =============================================================================
10
+ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
11
+ if [ -f "${REPO_ROOT}/.hop/production" ]; then
12
+ echo "ERROR: git push is not allowed on a production server." >&2
13
+ echo " This repository is read-only (production mode)." >&2
14
+ echo " Changes are deployed via 'hop upgrade', never pushed directly." >&2
15
+ exit 1
16
+ fi
17
+
18
+ exit 0
@@ -0,0 +1,34 @@
1
+ #!/bin/sh
2
+
3
+ # Half-ORM reference-transaction hook
4
+ # Blocks tag creation on production servers (read-only mode).
5
+ # Generated by half_orm_dev
6
+
7
+ # Only act on the "prepared" phase (before the transaction is committed).
8
+ if [ "$1" != "prepared" ]; then
9
+ exit 0
10
+ fi
11
+
12
+ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
13
+ if [ ! -f "${REPO_ROOT}/.hop/production" ]; then
14
+ exit 0
15
+ fi
16
+
17
+ # Allow tag updates that originate from an internal hop fetch operation.
18
+ # hop sets .hop/.fetching before calling git fetch and removes it after.
19
+ if [ -f "${REPO_ROOT}/.hop/.fetching" ]; then
20
+ exit 0
21
+ fi
22
+
23
+ # Block any local tag creation.
24
+ while read -r _ _ ref_name; do
25
+ case "$ref_name" in
26
+ refs/tags/*)
27
+ echo "ERROR: git tag is not allowed on a production server." >&2
28
+ echo " This repository is read-only (production mode)." >&2
29
+ exit 1
30
+ ;;
31
+ esac
32
+ done
33
+
34
+ exit 0
@@ -0,0 +1 @@
1
+ 1.0.0-a20
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: half_orm_dev
3
- Version: 1.0.0a19
3
+ Version: 1.0.0a20
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
@@ -72,4 +72,6 @@ half_orm_dev/templates/relation_test
72
72
  half_orm_dev/templates/sql_adapter
73
73
  half_orm_dev/templates/warning
74
74
  half_orm_dev/templates/git-hooks/pre-commit
75
- half_orm_dev/templates/git-hooks/prepare-commit-msg
75
+ half_orm_dev/templates/git-hooks/pre-push
76
+ half_orm_dev/templates/git-hooks/prepare-commit-msg
77
+ half_orm_dev/templates/git-hooks/reference-transaction
@@ -1 +0,0 @@
1
- 1.0.0-a19
File without changes
File without changes