half-orm-dev 0.17.5a3__tar.gz → 0.17.5a5__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.
- {half_orm_dev-0.17.5a3/half_orm_dev.egg-info → half_orm_dev-0.17.5a5}/PKG-INFO +1 -1
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/bootstrap_manager.py +41 -13
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/decorators.py +15 -4
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/patch_manager.py +110 -29
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/release_manager.py +21 -102
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/repo.py +36 -8
- half_orm_dev-0.17.5a5/half_orm_dev/version.txt +1 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5/half_orm_dev.egg-info}/PKG-INFO +1 -1
- half_orm_dev-0.17.5a3/half_orm_dev/version.txt +0 -1
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/AUTHORS +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/LICENSE +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/README.md +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/__init__.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/__init__.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/__init__.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/apply.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/bootstrap.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/check.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/clone.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/init.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/migrate.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/patch.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/release.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/restore.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/sync.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/todo.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/undo.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/update.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/commands/upgrade.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli/main.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/cli_extension.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/database.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/file_executor.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/hgit.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migration_manager.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migrations/0/17/1/00_move_to_hop.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migrations/0/17/1/01_txt_to_toml.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migrations/0/17/4/00_toml_dict_format.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migrations/0/17/4/01_add_bootstrap_table.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migrations/0/17/4/02_move_patches_to_subdirs.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migrations/0/17/5/01_update_pyproject_dependency.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/modules.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/patch_validator.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/patches/log +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/release_file.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/scripts/repair-metadata.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/.gitignore +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/MANIFEST.in +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/README +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/conftest_template +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/git-hooks/pre-commit +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/git-hooks/prepare-commit-msg +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/init_module_template +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/module_template_1 +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/module_template_2 +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/module_template_3 +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/pyproject.toml +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/relation_test +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/sql_adapter +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/warning +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/utils.py +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev.egg-info/SOURCES.txt +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev.egg-info/dependency_links.txt +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev.egg-info/requires.txt +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev.egg-info/top_level.txt +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/pyproject.toml +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/setup.cfg +0 -0
- {half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/setup.py +0 -0
|
@@ -78,7 +78,12 @@ class BootstrapManager:
|
|
|
78
78
|
# Schema might not exist yet, ignore
|
|
79
79
|
pass
|
|
80
80
|
|
|
81
|
-
def get_bootstrap_files(
|
|
81
|
+
def get_bootstrap_files(
|
|
82
|
+
self,
|
|
83
|
+
up_to_version: Optional[str] = None,
|
|
84
|
+
exclude_version: Optional[str] = None,
|
|
85
|
+
for_version: Optional[str] = None
|
|
86
|
+
) -> List[Path]:
|
|
82
87
|
"""
|
|
83
88
|
List bootstrap files sorted by numeric prefix.
|
|
84
89
|
|
|
@@ -87,8 +92,11 @@ class BootstrapManager:
|
|
|
87
92
|
|
|
88
93
|
Args:
|
|
89
94
|
up_to_version: If provided, only return files with version <= this version.
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
exclude_version: If provided, exclude files for exactly this version.
|
|
96
|
+
Used during promote to skip the version being promoted
|
|
97
|
+
(its bootstraps run after patches are applied).
|
|
98
|
+
for_version: If provided, only return files for exactly this version.
|
|
99
|
+
Used after patches to run only the promoted version's bootstraps.
|
|
92
100
|
|
|
93
101
|
Returns:
|
|
94
102
|
List of Path objects for bootstrap files in execution order
|
|
@@ -103,11 +111,20 @@ class BootstrapManager:
|
|
|
103
111
|
if not re.match(r'^\d+-', file_path.name):
|
|
104
112
|
continue
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
114
|
+
file_version = self._extract_version_from_filename(file_path.name)
|
|
115
|
+
|
|
116
|
+
# If for_version is specified, only include files for exactly this version
|
|
117
|
+
if for_version:
|
|
118
|
+
if file_version != for_version:
|
|
119
|
+
continue
|
|
120
|
+
else:
|
|
121
|
+
# If exclude_version is specified, skip files for that version
|
|
122
|
+
if exclude_version and file_version == exclude_version:
|
|
110
123
|
continue
|
|
124
|
+
# If up_to_version is specified, filter out files from newer versions
|
|
125
|
+
if up_to_version:
|
|
126
|
+
if file_version != 'unknown' and not self._version_le(file_version, up_to_version):
|
|
127
|
+
continue
|
|
111
128
|
|
|
112
129
|
files.append(file_path)
|
|
113
130
|
|
|
@@ -140,17 +157,24 @@ class BootstrapManager:
|
|
|
140
157
|
# Table might not exist yet (pre-migration)
|
|
141
158
|
return set()
|
|
142
159
|
|
|
143
|
-
def get_pending_files(
|
|
160
|
+
def get_pending_files(
|
|
161
|
+
self,
|
|
162
|
+
up_to_version: Optional[str] = None,
|
|
163
|
+
exclude_version: Optional[str] = None,
|
|
164
|
+
for_version: Optional[str] = None
|
|
165
|
+
) -> List[Path]:
|
|
144
166
|
"""
|
|
145
167
|
Get bootstrap files not yet executed.
|
|
146
168
|
|
|
147
169
|
Args:
|
|
148
170
|
up_to_version: If provided, only return files with version <= this version.
|
|
171
|
+
exclude_version: If provided, exclude files for exactly this version.
|
|
172
|
+
for_version: If provided, only return files for exactly this version.
|
|
149
173
|
|
|
150
174
|
Returns:
|
|
151
175
|
List of Path objects for files pending execution
|
|
152
176
|
"""
|
|
153
|
-
all_files = self.get_bootstrap_files(up_to_version)
|
|
177
|
+
all_files = self.get_bootstrap_files(up_to_version, exclude_version=exclude_version, for_version=for_version)
|
|
154
178
|
executed = self.get_executed_files()
|
|
155
179
|
|
|
156
180
|
return [f for f in all_files if f.name not in executed]
|
|
@@ -201,7 +225,9 @@ class BootstrapManager:
|
|
|
201
225
|
dry_run: bool = False,
|
|
202
226
|
force: bool = False,
|
|
203
227
|
exclude_patch_id: Optional[str] = None,
|
|
204
|
-
up_to_version: Optional[str] = None
|
|
228
|
+
up_to_version: Optional[str] = None,
|
|
229
|
+
exclude_version: Optional[str] = None,
|
|
230
|
+
for_version: Optional[str] = None
|
|
205
231
|
) -> dict:
|
|
206
232
|
"""
|
|
207
233
|
Execute pending bootstrap files.
|
|
@@ -215,6 +241,8 @@ class BootstrapManager:
|
|
|
215
241
|
up_to_version: If provided, only execute files with version <= this version.
|
|
216
242
|
Used during restore_database_from_schema to avoid executing
|
|
217
243
|
bootstraps for future releases.
|
|
244
|
+
exclude_version: If provided, exclude files for exactly this version.
|
|
245
|
+
for_version: If provided, only execute files for exactly this version.
|
|
218
246
|
|
|
219
247
|
Returns:
|
|
220
248
|
Dict with execution results:
|
|
@@ -231,11 +259,11 @@ class BootstrapManager:
|
|
|
231
259
|
}
|
|
232
260
|
|
|
233
261
|
if force:
|
|
234
|
-
files_to_execute = self.get_bootstrap_files(up_to_version)
|
|
262
|
+
files_to_execute = self.get_bootstrap_files(up_to_version, exclude_version=exclude_version, for_version=for_version)
|
|
235
263
|
else:
|
|
236
|
-
files_to_execute = self.get_pending_files(up_to_version)
|
|
264
|
+
files_to_execute = self.get_pending_files(up_to_version, exclude_version=exclude_version, for_version=for_version)
|
|
237
265
|
# Calculate skipped
|
|
238
|
-
all_files = self.get_bootstrap_files(up_to_version)
|
|
266
|
+
all_files = self.get_bootstrap_files(up_to_version, exclude_version=exclude_version, for_version=for_version)
|
|
239
267
|
executed = self.get_executed_files()
|
|
240
268
|
result['skipped'] = [f.name for f in all_files if f.name in executed]
|
|
241
269
|
|
|
@@ -4,6 +4,7 @@ Decorators for half-orm-dev.
|
|
|
4
4
|
Provides common decorators for ReleaseManager and PatchManager.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import signal
|
|
7
8
|
import sys
|
|
8
9
|
import inspect
|
|
9
10
|
from functools import wraps
|
|
@@ -13,9 +14,6 @@ def with_dynamic_branch_lock(branch_getter, timeout_minutes: int = 30):
|
|
|
13
14
|
"""
|
|
14
15
|
Decorator to protect methods with a dynamic branch lock.
|
|
15
16
|
|
|
16
|
-
Unlike with_branch_lock which uses a static branch name, this decorator
|
|
17
|
-
calls a function to determine the branch name at runtime.
|
|
18
|
-
|
|
19
17
|
IMPORTANT: Automatically syncs .hop/ directory to all other active branches
|
|
20
18
|
after the decorated method completes (from locked branch to all others).
|
|
21
19
|
|
|
@@ -77,8 +75,21 @@ def with_dynamic_branch_lock(branch_getter, timeout_minutes: int = 30):
|
|
|
77
75
|
return result
|
|
78
76
|
finally:
|
|
79
77
|
# Always release lock (even on error)
|
|
78
|
+
# Block SIGINT during cleanup to prevent Ctrl+C from
|
|
79
|
+
# interrupting lock release and leaving an orphan tag.
|
|
80
80
|
if lock_tag:
|
|
81
|
-
|
|
81
|
+
interrupted = False
|
|
82
|
+
original_handler = signal.getsignal(signal.SIGINT)
|
|
83
|
+
signal.signal(signal.SIGINT, lambda s, f: setattr(
|
|
84
|
+
wrapper, '_interrupted', True) or None)
|
|
85
|
+
wrapper._interrupted = False
|
|
86
|
+
try:
|
|
87
|
+
self._repo.hgit.release_branch_lock(lock_tag)
|
|
88
|
+
finally:
|
|
89
|
+
interrupted = wrapper._interrupted
|
|
90
|
+
signal.signal(signal.SIGINT, original_handler)
|
|
91
|
+
if interrupted:
|
|
92
|
+
raise KeyboardInterrupt()
|
|
82
93
|
|
|
83
94
|
return wrapper
|
|
84
95
|
return decorator
|
|
@@ -572,9 +572,10 @@ class PatchManager:
|
|
|
572
572
|
|
|
573
573
|
if release_schema_path and release_schema_path.exists():
|
|
574
574
|
# New workflow: restore from release schema (includes all staged patches)
|
|
575
|
-
#
|
|
575
|
+
# Exclude bootstraps for this version — apply_patch_files will
|
|
576
|
+
# execute the current patch's bootstrap below.
|
|
576
577
|
self._repo.restore_database_from_release_schema(
|
|
577
|
-
version,
|
|
578
|
+
version, exclude_bootstrap_version=version
|
|
578
579
|
)
|
|
579
580
|
|
|
580
581
|
# Apply only the current patch
|
|
@@ -583,9 +584,10 @@ class PatchManager:
|
|
|
583
584
|
else:
|
|
584
585
|
# Backward compatibility: old workflow
|
|
585
586
|
# Also generates release schema for migration of existing projects
|
|
586
|
-
#
|
|
587
|
+
# Exclude bootstraps for this version — apply_patch_files will
|
|
588
|
+
# execute them when applying staged patches below.
|
|
587
589
|
self._repo.restore_database_from_schema(
|
|
588
|
-
|
|
590
|
+
exclude_bootstrap_version=version
|
|
589
591
|
)
|
|
590
592
|
|
|
591
593
|
# Get and apply all staged release patches
|
|
@@ -692,11 +694,6 @@ class PatchManager:
|
|
|
692
694
|
|
|
693
695
|
# Apply files in lexicographic order
|
|
694
696
|
for patch_file in structure.files:
|
|
695
|
-
# Skip bootstrap files - they are executed via run_bootstrap() after merge
|
|
696
|
-
if is_bootstrap_file(patch_file.path):
|
|
697
|
-
click.echo(f" • {patch_file.name} (bootstrap - skipped, will run after merge)")
|
|
698
|
-
continue
|
|
699
|
-
|
|
700
697
|
if patch_file.is_sql:
|
|
701
698
|
click.echo(f" • {patch_file.name}")
|
|
702
699
|
try:
|
|
@@ -1061,6 +1058,11 @@ class PatchManager:
|
|
|
1061
1058
|
shutil.copy(file_path, dest)
|
|
1062
1059
|
copied.append(new_name)
|
|
1063
1060
|
|
|
1061
|
+
# Record as already executed — the bootstrap SQL was already
|
|
1062
|
+
# run by apply_patch_files during validation, so run_bootstrap
|
|
1063
|
+
# must skip it to avoid double execution.
|
|
1064
|
+
bootstrap_mgr.record_execution(new_name, version)
|
|
1065
|
+
|
|
1064
1066
|
click.echo(f" • Copied bootstrap file: {new_name}")
|
|
1065
1067
|
|
|
1066
1068
|
return copied
|
|
@@ -2033,11 +2035,15 @@ class PatchManager:
|
|
|
2033
2035
|
# 5. Merge patch branch into release branch
|
|
2034
2036
|
try:
|
|
2035
2037
|
self._repo.hgit.merge(patch_branch, message=f'''[HOP] Merge #{patch_id} into %"{version}"''')
|
|
2036
|
-
except Exception as e:
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
)
|
|
2038
|
+
except (GitCommandError, Exception) as e:
|
|
2039
|
+
# Auto-resolve conflicts in generated files (package dir).
|
|
2040
|
+
# Generated files are regenerated by modules.generate() so
|
|
2041
|
+
# conflicts there can be safely auto-resolved.
|
|
2042
|
+
if not self._auto_resolve_generated_conflicts(e, patch_branch, release_branch):
|
|
2043
|
+
raise PatchManagerError(
|
|
2044
|
+
f"Failed to merge {patch_branch} into {release_branch}: {e}\n"
|
|
2045
|
+
f"You may need to resolve conflicts manually."
|
|
2046
|
+
)
|
|
2041
2047
|
|
|
2042
2048
|
# 5b. Get merge commit hash
|
|
2043
2049
|
merge_commit = self._repo.hgit.last_commit()
|
|
@@ -2212,6 +2218,55 @@ class PatchManager:
|
|
|
2212
2218
|
# Return to original branch
|
|
2213
2219
|
self._repo.hgit.checkout(original_branch)
|
|
2214
2220
|
|
|
2221
|
+
def _auto_resolve_generated_conflicts(
|
|
2222
|
+
self,
|
|
2223
|
+
merge_error: Exception,
|
|
2224
|
+
patch_branch: str,
|
|
2225
|
+
target_branch: str = None
|
|
2226
|
+
) -> bool:
|
|
2227
|
+
"""
|
|
2228
|
+
Auto-resolve merge conflicts when they only affect generated files.
|
|
2229
|
+
|
|
2230
|
+
Generated files (under the package directory) are regenerated by
|
|
2231
|
+
modules.generate() after patch apply, so conflicts there are harmless
|
|
2232
|
+
and can be safely resolved by accepting either side.
|
|
2233
|
+
|
|
2234
|
+
Args:
|
|
2235
|
+
merge_error: The exception from the failed merge
|
|
2236
|
+
patch_branch: The patch branch being merged
|
|
2237
|
+
target_branch: Optional target branch name (for error messages)
|
|
2238
|
+
|
|
2239
|
+
Returns:
|
|
2240
|
+
True if conflicts were auto-resolved, False otherwise
|
|
2241
|
+
"""
|
|
2242
|
+
package_name = self._repo.name
|
|
2243
|
+
git = self._repo.hgit._HGit__git_repo.git
|
|
2244
|
+
|
|
2245
|
+
try:
|
|
2246
|
+
diff_output = git.diff('--name-only', '--diff-filter=U')
|
|
2247
|
+
if not isinstance(diff_output, str) or not diff_output.strip():
|
|
2248
|
+
return False
|
|
2249
|
+
conflicted = diff_output.strip().splitlines()
|
|
2250
|
+
except Exception:
|
|
2251
|
+
return False
|
|
2252
|
+
|
|
2253
|
+
non_generated = [
|
|
2254
|
+
f for f in conflicted
|
|
2255
|
+
if not f.startswith(f"{package_name}/")
|
|
2256
|
+
]
|
|
2257
|
+
|
|
2258
|
+
if non_generated:
|
|
2259
|
+
return False
|
|
2260
|
+
|
|
2261
|
+
# All conflicts are in generated files — auto-resolve
|
|
2262
|
+
# and conclude the interrupted merge commit.
|
|
2263
|
+
click.echo(f" • Auto-resolving conflicts in generated files...")
|
|
2264
|
+
for f in conflicted:
|
|
2265
|
+
git.checkout('--theirs', f)
|
|
2266
|
+
git.add(*conflicted)
|
|
2267
|
+
git.commit('--no-verify', '--no-edit')
|
|
2268
|
+
return True
|
|
2269
|
+
|
|
2215
2270
|
def _validate_patch_before_merge(
|
|
2216
2271
|
self,
|
|
2217
2272
|
patch_id: str,
|
|
@@ -2267,11 +2322,15 @@ class PatchManager:
|
|
|
2267
2322
|
click.echo(f" • Merging {patch_branch} into temp branch...")
|
|
2268
2323
|
try:
|
|
2269
2324
|
self._repo.hgit.merge(patch_branch, message=f"[VALIDATE] Test merge #{patch_id}")
|
|
2270
|
-
except Exception as e:
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
)
|
|
2325
|
+
except (GitCommandError, Exception) as e:
|
|
2326
|
+
# Auto-resolve conflicts in generated files (package dir).
|
|
2327
|
+
# Generated files are regenerated by modules.generate() so
|
|
2328
|
+
# conflicts there can be safely auto-resolved.
|
|
2329
|
+
if not self._auto_resolve_generated_conflicts(e, patch_branch):
|
|
2330
|
+
raise PatchManagerError(
|
|
2331
|
+
f"Failed to merge {patch_branch} during validation: {e}\n"
|
|
2332
|
+
f"Please resolve conflicts before closing the patch."
|
|
2333
|
+
)
|
|
2275
2334
|
|
|
2276
2335
|
# 3. Run patch apply and verify no modifications
|
|
2277
2336
|
click.echo(f" • Running patch apply to verify idempotency...")
|
|
@@ -2308,18 +2367,31 @@ class PatchManager:
|
|
|
2308
2367
|
# Generate modules
|
|
2309
2368
|
modules.generate(self._repo)
|
|
2310
2369
|
|
|
2311
|
-
#
|
|
2370
|
+
# Stage generated files (package dir) — they are always
|
|
2371
|
+
# regenerated from DB state so changes there are expected.
|
|
2372
|
+
package_name = self._repo.name
|
|
2373
|
+
package_dir = Path(self._repo.base_dir) / package_name
|
|
2374
|
+
if package_dir.exists():
|
|
2375
|
+
self._repo.hgit.add(str(package_dir))
|
|
2376
|
+
|
|
2377
|
+
# Check if any NON-generated files were modified
|
|
2312
2378
|
if not self._repo.hgit.repos_is_clean():
|
|
2313
2379
|
modified_files = self._repo.hgit.get_modified_files()
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
f
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2380
|
+
# Filter out generated files (already staged)
|
|
2381
|
+
non_generated = [
|
|
2382
|
+
f for f in modified_files
|
|
2383
|
+
if not f.startswith(f"{package_name}/")
|
|
2384
|
+
]
|
|
2385
|
+
if non_generated:
|
|
2386
|
+
raise PatchManagerError(
|
|
2387
|
+
f"Patch validation failed: patch apply modified files!\n"
|
|
2388
|
+
f"This indicates the patch is not idempotent or schema is out of sync.\n\n"
|
|
2389
|
+
f"Modified files:\n" + "\n".join(f" • {f}" for f in non_generated) + "\n\n"
|
|
2390
|
+
f"Actions required:\n"
|
|
2391
|
+
f" 1. Verify patch SQL is idempotent (uses CREATE IF NOT EXISTS, etc.)\n"
|
|
2392
|
+
f" 2. Ensure schema.sql is up to date with all previous patches\n"
|
|
2393
|
+
f" 3. Run 'half_orm dev patch apply' on your patch branch to test"
|
|
2394
|
+
)
|
|
2323
2395
|
|
|
2324
2396
|
click.echo(f" • {utils.Color.green('✓')} Patch apply succeeded with no modifications")
|
|
2325
2397
|
|
|
@@ -2361,6 +2433,15 @@ class PatchManager:
|
|
|
2361
2433
|
finally:
|
|
2362
2434
|
# 6. Cleanup: Delete temp branch and return to original branch
|
|
2363
2435
|
try:
|
|
2436
|
+
# Discard any staged/unstaged changes in the generated package
|
|
2437
|
+
# dir so checkout doesn't fail (they were staged during validation).
|
|
2438
|
+
package_name = self._repo.name
|
|
2439
|
+
try:
|
|
2440
|
+
self._repo.hgit._HGit__git_repo.git.reset('HEAD', '--', f'{package_name}/')
|
|
2441
|
+
self._repo.hgit._HGit__git_repo.git.checkout('--', f'{package_name}/')
|
|
2442
|
+
except Exception:
|
|
2443
|
+
pass
|
|
2444
|
+
|
|
2364
2445
|
# Return to original branch
|
|
2365
2446
|
if self._repo.hgit.branch != original_branch:
|
|
2366
2447
|
self._repo.hgit.checkout(original_branch)
|
|
@@ -733,7 +733,7 @@ class ReleaseManager:
|
|
|
733
733
|
|
|
734
734
|
return patches
|
|
735
735
|
|
|
736
|
-
def _apply_release_patches(self, version: str, hotfix=False, force_apply=False) -> None:
|
|
736
|
+
def _apply_release_patches(self, version: str, hotfix=False, force_apply=False, exclude_bootstrap_version: str = None) -> None:
|
|
737
737
|
"""
|
|
738
738
|
Apply all patches for a release version to the database.
|
|
739
739
|
|
|
@@ -751,6 +751,8 @@ class ReleaseManager:
|
|
|
751
751
|
hotfix: If True, skip RC patches (hotfix workflow)
|
|
752
752
|
force_apply: If True, always apply patches individually even if
|
|
753
753
|
release schema exists (used for production validation)
|
|
754
|
+
exclude_bootstrap_version: If provided, skip bootstrap scripts for
|
|
755
|
+
this version during restore (they will be run after patches).
|
|
754
756
|
|
|
755
757
|
Raises:
|
|
756
758
|
ReleaseManagerError: If patch application fails
|
|
@@ -759,11 +761,15 @@ class ReleaseManager:
|
|
|
759
761
|
release_schema_path = self._repo.get_release_schema_path(version)
|
|
760
762
|
if release_schema_path.exists() and not force_apply:
|
|
761
763
|
# New workflow: restore from release schema (already contains all staged patches)
|
|
762
|
-
self._repo.restore_database_from_release_schema(
|
|
764
|
+
self._repo.restore_database_from_release_schema(
|
|
765
|
+
version, exclude_bootstrap_version=exclude_bootstrap_version
|
|
766
|
+
)
|
|
763
767
|
return
|
|
764
768
|
|
|
765
769
|
# Fallback: old workflow - restore database from baseline
|
|
766
|
-
self._repo.restore_database_from_schema(
|
|
770
|
+
self._repo.restore_database_from_schema(
|
|
771
|
+
exclude_bootstrap_version=exclude_bootstrap_version
|
|
772
|
+
)
|
|
767
773
|
|
|
768
774
|
current_branch = self._repo.hgit.branch
|
|
769
775
|
|
|
@@ -821,7 +827,7 @@ class ReleaseManager:
|
|
|
821
827
|
# Return to original branch
|
|
822
828
|
self._repo.hgit.checkout(current_branch)
|
|
823
829
|
|
|
824
|
-
def _run_bootstrap_scripts(self, up_to_version: str = None) -> None:
|
|
830
|
+
def _run_bootstrap_scripts(self, up_to_version: str = None, for_version: str = None) -> None:
|
|
825
831
|
"""
|
|
826
832
|
Execute pending bootstrap scripts after patch application.
|
|
827
833
|
|
|
@@ -830,7 +836,7 @@ class ReleaseManager:
|
|
|
830
836
|
|
|
831
837
|
Args:
|
|
832
838
|
up_to_version: If provided, only execute bootstraps for versions <= this.
|
|
833
|
-
|
|
839
|
+
for_version: If provided, only execute bootstraps for exactly this version.
|
|
834
840
|
|
|
835
841
|
Raises:
|
|
836
842
|
ReleaseManagerError: If bootstrap execution fails
|
|
@@ -844,14 +850,14 @@ class ReleaseManager:
|
|
|
844
850
|
return
|
|
845
851
|
|
|
846
852
|
# Get pending files (filtered by version if specified)
|
|
847
|
-
pending = bootstrap_mgr.get_pending_files(up_to_version)
|
|
853
|
+
pending = bootstrap_mgr.get_pending_files(up_to_version, for_version=for_version)
|
|
848
854
|
if not pending:
|
|
849
855
|
return
|
|
850
856
|
|
|
851
857
|
print(f"\n📦 Executing {len(pending)} bootstrap script(s)...")
|
|
852
858
|
|
|
853
859
|
try:
|
|
854
|
-
result = bootstrap_mgr.run_bootstrap(up_to_version=up_to_version)
|
|
860
|
+
result = bootstrap_mgr.run_bootstrap(up_to_version=up_to_version, for_version=for_version)
|
|
855
861
|
|
|
856
862
|
if result['errors']:
|
|
857
863
|
errors = result['errors']
|
|
@@ -896,82 +902,6 @@ class ReleaseManager:
|
|
|
896
902
|
|
|
897
903
|
return all_patches
|
|
898
904
|
|
|
899
|
-
def _generate_data_sql_file(self, patch_list: List[str], version: str) -> Optional[Path]:
|
|
900
|
-
"""
|
|
901
|
-
Generate model/data-X.Y.Z.sql file from patches with @HOP:data annotation.
|
|
902
|
-
|
|
903
|
-
Collects all SQL files marked with `-- @HOP:data` from the patch list
|
|
904
|
-
and concatenates them into a single data SQL file for from-scratch
|
|
905
|
-
installations (clone, restore_database_from_schema).
|
|
906
|
-
|
|
907
|
-
This file is only generated for production releases. RC and hotfix
|
|
908
|
-
versions don't need this file because:
|
|
909
|
-
- In production upgrades, data is inserted by patch application
|
|
910
|
-
- This file is only for from-scratch installations
|
|
911
|
-
|
|
912
|
-
Args:
|
|
913
|
-
patch_list: List of patch IDs to process
|
|
914
|
-
version: Version string (e.g., "0.17.0")
|
|
915
|
-
|
|
916
|
-
Returns:
|
|
917
|
-
Path to generated file (model/data-X.Y.Z.sql), or None if no data files found
|
|
918
|
-
|
|
919
|
-
Examples:
|
|
920
|
-
self._generate_data_sql_file(
|
|
921
|
-
["456-auth", "457-roles"],
|
|
922
|
-
"0.17.0"
|
|
923
|
-
)
|
|
924
|
-
# Generates model/data-0.17.0.sql with data from both patches
|
|
925
|
-
"""
|
|
926
|
-
if not patch_list:
|
|
927
|
-
return None
|
|
928
|
-
|
|
929
|
-
try:
|
|
930
|
-
# Collect all data files from patches
|
|
931
|
-
data_files = self._repo.patch_manager._collect_data_files_from_patches(patch_list)
|
|
932
|
-
|
|
933
|
-
if not data_files:
|
|
934
|
-
# No data files found - skip generation
|
|
935
|
-
return None
|
|
936
|
-
|
|
937
|
-
# Generate output file in model/ directory
|
|
938
|
-
output_filename = f"data-{version}.sql"
|
|
939
|
-
output_path = Path(self._repo.model_dir) / output_filename
|
|
940
|
-
|
|
941
|
-
with output_path.open('w', encoding='utf-8') as out_file:
|
|
942
|
-
# Write header
|
|
943
|
-
out_file.write(f"-- Data file for version {version}\n")
|
|
944
|
-
out_file.write(f"-- Generated from patches: {', '.join(patch_list)}\n")
|
|
945
|
-
out_file.write(f"-- This file contains reference data (DML) for from-scratch installations\n")
|
|
946
|
-
out_file.write(f"--\n")
|
|
947
|
-
out_file.write(f"-- Usage: Automatically loaded by restore_database_from_schema()\n")
|
|
948
|
-
out_file.write(f"--\n\n")
|
|
949
|
-
|
|
950
|
-
# Concatenate all data files
|
|
951
|
-
for data_file in data_files:
|
|
952
|
-
# Write separator comment
|
|
953
|
-
out_file.write(f"-- ========================================\n")
|
|
954
|
-
out_file.write(f"-- Source: {data_file}\n")
|
|
955
|
-
out_file.write(f"-- ========================================\n\n")
|
|
956
|
-
|
|
957
|
-
# Write file content (skip first line which is -- @HOP:data)
|
|
958
|
-
content = data_file.read_text(encoding='utf-8')
|
|
959
|
-
lines = content.split('\n')
|
|
960
|
-
|
|
961
|
-
# Skip first line if it's the annotation
|
|
962
|
-
if lines and lines[0].strip() == "-- @HOP:data":
|
|
963
|
-
lines = lines[1:]
|
|
964
|
-
|
|
965
|
-
out_file.write('\n'.join(lines))
|
|
966
|
-
out_file.write('\n\n')
|
|
967
|
-
|
|
968
|
-
return output_path
|
|
969
|
-
|
|
970
|
-
except Exception as e:
|
|
971
|
-
raise ReleaseManagerError(
|
|
972
|
-
f"Failed to generate data SQL file data-{version}.sql: {e}"
|
|
973
|
-
)
|
|
974
|
-
|
|
975
905
|
def get_all_release_context_patches(self) -> List[str]:
|
|
976
906
|
"""
|
|
977
907
|
Get all validated patches for the next release context.
|
|
@@ -2569,12 +2499,14 @@ class ReleaseManager:
|
|
|
2569
2499
|
self._repo.hgit.checkout("-b", temp_branch)
|
|
2570
2500
|
|
|
2571
2501
|
# 6. Apply patches to database (validation)
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
#
|
|
2575
|
-
self.
|
|
2502
|
+
# Bootstrap SQL in patches is executed by apply_patch_files.
|
|
2503
|
+
# Bootstraps from bootstrap/ for OTHER versions run during restore.
|
|
2504
|
+
# Bootstraps for THIS version are excluded (already run via patches).
|
|
2505
|
+
self._apply_release_patches(
|
|
2506
|
+
version, force_apply=is_prod, exclude_bootstrap_version=version
|
|
2507
|
+
)
|
|
2576
2508
|
|
|
2577
|
-
#
|
|
2509
|
+
# 7. Register version in database
|
|
2578
2510
|
version_parts = version.split('.')
|
|
2579
2511
|
major, minor, patch_num = map(int, version_parts)
|
|
2580
2512
|
if is_prod:
|
|
@@ -2673,7 +2605,7 @@ class ReleaseManager:
|
|
|
2673
2605
|
|
|
2674
2606
|
# Delete temporary branch if it exists
|
|
2675
2607
|
try:
|
|
2676
|
-
self._repo.hgit.delete_local_branch(temp_branch
|
|
2608
|
+
self._repo.hgit.delete_local_branch(temp_branch)
|
|
2677
2609
|
print(f" Deleted temporary branch {temp_branch}")
|
|
2678
2610
|
except Exception:
|
|
2679
2611
|
pass # Branch might not exist
|
|
@@ -2781,12 +2713,6 @@ class ReleaseManager:
|
|
|
2781
2713
|
if release_schema_file.exists():
|
|
2782
2714
|
release_schema_file.unlink()
|
|
2783
2715
|
|
|
2784
|
-
# Generate data file
|
|
2785
|
-
prod_patches = self.read_release_patches(prod_file.name)
|
|
2786
|
-
data_file = self._generate_data_sql_file(prod_patches, version)
|
|
2787
|
-
if data_file:
|
|
2788
|
-
self._repo.hgit.add(str(data_file))
|
|
2789
|
-
|
|
2790
2716
|
def _cleanup_release_branch(self, release_branch: str) -> list:
|
|
2791
2717
|
"""Delete release branch after production promotion."""
|
|
2792
2718
|
deleted_branches = []
|
|
@@ -3226,13 +3152,6 @@ class ReleaseManager:
|
|
|
3226
3152
|
if toml_file.exists():
|
|
3227
3153
|
self._repo.hgit.rm(str(toml_file))
|
|
3228
3154
|
|
|
3229
|
-
# Regenerate model/data-X.Y.Z.sql with all patches (original release + all hotfixes)
|
|
3230
|
-
# This ensures from-scratch installations get all data
|
|
3231
|
-
all_patches = self._collect_all_version_patches(version)
|
|
3232
|
-
data_file = self._generate_data_sql_file(all_patches, version)
|
|
3233
|
-
if data_file:
|
|
3234
|
-
self._repo.hgit.add(str(data_file))
|
|
3235
|
-
|
|
3236
3155
|
# 6. Apply release patches and generate SQL dumps
|
|
3237
3156
|
self._apply_release_patches(version, True)
|
|
3238
3157
|
|
|
@@ -652,6 +652,24 @@ class Repo:
|
|
|
652
652
|
# Checkout to target branch
|
|
653
653
|
self.hgit.checkout(branch)
|
|
654
654
|
|
|
655
|
+
# Reset to origin (source of truth) before syncing .hop/
|
|
656
|
+
# This avoids non-fast-forward push failures when another
|
|
657
|
+
# actor has already synced this branch.
|
|
658
|
+
remote_ref = f"origin/{branch}"
|
|
659
|
+
try:
|
|
660
|
+
synced, status = self.hgit.compare_with_remote(branch)
|
|
661
|
+
if not synced and status == "diverged":
|
|
662
|
+
print(
|
|
663
|
+
f"Warning: branch {branch} has diverged from origin. "
|
|
664
|
+
f"Resetting to origin (source of truth).",
|
|
665
|
+
file=sys.stderr
|
|
666
|
+
)
|
|
667
|
+
if not synced:
|
|
668
|
+
self.hgit._HGit__git_repo.git.reset('--hard', remote_ref)
|
|
669
|
+
except Exception:
|
|
670
|
+
# Remote branch may not exist yet, continue without reset
|
|
671
|
+
pass
|
|
672
|
+
|
|
655
673
|
# Reload config for this branch
|
|
656
674
|
self.__config = Config(self.base_dir)
|
|
657
675
|
|
|
@@ -2304,7 +2322,7 @@ Each script is executed only once unless `--force` is used.
|
|
|
2304
2322
|
self.model.execute_query('CREATE SCHEMA public')
|
|
2305
2323
|
self.model.execute_query('GRANT ALL ON SCHEMA public TO public')
|
|
2306
2324
|
|
|
2307
|
-
def restore_database_from_schema(self, exclude_bootstrap_patch_id: Optional[str] = None) -> None:
|
|
2325
|
+
def restore_database_from_schema(self, exclude_bootstrap_patch_id: Optional[str] = None, exclude_bootstrap_version: Optional[str] = None) -> None:
|
|
2308
2326
|
"""
|
|
2309
2327
|
Restore database from model/schema.sql, metadata, and data files.
|
|
2310
2328
|
|
|
@@ -2417,13 +2435,14 @@ Each script is executed only once unless `--force` is used.
|
|
|
2417
2435
|
self.model.reconnect(reload=True)
|
|
2418
2436
|
|
|
2419
2437
|
# 7. Execute bootstrap scripts
|
|
2420
|
-
#
|
|
2421
|
-
#
|
|
2422
|
-
#
|
|
2438
|
+
# In patch apply context: ALL bootstraps run (only current patch excluded).
|
|
2439
|
+
# In promote context: exclude_bootstrap_version skips the version being
|
|
2440
|
+
# promoted (its bootstraps run after patches are applied).
|
|
2423
2441
|
from half_orm_dev.bootstrap_manager import BootstrapManager
|
|
2424
2442
|
bootstrap_mgr = BootstrapManager(self)
|
|
2425
2443
|
bootstrap_mgr.run_bootstrap(
|
|
2426
|
-
exclude_patch_id=exclude_bootstrap_patch_id
|
|
2444
|
+
exclude_patch_id=exclude_bootstrap_patch_id,
|
|
2445
|
+
exclude_version=exclude_bootstrap_version
|
|
2427
2446
|
)
|
|
2428
2447
|
|
|
2429
2448
|
except RepoError:
|
|
@@ -2563,7 +2582,8 @@ Each script is executed only once unless `--force` is used.
|
|
|
2563
2582
|
def restore_database_from_release_schema(
|
|
2564
2583
|
self,
|
|
2565
2584
|
version: str,
|
|
2566
|
-
exclude_bootstrap_patch_id: Optional[str] = None
|
|
2585
|
+
exclude_bootstrap_patch_id: Optional[str] = None,
|
|
2586
|
+
exclude_bootstrap_version: Optional[str] = None
|
|
2567
2587
|
) -> None:
|
|
2568
2588
|
"""
|
|
2569
2589
|
Restore database from release schema file.
|
|
@@ -2578,6 +2598,8 @@ Each script is executed only once unless `--force` is used.
|
|
|
2578
2598
|
version: Release version string (e.g., "0.17.1")
|
|
2579
2599
|
exclude_bootstrap_patch_id: If provided, skip bootstrap files
|
|
2580
2600
|
belonging to this patch (used during patch apply)
|
|
2601
|
+
exclude_bootstrap_version: If provided, skip bootstrap files for
|
|
2602
|
+
this version (used during promote)
|
|
2581
2603
|
|
|
2582
2604
|
Raises:
|
|
2583
2605
|
RepoError: If restoration fails
|
|
@@ -2591,7 +2613,10 @@ Each script is executed only once unless `--force` is used.
|
|
|
2591
2613
|
|
|
2592
2614
|
# Fallback to production schema if release schema doesn't exist
|
|
2593
2615
|
if not release_schema_path.exists():
|
|
2594
|
-
self.restore_database_from_schema(
|
|
2616
|
+
self.restore_database_from_schema(
|
|
2617
|
+
exclude_bootstrap_patch_id,
|
|
2618
|
+
exclude_bootstrap_version=exclude_bootstrap_version
|
|
2619
|
+
)
|
|
2595
2620
|
return
|
|
2596
2621
|
|
|
2597
2622
|
try:
|
|
@@ -2609,7 +2634,10 @@ Each script is executed only once unless `--force` is used.
|
|
|
2609
2634
|
# Execute bootstrap scripts
|
|
2610
2635
|
from half_orm_dev.bootstrap_manager import BootstrapManager
|
|
2611
2636
|
bootstrap_mgr = BootstrapManager(self)
|
|
2612
|
-
bootstrap_mgr.run_bootstrap(
|
|
2637
|
+
bootstrap_mgr.run_bootstrap(
|
|
2638
|
+
exclude_patch_id=exclude_bootstrap_patch_id,
|
|
2639
|
+
exclude_version=exclude_bootstrap_version
|
|
2640
|
+
)
|
|
2613
2641
|
|
|
2614
2642
|
except Exception as e:
|
|
2615
2643
|
raise RepoError(f"Failed to restore from release schema: {e}") from e
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.17.5-a5
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.17.5-a3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migrations/0/17/1/00_move_to_hop.py
RENAMED
|
File without changes
|
{half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/migrations/0/17/1/01_txt_to_toml.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{half_orm_dev-0.17.5a3 → half_orm_dev-0.17.5a5}/half_orm_dev/templates/git-hooks/prepare-commit-msg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|