half-orm-dev 0.17.3a7__py3-none-any.whl → 0.17.3a9__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.
- half_orm_dev/database.py +21 -157
- half_orm_dev/migrations/0/17/4/00_toml_dict_format.py +204 -0
- half_orm_dev/patch_manager.py +264 -61
- half_orm_dev/release_file.py +32 -18
- half_orm_dev/release_manager.py +168 -36
- half_orm_dev/repo.py +125 -54
- half_orm_dev/version.txt +1 -1
- {half_orm_dev-0.17.3a7.dist-info → half_orm_dev-0.17.3a9.dist-info}/METADATA +2 -2
- {half_orm_dev-0.17.3a7.dist-info → half_orm_dev-0.17.3a9.dist-info}/RECORD +13 -12
- {half_orm_dev-0.17.3a7.dist-info → half_orm_dev-0.17.3a9.dist-info}/WHEEL +0 -0
- {half_orm_dev-0.17.3a7.dist-info → half_orm_dev-0.17.3a9.dist-info}/licenses/AUTHORS +0 -0
- {half_orm_dev-0.17.3a7.dist-info → half_orm_dev-0.17.3a9.dist-info}/licenses/LICENSE +0 -0
- {half_orm_dev-0.17.3a7.dist-info → half_orm_dev-0.17.3a9.dist-info}/top_level.txt +0 -0
half_orm_dev/release_manager.py
CHANGED
|
@@ -680,6 +680,8 @@ class ReleaseManager:
|
|
|
680
680
|
"""
|
|
681
681
|
Lit les patch IDs d'un fichier de release.
|
|
682
682
|
|
|
683
|
+
Format: patch_id:merge_commit (one per line)
|
|
684
|
+
|
|
683
685
|
Ignore:
|
|
684
686
|
- Lignes vides
|
|
685
687
|
- Commentaires (#)
|
|
@@ -695,41 +697,107 @@ class ReleaseManager:
|
|
|
695
697
|
for line in f:
|
|
696
698
|
line = line.strip()
|
|
697
699
|
if line and not line.startswith('#'):
|
|
698
|
-
|
|
700
|
+
patch_id = line.split(':')[0]
|
|
701
|
+
patch_ids.append(patch_id)
|
|
699
702
|
|
|
700
703
|
return patch_ids
|
|
701
704
|
|
|
702
|
-
def
|
|
705
|
+
def read_release_patches_with_commits(self, filename: str) -> dict:
|
|
706
|
+
"""
|
|
707
|
+
Lit les patch IDs et merge_commits d'un fichier de release.
|
|
708
|
+
|
|
709
|
+
Format: patch_id:merge_commit (one per line)
|
|
710
|
+
|
|
711
|
+
Returns:
|
|
712
|
+
dict: {patch_id: merge_commit}
|
|
713
|
+
|
|
714
|
+
Example:
|
|
715
|
+
# File content:
|
|
716
|
+
# 1-premier:ce96282f
|
|
717
|
+
# 2-second:8e10f11b
|
|
718
|
+
patches = read_release_patches_with_commits("0.1.0-rc1.txt")
|
|
719
|
+
# → {"1-premier": "ce96282f", "2-second": "8e10f11b"}
|
|
720
|
+
"""
|
|
721
|
+
file_path = self._releases_dir / filename
|
|
722
|
+
|
|
723
|
+
if not file_path.exists():
|
|
724
|
+
return {}
|
|
725
|
+
|
|
726
|
+
patches = {}
|
|
727
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
728
|
+
for line in f:
|
|
729
|
+
line = line.strip()
|
|
730
|
+
if line and not line.startswith('#'):
|
|
731
|
+
patch_id, merge_commit = line.split(':', 1)
|
|
732
|
+
patches[patch_id] = merge_commit
|
|
733
|
+
|
|
734
|
+
return patches
|
|
735
|
+
|
|
736
|
+
def _apply_release_patches(self, version: str, hotfix=False, force_apply=False) -> None:
|
|
703
737
|
"""
|
|
704
738
|
Apply all patches for a release version to the database.
|
|
705
739
|
|
|
706
|
-
|
|
740
|
+
If a release schema exists (release-X.Y.Z.sql) and force_apply=False,
|
|
741
|
+
uses it directly. Otherwise, restores database from baseline and
|
|
742
|
+
applies patches in order:
|
|
707
743
|
1. All RC patches (rc1, rc2, etc.)
|
|
708
744
|
2. Stage patches
|
|
709
745
|
|
|
746
|
+
For staged patches with merge_commit recorded, checks out each commit
|
|
747
|
+
before applying the patch to ensure the correct Python code context.
|
|
748
|
+
|
|
710
749
|
Args:
|
|
711
750
|
version: Release version (e.g., "0.1.0")
|
|
751
|
+
hotfix: If True, skip RC patches (hotfix workflow)
|
|
752
|
+
force_apply: If True, always apply patches individually even if
|
|
753
|
+
release schema exists (used for production validation)
|
|
712
754
|
|
|
713
755
|
Raises:
|
|
714
756
|
ReleaseManagerError: If patch application fails
|
|
715
757
|
"""
|
|
716
|
-
#
|
|
758
|
+
# Check if release schema exists (new workflow)
|
|
759
|
+
release_schema_path = self._repo.get_release_schema_path(version)
|
|
760
|
+
if release_schema_path.exists() and not force_apply:
|
|
761
|
+
# New workflow: restore from release schema (already contains all staged patches)
|
|
762
|
+
self._repo.restore_database_from_release_schema(version)
|
|
763
|
+
return
|
|
764
|
+
|
|
765
|
+
# Fallback: old workflow - restore database from baseline
|
|
717
766
|
self._repo.restore_database_from_schema()
|
|
718
767
|
|
|
719
|
-
|
|
768
|
+
current_branch = self._repo.hgit.branch
|
|
769
|
+
|
|
770
|
+
# Collect patches already applied from RC files
|
|
771
|
+
applied_patches = set()
|
|
772
|
+
|
|
773
|
+
# Apply all RC patches in order (with merge_commit checkout)
|
|
720
774
|
if not hotfix:
|
|
721
775
|
rc_files = self._get_label_files(version, 'rc')
|
|
722
776
|
for rc_file in rc_files:
|
|
723
|
-
rc_patches = self.
|
|
724
|
-
for patch_id in rc_patches:
|
|
777
|
+
rc_patches = self.read_release_patches_with_commits(rc_file.name)
|
|
778
|
+
for patch_id, merge_commit in rc_patches.items():
|
|
779
|
+
if merge_commit:
|
|
780
|
+
self._repo.hgit.checkout(merge_commit)
|
|
725
781
|
self._repo.patch_manager.apply_patch_files(patch_id, self._repo.model)
|
|
782
|
+
applied_patches.add(patch_id)
|
|
726
783
|
|
|
727
|
-
# Apply staged patches from TOML file
|
|
728
|
-
#
|
|
784
|
+
# Apply staged patches from TOML file that are NOT already in RC files
|
|
785
|
+
# (patches added after promote_to_rc)
|
|
729
786
|
release_file = ReleaseFile(version, self._releases_dir)
|
|
730
787
|
if release_file.exists():
|
|
731
788
|
# Development: read from TOML
|
|
732
789
|
stage_patches = release_file.get_patches(status="staged")
|
|
790
|
+
|
|
791
|
+
# Apply only patches not already applied from RC
|
|
792
|
+
for patch_id in stage_patches:
|
|
793
|
+
if patch_id in applied_patches:
|
|
794
|
+
continue # Already applied from RC file
|
|
795
|
+
|
|
796
|
+
merge_commit = release_file.get_merge_commit(patch_id)
|
|
797
|
+
if merge_commit:
|
|
798
|
+
# Checkout the merge commit to have correct Python code
|
|
799
|
+
self._repo.hgit.checkout(merge_commit)
|
|
800
|
+
self._repo.patch_manager.apply_patch_files(patch_id, self._repo.model)
|
|
733
801
|
else:
|
|
734
802
|
# Production: read from hotfix snapshot if it exists
|
|
735
803
|
# This handles the case where we're applying a hotfix release
|
|
@@ -741,8 +809,14 @@ class ReleaseManager:
|
|
|
741
809
|
# No patches to apply
|
|
742
810
|
stage_patches = []
|
|
743
811
|
|
|
744
|
-
|
|
745
|
-
|
|
812
|
+
# For production snapshots (no TOML), apply patches without checkout
|
|
813
|
+
# (merge_commit info not available in .txt files)
|
|
814
|
+
for patch_id in stage_patches:
|
|
815
|
+
if patch_id not in applied_patches:
|
|
816
|
+
self._repo.patch_manager.apply_patch_files(patch_id, self._repo.model)
|
|
817
|
+
|
|
818
|
+
# Return to original branch
|
|
819
|
+
self._repo.hgit.checkout(current_branch)
|
|
746
820
|
|
|
747
821
|
def _collect_all_version_patches(self, version: str) -> List[str]:
|
|
748
822
|
"""
|
|
@@ -2249,6 +2323,24 @@ class ReleaseManager:
|
|
|
2249
2323
|
except Exception as e:
|
|
2250
2324
|
raise ReleaseManagerError(f"Failed to checkout release branch: {e}")
|
|
2251
2325
|
|
|
2326
|
+
# Generate release schema file
|
|
2327
|
+
# Use existing release schema as base if available, otherwise use prod
|
|
2328
|
+
try:
|
|
2329
|
+
base_release = self._find_base_release_schema(version)
|
|
2330
|
+
if base_release:
|
|
2331
|
+
self._repo.restore_database_from_release_schema(base_release)
|
|
2332
|
+
else:
|
|
2333
|
+
self._repo.restore_database_from_schema()
|
|
2334
|
+
|
|
2335
|
+
release_schema_path = self._repo.generate_release_schema(version)
|
|
2336
|
+
|
|
2337
|
+
# Commit release schema on release branch
|
|
2338
|
+
self._repo.hgit.add(str(release_schema_path))
|
|
2339
|
+
self._repo.hgit.commit('-m', f"[HOP] Add release schema for %{version}")
|
|
2340
|
+
self._repo.hgit.push()
|
|
2341
|
+
except Exception as e:
|
|
2342
|
+
raise ReleaseManagerError(f"Failed to generate release schema: {e}")
|
|
2343
|
+
|
|
2252
2344
|
return {
|
|
2253
2345
|
'version': version,
|
|
2254
2346
|
'branch': release_branch,
|
|
@@ -2289,6 +2381,53 @@ class ReleaseManager:
|
|
|
2289
2381
|
patches_files.sort(key=version_key)
|
|
2290
2382
|
return patches_files[0].stem.replace('-patches', '')
|
|
2291
2383
|
|
|
2384
|
+
def _find_base_release_schema(self, new_version: str) -> Optional[str]:
|
|
2385
|
+
"""
|
|
2386
|
+
Find the base release schema for a new release.
|
|
2387
|
+
|
|
2388
|
+
When creating a new release, determines which schema to use as base:
|
|
2389
|
+
- If a release with lower version exists and has a release schema, use it
|
|
2390
|
+
- Otherwise, return None (will use production schema)
|
|
2391
|
+
|
|
2392
|
+
This handles parallel releases:
|
|
2393
|
+
- Creating 0.18.0 (minor) when 0.17.1 (patch) exists → use release-0.17.1.sql
|
|
2394
|
+
|
|
2395
|
+
Note: Only one release per level can exist at a time (sequential promotion rule).
|
|
2396
|
+
|
|
2397
|
+
Args:
|
|
2398
|
+
new_version: Version being created (e.g., "0.18.0")
|
|
2399
|
+
|
|
2400
|
+
Returns:
|
|
2401
|
+
Version string of base release, or None if should use prod schema
|
|
2402
|
+
"""
|
|
2403
|
+
from packaging.version import Version
|
|
2404
|
+
|
|
2405
|
+
new_ver = Version(new_version)
|
|
2406
|
+
model_dir = Path(self._repo.model_dir)
|
|
2407
|
+
|
|
2408
|
+
# Find all existing release schema files
|
|
2409
|
+
release_schemas = list(model_dir.glob("release-*.sql"))
|
|
2410
|
+
if not release_schemas:
|
|
2411
|
+
return None
|
|
2412
|
+
|
|
2413
|
+
# Find the release with highest version lower than new_version
|
|
2414
|
+
best_match = None
|
|
2415
|
+
best_ver = None
|
|
2416
|
+
|
|
2417
|
+
for schema_file in release_schemas:
|
|
2418
|
+
match = re.match(r'release-(\d+\.\d+\.\d+)\.sql$', schema_file.name)
|
|
2419
|
+
if match:
|
|
2420
|
+
ver_str = match.group(1)
|
|
2421
|
+
try:
|
|
2422
|
+
ver = Version(ver_str)
|
|
2423
|
+
if ver < new_ver and (best_ver is None or ver > best_ver):
|
|
2424
|
+
best_ver = ver
|
|
2425
|
+
best_match = ver_str
|
|
2426
|
+
except Exception:
|
|
2427
|
+
continue
|
|
2428
|
+
|
|
2429
|
+
return best_match
|
|
2430
|
+
|
|
2292
2431
|
@with_dynamic_branch_lock(lambda self: "ho-prod")
|
|
2293
2432
|
def promote_to_rc(self) -> dict:
|
|
2294
2433
|
"""
|
|
@@ -2355,10 +2494,14 @@ class ReleaseManager:
|
|
|
2355
2494
|
# Stay on release branch for rename operations
|
|
2356
2495
|
# (allows continued development on this release)
|
|
2357
2496
|
|
|
2358
|
-
# Create RC snapshot from staged patches
|
|
2497
|
+
# Create RC snapshot from staged patches (with merge_commit)
|
|
2359
2498
|
rc_file = self._releases_dir / f"{version}-rc{rc_number}.txt"
|
|
2360
2499
|
staged_patches = release_file.get_patches(status="staged")
|
|
2361
|
-
|
|
2500
|
+
lines = []
|
|
2501
|
+
for patch_id in staged_patches:
|
|
2502
|
+
merge_commit = release_file.get_merge_commit(patch_id)
|
|
2503
|
+
lines.append(f"{patch_id}:{merge_commit}" if merge_commit else patch_id)
|
|
2504
|
+
rc_file.write_text("\n".join(lines) + "\n" if lines else "", encoding='utf-8')
|
|
2362
2505
|
|
|
2363
2506
|
# Keep TOML file for continued development (don't delete it)
|
|
2364
2507
|
|
|
@@ -2498,29 +2641,13 @@ class ReleaseManager:
|
|
|
2498
2641
|
"ho-prod has been restored to its previous state."
|
|
2499
2642
|
)
|
|
2500
2643
|
|
|
2501
|
-
# 3. Apply
|
|
2502
|
-
#
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
latest_rc = self._get_latest_label_number(version, "rc")
|
|
2509
|
-
rc_schema_version = f"{version}-rc{latest_rc}" if latest_rc > 0 else "0.0.0"
|
|
2510
|
-
|
|
2511
|
-
# Restore from RC schema if it exists, otherwise from baseline
|
|
2512
|
-
try:
|
|
2513
|
-
self._repo.restore_database_from_schema(rc_schema_version)
|
|
2514
|
-
except:
|
|
2515
|
-
self._repo.restore_database_from_schema()
|
|
2516
|
-
|
|
2517
|
-
# Apply new stage patches
|
|
2518
|
-
for patch_id in stage_patches:
|
|
2519
|
-
self._repo.patch_manager.apply_patch_files(patch_id, self._repo.model)
|
|
2520
|
-
else:
|
|
2521
|
-
# No new patches - just restore from latest RC
|
|
2522
|
-
# The database should already be in the correct state from RC
|
|
2523
|
-
pass
|
|
2644
|
+
# 3. Apply all patches (RC + staged) to database
|
|
2645
|
+
# Uses _apply_release_patches which handles:
|
|
2646
|
+
# - Reading RC files with merge_commits (patch_id:merge_commit format)
|
|
2647
|
+
# - Checking out each merge_commit before applying patch
|
|
2648
|
+
# - Reading staged patches from TOML with merge_commits
|
|
2649
|
+
# force_apply=True to validate by applying patches even if release schema exists
|
|
2650
|
+
self._apply_release_patches(version, force_apply=True)
|
|
2524
2651
|
|
|
2525
2652
|
# Register the release version in half_orm_meta.hop_release
|
|
2526
2653
|
version_parts = version.split('.')
|
|
@@ -2542,6 +2669,11 @@ class ReleaseManager:
|
|
|
2542
2669
|
if toml_file.exists():
|
|
2543
2670
|
toml_file.unlink()
|
|
2544
2671
|
|
|
2672
|
+
# Delete release schema file (no longer needed - prod schema takes over)
|
|
2673
|
+
release_schema_file = model_dir / f"release-{version}.sql"
|
|
2674
|
+
if release_schema_file.exists():
|
|
2675
|
+
release_schema_file.unlink()
|
|
2676
|
+
|
|
2545
2677
|
# Generate model/data-X.Y.Z.sql if any patches have @HOP:data files
|
|
2546
2678
|
# This file is used for from-scratch installations (clone, restore)
|
|
2547
2679
|
prod_patches = self.read_release_patches(prod_file.name)
|
half_orm_dev/repo.py
CHANGED
|
@@ -1199,10 +1199,7 @@ class Repo:
|
|
|
1199
1199
|
# Step 1b: Validate git origin URL (EARLY validation)
|
|
1200
1200
|
self._validate_git_origin_url(git_origin)
|
|
1201
1201
|
|
|
1202
|
-
# Step 2:
|
|
1203
|
-
self._verify_database_configured(package_name)
|
|
1204
|
-
|
|
1205
|
-
# Step 3: Connect to database and detect mode
|
|
1202
|
+
# Step 2: Connect to database and detect mode
|
|
1206
1203
|
devel_mode = self._detect_development_mode(package_name)
|
|
1207
1204
|
|
|
1208
1205
|
# Step 4: Setup project directory
|
|
@@ -1645,56 +1642,6 @@ class Repo:
|
|
|
1645
1642
|
# Store normalized name for later use
|
|
1646
1643
|
return normalized_name
|
|
1647
1644
|
|
|
1648
|
-
|
|
1649
|
-
def _verify_database_configured(self, package_name):
|
|
1650
|
-
"""
|
|
1651
|
-
Verify database is configured via init-database command.
|
|
1652
|
-
|
|
1653
|
-
Checks that database configuration file exists and is accessible.
|
|
1654
|
-
Does NOT create the database - assumes init-database was run first.
|
|
1655
|
-
|
|
1656
|
-
Args:
|
|
1657
|
-
package_name (str): Database name to verify
|
|
1658
|
-
|
|
1659
|
-
Raises:
|
|
1660
|
-
DatabaseNotConfiguredError: If configuration file doesn't exist
|
|
1661
|
-
DatabaseConnectionError: If cannot connect to configured database
|
|
1662
|
-
|
|
1663
|
-
Process:
|
|
1664
|
-
1. Check ~/.half_orm/<package_name> exists
|
|
1665
|
-
2. Attempt connection to verify database is accessible
|
|
1666
|
-
3. Store connection for later use
|
|
1667
|
-
|
|
1668
|
-
Examples:
|
|
1669
|
-
# Database configured
|
|
1670
|
-
_verify_database_configured("my_blog") # Success
|
|
1671
|
-
|
|
1672
|
-
# Database not configured
|
|
1673
|
-
_verify_database_configured("unconfigured_db")
|
|
1674
|
-
# Raises: DatabaseNotConfiguredError with helpful message
|
|
1675
|
-
"""
|
|
1676
|
-
# Try to load database configuration
|
|
1677
|
-
config = Database._load_configuration(package_name)
|
|
1678
|
-
|
|
1679
|
-
if config is None:
|
|
1680
|
-
raise ValueError(
|
|
1681
|
-
f"Database '{package_name}' is not configured.\n"
|
|
1682
|
-
f"Please run: half_orm dev init-database {package_name} [OPTIONS]\n"
|
|
1683
|
-
f"See 'half_orm dev init-database --help' for more information."
|
|
1684
|
-
)
|
|
1685
|
-
|
|
1686
|
-
# Try to connect to verify database is accessible
|
|
1687
|
-
try:
|
|
1688
|
-
model = Model(package_name)
|
|
1689
|
-
# Store model for later use
|
|
1690
|
-
return model
|
|
1691
|
-
except OperationalError as e:
|
|
1692
|
-
raise OperationalError(
|
|
1693
|
-
f"Cannot connect to database '{package_name}'.\n"
|
|
1694
|
-
f"Database may not exist or connection parameters may be incorrect.\n"
|
|
1695
|
-
f"Original error: {e}"
|
|
1696
|
-
)
|
|
1697
|
-
|
|
1698
1645
|
def _detect_development_mode(self, package_name):
|
|
1699
1646
|
"""
|
|
1700
1647
|
Detect development mode based on metadata presence in database.
|
|
@@ -2335,6 +2282,130 @@ See docs/half_orm_dev.md for complete documentation.
|
|
|
2335
2282
|
# Catch any unexpected errors
|
|
2336
2283
|
raise RepoError(f"Database restoration failed: {e}") from e
|
|
2337
2284
|
|
|
2285
|
+
def generate_release_schema(self, version: str) -> Path:
|
|
2286
|
+
"""
|
|
2287
|
+
Generate release schema SQL dump.
|
|
2288
|
+
|
|
2289
|
+
Creates .hop/model/release-{version}.sql with current database structure,
|
|
2290
|
+
metadata, and data. This file represents the complete state of a release
|
|
2291
|
+
in development (prod + all staged patches).
|
|
2292
|
+
|
|
2293
|
+
Used by:
|
|
2294
|
+
- release create: Generate initial release schema from prod baseline
|
|
2295
|
+
- patch merge: Update release schema after patch integration
|
|
2296
|
+
|
|
2297
|
+
Args:
|
|
2298
|
+
version: Release version string (e.g., "0.17.1", "0.18.0")
|
|
2299
|
+
|
|
2300
|
+
Returns:
|
|
2301
|
+
Path to generated release schema file
|
|
2302
|
+
|
|
2303
|
+
Raises:
|
|
2304
|
+
RepoError: If pg_dump fails or model_dir doesn't exist
|
|
2305
|
+
|
|
2306
|
+
Examples:
|
|
2307
|
+
# After merging patch into release
|
|
2308
|
+
schema_path = repo.generate_release_schema("0.17.1")
|
|
2309
|
+
# Creates: .hop/model/release-0.17.1.sql
|
|
2310
|
+
"""
|
|
2311
|
+
model_dir = Path(self.model_dir)
|
|
2312
|
+
|
|
2313
|
+
if not model_dir.exists():
|
|
2314
|
+
raise RepoError(f"Model directory does not exist: {model_dir}")
|
|
2315
|
+
|
|
2316
|
+
release_schema_file = model_dir / f"release-{version}.sql"
|
|
2317
|
+
temp_file = model_dir / f".release-{version}.sql.tmp"
|
|
2318
|
+
|
|
2319
|
+
try:
|
|
2320
|
+
# Dump complete database (schema + data) to temp file
|
|
2321
|
+
self.database.execute_pg_command(
|
|
2322
|
+
'pg_dump',
|
|
2323
|
+
self.name,
|
|
2324
|
+
'--no-owner',
|
|
2325
|
+
'-f',
|
|
2326
|
+
str(temp_file)
|
|
2327
|
+
)
|
|
2328
|
+
|
|
2329
|
+
# Filter out version-specific lines for cross-version compatibility
|
|
2330
|
+
content = temp_file.read_text()
|
|
2331
|
+
filtered_lines = []
|
|
2332
|
+
version_specific_sets = (
|
|
2333
|
+
'SET transaction_timeout', # PG17+
|
|
2334
|
+
)
|
|
2335
|
+
for line in content.split('\n'):
|
|
2336
|
+
if line.startswith('\\restrict') or line.startswith('\\unrestrict'):
|
|
2337
|
+
continue
|
|
2338
|
+
if line.startswith('-- Dumped from') or line.startswith('-- Dumped by'):
|
|
2339
|
+
continue
|
|
2340
|
+
if any(line.startswith(s) for s in version_specific_sets):
|
|
2341
|
+
continue
|
|
2342
|
+
filtered_lines.append(line)
|
|
2343
|
+
|
|
2344
|
+
release_schema_file.write_text('\n'.join(filtered_lines))
|
|
2345
|
+
|
|
2346
|
+
return release_schema_file
|
|
2347
|
+
|
|
2348
|
+
except Exception as e:
|
|
2349
|
+
raise RepoError(f"Failed to generate release schema: {e}") from e
|
|
2350
|
+
finally:
|
|
2351
|
+
if temp_file.exists():
|
|
2352
|
+
temp_file.unlink()
|
|
2353
|
+
|
|
2354
|
+
def restore_database_from_release_schema(self, version: str) -> None:
|
|
2355
|
+
"""
|
|
2356
|
+
Restore database from release schema file.
|
|
2357
|
+
|
|
2358
|
+
Restores database from .hop/model/release-{version}.sql which contains
|
|
2359
|
+
the complete state of a release in development (prod + staged patches).
|
|
2360
|
+
|
|
2361
|
+
If the release schema file doesn't exist, falls back to
|
|
2362
|
+
restore_database_from_schema() for backward compatibility.
|
|
2363
|
+
|
|
2364
|
+
Args:
|
|
2365
|
+
version: Release version string (e.g., "0.17.1")
|
|
2366
|
+
|
|
2367
|
+
Raises:
|
|
2368
|
+
RepoError: If restoration fails
|
|
2369
|
+
|
|
2370
|
+
Examples:
|
|
2371
|
+
# Before applying a candidate patch
|
|
2372
|
+
repo.restore_database_from_release_schema("0.17.1")
|
|
2373
|
+
# Database now has prod schema + all staged patches for 0.17.1
|
|
2374
|
+
"""
|
|
2375
|
+
release_schema_path = Path(self.model_dir) / f"release-{version}.sql"
|
|
2376
|
+
|
|
2377
|
+
# Fallback to production schema if release schema doesn't exist
|
|
2378
|
+
if not release_schema_path.exists():
|
|
2379
|
+
self.restore_database_from_schema()
|
|
2380
|
+
return
|
|
2381
|
+
|
|
2382
|
+
try:
|
|
2383
|
+
# Drop all user schemas
|
|
2384
|
+
self._reset_database_schemas()
|
|
2385
|
+
|
|
2386
|
+
# Load release schema
|
|
2387
|
+
self.database.execute_pg_command(
|
|
2388
|
+
'psql', '-d', self.name, '-f', str(release_schema_path)
|
|
2389
|
+
)
|
|
2390
|
+
|
|
2391
|
+
# Reload half_orm metadata cache
|
|
2392
|
+
self.model.reconnect(reload=True)
|
|
2393
|
+
|
|
2394
|
+
except Exception as e:
|
|
2395
|
+
raise RepoError(f"Failed to restore from release schema: {e}") from e
|
|
2396
|
+
|
|
2397
|
+
def get_release_schema_path(self, version: str) -> Path:
|
|
2398
|
+
"""
|
|
2399
|
+
Get path to release schema file.
|
|
2400
|
+
|
|
2401
|
+
Args:
|
|
2402
|
+
version: Release version string
|
|
2403
|
+
|
|
2404
|
+
Returns:
|
|
2405
|
+
Path to .hop/model/release-{version}.sql (may not exist)
|
|
2406
|
+
"""
|
|
2407
|
+
return Path(self.model_dir) / f"release-{version}.sql"
|
|
2408
|
+
|
|
2338
2409
|
def _deduce_metadata_path(self, schema_path: Path) -> Path | None:
|
|
2339
2410
|
"""
|
|
2340
2411
|
Deduce metadata file path from schema.sql symlink target.
|
half_orm_dev/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.17.3-
|
|
1
|
+
0.17.3-a9
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: half_orm_dev
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.3a9
|
|
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
|
|
@@ -23,7 +23,7 @@ Requires-Dist: GitPython
|
|
|
23
23
|
Requires-Dist: click
|
|
24
24
|
Requires-Dist: pydash
|
|
25
25
|
Requires-Dist: pytest
|
|
26
|
-
Requires-Dist: half_orm<0.18.0,>=0.17.
|
|
26
|
+
Requires-Dist: half_orm<0.18.0,>=0.17.3
|
|
27
27
|
Requires-Dist: tomli>=2.0.0; python_version < "3.11"
|
|
28
28
|
Requires-Dist: tomli_w>=1.0.0
|
|
29
29
|
Dynamic: license-file
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
half_orm_dev/__init__.py,sha256=0JpUPey1gacxXuIFGcpD2nTGso73fkak72qzTHttAJk,18
|
|
2
2
|
half_orm_dev/cli_extension.py,sha256=kwX3M11_rwr0pFcqHK_bpI3Pp4ztfTCVz2gLfTmzfeA,1066
|
|
3
|
-
half_orm_dev/database.py,sha256=
|
|
3
|
+
half_orm_dev/database.py,sha256=bV7QpgT6R-LpCJcaAKOo5qJ0jD03JenSoLV4f7ZdTag,56377
|
|
4
4
|
half_orm_dev/decorators.py,sha256=JKv_Z_JZUr-s-Vz551temHZhhecPfbvyhTbByRDjVAQ,4901
|
|
5
5
|
half_orm_dev/hgit.py,sha256=VdzCCQ__xG1IGJaGq4-rrhbA1bNkDw_dBqkUNIeTONg,58045
|
|
6
6
|
half_orm_dev/migration_manager.py,sha256=9RpciH8nyQrF0xV31kAeaYKkQl24Di1VHt-mAjjHhzM,14854
|
|
7
7
|
half_orm_dev/modules.py,sha256=4jfVb2yboRgb9mcO0sMF-iLigcZFTHEm4VRLN6GQXM4,16796
|
|
8
|
-
half_orm_dev/patch_manager.py,sha256=
|
|
8
|
+
half_orm_dev/patch_manager.py,sha256=yFVaSDid6VJAclQre-J-QWjyDIDNHi6EzRybtMEguow,110669
|
|
9
9
|
half_orm_dev/patch_validator.py,sha256=QNe1L6k_xwsnrOTcb3vkW2D0LbqrCRcZOGPnVyspVRk,10871
|
|
10
|
-
half_orm_dev/release_file.py,sha256=
|
|
11
|
-
half_orm_dev/release_manager.py,sha256=
|
|
12
|
-
half_orm_dev/repo.py,sha256=
|
|
10
|
+
half_orm_dev/release_file.py,sha256=2sfWYqjqfOuMtPDH6qNanXUD9UMaZ7yt912o6aZ0GYI,10570
|
|
11
|
+
half_orm_dev/release_manager.py,sha256=IBjQ26ostzXYu5PF-cx-Z7Wezt8SpG9SkeoTnTNFt2c,130985
|
|
12
|
+
half_orm_dev/repo.py,sha256=W14IR-G737QgVgWV6FMC3zVJfG87X9VbzOC3DLJoJng,100601
|
|
13
13
|
half_orm_dev/utils.py,sha256=M3yViUFfsO7Cp9MYSoUSkCZ6R9w_4jW45UDZUOT8FhI,1493
|
|
14
|
-
half_orm_dev/version.txt,sha256=
|
|
14
|
+
half_orm_dev/version.txt,sha256=mcwJgjmZIdLLkWl4QWMi2Zy-eHfrnal7--Y_GUibulg,10
|
|
15
15
|
half_orm_dev/cli/__init__.py,sha256=0CbMj8OIhZmglWakK7NhYPn302erUTEg2VHOdm1hRTQ,163
|
|
16
16
|
half_orm_dev/cli/main.py,sha256=3SVTl5WraNTSY6o7LfvE1dUHKg_RcuVaHHDIn_oINv4,11701
|
|
17
17
|
half_orm_dev/cli/commands/__init__.py,sha256=UhWf0AnWqy4gyFo2SJQv8pL_YJ43pE_c9TgopcjzKDg,1490
|
|
@@ -30,6 +30,7 @@ half_orm_dev/cli/commands/update.py,sha256=OarpDkC8hF08UUv1l2i-2qpHICgpB6WDn9ziY
|
|
|
30
30
|
half_orm_dev/cli/commands/upgrade.py,sha256=9puvnanp6D2n3cQGcmK1-AI6Z_GlTYPZcaZgN1m5wDE,6297
|
|
31
31
|
half_orm_dev/migrations/0/17/1/00_move_to_hop.py,sha256=esXYcdgNUJfUQUCtD_BBxHbdQi7_s4nWJ8bJYNIUfY4,4000
|
|
32
32
|
half_orm_dev/migrations/0/17/1/01_txt_to_toml.py,sha256=CJQo4-DTI0tPBD6VXNo0HsNkG2fkSxHAcQ-5cwXVt_4,4995
|
|
33
|
+
half_orm_dev/migrations/0/17/4/00_toml_dict_format.py,sha256=5LYVExrAhVmS_Fkp2uhs9j5xtWrp3FhzDhrwn3_CQdI,6224
|
|
33
34
|
half_orm_dev/patches/log,sha256=n7MNnGR09Obd87gXLzIi6zA76sI4RhOJzC25wb0TbKE,22
|
|
34
35
|
half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql,sha256=gMZ94YlyrftxcqDn0l-ToCTee4A_bnP58DpHcIT_T1w,1074
|
|
35
36
|
half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql,sha256=nhRbDi6sUenvVfOnoRuWSbLEC1cEfzrXbxDof2weq04,183
|
|
@@ -51,9 +52,9 @@ half_orm_dev/templates/sql_adapter,sha256=kAP5y7Qml3DKsbZLUeoVpeXjbQcWltHjkDznED
|
|
|
51
52
|
half_orm_dev/templates/warning,sha256=4hlZ_rRdpmkXxOeRoVd9xnXBARYXn95e-iXrD1f2u7k,490
|
|
52
53
|
half_orm_dev/templates/git-hooks/pre-commit,sha256=Hf084pqeiOebrv4xzA0aiaHbIXswmmNO-dSIXUfzMK0,4707
|
|
53
54
|
half_orm_dev/templates/git-hooks/prepare-commit-msg,sha256=zknOGGoaWKC97zfga2Xl2i_psnNo9MJbrEBuN91eHNw,1070
|
|
54
|
-
half_orm_dev-0.17.
|
|
55
|
-
half_orm_dev-0.17.
|
|
56
|
-
half_orm_dev-0.17.
|
|
57
|
-
half_orm_dev-0.17.
|
|
58
|
-
half_orm_dev-0.17.
|
|
59
|
-
half_orm_dev-0.17.
|
|
55
|
+
half_orm_dev-0.17.3a9.dist-info/licenses/AUTHORS,sha256=eWxqzRdLOt2gX0FMQj_wui03Od3jdlwa8xNe9tl84g0,113
|
|
56
|
+
half_orm_dev-0.17.3a9.dist-info/licenses/LICENSE,sha256=ufhxlSi6mttkGQTsGWrEoB3WA_fCPJ6-k07GSVBgyPw,644
|
|
57
|
+
half_orm_dev-0.17.3a9.dist-info/METADATA,sha256=5j_555Q9_75zj0GZADXj-PUEf-lXJ7xL-v5tZJzWO7w,16149
|
|
58
|
+
half_orm_dev-0.17.3a9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
59
|
+
half_orm_dev-0.17.3a9.dist-info/top_level.txt,sha256=M5hEsWfn5Kw0HL-VnNmS6Jw-3cwRyjims5a8cr18eTM,13
|
|
60
|
+
half_orm_dev-0.17.3a9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|