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.
@@ -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
- CustomCommands,
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
- env_var=f"SHIPIT_{dep_name.upper()}_VERSION",
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: f"pnpm install{' --frozen-lockfile' if has_lockfile else ''}",
82
- PackageManager.YARN: f"yarn install{' --frozen-lockfile' if has_lockfile else ''}",
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
- class NodeStaticProvider(StaticFileProvider):
118
- package_manager: PackageManager
119
- package_json: Optional[Dict[str, Any]]
120
- extra_dependencies: Set[str]
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
- def __init__(self, path: Path, custom_commands: CustomCommands):
124
- super().__init__(path, custom_commands)
125
- if (path / "package-lock.json").exists():
126
- self.package_manager = PackageManager.NPM
127
- elif (path / "pnpm-lock.yaml").exists():
128
- self.package_manager = PackageManager.PNPM
129
- elif (path / "yarn.lock").exists():
130
- self.package_manager = PackageManager.YARN
131
- elif (path / "bun.lockb").exists():
132
- self.package_manager = PackageManager.BUN
133
- else:
134
- self.package_manager = PackageManager.PNPM
135
-
136
- self.package_json = self.parse_package_json(path)
137
-
138
- if self.has_dependency(self.package_json, "gatsby"):
139
- self.static_generator = StaticGenerator.GATSBY
140
- elif self.has_dependency(self.package_json, "astro"):
141
- self.static_generator = StaticGenerator.ASTRO
142
- elif self.has_dependency(self.package_json, "@docusaurus/core"):
143
- self.static_generator = StaticGenerator.DOCUSAURUS
144
- elif self.has_dependency(self.package_json, "svelte"):
145
- self.static_generator = StaticGenerator.SVELTE
146
- elif self.has_dependency(
147
- self.package_json, "@remix-run/dev", "1"
148
- ) or self.has_dependency(self.package_json, "@remix-run/dev", "0"):
149
- self.static_generator = StaticGenerator.REMIX_OLD
150
- elif self.has_dependency(self.package_json, "@remix-run/dev"):
151
- self.static_generator = StaticGenerator.REMIX_V2
152
- elif self.has_dependency(self.package_json, "vite"):
153
- self.static_generator = StaticGenerator.VITE
154
- elif self.has_dependency(self.package_json, "next"):
155
- self.static_generator = StaticGenerator.NEXT
156
- elif self.has_dependency(self.package_json, "nuxt", "2") or self.has_dependency(
157
- self.package_json, "nuxt", "1"
158
- ):
159
- self.static_generator = StaticGenerator.NUXT_OLD
160
- elif self.has_dependency(self.package_json, "nuxt"):
161
- self.static_generator = StaticGenerator.NUXT_V3
162
-
163
- # if self.has_dependency(self.package_json, "sharp"):
164
- # self.extra_dependencies.add("libvips")
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, custom_commands: CustomCommands
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 initialize(self) -> None:
228
- pass
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
- env_var="SHIPIT_NODE_VERSION",
243
- default_version="22",
348
+ var_name="config.node_version",
244
349
  use_in_build=True,
245
350
  ),
246
351
  package_manager_dep,
247
- DependencySpec("static-web-server", use_in_serve=True),
352
+ *super().dependencies(),
248
353
  ]
249
354
 
250
- def declarations(self) -> Optional[str]:
251
- return None
252
-
253
- def get_output_dir(self) -> str:
254
- if self.static_generator == StaticGenerator.NEXT:
255
- return "out"
256
- elif self.static_generator in [
257
- StaticGenerator.ASTRO,
258
- StaticGenerator.VITE,
259
- StaticGenerator.NUXT_OLD,
260
- StaticGenerator.NUXT_V3,
261
- StaticGenerator.REMIX_V2,
262
- ]:
263
- return "dist"
264
- elif self.static_generator == StaticGenerator.GATSBY:
265
- return "public"
266
- elif self.static_generator == StaticGenerator.REMIX_OLD:
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
- output_dir = self.get_output_dir()
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
- input_files = ["package.json"]
379
+ ignored_files = ["node_modules", ".git"]
313
380
  if has_lockfile:
314
- input_files.append(lockfile)
315
- inputs_install_files = ", ".join([f'"{file}"' for file in input_files])
316
-
317
- return [
318
- 'workdir(temp["build"])',
319
- # 'run("npx corepack enable", inputs=["package.json"], group="install")',
320
- f'run("{install_command}", inputs=[{inputs_install_files}], group="install")',
321
- 'copy(".", ".", ignore=["node_modules", ".git"])',
322
- f'run("{get_build_command}", outputs=["{output_dir}"], group="build")',
323
- f'run("cp -R {output_dir}/* {{}}/".format(app["build"]))',
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
- CustomCommands,
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, custom_commands: CustomCommands):
27
+ def __init__(self, path: Path, config: PhpConfig):
21
28
  self.path = path
22
- self.custom_commands = custom_commands
23
- self.has_composer = _exists(self.path, "composer.json", "composer.lock") or (
24
- custom_commands.install and custom_commands.install.startswith("composer ")
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, custom_commands: CustomCommands
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 custom_commands.start and custom_commands.start.startswith("php "):
59
+ if config.commands.start and config.commands.start.startswith("php "):
44
60
  return DetectResult(cls.name(), 70)
45
- if custom_commands.install and custom_commands.install.startswith("composer "):
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 initialize(self) -> None:
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
- env_var="SHIPIT_PHP_VERSION",
63
- default_version="8.3",
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.has_composer:
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["build"])',
88
+ 'workdir(app.path)',
82
89
  ]
83
90
  if _exists(self.path, "php.ini"):
84
- steps.append('copy("php.ini", "{}/php.ini".format(assets["build"]))')
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["build"]), base="assets")'
94
+ 'copy("php/php.ini", "{}/php.ini".format(assets.path), base="assets")'
88
95
  )
89
96
 
90
- if self.has_composer:
91
- steps.append('env(HOME=HOME, COMPOSER_FUND="0")')
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
- commands = self.base_commands()
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["serve"])'
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["serve"])'
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["serve"])'}
122
+ return {"start": '"php -S localhost:{} -t {}".format(PORT, app.serve_path)'}
119
123
  return {
120
- "start": '"php -S localhost:{} -t {}".format(PORT, app["serve"])',
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["serve"])',
138
+ "PHP_INI_SCAN_DIR": '"{}".format(assets.serve_path)',
135
139
  }
136
140
 
137
141
  def services(self) -> list[ServiceSpec]: