shipit-cli 0.5.2__tar.gz → 0.6.1__tar.gz

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.
Files changed (23) hide show
  1. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/PKG-INFO +1 -1
  2. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/pyproject.toml +1 -1
  3. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/cli.py +99 -10
  4. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/generator.py +10 -0
  5. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/base.py +9 -1
  6. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/gatsby.py +4 -1
  7. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/hugo.py +4 -1
  8. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/laravel.py +4 -1
  9. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/mkdocs.py +4 -1
  10. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/node_static.py +4 -1
  11. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/php.py +4 -1
  12. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/python.py +18 -1
  13. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/staticfile.py +4 -1
  14. shipit_cli-0.6.1/src/shipit/version.py +5 -0
  15. shipit_cli-0.5.2/src/shipit/version.py +0 -5
  16. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/.gitignore +0 -0
  17. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/README.md +0 -0
  18. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/__init__.py +0 -0
  19. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/assets/php/php.ini +0 -0
  20. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/src/shipit/providers/registry.py +0 -0
  21. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/tests/test_examples_build.py +0 -0
  22. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/tests/test_generate_shipit_examples.py +0 -0
  23. {shipit_cli-0.5.2 → shipit_cli-0.6.1}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shipit-cli
3
- Version: 0.5.2
3
+ Version: 0.6.1
4
4
  Summary: Add your description here
5
5
  Project-URL: homepage, https://wasmer.io
6
6
  Project-URL: repository, https://github.com/wasmerio/shipit
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "shipit-cli"
3
- version = "0.5.2"
3
+ version = "0.6.1"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -16,6 +16,7 @@ from typing import (
16
16
  Set,
17
17
  TypedDict,
18
18
  Union,
19
+ Literal,
19
20
  cast,
20
21
  )
21
22
  from shutil import copy, copytree, ignore_patterns
@@ -48,6 +49,12 @@ class Mount:
48
49
  serve_path: Path
49
50
 
50
51
 
52
+ @dataclass
53
+ class Service:
54
+ name: str
55
+ provider: Literal["postgres", "mysql", "redis"] # Right now we only support postgres and mysql
56
+
57
+
51
58
  @dataclass
52
59
  class Serve:
53
60
  name: str
@@ -61,6 +68,7 @@ class Serve:
61
68
  workers: Optional[List[str]] = None
62
69
  mounts: Optional[List[Mount]] = None
63
70
  env: Optional[Dict[str, str]] = None
71
+ services: Optional[List[Service]] = None
64
72
 
65
73
 
66
74
  @dataclass
@@ -309,10 +317,10 @@ RUN chmod {oct(mode)[2:]} {path.absolute()}
309
317
  return
310
318
  if dependency.version:
311
319
  self.docker_file_contents += (
312
- f"RUN pkgm install {dependency.name}@{dependency.version}\n"
320
+ f"RUN mise use --global {dependency.name}@{dependency.version}\n"
313
321
  )
314
322
  else:
315
- self.docker_file_contents += f"RUN pkgm install {dependency.name}\n"
323
+ self.docker_file_contents += f"RUN mise use --global {dependency.name}\n"
316
324
 
