half-orm-dev 0.17.0a1__tar.gz → 0.17.0a2__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.0a1/half_orm_dev.egg-info → half_orm_dev-0.17.0a2}/PKG-INFO +2 -2
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/README.md +1 -1
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/__init__.py +3 -0
- half_orm_dev-0.17.0a2/half_orm_dev/cli/commands/check.py +221 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/patch.py +0 -1
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/release.py +27 -33
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/main.py +3 -3
- half_orm_dev-0.17.0a2/half_orm_dev/decorators.py +49 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/hgit.py +468 -3
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/patch_manager.py +33 -30
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/patch_validator.py +3 -3
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/release_manager.py +1112 -1262
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/repo.py +223 -5
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/conftest_template +3 -8
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/utils.py +13 -0
- half_orm_dev-0.17.0a2/half_orm_dev/version.txt +1 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2/half_orm_dev.egg-info}/PKG-INFO +2 -2
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev.egg-info/SOURCES.txt +2 -4
- half_orm_dev-0.17.0a1/half_orm_dev/cli/commands/prepare.py +0 -21
- half_orm_dev-0.17.0a1/half_orm_dev/cli/commands/prepare_release.py +0 -119
- half_orm_dev-0.17.0a1/half_orm_dev/cli/commands/promote_to.py +0 -127
- half_orm_dev-0.17.0a1/half_orm_dev/templates/pre-commit +0 -59
- half_orm_dev-0.17.0a1/half_orm_dev/version.txt +0 -1
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/AUTHORS +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/LICENSE +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/__init__.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/__init__.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/apply.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/clone.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/init.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/new.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/restore.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/sync.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/todo.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/undo.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/update.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli/commands/upgrade.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/cli_extension.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/database.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/hop.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/manifest.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/modules.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/patch.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/patches/log +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/patches/sql/half_orm_meta.sql +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/.gitignore +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/MANIFEST.in +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/Pipfile +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/README +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/init_module_template +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/module_template_1 +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/module_template_2 +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/module_template_3 +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/relation_test +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/setup.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/sql_adapter +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev/templates/warning +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev.egg-info/dependency_links.txt +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev.egg-info/requires.txt +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/half_orm_dev.egg-info/top_level.txt +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/setup.cfg +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/setup.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/tests/__init__.py +0 -0
- {half_orm_dev-0.17.0a1 → half_orm_dev-0.17.0a2}/tests/conftest.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: half_orm_dev
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.0a2
|
|
4
4
|
Summary: half_orm development Framework.
|
|
5
5
|
Home-page: https://github.com/collorg/halfORM_dev
|
|
6
6
|
Author: Joël Maïzi
|
|
@@ -928,7 +928,7 @@ This project is licensed under the GNU General Public License v3.0 - see the [LI
|
|
|
928
928
|
---
|
|
929
929
|
|
|
930
930
|
**Version**: 0.17.0
|
|
931
|
-
**halfORM**: Compatible with halfORM 0.
|
|
931
|
+
**halfORM**: Compatible with halfORM 0.17.x
|
|
932
932
|
**Python**: 3.8+
|
|
933
933
|
**PostgreSQL**: Tested with 13+ (might work with earlier versions)
|
|
934
934
|
|
|
@@ -888,7 +888,7 @@ This project is licensed under the GNU General Public License v3.0 - see the [LI
|
|
|
888
888
|
---
|
|
889
889
|
|
|
890
890
|
**Version**: 0.17.0
|
|
891
|
-
**halfORM**: Compatible with halfORM 0.
|
|
891
|
+
**halfORM**: Compatible with halfORM 0.17.x
|
|
892
892
|
**Python**: 3.8+
|
|
893
893
|
**PostgreSQL**: Tested with 13+ (might work with earlier versions)
|
|
894
894
|
|
|
@@ -12,6 +12,7 @@ from .patch import patch
|
|
|
12
12
|
from .release import release
|
|
13
13
|
from .update import update
|
|
14
14
|
from .upgrade import upgrade
|
|
15
|
+
from .check import check
|
|
15
16
|
from .todo import apply_release
|
|
16
17
|
from .todo import create_hotfix
|
|
17
18
|
from .todo import rollback
|
|
@@ -29,6 +30,7 @@ ALL_COMMANDS = {
|
|
|
29
30
|
'release': release,
|
|
30
31
|
'update': update, # Adapted for production
|
|
31
32
|
'upgrade': upgrade, # Adapted for production
|
|
33
|
+
'check': check, # Project health check and updates
|
|
32
34
|
# 🚧 (stubs)
|
|
33
35
|
'apply_release': apply_release,
|
|
34
36
|
|
|
@@ -48,6 +50,7 @@ __all__ = [
|
|
|
48
50
|
'patch',
|
|
49
51
|
'release',
|
|
50
52
|
'upgrade',
|
|
53
|
+
'check',
|
|
51
54
|
'create_hotfix',
|
|
52
55
|
'rollback',
|
|
53
56
|
# Adapted commands
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Check command - Verify and update project configuration.
|
|
3
|
+
|
|
4
|
+
Checks project health and updates components as needed:
|
|
5
|
+
- Git hooks (pre-commit)
|
|
6
|
+
- Configuration files
|
|
7
|
+
- Template files
|
|
8
|
+
- Clean up stale branches
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
from half_orm_dev.repo import Repo
|
|
13
|
+
from half_orm import utils
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command()
|
|
17
|
+
@click.option(
|
|
18
|
+
'--prune-branches', '-p',
|
|
19
|
+
is_flag=True,
|
|
20
|
+
help='Also clean up local branches that no longer exist on remote'
|
|
21
|
+
)
|
|
22
|
+
@click.option(
|
|
23
|
+
'--dry-run',
|
|
24
|
+
is_flag=True,
|
|
25
|
+
help='Show what would be done without making changes'
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
'--verbose', '-v',
|
|
29
|
+
is_flag=True,
|
|
30
|
+
help='Show detailed information'
|
|
31
|
+
)
|
|
32
|
+
def check(prune_branches: bool, dry_run: bool, verbose: bool) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Verify and update project configuration.
|
|
35
|
+
|
|
36
|
+
Checks project health and updates components as needed. This command
|
|
37
|
+
is also run automatically at the start of other commands.
|
|
38
|
+
|
|
39
|
+
Checks performed:
|
|
40
|
+
• Git hooks are up to date (pre-commit)
|
|
41
|
+
• Repository is properly configured
|
|
42
|
+
• Optionally: Clean up stale local branches
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
# Basic check and update
|
|
46
|
+
half_orm dev check
|
|
47
|
+
|
|
48
|
+
# Check and clean up stale branches
|
|
49
|
+
half_orm dev check --prune-branches
|
|
50
|
+
|
|
51
|
+
# Preview what would be done
|
|
52
|
+
half_orm dev check --dry-run
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
repo = Repo()
|
|
56
|
+
|
|
57
|
+
# Perform check (delegates to Repo)
|
|
58
|
+
result = repo.check_and_update(
|
|
59
|
+
prune_branches=prune_branches,
|
|
60
|
+
dry_run=dry_run,
|
|
61
|
+
silent=False # Show messages
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Display results
|
|
65
|
+
_display_check_results(result, dry_run, prune_branches, verbose)
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
click.echo(utils.Color.red(f"❌ Error: {e}"), err=True)
|
|
69
|
+
if verbose:
|
|
70
|
+
import traceback
|
|
71
|
+
traceback.print_exc()
|
|
72
|
+
raise click.Abort()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _display_check_results(result: dict, dry_run: bool, prune_branches: bool, verbose: bool):
|
|
76
|
+
"""Display check results to user."""
|
|
77
|
+
# Hooks
|
|
78
|
+
hooks = result.get('hooks', {})
|
|
79
|
+
if hooks.get('installed'):
|
|
80
|
+
if hooks['action'] == 'updated':
|
|
81
|
+
click.echo(f"✓ {utils.Color.green('Pre-commit hook updated')}")
|
|
82
|
+
elif hooks['action'] == 'installed':
|
|
83
|
+
click.echo(f"✓ {utils.Color.green('Pre-commit hook installed')}")
|
|
84
|
+
elif verbose:
|
|
85
|
+
click.echo(f"✓ {utils.Color.green('Pre-commit hook up to date')}")
|
|
86
|
+
|
|
87
|
+
# Active branches
|
|
88
|
+
active = result.get('active_branches', {})
|
|
89
|
+
patch_branches = active.get('patch_branches', [])
|
|
90
|
+
release_branches = active.get('release_branches', [])
|
|
91
|
+
|
|
92
|
+
# Show current branch
|
|
93
|
+
current = active.get('current_branch')
|
|
94
|
+
if current:
|
|
95
|
+
click.echo(f"\n📍 {utils.Color.bold('Current branch:')} {current}")
|
|
96
|
+
|
|
97
|
+
# Show patch branches
|
|
98
|
+
if patch_branches:
|
|
99
|
+
click.echo(f"\n🔧 {utils.Color.bold('Patch branches')} ({len(patch_branches)}):")
|
|
100
|
+
for branch_info in patch_branches:
|
|
101
|
+
_display_branch_info(branch_info, verbose)
|
|
102
|
+
elif verbose:
|
|
103
|
+
click.echo(f"\n🔧 {utils.Color.bold('Patch branches:')} None")
|
|
104
|
+
|
|
105
|
+
# Show patch branches in stage release (grouped by version)
|
|
106
|
+
active_release = [b for b in release_branches if b.get('in_stage_file', False)]
|
|
107
|
+
if active_release:
|
|
108
|
+
click.echo(f"\n📦 {utils.Color.bold('Patch branches in stage release:')}")
|
|
109
|
+
_display_release_branches_grouped(active_release, verbose)
|
|
110
|
+
elif verbose:
|
|
111
|
+
click.echo(f"\n📦 {utils.Color.bold('Patch branches in stage release:')} None")
|
|
112
|
+
|
|
113
|
+
# Show stale release branches (exist locally but not in stage)
|
|
114
|
+
stale_release = [b for b in release_branches if not b.get('in_stage_file', False)]
|
|
115
|
+
if stale_release and verbose:
|
|
116
|
+
click.echo(f"\n⚠️ {utils.Color.blue('Stale release branches')} ({len(stale_release)}):")
|
|
117
|
+
for branch_info in stale_release[:5]:
|
|
118
|
+
click.echo(f" • {branch_info['name']}")
|
|
119
|
+
if not branch_info['exists_on_remote']:
|
|
120
|
+
click.echo(f" {utils.Color.red('⚠ Not on remote - can be deleted')}")
|
|
121
|
+
if len(stale_release) > 5:
|
|
122
|
+
click.echo(f" ... and {len(stale_release) - 5} more")
|
|
123
|
+
|
|
124
|
+
# Prune results
|
|
125
|
+
if prune_branches:
|
|
126
|
+
branches = result.get('branches', {})
|
|
127
|
+
deleted = branches.get('deleted', [])
|
|
128
|
+
|
|
129
|
+
if deleted:
|
|
130
|
+
click.echo()
|
|
131
|
+
if dry_run:
|
|
132
|
+
click.echo(f"○ {utils.Color.blue(f'Would delete {len(deleted)} stale branch(es)')}")
|
|
133
|
+
else:
|
|
134
|
+
click.echo(f"✓ {utils.Color.green(f'Deleted {len(deleted)} stale branch(es)')}")
|
|
135
|
+
|
|
136
|
+
if verbose:
|
|
137
|
+
for branch in deleted[:10]:
|
|
138
|
+
symbol = "○" if dry_run else "✓"
|
|
139
|
+
click.echo(f" {symbol} {branch}")
|
|
140
|
+
if len(deleted) > 10:
|
|
141
|
+
click.echo(f" ... and {len(deleted) - 10} more")
|
|
142
|
+
|
|
143
|
+
if branches.get('errors'):
|
|
144
|
+
click.echo(f"⚠ {utils.Color.red('Some errors occurred during cleanup')}")
|
|
145
|
+
if verbose:
|
|
146
|
+
for branch, error in branches['errors'][:3]:
|
|
147
|
+
click.echo(f" {branch}: {error}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _display_release_branches_grouped(branches: list, verbose: bool):
|
|
151
|
+
"""Display release branches grouped by version and sorted by order."""
|
|
152
|
+
from collections import defaultdict
|
|
153
|
+
|
|
154
|
+
# Group branches by version
|
|
155
|
+
by_version = defaultdict(list)
|
|
156
|
+
for branch_info in branches:
|
|
157
|
+
name = branch_info['name']
|
|
158
|
+
# Extract version from ho-release/{version}/{patch_id}
|
|
159
|
+
parts = name.split('/')
|
|
160
|
+
if len(parts) >= 3 and parts[0] == 'ho-release':
|
|
161
|
+
version = parts[1]
|
|
162
|
+
patch_id = '/'.join(parts[2:]) # Handle patch IDs with slashes
|
|
163
|
+
by_version[version].append((patch_id, branch_info))
|
|
164
|
+
|
|
165
|
+
# Display each version group
|
|
166
|
+
for version in sorted(by_version.keys()):
|
|
167
|
+
patches = by_version[version]
|
|
168
|
+
|
|
169
|
+
# Sort patches by their order in the stage file
|
|
170
|
+
patches_sorted = sorted(patches, key=lambda x: x[1].get('order', 999))
|
|
171
|
+
|
|
172
|
+
click.echo(f"\n {utils.Color.bold(f'Release {version}')} ({len(patches)} patch{'es' if len(patches) > 1 else ''}):")
|
|
173
|
+
for patch_id, branch_info in patches_sorted:
|
|
174
|
+
_display_branch_info(branch_info, verbose, indent=" ", show_patch_id_only=True)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _display_branch_info(branch_info: dict, verbose: bool, indent: str = " ", show_patch_id_only: bool = False):
|
|
178
|
+
"""Display information about a single branch.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
branch_info: Branch information dict
|
|
182
|
+
verbose: Show verbose output
|
|
183
|
+
indent: Indentation prefix
|
|
184
|
+
show_patch_id_only: If True, show only patch_id instead of full branch name
|
|
185
|
+
"""
|
|
186
|
+
name = branch_info['name']
|
|
187
|
+
is_current = branch_info.get('is_current', False)
|
|
188
|
+
exists_on_remote = branch_info.get('exists_on_remote', False)
|
|
189
|
+
sync_status = branch_info.get('sync_status', 'unknown')
|
|
190
|
+
ahead = branch_info.get('ahead', 0)
|
|
191
|
+
behind = branch_info.get('behind', 0)
|
|
192
|
+
|
|
193
|
+
# Extract display name
|
|
194
|
+
if show_patch_id_only:
|
|
195
|
+
# Extract patch_id from ho-release/{version}/{patch_id}
|
|
196
|
+
parts = name.split('/')
|
|
197
|
+
if len(parts) >= 3:
|
|
198
|
+
display_name = '/'.join(parts[2:])
|
|
199
|
+
else:
|
|
200
|
+
display_name = name
|
|
201
|
+
else:
|
|
202
|
+
display_name = name
|
|
203
|
+
|
|
204
|
+
# Symbol for current branch
|
|
205
|
+
marker = "→ " if is_current else ""
|
|
206
|
+
|
|
207
|
+
# Status symbol and text
|
|
208
|
+
if not exists_on_remote:
|
|
209
|
+
status = utils.Color.red("⚠ no remote")
|
|
210
|
+
elif sync_status == 'synced':
|
|
211
|
+
status = utils.Color.green("✓ synced")
|
|
212
|
+
elif sync_status == 'ahead':
|
|
213
|
+
status = utils.Color.blue(f"↑ {ahead} ahead")
|
|
214
|
+
elif sync_status == 'behind':
|
|
215
|
+
status = utils.Color.blue(f"↓ {behind} behind")
|
|
216
|
+
elif sync_status == 'diverged':
|
|
217
|
+
status = utils.Color.red(f"⚠ diverged (↑{ahead} ↓{behind})")
|
|
218
|
+
else:
|
|
219
|
+
status = "?"
|
|
220
|
+
|
|
221
|
+
click.echo(f"{indent}{marker}• {display_name} - {status}")
|
|
@@ -301,7 +301,6 @@ def patch_add(patch_id: str, to_version: Optional[str] = None) -> None:
|
|
|
301
301
|
click.echo(f" Stage file: {utils.Color.bold(result['stage_file'])}")
|
|
302
302
|
click.echo(f" Patch added: {utils.Color.bold(result['patch_id'])}")
|
|
303
303
|
click.echo(f" Tests passed: {utils.Color.green('✓')}")
|
|
304
|
-
click.echo(f" Archived branch: {utils.Color.bold(result['archived_branch'])}")
|
|
305
304
|
|
|
306
305
|
if result.get('notified_branches'):
|
|
307
306
|
click.echo(f" Notified: {len(result['notified_branches'])} active branch(es)")
|
|
@@ -101,29 +101,31 @@ def release_new(level: str) -> None:
|
|
|
101
101
|
# Get ReleaseManager
|
|
102
102
|
release_mgr = repo.release_manager
|
|
103
103
|
|
|
104
|
-
click.echo(f"
|
|
104
|
+
click.echo(f"Creating {level} release with integration branch...")
|
|
105
105
|
click.echo()
|
|
106
106
|
|
|
107
|
-
#
|
|
108
|
-
result = release_mgr.
|
|
107
|
+
# Create new release with integration branch
|
|
108
|
+
result = release_mgr.new_release(level)
|
|
109
109
|
|
|
110
110
|
# Extract result info
|
|
111
111
|
version = result['version']
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
branch = result['branch']
|
|
113
|
+
stage_file = result['stage_file']
|
|
114
114
|
|
|
115
115
|
# Success message
|
|
116
|
-
click.echo(f"✅ {utils.Color.bold('Release
|
|
116
|
+
click.echo(f"✅ {utils.Color.bold('Release created successfully!')}")
|
|
117
117
|
click.echo()
|
|
118
|
-
click.echo(f"
|
|
119
|
-
click.echo(f"
|
|
118
|
+
click.echo(f" Version: {utils.Color.bold(version)}")
|
|
119
|
+
click.echo(f" Release branch: {utils.Color.bold(branch)}")
|
|
120
120
|
click.echo(f" Stage file: {utils.Color.bold(stage_file)}")
|
|
121
121
|
click.echo()
|
|
122
122
|
click.echo(f"📝 Next steps:")
|
|
123
123
|
click.echo(f" 1. Create patches: {utils.Color.bold(f'half_orm dev patch new <patch_id>')}")
|
|
124
|
-
click.echo(f" 2. Add to release: {utils.Color.bold(f'half_orm dev patch add <patch_id>')}")
|
|
124
|
+
click.echo(f" 2. Add to release: {utils.Color.bold(f'half_orm dev patch add <patch_id> --to-version={version}')}")
|
|
125
125
|
click.echo(f" 3. Promote to RC: {utils.Color.bold('half_orm dev release promote rc')}")
|
|
126
126
|
click.echo()
|
|
127
|
+
click.echo(f"ℹ️ Patches will be merged into {utils.Color.bold(branch)} for integration testing")
|
|
128
|
+
click.echo()
|
|
127
129
|
|
|
128
130
|
except ReleaseManagerError as e:
|
|
129
131
|
# Handle validation errors (branch, clean, sync, etc.)
|
|
@@ -222,12 +224,17 @@ def release_promote(target: str) -> None:
|
|
|
222
224
|
try:
|
|
223
225
|
# Get repository instance
|
|
224
226
|
repo = Repo()
|
|
227
|
+
release_mgr = repo.release_manager
|
|
225
228
|
|
|
226
229
|
# Delegate to ReleaseManager
|
|
227
230
|
click.echo(f"Promoting release to {target.upper()}...")
|
|
228
231
|
click.echo()
|
|
229
232
|
|
|
230
|
-
|
|
233
|
+
# ReleaseManager auto-detects which version to promote
|
|
234
|
+
if target.lower() == 'rc':
|
|
235
|
+
result = release_mgr.promote_to_rc()
|
|
236
|
+
else: # prod
|
|
237
|
+
result = release_mgr.promote_to_prod()
|
|
231
238
|
|
|
232
239
|
# Display success message
|
|
233
240
|
click.echo(f"✓ {utils.Color.green('Success!')}")
|
|
@@ -236,38 +243,25 @@ def release_promote(target: str) -> None:
|
|
|
236
243
|
# Target-specific output
|
|
237
244
|
if target.lower() == 'rc':
|
|
238
245
|
# RC promotion output
|
|
239
|
-
click.echo(f"
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
click.echo(f" Patches merged: {utils.Color.bold(str(len(patches)))} patch(es)")
|
|
243
|
-
click.echo(f" Branches cleaned: {utils.Color.bold(str(len(result['branches_deleted'])))} branch(es)")
|
|
244
|
-
|
|
245
|
-
if result.get('notified_branches'):
|
|
246
|
-
click.echo(f" Notified: {len(result['notified_branches'])} active branch(es)")
|
|
247
|
-
|
|
246
|
+
click.echo(f" Version: {utils.Color.bold(result['version'])}")
|
|
247
|
+
click.echo(f" Tag: {utils.Color.bold(result['tag'])}")
|
|
248
|
+
click.echo(f" Branch: {utils.Color.bold(result['branch'])}")
|
|
248
249
|
click.echo()
|
|
249
250
|
click.echo("📝 Next steps:")
|
|
250
251
|
click.echo(f" • Test RC thoroughly")
|
|
251
|
-
click.echo(f" • Fix issues: Create patch, add to new stage, promote again")
|
|
252
252
|
click.echo(f" • Deploy to production: {utils.Color.bold('half_orm dev release promote prod')}")
|
|
253
253
|
|
|
254
254
|
else:
|
|
255
255
|
# Production promotion output
|
|
256
|
-
click.echo(f"
|
|
257
|
-
click.echo(f"
|
|
258
|
-
|
|
259
|
-
if
|
|
260
|
-
click.echo(f"
|
|
261
|
-
if result.get('metadata_file'):
|
|
262
|
-
click.echo(f" Metadata: {utils.Color.bold(result['metadata_file'])}")
|
|
263
|
-
if result.get('symlink_updated'):
|
|
264
|
-
click.echo(f" Symlink: schema.sql → {utils.Color.bold(result['schema_file'])}")
|
|
265
|
-
|
|
256
|
+
click.echo(f" Version: {utils.Color.bold(result['version'])}")
|
|
257
|
+
click.echo(f" Tag: {utils.Color.bold(result['tag'])}")
|
|
258
|
+
deleted = result.get('deleted_branches', [])
|
|
259
|
+
if deleted:
|
|
260
|
+
click.echo(f" Branches deleted: {utils.Color.bold(str(len(deleted)))}")
|
|
266
261
|
click.echo()
|
|
267
262
|
click.echo("📝 Next steps:")
|
|
268
|
-
click.echo(f"
|
|
269
|
-
click.echo(f" •
|
|
270
|
-
click.echo(f" • Start next cycle: {utils.Color.bold('half_orm dev release new patch')}")
|
|
263
|
+
click.echo(f" • Deploy to production servers")
|
|
264
|
+
click.echo(f" • Start next cycle: {utils.Color.bold('half_orm dev release new minor')}")
|
|
271
265
|
|
|
272
266
|
click.echo()
|
|
273
267
|
|
|
@@ -31,15 +31,15 @@ class Hop:
|
|
|
31
31
|
# Inside hop repository
|
|
32
32
|
if not self.__repo.devel:
|
|
33
33
|
# Sync-only mode (no metadata)
|
|
34
|
-
return ['sync-package']
|
|
34
|
+
return ['sync-package', 'check']
|
|
35
35
|
|
|
36
36
|
# Development mode (metadata present)
|
|
37
37
|
if self.__repo.database.production:
|
|
38
38
|
# PRODUCTION ENVIRONMENT - Release deployment only
|
|
39
|
-
return ['update', 'upgrade']
|
|
39
|
+
return ['update', 'upgrade', 'check']
|
|
40
40
|
else:
|
|
41
41
|
# DEVELOPMENT ENVIRONMENT - Patch development
|
|
42
|
-
return ['patch', 'release']
|
|
42
|
+
return ['patch', 'release', 'check']
|
|
43
43
|
|
|
44
44
|
@property
|
|
45
45
|
def repo_checked(self):
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Decorators for half-orm-dev.
|
|
3
|
+
|
|
4
|
+
Provides common decorators for ReleaseManager and PatchManager.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from functools import wraps
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def with_ho_prod_lock(branch: str = "ho-prod", timeout_minutes: int = 30):
|
|
11
|
+
"""
|
|
12
|
+
Decorator to protect methods that modify ho-prod with a lock tag.
|
|
13
|
+
|
|
14
|
+
The lock tag allows the pre-commit hook to permit commits on ho-prod
|
|
15
|
+
during the execution of the decorated method.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
branch: Branch to lock (default: "ho-prod")
|
|
19
|
+
timeout_minutes: Lock timeout in minutes (default: 30)
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
@with_ho_prod_lock()
|
|
23
|
+
def my_method(self, ...):
|
|
24
|
+
# Can commit to ho-prod here
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
Notes:
|
|
28
|
+
- The decorator assumes `self._repo.hgit` has `acquire_branch_lock()`
|
|
29
|
+
and `release_branch_lock()` methods
|
|
30
|
+
- The lock is ALWAYS released in the finally block, even on error
|
|
31
|
+
- If lock acquisition fails, the method is not executed
|
|
32
|
+
"""
|
|
33
|
+
def decorator(func):
|
|
34
|
+
@wraps(func)
|
|
35
|
+
def wrapper(self, *args, **kwargs):
|
|
36
|
+
lock_tag = None
|
|
37
|
+
try:
|
|
38
|
+
# Acquire lock
|
|
39
|
+
lock_tag = self._repo.hgit.acquire_branch_lock(branch, timeout_minutes=timeout_minutes)
|
|
40
|
+
|
|
41
|
+
# Execute the method
|
|
42
|
+
return func(self, *args, **kwargs)
|
|
43
|
+
finally:
|
|
44
|
+
# Always release lock (even on error)
|
|
45
|
+
if lock_tag:
|
|
46
|
+
self._repo.hgit.release_branch_lock(lock_tag)
|
|
47
|
+
|
|
48
|
+
return wrapper
|
|
49
|
+
return decorator
|