shipit-cli 0.1.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/__init__.py +0 -0
- shipit/assets/php/php.ini +103 -0
- shipit/cli.py +1281 -0
- shipit/generator.py +148 -0
- shipit/providers/base.py +68 -0
- shipit/providers/gatsby.py +64 -0
- shipit/providers/hugo.py +47 -0
- shipit/providers/laravel.py +74 -0
- shipit/providers/mkdocs.py +81 -0
- shipit/providers/node_static.py +65 -0
- shipit/providers/php.py +73 -0
- shipit/providers/python.py +104 -0
- shipit/providers/registry.py +26 -0
- shipit/providers/staticfile.py +61 -0
- shipit/version.py +5 -0
- shipit_cli-0.1.0.dist-info/METADATA +13 -0
- shipit_cli-0.1.0.dist-info/RECORD +19 -0
- shipit_cli-0.1.0.dist-info/WHEEL +4 -0
- shipit_cli-0.1.0.dist-info/entry_points.txt +2 -0
shipit/generator.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from shipit.providers.base import DependencySpec, Provider, ProviderPlan, DetectResult
|
|
7
|
+
from shipit.providers.registry import providers as registry_providers
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _providers() -> list[Provider]:
|
|
11
|
+
# Load providers from modular registry
|
|
12
|
+
return registry_providers()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def detect_provider(path: Path) -> Provider:
|
|
16
|
+
matches: list[tuple[Provider, DetectResult]] = []
|
|
17
|
+
for p in _providers():
|
|
18
|
+
res = p.detect(path)
|
|
19
|
+
if res:
|
|
20
|
+
matches.append((p, res))
|
|
21
|
+
if not matches:
|
|
22
|
+
# Default to static site as the safest fallback
|
|
23
|
+
from shipit.providers.staticfile import StaticFileProvider
|
|
24
|
+
|
|
25
|
+
return StaticFileProvider()
|
|
26
|
+
# Highest score wins; tie-breaker by order
|
|
27
|
+
matches.sort(key=lambda x: x[1].score, reverse=True)
|
|
28
|
+
return matches[0][0]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _sanitize_alias(name: str) -> str:
|
|
32
|
+
# Keep it predictable and valid in Starlark: letters, numbers, underscore
|
|
33
|
+
# Remove dashes to keep prior style (e.g., staticwebserver)
|
|
34
|
+
allowed = [c if c.isalnum() or c == "_" else "" for c in name]
|
|
35
|
+
alias = "".join(allowed)
|
|
36
|
+
return alias.replace("-", "")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _emit_dependencies_declarations(
|
|
40
|
+
deps: List[DependencySpec],
|
|
41
|
+
) -> tuple[str, List[str], List[str]]:
|
|
42
|
+
lines: List[str] = []
|
|
43
|
+
declared: set[str] = set()
|
|
44
|
+
serve_vars: List[str] = []
|
|
45
|
+
build_vars: List[str] = []
|
|
46
|
+
|
|
47
|
+
for dep in deps:
|
|
48
|
+
alias = dep.alias or _sanitize_alias(dep.name)
|
|
49
|
+
|
|
50
|
+
# Track serve variables in order of appearance (deduped)
|
|
51
|
+
if dep.use_in_serve and alias not in serve_vars:
|
|
52
|
+
serve_vars.append(alias)
|
|
53
|
+
if dep.use_in_build and alias not in build_vars:
|
|
54
|
+
build_vars.append(alias)
|
|
55
|
+
|
|
56
|
+
# Only declare each dependency once
|
|
57
|
+
if alias in declared:
|
|
58
|
+
continue
|
|
59
|
+
declared.add(alias)
|
|
60
|
+
|
|
61
|
+
version_var = None
|
|
62
|
+
if dep.env_var:
|
|
63
|
+
default = f' or "{dep.default_version}"' if dep.default_version else ""
|
|
64
|
+
version_key = alias + "_version"
|
|
65
|
+
lines.append(f'{version_key} = getenv("{dep.env_var}"){default}')
|
|
66
|
+
version_var = version_key
|
|
67
|
+
if version_var:
|
|
68
|
+
lines.append(f'{alias} = dep("{dep.name}", {version_var})')
|
|
69
|
+
else:
|
|
70
|
+
lines.append(f'{alias} = dep("{dep.name}")')
|
|
71
|
+
|
|
72
|
+
return "\n".join(lines), serve_vars, build_vars
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _render_assets(assets: Optional[Dict[str, str]]) -> Optional[str]:
|
|
76
|
+
if not assets:
|
|
77
|
+
return None
|
|
78
|
+
inner = ",\n".join([f' "{k}": {v}' for k, v in assets.items()])
|
|
79
|
+
return f"{{\n{inner}\n }}"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def generate_shipit(path: Path) -> str:
|
|
83
|
+
provider = detect_provider(path)
|
|
84
|
+
provider.initialize(path)
|
|
85
|
+
|
|
86
|
+
# Collect parts
|
|
87
|
+
plan = ProviderPlan(
|
|
88
|
+
serve_name=provider.serve_name(path),
|
|
89
|
+
provider=provider.provider_kind(path),
|
|
90
|
+
declarations=provider.declarations(path),
|
|
91
|
+
dependencies=provider.dependencies(path),
|
|
92
|
+
build_steps=provider.build_steps(path),
|
|
93
|
+
prepare=provider.prepare_steps(path),
|
|
94
|
+
commands=provider.commands(path),
|
|
95
|
+
assets=provider.assets(path),
|
|
96
|
+
mounts=provider.mounts(path),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Declare dependency variables (combined) and collect serve deps
|
|
100
|
+
dep_block, serve_dep_vars, build_dep_vars = _emit_dependencies_declarations(
|
|
101
|
+
plan.dependencies
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Compose serve(...) body
|
|
105
|
+
# Auto-insert a use(...) step at the beginning if not explicitly provided
|
|
106
|
+
build_steps: List[str] = list(plan.build_steps)
|
|
107
|
+
if build_dep_vars and not any("use(" in s for s in build_steps):
|
|
108
|
+
build_steps.insert(0, f"use({', '.join(build_dep_vars)})")
|
|
109
|
+
|
|
110
|
+
build_steps_block = ",\n".join([f" {s}" for s in build_steps])
|
|
111
|
+
deps_array = ", ".join(serve_dep_vars)
|
|
112
|
+
commands_lines = ",\n".join([f' "{k}": {v}' for k, v in plan.commands.items()])
|
|
113
|
+
assets_block = _render_assets(plan.assets)
|
|
114
|
+
mounts_block = None
|
|
115
|
+
if plan.mounts:
|
|
116
|
+
mounts_block = ",\n".join([f" {k}: {v}" for k, v in plan.mounts.items()])
|
|
117
|
+
|
|
118
|
+
out: List[str] = []
|
|
119
|
+
if dep_block:
|
|
120
|
+
out.append(dep_block)
|
|
121
|
+
out.append("")
|
|
122
|
+
if plan.declarations:
|
|
123
|
+
out.append(plan.declarations)
|
|
124
|
+
out.append("")
|
|
125
|
+
out.append("serve(")
|
|
126
|
+
out.append(f' name="{plan.serve_name}",')
|
|
127
|
+
out.append(f' provider="{plan.provider}",')
|
|
128
|
+
out.append(" build=[")
|
|
129
|
+
out.append(build_steps_block)
|
|
130
|
+
out.append(" ],")
|
|
131
|
+
if assets_block:
|
|
132
|
+
out.append(" assets=" + assets_block + ",")
|
|
133
|
+
out.append(f" deps=[{deps_array}],")
|
|
134
|
+
if plan.prepare:
|
|
135
|
+
prepare_steps_block = ",\n".join([f" {s}" for s in plan.prepare])
|
|
136
|
+
out.append(" prepare=[")
|
|
137
|
+
out.append(prepare_steps_block)
|
|
138
|
+
out.append(" ],")
|
|
139
|
+
out.append(" commands = {")
|
|
140
|
+
out.append(commands_lines)
|
|
141
|
+
out.append(" },")
|
|
142
|
+
if mounts_block:
|
|
143
|
+
out.append(" mounts={")
|
|
144
|
+
out.append(mounts_block)
|
|
145
|
+
out.append(" },")
|
|
146
|
+
out.append(")")
|
|
147
|
+
out.append("")
|
|
148
|
+
return "\n".join(out)
|
shipit/providers/base.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, List, Optional, Protocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class DetectResult:
|
|
10
|
+
name: str
|
|
11
|
+
score: int # Higher score wins when multiple providers match
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Provider(Protocol):
|
|
15
|
+
def name(self) -> str: ...
|
|
16
|
+
def detect(self, path: Path) -> Optional[DetectResult]: ...
|
|
17
|
+
def initialize(self, path: Path) -> None: ...
|
|
18
|
+
# Structured plan steps
|
|
19
|
+
def serve_name(self, path: Path) -> str: ...
|
|
20
|
+
def provider_kind(self, path: Path) -> str: ...
|
|
21
|
+
def dependencies(self, path: Path) -> list["DependencySpec"]: ...
|
|
22
|
+
def declarations(self, path: Path) -> Optional[str]: ...
|
|
23
|
+
def build_steps(self, path: Path) -> list[str]: ...
|
|
24
|
+
# Prepare: list of Starlark step calls (currently only run(...))
|
|
25
|
+
def prepare_steps(self, path: Path) -> Optional[List[str]]: ...
|
|
26
|
+
def commands(self, path: Path) -> Dict[str, str]: ...
|
|
27
|
+
def assets(self, path: Path) -> Optional[Dict[str, str]]: ...
|
|
28
|
+
def mounts(self, path: Path) -> Optional[Dict[str, str]]: ...
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class DependencySpec:
|
|
33
|
+
name: str
|
|
34
|
+
env_var: Optional[str] = None
|
|
35
|
+
default_version: Optional[str] = None
|
|
36
|
+
alias: Optional[str] = None # Variable name in Shipit plan
|
|
37
|
+
use_in_build: bool = False
|
|
38
|
+
use_in_serve: bool = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class ProviderPlan:
|
|
43
|
+
serve_name: str
|
|
44
|
+
provider: str
|
|
45
|
+
declarations: Optional[str] = None
|
|
46
|
+
dependencies: List[DependencySpec] = field(default_factory=list)
|
|
47
|
+
build_steps: List[str] = field(default_factory=list)
|
|
48
|
+
prepare: Optional[List[str]] = None
|
|
49
|
+
commands: Dict[str, str] = field(default_factory=dict)
|
|
50
|
+
assets: Optional[Dict[str, str]] = None
|
|
51
|
+
mounts: Optional[Dict[str, str]] = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _exists(path: Path, *candidates: str) -> bool:
|
|
55
|
+
return any((path / c).exists() for c in candidates)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _has_dependency(pkg_json: Path, dep: str) -> bool:
|
|
59
|
+
try:
|
|
60
|
+
import json
|
|
61
|
+
|
|
62
|
+
data = json.loads(pkg_json.read_text())
|
|
63
|
+
for section in ("dependencies", "devDependencies", "peerDependencies"):
|
|
64
|
+
if dep in data.get(section, {}):
|
|
65
|
+
return True
|
|
66
|
+
except Exception:
|
|
67
|
+
return False
|
|
68
|
+
return False
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Optional
|
|
5
|
+
|
|
6
|
+
from .base import DetectResult, DependencySpec, Provider, _exists, _has_dependency
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GatsbyProvider:
|
|
10
|
+
def name(self) -> str:
|
|
11
|
+
return "gatsby"
|
|
12
|
+
|
|
13
|
+
def detect(self, path: Path) -> Optional[DetectResult]:
|
|
14
|
+
pkg = path / "package.json"
|
|
15
|
+
if not pkg.exists():
|
|
16
|
+
return None
|
|
17
|
+
if _exists(path, "gatsby-config.js", "gatsby-config.ts") or _has_dependency(
|
|
18
|
+
pkg, "gatsby"
|
|
19
|
+
):
|
|
20
|
+
return DetectResult(self.name(), 90)
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
def initialize(self, path: Path) -> None:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def serve_name(self, path: Path) -> str:
|
|
27
|
+
return path.name
|
|
28
|
+
|
|
29
|
+
def provider_kind(self, path: Path) -> str:
|
|
30
|
+
return "staticsite"
|
|
31
|
+
|
|
32
|
+
def declarations(self, path: Path) -> Optional[str]:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
def dependencies(self, path: Path) -> list[DependencySpec]:
|
|
36
|
+
return [
|
|
37
|
+
DependencySpec(
|
|
38
|
+
"node",
|
|
39
|
+
env_var="SHIPIT_NODE_VERSION",
|
|
40
|
+
default_version="22",
|
|
41
|
+
use_in_build=True,
|
|
42
|
+
),
|
|
43
|
+
DependencySpec("npm", use_in_build=True),
|
|
44
|
+
DependencySpec("static-web-server", env_var="SHIPIT_SWS_VERSION", use_in_serve=True),
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
def build_steps(self, path: Path) -> list[str]:
|
|
48
|
+
return [
|
|
49
|
+
"run(\"npm install\", inputs=[\"package.json\", \"package-lock.json\"], group=\"install\")",
|
|
50
|
+
"copy(\".\", \".\", ignore=[\"node_modules\", \".git\"])",
|
|
51
|
+
"run(\"npm run build\", outputs=[\"public\"], group=\"build\")",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
def prepare_steps(self, path: Path) -> Optional[list[str]]:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
def commands(self, path: Path) -> Dict[str, str]:
|
|
58
|
+
return {"start": '"static-web-server --root /app/public"'}
|
|
59
|
+
|
|
60
|
+
def assets(self, path: Path) -> Optional[Dict[str, str]]:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
def mounts(self, path: Path) -> Optional[Dict[str, str]]:
|
|
64
|
+
return None
|
shipit/providers/hugo.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Optional
|
|
5
|
+
|
|
6
|
+
from .base import DetectResult, DependencySpec, Provider, _exists
|
|
7
|
+
from .staticfile import StaticFileProvider
|
|
8
|
+
|
|
9
|
+
class HugoProvider(StaticFileProvider):
|
|
10
|
+
static_dir = "public"
|
|
11
|
+
|
|
12
|
+
def name(self) -> str:
|
|
13
|
+
return "hugo"
|
|
14
|
+
|
|
15
|
+
def detect(self, path: Path) -> Optional[DetectResult]:
|
|
16
|
+
if _exists(path, "hugo.toml", "hugo.json", "hugo.yaml", "hugo.yml"):
|
|
17
|
+
return DetectResult(self.name(), 80)
|
|
18
|
+
if (
|
|
19
|
+
_exists(path, "config.toml", "config.json", "config.yaml", "config.yml")
|
|
20
|
+
and _exists(path, "content")
|
|
21
|
+
and (_exists(path, "static") or _exists(path, "themes"))
|
|
22
|
+
):
|
|
23
|
+
return DetectResult(self.name(), 40)
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
def serve_name(self, path: Path) -> str:
|
|
27
|
+
return path.name
|
|
28
|
+
|
|
29
|
+
def provider_kind(self, path: Path) -> str:
|
|
30
|
+
return "staticsite"
|
|
31
|
+
|
|
32
|
+
def dependencies(self, path: Path) -> list[DependencySpec]:
|
|
33
|
+
return [
|
|
34
|
+
DependencySpec(
|
|
35
|
+
"hugo",
|
|
36
|
+
env_var="SHIPIT_HUGO_VERSION",
|
|
37
|
+
default_version="0.149.0",
|
|
38
|
+
use_in_build=True,
|
|
39
|
+
),
|
|
40
|
+
*super().dependencies(path),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def build_steps(self, path: Path) -> list[str]:
|
|
44
|
+
return [
|
|
45
|
+
'copy(".", ".", ignore=[".git"])',
|
|
46
|
+
'run("hugo build", outputs=["public"], group="build")',
|
|
47
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Optional
|
|
5
|
+
|
|
6
|
+
from .base import DetectResult, DependencySpec, Provider, _exists
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LaravelProvider:
|
|
10
|
+
def name(self) -> str:
|
|
11
|
+
return "laravel"
|
|
12
|
+
|
|
13
|
+
def detect(self, path: Path) -> Optional[DetectResult]:
|
|
14
|
+
if _exists(path, "artisan") and _exists(path, "composer.json"):
|
|
15
|
+
return DetectResult(self.name(), 95)
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
def initialize(self, path: Path) -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def serve_name(self, path: Path) -> str:
|
|
22
|
+
return path.name
|
|
23
|
+
|
|
24
|
+
def provider_kind(self, path: Path) -> str:
|
|
25
|
+
return "php"
|
|
26
|
+
|
|
27
|
+
def dependencies(self, path: Path) -> list[DependencySpec]:
|
|
28
|
+
return [
|
|
29
|
+
DependencySpec(
|
|
30
|
+
"php",
|
|
31
|
+
env_var="SHIPIT_PHP_VERSION",
|
|
32
|
+
default_version="8.3",
|
|
33
|
+
use_in_build=True,
|
|
34
|
+
use_in_serve=True,
|
|
35
|
+
),
|
|
36
|
+
DependencySpec("composer", use_in_build=True),
|
|
37
|
+
DependencySpec("pie", use_in_build=True),
|
|
38
|
+
DependencySpec("pnpm", use_in_build=True),
|
|
39
|
+
DependencySpec("bash", use_in_serve=True),
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
def declarations(self, path: Path) -> Optional[str]:
|
|
43
|
+
return "HOME = getenv(\"HOME\")"
|
|
44
|
+
|
|
45
|
+
def build_steps(self, path: Path) -> list[str]:
|
|
46
|
+
return [
|
|
47
|
+
"env(HOME=HOME, COMPOSER_FUND=\"0\")",
|
|
48
|
+
"run(\"pie install php/pdo_pgsql\")",
|
|
49
|
+
"run(\"composer install --optimize-autoloader --no-scripts --no-interaction\", inputs=[\"composer.json\", \"composer.lock\", \"artisan\"], outputs=[\".\"], group=\"install\")",
|
|
50
|
+
"run(\"pnpm install\", inputs=[\"package.json\", \"package-lock.json\"], outputs=[\".\"], group=\"install\")",
|
|
51
|
+
"copy(\".\", \".\", ignore=[\".git\"])",
|
|
52
|
+
"run(\"pnpm run build\", outputs=[\".\"], group=\"build\")",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
def prepare_steps(self, path: Path) -> Optional[list[str]]:
|
|
56
|
+
return [
|
|
57
|
+
'run("mkdir -p storage/framework/{sessions,views,cache,testing} storage/logs bootstrap/cache")',
|
|
58
|
+
'run("php artisan config:cache")',
|
|
59
|
+
'run("php artisan event:cache")',
|
|
60
|
+
'run("php artisan route:cache")',
|
|
61
|
+
'run("php artisan view:cache")',
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
def commands(self, path: Path) -> Dict[str, str]:
|
|
65
|
+
return {
|
|
66
|
+
"start": '"php -S localhost:8080 -t public"',
|
|
67
|
+
"after_deploy": '"php artisan migrate"',
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def assets(self, path: Path) -> Optional[Dict[str, str]]:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
def mounts(self, path: Path) -> Optional[Dict[str, str]]:
|
|
74
|
+
return None
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Optional
|
|
5
|
+
|
|
6
|
+
from .base import DetectResult, DependencySpec, Provider, _exists
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MkdocsProvider:
|
|
10
|
+
def name(self) -> str:
|
|
11
|
+
return "mkdocs"
|
|
12
|
+
|
|
13
|
+
def detect(self, path: Path) -> Optional[DetectResult]:
|
|
14
|
+
if _exists(path, "mkdocs.yml", "mkdocs.yaml"):
|
|
15
|
+
return DetectResult(self.name(), 85)
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
def initialize(self, path: Path) -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def serve_name(self, path: Path) -> str:
|
|
22
|
+
return path.name
|
|
23
|
+
|
|
24
|
+
def provider_kind(self, path: Path) -> str:
|
|
25
|
+
return "mkdocs-site"
|
|
26
|
+
|
|
27
|
+
def dependencies(self, path: Path) -> list[DependencySpec]:
|
|
28
|
+
return [
|
|
29
|
+
DependencySpec(
|
|
30
|
+
"python",
|
|
31
|
+
env_var="SHIPIT_PYTHON_VERSION",
|
|
32
|
+
default_version="3.13",
|
|
33
|
+
use_in_build=True,
|
|
34
|
+
),
|
|
35
|
+
DependencySpec(
|
|
36
|
+
"uv",
|
|
37
|
+
env_var="SHIPIT_UV_VERSION",
|
|
38
|
+
default_version="0.8.15",
|
|
39
|
+
use_in_build=True,
|
|
40
|
+
),
|
|
41
|
+
DependencySpec(
|
|
42
|
+
"static-web-server",
|
|
43
|
+
env_var="SHIPIT_SWS_VERSION",
|
|
44
|
+
default_version="2.38.0",
|
|
45
|
+
use_in_serve=True,
|
|
46
|
+
),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
def declarations(self, path: Path) -> Optional[str]:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
def build_steps(self, path: Path) -> list[str]:
|
|
53
|
+
has_requirements = _exists(path, "requirements.txt")
|
|
54
|
+
if has_requirements:
|
|
55
|
+
install_lines = [
|
|
56
|
+
"run(\"uv init --no-managed-python\", inputs=[], outputs=[\".\"], group=\"install\")",
|
|
57
|
+
"run(f\"uv add -r requirements.txt\", inputs=[\"requirements.txt\"], outputs=[\".venv\"], group=\"install\")",
|
|
58
|
+
]
|
|
59
|
+
else:
|
|
60
|
+
install_lines = [
|
|
61
|
+
"mkdocs_version = getenv(\"SHIPIT_MKDOCS_VERSION\") or \"1.6.1\"",
|
|
62
|
+
"run(\"uv init --no-managed-python\", inputs=[], outputs=[\".venv\"], group=\"install\")",
|
|
63
|
+
"run(f\"uv add mkdocs=={mkdocs_version}\", group=\"install\")",
|
|
64
|
+
]
|
|
65
|
+
return [
|
|
66
|
+
*install_lines,
|
|
67
|
+
"copy(\".\", \".\", ignore=[\".venv\", \".git\", \"__pycache__\"])",
|
|
68
|
+
"run(\"uv run mkdocs build\", outputs=[\".\"], group=\"build\")",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
def prepare_steps(self, path: Path) -> Optional[list[str]]:
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
def commands(self, path: Path) -> Dict[str, str]:
|
|
75
|
+
return {"start": '"static-web-server --root {}".format(buildpath("site"))'}
|
|
76
|
+
|
|
77
|
+
def assets(self, path: Path) -> Optional[Dict[str, str]]:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
def mounts(self, path: Path) -> Optional[Dict[str, str]]:
|
|
81
|
+
return None
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Optional
|
|
5
|
+
|
|
6
|
+
from .base import DetectResult, DependencySpec, Provider, _exists, _has_dependency
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NodeStaticProvider:
|
|
10
|
+
def name(self) -> str:
|
|
11
|
+
return "node-static"
|
|
12
|
+
|
|
13
|
+
def detect(self, path: Path) -> Optional[DetectResult]:
|
|
14
|
+
pkg = path / "package.json"
|
|
15
|
+
if not pkg.exists():
|
|
16
|
+
return None
|
|
17
|
+
static_generators = ["astro", "vite", "next", "nuxt"]
|
|
18
|
+
if any(_has_dependency(pkg, dep) for dep in static_generators):
|
|
19
|
+
return DetectResult(self.name(), 40)
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
def initialize(self, path: Path) -> None:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def serve_name(self, path: Path) -> str:
|
|
26
|
+
return path.name
|
|
27
|
+
|
|
28
|
+
def provider_kind(self, path: Path) -> str:
|
|
29
|
+
return "staticsite"
|
|
30
|
+
|
|
31
|
+
def dependencies(self, path: Path) -> list[DependencySpec]:
|
|
32
|
+
return [
|
|
33
|
+
DependencySpec(
|
|
34
|
+
"node",
|
|
35
|
+
env_var="SHIPIT_NODE_VERSION",
|
|
36
|
+
default_version="22",
|
|
37
|
+
use_in_build=True,
|
|
38
|
+
),
|
|
39
|
+
DependencySpec("npm", use_in_build=True),
|
|
40
|
+
DependencySpec("static-web-server", use_in_serve=True),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def declarations(self, path: Path) -> Optional[str]:
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
def build_steps(self, path: Path) -> list[str]:
|
|
47
|
+
output_dir = "dist" if (path / "dist").exists() else "public"
|
|
48
|
+
return [
|
|
49
|
+
"run(\"npm install\", inputs=[\"package.json\", \"package-lock.json\"], group=\"install\")",
|
|
50
|
+
"copy(\".\", \".\", ignore=[\"node_modules\", \".git\"])",
|
|
51
|
+
f"run(\"npm run build\", outputs=[\"{output_dir}\"], group=\"build\")",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
def prepare_steps(self, path: Path) -> Optional[list[str]]:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
def commands(self, path: Path) -> Dict[str, str]:
|
|
58
|
+
output_dir = "dist" if (path / "dist").exists() else "public"
|
|
59
|
+
return {"start": f'"static-web-server --root /app/{output_dir}"'}
|
|
60
|
+
|
|
61
|
+
def assets(self, path: Path) -> Optional[Dict[str, str]]:
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
def mounts(self, path: Path) -> Optional[Dict[str, str]]:
|
|
65
|
+
return None
|
shipit/providers/php.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, Optional
|
|
5
|
+
|
|
6
|
+
from .base import DetectResult, DependencySpec, Provider, _exists
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PhpProvider:
|
|
10
|
+
def name(self) -> str:
|
|
11
|
+
return "php"
|
|
12
|
+
|
|
13
|
+
def detect(self, path: Path) -> Optional[DetectResult]:
|
|
14
|
+
if _exists(path, "composer.json") and _exists(path, "public/index.php"):
|
|
15
|
+
return DetectResult(self.name(), 60)
|
|
16
|
+
if _exists(path, "index.php") and not _exists(path, "composer.json"):
|
|
17
|
+
return DetectResult(self.name(), 10)
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
def initialize(self, path: Path) -> None:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
def serve_name(self, path: Path) -> str:
|
|
24
|
+
return path.name
|
|
25
|
+
|
|
26
|
+
def provider_kind(self, path: Path) -> str:
|
|
27
|
+
return "php"
|
|
28
|
+
|
|
29
|
+
def has_composer(self, path: Path) -> bool:
|
|
30
|
+
return _exists(path, "composer.json", "composer.lock")
|
|
31
|
+
|
|
32
|
+
def dependencies(self, path: Path) -> list[DependencySpec]:
|
|
33
|
+
deps = [
|
|
34
|
+
DependencySpec(
|
|
35
|
+
"php",
|
|
36
|
+
env_var="SHIPIT_PHP_VERSION",
|
|
37
|
+
default_version="8.3",
|
|
38
|
+
use_in_build=True,
|
|
39
|
+
use_in_serve=True,
|
|
40
|
+
),
|
|
41
|
+
]
|
|
42
|
+
if self.has_composer(path):
|
|
43
|
+
deps.append(DependencySpec("composer", use_in_build=True))
|
|
44
|
+
deps.append(DependencySpec("bash", use_in_serve=True))
|
|
45
|
+
return deps
|
|
46
|
+
|
|
47
|
+
def declarations(self, path: Path) -> Optional[str]:
|
|
48
|
+
return "HOME = getenv(\"HOME\")"
|
|
49
|
+
|
|
50
|
+
def build_steps(self, path: Path) -> list[str]:
|
|
51
|
+
steps = []
|
|
52
|
+
|
|
53
|
+
if self.has_composer(path):
|
|
54
|
+
steps.append("env(HOME=HOME, COMPOSER_FUND=\"0\")")
|
|
55
|
+
steps.append("run(\"composer install --optimize-autoloader --no-scripts --no-interaction\", inputs=[\"composer.json\", \"composer.lock\"], outputs=[\".\"], group=\"install\")")
|
|
56
|
+
|
|
57
|
+
steps.append("copy(\".\", \".\", ignore=[\".git\"])")
|
|
58
|
+
return steps
|
|
59
|
+
|
|
60
|
+
def prepare_steps(self, path: Path) -> Optional[list[str]]:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
def commands(self, path: Path) -> Dict[str, str]:
|
|
64
|
+
if _exists(path, "public/index.php"):
|
|
65
|
+
return {"start": '"php -S localhost:8080 -t public"'}
|
|
66
|
+
elif _exists(path, "index.php"):
|
|
67
|
+
return {"start": '"php -S localhost:8080" -t .'}
|
|
68
|
+
|
|
69
|
+
def assets(self, path: Path) -> Optional[Dict[str, str]]:
|
|
70
|
+
return {"php.ini": "get_asset(\"php/php.ini\")"}
|
|
71
|
+
|
|
72
|
+
def mounts(self, path: Path) -> Optional[Dict[str, str]]:
|
|
73
|
+
return None
|