317
325
  def build(
318
326
  self, env: Dict[str, str], mounts: List[Mount], steps: List[Step]
@@ -320,15 +328,26 @@ RUN chmod {oct(mode)[2:]} {path.absolute()}
320
328
  base_path = self.docker_path
321
329
  shutil.rmtree(base_path, ignore_errors=True)
322
330
  base_path.mkdir(parents=True, exist_ok=True)
323
- self.docker_file_contents = "FROM debian:bookworm-slim AS build\n"
331
+ self.docker_file_contents = "FROM debian:trixie-slim AS build\n"
332
+
324
333
  self.docker_file_contents += """
325
334
  RUN apt-get update \\
326
- && apt-get -y --no-install-recommends install sudo curl ca-certificates locate git zip unzip \\
335
+ && apt-get -y --no-install-recommends install \\
336
+ build-essential gcc make \\
337
+ dpkg-dev pkg-config \\
338
+ libmariadb-dev libmariadb-dev-compat libpq-dev \\
339
+ sudo curl ca-certificates \\
327
340
  && rm -rf /var/lib/apt/lists/*
328
341
 
329
342
  SHELL ["/bin/bash", "-o", "pipefail", "-c"]
330
-
331
- RUN curl https://pkgx.sh | sh
343
+ ENV MISE_DATA_DIR="/mise"
344
+ ENV MISE_CONFIG_DIR="/mise"
345
+ ENV MISE_CACHE_DIR="/mise/cache"
346
+ ENV MISE_INSTALL_PATH="/usr/local/bin/mise"
347
+ ENV PATH="/mise/shims:$PATH"
348
+ # ENV MISE_VERSION="..."
349
+
350
+ RUN curl https://mise.run | sh
332
351
  """
333
352
  # docker_file_contents += "RUN curl https://mise.run | sh\n"
334
353
  # self.docker_file_contents += """
@@ -624,20 +643,31 @@ class LocalBuilder:
624
643
 
625
644
  def build_serve(self, serve: Serve) -> None:
626
645
  console.print("\n[bold]Building serve[/bold]")
627
- build_path = self.get_build_path()
628
646
  serve_command_path = self.get_serve_path() / "bin"
629
647
  serve_command_path.mkdir(parents=True, exist_ok=False)
630
648
  path = self.get_path() / ".path"
631
- # path_resolved = [str((build_path/path).resolve()) for path in path.read_text().split(os.pathsep) if path]
632
- # path_text = os.pathsep.join(path_resolved)
633
649
  path_text = path.read_text()
634
650
  console.print(f"[bold]Serve Commands:[/bold]")
635
651
  for command in serve.commands:
636
652
  console.print(f"* {command}")
637
653
  command_path = serve_command_path / command
654
+ content = f"#!/bin/bash\ncd {serve.cwd}\nPATH={path_text}:$PATH {serve.commands[command]}"
638
655
  command_path.write_text(
639
- f"#!/bin/bash\ncd {build_path}\nPATH={path_text}:$PATH {serve.commands[command]}"
656
+ content
657
+ )
658
+ manifest_panel = Panel(
659
+ Syntax(
660
+ content.strip(),
661
+ "bash",
662
+ theme="monokai",
663
+ background_color="default",
664
+ line_numbers=True,
665
+ ),
666
+ box=box.SQUARE,
667
+ border_style="bright_black",
668
+ expand=False,
640
669
  )
670
+ console.print(manifest_panel, markup=False, highlight=True)
641
671
  command_path.chmod(0o755)
642
672
 
643
673
  def run_serve_command(self, command: str) -> None:
@@ -686,6 +716,13 @@ class WasmerBuilder:
686
716
  },
687
717
  "scripts": {"pandoc"},
688
718
  },
