shipit-cli 0.8.0__py3-none-any.whl → 0.9.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 CHANGED
@@ -305,7 +305,7 @@ class DockerBuilder:
305
305
  image_name,
306
306
  command,
307
307
  *(extra_args or []),
308
- _env=os.environ, # Pass the current environment variables to the Docker client
308
+ _env={"DOCKER_BUILDKIT": "1", **os.environ}, # Pass the current environment variables to the Docker client
309
309
  _out=write_stdout,
310
310
  _err=write_stderr,
311
311
  )
@@ -370,7 +370,8 @@ RUN chmod {oct(mode)[2:]} {path.absolute()}
370
370
  base_path = self.docker_path
371
371
  shutil.rmtree(base_path, ignore_errors=True)
372
372
  base_path.mkdir(parents=True, exist_ok=True)
373
- self.docker_file_contents = "FROM debian:trixie-slim AS build\n"
373
+ self.docker_file_contents = "# syntax=docker/dockerfile:1.7-labs\n"
374
+ self.docker_file_contents += "FROM debian:trixie-slim AS build\n"
374
375
 
375
376
  self.docker_file_contents += """
376
377
  RUN apt-get update \\
@@ -378,6 +379,8 @@ RUN apt-get update \\
378
379
  build-essential gcc make autoconf libtool bison \\
379
380
  dpkg-dev pkg-config re2c locate \\
380
381
  libmariadb-dev libmariadb-dev-compat libpq-dev \\
382
+ libvips-dev default-libmysqlclient-dev libmagickwand-dev \\
383
+ libicu-dev libxml2-dev libxslt-dev \\
381
384
  sudo curl ca-certificates \\
382
385
  && rm -rf /var/lib/apt/lists/*
383
386
 
@@ -435,7 +438,11 @@ RUN curl https://mise.run | sh
435
438
  else:
436
439
  raise Exception(f"Asset {step.source} does not exist")
437
440
  else:
438
- self.docker_file_contents += f"COPY {step.source} {step.target}\n"
441
+ if step.ignore:
442
+ exclude = " \\\n" +" \\\n".join([f" --exclude={ignore}" for ignore in step.ignore]) + " \\\n "
443
+ else:
444
+ exclude = ""
445
+ self.docker_file_contents += f"COPY{exclude} {step.source} {step.target}\n"
439
446
  elif isinstance(step, EnvStep):
440
447
  env_vars = " ".join(
441
448
  [f"{key}={value}" for key, value in step.variables.items()]
@@ -538,9 +545,10 @@ class LocalBuilder:
538
545
  exe = shutil.which(program, path=PATH)
539
546
  if not exe:
540
547
  raise Exception(f"Program is not installed: {program}")
541
- cmd = sh.Command(exe) # "grep"
548
+ cmd = sh.Command("bash") # "grep"
542
549
  result = cmd(
543
- *parts[1:],
550
+ "-c",
551
+ command_line,
544
552
  _env={**env, "PATH": PATH},
545
553
  _cwd=build_path,
546
554
  _out=write_stdout,
@@ -1,37 +1,206 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
4
+ import yaml
3
5
  from pathlib import Path
4
- from typing import Dict, Optional
6
+ from typing import Dict, Optional, Any, Set
7
+ from enum import Enum
8
+ from semantic_version import Version, NpmSpec
9
+
5
10
 
6
11
  from .base import (
7
12
  DetectResult,
8
13
  DependencySpec,
9
14
  Provider,
10
15
  _exists,
11
- _has_dependency,
12
16
  MountSpec,
13
17
  ServiceSpec,
14
18
  VolumeSpec,
15
19
  CustomCommands,
16
20
  )
21
+ from .staticfile import StaticFileProvider
22
+
23
+
24
+ class PackageManager(Enum):
25
+ NPM = "npm"
26
+ PNPM = "pnpm"
27
+ YARN = "yarn"
28
+ BUN = "bun"
29
+
30
+ def as_dependency(self, path) -> DependencySpec:
31
+ dep_name = {
32
+ PackageManager.NPM: "npm",
33
+ PackageManager.PNPM: "pnpm",
34
+ PackageManager.YARN: "yarn",
35
+ PackageManager.BUN: "bun",
36
+ }[self]
37
+
38
+ default_version = None
39
+ if self == PackageManager.PNPM:
40
+ lockfile = path / self.lockfile()
41
+ lockfile_version = self.pnpm_lockfile_version(lockfile)
42
+ if lockfile_version:
43
+ if lockfile_version.startswith("5."):
44
+ default_version = "7"
45
+ elif lockfile_version.startswith("6."):
46
+ default_version = "8"
47
+
48
+ return DependencySpec(
49
+ dep_name,
50
+ env_var=f"SHIPIT_{dep_name.upper()}_VERSION",
51
+ default_version=default_version,
52
+ )
53
+
54
+ def lockfile(self) -> (str):
55
+ return {
56
+ PackageManager.NPM: "package-lock.json",
57
+ PackageManager.PNPM: "pnpm-lock.yaml",
58
+ PackageManager.YARN: "yarn.lock",
59
+ PackageManager.BUN: "bun.lockb",
60
+ }[self]
61
+
62
+ @classmethod
63
+ def pnpm_lockfile_version(cls, lockfile: Path) -> Optional[str]:
64
+ # Read line by line and return the lockfileVersion
65
+ with open(lockfile, "r") as f:
66
+ for line in f:
67
+ if "lockfileVersion" in line:
68
+ try:
69
+ config = yaml.safe_load(line)
70
+ version = config.get("lockfileVersion")
71
+ assert isinstance(version, (str, bytes))
72
+ return version
73
+ except:
74
+ pass
75
+ return None
76
+
77
+ def install_command(self, has_lockfile: bool = False) -> str:
78
+ return {
79
+ PackageManager.NPM: f"npm {'ci' if has_lockfile else 'install'}",
80
+ PackageManager.PNPM: f"pnpm install{' --frozen-lockfile' if has_lockfile else ''}",
81
+ PackageManager.YARN: f"yarn install{' --frozen-lockfile' if has_lockfile else ''}",
82
+ PackageManager.BUN: f"bun install{' --no-save' if has_lockfile else ''}",
83
+ }[self]
84
+
85
+ def run_command(self, command: str) -> str:
86
+ return {
87
+ PackageManager.NPM: f"npm run {command}",
88
+ PackageManager.PNPM: f"pnpm run {command}",
89
+ PackageManager.YARN: f"yarn run {command}",
90
+ PackageManager.BUN: f"bun run {command}",
91
+ }[self]
92
+
93
+ def run_execute_command(self, command: str) -> str:
94
+ return {
95
+ PackageManager.NPM: f"npx {command}",
96
+ PackageManager.PNPM: f"pnpx {command}",
97
+ PackageManager.YARN: f"ypx {command}",
98
+ PackageManager.BUN: f"bunx {command}",
99
+ }[self]
100
+
101
+
102
+ class StaticGenerator(Enum):
103
+ ASTRO = "astro"
104
+ VITE = "vite"
105
+ NEXT = "next"
106
+ GATSBY = "gatsby"
107
+ DOCUSAURUS = "docusaurus"
108
+ SVELTE = "svelte"
109
+ REMIX = "remix"
110
+ NUXT_OLD = "nuxt"
111
+ NUXT_V3 = "nuxt3"
17
112
 
18
113
 
19
- class NodeStaticProvider:
114
+ class NodeStaticProvider(StaticFileProvider):
115
+ package_manager: PackageManager
116
+ package_json: Optional[Dict[str, Any]]
117
+ extra_dependencies: Set[str]
118
+
20
119
  def __init__(self, path: Path, custom_commands: CustomCommands):
21
- self.path = path
22
- self.custom_commands = custom_commands
120
+ super().__init__(path, custom_commands)
121
+ if (path / "package-lock.json").exists():
122
+ self.package_manager = PackageManager.NPM
123
+ elif (path / "pnpm-lock.yaml").exists():
124
+ self.package_manager = PackageManager.PNPM
125
+ elif (path / "yarn.lock").exists():
126
+ self.package_manager = PackageManager.YARN
127
+ elif (path / "bun.lockb").exists():
128
+ self.package_manager = PackageManager.BUN
129
+ else:
130
+ self.package_manager = PackageManager.PNPM
131
+
132
+ self.package_json = self.parse_package_json(path)
133
+
134
+ if self.has_dependency(self.package_json, "gatsby"):
135
+ self.static_generator = StaticGenerator.GATSBY
136
+ elif self.has_dependency(self.package_json, "astro"):
137
+ self.static_generator = StaticGenerator.ASTRO
138
+ elif self.has_dependency(self.package_json, "@docusaurus/core"):
139
+ self.static_generator = StaticGenerator.DOCUSAURUS
140
+ elif self.has_dependency(self.package_json, "svelte"):
141
+ self.static_generator = StaticGenerator.SVELTE
142
+ elif self.has_dependency(self.package_json, "@remix-run/dev"):
143
+ self.static_generator = StaticGenerator.REMIX
144
+ elif self.has_dependency(self.package_json, "vite"):
145
+ self.static_generator = StaticGenerator.VITE
146
+ elif self.has_dependency(self.package_json, "next"):
147
+ self.static_generator = StaticGenerator.NEXT
148
+ elif self.has_dependency(self.package_json, "nuxt", "2") or self.has_dependency(
149
+ self.package_json, "nuxt", "1"
150
+ ):
151
+ self.static_generator = StaticGenerator.NUXT_OLD
152
+ elif self.has_dependency(self.package_json, "nuxt"):
153
+ self.static_generator = StaticGenerator.NUXT_V3
154
+
155
+ # if self.has_dependency(self.package_json, "sharp"):
156
+ # self.extra_dependencies.add("libvips")
157
+
158
+ @classmethod
159
+ def parse_package_json(cls, path: Path) -> Optional[Dict[str, Any]]:
160
+ package_json_path = path / "package.json"
161
+ if not package_json_path.exists():
162
+ return None
163
+ try:
164
+ package_json = json.loads(package_json_path.read_text())
165
+ assert isinstance(package_json, dict), (
166
+ "package.json must be a valid JSON object"
167
+ )
168
+ return package_json
169
+ except Exception:
170
+ return None
171
+
172
+ @classmethod
173
+ def has_dependency(
174
+ cls, package_json: Optional[Dict[str, Any]], dep: str, version: Optional[str] = None
175
+ ) -> bool:
176
+ if not package_json:
177
+ return False
178
+ for section in ("dependencies", "devDependencies", "peerDependencies"):
179
+ dep_section = package_json.get(section, {})
180
+ if dep in dep_section:
181
+ if version:
182
+ try:
183
+ constraint = NpmSpec(dep_section[dep])
184
+ return Version(version) in constraint
185
+ except Exception:
186
+ pass
187
+ else:
188
+ return True
189
+ return False
23
190
 
24
191
  @classmethod
25
192
  def name(cls) -> str:
26
193
  return "node-static"
27
194
 
28
195
  @classmethod
29
- def detect(cls, path: Path, custom_commands: CustomCommands) -> Optional[DetectResult]:
30
- pkg = path / "package.json"
31
- if not pkg.exists():
196
+ def detect(
197
+ cls, path: Path, custom_commands: CustomCommands
198
+ ) -> Optional[DetectResult]:
199
+ package_json = cls.parse_package_json(path)
200
+ if not package_json:
32
201
  return None
33
- static_generators = ["astro", "vite", "next", "nuxt"]
34
- if any(_has_dependency(pkg, dep) for dep in static_generators):
202
+ static_generators = ["astro", "vite", "next", "nuxt", "gatsby", "svelte", "@docusaurus/core", "@remix-run/dev"]
203
+ if any(cls.has_dependency(package_json, dep) for dep in static_generators):
35
204
  return DetectResult(cls.name(), 40)
36
205
  return None
37
206
 
@@ -45,6 +214,8 @@ class NodeStaticProvider:
45
214
  return "staticsite"
46
215
 
47
216
  def dependencies(self) -> list[DependencySpec]:
217
+ package_manager_dep = self.package_manager.as_dependency(self.path)
218
+ package_manager_dep.use_in_build = True
48
219
  return [
49
220
  DependencySpec(
50
221
  "node",
@@ -52,37 +223,82 @@ class NodeStaticProvider:
52
223
  default_version="22",
53
224
  use_in_build=True,
54
225
  ),
55
- DependencySpec("npm", use_in_build=True),
226
+ package_manager_dep,
56
227
  DependencySpec("static-web-server", use_in_serve=True),
57
228
  ]
58
229
 
59
230
  def declarations(self) -> Optional[str]:
60
231
  return None
61
232
 
233
+ def get_output_dir(self) -> str:
234
+ if self.static_generator == StaticGenerator.NEXT:
235
+ return "out"
236
+ elif self.static_generator in [StaticGenerator.ASTRO, StaticGenerator.VITE, StaticGenerator.NUXT_OLD, StaticGenerator.NUXT_V3]:
237
+ return "dist"
238
+ elif self.static_generator in [StaticGenerator.GATSBY, StaticGenerator.REMIX]:
239
+ return "public"
240
+ elif self.static_generator in [StaticGenerator.DOCUSAURUS, StaticGenerator.SVELTE]:
241
+ return "build"
242
+ else:
243
+ return "dist"
244
+
245
+ def get_build_command(self) -> bool:
246
+ if not self.package_json:
247
+ return False
248
+ build_command = self.package_json.get("scripts", {}).get("build")
249
+ if build_command:
250
+ return self.package_manager.run_command("build")
251
+ if self.static_generator == StaticGenerator.GATSBY:
252
+ return self.package_manager.run_execute_command("gatsby build")
253
+ if self.static_generator == StaticGenerator.ASTRO:
254
+ return self.package_manager.run_execute_command("astro build")
255
+ elif self.static_generator == StaticGenerator.REMIX:
256
+ return self.package_manager.run_execute_command("remix-ssg build")
257
+ elif self.static_generator == StaticGenerator.DOCUSAURUS:
258
+ return self.package_manager.run_execute_command("docusaurus build")
259
+ elif self.static_generator == StaticGenerator.SVELTE:
260
+ return self.package_manager.run_execute_command("svelte-kit build")
261
+ elif self.static_generator == StaticGenerator.VITE:
262
+ return self.package_manager.run_execute_command("vite build")
263
+ elif self.static_generator == StaticGenerator.NEXT:
264
+ return self.package_manager.run_execute_command("next export")
265
+ elif self.static_generator == StaticGenerator.NUXT_V3:
266
+ return self.package_manager.run_execute_command("nuxi generate")
267
+ elif self.static_generator == StaticGenerator.NUXT_OLD:
268
+ return self.package_manager.run_execute_command("nuxt generate")
269
+ return False
270
+
62
271
  def build_steps(self) -> list[str]:
63
- output_dir = "dist" if (self.path / "dist").exists() else "public"
272
+ output_dir = self.get_output_dir()
273
+ get_build_command = self.get_build_command()
274
+ lockfile = self.package_manager.lockfile()
275
+ has_lockfile = (self.path / lockfile).exists()
276
+ install_command = self.package_manager.install_command(has_lockfile=has_lockfile)
277
+ input_files = ["package.json"]
278
+ if has_lockfile:
279
+ input_files.append(lockfile)
280
+ inputs_install_files = ", ".join([f'"{file}"' for file in input_files])
281
+
64
282
  return [
65
- "run(\"npm install\", inputs=[\"package.json\", \"package-lock.json\"], group=\"install\")",
66
- "copy(\".\", \".\", ignore=[\"node_modules\", \".git\"])",
67
- f"run(\"npm run build\", outputs=[\"{output_dir}\"], group=\"build\")",
68
- f"run(\"cp -R {output_dir}/* {{}}/\".format(app[\"build\"]))",
283
+ 'workdir(temp["build"])',
284
+ # 'run("npx corepack enable", inputs=["package.json"], group="install")',
285
+ f'run("{install_command}", inputs=[{inputs_install_files}], group="install")',
286
+ 'copy(".", ".", ignore=["node_modules", ".git"])',
287
+ f'run("{get_build_command}", outputs=["{output_dir}"], group="build")',
288
+ f'run("cp -R {output_dir}/* {{}}/".format(app["build"]))',
69
289
  ]
70
290
 
71
291
  def prepare_steps(self) -> Optional[list[str]]:
72
292
  return None
73
293
 
74
- def commands(self) -> Dict[str, str]:
75
- output_dir = "dist" if (self.path / "dist").exists() else "public"
76
- return {"start": f'"static-web-server --root /app/{output_dir}"'}
77
-
78
294
  def mounts(self) -> list[MountSpec]:
79
- return [MountSpec("app")]
295
+ return [MountSpec("temp"), *super().mounts()]
80
296
 
81
297
  def volumes(self) -> list[VolumeSpec]:
82
298
  return []
83
299
 
84
300
  def env(self) -> Optional[Dict[str, str]]:
85
301
  return None
86
-
302
+
87
303
  def services(self) -> list[ServiceSpec]:
88
304
  return []
@@ -16,7 +16,7 @@ def providers() -> list[type[Provider]]:
16
16
  # Order matters: more specific providers first
17
17
  return [
18
18
  LaravelProvider,
19
- GatsbyProvider,
19
+ # GatsbyProvider,
20
20
  HugoProvider,
21
21
  MkdocsProvider,
22
22
  PythonProvider,
@@ -19,6 +19,8 @@ from .base import (
19
19
 
20
20
  class StaticFileProvider:
21
21
  config: Optional[dict] = None
22
+ path: Path
23
+ custom_commands: CustomCommands
22
24
 
23
25
  def __init__(self, path: Path, custom_commands: CustomCommands):
24
26
  self.path = path
shipit/version.py CHANGED
@@ -1,5 +1,5 @@
1
1
  __all__ = ["version", "version_info"]
2
2
 
3
3
 
4
- version = "0.8.0"
5
- version_info = (0, 8, 0, "final", 0)
4
+ version = "0.9.0"
5
+ version_info = (0, 9, 0, "final", 0)
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shipit-cli
3
- Version: 0.8.0
4
- Summary: Add your description here
3
+ Version: 0.9.0
4
+ Summary: Shipit CLI is the best way to build, serve and deploy your projects anywhere.
5
5
  Project-URL: homepage, https://wasmer.io
6
6
  Project-URL: repository, https://github.com/wasmerio/shipit
7
7
  Project-URL: Changelog, https://github.com/wasmerio/shipit/changelog
@@ -10,6 +10,7 @@ Requires-Dist: dotenv>=0.9.9
10
10
  Requires-Dist: pyyaml>=6.0.2
11
11
  Requires-Dist: requests>=2.32.5
12
12
  Requires-Dist: rich>=14.1.0
13
+ Requires-Dist: semantic-version>=2.10.0
13
14
  Requires-Dist: sh>=2.2.2
14
15
  Requires-Dist: starlark-pyo3>=2025.1
15
16
  Requires-Dist: tomlkit>=0.13.3
@@ -1,8 +1,8 @@
1
1
  shipit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- shipit/cli.py,sha256=wfHNLe0Pe0uheeiFuWQi3DFlUA86oTN8-mojaKxru94,58696
2
+ shipit/cli.py,sha256=NMZ6xSLq8MlV9yT3FLi9XZ5IBw2IMppoPna_vl9LK1Y,59170
3
3
  shipit/generator.py,sha256=W4MynSFwId4PRWWrF0R3NsANye_Zv8TwXCUXai94pW0,6553
4
4
  shipit/procfile.py,sha256=GlfdwzFUr0GWGKaaiXlLKNFInWaRNMy_wN14UEyU_5Q,2974
5
- shipit/version.py,sha256=tLXsjc9rJ1_Aj9p3fPQjFAv5Eanmo1dQaEiaHbiBSwY,95
5
+ shipit/version.py,sha256=0hkQAsOavRu0fhDHnq4yN300QZjC88Y_Nr7gIReJ_9Y,95
6
6
  shipit/assets/php/php.ini,sha256=f4irndAjB4GuuouEImRkNV22Q-yw1KqR-43jAMDw730,2531
7
7
  shipit/assets/wordpress/install.sh,sha256=fJkVeGAw_dj-ywmQipAWRjaz57sayD6F0OFvZKMIKug,669
8
8
  shipit/assets/wordpress/wp-config.php,sha256=IdGQoeg8E89JiqwxO2i8WnGMzmhNWgRz80X6lbU5GXc,4134
@@ -11,13 +11,13 @@ shipit/providers/gatsby.py,sha256=kzfS-z040GaJ0a9u2_6S6K-ykGSX2yPG17VpjUWBOBA,23
11
11
  shipit/providers/hugo.py,sha256=l3IZ14LGYc3wQ7Uk0iQMDNN9Syd1G4HPzm0ePCGFyzE,1808
12
12
  shipit/providers/laravel.py,sha256=2rCuOi2kC5OYQfAG7C0NMhG7KEgzfudwUT_CvVFz4hM,3031
13
13
  shipit/providers/mkdocs.py,sha256=mDJpT3rzYAr5Vw-yt5fCpV0QgBZyksn9MrkPd4nzROU,2200
14
- shipit/providers/node_static.py,sha256=QVUTTZbvUGtj2yT9WTbGmuM30rJzejnp76crIqSGA7o,2564
14
+ shipit/providers/node_static.py,sha256=j5VKT_RblonIHTtpiUNai4e_izZUhbyLtPTsGeALpcY,11508
15
15
  shipit/providers/php.py,sha256=BSOqS3UhABoe_ued8aXtyn0KdqWbOCq6VV2ehtoBlmk,3547
16
16
  shipit/providers/python.py,sha256=kQpaaQi8ajHM_SFk2xI5lRIeOc6PXSKumb-Tv_5n6m0,20954
17
- shipit/providers/registry.py,sha256=lHUViVuPJf1OIZD8I_NTX4LP7E3Uo5-MmLfarmAA_cM,729
18
- shipit/providers/staticfile.py,sha256=BxZ7Z8dN0tAd-s8_m-NPgqJrVSvKwxCd-us-hCanL1E,2626
17
+ shipit/providers/registry.py,sha256=JCuQaYTvJcWK1nS-om9TIQgGW6pT5BuNLIRzChLLFWE,731
18
+ shipit/providers/staticfile.py,sha256=F4thEuihDW-h5DO-lotrjSdHYWzoXofQPpsAgWonvpY,2677
19
19
  shipit/providers/wordpress.py,sha256=Wjc1fgFburewlf4hQ2ZmxTQko_SHQW-8jrDukzKx0Gs,3019
20
- shipit_cli-0.8.0.dist-info/METADATA,sha256=p_XVn3jE5BNj2JQm_5-xBqszHGjdZYb2me6Ur0BOgTk,523
21
- shipit_cli-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- shipit_cli-0.8.0.dist-info/entry_points.txt,sha256=7AE1NjSrHaSDfbfsRRO50KKnHFTbB0Imsccd1WynzAQ,72
23
- shipit_cli-0.8.0.dist-info/RECORD,,
20
+ shipit_cli-0.9.0.dist-info/METADATA,sha256=ULUMj5u3OzMZA00AoJJOxA0iAsHIw5R-d8IODyR0hJ8,615
21
+ shipit_cli-0.9.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ shipit_cli-0.9.0.dist-info/entry_points.txt,sha256=7AE1NjSrHaSDfbfsRRO50KKnHFTbB0Imsccd1WynzAQ,72
23
+ shipit_cli-0.9.0.dist-info/RECORD,,