krickstart 1.2.0__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.
- devspeed/__init__.py +15 -0
- devspeed/cli.py +21 -0
- devspeed/config.py +29 -0
- devspeed/core/__init__.py +29 -0
- devspeed/core/compose_generator.py +3 -0
- devspeed/errors.py +29 -0
- devspeed/plugin.py +25 -0
- devspeed/plugins/__init__.py +21 -0
- devspeed/plugins/base.py +7 -0
- devspeed/plugins/registry.py +29 -0
- devspeed/stacks/__init__.py +21 -0
- devspeed/stacks/common.py +66 -0
- devspeed/stacks/django_postgres.py +168 -0
- devspeed/stacks/express_mongodb.py +128 -0
- devspeed/stacks/fastapi_postgres.py +140 -0
- devspeed/stacks/flask_postgres.py +139 -0
- devspeed/stacks/go_gin_postgres.py +161 -0
- devspeed/stacks/laravel_mysql.py +134 -0
- devspeed/stacks/nextjs_postgres.py +163 -0
- devspeed/stacks/node_postgres_redis.py +171 -0
- devspeed/stacks/spring_boot_mysql.py +133 -0
- devspeed/stacks/spring_boot_postgres.py +110 -0
- krickstart-1.2.0.dist-info/METADATA +249 -0
- krickstart-1.2.0.dist-info/RECORD +28 -0
- krickstart-1.2.0.dist-info/WHEEL +5 -0
- krickstart-1.2.0.dist-info/entry_points.txt +15 -0
- krickstart-1.2.0.dist-info/licenses/LICENSE +21 -0
- krickstart-1.2.0.dist-info/top_level.txt +1 -0
devspeed/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Legacy compatibility package alias for Slipway."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
from slipway import PLUGIN_API_VERSION, StackPlugin, __version__
|
|
8
|
+
|
|
9
|
+
warnings.warn(
|
|
10
|
+
"'devspeed' has been renamed to 'slipway'. Please update imports to 'slipway'.",
|
|
11
|
+
DeprecationWarning,
|
|
12
|
+
stacklevel=2,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = ["PLUGIN_API_VERSION", "StackPlugin", "__version__"]
|
devspeed/cli.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Backward-compatibility aliases for devspeed.cli."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from slipway.cli import (
|
|
6
|
+
GENERATED_COMPOSE,
|
|
7
|
+
GENERATED_ENV,
|
|
8
|
+
GENERATED_ENV_EXAMPLE,
|
|
9
|
+
build_parser,
|
|
10
|
+
deprecated_alias_main,
|
|
11
|
+
)
|
|
12
|
+
from slipway.cli import deprecated_alias_main as main
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"GENERATED_COMPOSE",
|
|
16
|
+
"GENERATED_ENV",
|
|
17
|
+
"GENERATED_ENV_EXAMPLE",
|
|
18
|
+
"build_parser",
|
|
19
|
+
"deprecated_alias_main",
|
|
20
|
+
"main",
|
|
21
|
+
]
|
devspeed/config.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Backward-compatibility aliases for devspeed.config."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from slipway.config import (
|
|
6
|
+
CONFIG_FILENAME,
|
|
7
|
+
LEGACY_CONFIG_FILENAME,
|
|
8
|
+
DevspeedConfig,
|
|
9
|
+
LifecycleConfig,
|
|
10
|
+
ServiceConfig,
|
|
11
|
+
SlipwayConfig,
|
|
12
|
+
config_path,
|
|
13
|
+
load_config,
|
|
14
|
+
save_config,
|
|
15
|
+
validate_config,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"CONFIG_FILENAME",
|
|
20
|
+
"LEGACY_CONFIG_FILENAME",
|
|
21
|
+
"DevspeedConfig",
|
|
22
|
+
"LifecycleConfig",
|
|
23
|
+
"ServiceConfig",
|
|
24
|
+
"SlipwayConfig",
|
|
25
|
+
"config_path",
|
|
26
|
+
"load_config",
|
|
27
|
+
"save_config",
|
|
28
|
+
"validate_config",
|
|
29
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Core generation services for DevSpeed (legacy alias)."""
|
|
2
|
+
|
|
3
|
+
from slipway.core.compose_generator import (
|
|
4
|
+
DEFAULT_COMPOSE_FILENAME,
|
|
5
|
+
DEFAULT_ENV_FILENAME,
|
|
6
|
+
ComposeGenerationError,
|
|
7
|
+
ComposeGenerator,
|
|
8
|
+
ConfigValidationError,
|
|
9
|
+
GeneratedArtifacts,
|
|
10
|
+
GeneratorSettings,
|
|
11
|
+
OutputFileExistsError,
|
|
12
|
+
PluginResolutionError,
|
|
13
|
+
PortConflictError,
|
|
14
|
+
WriteResult,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"DEFAULT_COMPOSE_FILENAME",
|
|
19
|
+
"DEFAULT_ENV_FILENAME",
|
|
20
|
+
"ComposeGenerationError",
|
|
21
|
+
"ComposeGenerator",
|
|
22
|
+
"ConfigValidationError",
|
|
23
|
+
"GeneratedArtifacts",
|
|
24
|
+
"GeneratorSettings",
|
|
25
|
+
"OutputFileExistsError",
|
|
26
|
+
"PluginResolutionError",
|
|
27
|
+
"PortConflictError",
|
|
28
|
+
"WriteResult",
|
|
29
|
+
]
|
devspeed/errors.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Backward-compatibility aliases for devspeed.errors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from slipway.errors import (
|
|
6
|
+
CommandFailedError,
|
|
7
|
+
ComposeNotFoundError,
|
|
8
|
+
ConfigNotFoundError,
|
|
9
|
+
ConfigValidationError,
|
|
10
|
+
DevspeedError,
|
|
11
|
+
DockerNotFoundError,
|
|
12
|
+
DockerNotRunningError,
|
|
13
|
+
PortConflictError,
|
|
14
|
+
SlipwayError,
|
|
15
|
+
UnknownStackError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"CommandFailedError",
|
|
20
|
+
"ComposeNotFoundError",
|
|
21
|
+
"ConfigNotFoundError",
|
|
22
|
+
"ConfigValidationError",
|
|
23
|
+
"DevspeedError",
|
|
24
|
+
"DockerNotFoundError",
|
|
25
|
+
"DockerNotRunningError",
|
|
26
|
+
"PortConflictError",
|
|
27
|
+
"SlipwayError",
|
|
28
|
+
"UnknownStackError",
|
|
29
|
+
]
|
devspeed/plugin.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Backward-compatibility aliases for devspeed.plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from slipway.plugin import (
|
|
6
|
+
ENTRY_POINT_GROUP,
|
|
7
|
+
PLUGIN_API_VERSION,
|
|
8
|
+
PluginError,
|
|
9
|
+
PluginRegistry,
|
|
10
|
+
StackPlugin,
|
|
11
|
+
builtin_registry,
|
|
12
|
+
discover_pypi_stack_packages,
|
|
13
|
+
pypi_package_metadata,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ENTRY_POINT_GROUP",
|
|
18
|
+
"PLUGIN_API_VERSION",
|
|
19
|
+
"PluginError",
|
|
20
|
+
"PluginRegistry",
|
|
21
|
+
"StackPlugin",
|
|
22
|
+
"builtin_registry",
|
|
23
|
+
"discover_pypi_stack_packages",
|
|
24
|
+
"pypi_package_metadata",
|
|
25
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Public plugin API for DevSpeed."""
|
|
2
|
+
|
|
3
|
+
from devspeed.plugins.base import PLUGIN_API_VERSION, PluginError, StackPlugin
|
|
4
|
+
from devspeed.plugins.registry import (
|
|
5
|
+
ENTRY_POINT_GROUP,
|
|
6
|
+
PluginRegistry,
|
|
7
|
+
builtin_registry,
|
|
8
|
+
discover_pypi_stack_packages,
|
|
9
|
+
pypi_package_metadata,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ENTRY_POINT_GROUP",
|
|
14
|
+
"PLUGIN_API_VERSION",
|
|
15
|
+
"PluginError",
|
|
16
|
+
"PluginRegistry",
|
|
17
|
+
"StackPlugin",
|
|
18
|
+
"builtin_registry",
|
|
19
|
+
"discover_pypi_stack_packages",
|
|
20
|
+
"pypi_package_metadata",
|
|
21
|
+
]
|
devspeed/plugins/base.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Backward-compatibility aliases for devspeed.plugins.registry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from slipway.plugins.registry import (
|
|
6
|
+
ENTRY_POINT_GROUP,
|
|
7
|
+
LEGACY_ENTRY_POINT_GROUP,
|
|
8
|
+
PLUGIN_API_VERSION,
|
|
9
|
+
PluginError,
|
|
10
|
+
PluginFactory,
|
|
11
|
+
PluginRegistry,
|
|
12
|
+
StackPlugin,
|
|
13
|
+
builtin_registry,
|
|
14
|
+
discover_pypi_stack_packages,
|
|
15
|
+
pypi_package_metadata,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"ENTRY_POINT_GROUP",
|
|
20
|
+
"LEGACY_ENTRY_POINT_GROUP",
|
|
21
|
+
"PLUGIN_API_VERSION",
|
|
22
|
+
"PluginError",
|
|
23
|
+
"PluginFactory",
|
|
24
|
+
"PluginRegistry",
|
|
25
|
+
"StackPlugin",
|
|
26
|
+
"builtin_registry",
|
|
27
|
+
"discover_pypi_stack_packages",
|
|
28
|
+
"pypi_package_metadata",
|
|
29
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Backward-compatibility aliases for devspeed.stacks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from slipway.stacks import (
|
|
6
|
+
STACKS,
|
|
7
|
+
PluginRegistry,
|
|
8
|
+
StackPlugin,
|
|
9
|
+
get_registry,
|
|
10
|
+
get_stack,
|
|
11
|
+
list_stacks,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"STACKS",
|
|
16
|
+
"PluginRegistry",
|
|
17
|
+
"StackPlugin",
|
|
18
|
+
"get_registry",
|
|
19
|
+
"get_stack",
|
|
20
|
+
"list_stacks",
|
|
21
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Shared rendering helpers for stack templates."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def postgres_service_yaml(
|
|
11
|
+
postgres: Mapping[str, Any],
|
|
12
|
+
project: str,
|
|
13
|
+
*,
|
|
14
|
+
database_url: bool = False,
|
|
15
|
+
) -> str:
|
|
16
|
+
"""Render the common Postgres service and named volume block."""
|
|
17
|
+
database_line = ""
|
|
18
|
+
if database_url:
|
|
19
|
+
database_line = (
|
|
20
|
+
f" - DATABASE_URL=postgres://{postgres['user']}:{postgres['password']}"
|
|
21
|
+
f"@postgres:5432/{postgres['db']}\n"
|
|
22
|
+
)
|
|
23
|
+
return f"""\
|
|
24
|
+
postgres:
|
|
25
|
+
image: postgres:16-alpine
|
|
26
|
+
ports:
|
|
27
|
+
- \"127.0.0.1:{postgres["port"]}:5432\"
|
|
28
|
+
|
|
29
|
+
environment:
|
|
30
|
+
- POSTGRES_DB={postgres["db"]}
|
|
31
|
+
- POSTGRES_USER={postgres["user"]}
|
|
32
|
+
- POSTGRES_PASSWORD={postgres["password"]}
|
|
33
|
+
{database_line} volumes:
|
|
34
|
+
- {project}_pgdata:/var/lib/postgresql/data
|
|
35
|
+
healthcheck:
|
|
36
|
+
test: [\"CMD-SHELL\", \"pg_isready -U {postgres["user"]}\"]
|
|
37
|
+
interval: 3s
|
|
38
|
+
timeout: 3s
|
|
39
|
+
retries: 10
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
volumes:
|
|
43
|
+
{project}_pgdata:
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def env_example(env_text: str) -> str:
|
|
48
|
+
"""Mask common local credentials while preserving useful connection examples."""
|
|
49
|
+
masked = re.sub(r"(://[^:]+:)[^@]+@", r"\1<password>@", env_text)
|
|
50
|
+
return re.sub(r"(?m)^(POSTGRES_PASSWORD=).*$", r"\1<password>", masked)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def lifecycle_command(
|
|
54
|
+
config: Mapping[str, Any],
|
|
55
|
+
default_install: str,
|
|
56
|
+
default_dev: str,
|
|
57
|
+
) -> str:
|
|
58
|
+
"""Build the app command, allowing config to override lifecycle steps."""
|
|
59
|
+
lifecycle = config.get("lifecycle", {})
|
|
60
|
+
if not isinstance(lifecycle, Mapping):
|
|
61
|
+
lifecycle = {}
|
|
62
|
+
install = lifecycle.get("install", default_install)
|
|
63
|
+
dev = lifecycle.get("dev", default_dev)
|
|
64
|
+
if not isinstance(install, str) or not isinstance(dev, str):
|
|
65
|
+
raise TypeError("lifecycle.install and lifecycle.dev must be strings")
|
|
66
|
+
return f'sh -c "{install} && {dev}"'
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Django and Postgres stack plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from devspeed.plugins.base import StackPlugin
|
|
9
|
+
|
|
10
|
+
from .common import lifecycle_command, postgres_service_yaml
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DjangoPostgresPlugin(StackPlugin):
|
|
14
|
+
"""Provide a hot-reloading Django application with Postgres."""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def name(self) -> str:
|
|
18
|
+
"""Return the stable configuration identifier."""
|
|
19
|
+
return "django-postgres"
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def description(self) -> str:
|
|
23
|
+
"""Return the stack description shown by the CLI."""
|
|
24
|
+
return "Django web app + Postgres"
|
|
25
|
+
|
|
26
|
+
def default_config(self, project_name: str) -> dict[str, Any]:
|
|
27
|
+
"""Return defaults for a Django project."""
|
|
28
|
+
return {
|
|
29
|
+
"project": project_name,
|
|
30
|
+
"stack": self.name,
|
|
31
|
+
"lifecycle": {
|
|
32
|
+
"install": "pip install --no-cache-dir -r requirements.txt",
|
|
33
|
+
"dev": "python manage.py migrate --noinput && python manage.py runserver 0.0.0.0:8000",
|
|
34
|
+
},
|
|
35
|
+
"services": {
|
|
36
|
+
"app": {"port": 8000, "python_version": "3.12"},
|
|
37
|
+
"postgres": {
|
|
38
|
+
"port": 5432,
|
|
39
|
+
"db": project_name.replace("-", "_"),
|
|
40
|
+
"user": "devspeed",
|
|
41
|
+
"password": "devspeed",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def starter_files(self, config: Mapping[str, Any]) -> dict[str, str]:
|
|
47
|
+
"""Return a minimal Django project for a new project."""
|
|
48
|
+
return {
|
|
49
|
+
"requirements.txt": "Django>=5.1,<6\npsycopg[binary]>=3.2,<4\n",
|
|
50
|
+
"manage.py": """import os
|
|
51
|
+
import sys
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
56
|
+
from django.core.management import execute_from_command_line
|
|
57
|
+
execute_from_command_line(sys.argv)
|
|
58
|
+
""",
|
|
59
|
+
"config/__init__.py": "",
|
|
60
|
+
"config/settings.py": """import os
|
|
61
|
+
|
|
62
|
+
SECRET_KEY = "devspeed-local-only"
|
|
63
|
+
DEBUG = True
|
|
64
|
+
ROOT_URLCONF = "config.urls"
|
|
65
|
+
ALLOWED_HOSTS = ["*"]
|
|
66
|
+
INSTALLED_APPS = ["django.contrib.contenttypes"]
|
|
67
|
+
MIDDLEWARE = []
|
|
68
|
+
DATABASES = {"default": {
|
|
69
|
+
"ENGINE": "django.db.backends.postgresql",
|
|
70
|
+
"NAME": os.getenv("POSTGRES_DB", "devspeed"),
|
|
71
|
+
"USER": os.getenv("POSTGRES_USER", "devspeed"),
|
|
72
|
+
"PASSWORD": os.getenv("POSTGRES_PASSWORD", "devspeed"),
|
|
73
|
+
"HOST": os.getenv("POSTGRES_HOST", "postgres"),
|
|
74
|
+
"PORT": "5432",
|
|
75
|
+
}}
|
|
76
|
+
""",
|
|
77
|
+
"config/urls.py": """from django.http import JsonResponse
|
|
78
|
+
from django.urls import path
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
urlpatterns = [path("", lambda request: JsonResponse({"message": "Your DevSpeed app is running"}))]
|
|
82
|
+
""",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
def compose_yaml(self, config: Mapping[str, Any]) -> str:
|
|
86
|
+
"""Render the Django and Postgres Compose services."""
|
|
87
|
+
services = config["services"]
|
|
88
|
+
app = services["app"]
|
|
89
|
+
postgres = services["postgres"]
|
|
90
|
+
project = config["project"]
|
|
91
|
+
app_command = lifecycle_command(
|
|
92
|
+
config,
|
|
93
|
+
"pip install --no-cache-dir -r requirements.txt",
|
|
94
|
+
f"python manage.py migrate --noinput && python manage.py runserver 0.0.0.0:{app['port']}",
|
|
95
|
+
)
|
|
96
|
+
postgres_yaml = postgres_service_yaml(postgres, project, database_url=False)
|
|
97
|
+
|
|
98
|
+
return f"""\
|
|
99
|
+
name: {project}
|
|
100
|
+
|
|
101
|
+
services:
|
|
102
|
+
app:
|
|
103
|
+
image: python:{app["python_version"]}-slim
|
|
104
|
+
working_dir: /app
|
|
105
|
+
volumes:
|
|
106
|
+
- ./:/app
|
|
107
|
+
command: {app_command}
|
|
108
|
+
ports:
|
|
109
|
+
- "{app["port"]}:{app["port"]}"
|
|
110
|
+
environment:
|
|
111
|
+
- DATABASE_URL=postgres://{postgres["user"]}:{postgres["password"]}@postgres:5432/{postgres["db"]}
|
|
112
|
+
- POSTGRES_DB={postgres["db"]}
|
|
113
|
+
- POSTGRES_USER={postgres["user"]}
|
|
114
|
+
- POSTGRES_PASSWORD={postgres["password"]}
|
|
115
|
+
- POSTGRES_HOST=postgres
|
|
116
|
+
depends_on:
|
|
117
|
+
postgres:
|
|
118
|
+
condition: service_healthy
|
|
119
|
+
|
|
120
|
+
{postgres_yaml}
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def env_file(self, config: Mapping[str, Any]) -> str:
|
|
124
|
+
"""Render the host Postgres connection variable."""
|
|
125
|
+
postgres = config["services"]["postgres"]
|
|
126
|
+
return (
|
|
127
|
+
f"DATABASE_URL=postgres://{postgres['user']}:{postgres['password']}"
|
|
128
|
+
f"@localhost:{postgres['port']}/{postgres['db']}\n"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def post_up_hints(self, config: Mapping[str, Any]) -> list[str]:
|
|
132
|
+
"""Return startup notes for the generated Django environment."""
|
|
133
|
+
app_port = config["services"]["app"]["port"]
|
|
134
|
+
return [
|
|
135
|
+
"App container installs requirements.txt and runs Django's development server.",
|
|
136
|
+
f"Django should be reachable at http://localhost:{app_port} once dependencies finish installing.",
|
|
137
|
+
"Make sure manage.py and requirements.txt are present at the project root.",
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
plugin = DjangoPostgresPlugin()
|
|
142
|
+
NAME = plugin.name
|
|
143
|
+
DESCRIPTION = plugin.description
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def default_config(project_name: str) -> dict[str, Any]:
|
|
147
|
+
"""Backward-compatible wrapper for the plugin method."""
|
|
148
|
+
return plugin.default_config(project_name)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def starter_files(config: Mapping[str, Any]) -> dict[str, str]:
|
|
152
|
+
"""Backward-compatible wrapper for the plugin method."""
|
|
153
|
+
return plugin.starter_files(config)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def compose_yaml(config: Mapping[str, Any]) -> str:
|
|
157
|
+
"""Backward-compatible wrapper for the plugin method."""
|
|
158
|
+
return plugin.compose_yaml(config)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def env_file(config: Mapping[str, Any]) -> str:
|
|
162
|
+
"""Backward-compatible wrapper for the plugin method."""
|
|
163
|
+
return plugin.env_file(config)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def post_up_hints(config: Mapping[str, Any]) -> list[str]:
|
|
167
|
+
"""Backward-compatible wrapper for the plugin method."""
|
|
168
|
+
return plugin.post_up_hints(config)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Express and MongoDB stack plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from devspeed.plugins.base import StackPlugin
|
|
9
|
+
|
|
10
|
+
from .common import lifecycle_command
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ExpressMongodbPlugin(StackPlugin):
|
|
14
|
+
"""Provide a Node.js Express application with MongoDB."""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def name(self) -> str:
|
|
18
|
+
return "express-mongodb"
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def description(self) -> str:
|
|
22
|
+
return "Node.js Express API + MongoDB"
|
|
23
|
+
|
|
24
|
+
def default_config(self, project_name: str) -> dict[str, Any]:
|
|
25
|
+
return {
|
|
26
|
+
"project": project_name,
|
|
27
|
+
"stack": self.name,
|
|
28
|
+
"lifecycle": {
|
|
29
|
+
"install": "npm install",
|
|
30
|
+
"dev": "npm run dev",
|
|
31
|
+
},
|
|
32
|
+
"services": {
|
|
33
|
+
"app": {"port": 3000, "node_version": "20"},
|
|
34
|
+
"mongodb": {
|
|
35
|
+
"port": 27017,
|
|
36
|
+
"user": "devspeed",
|
|
37
|
+
"password": "devspeed",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
def starter_files(self, config: Mapping[str, Any]) -> dict[str, str]:
|
|
43
|
+
return {
|
|
44
|
+
"package.json": '{\n "name": "devspeed-express",\n "version": "1.0.0",\n "scripts": { "dev": "node app.js" },\n "dependencies": { "express": "^4.19.0", "mongoose": "^8.0.0" }\n}',
|
|
45
|
+
"app.js": 'const express = require("express");\nconst mongoose = require("mongoose");\n\nconst app = express();\nconst port = 3000;\n\nmongoose.connect(process.env.MONGODB_URI).then(() => console.log("Connected to MongoDB"));\n\napp.get("/", (req, res) => res.json({ message: "Your DevSpeed app is running" }));\n\napp.listen(port, () => console.log(`App listening on port ${port}`));',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def compose_yaml(self, config: Mapping[str, Any]) -> str:
|
|
49
|
+
services = config["services"]
|
|
50
|
+
app = services["app"]
|
|
51
|
+
mongo = services["mongodb"]
|
|
52
|
+
project = config["project"]
|
|
53
|
+
command = lifecycle_command(
|
|
54
|
+
config,
|
|
55
|
+
"npm install",
|
|
56
|
+
"npm run dev",
|
|
57
|
+
)
|
|
58
|
+
return f"""\
|
|
59
|
+
name: {project}
|
|
60
|
+
|
|
61
|
+
services:
|
|
62
|
+
app:
|
|
63
|
+
image: node:{app["node_version"]}-slim
|
|
64
|
+
working_dir: /app
|
|
65
|
+
volumes:
|
|
66
|
+
- ./:/app
|
|
67
|
+
command: {command}
|
|
68
|
+
ports:
|
|
69
|
+
- "{app["port"]}:{app["port"]}"
|
|
70
|
+
environment:
|
|
71
|
+
- MONGODB_URI=mongodb://{mongo["user"]}:{mongo["password"]}@mongodb:27017/devspeed_db?authSource=admin
|
|
72
|
+
depends_on:
|
|
73
|
+
mongodb:
|
|
74
|
+
condition: service_healthy
|
|
75
|
+
|
|
76
|
+
mongodb:
|
|
77
|
+
image: mongo:6-alpine
|
|
78
|
+
ports:
|
|
79
|
+
- "{mongo["port"]}:27017"
|
|
80
|
+
environment:
|
|
81
|
+
- MONGO_INITDB_ROOT_USERNAME={mongo["user"]}
|
|
82
|
+
- MONGO_INITDB_ROOT_PASSWORD={mongo["password"]}
|
|
83
|
+
healthcheck:
|
|
84
|
+
test: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"]
|
|
85
|
+
interval: 3s
|
|
86
|
+
timeout: 3s
|
|
87
|
+
retries: 10
|
|
88
|
+
|
|
89
|
+
volumes:
|
|
90
|
+
{project}_mongodata:
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def env_file(self, config: Mapping[str, Any]) -> str:
|
|
94
|
+
mongo = config["services"]["mongodb"]
|
|
95
|
+
return f"MONGODB_URI=mongodb://{mongo['user']}:{mongo['password']}@localhost:{mongo['port']}/devspeed_db?authSource=admin\n"
|
|
96
|
+
|
|
97
|
+
def post_up_hints(self, config: Mapping[str, Any]) -> list[str]:
|
|
98
|
+
port = config["services"]["app"]["port"]
|
|
99
|
+
return [
|
|
100
|
+
"App container runs npm install and npm run dev.",
|
|
101
|
+
f"API should be reachable at http://localhost:{port} once dependencies finish installing.",
|
|
102
|
+
"Make sure app.js is configured to use MONGODB_URI.",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
plugin = ExpressMongodbPlugin()
|
|
107
|
+
NAME = plugin.name
|
|
108
|
+
DESCRIPTION = plugin.description
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def default_config(project_name: str) -> dict[str, Any]:
|
|
112
|
+
return plugin.default_config(project_name)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def starter_files(config: Mapping[str, Any]) -> dict[str, str]:
|
|
116
|
+
return plugin.starter_files(config)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def compose_yaml(config: Mapping[str, Any]) -> str:
|
|
120
|
+
return plugin.compose_yaml(config)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def env_file(config: Mapping[str, Any]) -> str:
|
|
124
|
+
return plugin.env_file(config)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def post_up_hints(config: Mapping[str, Any]) -> list[str]:
|
|
128
|
+
return plugin.post_up_hints(config)
|