719
+ "ffmpeg": {
720
+ "dependencies": {
721
+ "latest": "wasmer/ffmpeg@=1.0.5",
722
+ "N-111519": "wasmer/ffmpeg@=1.0.5",
723
+ },
724
+ "scripts": {"ffmpeg"},
725
+ },
689
726
  "php": {
690
727
  "dependencies": {
691
728
  "latest": "php/php-32@=8.3.2104",
@@ -943,8 +980,46 @@ class WasmerBuilder:
943
980
  }
944
981
  # Update the app to use the new package
945
982
  yaml_config["package"] = "."
983
+ if serve.services:
984
+ capabilities = yaml_config.get("capabilities", {})
985
+ has_mysql = any(service.provider == "mysql" for service in serve.services)
986
+ # has_postgres = any(service.provider == "postgres" for service in serve.services)
987
+ # has_redis = any(service.provider == "redis" for service in serve.services)
988
+ if has_mysql:
989
+ capabilities["database"] = {
990
+ "engine": "mysql"
991
+ }
992
+ yaml_config["capabilities"] = capabilities
993
+
994
+ if "after_deploy" in serve.commands:
995
+ jobs = yaml_config.get("jobs", [])
996
+ jobs.append({
997
+ "name": "after_deploy",
998
+ "trigger": "post-deployment",
999
+ "action": {
1000
+ "execute": {
1001
+ "command": "after_deploy"
1002
+ }
1003
+ }
1004
+ })
1005
+ yaml_config["jobs"] = jobs
946
1006
 
947
1007
  app_yaml = yaml.dump(yaml_config)
1008
+
1009
+ console.print(f"\n[bold]Created app.yaml manifest ✅[/bold]")
1010
+ app_yaml_panel = Panel(
1011
+ Syntax(
1012
+ app_yaml.strip(),
1013
+ "yaml",
1014
+ theme="monokai",
1015
+ background_color="default",
1016
+ line_numbers=True,
1017
+ ),
1018
+ box=box.SQUARE,
1019
+ border_style="bright_black",
1020
+ expand=False,
1021
+ )
1022
+ console.print(app_yaml_panel, markup=False, highlight=True)
948
1023
  (self.wasmer_dir_path / "app.yaml").write_text(app_yaml)
949
1024
 
950
1025
  # self.inner_builder.build_serve(serve)
@@ -1026,12 +1101,17 @@ class Ctx:
1026
1101
  self.steps: List[Step] = []
1027
1102
  self.serves: Dict[str, Serve] = {}
1028
1103
  self.mounts: List[Mount] = []
1104
+ self.services: Dict[str, Service] = {}
1029
1105
 
1030
1106
  def add_package(self, package: Package) -> str:
1031
1107
  index = f"{package.name}@{package.version}" if package.version else package.name
1032
1108
  self.packages[index] = package
1033
1109
  return f"ref:package:{index}"
1034
1110
 
1111
+ def add_service(self, service: Service) -> str:
1112
+ self.services[service.name] = service
1113
+ return f"ref:service:{service.name}"
1114
+
1035
1115
  def get_ref(self, index: str) -> Any:
1036
1116
  if index.startswith("ref:package:"):
1037
1117
  return self.packages[index[len("ref:package:") :]]
@@ -1043,6 +1123,8 @@ class Ctx:
1043
1123
  return self.steps[int(index[len("ref:step:") :])]
1044
1124
  elif index.startswith("ref:mount:"):
1045
1125
  return self.mounts[int(index[len("ref:mount:") :])]
1126
+ elif index.startswith("ref:service:"):
1127
+ return self.services[index[len("ref:service:") :]]
1046
1128
  else:
1047
1129
  raise Exception(f"Invalid reference: {index}")
1048
1130
 
@@ -1072,6 +1154,10 @@ class Ctx:
1072
1154
  def dep(self, name: str, version: Optional[str] = None) -> str:
1073
1155
  package = Package(name, version)
1074
1156
  return self.add_package(package)
1157
+
1158
+ def service(self, name: str, provider: Literal["postgres", "mysql", "redis"]) -> str:
1159
+ service = Service(name, provider)
1160
+ return self.add_service(service)
1075
1161
 
1076
1162
  def serve(
1077
1163
  self,
@@ -1086,6 +1172,7 @@ class Ctx:
1086
1172
  workers: Optional[List[str]] = None,
1087
1173
  mounts: Optional[List[Mount]] = None,
1088
1174
  env: Optional[Dict[str, str]] = None,
1175
+ services: Optional[List[str]] = None,
1089
1176
  ) -> str:
1090
1177
  build_refs = [cast(Step, r) for r in self.get_refs(build)]
1091
1178
  prepare_steps: Optional[List[PrepareStep]] = None
@@ -1110,6 +1197,7 @@ class Ctx:
1110
1197
  if mounts
1111
1198
  else None,
1112
1199
  env=env,
1200
+ services=self.get_refs(services) if services else None,
1113
1201
  )
1114
1202
  return self.add_serve(serve)
1115
1203
 
@@ -1435,6 +1523,7 @@ def build(
1435
1523
  glb = sl.Globals.standard()
1436
1524
  mod = sl.Module()
1437
1525
 
1526
+ mod.add_callable("service", ctx.service)
1438
1527
  mod.add_callable("getenv", ctx.getenv)
1439
1528
  mod.add_callable("dep", ctx.dep)
1440
1529
  mod.add_callable("serve", ctx.serve)
@@ -89,6 +89,7 @@ def generate_shipit(path: Path) -> str:
89
89
  dependencies=provider.dependencies(),
90
90
  build_steps=provider.build_steps(),
91
91
  prepare=provider.prepare_steps(),
92
+ services=provider.services(),
92
93
  commands=provider.commands(),
93
94
  env=provider.env(),
94
95
  )
@@ -127,6 +128,10 @@ def generate_shipit(path: Path) -> str:
127
128
  out.append("")
128
129
  for m in plan.mounts:
129
130
  out.append(f"{m.name} = mount(\"{m.name}\")")
131
+ if plan.services:
132
+ for s in plan.services:
133
+ out.append(f"{s.name} = service(\n name=\"{s.name}\",\n provider=\"{s.provider}\"\n)")
134
+
130
135
  if plan.declarations:
