quickscale-cli 0.62.0__tar.gz → 0.63.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quickscale-cli
3
- Version: 0.62.0
3
+ Version: 0.63.0
4
4
  Summary: Command-line interface for QuickScale Django project generator
5
5
  Home-page: https://github.com/Experto-AI/quickscale
6
6
  License: Apache-2.0
@@ -19,6 +19,6 @@ Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Requires-Dist: click (>=8.1.0,<9.0.0)
22
- Requires-Dist: quickscale-core (>=0.62.0,<0.63.0)
22
+ Requires-Dist: quickscale-core (>=0.63.0,<0.64.0)
23
23
  Project-URL: Documentation, https://github.com/Experto-AI/quickscale/tree/main/docs
24
24
  Project-URL: Repository, https://github.com/Experto-AI/quickscale
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "quickscale-cli"
3
- version = "0.62.0"
3
+ version = "0.63.0"
4
4
  description = "Command-line interface for QuickScale Django project generator"
5
5
  authors = ["Experto AI <victor@experto.ai>"]
6
6
  license = "Apache-2.0"
@@ -25,7 +25,7 @@ packages = [{include = "quickscale_cli", from = "src"}]
25
25
  [tool.poetry.dependencies]
26
26
  python = "^3.10"
27
27
  click = "^8.1.0"
28
- quickscale-core = "^0.62.0"
28
+ quickscale-core = "^0.63.0"
29
29
 
30
30
  [tool.poetry.group.dev.dependencies]
31
31
  pytest = "^7.4.0"
@@ -108,5 +108,3 @@ check_untyped_defs = false
108
108
  [[tool.mypy.overrides]]
109
109
  module = "quickscale_core.*"
110
110
  ignore_missing_imports = true
111
-
112
-
@@ -1,2 +1,2 @@
1
1
  # Auto-generated by scripts/version_tool.sh
2
- __version__ = "0.62.0"
2
+ __version__ = "0.63.0"
@@ -1,6 +1,7 @@
1
1
  """Module management commands for QuickScale CLI."""
2
2
 
3
3
  from pathlib import Path
4
+ from typing import Any
4
5
 
5
6
  import click
6
7
 
@@ -15,10 +16,141 @@ from quickscale_core.utils.git_utils import (
15
16
  run_git_subtree_push,
16
17
  )
17
18
 
18
- # Available modules (placeholders in v0.62.0)
19
+ # Available modules
19
20
  AVAILABLE_MODULES = ["auth", "billing", "teams"]
20
21
 
21
22
 
