shipit-cli 0.3.4__py3-none-any.whl → 0.4.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.
- shipit/cli.py +45 -6
- shipit/generator.py +6 -1
- shipit/providers/python.py +186 -18
- shipit/version.py +2 -2
- {shipit_cli-0.3.4.dist-info → shipit_cli-0.4.0.dist-info}/METADATA +1 -1
- {shipit_cli-0.3.4.dist-info → shipit_cli-0.4.0.dist-info}/RECORD +8 -8
- {shipit_cli-0.3.4.dist-info → shipit_cli-0.4.0.dist-info}/WHEEL +0 -0
- {shipit_cli-0.3.4.dist-info → shipit_cli-0.4.0.dist-info}/entry_points.txt +0 -0
shipit/cli.py
CHANGED
|
@@ -55,6 +55,7 @@ class Serve:
|
|
|
55
55
|
build: List["Step"]
|
|
56
56
|
deps: List["Package"]
|
|
57
57
|
commands: Dict[str, str]
|
|
58
|
+
cwd: Optional[str] = None
|
|
58
59
|
assets: Optional[Dict[str, str]] = None
|
|
59
60
|
prepare: Optional[List["PrepareStep"]] = None
|
|
60
61
|
workers: Optional[List[str]] = None
|
|
@@ -577,18 +578,33 @@ class LocalBuilder:
|
|
|
577
578
|
self.create_file(asset_path, assets[asset])
|
|
578
579
|
|
|
579
580
|
def build_prepare(self, serve: Serve) -> None:
|
|
580
|
-
app_dir = self.get_build_path()
|
|
581
581
|
self.prepare_bash_script.parent.mkdir(parents=True, exist_ok=True)
|
|
582
582
|
commands: List[str] = []
|
|
583
|
+
if serve.cwd:
|
|
584
|
+
commands.append(f"cd {serve.cwd}")
|
|
583
585
|
if serve.prepare:
|
|
584
586
|
for step in serve.prepare:
|
|
585
587
|
if isinstance(step, RunStep):
|
|
586
588
|
commands.append(step.command)
|
|
587
589
|
elif isinstance(step, WorkdirStep):
|
|
588
590
|
commands.append(f"cd {step.path}")
|
|
589
|
-
content = "#!/bin/bash\
|
|
590
|
-
|
|
591
|
+
content = "#!/bin/bash\n{body}".format(
|
|
592
|
+
body="\n".join(commands)
|
|
591
593
|
)
|
|
594
|
+
console.print(f"\n[bold]Created prepare.sh script to run before packaging ✅[/bold]")
|
|
595
|
+
manifest_panel = Panel(
|
|
596
|
+
Syntax(
|
|
597
|
+
content,
|
|
598
|
+
"bash",
|
|
599
|
+
theme="monokai",
|
|
600
|
+
background_color="default",
|
|
601
|
+
line_numbers=True,
|
|
602
|
+
),
|
|
603
|
+
box=box.SQUARE,
|
|
604
|
+
border_style="bright_black",
|
|
605
|
+
expand=False,
|
|
606
|
+
)
|
|
607
|
+
console.print(manifest_panel, markup=False, highlight=True)
|
|
592
608
|
self.prepare_bash_script.write_text(content)
|
|
593
609
|
self.prepare_bash_script.chmod(0o755)
|
|
594
610
|
|
|
@@ -739,15 +755,35 @@ class WasmerBuilder:
|
|
|
739
755
|
env_lines = ""
|
|
740
756
|
|
|
741
757
|
commands: List[str] = []
|
|
758
|
+
if serve.cwd:
|
|
759
|
+
commands.append(f"cd {serve.cwd}")
|
|
760
|
+
|
|
742
761
|
if serve.prepare:
|
|
743
762
|
for step in serve.prepare:
|
|
744
763
|
if isinstance(step, RunStep):
|
|
745
764
|
commands.append(step.command)
|
|
746
765
|
elif isinstance(step, WorkdirStep):
|
|
747
766
|
commands.append(f"cd {step.path}")
|
|
767
|
+
|
|
748
768
|
body = "\n".join(filter(None, [env_lines, *commands]))
|
|
769
|
+
content = f"#!/bin/bash\n\n{body}"
|
|
770
|
+
console.print(f"\n[bold]Created prepare.sh script to run before packaging ✅[/bold]")
|
|
771
|
+
manifest_panel = Panel(
|
|
772
|
+
Syntax(
|
|
773
|
+
content,
|
|
774
|
+
"bash",
|
|
775
|
+
theme="monokai",
|
|
776
|
+
background_color="default",
|
|
777
|
+
line_numbers=True,
|
|
778
|
+
),
|
|
779
|
+
box=box.SQUARE,
|
|
780
|
+
border_style="bright_black",
|
|
781
|
+
expand=False,
|
|
782
|
+
)
|
|
783
|
+
console.print(manifest_panel, markup=False, highlight=True)
|
|
784
|
+
|
|
749
785
|
(prepare_dir / "prepare.sh").write_text(
|
|
750
|
-
|
|
786
|
+
content,
|
|
751
787
|
)
|
|
752
788
|
(prepare_dir / "prepare.sh").chmod(0o755)
|
|
753
789
|
|
|
@@ -842,7 +878,8 @@ class WasmerBuilder:
|
|
|
842
878
|
command.add("module", program_binary["script"])
|
|
843
879
|
command.add("runner", "wasi")
|
|
844
880
|
wasi_args = table()
|
|
845
|
-
|
|
881
|
+
if serve.cwd:
|
|
882
|
+
wasi_args.add("cwd", serve.cwd)
|
|
846
883
|
wasi_args.add("main-args", parts[1:])
|
|
847
884
|
env = program_binary.get("env") or {}
|
|
848
885
|
if serve.env:
|
|
@@ -1024,6 +1061,7 @@ class Ctx:
|
|
|
1024
1061
|
build: List[str],
|
|
1025
1062
|
deps: List[str],
|
|
1026
1063
|
commands: Dict[str, str],
|
|
1064
|
+
cwd: Optional[str] = None,
|
|
1027
1065
|
assets: Optional[Dict[str, str]] = None,
|
|
1028
1066
|
prepare: Optional[List[str]] = None,
|
|
1029
1067
|
workers: Optional[List[str]] = None,
|
|
@@ -1043,6 +1081,7 @@ class Ctx:
|
|
|
1043
1081
|
name=name,
|
|
1044
1082
|
provider=provider,
|
|
1045
1083
|
build=build_refs,
|
|
1084
|
+
cwd=cwd,
|
|
1046
1085
|
assets=assets,
|
|
1047
1086
|
deps=dep_refs,
|
|
1048
1087
|
commands=commands,
|
|
@@ -1433,7 +1472,7 @@ def main() -> None:
|
|
|
1433
1472
|
app()
|
|
1434
1473
|
except Exception as e:
|
|
1435
1474
|
console.print(f"[bold red]{type(e).__name__}[/bold red]: {e}")
|
|
1436
|
-
raise e
|
|
1475
|
+
# raise e
|
|
1437
1476
|
|
|
1438
1477
|
|
|
1439
1478
|
if __name__ == "__main__":
|
shipit/generator.py
CHANGED
|
@@ -115,8 +115,10 @@ def generate_shipit(path: Path) -> str:
|
|
|
115
115
|
env_lines = ",\n".join([f' "{k}": {v}' for k, v in plan.env.items()])
|
|
116
116
|
assets_block = _render_assets(plan.assets)
|
|
117
117
|
mounts_block = None
|
|
118
|
+
attach_serve_names: list[str] = []
|
|
118
119
|
if plan.mounts:
|
|
119
|
-
mounts = filter(lambda m: m.attach_to_serve, plan.mounts)
|
|
120
|
+
mounts = list(filter(lambda m: m.attach_to_serve, plan.mounts))
|
|
121
|
+
attach_serve_names = [m.name for m in mounts]
|
|
120
122
|
mounts_block = ",\n".join([f" {m.name}" for m in mounts])
|
|
121
123
|
|
|
122
124
|
out: List[str] = []
|
|
@@ -131,6 +133,9 @@ def generate_shipit(path: Path) -> str:
|
|
|
131
133
|
out.append("serve(")
|
|
132
134
|
out.append(f' name="{plan.serve_name}",')
|
|
133
135
|
out.append(f' provider="{plan.provider}",')
|
|
136
|
+
# If app is mounted for serve, set cwd to the app serve path
|
|
137
|
+
if "app" in attach_serve_names:
|
|
138
|
+
out.append(' cwd=app["serve"],')
|
|
134
139
|
out.append(" build=[")
|
|
135
140
|
out.append(build_steps_block)
|
|
136
141
|
out.append(" ],")
|
shipit/providers/python.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from pathlib import Path
|
|
4
|
-
from typing import Dict, Optional
|
|
5
|
+
from typing import Dict, Optional, Set
|
|
6
|
+
from enum import Enum
|
|
5
7
|
|
|
6
8
|
from .base import (
|
|
7
9
|
DetectResult,
|
|
@@ -12,9 +14,140 @@ from .base import (
|
|
|
12
14
|
)
|
|
13
15
|
|
|
14
16
|
|
|
17
|
+
class PythonFramework(Enum):
|
|
18
|
+
Django = "django"
|
|
19
|
+
FastAPI = "fastapi"
|
|
20
|
+
Flask = "flask"
|
|
21
|
+
FastHTML = "python-fasthtml"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PythonServer(Enum):
|
|
25
|
+
Hypercorn = "hypercorn"
|
|
26
|
+
Uvicorn = "uvicorn"
|
|
27
|
+
# Gunicorn = "gunicorn"
|
|
28
|
+
Daphne = "daphne"
|
|
29
|
+
|
|
30
|
+
class DatabaseType(Enum):
|
|
31
|
+
MySQL = "mysql"
|
|
32
|
+
PostgreSQL = "postgresql"
|
|
33
|
+
|
|
34
|
+
|
|
15
35
|
class PythonProvider:
|
|
36
|
+
framework: Optional[PythonFramework] = None
|
|
37
|
+
server: Optional[PythonServer] = None
|
|
38
|
+
database: Optional[DatabaseType] = None
|
|
39
|
+
extra_dependencies: Set[str]
|
|
40
|
+
asgi_application: Optional[str] = None
|
|
41
|
+
wsgi_application: Optional[str] = None
|
|
42
|
+
|
|
16
43
|
def __init__(self, path: Path):
|
|
17
44
|
self.path = path
|
|
45
|
+
if _exists(self.path, ".python-version"):
|
|
46
|
+
python_version = (self.path / ".python-version").read_text().strip()
|
|
47
|
+
else:
|
|
48
|
+
python_version = "3.13"
|
|
49
|
+
self.default_python_version = python_version
|
|
50
|
+
self.extra_dependencies = set()
|
|
51
|
+
|
|
52
|
+
pg_deps = {
|
|
53
|
+
"asyncpg",
|
|
54
|
+
"aiopg",
|
|
55
|
+
"psycopg",
|
|
56
|
+
"psycopg2",
|
|
57
|
+
"psycopg-binary",
|
|
58
|
+
"psycopg2-binary"}
|
|
59
|
+
mysql_deps = {"mysqlclient", "pymysql", "mysql-connector-python", "aiomysql"}
|
|
60
|
+
found_deps = self.check_deps(
|
|
61
|
+
"django",
|
|
62
|
+
"fastapi",
|
|
63
|
+
"flask",
|
|
64
|
+
"python-fasthtml",
|
|
65
|
+
"daphne",
|
|
66
|
+
"hypercorn",
|
|
67
|
+
"uvicorn",
|
|
68
|
+
# "gunicorn",
|
|
69
|
+
*mysql_deps,
|
|
70
|
+
*pg_deps,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# ASGI/WSGI Server
|
|
74
|
+
if "uvicorn" in found_deps:
|
|
75
|
+
server = PythonServer.Uvicorn
|
|
76
|
+
elif "hypercorn" in found_deps:
|
|
77
|
+
server = PythonServer.Hypercorn
|
|
78
|
+
# elif "gunicorn" in found_deps:
|
|
79
|
+
# server = PythonServer.Gunicorn
|
|
80
|
+
elif "daphne" in found_deps:
|
|
81
|
+
server = PythonServer.Daphne
|
|
82
|
+
else:
|
|
83
|
+
server = None
|
|
84
|
+
self.server = server
|
|
85
|
+
|
|
86
|
+
# Set framework
|
|
87
|
+
if _exists(self.path, "manage.py") and ("django" in found_deps):
|
|
88
|
+
framework = PythonFramework.Django
|
|
89
|
+
# Find the settings.py file using glob
|
|
90
|
+
settings_file = next(self.path.glob( "**/settings.py"))
|
|
91
|
+
if settings_file:
|
|
92
|
+
asgi_match = re.search(r"ASGI_APPLICATION\s*=\s*['\"](.*)['\"]", settings_file.read_text())
|
|
93
|
+
if asgi_match:
|
|
94
|
+
self.asgi_application = asgi_match.group(1)
|
|
95
|
+
else:
|
|
96
|
+
wsgi_match = re.search(r"WSGI_APPLICATION\s*=\s*['\"](.*)['\"]", settings_file.read_text())
|
|
97
|
+
if wsgi_match:
|
|
98
|
+
self.wsgi_application = wsgi_match.group(1)
|
|
99
|
+
|
|
100
|
+
if not self.server:
|
|
101
|
+
if self.asgi_application:
|
|
102
|
+
self.extra_dependencies = {"uvicorn"}
|
|
103
|
+
self.server = PythonServer.Uvicorn
|
|
104
|
+
elif self.wsgi_application:
|
|
105
|
+
# gunicorn can't run with Wasmer atm
|
|
106
|
+
self.extra_dependencies = {"uvicorn"}
|
|
107
|
+
self.server = PythonServer.Uvicorn
|
|
108
|
+
elif "fastapi" in found_deps:
|
|
109
|
+
framework = PythonFramework.FastAPI
|
|
110
|
+
if not self.server:
|
|
111
|
+
self.extra_dependencies = {"uvicorn"}
|
|
112
|
+
self.server = PythonServer.Uvicorn
|
|
113
|
+
elif "flask" in found_deps:
|
|
114
|
+
framework = PythonFramework.Flask
|
|
115
|
+
elif "fastapi" in found_deps:
|
|
116
|
+
framework = PythonFramework.FastAPI
|
|
117
|
+
elif "flask" in found_deps:
|
|
118
|
+
framework = PythonFramework.Flask
|
|
119
|
+
elif "python-fasthtml" in found_deps:
|
|
120
|
+
framework = PythonFramework.FastHTML
|
|
121
|
+
else:
|
|
122
|
+
framework = None
|
|
123
|
+
self.framework = framework
|
|
124
|
+
|
|
125
|
+
# Database
|
|
126
|
+
if mysql_deps & found_deps:
|
|
127
|
+
database = DatabaseType.MySQL
|
|
128
|
+
elif pg_deps & found_deps:
|
|
129
|
+
database = DatabaseType.PostgreSQL
|
|
130
|
+
else:
|
|
131
|
+
database = None
|
|
132
|
+
self.database = database
|
|
133
|
+
|
|
134
|
+
def check_deps(self, *deps: str) -> Set[str]:
|
|
135
|
+
deps = set([dep.lower() for dep in deps])
|
|
136
|
+
initial_deps = set(deps)
|
|
137
|
+
for file in ["requirements.txt", "pyproject.toml"]:
|
|
138
|
+
if _exists(self.path, file):
|
|
139
|
+
for line in (self.path / file).read_text().splitlines():
|
|
140
|
+
for dep in set(deps):
|
|
141
|
+
if dep in line.lower():
|
|
142
|
+
deps.remove(dep)
|
|
143
|
+
if not deps:
|
|
144
|
+
break
|
|
145
|
+
if not deps:
|
|
146
|
+
break
|
|
147
|
+
if not deps:
|
|
148
|
+
break
|
|
149
|
+
return initial_deps-deps
|
|
150
|
+
|
|
18
151
|
@classmethod
|
|
19
152
|
def name(cls) -> str:
|
|
20
153
|
return "python"
|
|
@@ -37,16 +170,11 @@ class PythonProvider:
|
|
|
37
170
|
return "python"
|
|
38
171
|
|
|
39
172
|
def dependencies(self) -> list[DependencySpec]:
|
|
40
|
-
if _exists(self.path, ".python-version"):
|
|
41
|
-
python_version = (self.path / ".python-version").read_text().strip()
|
|
42
|
-
else:
|
|
43
|
-
python_version = "3.13"
|
|
44
|
-
|
|
45
173
|
return [
|
|
46
174
|
DependencySpec(
|
|
47
175
|
"python",
|
|
48
176
|
env_var="SHIPIT_PYTHON_VERSION",
|
|
49
|
-
default_version=
|
|
177
|
+
default_version=self.default_python_version,
|
|
50
178
|
use_in_build=True,
|
|
51
179
|
use_in_serve=True,
|
|
52
180
|
),
|
|
@@ -63,7 +191,8 @@ class PythonProvider:
|
|
|
63
191
|
"cross_platform = getenv(\"SHIPIT_PYTHON_CROSS_PLATFORM\")\n"
|
|
64
192
|
"python_extra_index_url = getenv(\"SHIPIT_PYTHON_EXTRA_INDEX_URL\")\n"
|
|
65
193
|
"precompile_python = getenv(\"SHIPIT_PYTHON_PRECOMPILE\") in [\"true\", \"True\", \"TRUE\", \"1\", \"on\", \"yes\", \"y\", \"Y\", \"YES\", \"On\", \"ON\"]\n"
|
|
66
|
-
"python_cross_packages_path = venv[\"build\"] + f\"/lib/python{python_version}/site-packages\""
|
|
194
|
+
"python_cross_packages_path = venv[\"build\"] + f\"/lib/python{python_version}/site-packages\"\n"
|
|
195
|
+
"python_serve_path = \"{}/lib/python{}/site-packages\".format(venv[\"serve\"], python_version)\n"
|
|
67
196
|
)
|
|
68
197
|
|
|
69
198
|
def build_steps(self) -> list[str]:
|
|
@@ -78,13 +207,15 @@ class PythonProvider:
|
|
|
78
207
|
input_files.append("uv.lock")
|
|
79
208
|
extra_args = " --locked"
|
|
80
209
|
inputs = ", ".join([f"\"{input}\"" for input in input_files])
|
|
81
|
-
|
|
210
|
+
extra_deps = ", ".join([f"{dep}" for dep in self.extra_dependencies])
|
|
211
|
+
steps += list(filter(None, [
|
|
82
212
|
"env(UV_PROJECT_ENVIRONMENT=local_venv[\"build\"] if cross_platform else venv[\"build\"])",
|
|
83
213
|
"run(f\"uv sync --compile --python python{python_version} --no-managed-python" + extra_args + "\", inputs=[" + inputs + "], group=\"install\")",
|
|
214
|
+
f"run(\"uv add {extra_deps}\", inputs=[\"pyproject.toml\"], group=\"install\")" if extra_deps else None,
|
|
84
215
|
"run(f\"uv pip compile pyproject.toml --python-version={python_version} --universal --extra-index-url {python_extra_index_url} --index-url=https://pypi.org/simple --emit-index-url --only-binary :all: -o cross-requirements.txt\", inputs=[\"pyproject.toml\"], outputs=[\"cross-requirements.txt\"]) if cross_platform else None",
|
|
85
|
-
"run(f\"uvx pip install -r cross-requirements.txt --target {python_cross_packages_path} --platform {cross_platform} --only-binary=:all: --python-version={python_version} --compile\") if cross_platform else None",
|
|
216
|
+
f"run(f\"uvx pip install -r cross-requirements.txt {extra_deps} --target {{python_cross_packages_path}} --platform {{cross_platform}} --only-binary=:all: --python-version={{python_version}} --compile\") if cross_platform else None",
|
|
86
217
|
"run(\"rm cross-requirements.txt\") if cross_platform else None",
|
|
87
|
-
]
|
|
218
|
+
]))
|
|
88
219
|
if _exists(self.path, "requirements.txt"):
|
|
89
220
|
steps += [
|
|
90
221
|
"env(UV_PROJECT_ENVIRONMENT=local_venv[\"build\"] if cross_platform else venv[\"build\"])",
|
|
@@ -103,24 +234,57 @@ class PythonProvider:
|
|
|
103
234
|
|
|
104
235
|
def prepare_steps(self) -> Optional[list[str]]:
|
|
105
236
|
return [
|
|
106
|
-
'workdir(app["serve"])',
|
|
107
237
|
'run("echo \\\"Precompiling Python code...\\\"") if precompile_python else None',
|
|
108
|
-
'run("python -m compileall -o 2
|
|
238
|
+
'run(f"python -m compileall -o 2 {python_serve_path}") if precompile_python else None',
|
|
109
239
|
'run("echo \\\"Precompiling package code...\\\"") if precompile_python else None',
|
|
110
|
-
'run("python -m compileall -o 2 .") if precompile_python else None',
|
|
240
|
+
'run("python -m compileall -o 2 {}".format(app["serve"])) if precompile_python else None',
|
|
111
241
|
]
|
|
112
242
|
|
|
113
243
|
def commands(self) -> Dict[str, str]:
|
|
114
|
-
if
|
|
115
|
-
start_cmd =
|
|
244
|
+
if self.framework == PythonFramework.Django:
|
|
245
|
+
start_cmd = None
|
|
246
|
+
if self.server == PythonServer.Daphne and self.asgi_application:
|
|
247
|
+
asgi_application = format_app_import(self.asgi_application)
|
|
248
|
+
start_cmd = f'"python -m daphne {asgi_application} --bind 0.0.0.0 --port 8000"'
|
|
249
|
+
elif self.server == PythonServer.Uvicorn:
|
|
250
|
+
if self.asgi_application:
|
|
251
|
+
asgi_application = format_app_import(self.asgi_application)
|
|
252
|
+
start_cmd = f'"python -m uvicorn {asgi_application} --host 0.0.0.0 --port 8000"'
|
|
253
|
+
elif self.wsgi_application:
|
|
254
|
+
wsgi_application = format_app_import(self.wsgi_application)
|
|
255
|
+
start_cmd = f'"python -m uvicorn {wsgi_application} --interface=wsgi --host 0.0.0.0 --port 8000"'
|
|
256
|
+
# elif self.server == PythonServer.Gunicorn:
|
|
257
|
+
# start_cmd = f'"python -m gunicorn {self.wsgi_application} --bind 0.0.0.0 --port 8000"'
|
|
258
|
+
if not start_cmd:
|
|
259
|
+
# We run the default runserver command if no server is specified
|
|
260
|
+
start_cmd = '"python manage.py runserver 0.0.0.0:8000"'
|
|
116
261
|
migrate_cmd = '"python manage.py migrate"'
|
|
117
262
|
return {"start": start_cmd, "after_deploy": migrate_cmd}
|
|
263
|
+
elif self.framework == PythonFramework.FastAPI:
|
|
264
|
+
if _exists(self.path, "main.py"):
|
|
265
|
+
path = "main:app"
|
|
266
|
+
elif _exists(self.path, "src/main.py"):
|
|
267
|
+
path = "src.main:app"
|
|
268
|
+
|
|
269
|
+
if self.server == PythonServer.Uvicorn:
|
|
270
|
+
start_cmd = f'"python -m uvicorn {path} --host 0.0.0.0 --port 8000"'
|
|
271
|
+
elif self.server == PythonServer.Hypercorn:
|
|
272
|
+
start_cmd = f'"python -m hypercorn {path} --bind 0.0.0.0:8000"'
|
|
273
|
+
else:
|
|
274
|
+
start_cmd = '"python -c \'print(\\\"No start command detected, please provide a start command manually\\\")\'"'
|
|
275
|
+
return {"start": start_cmd}
|
|
276
|
+
elif self.framework == PythonFramework.FastHTML:
|
|
277
|
+
if _exists(self.path, "main.py"):
|
|
278
|
+
path = "main:app"
|
|
279
|
+
elif _exists(self.path, "src/main.py"):
|
|
280
|
+
path = "src.main:app"
|
|
281
|
+
start_cmd = f'"python -m uvicorn {path} --host 0.0.0.0 --port 8000"'
|
|
118
282
|
elif _exists(self.path, "main.py"):
|
|
119
283
|
start_cmd = '"python main.py"'
|
|
120
284
|
elif _exists(self.path, "src/main.py"):
|
|
121
285
|
start_cmd = '"python src/main.py"'
|
|
122
286
|
else:
|
|
123
|
-
start_cmd = '"python -c \'print(\\\"
|
|
287
|
+
start_cmd = '"python -c \'print(\\\"No start command detected, please provide a start command manually\\\")\'"'
|
|
124
288
|
return {"start": start_cmd}
|
|
125
289
|
|
|
126
290
|
def assets(self) -> Optional[Dict[str, str]]:
|
|
@@ -137,5 +301,9 @@ class PythonProvider:
|
|
|
137
301
|
# For Django projects, generate an empty env dict to surface the field
|
|
138
302
|
# in the Shipit file. Other Python projects omit it by default.
|
|
139
303
|
return {
|
|
140
|
-
"PYTHONPATH": "
|
|
304
|
+
"PYTHONPATH": "python_serve_path"
|
|
141
305
|
}
|
|
306
|
+
|
|
307
|
+
def format_app_import(asgi_application: str) -> str:
|
|
308
|
+
# Transform "mysite.asgi.application" to "mysite.asgi:application" using regex
|
|
309
|
+
return re.sub(r"\.([^.]+)$", r":\1", asgi_application)
|
shipit/version.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
shipit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
shipit/cli.py,sha256=
|
|
3
|
-
shipit/generator.py,sha256=
|
|
4
|
-
shipit/version.py,sha256=
|
|
2
|
+
shipit/cli.py,sha256=5b5e1H-SscXBraZXxJ5cVs_IJSNqjWUt2IjcZtOdALE,49615
|
|
3
|
+
shipit/generator.py,sha256=4pezEo4OzpDFSFqiFpGCpdwo72VkItS490R_f2rIz2k,5755
|
|
4
|
+
shipit/version.py,sha256=zmMUF3ZPl6yX0INjIEDY59a3aBdXE4OLXagX09Ji9cE,95
|
|
5
5
|
shipit/assets/php/php.ini,sha256=f4irndAjB4GuuouEImRkNV22Q-yw1KqR-43jAMDw730,2531
|
|
6
6
|
shipit/providers/base.py,sha256=a_5VA1tV4_QbH83yjPCTHsNR23EJT2CiKUpWA_pu_lo,2373
|
|
7
7
|
shipit/providers/gatsby.py,sha256=uwNjIJloS9JwKXqkbhihgdTTpJL4iL4bLCZ5kuzqqNs,2138
|
|
@@ -10,10 +10,10 @@ shipit/providers/laravel.py,sha256=rDpfx7RyF4sK0xxDAWefX0IiguU2xdgEXP2jJp1Jdzo,2
|
|
|
10
10
|
shipit/providers/mkdocs.py,sha256=QJFNt7QWMvYbWeo7WjOgFVKGVcdUnUmUNfFbe4c8ThM,2812
|
|
11
11
|
shipit/providers/node_static.py,sha256=K55BXkNz4QXSXR2wdg5diP6HLpmksC70ph16zox7v6Y,2309
|
|
12
12
|
shipit/providers/php.py,sha256=Hmtv47K5qtYbQ3v9SSk-_KTNlhXedStg2MxhTTOK9ac,2594
|
|
13
|
-
shipit/providers/python.py,sha256=
|
|
13
|
+
shipit/providers/python.py,sha256=KnDH7nB1Ipz_oAqXPBz7FbEo3s7kwvQOhOZSHKCpltw,13430
|
|
14
14
|
shipit/providers/registry.py,sha256=UisII1dr24ZxmDD8GnpTsyNwPN9W8MnAHQ1Px1iJ-OQ,661
|
|
15
15
|
shipit/providers/staticfile.py,sha256=Y4oqw6dNDU2crzcWQ5SEgnXHoDy0CXRntABwlgdf1mo,1827
|
|
16
|
-
shipit_cli-0.
|
|
17
|
-
shipit_cli-0.
|
|
18
|
-
shipit_cli-0.
|
|
19
|
-
shipit_cli-0.
|
|
16
|
+
shipit_cli-0.4.0.dist-info/METADATA,sha256=_B-0rn-0ASJDMFL4faoV5mTzOSpi5ge5bdJuFjio3tc,462
|
|
17
|
+
shipit_cli-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
18
|
+
shipit_cli-0.4.0.dist-info/entry_points.txt,sha256=7AE1NjSrHaSDfbfsRRO50KKnHFTbB0Imsccd1WynzAQ,72
|
|
19
|
+
shipit_cli-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|