131
136
  out.append(plan.declarations)
132
137
  out.append("")
@@ -157,6 +162,11 @@ def generate_shipit(path: Path) -> str:
157
162
  out.append(" commands = {")
158
163
  out.append(commands_lines)
159
164
  out.append(" },")
165
+ if plan.services:
166
+ out.append(" services=[")
167
+ for s in plan.services:
168
+ out.append(f" {s.name},")
169
+ out.append(" ],")
160
170
  if mounts_block:
161
171
  out.append(" mounts=[")
162
172
  out.append(mounts_block)
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
4
  from pathlib import Path
5
- from typing import Dict, List, Optional, Protocol
5
+ from typing import Dict, List, Optional, Protocol, Literal
6
6
 
7
7
 
8
8
  @dataclass
@@ -28,6 +28,7 @@ class Provider(Protocol):
28
28
  def prepare_steps(self) -> Optional[List[str]]: ...
29
29
  def commands(self) -> Dict[str, str]: ...
30
30
  def assets(self) -> Optional[Dict[str, str]]: ...
31
+ def services(self) -> List["ServiceSpec"]: ...
31
32
  def mounts(self) -> List["MountSpec"]: ...
32
33
  def env(self) -> Optional[Dict[str, str]]: ...
33
34
 
@@ -49,6 +50,12 @@ class MountSpec:
49
50
  attach_to_serve: bool = True
50
51
 
51
52
 
53
+ @dataclass
54
+ class ServiceSpec:
55
+ name: str
56
+ provider: Literal["postgres", "mysql", "redis"]
57
+
58
+
52
59
  @dataclass
53
60
  class ProviderPlan:
54
61
  serve_name: str
@@ -58,6 +65,7 @@ class ProviderPlan:
58
65
  dependencies: List[DependencySpec] = field(default_factory=list)
59
66
  build_steps: List[str] = field(default_factory=list)
60
67
  prepare: Optional[List[str]] = None
68
+ services: List[ServiceSpec] = field(default_factory=list)
61
69
  commands: Dict[str, str] = field(default_factory=dict)
62
70
  assets: Optional[Dict[str, str]] = None
63
71
  env: Optional[Dict[str, str]] = None
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Dict, Optional
5
5
 
6
- from .base import DetectResult, DependencySpec, Provider, _exists, _has_dependency, MountSpec
6
+ from .base import DetectResult, DependencySpec, Provider, _exists, _has_dependency, MountSpec, ServiceSpec
7
7
 
8
8
 
9
9
  class GatsbyProvider:
@@ -70,3 +70,6 @@ class GatsbyProvider:
70
70
 
71
71
  def env(self) -> Optional[Dict[str, str]]:
72
72
  return None
73
+
74
+ def services(self) -> list[ServiceSpec]:
75
+ return []
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Dict, Optional
5
5
 
6
- from .base import DetectResult, DependencySpec, Provider, _exists
6
+ from .base import DetectResult, DependencySpec, Provider, _exists, ServiceSpec
7
7
  from .staticfile import StaticFileProvider
8
8
 
9
9
  class HugoProvider(StaticFileProvider):
@@ -45,3 +45,6 @@ class HugoProvider(StaticFileProvider):
45
45
  'copy(".", ".", ignore=[".git"])',
46
46
  'run("hugo build --destination={}".format(app["build"]), group="build")',
47
47
  ]
48
+
49
+ def services(self) -> list[ServiceSpec]:
50
+ return []
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Dict, Optional
5
5
 
6
- from .base import DetectResult, DependencySpec, Provider, _exists, MountSpec
6
+ from .base import DetectResult, DependencySpec, Provider, _exists, MountSpec, ServiceSpec
7
7
 
8
8
 
9
9
  class LaravelProvider:
@@ -81,3 +81,6 @@ class LaravelProvider:
81
81
 
82
82
  def env(self) -> Optional[Dict[str, str]]:
83
83
  return None
84
+
85
+ def services(self) -> list[ServiceSpec]:
86
+ return []
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Dict, Optional
5
5
 
6
- from .base import DetectResult, DependencySpec, Provider, _exists, MountSpec
6
+ from .base import DetectResult, DependencySpec, Provider, _exists, MountSpec, ServiceSpec
7
7
  from .staticfile import StaticFileProvider
8
8
  from .python import PythonProvider
9
9
 
@@ -58,3 +58,6 @@ class MkdocsProvider(StaticFileProvider):
58
58
 
