shipit-cli 0.13.4__py3-none-any.whl → 0.15.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/builders/__init__.py +9 -0
- shipit/builders/base.py +14 -0
- shipit/builders/docker.py +250 -0
- shipit/builders/local.py +161 -0
- shipit/cli.py +291 -1323
- shipit/generator.py +56 -36
- shipit/procfile.py +4 -72
- shipit/providers/base.py +50 -11
- shipit/providers/hugo.py +64 -14
- shipit/providers/jekyll.py +123 -0
- shipit/providers/laravel.py +40 -31
- shipit/providers/mkdocs.py +34 -19
- shipit/providers/node_static.py +219 -136
- shipit/providers/php.py +42 -38
- shipit/providers/python.py +284 -228
- shipit/providers/registry.py +2 -2
- shipit/providers/staticfile.py +45 -26
- shipit/providers/wordpress.py +26 -27
- shipit/runners/__init__.py +9 -0
- shipit/runners/base.py +17 -0
- shipit/runners/local.py +105 -0
- shipit/runners/wasmer.py +470 -0
- shipit/shipit_types.py +103 -0
- shipit/ui.py +14 -0
- shipit/utils.py +10 -0
- shipit/version.py +2 -2
- {shipit_cli-0.13.4.dist-info → shipit_cli-0.15.0.dist-info}/METADATA +6 -3
- shipit_cli-0.15.0.dist-info/RECORD +34 -0
- {shipit_cli-0.13.4.dist-info → shipit_cli-0.15.0.dist-info}/WHEEL +1 -1
- shipit_cli-0.13.4.dist-info/RECORD +0 -22
- {shipit_cli-0.13.4.dist-info → shipit_cli-0.15.0.dist-info}/entry_points.txt +0 -0
shipit/providers/node_static.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
import json
|
|
4
2
|
import yaml
|
|
5
3
|
from pathlib import Path
|
|
6
|
-
from typing import Dict, Optional, Any, Set
|
|
4
|
+
from typing import Dict, Optional, Any, Set, List
|
|
7
5
|
from enum import Enum
|
|
8
6
|
from semantic_version import Version, NpmSpec
|
|
7
|
+
from pydantic import Field
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
from .base import (
|
|
@@ -15,9 +14,10 @@ from .base import (
|
|
|
15
14
|
MountSpec,
|
|
16
15
|
ServiceSpec,
|
|
17
16
|
VolumeSpec,
|
|
18
|
-
|
|
17
|
+
Config,
|
|
19
18
|
)
|
|
20
|
-
from .staticfile import StaticFileProvider
|
|
19
|
+
from .staticfile import StaticFileProvider, StaticFileConfig
|
|
20
|
+
from pydantic_settings import SettingsConfigDict
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class PackageManager(Enum):
|
|
@@ -46,7 +46,7 @@ class PackageManager(Enum):
|
|
|
46
46
|
|
|
47
47
|
return DependencySpec(
|
|
48
48
|
dep_name,
|
|
49
|
-
|
|
49
|
+
var_name=f"config.{dep_name.lower()}_version",
|
|
50
50
|
default_version=default_version,
|
|
51
51
|
)
|
|
52
52
|
|
|
@@ -78,8 +78,8 @@ class PackageManager(Enum):
|
|
|
78
78
|
def install_command(self, has_lockfile: bool = False) -> str:
|
|
79
79
|
return {
|
|
80
80
|
PackageManager.NPM: f"npm {'ci' if has_lockfile else 'install'}",
|
|
81
|
-
PackageManager.PNPM:
|
|
82
|
-
PackageManager.YARN:
|
|
81
|
+
PackageManager.PNPM: "pnpm install",
|
|
82
|
+
PackageManager.YARN: "yarn install",
|
|
83
83
|
PackageManager.BUN: f"bun install{' --no-save' if has_lockfile else ''}",
|
|
84
84
|
}[self]
|
|
85
85
|
|
|
@@ -105,6 +105,7 @@ class StaticGenerator(Enum):
|
|
|
105
105
|
VITE = "vite"
|
|
106
106
|
NEXT = "next"
|
|
107
107
|
GATSBY = "gatsby"
|
|
108
|
+
DOCUSAURUS_OLD = "docusaurus-old"
|
|
108
109
|
DOCUSAURUS = "docusaurus"
|
|
109
110
|
SVELTE = "svelte"
|
|
110
111
|
REMIX = "remix"
|
|
@@ -113,55 +114,146 @@ class StaticGenerator(Enum):
|
|
|
113
114
|
REMIX_OLD = "remix-old"
|
|
114
115
|
REMIX_V2 = "remix-v2"
|
|
115
116
|
|
|
117
|
+
def get_output_dir(self) -> str:
|
|
118
|
+
if self == StaticGenerator.NEXT:
|
|
119
|
+
return "out"
|
|
120
|
+
elif self == StaticGenerator.NUXT_V3:
|
|
121
|
+
return ".output/public"
|
|
122
|
+
elif self in [
|
|
123
|
+
StaticGenerator.ASTRO,
|
|
124
|
+
StaticGenerator.VITE,
|
|
125
|
+
StaticGenerator.NUXT_OLD,
|
|
126
|
+
StaticGenerator.REMIX_V2,
|
|
127
|
+
]:
|
|
128
|
+
return "dist"
|
|
129
|
+
elif self == StaticGenerator.GATSBY:
|
|
130
|
+
return "public"
|
|
131
|
+
elif self == StaticGenerator.REMIX_OLD:
|
|
132
|
+
return "build/client"
|
|
133
|
+
elif self in [
|
|
134
|
+
StaticGenerator.DOCUSAURUS,
|
|
135
|
+
StaticGenerator.DOCUSAURUS_OLD,
|
|
136
|
+
StaticGenerator.SVELTE,
|
|
137
|
+
]:
|
|
138
|
+
return "build"
|
|
139
|
+
else:
|
|
140
|
+
return "dist"
|
|
116
141
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
142
|
+
@classmethod
|
|
143
|
+
def detect_generators_from_command(cls, build_command) -> List["StaticGenerator"]:
|
|
144
|
+
commands = {
|
|
145
|
+
"gatsby": [StaticGenerator.GATSBY],
|
|
146
|
+
"astro": [StaticGenerator.ASTRO],
|
|
147
|
+
"remix-ssg": [StaticGenerator.REMIX_OLD],
|
|
148
|
+
"vite": [StaticGenerator.VITE, StaticGenerator.REMIX_V2],
|
|
149
|
+
"docusaurus": [StaticGenerator.DOCUSAURUS, StaticGenerator.DOCUSAURUS_OLD],
|
|
150
|
+
"next": [StaticGenerator.NEXT],
|
|
151
|
+
"nuxi": [StaticGenerator.NUXT_V3],
|
|
152
|
+
"nuxt": [StaticGenerator.NUXT_OLD],
|
|
153
|
+
"svelte-kit": [StaticGenerator.SVELTE],
|
|
154
|
+
}
|
|
155
|
+
build_command = build_command.split(" ")[0]
|
|
156
|
+
if build_command in commands:
|
|
157
|
+
return commands[build_command]
|
|
158
|
+
else:
|
|
159
|
+
return []
|
|
160
|
+
|
|
161
|
+
def build_command(self) -> str:
|
|
162
|
+
return {
|
|
163
|
+
StaticGenerator.GATSBY: "gatsby build",
|
|
164
|
+
StaticGenerator.ASTRO: "astro build",
|
|
165
|
+
StaticGenerator.REMIX_OLD: "remix-ssg build",
|
|
166
|
+
StaticGenerator.REMIX_V2: "vite build",
|
|
167
|
+
StaticGenerator.DOCUSAURUS: "docusaurus build",
|
|
168
|
+
StaticGenerator.DOCUSAURUS_OLD: "docusaurus build",
|
|
169
|
+
StaticGenerator.SVELTE: "svelte-kit build",
|
|
170
|
+
StaticGenerator.VITE: "vite build",
|
|
171
|
+
StaticGenerator.NEXT: "next export",
|
|
172
|
+
StaticGenerator.NUXT_V3: "nuxi generate",
|
|
173
|
+
StaticGenerator.NUXT_OLD: "nuxt generate",
|
|
174
|
+
StaticGenerator.REMIX: "remix-ssg build",
|
|
175
|
+
}[self]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class NodeStaticConfig(StaticFileConfig):
|
|
179
|
+
model_config = SettingsConfigDict(extra="ignore", env_prefix="SHIPIT_")
|
|
180
|
+
|
|
181
|
+
package_manager: Optional[PackageManager] = None
|
|
182
|
+
extra_dependencies: Set[str] = Field(default_factory=set)
|
|
121
183
|
static_generator: Optional[StaticGenerator] = None
|
|
184
|
+
build_command: Optional[str] = None
|
|
185
|
+
node_version: Optional[str] = "22"
|
|
186
|
+
npm_version: Optional[str] = None
|
|
187
|
+
pnpm_version: Optional[str] = None
|
|
188
|
+
yarn_version: Optional[str] = None
|
|
189
|
+
bun_version: Optional[str] = None
|
|
122
190
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
191
|
+
|
|
192
|
+
class NodeStaticProvider(StaticFileProvider):
|
|
193
|
+
only_build: bool = False
|
|
194
|
+
|
|
195
|
+
def __init__(
|
|
196
|
+
self, path: Path, config: NodeStaticConfig, only_build: bool = False
|
|
197
|
+
):
|
|
198
|
+
super().__init__(path, config)
|
|
199
|
+
self.only_build = only_build
|
|
200
|
+
|
|
201
|
+
@classmethod
|
|
202
|
+
def load_config(
|
|
203
|
+
cls, path: Path, base_config: Config
|
|
204
|
+
) -> NodeStaticConfig:
|
|
205
|
+
config = NodeStaticConfig(**base_config.model_dump())
|
|
206
|
+
if not config.package_manager:
|
|
207
|
+
if (path / "package-lock.json").exists():
|
|
208
|
+
config.package_manager = PackageManager.NPM
|
|
209
|
+
elif (path / "pnpm-lock.yaml").exists():
|
|
210
|
+
config.package_manager = PackageManager.PNPM
|
|
211
|
+
elif (path / "yarn.lock").exists():
|
|
212
|
+
config.package_manager = PackageManager.YARN
|
|
213
|
+
elif (path / "bun.lockb").exists():
|
|
214
|
+
config.package_manager = PackageManager.BUN
|
|
215
|
+
else:
|
|
216
|
+
config.package_manager = PackageManager.PNPM
|
|
217
|
+
|
|
218
|
+
package_json = cls.parse_package_json(path)
|
|
219
|
+
|
|
220
|
+
if not config.static_generator:
|
|
221
|
+
if cls.has_dependency(package_json, "gatsby"):
|
|
222
|
+
config.static_generator = StaticGenerator.GATSBY
|
|
223
|
+
elif cls.has_dependency(package_json, "astro"):
|
|
224
|
+
config.static_generator = StaticGenerator.ASTRO
|
|
225
|
+
elif cls.has_dependency(package_json, "docusaurus"):
|
|
226
|
+
config.static_generator = StaticGenerator.DOCUSAURUS_OLD
|
|
227
|
+
elif cls.has_dependency(package_json, "@docusaurus/core"):
|
|
228
|
+
config.static_generator = StaticGenerator.DOCUSAURUS
|
|
229
|
+
elif cls.has_dependency(package_json, "svelte"):
|
|
230
|
+
config.static_generator = StaticGenerator.SVELTE
|
|
231
|
+
elif cls.has_dependency(
|
|
232
|
+
package_json, "@remix-run/dev", "1"
|
|
233
|
+
) or cls.has_dependency(package_json, "@remix-run/dev", "0"):
|
|
234
|
+
config.static_generator = StaticGenerator.REMIX_OLD
|
|
235
|
+
elif cls.has_dependency(package_json, "@remix-run/dev"):
|
|
236
|
+
config.static_generator = StaticGenerator.REMIX_V2
|
|
237
|
+
elif cls.has_dependency(package_json, "vite"):
|
|
238
|
+
config.static_generator = StaticGenerator.VITE
|
|
239
|
+
elif cls.has_dependency(package_json, "next"):
|
|
240
|
+
config.static_generator = StaticGenerator.NEXT
|
|
241
|
+
elif cls.has_dependency(package_json, "nuxt", "2") or cls.has_dependency(
|
|
242
|
+
package_json, "nuxt", "1"
|
|
243
|
+
):
|
|
244
|
+
config.static_generator = StaticGenerator.NUXT_OLD
|
|
245
|
+
elif cls.has_dependency(package_json, "nuxt"):
|
|
246
|
+
config.static_generator = StaticGenerator.NUXT_V3
|
|
247
|
+
|
|
248
|
+
if not config.build_command:
|
|
249
|
+
config.build_command = cls.get_build_command(
|
|
250
|
+
package_json, config.package_manager, config.static_generator
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if not config.static_dir:
|
|
254
|
+
config.static_dir = config.static_generator.get_output_dir()
|
|
255
|
+
|
|
256
|
+
return config
|
|
165
257
|
|
|
166
258
|
@classmethod
|
|
167
259
|
def parse_package_json(cls, path: Path) -> Optional[Dict[str, Any]]:
|
|
@@ -205,8 +297,27 @@ class NodeStaticProvider(StaticFileProvider):
|
|
|
205
297
|
|
|
206
298
|
@classmethod
|
|
207
299
|
def detect(
|
|
208
|
-
cls, path: Path,
|
|
300
|
+
cls, path: Path, config: Config
|
|
209
301
|
) -> Optional[DetectResult]:
|
|
302
|
+
if config.commands.install:
|
|
303
|
+
# Detect this provider from the install command
|
|
304
|
+
if config.commands.install in ["npm install", "npm ci", "npm i", "pnpm install", "pnpm ci", "pnpm i", "yarn install", "yarn ci", "yarn i", "bun install", "bun ci", "bun i"]:
|
|
305
|
+
return DetectResult(cls.name(), 40)
|
|
306
|
+
|
|
307
|
+
if config.commands.build:
|
|
308
|
+
if config.commands.build.startswith("npm run "):
|
|
309
|
+
return DetectResult(cls.name(), 40)
|
|
310
|
+
elif config.commands.build.startswith("pnpm run "):
|
|
311
|
+
return DetectResult(cls.name(), 40)
|
|
312
|
+
elif config.commands.build.startswith("yarn run "):
|
|
313
|
+
return DetectResult(cls.name(), 40)
|
|
314
|
+
elif config.commands.build.startswith("bun run "):
|
|
315
|
+
return DetectResult(cls.name(), 40)
|
|
316
|
+
|
|
317
|
+
static_generators = StaticGenerator.detect_generators_from_command(config.commands.build)
|
|
318
|
+
if static_generators:
|
|
319
|
+
return DetectResult(cls.name(), 40)
|
|
320
|
+
|
|
210
321
|
package_json = cls.parse_package_json(path)
|
|
211
322
|
if not package_json:
|
|
212
323
|
return None
|
|
@@ -217,6 +328,7 @@ class NodeStaticProvider(StaticFileProvider):
|
|
|
217
328
|
"nuxt",
|
|
218
329
|
"gatsby",
|
|
219
330
|
"svelte",
|
|
331
|
+
"docusaurus",
|
|
220
332
|
"@docusaurus/core",
|
|
221
333
|
"@remix-run/dev",
|
|
222
334
|
]
|
|
@@ -224,109 +336,80 @@ class NodeStaticProvider(StaticFileProvider):
|
|
|
224
336
|
return DetectResult(cls.name(), 40)
|
|
225
337
|
return None
|
|
226
338
|
|
|
227
|
-
def
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def serve_name(self) -> str:
|
|
231
|
-
return self.path.name
|
|
232
|
-
|
|
233
|
-
def platform(self) -> Optional[str]:
|
|
234
|
-
return self.static_generator.value if self.static_generator else None
|
|
339
|
+
def serve_name(self) -> Optional[str]:
|
|
340
|
+
return None
|
|
235
341
|
|
|
236
342
|
def dependencies(self) -> list[DependencySpec]:
|
|
237
|
-
package_manager_dep = self.package_manager.as_dependency(self.path)
|
|
343
|
+
package_manager_dep = self.config.package_manager.as_dependency(self.path)
|
|
238
344
|
package_manager_dep.use_in_build = True
|
|
239
345
|
return [
|
|
240
346
|
DependencySpec(
|
|
241
347
|
"node",
|
|
242
|
-
|
|
243
|
-
default_version="22",
|
|
348
|
+
var_name="config.node_version",
|
|
244
349
|
use_in_build=True,
|
|
245
350
|
),
|
|
246
351
|
package_manager_dep,
|
|
247
|
-
|
|
352
|
+
*super().dependencies(),
|
|
248
353
|
]
|
|
249
354
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return "build/client"
|
|
268
|
-
elif self.static_generator in [
|
|
269
|
-
StaticGenerator.DOCUSAURUS,
|
|
270
|
-
StaticGenerator.SVELTE,
|
|
271
|
-
]:
|
|
272
|
-
return "build"
|
|
273
|
-
else:
|
|
274
|
-
return "dist"
|
|
275
|
-
|
|
276
|
-
def get_build_command(self) -> bool:
|
|
277
|
-
if not self.package_json:
|
|
278
|
-
return False
|
|
279
|
-
build_command = self.package_json.get("scripts", {}).get("build")
|
|
280
|
-
if build_command:
|
|
281
|
-
return self.package_manager.run_command("build")
|
|
282
|
-
if self.static_generator == StaticGenerator.GATSBY:
|
|
283
|
-
return self.package_manager.run_execute_command("gatsby build")
|
|
284
|
-
if self.static_generator == StaticGenerator.ASTRO:
|
|
285
|
-
return self.package_manager.run_execute_command("astro build")
|
|
286
|
-
elif self.static_generator == StaticGenerator.REMIX_OLD:
|
|
287
|
-
return self.package_manager.run_execute_command("remix-ssg build")
|
|
288
|
-
elif self.static_generator == StaticGenerator.REMIX_V2:
|
|
289
|
-
return self.package_manager.run_execute_command("vite build")
|
|
290
|
-
elif self.static_generator == StaticGenerator.DOCUSAURUS:
|
|
291
|
-
return self.package_manager.run_execute_command("docusaurus build")
|
|
292
|
-
elif self.static_generator == StaticGenerator.SVELTE:
|
|
293
|
-
return self.package_manager.run_execute_command("svelte-kit build")
|
|
294
|
-
elif self.static_generator == StaticGenerator.VITE:
|
|
295
|
-
return self.package_manager.run_execute_command("vite build")
|
|
296
|
-
elif self.static_generator == StaticGenerator.NEXT:
|
|
297
|
-
return self.package_manager.run_execute_command("next export")
|
|
298
|
-
elif self.static_generator == StaticGenerator.NUXT_V3:
|
|
299
|
-
return self.package_manager.run_execute_command("nuxi generate")
|
|
300
|
-
elif self.static_generator == StaticGenerator.NUXT_OLD:
|
|
301
|
-
return self.package_manager.run_execute_command("nuxt generate")
|
|
302
|
-
return False
|
|
355
|
+
@classmethod
|
|
356
|
+
def get_build_command(
|
|
357
|
+
cls,
|
|
358
|
+
package_json: Optional[Dict[str, Any]],
|
|
359
|
+
package_manager: PackageManager,
|
|
360
|
+
static_generator: StaticGenerator,
|
|
361
|
+
) -> Optional[str]:
|
|
362
|
+
if package_json:
|
|
363
|
+
scripts = package_json.get("scripts", {})
|
|
364
|
+
generate_command = scripts.get("generate")
|
|
365
|
+
if generate_command:
|
|
366
|
+
return package_manager.run_command("generate")
|
|
367
|
+
build_command = scripts.get("build")
|
|
368
|
+
if build_command:
|
|
369
|
+
return package_manager.run_command("build")
|
|
370
|
+
command = static_generator.build_command()
|
|
371
|
+
return package_manager.run_execute_command(command)
|
|
303
372
|
|
|
304
373
|
def build_steps(self) -> list[str]:
|
|
305
|
-
|
|
306
|
-
get_build_command = self.get_build_command()
|
|
307
|
-
lockfile = self.package_manager.lockfile()
|
|
374
|
+
lockfile = self.config.package_manager.lockfile()
|
|
308
375
|
has_lockfile = (self.path / lockfile).exists()
|
|
309
|
-
install_command = self.package_manager.install_command(
|
|
376
|
+
install_command = self.config.package_manager.install_command(
|
|
310
377
|
has_lockfile=has_lockfile
|
|
311
378
|
)
|
|
312
|
-
|
|
379
|
+
ignored_files = ["node_modules", ".git"]
|
|
313
380
|
if has_lockfile:
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
381
|
+
ignored_files.append(lockfile)
|
|
382
|
+
all_ignored_files = ", ".join([f'"{file}"' for file in ignored_files])
|
|
383
|
+
|
|
384
|
+
return filter(
|
|
385
|
+
None,
|
|
386
|
+
[
|
|
387
|
+
'workdir(temp.path)' if not self.only_build else None,
|
|
388
|
+
f'copy("{lockfile}")' if has_lockfile else None,
|
|
389
|
+
'env(CI="true", NODE_ENV="production", NPM_CONFIG_FUND="false")'
|
|
390
|
+
if self.config.package_manager == PackageManager.NPM
|
|
391
|
+
else None,
|
|
392
|
+
# 'run("npx corepack enable", inputs=["package.json"], group="install")',
|
|
393
|
+
f'run("{install_command}", inputs=["package.json"], group="install")',
|
|
394
|
+
f'copy(".", ignore=[{all_ignored_files}])',
|
|
395
|
+
f'run("{self.config.build_command}", outputs=[config.static_dir], group="build")'
|
|
396
|
+
if not self.only_build
|
|
397
|
+
else None,
|
|
398
|
+
f'run("{self.config.build_command}", group="build")'
|
|
399
|
+
if self.only_build
|
|
400
|
+
else None,
|
|
401
|
+
'run("cp -R {}/* {}/".format(config.static_dir, static_app.path))'
|
|
402
|
+
if not self.only_build
|
|
403
|
+
else None,
|
|
404
|
+
],
|
|
405
|
+
)
|
|
325
406
|
|
|
326
407
|
def prepare_steps(self) -> Optional[list[str]]:
|
|
327
408
|
return None
|
|
328
409
|
|
|
329
410
|
def mounts(self) -> list[MountSpec]:
|
|
411
|
+
if self.only_build:
|
|
412
|
+
return []
|
|
330
413
|
return [MountSpec("temp", attach_to_serve=False), *super().mounts()]
|
|
331
414
|
|
|
332
415
|
def volumes(self) -> list[VolumeSpec]:
|
shipit/providers/php.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
import json
|
|
4
2
|
from pathlib import Path
|
|
5
|
-
from typing import Dict, Optional
|
|
3
|
+
from typing import Dict, Optional, Literal
|
|
6
4
|
|
|
7
5
|
from .base import (
|
|
8
6
|
DetectResult,
|
|
@@ -12,17 +10,35 @@ from .base import (
|
|
|
12
10
|
MountSpec,
|
|
13
11
|
ServiceSpec,
|
|
14
12
|
VolumeSpec,
|
|
15
|
-
|
|
13
|
+
Config,
|
|
16
14
|
)
|
|
15
|
+
from pydantic_settings import SettingsConfigDict
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PhpConfig(Config):
|
|
19
|
+
model_config = SettingsConfigDict(extra="ignore", env_prefix="SHIPIT_")
|
|
20
|
+
|
|
21
|
+
use_composer: bool = False
|
|
22
|
+
php_version: Optional[str] = "8.3"
|
|
23
|
+
php_architecture: Optional[Literal["64-bit", "32-bit"]] = "64-bit"
|
|
17
24
|
|
|
18
25
|
|
|
19
26
|
class PhpProvider:
|
|
20
|
-
def __init__(self, path: Path,
|
|
27
|
+
def __init__(self, path: Path, config: PhpConfig):
|
|
21
28
|
self.path = path
|
|
22
|
-
self.
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
self.config = config
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def load_config(cls, path: Path, base_config: Config) -> PhpConfig:
|
|
33
|
+
use_composer = (
|
|
34
|
+
_exists(path, "composer.json", "composer.lock")
|
|
35
|
+
or (
|
|
36
|
+
base_config.commands.install
|
|
37
|
+
and base_config.commands.install.startswith("composer ")
|
|
38
|
+
)
|
|
39
|
+
or False
|
|
25
40
|
)
|
|
41
|
+
return PhpConfig(use_composer=use_composer, **base_config.model_dump())
|
|
26
42
|
|
|
27
43
|
@classmethod
|
|
28
44
|
def name(cls) -> str:
|
|
@@ -30,7 +46,7 @@ class PhpProvider:
|
|
|
30
46
|
|
|
31
47
|
@classmethod
|
|
32
48
|
def detect(
|
|
33
|
-
cls, path: Path,
|
|
49
|
+
cls, path: Path, config: Config
|
|
34
50
|
) -> Optional[DetectResult]:
|
|
35
51
|
if _exists(path, "composer.json") and _exists(path, "public/index.php"):
|
|
36
52
|
return DetectResult(cls.name(), 60)
|
|
@@ -40,55 +56,46 @@ class PhpProvider:
|
|
|
40
56
|
or _exists(path, "app/index.php")
|
|
41
57
|
):
|
|
42
58
|
return DetectResult(cls.name(), 10)
|
|
43
|
-
if
|
|
59
|
+
if config.commands.start and config.commands.start.startswith("php "):
|
|
44
60
|
return DetectResult(cls.name(), 70)
|
|
45
|
-
if
|
|
61
|
+
if config.commands.install and config.commands.install.startswith("composer "):
|
|
46
62
|
return DetectResult(cls.name(), 30)
|
|
47
63
|
return None
|
|
48
64
|
|
|
49
|
-
def
|
|
50
|
-
pass
|
|
51
|
-
|
|
52
|
-
def serve_name(self) -> str:
|
|
53
|
-
return self.path.name
|
|
54
|
-
|
|
55
|
-
def platform(self) -> Optional[str]:
|
|
65
|
+
def serve_name(self) -> Optional[str]:
|
|
56
66
|
return None
|
|
57
67
|
|
|
58
68
|
def dependencies(self) -> list[DependencySpec]:
|
|
59
69
|
deps = [
|
|
60
70
|
DependencySpec(
|
|
61
71
|
"php",
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
architecture_var="SHIPIT_PHP_ARCHITECTURE",
|
|
72
|
+
var_name="config.php_version",
|
|
73
|
+
architecture_var_name="config.php_architecture",
|
|
65
74
|
use_in_build=True,
|
|
66
75
|
use_in_serve=True,
|
|
67
76
|
),
|
|
68
77
|
]
|
|
69
|
-
if self.
|
|
78
|
+
if self.config.use_composer:
|
|
70
79
|
deps.append(DependencySpec("composer", use_in_build=True))
|
|
71
80
|
deps.append(DependencySpec("bash", use_in_serve=True))
|
|
72
81
|
return deps
|
|
73
82
|
|
|
74
83
|
def declarations(self) -> Optional[str]:
|
|
75
|
-
if self.has_composer:
|
|
76
|
-
return 'HOME = getenv("HOME")\n'
|
|
77
84
|
return None
|
|
78
85
|
|
|
79
86
|
def build_steps(self) -> list[str]:
|
|
80
87
|
steps = [
|
|
81
|
-
'workdir(app
|
|
88
|
+
'workdir(app.path)',
|
|
82
89
|
]
|
|
83
90
|
if _exists(self.path, "php.ini"):
|
|
84
|
-
steps.append('copy("php.ini", "{}/php.ini".format(assets
|
|
91
|
+
steps.append('copy("php.ini", "{}/php.ini".format(assets.path))')
|
|
85
92
|
else:
|
|
86
93
|
steps.append(
|
|
87
|
-
'copy("php/php.ini", "{}/php.ini".format(assets
|
|
94
|
+
'copy("php/php.ini", "{}/php.ini".format(assets.path), base="assets")'
|
|
88
95
|
)
|
|
89
96
|
|
|
90
|
-
if self.
|
|
91
|
-
steps.append('env(
|
|
97
|
+
if self.config.use_composer:
|
|
98
|
+
steps.append('env(COMPOSER_HOME="/tmp", COMPOSER_FUND="0")')
|
|
92
99
|
steps.append(
|
|
93
100
|
'run("composer install --optimize-autoloader --no-scripts --no-interaction", inputs=["composer.json", "composer.lock"], outputs=["."], group="install")'
|
|
94
101
|
)
|
|
@@ -100,24 +107,21 @@ class PhpProvider:
|
|
|
100
107
|
return None
|
|
101
108
|
|
|
102
109
|
def commands(self) -> Dict[str, str]:
|
|
103
|
-
|
|
104
|
-
if self.custom_commands.start:
|
|
105
|
-
commands["start"] = json.dumps(self.custom_commands.start)
|
|
106
|
-
return commands
|
|
110
|
+
return self.base_commands()
|
|
107
111
|
|
|
108
112
|
def base_commands(self) -> Dict[str, str]:
|
|
109
113
|
if _exists(self.path, "public/index.php"):
|
|
110
114
|
return {
|
|
111
|
-
"start": '"php -S localhost:{} -t {}/public".format(PORT, app
|
|
115
|
+
"start": '"php -S localhost:{} -t {}/public".format(PORT, app.serve_path)'
|
|
112
116
|
}
|
|
113
117
|
elif _exists(self.path, "app/index.php"):
|
|
114
118
|
return {
|
|
115
|
-
"start": '"php -S localhost:{} -t {}/app".format(PORT, app
|
|
119
|
+
"start": '"php -S localhost:{} -t {}/app".format(PORT, app.serve_path)'
|
|
116
120
|
}
|
|
117
121
|
elif _exists(self.path, "index.php"):
|
|
118
|
-
return {"start": '"php -S localhost:{} -t {}".format(PORT, app
|
|
122
|
+
return {"start": '"php -S localhost:{} -t {}".format(PORT, app.serve_path)'}
|
|
119
123
|
return {
|
|
120
|
-
"start": '"php -S localhost:{} -t {}".format(PORT, app
|
|
124
|
+
"start": '"php -S localhost:{} -t {}".format(PORT, app.serve_path)',
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
def mounts(self) -> list[MountSpec]:
|
|
@@ -131,7 +135,7 @@ class PhpProvider:
|
|
|
131
135
|
|
|
132
136
|
def env(self) -> Optional[Dict[str, str]]:
|
|
133
137
|
return {
|
|
134
|
-
"PHP_INI_SCAN_DIR": '"{}".format(assets
|
|
138
|
+
"PHP_INI_SCAN_DIR": '"{}".format(assets.serve_path)',
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
def services(self) -> list[ServiceSpec]:
|