half-orm-dev 0.16.0a9__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/__init__.py +1 -0
- half_orm_dev/cli/__init__.py +9 -0
- half_orm_dev/cli/commands/__init__.py +56 -0
- half_orm_dev/cli/commands/apply.py +13 -0
- half_orm_dev/cli/commands/clone.py +102 -0
- half_orm_dev/cli/commands/init.py +331 -0
- half_orm_dev/cli/commands/new.py +15 -0
- half_orm_dev/cli/commands/patch.py +317 -0
- half_orm_dev/cli/commands/prepare.py +21 -0
- half_orm_dev/cli/commands/prepare_release.py +119 -0
- half_orm_dev/cli/commands/promote_to.py +127 -0
- half_orm_dev/cli/commands/release.py +344 -0
- half_orm_dev/cli/commands/restore.py +14 -0
- half_orm_dev/cli/commands/sync.py +13 -0
- half_orm_dev/cli/commands/todo.py +73 -0
- half_orm_dev/cli/commands/undo.py +17 -0
- half_orm_dev/cli/commands/update.py +73 -0
- half_orm_dev/cli/commands/upgrade.py +191 -0
- half_orm_dev/cli/main.py +103 -0
- half_orm_dev/cli_extension.py +38 -0
- half_orm_dev/database.py +1389 -0
- half_orm_dev/hgit.py +1025 -0
- half_orm_dev/hop.py +167 -0
- half_orm_dev/manifest.py +43 -0
- half_orm_dev/modules.py +456 -0
- half_orm_dev/patch.py +281 -0
- half_orm_dev/patch_manager.py +1694 -0
- half_orm_dev/patch_validator.py +335 -0
- half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +34 -0
- half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +2 -0
- half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +3 -0
- half_orm_dev/patches/log +2 -0
- half_orm_dev/patches/sql/half_orm_meta.sql +208 -0
- half_orm_dev/release_manager.py +2841 -0
- half_orm_dev/repo.py +1562 -0
- half_orm_dev/templates/.gitignore +15 -0
- half_orm_dev/templates/MANIFEST.in +1 -0
- half_orm_dev/templates/Pipfile +13 -0
- half_orm_dev/templates/README +25 -0
- half_orm_dev/templates/conftest_template +42 -0
- half_orm_dev/templates/init_module_template +10 -0
- half_orm_dev/templates/module_template_1 +12 -0
- half_orm_dev/templates/module_template_2 +6 -0
- half_orm_dev/templates/module_template_3 +3 -0
- half_orm_dev/templates/relation_test +23 -0
- half_orm_dev/templates/setup.py +81 -0
- half_orm_dev/templates/sql_adapter +9 -0
- half_orm_dev/templates/warning +12 -0
- half_orm_dev/utils.py +49 -0
- half_orm_dev/version.txt +1 -0
- half_orm_dev-0.16.0a9.dist-info/METADATA +935 -0
- half_orm_dev-0.16.0a9.dist-info/RECORD +58 -0
- half_orm_dev-0.16.0a9.dist-info/WHEEL +5 -0
- half_orm_dev-0.16.0a9.dist-info/licenses/AUTHORS +3 -0
- half_orm_dev-0.16.0a9.dist-info/licenses/LICENSE +14 -0
- half_orm_dev-0.16.0a9.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/conftest.py +329 -0
half_orm_dev/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__all__ = ['cli']
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Commands module for half-orm-dev CLI
|
|
3
|
+
|
|
4
|
+
Provides all individual command implementations.
|
|
5
|
+
REFACTORED in v0.16.0 - Git-centric patch workflow
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# ✅ New Git-centric commands (stubs for now)
|
|
9
|
+
from .init import init
|
|
10
|
+
from .clone import clone
|
|
11
|
+
from .patch import patch
|
|
12
|
+
from .release import release
|
|
13
|
+
from .update import update
|
|
14
|
+
from .upgrade import upgrade
|
|
15
|
+
from .todo import apply_release
|
|
16
|
+
from .todo import create_hotfix
|
|
17
|
+
from .todo import rollback
|
|
18
|
+
|
|
19
|
+
# ♻️ Adapted existing commands
|
|
20
|
+
from .todo import sync_package # Unchanged
|
|
21
|
+
from .todo import restore # Adapted for new architecture
|
|
22
|
+
|
|
23
|
+
# Registry of all available commands - Git-centric architecture
|
|
24
|
+
ALL_COMMANDS = {
|
|
25
|
+
# Core workflow
|
|
26
|
+
'init': init,
|
|
27
|
+
'clone': clone,
|
|
28
|
+
'patch': patch,
|
|
29
|
+
'release': release,
|
|
30
|
+
'update': update, # Adapted for production
|
|
31
|
+
'upgrade': upgrade, # Adapted for production
|
|
32
|
+
# 🚧 (stubs)
|
|
33
|
+
'apply_release': apply_release,
|
|
34
|
+
|
|
35
|
+
# 🚧 Emergency workflow (stubs)
|
|
36
|
+
'create-hotfix': create_hotfix,
|
|
37
|
+
'rollback': rollback,
|
|
38
|
+
|
|
39
|
+
# ♻️ Adapted commands
|
|
40
|
+
'sync-package': sync_package, # Unchanged
|
|
41
|
+
'restore': restore, # Adapted
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
# New commands
|
|
46
|
+
'init',
|
|
47
|
+
'clone',
|
|
48
|
+
'patch',
|
|
49
|
+
'release',
|
|
50
|
+
'upgrade',
|
|
51
|
+
'create_hotfix',
|
|
52
|
+
'rollback',
|
|
53
|
+
# Adapted commands
|
|
54
|
+
'sync_package',
|
|
55
|
+
'ALL_COMMANDS'
|
|
56
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Clone command - Clone existing half_orm_dev project from Git repository
|
|
3
|
+
|
|
4
|
+
Clones a Git repository, checks out ho-prod branch, sets up local database,
|
|
5
|
+
and restores schema to production version.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from half_orm_dev.repo import Repo, RepoError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.command('clone')
|
|
14
|
+
@click.argument('git_origin')
|
|
15
|
+
@click.option('--database-name', default=None, help='Custom local database name (default: use project name)')
|
|
16
|
+
@click.option('--dest-dir', default=None, help='Destination directory name (default: infer from git URL)')
|
|
17
|
+
@click.option('--production', is_flag=True, help='Production mode (default: False)')
|
|
18
|
+
@click.option('--no-create-db', is_flag=True, help='Skip database creation (database must exist)')
|
|
19
|
+
def clone(git_origin, database_name, dest_dir, production, no_create_db):
|
|
20
|
+
"""
|
|
21
|
+
Clone existing half_orm_dev project and setup local database.
|
|
22
|
+
|
|
23
|
+
Clones a Git repository, checks out the ho-prod branch, creates/configures
|
|
24
|
+
the local database, and restores the schema to production version.
|
|
25
|
+
|
|
26
|
+
ARGUMENTS:
|
|
27
|
+
git_origin: Git repository URL (HTTPS, SSH, file://)
|
|
28
|
+
|
|
29
|
+
WORKFLOW:
|
|
30
|
+
1. Clone repository from git_origin
|
|
31
|
+
2. Checkout ho-prod branch
|
|
32
|
+
3. Create .hop/alt_config if --database-name provided
|
|
33
|
+
4. Setup database (create + metadata if needed)
|
|
34
|
+
5. Restore database from model/schema.sql
|
|
35
|
+
|
|
36
|
+
EXAMPLES:
|
|
37
|
+
# Basic clone (prompts for database connection params)
|
|
38
|
+
half_orm dev clone https://github.com/user/project.git
|
|
39
|
+
|
|
40
|
+
# Clone with custom database name
|
|
41
|
+
half_orm dev clone https://github.com/user/project.git --database-name my_local_db
|
|
42
|
+
|
|
43
|
+
# Clone to specific directory
|
|
44
|
+
half_orm dev clone https://github.com/user/project.git --dest-dir my_project
|
|
45
|
+
|
|
46
|
+
# Production mode (database must already exist)
|
|
47
|
+
half_orm dev clone https://github.com/user/project.git --production --no-create-db
|
|
48
|
+
|
|
49
|
+
NOTES:
|
|
50
|
+
- Changes current directory to cloned project
|
|
51
|
+
- Interactive prompts for missing connection parameters
|
|
52
|
+
- Requires model/schema.sql in repository for schema restoration
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
click.echo(f"🔄 Cloning half_orm project from {git_origin}...")
|
|
56
|
+
click.echo()
|
|
57
|
+
|
|
58
|
+
# Execute clone
|
|
59
|
+
Repo.clone_repo(
|
|
60
|
+
git_origin=git_origin,
|
|
61
|
+
database_name=database_name,
|
|
62
|
+
dest_dir=dest_dir,
|
|
63
|
+
production=production,
|
|
64
|
+
create_db=not no_create_db
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Determine project name for success message
|
|
68
|
+
if dest_dir:
|
|
69
|
+
project_name = dest_dir
|
|
70
|
+
else:
|
|
71
|
+
project_name = git_origin.rstrip('/').split('/')[-1]
|
|
72
|
+
if project_name.endswith('.git'):
|
|
73
|
+
project_name = project_name[:-4]
|
|
74
|
+
|
|
75
|
+
click.echo()
|
|
76
|
+
click.echo(f"✅ Project '{project_name}' cloned and configured successfully!")
|
|
77
|
+
click.echo(f" 📁 Project directory: {Path.cwd()}")
|
|
78
|
+
click.echo(f" 🗄️ Database restored to production version")
|
|
79
|
+
click.echo()
|
|
80
|
+
click.echo(f"Next steps:")
|
|
81
|
+
click.echo(f" • cd {project_name}")
|
|
82
|
+
click.echo(f" • half_orm dev create-patch <patch_id> # Start developing")
|
|
83
|
+
click.echo()
|
|
84
|
+
|
|
85
|
+
except FileExistsError as e:
|
|
86
|
+
click.echo(f"❌ Error: {e}", err=True)
|
|
87
|
+
click.echo(f" Remove the existing directory or choose a different destination.", err=True)
|
|
88
|
+
raise click.Abort()
|
|
89
|
+
|
|
90
|
+
except RepoError as e:
|
|
91
|
+
click.echo(f"❌ Clone failed: {e}", err=True)
|
|
92
|
+
click.echo()
|
|
93
|
+
click.echo(f"Common issues:")
|
|
94
|
+
click.echo(f" • Invalid or inaccessible Git URL")
|
|
95
|
+
click.echo(f" • Missing 'ho-prod' branch in repository")
|
|
96
|
+
click.echo(f" • Database connection or permission issues")
|
|
97
|
+
click.echo(f" • Missing model/schema.sql in repository")
|
|
98
|
+
raise click.Abort()
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
click.echo(f"❌ Unexpected error: {e}", err=True)
|
|
102
|
+
raise click.Abort()
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Init command - Initialize new half_orm_dev project (unified workflow)
|
|
3
|
+
|
|
4
|
+
Orchestrates database setup and project creation in a single command.
|
|
5
|
+
This is a convenience wrapper around the existing init-database and init-project logic.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from half_orm import utils
|
|
11
|
+
from half_orm.model import Model
|
|
12
|
+
from half_orm_dev.database import Database
|
|
13
|
+
from half_orm_dev.repo import Repo
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DatabaseExistsWithMetadataError(Exception):
|
|
17
|
+
"""Raised when database already has metadata (existing project)."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ProjectDirectoryExistsError(Exception):
|
|
22
|
+
"""Raised when project directory already exists."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@click.command('init')
|
|
27
|
+
@click.argument('project_name')
|
|
28
|
+
@click.option('--host', default='localhost', help='PostgreSQL host (default: localhost)')
|
|
29
|
+
@click.option('--port', default=5432, type=int, help='PostgreSQL port (default: 5432)')
|
|
30
|
+
@click.option('--user', default=None, help='Database user (default: $USER)')
|
|
31
|
+
@click.option('--password', default=None, help='Database password (prompts if missing)')
|
|
32
|
+
@click.option('--git-origin', default=None, help='Git remote origin URL (prompts if missing)')
|
|
33
|
+
@click.option('--production', is_flag=True, help='Mark as production environment (default: False)')
|
|
34
|
+
@click.option('--force-sync-only', is_flag=True, help='Skip metadata installation, force sync-only mode')
|
|
35
|
+
@click.option('--create-db', is_flag=False, default=True)
|
|
36
|
+
@click.option('--docker', default=None, help='Docker container name for PostgreSQL')
|
|
37
|
+
def init(project_name, host, port, user, password, git_origin, production, force_sync_only, create_db, docker):
|
|
38
|
+
"""
|
|
39
|
+
Initialize a new half_orm_dev project with database and code structure.
|
|
40
|
+
|
|
41
|
+
Creates both database (with metadata) and project structure in a single command.
|
|
42
|
+
|
|
43
|
+
\b
|
|
44
|
+
ARGUMENTS:
|
|
45
|
+
project_name: Name of the project (= database name = Python package name)
|
|
46
|
+
|
|
47
|
+
\b
|
|
48
|
+
WORKFLOW:
|
|
49
|
+
1. Check if database exists and has metadata
|
|
50
|
+
2. Create/configure database with half_orm_dev metadata
|
|
51
|
+
3. Create project directory structure with Git repository
|
|
52
|
+
4. Generate Python package from database schema
|
|
53
|
+
|
|
54
|
+
\b
|
|
55
|
+
EXAMPLES:
|
|
56
|
+
# Create new project with new database (interactive)
|
|
57
|
+
half_orm dev init my_blog
|
|
58
|
+
|
|
59
|
+
# Create with explicit connection parameters
|
|
60
|
+
half_orm dev init my_blog --host=localhost --user=dev --git-origin=https://github.com/user/my_blog.git
|
|
61
|
+
|
|
62
|
+
# Force sync-only mode (no metadata, limited functionality)
|
|
63
|
+
half_orm dev init legacy_project --force-sync-only
|
|
64
|
+
|
|
65
|
+
\b
|
|
66
|
+
PROJECT STRUCTURE CREATED:
|
|
67
|
+
my_blog/
|
|
68
|
+
├── .git/ (ho-prod branch)
|
|
69
|
+
├── .hop/config (project configuration)
|
|
70
|
+
├── Patches/ (schema patches)
|
|
71
|
+
├── releases/ (release management)
|
|
72
|
+
├── model/ (schema snapshots)
|
|
73
|
+
├── backups/ (database backups)
|
|
74
|
+
├── my_blog/ (Python package)
|
|
75
|
+
├── tests/ (test directory)
|
|
76
|
+
├── README.md
|
|
77
|
+
├── .gitignore
|
|
78
|
+
└── pyproject.toml
|
|
79
|
+
|
|
80
|
+
\b
|
|
81
|
+
MODES:
|
|
82
|
+
- Full development mode (default): Database with half_orm_dev metadata
|
|
83
|
+
→ Enables: create-patch, apply-patch, prepare-release, etc.
|
|
84
|
+
|
|
85
|
+
- Sync-only mode (--force-sync-only or user declines metadata):
|
|
86
|
+
→ Only enables: sync-package (code generation from schema)
|
|
87
|
+
→ Limited functionality, no patch management
|
|
88
|
+
|
|
89
|
+
\b
|
|
90
|
+
ERROR CASES:
|
|
91
|
+
- Database exists with metadata:
|
|
92
|
+
→ Error: "Use 'half_orm dev clone <git-url>' to work on existing project"
|
|
93
|
+
|
|
94
|
+
- Project directory already exists:
|
|
95
|
+
→ Error: "Directory already exists, choose a different name"
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
database_name = project_name
|
|
99
|
+
package_name = project_name
|
|
100
|
+
|
|
101
|
+
click.echo(f"🚀 Initializing half_orm project '{project_name}'...")
|
|
102
|
+
click.echo()
|
|
103
|
+
|
|
104
|
+
# ============================================================
|
|
105
|
+
# STEP 1: PRE-FLIGHT CHECKS
|
|
106
|
+
# ============================================================
|
|
107
|
+
|
|
108
|
+
# Check 1: Project directory must not exist
|
|
109
|
+
project_dir = Path.cwd() / project_name
|
|
110
|
+
if project_dir.exists():
|
|
111
|
+
raise ProjectDirectoryExistsError(
|
|
112
|
+
f"Directory '{project_name}' already exists in current directory.\n"
|
|
113
|
+
f"Choose a different project name or remove the existing directory."
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Prepare connection options (will be used by both check and setup)
|
|
117
|
+
connection_options = {
|
|
118
|
+
'host': host,
|
|
119
|
+
'port': port,
|
|
120
|
+
'user': user,
|
|
121
|
+
'password': password,
|
|
122
|
+
'production': production,
|
|
123
|
+
'docker_container': docker
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Check 2: Database status (exists? has metadata?)
|
|
127
|
+
click.echo(f"🔍 Checking database status...")
|
|
128
|
+
db_exists, has_metadata = _check_database_status(
|
|
129
|
+
database_name, connection_options
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Check 3: Existing project detection
|
|
133
|
+
if has_metadata:
|
|
134
|
+
raise DatabaseExistsWithMetadataError(
|
|
135
|
+
f"Database '{database_name}' already has half_orm_dev metadata (existing project).\n"
|
|
136
|
+
f"To work on this project, use: half_orm dev clone <git-url>"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# ============================================================
|
|
140
|
+
# STEP 2: DATABASE SETUP
|
|
141
|
+
# ============================================================
|
|
142
|
+
|
|
143
|
+
# Determine metadata installation strategy
|
|
144
|
+
install_metadata = True
|
|
145
|
+
create_db = not db_exists
|
|
146
|
+
|
|
147
|
+
if db_exists:
|
|
148
|
+
# Database exists without metadata
|
|
149
|
+
click.echo(f"ℹ️ Database '{database_name}' exists without half_orm_dev metadata.")
|
|
150
|
+
|
|
151
|
+
if force_sync_only:
|
|
152
|
+
install_metadata = False
|
|
153
|
+
click.echo("⚠️ Sync-only mode forced: metadata installation skipped.")
|
|
154
|
+
else:
|
|
155
|
+
# Interactive prompt
|
|
156
|
+
install_metadata = click.confirm(
|
|
157
|
+
"Install half_orm_dev metadata for full development mode?",
|
|
158
|
+
default=True
|
|
159
|
+
)
|
|
160
|
+
if not install_metadata:
|
|
161
|
+
click.echo("⚠️ Continuing in sync-only mode (limited functionality).")
|
|
162
|
+
|
|
163
|
+
click.echo()
|
|
164
|
+
|
|
165
|
+
# Execute database setup (reuses existing Database.setup_database)
|
|
166
|
+
click.echo(f"🗄️ Setting up database '{database_name}'...")
|
|
167
|
+
Database.setup_database(
|
|
168
|
+
database_name=database_name,
|
|
169
|
+
connection_options=connection_options,
|
|
170
|
+
create_db=create_db,
|
|
171
|
+
add_metadata=install_metadata
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
click.echo(f"✅ Database '{database_name}' configured successfully.")
|
|
175
|
+
click.echo()
|
|
176
|
+
|
|
177
|
+
# ============================================================
|
|
178
|
+
# STEP 3: PROJECT STRUCTURE CREATION
|
|
179
|
+
# ============================================================
|
|
180
|
+
|
|
181
|
+
# Prompt for git_origin if not provided
|
|
182
|
+
if not git_origin:
|
|
183
|
+
git_origin = _prompt_for_git_origin(project_name)
|
|
184
|
+
|
|
185
|
+
click.echo(f"📁 Creating project structure...")
|
|
186
|
+
|
|
187
|
+
# Now safe to instantiate Repo (database is configured)
|
|
188
|
+
repo = Repo()
|
|
189
|
+
repo.init_git_centric_project(
|
|
190
|
+
package_name=package_name,
|
|
191
|
+
git_origin=git_origin
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# ============================================================
|
|
195
|
+
# STEP 4: SUCCESS MESSAGE
|
|
196
|
+
# ============================================================
|
|
197
|
+
|
|
198
|
+
click.echo()
|
|
199
|
+
click.echo(f"✅ Project '{project_name}' initialized successfully!")
|
|
200
|
+
click.echo()
|
|
201
|
+
click.echo("📂 Project structure:")
|
|
202
|
+
click.echo(f" {project_name}/")
|
|
203
|
+
click.echo(f" ├── .git/ (ho-prod branch)")
|
|
204
|
+
click.echo(f" ├── .hop/config")
|
|
205
|
+
click.echo(f" ├── Patches/")
|
|
206
|
+
click.echo(f" ├── releases/")
|
|
207
|
+
click.echo(f" ├── model/")
|
|
208
|
+
click.echo(f" ├── {package_name}/ (Python package)")
|
|
209
|
+
click.echo(f" └── tests/")
|
|
210
|
+
click.echo()
|
|
211
|
+
click.echo("🚀 Next steps:")
|
|
212
|
+
click.echo(f" cd {project_name}/")
|
|
213
|
+
|
|
214
|
+
if install_metadata:
|
|
215
|
+
click.echo(" half_orm dev create-patch <patch-name> # Start developing")
|
|
216
|
+
else:
|
|
217
|
+
click.echo(" half_orm dev sync-package # Sync Python code with schema")
|
|
218
|
+
click.echo()
|
|
219
|
+
click.echo(" ⚠️ Note: Sync-only mode has limited functionality.")
|
|
220
|
+
click.echo(" To enable full development mode later, you'll need to:")
|
|
221
|
+
click.echo(" 1. Install metadata manually in the database")
|
|
222
|
+
click.echo(" 2. Reconfigure the project")
|
|
223
|
+
|
|
224
|
+
except DatabaseExistsWithMetadataError as e:
|
|
225
|
+
click.echo()
|
|
226
|
+
utils.error(str(e), exit_code=1)
|
|
227
|
+
|
|
228
|
+
except ProjectDirectoryExistsError as e:
|
|
229
|
+
click.echo()
|
|
230
|
+
utils.error(str(e), exit_code=1)
|
|
231
|
+
|
|
232
|
+
except Exception as e:
|
|
233
|
+
click.echo()
|
|
234
|
+
utils.error(f"Project initialization failed: {e}", exit_code=1)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _check_database_status(database_name, connection_options):
|
|
238
|
+
"""
|
|
239
|
+
Check if database exists and has half_orm_dev metadata.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
database_name (str): Name of the database to check
|
|
243
|
+
connection_options (dict): Already collected connection parameters
|
|
244
|
+
- host (str): PostgreSQL host
|
|
245
|
+
- port (int): PostgreSQL port
|
|
246
|
+
- user (str): Database user
|
|
247
|
+
- password (str): Database password
|
|
248
|
+
- production (bool): Production flag
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
tuple: (db_exists: bool, has_metadata: bool)
|
|
252
|
+
- (False, False): Database doesn't exist
|
|
253
|
+
- (True, False): Database exists without metadata
|
|
254
|
+
- (True, True): Database exists with metadata (existing project)
|
|
255
|
+
|
|
256
|
+
Implementation:
|
|
257
|
+
Attempts connection using Model. If successful, checks for
|
|
258
|
+
half_orm_meta.hop_release table. Uses already-collected parameters
|
|
259
|
+
to avoid re-prompting the user.
|
|
260
|
+
"""
|
|
261
|
+
try:
|
|
262
|
+
# Try to create Model instance
|
|
263
|
+
# Model will attempt to read from ~/.half_orm/<database_name> config
|
|
264
|
+
# If config doesn't exist yet, connection will fail and we return (False, False)
|
|
265
|
+
model = Model(database_name)
|
|
266
|
+
|
|
267
|
+
# Database exists and is accessible, check for metadata
|
|
268
|
+
has_metadata = model.has_relation('half_orm_meta.hop_release')
|
|
269
|
+
|
|
270
|
+
# Clean up
|
|
271
|
+
model.disconnect()
|
|
272
|
+
|
|
273
|
+
return (True, has_metadata)
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
# Database doesn't exist or not accessible yet
|
|
277
|
+
# This is expected for new databases before setup_database runs
|
|
278
|
+
error_msg = str(e).lower()
|
|
279
|
+
if 'does not exist' in error_msg or 'database' in error_msg or 'connection' in error_msg:
|
|
280
|
+
return (False, False)
|
|
281
|
+
else:
|
|
282
|
+
# Unexpected error, re-raise
|
|
283
|
+
raise
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _prompt_for_git_origin(project_name):
|
|
287
|
+
"""
|
|
288
|
+
Interactively prompt user for git origin URL.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
project_name (str): Project name (used for suggestion)
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
str: Git origin URL provided by user
|
|
295
|
+
|
|
296
|
+
Prompts:
|
|
297
|
+
"Git remote origin URL (e.g., https://github.com/user/my_blog.git): "
|
|
298
|
+
|
|
299
|
+
Validation:
|
|
300
|
+
- Must not be empty
|
|
301
|
+
- Basic URL format check (starts with http/git/ssh)
|
|
302
|
+
"""
|
|
303
|
+
click.echo("🔗 Git repository configuration:")
|
|
304
|
+
click.echo(f" Example: https://github.com/<user>/{project_name}.git")
|
|
305
|
+
click.echo()
|
|
306
|
+
|
|
307
|
+
while True:
|
|
308
|
+
git_origin = click.prompt(
|
|
309
|
+
"Git remote origin URL",
|
|
310
|
+
type=str,
|
|
311
|
+
default=""
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Validation
|
|
315
|
+
if not git_origin or git_origin.strip() == "":
|
|
316
|
+
click.echo("❌ Git origin URL cannot be empty. Please provide a valid URL.")
|
|
317
|
+
continue
|
|
318
|
+
|
|
319
|
+
git_origin = git_origin.strip()
|
|
320
|
+
|
|
321
|
+
# Basic format validation
|
|
322
|
+
valid_prefixes = ('http://', 'https://', 'git://', 'git@', 'ssh://')
|
|
323
|
+
if not any(git_origin.startswith(prefix) for prefix in valid_prefixes):
|
|
324
|
+
click.echo(
|
|
325
|
+
"⚠️ Warning: Git URL should start with http://, https://, git://, git@, or ssh://\n"
|
|
326
|
+
" Example: https://github.com/user/repo.git"
|
|
327
|
+
)
|
|
328
|
+
if not click.confirm("Use this URL anyway?", default=False):
|
|
329
|
+
continue
|
|
330
|
+
|
|
331
|
+
return git_origin
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
New command - Creates a new hop project
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from half_orm_dev.repo import Repo
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command()
|
|
10
|
+
@click.argument('package_name')
|
|
11
|
+
@click.option('-d', '--devel', is_flag=True, help="Development mode")
|
|
12
|
+
def new(package_name, devel=False):
|
|
13
|
+
"""Creates a new hop project named <package_name>."""
|
|
14
|
+
repo = Repo()
|
|
15
|
+
repo.init(package_name, devel)
|