59
59
  def env(self) -> Optional[Dict[str, str]]:
60
60
  return self.python_provider.env()
61
+
62
+ def services(self) -> list[ServiceSpec]:
63
+ return []
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Dict, Optional
5
5
 
6
- from .base import DetectResult, DependencySpec, Provider, _exists, _has_dependency, MountSpec
6
+ from .base import DetectResult, DependencySpec, Provider, _exists, _has_dependency, MountSpec, ServiceSpec
7
7
 
8
8
 
9
9
  class NodeStaticProvider:
@@ -71,3 +71,6 @@ class NodeStaticProvider:
71
71
 
72
72
  def env(self) -> Optional[Dict[str, str]]:
73
73
  return None
74
+
75
+ def services(self) -> list[ServiceSpec]:
76
+ return []
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Dict, Optional
5
5
 
6
- from .base import DetectResult, DependencySpec, Provider, _exists, MountSpec
6
+ from .base import DetectResult, DependencySpec, Provider, _exists, MountSpec, ServiceSpec
7
7
 
8
8
 
9
9
  class PhpProvider:
@@ -80,3 +80,6 @@ class PhpProvider:
80
80
 
81
81
  def env(self) -> Optional[Dict[str, str]]:
82
82
  return None
83
+
84
+ def services(self) -> list[ServiceSpec]:
85
+ return []
@@ -11,6 +11,7 @@ from .base import (
11
11
  Provider,
12
12
  _exists,
13
13
  MountSpec,
14
+ ServiceSpec,
14
15
  )
15
16
 
16
17
 
@@ -74,7 +75,7 @@ class PythonProvider:
74
75
  "psycopg-binary",
75
76
  "psycopg2-binary",
76
77
  }
77
- mysql_deps = {"mysqlclient", "pymysql", "mysql-connector-python", "aiomysql"}
78
+ mysql_deps = {"mysqlclient", "pymysql", "mysql-connector-python", "aiomysql", "asyncmy"}
78
79
  found_deps = self.check_deps(
79
80
  "file://", # This is not really a dependency, but as a way to check if the install script requires all files
80
81
  "streamlit",
@@ -238,6 +239,15 @@ class PythonProvider:
238
239
  use_in_serve=True,
239
240
  )
240
241
  )
242
+ if self.uses_ffmpeg:
243
+ deps.append(
244
+ DependencySpec(
245
+ "ffmpeg",
246
+ env_var="SHIPIT_FFMPEG_VERSION",
247
+ use_in_build=False,
248
+ use_in_serve=True,
249
+ )
250
+ )
241
251
  return deps
242
252
 
243
253
  def declarations(self) -> Optional[str]:
@@ -438,6 +448,13 @@ class PythonProvider:
438
448
  env_vars["FASTMCP_HOST"] = '"0.0.0.0"'
439
449
  env_vars["FASTMCP_PORT"] = '"8000"'
440
450
  return env_vars
451
+
452
+ def services(self) -> list[ServiceSpec]:
453
+ if self.database == DatabaseType.MySQL:
454
+ return [ServiceSpec(name="database", provider="mysql")]
455
+ elif self.database == DatabaseType.PostgreSQL:
456
+ return [ServiceSpec(name="database", provider="postgres")]
457
+ return []
441
458
 
442
459
 
443
460
  def format_app_import(asgi_application: str) -> str:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
  from typing import Dict, Optional
5
5
 
6
- from .base import DetectResult, DependencySpec, Provider, _exists, MountSpec
6
+ from .base import DetectResult, DependencySpec, Provider, _exists, MountSpec, ServiceSpec
7
7
 
8
8
 
9
9
  class StaticFileProvider:
@@ -68,3 +68,6 @@ class StaticFileProvider:
68
68
 
69
69
  def env(self) -> Optional[Dict[str, str]]:
70
70
  return None
71
+
72
+ def services(self) -> list[ServiceSpec]:
73
+ return []
@@ -0,0 +1,5 @@
1
+ __all__ = ["version", "version_info"]
2
+
3
+
4
+ version = "0.6.1"
5
+ version_info = (0, 6, 1, "final", 0)
@@ -1,5 +0,0 @@
1
- __all__ = ["version", "version_info"]
2
-
3
-
4
- version = "0.5.2"
5
- version_info = (0, 5, 2, "final", 0)
File without changes
File without changes