shipit-cli 0.10.1__tar.gz → 0.11.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.
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/PKG-INFO +1 -1
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/pyproject.toml +1 -1
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/cli.py +192 -42
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/generator.py +27 -11
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/base.py +3 -13
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/hugo.py +3 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/laravel.py +3 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/mkdocs.py +3 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/node_static.py +4 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/php.py +41 -17
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/python.py +12 -5
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/registry.py +0 -2
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/staticfile.py +3 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/providers/wordpress.py +7 -5
- shipit_cli-0.11.1/src/shipit/version.py +5 -0
- shipit_cli-0.10.1/src/shipit/providers/gatsby.py +0 -87
- shipit_cli-0.10.1/src/shipit/version.py +0 -5
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/.gitignore +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/README.md +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/__init__.py +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/assets/php/php.ini +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/assets/wordpress/install.sh +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/assets/wordpress/wp-config.php +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/src/shipit/procfile.py +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/tests/test_e2e.py +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.1}/tests/test_generate_shipit_examples.py +0 -0
- {shipit_cli-0.10.1 → shipit_cli-0.11.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.
|
|
3
|
+
Version: 0.11.1
|
|
4
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
|
|
@@ -16,6 +16,7 @@ from typing import (
|
|
|
16
16
|
Optional,
|
|
17
17
|
Protocol,
|
|
18
18
|
Set,
|
|
19
|
+
Tuple,
|
|
19
20
|
TypedDict,
|
|
20
21
|
Union,
|
|
21
22
|
Literal,
|
|
@@ -33,7 +34,7 @@ from rich.rule import Rule
|
|
|
33
34
|
from rich.syntax import Syntax
|
|
34
35
|
|
|
35
36
|
from shipit.version import version as shipit_version
|
|
36
|
-
from shipit.generator import generate_shipit
|
|
37
|
+
from shipit.generator import generate_shipit, detect_provider
|
|
37
38
|
from shipit.providers.base import CustomCommands
|
|
38
39
|
from shipit.procfile import Procfile
|
|
39
40
|
from dotenv import dotenv_values
|
|
@@ -88,11 +89,13 @@ class Serve:
|
|
|
88
89
|
class Package:
|
|
89
90
|
name: str
|
|
90
91
|
version: Optional[str] = None
|
|
92
|
+
architecture: Optional[Literal["64-bit", "32-bit"]] = None
|
|
91
93
|
|
|
92
94
|
def __str__(self) -> str: # pragma: no cover - simple representation
|
|
95
|
+
name = f"{self.name}({self.architecture})" if self.architecture else self.name
|
|
93
96
|
if self.version is None:
|
|
94
|
-
return
|
|
95
|
-
return f"{
|
|
97
|
+
return name
|
|
98
|
+
return f"{name}@{self.version}"
|
|
96
99
|
|
|
97
100
|
|
|
98
101
|
@dataclass
|
|
@@ -780,6 +783,25 @@ class WasmerBuilder:
|
|
|
780
783
|
"dependencies": {
|
|
781
784
|
"latest": "php/php-32@=8.3.2102",
|
|
782
785
|
"8.3": "php/php-32@=8.3.2102",
|
|
786
|
+
"8.2": "php/php-32@=8.2.2801",
|
|
787
|
+
"8.1": "php/php-32@=8.1.3201",
|
|
788
|
+
"7.4": "php/php-32@=7.4.3301",
|
|
789
|
+
},
|
|
790
|
+
"architecture_dependencies": {
|
|
791
|
+
"64-bit": {
|
|
792
|
+
"latest": "php/php-64@=8.3.2102",
|
|
793
|
+
"8.3": "php/php-64@=8.3.2102",
|
|
794
|
+
"8.2": "php/php-64@=8.2.2801",
|
|
795
|
+
"8.1": "php/php-64@=8.1.3201",
|
|
796
|
+
"7.4": "php/php-64@=7.4.3301",
|
|
797
|
+
},
|
|
798
|
+
"32-bit": {
|
|
799
|
+
"latest": "php/php-32@=8.3.2102",
|
|
800
|
+
"8.3": "php/php-32@=8.3.2102",
|
|
801
|
+
"8.2": "php/php-32@=8.2.2801",
|
|
802
|
+
"8.1": "php/php-32@=8.1.3201",
|
|
803
|
+
"7.4": "php/php-32@=7.4.3301",
|
|
804
|
+
},
|
|
783
805
|
},
|
|
784
806
|
"scripts": {"php"},
|
|
785
807
|
"aliases": {},
|
|
@@ -925,13 +947,20 @@ class WasmerBuilder:
|
|
|
925
947
|
for dep in deps:
|
|
926
948
|
if dep.name in self.mapper:
|
|
927
949
|
version = dep.version or "latest"
|
|
928
|
-
|
|
950
|
+
mapped_dependencies = self.mapper[dep.name]["dependencies"]
|
|
951
|
+
if dep.architecture:
|
|
952
|
+
architecture_dependencies = (
|
|
953
|
+
self.mapper[dep.name]
|
|
954
|
+
.get("architecture_dependencies", {})
|
|
955
|
+
.get(dep.architecture, {})
|
|
956
|
+
)
|
|
957
|
+
if architecture_dependencies:
|
|
958
|
+
mapped_dependencies = architecture_dependencies
|
|
959
|
+
if version in mapped_dependencies:
|
|
929
960
|
console.print(
|
|
930
961
|
f"* {dep.name}@{version} mapped to {self.mapper[dep.name]['dependencies'][version]}"
|
|
931
962
|
)
|
|
932
|
-
package_name, version =
|
|
933
|
-
version
|
|
934
|
-
].split("@")
|
|
963
|
+
package_name, version = mapped_dependencies[version].split("@")
|
|
935
964
|
dependencies.add(package_name, version)
|
|
936
965
|
scripts = self.mapper[dep.name].get("scripts") or []
|
|
937
966
|
for script in scripts:
|
|
@@ -1156,6 +1185,7 @@ class Ctx:
|
|
|
1156
1185
|
self.mounts: List[Mount] = []
|
|
1157
1186
|
self.volumes: List[Volume] = []
|
|
1158
1187
|
self.services: Dict[str, Service] = {}
|
|
1188
|
+
self.getenv_variables: Set[str] = set()
|
|
1159
1189
|
|
|
1160
1190
|
def add_package(self, package: Package) -> str:
|
|
1161
1191
|
index = f"{package.name}@{package.version}" if package.version else package.name
|
|
@@ -1202,10 +1232,16 @@ class Ctx:
|
|
|
1202
1232
|
return f"ref:step:{len(self.steps) - 1}"
|
|
1203
1233
|
|
|
1204
1234
|
def getenv(self, name: str) -> Optional[str]:
|
|
1235
|
+
self.getenv_variables.add(name)
|
|
1205
1236
|
return self.builder.getenv(name)
|
|
1206
1237
|
|
|
1207
|
-
def dep(
|
|
1208
|
-
|
|
1238
|
+
def dep(
|
|
1239
|
+
self,
|
|
1240
|
+
name: str,
|
|
1241
|
+
version: Optional[str] = None,
|
|
1242
|
+
architecture: Optional[Literal["64-bit", "32-bit"]] = None,
|
|
1243
|
+
) -> str:
|
|
1244
|
+
package = Package(name, version, architecture)
|
|
1209
1245
|
return self.add_package(package)
|
|
1210
1246
|
|
|
1211
1247
|
def service(
|
|
@@ -1318,6 +1354,43 @@ class Ctx:
|
|
|
1318
1354
|
}
|
|
1319
1355
|
|
|
1320
1356
|
|
|
1357
|
+
def evaluate_shipit(path: Path, builder: Builder) -> Tuple[Ctx, Serve]:
|
|
1358
|
+
shipit_file = path / "Shipit"
|
|
1359
|
+
if not shipit_file.exists():
|
|
1360
|
+
raise FileNotFoundError(
|
|
1361
|
+
f"Shipit file not found at {shipit_file}. Run `shipit generate {path}` to create it."
|
|
1362
|
+
)
|
|
1363
|
+
source = shipit_file.read_text()
|
|
1364
|
+
ctx = Ctx(builder)
|
|
1365
|
+
glb = sl.Globals.standard()
|
|
1366
|
+
mod = sl.Module()
|
|
1367
|
+
|
|
1368
|
+
mod.add_callable("service", ctx.service)
|
|
1369
|
+
mod.add_callable("getenv", ctx.getenv)
|
|
1370
|
+
mod.add_callable("dep", ctx.dep)
|
|
1371
|
+
mod.add_callable("serve", ctx.serve)
|
|
1372
|
+
mod.add_callable("run", ctx.run)
|
|
1373
|
+
mod.add_callable("mount", ctx.mount)
|
|
1374
|
+
mod.add_callable("volume", ctx.volume)
|
|
1375
|
+
mod.add_callable("workdir", ctx.workdir)
|
|
1376
|
+
mod.add_callable("copy", ctx.copy)
|
|
1377
|
+
mod.add_callable("path", ctx.path)
|
|
1378
|
+
mod.add_callable("env", ctx.env)
|
|
1379
|
+
mod.add_callable("use", ctx.use)
|
|
1380
|
+
|
|
1381
|
+
dialect = sl.Dialect.extended()
|
|
1382
|
+
dialect.enable_f_strings = True
|
|
1383
|
+
|
|
1384
|
+
ast = sl.parse("shipit", source, dialect=dialect)
|
|
1385
|
+
|
|
1386
|
+
sl.eval(mod, ast, glb)
|
|
1387
|
+
if not ctx.serves:
|
|
1388
|
+
raise ValueError(f"No serve definition found in {shipit_file}")
|
|
1389
|
+
assert len(ctx.serves) <= 1, "Only one serve is allowed for now"
|
|
1390
|
+
serve = next(iter(ctx.serves.values()))
|
|
1391
|
+
return ctx, serve
|
|
1392
|
+
|
|
1393
|
+
|
|
1321
1394
|
def print_help() -> None:
|
|
1322
1395
|
panel = Panel(
|
|
1323
1396
|
f"Shipit {shipit_version}",
|
|
@@ -1418,6 +1491,10 @@ def auto(
|
|
|
1418
1491
|
None,
|
|
1419
1492
|
help="The environment to use (defaults to `.env`, it will use .env.<env_name> if provided)",
|
|
1420
1493
|
),
|
|
1494
|
+
use_provider: Optional[str] = typer.Option(
|
|
1495
|
+
None,
|
|
1496
|
+
help="Use a specific provider to build the project.",
|
|
1497
|
+
),
|
|
1421
1498
|
):
|
|
1422
1499
|
if not path.exists():
|
|
1423
1500
|
raise Exception(f"The path {path} does not exist")
|
|
@@ -1430,6 +1507,7 @@ def auto(
|
|
|
1430
1507
|
install_command=install_command,
|
|
1431
1508
|
build_command=build_command,
|
|
1432
1509
|
start_command=start_command,
|
|
1510
|
+
use_provider=use_provider,
|
|
1433
1511
|
)
|
|
1434
1512
|
|
|
1435
1513
|
build(
|
|
@@ -1488,6 +1566,10 @@ def generate(
|
|
|
1488
1566
|
None,
|
|
1489
1567
|
help="The start command to use (overwrites the default)",
|
|
1490
1568
|
),
|
|
1569
|
+
use_provider: Optional[str] = typer.Option(
|
|
1570
|
+
None,
|
|
1571
|
+
help="Use a specific provider to build the project.",
|
|
1572
|
+
),
|
|
1491
1573
|
):
|
|
1492
1574
|
if not path.exists():
|
|
1493
1575
|
raise Exception(f"The path {path} does not exist")
|
|
@@ -1518,7 +1600,7 @@ def generate(
|
|
|
1518
1600
|
custom_commands.install = install_command
|
|
1519
1601
|
if build_command:
|
|
1520
1602
|
custom_commands.build = build_command
|
|
1521
|
-
content = generate_shipit(path, custom_commands)
|
|
1603
|
+
content = generate_shipit(path, custom_commands, use_provider=use_provider)
|
|
1522
1604
|
out.write_text(content)
|
|
1523
1605
|
console.print(f"[bold]Generated Shipit[/bold] at {out.absolute()}")
|
|
1524
1606
|
|
|
@@ -1528,7 +1610,8 @@ def generate(
|
|
|
1528
1610
|
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
|
|
1529
1611
|
)
|
|
1530
1612
|
def _default(ctx: typer.Context) -> None:
|
|
1531
|
-
|
|
1613
|
+
if ctx.invoked_subcommand is None:
|
|
1614
|
+
print_help()
|
|
1532
1615
|
|
|
1533
1616
|
|
|
1534
1617
|
@app.command(name="deploy")
|
|
@@ -1612,6 +1695,103 @@ def serve(
|
|
|
1612
1695
|
raise Exception("Wasmer deploy is only supported for Wasmer builders")
|
|
1613
1696
|
|
|
1614
1697
|
|
|
1698
|
+
@app.command(name="plan")
|
|
1699
|
+
def plan(
|
|
1700
|
+
path: Path = typer.Argument(
|
|
1701
|
+
Path("."),
|
|
1702
|
+
help="Project path (defaults to current directory).",
|
|
1703
|
+
show_default=False,
|
|
1704
|
+
),
|
|
1705
|
+
wasmer: bool = typer.Option(
|
|
1706
|
+
False,
|
|
1707
|
+
help="Use Wasmer to evaluate the project.",
|
|
1708
|
+
),
|
|
1709
|
+
wasmer_bin: Optional[Path] = typer.Option(
|
|
1710
|
+
None,
|
|
1711
|
+
help="The path to the Wasmer binary.",
|
|
1712
|
+
),
|
|
1713
|
+
wasmer_registry: Optional[str] = typer.Option(
|
|
1714
|
+
None,
|
|
1715
|
+
help="Wasmer registry.",
|
|
1716
|
+
),
|
|
1717
|
+
wasmer_token: Optional[str] = typer.Option(
|
|
1718
|
+
None,
|
|
1719
|
+
help="Wasmer token.",
|
|
1720
|
+
),
|
|
1721
|
+
docker: bool = typer.Option(
|
|
1722
|
+
False,
|
|
1723
|
+
help="Use Docker to evaluate the project.",
|
|
1724
|
+
),
|
|
1725
|
+
docker_client: Optional[str] = typer.Option(
|
|
1726
|
+
None,
|
|
1727
|
+
help="Use a specific Docker client (such as depot, podman, etc.)",
|
|
1728
|
+
),
|
|
1729
|
+
) -> None:
|
|
1730
|
+
if not path.exists():
|
|
1731
|
+
raise Exception(f"The path {path} does not exist")
|
|
1732
|
+
|
|
1733
|
+
custom_commands = CustomCommands()
|
|
1734
|
+
procfile_path = path / "Procfile"
|
|
1735
|
+
if procfile_path.exists():
|
|
1736
|
+
try:
|
|
1737
|
+
procfile = Procfile.loads(procfile_path.read_text())
|
|
1738
|
+
custom_commands.start = procfile.get_start_command()
|
|
1739
|
+
except Exception:
|
|
1740
|
+
pass
|
|
1741
|
+
|
|
1742
|
+
builder: Builder
|
|
1743
|
+
if docker or docker_client:
|
|
1744
|
+
builder = DockerBuilder(path, docker_client)
|
|
1745
|
+
else:
|
|
1746
|
+
builder = LocalBuilder(path)
|
|
1747
|
+
if wasmer:
|
|
1748
|
+
builder = WasmerBuilder(
|
|
1749
|
+
builder, path, registry=wasmer_registry, token=wasmer_token, bin=wasmer_bin
|
|
1750
|
+
)
|
|
1751
|
+
|
|
1752
|
+
ctx, serve = evaluate_shipit(path, builder)
|
|
1753
|
+
metadata_commands: Dict[str, Optional[str]] = {
|
|
1754
|
+
"start": serve.commands.get("start"),
|
|
1755
|
+
"after_deploy": serve.commands.get("after_deploy"),
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
def _collect_group_commands(group: str) -> Optional[str]:
|
|
1759
|
+
commands = [
|
|
1760
|
+
step.command
|
|
1761
|
+
for step in serve.build
|
|
1762
|
+
if isinstance(step, RunStep) and step.group == group
|
|
1763
|
+
]
|
|
1764
|
+
if not commands:
|
|
1765
|
+
return None
|
|
1766
|
+
return " && ".join(commands)
|
|
1767
|
+
|
|
1768
|
+
metadata_install = _collect_group_commands("install")
|
|
1769
|
+
metadata_build = _collect_group_commands("build")
|
|
1770
|
+
metadata_commands["install"] = metadata_install
|
|
1771
|
+
metadata_commands["build"] = metadata_build
|
|
1772
|
+
platform: Optional[str]
|
|
1773
|
+
try:
|
|
1774
|
+
provider_cls = detect_provider(path, custom_commands)
|
|
1775
|
+
provider_instance = provider_cls(path, custom_commands)
|
|
1776
|
+
provider_instance.initialize()
|
|
1777
|
+
platform = provider_instance.platform()
|
|
1778
|
+
except Exception:
|
|
1779
|
+
platform = None
|
|
1780
|
+
plan_output = {
|
|
1781
|
+
"provider": serve.provider,
|
|
1782
|
+
"metadata": {
|
|
1783
|
+
"platform": platform,
|
|
1784
|
+
"commands": metadata_commands,
|
|
1785
|
+
},
|
|
1786
|
+
"config": sorted(ctx.getenv_variables),
|
|
1787
|
+
"services": [
|
|
1788
|
+
{"name": svc.name, "provider": svc.provider}
|
|
1789
|
+
for svc in (serve.services or [])
|
|
1790
|
+
],
|
|
1791
|
+
}
|
|
1792
|
+
print(json.dumps(plan_output, indent=4))
|
|
1793
|
+
|
|
1794
|
+
|
|
1615
1795
|
@app.command(name="build")
|
|
1616
1796
|
def build(
|
|
1617
1797
|
path: Path = typer.Argument(
|
|
@@ -1659,12 +1839,6 @@ def build(
|
|
|
1659
1839
|
if not path.exists():
|
|
1660
1840
|
raise Exception(f"The path {path} does not exist")
|
|
1661
1841
|
|
|
1662
|
-
ab_file = path / "Shipit"
|
|
1663
|
-
if not ab_file.exists():
|
|
1664
|
-
raise FileNotFoundError(
|
|
1665
|
-
f"Shipit file not found at {ab_file}. Run `shipit generate {path}` to create it."
|
|
1666
|
-
)
|
|
1667
|
-
source = open(ab_file).read()
|
|
1668
1842
|
builder: Builder
|
|
1669
1843
|
if docker or docker_client:
|
|
1670
1844
|
builder = DockerBuilder(path, docker_client)
|
|
@@ -1675,30 +1849,7 @@ def build(
|
|
|
1675
1849
|
builder, path, registry=wasmer_registry, token=wasmer_token, bin=wasmer_bin
|
|
1676
1850
|
)
|
|
1677
1851
|
|
|
1678
|
-
ctx =
|
|
1679
|
-
glb = sl.Globals.standard()
|
|
1680
|
-
mod = sl.Module()
|
|
1681
|
-
|
|
1682
|
-
mod.add_callable("service", ctx.service)
|
|
1683
|
-
mod.add_callable("getenv", ctx.getenv)
|
|
1684
|
-
mod.add_callable("dep", ctx.dep)
|
|
1685
|
-
mod.add_callable("serve", ctx.serve)
|
|
1686
|
-
mod.add_callable("run", ctx.run)
|
|
1687
|
-
mod.add_callable("mount", ctx.mount)
|
|
1688
|
-
mod.add_callable("volume", ctx.volume)
|
|
1689
|
-
mod.add_callable("workdir", ctx.workdir)
|
|
1690
|
-
mod.add_callable("copy", ctx.copy)
|
|
1691
|
-
mod.add_callable("path", ctx.path)
|
|
1692
|
-
mod.add_callable("env", ctx.env)
|
|
1693
|
-
mod.add_callable("use", ctx.use)
|
|
1694
|
-
|
|
1695
|
-
dialect = sl.Dialect.extended()
|
|
1696
|
-
dialect.enable_f_strings = True
|
|
1697
|
-
|
|
1698
|
-
ast = sl.parse("shipit", source, dialect=dialect)
|
|
1699
|
-
|
|
1700
|
-
sl.eval(mod, ast, glb)
|
|
1701
|
-
assert len(ctx.serves) <= 1, "Only one serve is allowed for now"
|
|
1852
|
+
ctx, serve = evaluate_shipit(path, builder)
|
|
1702
1853
|
env = {
|
|
1703
1854
|
"PATH": "",
|
|
1704
1855
|
"COLORTERM": os.environ.get("COLORTERM", ""),
|
|
@@ -1706,7 +1857,6 @@ def build(
|
|
|
1706
1857
|
"LS_COLORS": os.environ.get("LS_COLORS", "0"),
|
|
1707
1858
|
"CLICOLOR": os.environ.get("CLICOLOR", "0"),
|
|
1708
1859
|
}
|
|
1709
|
-
serve = next(iter(ctx.serves.values()))
|
|
1710
1860
|
|
|
1711
1861
|
if skip_docker_if_safe_build and serve.build and len(serve.build) > 0:
|
|
1712
1862
|
# If it doesn't have a run step, then it's safe to skip Docker and run all the
|
|
@@ -64,27 +64,39 @@ def _emit_dependencies_declarations(
|
|
|
64
64
|
declared.add(alias)
|
|
65
65
|
|
|
66
66
|
version_var = None
|
|
67
|
+
architecture_var = None
|
|
67
68
|
if dep.env_var:
|
|
68
69
|
default = f' or "{dep.default_version}"' if dep.default_version else ""
|
|
69
70
|
version_key = alias + "_version"
|
|
70
71
|
lines.append(f'{version_key} = getenv("{dep.env_var}"){default}')
|
|
71
72
|
version_var = version_key
|
|
73
|
+
if dep.architecture_var:
|
|
74
|
+
architecture_key = alias + "_architecture"
|
|
75
|
+
lines.append(f'{architecture_key} = getenv("{dep.architecture_var}")')
|
|
76
|
+
architecture_var = architecture_key
|
|
77
|
+
vars = [f'"{dep.name}"']
|
|
72
78
|
if version_var:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
vars.append(version_var)
|
|
80
|
+
if architecture_var:
|
|
81
|
+
vars.append(f"architecture={architecture_var}")
|
|
82
|
+
lines.append(f"{alias} = dep({', '.join(vars)})")
|
|
76
83
|
|
|
77
84
|
return "\n".join(lines), serve_vars, build_vars
|
|
78
85
|
|
|
79
86
|
|
|
80
|
-
def generate_shipit(path: Path, custom_commands: CustomCommands) -> str:
|
|
81
|
-
provider_cls =
|
|
87
|
+
def generate_shipit(path: Path, custom_commands: CustomCommands, use_provider: Optional[str] = None) -> str:
|
|
88
|
+
provider_cls = None
|
|
89
|
+
if use_provider:
|
|
90
|
+
provider_cls = next((p for p in _providers() if p.name().lower() == use_provider.lower()), None)
|
|
91
|
+
if not provider_cls:
|
|
92
|
+
provider_cls = detect_provider(path, custom_commands)
|
|
82
93
|
provider = provider_cls(path, custom_commands)
|
|
83
94
|
|
|
84
95
|
# Collect parts
|
|
85
96
|
plan = ProviderPlan(
|
|
86
97
|
serve_name=provider.serve_name(),
|
|
87
98
|
provider=provider.provider_kind(),
|
|
99
|
+
platform=provider.platform(),
|
|
88
100
|
mounts=provider.mounts(),
|
|
89
101
|
volumes=provider.volumes(),
|
|
90
102
|
declarations=provider.declarations(),
|
|
@@ -109,7 +121,9 @@ def generate_shipit(path: Path, custom_commands: CustomCommands) -> str:
|
|
|
109
121
|
|
|
110
122
|
build_steps_block = ",\n".join([f" {s}" for s in build_steps])
|
|
111
123
|
deps_array = ", ".join(serve_dep_vars)
|
|
112
|
-
commands_lines = ",\n".join(
|
|
124
|
+
commands_lines = ",\n".join(
|
|
125
|
+
[f' "{k}": {v}.replace("$PORT", PORT)' for k, v in plan.commands.items()]
|
|
126
|
+
)
|
|
113
127
|
env_lines = None
|
|
114
128
|
if plan.env is not None:
|
|
115
129
|
if len(plan.env) == 0:
|
|
@@ -137,24 +151,26 @@ def generate_shipit(path: Path, custom_commands: CustomCommands) -> str:
|
|
|
137
151
|
out.append("")
|
|
138
152
|
|
|
139
153
|
for m in plan.mounts:
|
|
140
|
-
out.append(f
|
|
154
|
+
out.append(f'{m.name} = mount("{m.name}")')
|
|
141
155
|
out.append("")
|
|
142
156
|
|
|
143
157
|
if plan.volumes:
|
|
144
158
|
for v in plan.volumes:
|
|
145
|
-
out.append(f
|
|
159
|
+
out.append(f'{v.var_name or v.name} = volume("{v.name}", {v.serve_path})')
|
|
146
160
|
out.append("")
|
|
147
161
|
|
|
148
162
|
if plan.services:
|
|
149
163
|
for s in plan.services:
|
|
150
|
-
out.append(
|
|
164
|
+
out.append(
|
|
165
|
+
f'{s.name} = service(\n name="{s.name}",\n provider="{s.provider}"\n)'
|
|
166
|
+
)
|
|
151
167
|
out.append("")
|
|
152
168
|
|
|
153
|
-
out.append(
|
|
169
|
+
out.append('PORT = getenv("PORT") or "8080"')
|
|
154
170
|
|
|
155
171
|
if plan.declarations:
|
|
156
172
|
out.append(plan.declarations)
|
|
157
|
-
|
|
173
|
+
|
|
158
174
|
out.append("")
|
|
159
175
|
out.append("serve(")
|
|
160
176
|
out.append(f' name="{plan.serve_name}",')
|
|
@@ -29,6 +29,7 @@ class Provider(Protocol):
|
|
|
29
29
|
# Structured plan steps (no path args; use self.path)
|
|
30
30
|
def serve_name(self) -> str: ...
|
|
31
31
|
def provider_kind(self) -> str: ...
|
|
32
|
+
def platform(self) -> Optional[str]: ...
|
|
32
33
|
def dependencies(self) -> list["DependencySpec"]: ...
|
|
33
34
|
def declarations(self) -> Optional[str]: ...
|
|
34
35
|
def build_steps(self) -> list[str]: ...
|
|
@@ -46,6 +47,7 @@ class DependencySpec:
|
|
|
46
47
|
name: str
|
|
47
48
|
env_var: Optional[str] = None
|
|
48
49
|
default_version: Optional[str] = None
|
|
50
|
+
architecture_var: Optional[str] = None
|
|
49
51
|
alias: Optional[str] = None # Variable name in Shipit plan
|
|
50
52
|
use_in_build: bool = False
|
|
51
53
|
use_in_serve: bool = False
|
|
@@ -77,6 +79,7 @@ class ProviderPlan:
|
|
|
77
79
|
serve_name: str
|
|
78
80
|
provider: str
|
|
79
81
|
mounts: List[MountSpec]
|
|
82
|
+
platform: Optional[str] = None
|
|
80
83
|
volumes: List[VolumeSpec] = field(default_factory=list)
|
|
81
84
|
declarations: Optional[str] = None
|
|
82
85
|
dependencies: List[DependencySpec] = field(default_factory=list)
|
|
@@ -89,16 +92,3 @@ class ProviderPlan:
|
|
|
89
92
|
|
|
90
93
|
def _exists(path: Path, *candidates: str) -> bool:
|
|
91
94
|
return any((path / c).exists() for c in candidates)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def _has_dependency(pkg_json: Path, dep: str) -> bool:
|
|
95
|
-
try:
|
|
96
|
-
import json
|
|
97
|
-
|
|
98
|
-
data = json.loads(pkg_json.read_text())
|
|
99
|
-
for section in ("dependencies", "devDependencies", "peerDependencies"):
|
|
100
|
-
if dep in data.get(section, {}):
|
|
101
|
-
return True
|
|
102
|
-
except Exception:
|
|
103
|
-
return False
|
|
104
|
-
return False
|
|
@@ -43,6 +43,9 @@ class MkdocsProvider(StaticFileProvider):
|
|
|
43
43
|
def provider_kind(self) -> str:
|
|
44
44
|
return "mkdocs-site"
|
|
45
45
|
|
|
46
|
+
def platform(self) -> Optional[str]:
|
|
47
|
+
return "mkdocs"
|
|
48
|
+
|
|
46
49
|
def dependencies(self) -> list[DependencySpec]:
|
|
47
50
|
return [
|
|
48
51
|
*self.python_provider.dependencies(),
|
|
@@ -122,6 +122,7 @@ class NodeStaticProvider(StaticFileProvider):
|
|
|
122
122
|
|
|
123
123
|
def __init__(self, path: Path, custom_commands: CustomCommands):
|
|
124
124
|
super().__init__(path, custom_commands)
|
|
125
|
+
self.static_generator: Optional[StaticGenerator] = None
|
|
125
126
|
if (path / "package-lock.json").exists():
|
|
126
127
|
self.package_manager = PackageManager.NPM
|
|
127
128
|
elif (path / "pnpm-lock.yaml").exists():
|
|
@@ -233,6 +234,9 @@ class NodeStaticProvider(StaticFileProvider):
|
|
|
233
234
|
def provider_kind(self) -> str:
|
|
234
235
|
return "staticsite"
|
|
235
236
|
|
|
237
|
+
def platform(self) -> Optional[str]:
|
|
238
|
+
return self.static_generator.value if self.static_generator else None
|
|
239
|
+
|
|
236
240
|
def dependencies(self) -> list[DependencySpec]:
|
|
237
241
|
package_manager_dep = self.package_manager.as_dependency(self.path)
|
|
238
242
|
package_manager_dep.use_in_build = True
|
|
@@ -20,19 +20,30 @@ class PhpProvider:
|
|
|
20
20
|
def __init__(self, path: Path, custom_commands: CustomCommands):
|
|
21
21
|
self.path = path
|
|
22
22
|
self.custom_commands = custom_commands
|
|
23
|
-
|
|
23
|
+
self.has_composer = _exists(self.path, "composer.json", "composer.lock") or (
|
|
24
|
+
custom_commands.install and custom_commands.install.startswith("composer ")
|
|
25
|
+
)
|
|
26
|
+
|
|
24
27
|
@classmethod
|
|
25
28
|
def name(cls) -> str:
|
|
26
29
|
return "php"
|
|
27
30
|
|
|
28
31
|
@classmethod
|
|
29
|
-
def detect(
|
|
32
|
+
def detect(
|
|
33
|
+
cls, path: Path, custom_commands: CustomCommands
|
|
34
|
+
) -> Optional[DetectResult]:
|
|
30
35
|
if _exists(path, "composer.json") and _exists(path, "public/index.php"):
|
|
31
36
|
return DetectResult(cls.name(), 60)
|
|
32
|
-
if
|
|
37
|
+
if (
|
|
38
|
+
_exists(path, "index.php")
|
|
39
|
+
or _exists(path, "public/index.php")
|
|
40
|
+
or _exists(path, "app/index.php")
|
|
41
|
+
):
|
|
33
42
|
return DetectResult(cls.name(), 10)
|
|
34
43
|
if custom_commands.start and custom_commands.start.startswith("php "):
|
|
35
44
|
return DetectResult(cls.name(), 70)
|
|
45
|
+
if custom_commands.install and custom_commands.install.startswith("composer "):
|
|
46
|
+
return DetectResult(cls.name(), 30)
|
|
36
47
|
return None
|
|
37
48
|
|
|
38
49
|
def initialize(self) -> None:
|
|
@@ -44,8 +55,8 @@ class PhpProvider:
|
|
|
44
55
|
def provider_kind(self) -> str:
|
|
45
56
|
return "php"
|
|
46
57
|
|
|
47
|
-
def
|
|
48
|
-
return
|
|
58
|
+
def platform(self) -> Optional[str]:
|
|
59
|
+
return None
|
|
49
60
|
|
|
50
61
|
def dependencies(self) -> list[DependencySpec]:
|
|
51
62
|
deps = [
|
|
@@ -53,32 +64,39 @@ class PhpProvider:
|
|
|
53
64
|
"php",
|
|
54
65
|
env_var="SHIPIT_PHP_VERSION",
|
|
55
66
|
default_version="8.3",
|
|
67
|
+
architecture_var="SHIPIT_PHP_ARCHITECTURE",
|
|
56
68
|
use_in_build=True,
|
|
57
69
|
use_in_serve=True,
|
|
58
70
|
),
|
|
59
71
|
]
|
|
60
|
-
if self.has_composer
|
|
72
|
+
if self.has_composer:
|
|
61
73
|
deps.append(DependencySpec("composer", use_in_build=True))
|
|
62
74
|
deps.append(DependencySpec("bash", use_in_serve=True))
|
|
63
75
|
return deps
|
|
64
76
|
|
|
65
77
|
def declarations(self) -> Optional[str]:
|
|
66
|
-
|
|
78
|
+
if self.has_composer:
|
|
79
|
+
return 'HOME = getenv("HOME")\n'
|
|
80
|
+
return None
|
|
67
81
|
|
|
68
82
|
def build_steps(self) -> list[str]:
|
|
69
83
|
steps = [
|
|
70
|
-
|
|
84
|
+
'workdir(app["build"])',
|
|
71
85
|
]
|
|
72
86
|
if _exists(self.path, "php.ini"):
|
|
73
87
|
steps.append('copy("php.ini", "{}/php.ini".format(assets["build"]))')
|
|
74
88
|
else:
|
|
75
|
-
steps.append(
|
|
89
|
+
steps.append(
|
|
90
|
+
'copy("php/php.ini", "{}/php.ini".format(assets["build"]), base="assets")'
|
|
91
|
+
)
|
|
76
92
|
|
|
77
|
-
if self.has_composer
|
|
78
|
-
steps.append(
|
|
79
|
-
steps.append(
|
|
93
|
+
if self.has_composer:
|
|
94
|
+
steps.append('env(HOME=HOME, COMPOSER_FUND="0")')
|
|
95
|
+
steps.append(
|
|
96
|
+
'run("composer install --optimize-autoloader --no-scripts --no-interaction", inputs=["composer.json", "composer.lock"], outputs=["."], group="install")'
|
|
97
|
+
)
|
|
80
98
|
|
|
81
|
-
steps.append(
|
|
99
|
+
steps.append('copy(".", ".", ignore=[".git"])')
|
|
82
100
|
return steps
|
|
83
101
|
|
|
84
102
|
def prepare_steps(self) -> Optional[list[str]]:
|
|
@@ -92,12 +110,18 @@ class PhpProvider:
|
|
|
92
110
|
|
|
93
111
|
def base_commands(self) -> Dict[str, str]:
|
|
94
112
|
if _exists(self.path, "public/index.php"):
|
|
95
|
-
return {
|
|
113
|
+
return {
|
|
114
|
+
"start": '"php -S localhost:{} -t {}/public".format(PORT, app["serve"])'
|
|
115
|
+
}
|
|
96
116
|
elif _exists(self.path, "app/index.php"):
|
|
97
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
"start": '"php -S localhost:{} -t {}/app".format(PORT, app["serve"])'
|
|
119
|
+
}
|
|
98
120
|
elif _exists(self.path, "index.php"):
|
|
99
121
|
return {"start": '"php -S localhost:{} -t {}".format(PORT, app["serve"])'}
|
|
100
|
-
return {
|
|
122
|
+
return {
|
|
123
|
+
"start": '"php -S localhost:{} -t {}".format(PORT, app["serve"])',
|
|
124
|
+
}
|
|
101
125
|
|
|
102
126
|
def mounts(self) -> list[MountSpec]:
|
|
103
127
|
return [
|
|
@@ -112,6 +136,6 @@ class PhpProvider:
|
|
|
112
136
|
return {
|
|
113
137
|
"PHP_INI_SCAN_DIR": '"{}".format(assets["serve"])',
|
|
114
138
|
}
|
|
115
|
-
|
|
139
|
+
|
|
116
140
|
def services(self) -> list[ServiceSpec]:
|
|
117
141
|
return []
|
|
@@ -216,7 +216,7 @@ class PythonProvider:
|
|
|
216
216
|
return DetectResult(cls.name(), 70)
|
|
217
217
|
return DetectResult(cls.name(), 50)
|
|
218
218
|
if custom_commands.start:
|
|
219
|
-
if custom_commands.start.startswith("python ") or custom_commands.start.startswith("uv ") or custom_commands.start.startswith("uvicorn "):
|
|
219
|
+
if custom_commands.start.startswith("python ") or custom_commands.start.startswith("uv ") or custom_commands.start.startswith("uvicorn ") or custom_commands.start.startswith("gunicorn "):
|
|
220
220
|
return DetectResult(cls.name(), 80)
|
|
221
221
|
return None
|
|
222
222
|
|
|
@@ -229,6 +229,9 @@ class PythonProvider:
|
|
|
229
229
|
def provider_kind(self) -> str:
|
|
230
230
|
return "python"
|
|
231
231
|
|
|
232
|
+
def platform(self) -> Optional[str]:
|
|
233
|
+
return self.framework.value if self.framework else None
|
|
234
|
+
|
|
232
235
|
def dependencies(self) -> list[DependencySpec]:
|
|
233
236
|
deps = [
|
|
234
237
|
DependencySpec(
|
|
@@ -307,9 +310,9 @@ class PythonProvider:
|
|
|
307
310
|
# Join inputs
|
|
308
311
|
inputs = ", ".join([f'"{input}"' for input in input_files])
|
|
309
312
|
steps += [
|
|
310
|
-
'env(UV_PROJECT_ENVIRONMENT=local_venv["build"] if cross_platform else venv["build"])',
|
|
313
|
+
'env(UV_PROJECT_ENVIRONMENT=local_venv["build"] if cross_platform else venv["build"], UV_PYTHON_PREFERENCE="only-system", UV_PYTHON=f"python{python_version}")',
|
|
311
314
|
'copy(".", ".")' if self.install_requires_all_files else None,
|
|
312
|
-
f'run(f"uv sync
|
|
315
|
+
f'run(f"uv sync{extra_args}", inputs=[{inputs}], group="install")',
|
|
313
316
|
'copy("pyproject.toml", "pyproject.toml")'
|
|
314
317
|
if not self.install_requires_all_files
|
|
315
318
|
else None,
|
|
@@ -317,14 +320,14 @@ class PythonProvider:
|
|
|
317
320
|
]
|
|
318
321
|
if not self.only_build:
|
|
319
322
|
steps += [
|
|
320
|
-
'run(f"uv pip compile pyproject.toml --
|
|
323
|
+
'run(f"uv pip compile pyproject.toml --universal --extra-index-url {python_extra_index_url} --index-url=https://pypi.org/simple --emit-index-url --no-deps -o cross-requirements.txt", outputs=["cross-requirements.txt"]) if cross_platform else None',
|
|
321
324
|
f'run(f"uvx pip install -r cross-requirements.txt {extra_deps} --target {{python_cross_packages_path}} --platform {{cross_platform}} --only-binary=:all: --python-version={{python_version}} --compile") if cross_platform else None',
|
|
322
325
|
'run("rm cross-requirements.txt") if cross_platform else None',
|
|
323
326
|
]
|
|
324
327
|
elif has_requirements or extra_deps:
|
|
325
328
|
steps += [
|
|
326
329
|
'env(UV_PROJECT_ENVIRONMENT=local_venv["build"] if cross_platform else venv["build"])',
|
|
327
|
-
'run(f"uv init --no-workspace
|
|
330
|
+
'run(f"uv init --no-workspace", inputs=[], outputs=["uv.lock"], group="install")',
|
|
328
331
|
'copy(".", ".")' if self.install_requires_all_files else None,
|
|
329
332
|
]
|
|
330
333
|
if has_requirements:
|
|
@@ -351,6 +354,10 @@ class PythonProvider:
|
|
|
351
354
|
'run("mkdir -p {}/bin".format(venv["build"])) if cross_platform else None',
|
|
352
355
|
'run("cp {}/bin/mcp {}/bin/mcp".format(local_venv["build"], venv["build"])) if cross_platform else None',
|
|
353
356
|
]
|
|
357
|
+
if self.framework == PythonFramework.Django:
|
|
358
|
+
steps += [
|
|
359
|
+
'run("python manage.py collectstatic --noinput", group="build")',
|
|
360
|
+
]
|
|
354
361
|
return list(filter(None, steps))
|
|
355
362
|
|
|
356
363
|
def prepare_steps(self) -> Optional[list[str]]:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from .base import Provider
|
|
4
|
-
from .gatsby import GatsbyProvider
|
|
5
4
|
from .hugo import HugoProvider
|
|
6
5
|
from .laravel import LaravelProvider
|
|
7
6
|
from .mkdocs import MkdocsProvider
|
|
@@ -16,7 +15,6 @@ def providers() -> list[type[Provider]]:
|
|
|
16
15
|
# Order matters: more specific providers first
|
|
17
16
|
return [
|
|
18
17
|
LaravelProvider,
|
|
19
|
-
# GatsbyProvider,
|
|
20
18
|
HugoProvider,
|
|
21
19
|
MkdocsProvider,
|
|
22
20
|
PythonProvider,
|
|
@@ -19,8 +19,7 @@ from .php import PhpProvider
|
|
|
19
19
|
|
|
20
20
|
class WordPressProvider(PhpProvider):
|
|
21
21
|
def __init__(self, path: Path, custom_commands: CustomCommands):
|
|
22
|
-
|
|
23
|
-
self.custom_commands = custom_commands
|
|
22
|
+
super().__init__(path, custom_commands)
|
|
24
23
|
|
|
25
24
|
@classmethod
|
|
26
25
|
def name(cls) -> str:
|
|
@@ -47,6 +46,9 @@ class WordPressProvider(PhpProvider):
|
|
|
47
46
|
def provider_kind(self) -> str:
|
|
48
47
|
return "php"
|
|
49
48
|
|
|
49
|
+
def platform(self) -> Optional[str]:
|
|
50
|
+
return "wordpress"
|
|
51
|
+
|
|
50
52
|
def dependencies(self) -> list[DependencySpec]:
|
|
51
53
|
return [
|
|
52
54
|
*super().dependencies(),
|
|
@@ -54,7 +56,7 @@ class WordPressProvider(PhpProvider):
|
|
|
54
56
|
]
|
|
55
57
|
|
|
56
58
|
def declarations(self) -> Optional[str]:
|
|
57
|
-
return super().declarations() + (
|
|
59
|
+
return (super().declarations() or "") + (
|
|
58
60
|
'wp_cli_version = getenv("SHIPIT_WPCLI_VERSION")\n'
|
|
59
61
|
"if wp_cli_version:\n"
|
|
60
62
|
' wp_cli_download_url = f"https://github.com/wp-cli/wp-cli/releases/download/v{wp_cli_version}/wp-cli-{wp_cli_version}.phar"\n'
|
|
@@ -65,7 +67,7 @@ class WordPressProvider(PhpProvider):
|
|
|
65
67
|
def build_steps(self) -> list[str]:
|
|
66
68
|
steps = [
|
|
67
69
|
'copy(wp_cli_download_url, "{}/wp-cli.phar".format(assets["build"]))',
|
|
68
|
-
'copy("wordpress/install.sh", "{}/
|
|
70
|
+
'copy("wordpress/install.sh", "{}/setup-wp.sh".format(assets["build"]), base="assets")',
|
|
69
71
|
]
|
|
70
72
|
if not _exists(self.path, "wp-config.php"):
|
|
71
73
|
steps.append(
|
|
@@ -80,7 +82,7 @@ class WordPressProvider(PhpProvider):
|
|
|
80
82
|
commands = super().commands()
|
|
81
83
|
return {
|
|
82
84
|
"wp": '"php {}/wp-cli.phar --allow-root --path={}".format(assets["serve"], app["serve"])',
|
|
83
|
-
"after_deploy": '"bash {}/
|
|
85
|
+
"after_deploy": '"bash {}/setup-wp.sh".format(assets["serve"])',
|
|
84
86
|
**commands,
|
|
85
87
|
}
|
|
86
88
|
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Dict, Optional
|
|
5
|
-
|
|
6
|
-
from .base import (
|
|
7
|
-
DetectResult,
|
|
8
|
-
DependencySpec,
|
|
9
|
-
Provider,
|
|
10
|
-
_exists,
|
|
11
|
-
_has_dependency,
|
|
12
|
-
MountSpec,
|
|
13
|
-
ServiceSpec,
|
|
14
|
-
VolumeSpec,
|
|
15
|
-
CustomCommands,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class GatsbyProvider:
|
|
20
|
-
def __init__(self, path: Path, custom_commands: CustomCommands):
|
|
21
|
-
self.path = path
|
|
22
|
-
self.custom_commands = custom_commands
|
|
23
|
-
|
|
24
|
-
@classmethod
|
|
25
|
-
def name(cls) -> str:
|
|
26
|
-
return "gatsby"
|
|
27
|
-
|
|
28
|
-
@classmethod
|
|
29
|
-
def detect(cls, path: Path, custom_commands: CustomCommands) -> Optional[DetectResult]:
|
|
30
|
-
pkg = path / "package.json"
|
|
31
|
-
if not pkg.exists():
|
|
32
|
-
return None
|
|
33
|
-
if _exists(path, "gatsby-config.js", "gatsby-config.ts") or _has_dependency(
|
|
34
|
-
pkg, "gatsby"
|
|
35
|
-
):
|
|
36
|
-
return DetectResult(cls.name(), 90)
|
|
37
|
-
return None
|
|
38
|
-
|
|
39
|
-
def initialize(self) -> None:
|
|
40
|
-
pass
|
|
41
|
-
|
|
42
|
-
def serve_name(self) -> str:
|
|
43
|
-
return self.path.name
|
|
44
|
-
|
|
45
|
-
def provider_kind(self) -> str:
|
|
46
|
-
return "staticsite"
|
|
47
|
-
|
|
48
|
-
def declarations(self) -> Optional[str]:
|
|
49
|
-
return None
|
|
50
|
-
|
|
51
|
-
def dependencies(self) -> list[DependencySpec]:
|
|
52
|
-
return [
|
|
53
|
-
DependencySpec(
|
|
54
|
-
"node",
|
|
55
|
-
env_var="SHIPIT_NODE_VERSION",
|
|
56
|
-
default_version="22",
|
|
57
|
-
use_in_build=True,
|
|
58
|
-
),
|
|
59
|
-
DependencySpec("npm", use_in_build=True),
|
|
60
|
-
DependencySpec("static-web-server", env_var="SHIPIT_SWS_VERSION", use_in_serve=True),
|
|
61
|
-
]
|
|
62
|
-
|
|
63
|
-
def build_steps(self) -> list[str]:
|
|
64
|
-
return [
|
|
65
|
-
"run(\"npm install\", inputs=[\"package.json\", \"package-lock.json\"], group=\"install\")",
|
|
66
|
-
"copy(\".\", \".\", ignore=[\"node_modules\", \".git\"])",
|
|
67
|
-
"run(\"npm run build\", outputs=[\"public\"], group=\"build\")",
|
|
68
|
-
"run(\"cp -R public/* {}/\".format(app[\"build\"]))",
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
def prepare_steps(self) -> Optional[list[str]]:
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
def commands(self) -> Dict[str, str]:
|
|
75
|
-
return {"start": '"static-web-server --root /app"'}
|
|
76
|
-
|
|
77
|
-
def mounts(self) -> list[MountSpec]:
|
|
78
|
-
return [MountSpec("app")]
|
|
79
|
-
|
|
80
|
-
def volumes(self) -> list[VolumeSpec]:
|
|
81
|
-
return []
|
|
82
|
-
|
|
83
|
-
def env(self) -> Optional[Dict[str, str]]:
|
|
84
|
-
return None
|
|
85
|
-
|
|
86
|
-
def services(self) -> list[ServiceSpec]:
|
|
87
|
-
return []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|