23
+ def configure_auth_module() -> dict[str, Any]:
24
+ """Interactive configuration for auth module"""
25
+ click.echo("\n⚙️ Configuring auth module...")
26
+ click.echo("Answer these questions to customize the authentication setup:\n")
27
+
28
+ config = {
29
+ "allow_registration": click.confirm("Enable user registration?", default=True),
30
+ "email_verification": click.prompt(
31
+ "Email verification",
32
+ type=click.Choice(["none", "optional", "mandatory"], case_sensitive=False),
33
+ default="none",
34
+ show_choices=True,
35
+ ),
36
+ "authentication_method": click.prompt(
37
+ "Authentication method",
38
+ type=click.Choice(["email", "username", "both"], case_sensitive=False),
39
+ default="email",
40
+ show_choices=True,
41
+ ),
42
+ }
43
+
44
+ return config
45
+
46
+
47
+ def apply_auth_configuration(project_path: Path, config: dict[str, Any]) -> None:
48
+ """Apply auth module configuration to project settings"""
49
+ settings_path = project_path / "settings.py"
50
+ urls_path = project_path / "urls.py"
51
+
52
+ if not settings_path.exists():
53
+ click.secho("⚠️ Warning: settings.py not found, skipping auto-configuration", fg="yellow")
54
+ return
55
+
56
+ # Read settings.py
57
+ with open(settings_path) as f:
58
+ settings_content = f.read()
59
+
60
+ # Check if already configured
61
+ if "quickscale_modules_auth" in settings_content:
62
+ click.echo("ℹ️ Auth module already configured in settings.py")
63
+ return
64
+
65
+ # Add required apps to INSTALLED_APPS
66
+ installed_apps_addition = """
67
+ # QuickScale Auth Module - Added by quickscale embed
68
+ INSTALLED_APPS += [
69
+ "django.contrib.sites", # Required by allauth
70
+ "allauth",
71
+ "allauth.account",
72
+ "quickscale_modules_auth",
73
+ ]
74
+
75
+ # Authentication Configuration
76
+ AUTHENTICATION_BACKENDS = [
77
+ "django.contrib.auth.backends.ModelBackend",
78
+ "allauth.account.auth_backends.AuthenticationBackend",
79
+ ]
80
+
81
+ # Custom User Model
82
+ AUTH_USER_MODEL = "quickscale_modules_auth.User"
83
+
84
+ # Site ID (required by django.contrib.sites)
85
+ SITE_ID = 1
86
+
87
+ # Allauth Settings
88
+ """
89
+
90
+ # Add configuration based on user choices
91
+ if config["authentication_method"] == "email":
92
+ installed_apps_addition += 'ACCOUNT_AUTHENTICATION_METHOD = "email"\n'
93
+ installed_apps_addition += "ACCOUNT_USERNAME_REQUIRED = False\n"
94
+ installed_apps_addition += "ACCOUNT_EMAIL_REQUIRED = True\n"
95
+ elif config["authentication_method"] == "username":
96
+ installed_apps_addition += 'ACCOUNT_AUTHENTICATION_METHOD = "username"\n'
97
+ installed_apps_addition += "ACCOUNT_USERNAME_REQUIRED = True\n"
98
+ installed_apps_addition += "ACCOUNT_EMAIL_REQUIRED = False\n"
99
+ else: # both
100
+ installed_apps_addition += 'ACCOUNT_AUTHENTICATION_METHOD = "email"\n'
101
+ installed_apps_addition += "ACCOUNT_USERNAME_REQUIRED = True\n"
102
+ installed_apps_addition += "ACCOUNT_EMAIL_REQUIRED = True\n"
103
+
104
+ installed_apps_addition += f'ACCOUNT_EMAIL_VERIFICATION = "{config["email_verification"]}"\n'
105
+ installed_apps_addition += f'ACCOUNT_ALLOW_REGISTRATION = {config["allow_registration"]}\n'
106
+ installed_apps_addition += (
107
+ 'ACCOUNT_ADAPTER = "quickscale_modules_auth.adapters.QuickscaleAccountAdapter"\n'
108
+ )
109
+ installed_apps_addition += (
110
+ 'ACCOUNT_SIGNUP_FORM_CLASS = "quickscale_modules_auth.forms.SignupForm"\n'
111
+ )
112
+ installed_apps_addition += 'LOGIN_REDIRECT_URL = "/accounts/profile/"\n'
113
+ installed_apps_addition += 'LOGOUT_REDIRECT_URL = "/"\n'
114
+ installed_apps_addition += "SESSION_COOKIE_AGE = 1209600 # 2 weeks\n"
115
+
116
+ # Append to settings.py
117
+ with open(settings_path, "a") as f:
118
+ f.write("\n" + installed_apps_addition)
119
+
120
+ click.secho(" ✅ Updated settings.py with auth configuration", fg="green")
121
+
122
+ # Update urls.py
123
+ if urls_path.exists():
124
+ with open(urls_path) as f:
125
+ urls_content = f.read()
126
+
127
+ if "quickscale_modules_auth" not in urls_content:
128
+ # Find urlpatterns and add auth URLs
129
+ if "urlpatterns = [" in urls_content:
130
+ urls_addition = (
131
+ ' path("accounts/", include("quickscale_modules_auth.urls")), # Auth URLs\n'
132
+ )
133
+ urls_content = urls_content.replace(
134
+ "urlpatterns = [", "urlpatterns = [\n" + urls_addition
135
+ )
136
+
137
+ with open(urls_path, "w") as f:
138
+ f.write(urls_content)
139
+
140
+ click.secho(" ✅ Updated urls.py with auth URLs", fg="green")
141
+
142
+ # Show configuration summary
143
+ click.echo("\n📋 Configuration applied:")
144
+ click.echo(f" • Registration: {'Enabled' if config['allow_registration'] else 'Disabled'}")
145
+ click.echo(f" • Email verification: {config['email_verification']}")
146
+ click.echo(f" • Authentication: {config['authentication_method']}")
147
+
148
+
149
+ MODULE_CONFIGURATORS = {
150
+ "auth": (configure_auth_module, apply_auth_configuration),
151
+ }
152
+
153
+
22
154
  @click.command()
23
155
  @click.option(
24
156
  "--module",
@@ -91,31 +223,49 @@ def embed(module: str, remote: str) -> None:
91
223
  click.echo(f"\n📖 Branch '{branch}' does not exist on remote: {remote}", err=True)
92
224
  raise click.Abort()
93
225
 
226
+ # Interactive module configuration (v0.63.0+)
227
+ config = {}
228
+ if module in MODULE_CONFIGURATORS:
229
+ configurator, applier = MODULE_CONFIGURATORS[module]
230
+ config = configurator()
231
+
94
232
  # Embed module via git subtree
95
233
  prefix = f"modules/{module}"
96
234
  click.echo(f"\n📦 Embedding {module} module from {branch}...")
97
235
 
98
236
  run_git_subtree_add(prefix=prefix, remote=remote, branch=branch, squash=True)
99
237
 
100
- # Update configuration
238
+ # Update configuration tracking
101
239
  add_module(
102
240
  module_name=module,
103
241
  prefix=prefix,
104
242
  branch=branch,
105
- version="v0.62.0", # Placeholder version
243
+ version="v0.63.0",
106
244
  )
107
245
 
246
+ # Apply module-specific configuration
247
+ if module in MODULE_CONFIGURATORS and config:
248
+ _, applier = MODULE_CONFIGURATORS[module]
249
+ project_root = Path.cwd()
250
+ applier(project_root, config)
251
+
108
252
  # Success message
109
253
  click.secho(f"\n✅ Module '{module}' embedded successfully!", fg="green", bold=True)
110
254
  click.echo(f" Location: {module_path}")
111
255
  click.echo(f" Branch: {branch}")
112
256
 
113
- # Next steps
257
+ # Module-specific next steps
114
258
  click.echo("\n📋 Next steps:")
115
- click.echo(f" 1. Review the module code in modules/{module}/")
116
- click.echo(f" 2. Add to INSTALLED_APPS in settings: 'modules.{module}'")
117
- click.echo(" 3. Run migrations: python manage.py migrate")
118
- click.echo("\n💡 Note: This is a v0.62.0 placeholder. Full implementation coming soon!")
259
+ if module == "auth":
260
+ click.echo(f" 1. Review module code in modules/{module}/")
261
+ click.echo(" 2. Run migrations: python manage.py migrate")
262
+ click.echo(" 3. Create superuser (optional): python manage.py createsuperuser")
263
+ click.echo(" 4. Visit http://localhost:8000/accounts/login/")
264
+ click.echo("\n� Documentation: modules/auth/README.md")
265
+ else:
266
+ click.echo(f" 1. Review the module code in modules/{module}/")
267
+ click.echo(f" 2. Follow setup instructions in modules/{module}/README.md")
268
+ click.echo(" 3. Run migrations: python manage.py migrate")
119
269
 
120
270
  except GitError as e:
121
271
  click.secho(f"❌ Git error: {e}", fg="red", err=True)
@@ -24,7 +24,8 @@ def get_railway_cli_version() -> str | None:
24
24
  """
25
25
  Get the current Railway CLI version string.
26
26
 
27
- Returns:
27
+ Returns
28
+ -------
28
29
  Version string (e.g., "4.0.0") or None if not installed or error
29
30
 
30
31
  """
@@ -51,7 +52,8 @@ def install_railway_cli() -> bool:
51
52
  """
52
53
  Install Railway CLI via npm.
53
54
 
54
- Returns:
55
+ Returns
56
+ -------
55
57
  True if installation successful, False otherwise
56
58
 
57
59
  """
@@ -71,7 +73,8 @@ def upgrade_railway_cli() -> bool:
71
73
  """
72
74
  Upgrade Railway CLI to the latest version via npm.
73
75
 
74
- Returns:
76
+ Returns
77
+ -------
75
78
  True if upgrade successful, False otherwise
76
79
 
77
80
  """
@@ -93,7 +96,8 @@ def login_railway_cli_browserless() -> bool:
93
96
 
94
97
  This will prompt the user to visit a URL and enter a pairing code.
95
98
 
96
- Returns:
99
+ Returns
100
+ -------
97
101
  True if login successful, False otherwise
98
102
 
99
103
  """
@@ -207,11 +211,13 @@ def run_railway_command(
207
211
  Execute Railway CLI command with error handling.
208
212
 
209
213
  Args:
214
+ ----
210
215
  args: Command arguments to pass to railway CLI
211
216
  timeout: Command timeout in seconds
212
217
  interactive: If True, run command interactively without capturing output
213
218
 
214
219
  Returns:
220
+ -------
215
221
  CompletedProcess result
216
222
 
217
223
  """
@@ -247,6 +253,7 @@ def set_railway_variable(key: str, value: str, service: str | None = None) -> bo
247
253
  Set Railway environment variable.
248
254
 
249
255
  Args:
256
+ ----
250
257
  key: Environment variable name
251
258
  value: Environment variable value
252
259
  service: Service name to set variable for (optional)
@@ -272,10 +279,12 @@ def set_railway_variables_batch(
272
279
  triggering a deployment for each variable.
273
280
 
274
281
  Args:
282
+ ----
275
283
  variables: Dictionary of environment variable names and values
276
284
  service: Service name to set variables for (optional)
277
285
 
278
286
  Returns:
287
+ -------
279
288
  Tuple of (success, failed_keys)
280
289
  - success: True if all variables were set successfully
281
290
  - failed_keys: List of variable keys that failed to set
@@ -327,9 +336,11 @@ def get_deployment_url(service: str | None = None) -> str | None:
327
336
  Get deployment URL from Railway project.
328
337
 
329
338
  Args:
339
+ ----
330
340
  service: Service name (unused - kept for compatibility)
331
341
 
332
342
  Note:
343
+ ----
333
344
  Railway CLI v4's `status` command does not accept --service flag.
334
345
  This function returns the first deployment URL found.
335
346
 
@@ -356,9 +367,11 @@ def get_app_service_name(project_name: str | None = None) -> str:
356
367
  Determine the app service name for Railway deployment.
357
368
 
358
369
  Args:
370
+ ----
359
371
  project_name: Project name (defaults to current directory name)
360
372
 
361
373
  Returns:
374
+ -------
362
375
  Service name to use for the app
363
376
 
364
377
  """
@@ -376,9 +389,11 @@ def generate_railway_domain(service: str) -> str | None:
376
389
  Generate Railway public domain for a service.
377
390
 
378
391
  Args:
392
+ ----
379
393
  service: Service name to generate domain for
380
394
 
381
395
  Returns:
396
+ -------
382
397
  Generated domain URL or None if failed
383
398
 
384
399
  """
@@ -412,7 +427,8 @@ def check_uncommitted_changes() -> tuple[bool, str]:
412
427
  """
413
428
  Check if there are uncommitted changes in the Git repository.
414
429
 
415
- Returns:
430
+ Returns
431
+ -------
416
432
  Tuple of (has_changes, status_output)
417
433
  - has_changes: True if there are uncommitted changes
418
434
  - status_output: Git status output string
@@ -438,7 +454,8 @@ def verify_railway_json() -> tuple[bool, str]:
438
454
  """
439
455
  Verify that railway.json exists and is valid JSON.
440
456
 
441
- Returns:
457
+ Returns
458
+ -------
442
459
  Tuple of (is_valid, error_message)
443
460
  - is_valid: True if file exists and is valid JSON
444
461
  - error_message: Error description if validation fails, empty string otherwise
@@ -463,7 +480,8 @@ def verify_dockerfile() -> tuple[bool, str]:
463
480
  """
464
481
  Verify that Dockerfile exists in the project root.
465
482
 
466
- Returns:
483
+ Returns
484
+ -------
467
485
  Tuple of (exists, error_message)
468
486
  - exists: True if Dockerfile exists
469
487
  - error_message: Error description if check fails, empty string otherwise
@@ -487,7 +505,8 @@ def verify_railway_dependencies() -> tuple[bool, list[str]]:
487
505
  - dj-database-url (Database URL parsing)
488
506
  - whitenoise (Static file serving)
489
507
 
490
- Returns:
508
+ Returns
509
+ -------
491
510
  Tuple of (all_present, missing_deps)
492
511
  - all_present: True if all required dependencies are present
493
512
  - missing_deps: List of missing dependency names
@@ -520,7 +539,8 @@ def check_poetry_lock_consistency() -> tuple[bool, str]:
520
539
  """
521
540
  Check if poetry.lock is consistent with pyproject.toml.
522
541
 
523
- Returns:
542
+ Returns
543
+ -------
524
544
  Tuple of (is_consistent, message)
525
545
  - is_consistent: True if poetry.lock is consistent with pyproject.toml
526
546
  - message: Status message
@@ -557,7 +577,8 @@ def fix_poetry_lock() -> tuple[bool, str]:
557
577
  """
558
578
  Fix poetry.lock by running poetry lock --no-update.
559
579
 
560
- Returns:
580
+ Returns
581
+ -------
561
582
  Tuple of (success, message)
562
583
  - success: True if lock file was fixed successfully
563
584
  - message: Status message
@@ -586,9 +607,11 @@ def get_railway_variables(service: str | None = None) -> dict[str, str] | None:
586
607
  Get all environment variables for a Railway service.
587
608
 
588
609
  Args:
610
+ ----
589
611
  service: Service name (optional)
590
612
 
591
613
  Returns:
614
+ -------
592
615
  Dictionary of variable names and values, or None if failed
593
616
 
594
617
  """
@@ -625,9 +648,11 @@ def link_database_to_service(service: str) -> tuple[bool, str]:
625
648
  This creates a DATABASE_URL reference variable that points to the PostgreSQL service.
626
649
 
627
650
  Args:
651
+ ----
628
652
  service: App service name to link database to
629
653
 
630
654
  Returns:
655
+ -------
631
656
  Tuple of (success, message)
632
657
  - success: True if link was created successfully
633
658
  - message: Status or error message