shipit-cli 0.19.7__tar.gz → 0.19.9__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 (49) hide show
  1. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/PKG-INFO +1 -1
  2. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/pyproject.toml +1 -1
  3. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/assets/wordpress/install.sh +35 -27
  4. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/php.py +10 -2
  5. shipit_cli-0.19.9/src/shipit/providers/wordpress.py +305 -0
  6. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/runners/wasmer.py +15 -15
  7. shipit_cli-0.19.9/src/shipit/version.py +5 -0
  8. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_e2e.py +35 -0
  9. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_generate_shipit_examples.py +8 -1
  10. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_wordpress_phpix.py +84 -2
  11. shipit_cli-0.19.7/src/shipit/providers/wordpress.py +0 -122
  12. shipit_cli-0.19.7/src/shipit/version.py +0 -5
  13. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/.gitignore +0 -0
  14. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/README.md +0 -0
  15. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/__init__.py +0 -0
  16. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/assets/php/php.ini +0 -0
  17. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/assets/wordpress/.htaccess +0 -0
  18. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/assets/wordpress/start.php +0 -0
  19. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/assets/wordpress/wp-config.php +0 -0
  20. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/builders/__init__.py +0 -0
  21. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/builders/base.py +0 -0
  22. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/builders/docker.py +0 -0
  23. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/builders/local.py +0 -0
  24. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/cli.py +0 -0
  25. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/generator.py +0 -0
  26. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/procfile.py +0 -0
  27. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/base.py +0 -0
  28. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/go.py +0 -0
  29. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/hugo.py +0 -0
  30. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/jekyll.py +0 -0
  31. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/laravel.py +0 -0
  32. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/mkdocs.py +0 -0
  33. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/node_static.py +0 -0
  34. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/python.py +0 -0
  35. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/registry.py +0 -0
  36. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/providers/staticfile.py +0 -0
  37. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/runners/__init__.py +0 -0
  38. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/runners/base.py +0 -0
  39. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/runners/local.py +0 -0
  40. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/shipit_types.py +0 -0
  41. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/ui.py +0 -0
  42. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/utils.py +0 -0
  43. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/src/shipit/volumes.py +0 -0
  44. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_cli_after_deploy.py +0 -0
  45. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_php_provider.py +0 -0
  46. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_staticfile_provider.py +0 -0
  47. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_version.py +0 -0
  48. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_volumes.py +0 -0
  49. {shipit_cli-0.19.7 → shipit_cli-0.19.9}/tests/test_wasmer_annotations.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shipit-cli
3
- Version: 0.19.7
3
+ Version: 0.19.9
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "shipit-cli"
3
- version = "0.19.7"
3
+ version = "0.19.9"
4
4
  description = "Shipit CLI is the best way to build, serve and deploy your projects anywhere."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -7,11 +7,23 @@ export PAGER="cat"
7
7
  WP_ADMIN_EMAIL=${WP_ADMIN_EMAIL:-"admin@example.com"}
8
8
  WP_ADMIN_USERNAME=${WP_ADMIN_USERNAME:-"admin"}
9
9
  WP_ADMIN_PASSWORD=${WP_ADMIN_PASSWORD:-"admin"}
10
- WP_LOCALE=${WP_LOCALE:-"en_US"}
10
+ WP_DEFAULT_LOCALE=${WP_DEFAULT_LOCALE:-"en_US"}
11
+ WP_LOCALE=${WP_LOCALE:-"$WP_DEFAULT_LOCALE"}
11
12
  WP_SITEURL=${WP_SITEURL:-"http://localhost"}
12
13
  WP_SITE_TITLE=${WP_SITE_TITLE:-"WordPress"}
13
14
 
14
- if wp core is-installed; then
15
+ echo "📁 Initializing wp-content..."
16
+ mkdir -p wp-content/plugins
17
+ mkdir -p wp-content/themes
18
+ mkdir -p wp-content/upgrade
19
+
20
+ if [ -n "${WPCONTENT_BASE_PATH:-}" ] && [ -d "${WPCONTENT_BASE_PATH}" ]; then
21
+ shopt -s dotglob nullglob
22
+ cp -R "${WPCONTENT_BASE_PATH}"/* /app/wp-content || true
23
+ shopt -u dotglob nullglob
24
+ fi
25
+
26
+ if wp core is-installed && [ "${WP_FORCE_SETUP:-false}" != "true" ]; then
15
27
  echo "🚀 Setting up WordPress from an existing installation"
16
28
  if [ "${WP_UPDATE_DB:-true}" = "true" ]; then
17
29
  echo "🛠️ Activating maintenance mode..."
@@ -23,20 +35,6 @@ if wp core is-installed; then
23
35
  fi
24
36
  else
25
37
  echo "🚀 Setting up WordPress from a fresh install"
26
- echo "📁 Initializing wp-content..."
27
-
28
- mkdir -p wp-content/plugins
29
- mkdir -p wp-content/themes
30
- mkdir -p wp-content/upgrade
31
-
32
- if [ -n "${WPCONTENT_BASE_PATH:-}" ] && [ -d "${WPCONTENT_BASE_PATH}" ]; then
33
- shopt -s dotglob nullglob
34
- # Note: change this back to copy all, once using the WP Zip files
35
- # cp -R "${WPCONTENT_BASE_PATH}"/* /app/wp-content
36
- cp -R "${WPCONTENT_BASE_PATH}"/plugins/* /app/wp-content/plugins || true
37
- cp -R "${WPCONTENT_BASE_PATH}"/themes/twentytwenty* /app/wp-content/themes || true
38
- shopt -u dotglob nullglob
39
- fi
40
38
 
41
39
  echo "⚙️ Installing WordPress core"
42
40
 
@@ -60,7 +58,7 @@ if [ -n "${WP_PLUGINS:-}" ]; then
60
58
  for PLUGIN_ENTRY in $WP_PLUGINS; do
61
59
  if [[ "$PLUGIN_ENTRY" =~ ^https?:// ]]; then
62
60
  echo "• Installing plugin from URL: $PLUGIN_ENTRY"
63
- wp plugin install "$PLUGIN_ENTRY" --activate
61
+ wp plugin install "$PLUGIN_ENTRY" --force --activate
64
62
  else
65
63
  # Extract name and version using parameter expansion
66
64
  PLUGIN_NAME="${PLUGIN_ENTRY%%:*}"
@@ -68,15 +66,25 @@ if [ -n "${WP_PLUGINS:-}" ]; then
68
66
 
69
67
  if [[ "$PLUGIN_NAME" == "$PLUGIN_VERSION" ]]; then
70
68
  echo "• Installing plugin '${PLUGIN_NAME}' (latest version)..."
71
- wp plugin install "$PLUGIN_NAME" --activate
69
+ wp plugin install "$PLUGIN_NAME" --force --activate
72
70
  else
73
71
  echo "• Installing plugin '${PLUGIN_NAME}' (version: ${PLUGIN_VERSION})..."
74
- wp plugin install "$PLUGIN_NAME" --version="$PLUGIN_VERSION" --activate
72
+ wp plugin install "$PLUGIN_NAME" --force --version="$PLUGIN_VERSION" --activate
75
73
  fi
76
74
  fi
77
75
  done
78
76
  fi
79
77
 
78
+ if [ -n "${WP_PLUGINS_ACTIVATE:-}" ]; then
79
+ echo "✨ Activating plugins: $WP_PLUGINS_ACTIVATE"
80
+ IFS=',' # Split by commas
81
+
82
+ for PLUGIN_ENTRY in $WP_PLUGINS_ACTIVATE; do
83
+ echo "• Activating plugin: $PLUGIN_ENTRY"
84
+ wp plugin activate "$PLUGIN_ENTRY"
85
+ done
86
+ fi
87
+
80
88
  # Install themes from WP_THEMES environment variable
81
89
  if [ -n "${WP_THEMES:-}" ]; then
82
90
  echo "🎨 Installing themes from WP_THEMES: $WP_THEMES"
@@ -85,17 +93,17 @@ if [ -n "${WP_THEMES:-}" ]; then
85
93
  for THEME_ENTRY in $WP_THEMES; do
86
94
  if [[ "$THEME_ENTRY" =~ ^https?:// ]]; then
87
95
  echo "• Installing theme from URL: $THEME_ENTRY"
88
- wp theme install "$THEME_ENTRY"
96
+ wp theme install "$THEME_ENTRY" --force
89
97
  else
90
98
  THEME_NAME="${THEME_ENTRY%%:*}"
91
99
  THEME_VERSION="${THEME_ENTRY#*:}"
92
100
 
93
101
  if [[ "$THEME_NAME" == "$THEME_VERSION" ]]; then
94
102
  echo "• Installing theme '${THEME_NAME}' (latest version)..."
95
- wp theme install "$THEME_NAME"
103
+ wp theme install "$THEME_NAME" --force
96
104
  else
97
105
  echo "• Installing theme '${THEME_NAME}' (version: ${THEME_VERSION})..."
98
- wp theme install "$THEME_NAME" --version="$THEME_VERSION"
106
+ wp theme install "$THEME_NAME" --force --version="$THEME_VERSION"
99
107
  fi
100
108
  fi
101
109
  done
@@ -108,14 +116,14 @@ fi
108
116
 
109
117
  if [ -n "${WP_LOCALE:-}" ]; then
110
118
  echo "🌐 Setting locale: $WP_LOCALE"
111
- wp language core install "$WP_LOCALE"
112
- wp language theme install --all "$WP_LOCALE"
113
- wp language plugin install --all "$WP_LOCALE"
119
+ # wp language core install "$WP_LOCALE"
120
+ # wp language theme install --all "$WP_LOCALE"
121
+ # wp language plugin install --all "$WP_LOCALE"
114
122
  wp site switch-language "$WP_LOCALE"
115
123
  fi
116
124
 
117
- # echo "✍️ Rewriting permalinks structure"
118
- # wp rewrite flush --hard || true
125
+ echo "✍️ Rewriting permalinks structure"
126
+ wp rewrite flush --hard || true
119
127
 
120
128
  # if [ ! -f "/app/wp-content/wp-config.php" ]; then
121
129
  # cat > /app/wp-content/wp-config.php <<EOF
@@ -184,13 +184,21 @@ class PhpProvider:
184
184
  def dependencies(self) -> list[DependencySpec]:
185
185
  deps = [
186
186
  DependencySpec(
187
- "php" if not self.config.phpix else "phpix",
187
+ "php",
188
188
  var_name="config.php_version",
189
189
  architecture_var_name="config.php_architecture",
190
190
  use_in_build=True,
191
- use_in_serve=True,
191
+ use_in_serve=not self.config.phpix,
192
192
  ),
193
193
  ]
194
+ if self.config.phpix:
195
+ deps.append(DependencySpec(
196
+ "phpix",
197
+ var_name="config.php_version",
198
+ architecture_var_name="config.php_architecture",
199
+ use_in_build=False,
200
+ use_in_serve=True,
201
+ ))
194
202
  if self.config.use_composer:
195
203
  deps.append(DependencySpec("composer", use_in_build=True))
196
204
  deps.append(DependencySpec("bash", use_in_serve=True))
@@ -0,0 +1,305 @@
1
+ import json
2
+ import re
3
+ from dataclasses import dataclass
4
+ from functools import cached_property
5
+ from pathlib import Path
6
+ from typing import Dict, Literal, Optional
7
+
8
+ from pydantic_settings import SettingsConfigDict
9
+
10
+ from .base import (
11
+ Config,
12
+ DetectResult,
13
+ DependencySpec,
14
+ MountSpec,
15
+ ServiceSpec,
16
+ VolumeSpec,
17
+ _exists,
18
+ )
19
+ from .php import PhpConfig, PhpProvider
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class WordPressExtension:
24
+ kind: Literal["plugin", "theme"]
25
+ slug: str
26
+ activate_target: str
27
+ source_file: Optional[str] = None
28
+
29
+ @property
30
+ def content_dir(self) -> str:
31
+ return f"{self.kind}s"
32
+
33
+
34
+ class WordPressConfig(PhpConfig):
35
+ model_config = SettingsConfigDict(extra="ignore", env_prefix="SHIPIT_")
36
+
37
+ wp_version: Optional[str] = None
38
+ wp_locale: Optional[str] = None
39
+ wp_cli_version: Optional[str] = None
40
+
41
+
42
+ class WordPressProvider(PhpProvider):
43
+ HEADER_SCAN_BYTES = 8192
44
+ PLUGIN_HEADER_RE = re.compile(
45
+ r"^[ \t/*#@]*Plugin Name\s*:", re.I | re.M
46
+ )
47
+ THEME_HEADER_RE = re.compile(
48
+ r"^[ \t/*#@]*Theme Name\s*:", re.I | re.M
49
+ )
50
+
51
+ @classmethod
52
+ def name(cls) -> str:
53
+ return "wordpress"
54
+
55
+ @classmethod
56
+ def load_config(
57
+ cls, path: Path, config: Config
58
+ ) -> WordPressConfig:
59
+ php_config = super().load_config(path, config)
60
+ wp_config = WordPressConfig(**php_config.model_dump())
61
+ if cls.detect_extension(path) and not wp_config.wp_version:
62
+ wp_config.wp_version = "latest"
63
+ return wp_config
64
+
65
+ @classmethod
66
+ def detect(
67
+ cls, path: Path, config: Config
68
+ ) -> Optional[DetectResult]:
69
+ if (
70
+ _exists(path, "wp-content")
71
+ and _exists(path, "index.php")
72
+ and _exists(path, "wp-load.php")
73
+ ):
74
+ return DetectResult(cls.name(), 80)
75
+
76
+ wp_config = cls.load_config(path, config)
77
+ if wp_config.wp_version:
78
+ return DetectResult(cls.name(), 80)
79
+ if cls.detect_extension(path):
80
+ return DetectResult(cls.name(), 75)
81
+ return None
82
+
83
+ @classmethod
84
+ def detect_extension(cls, path: Path) -> Optional[WordPressExtension]:
85
+ theme = cls._detect_theme(path)
86
+ if theme:
87
+ return theme
88
+ return cls._detect_plugin(path)
89
+
90
+ @classmethod
91
+ def _detect_plugin(cls, path: Path) -> Optional[WordPressExtension]:
92
+ plugin_files = sorted(path.glob("*.php"))
93
+ for plugin_file in plugin_files:
94
+ if not cls._file_has_header(plugin_file, cls.PLUGIN_HEADER_RE):
95
+ continue
96
+ slug = cls._slugify(path.name)
97
+ return WordPressExtension(
98
+ kind="plugin",
99
+ slug=slug,
100
+ activate_target=f"{slug}/{plugin_file.name}",
101
+ source_file=plugin_file.name,
102
+ )
103
+ return None
104
+
105
+ @classmethod
106
+ def _detect_theme(cls, path: Path) -> Optional[WordPressExtension]:
107
+ style_css = path / "style.css"
108
+ if not cls._file_has_header(style_css, cls.THEME_HEADER_RE):
109
+ return None
110
+ if not (
111
+ (path / "index.php").is_file()
112
+ or (path / "templates" / "index.html").is_file()
113
+ or (path / "theme.json").is_file()
114
+ ):
115
+ return None
116
+ slug = cls._slugify(path.name)
117
+ return WordPressExtension(
118
+ kind="theme",
119
+ slug=slug,
120
+ activate_target=slug,
121
+ source_file="style.css",
122
+ )
123
+
124
+ @classmethod
125
+ def _file_has_header(cls, path: Path, pattern: re.Pattern[str]) -> bool:
126
+ if not path.is_file():
127
+ return False
128
+ try:
129
+ contents = path.read_text(errors="ignore")[: cls.HEADER_SCAN_BYTES]
130
+ except OSError:
131
+ return False
132
+ return bool(pattern.search(contents))
133
+
134
+ @staticmethod
135
+ def _slugify(value: str) -> str:
136
+ slug = re.sub(r"[^a-z0-9_-]+", "-", value.lower()).strip("-_")
137
+ return slug or "wordpress-extension"
138
+
139
+ @cached_property
140
+ def extension(self) -> Optional[WordPressExtension]:
141
+ return self.detect_extension(self.path)
142
+
143
+ def dependencies(self) -> list[DependencySpec]:
144
+ return [
145
+ *super().dependencies(),
146
+ DependencySpec("bash", use_in_build=False, use_in_serve=True),
147
+ ]
148
+
149
+ def declarations(self) -> Optional[str]:
150
+ return (super().declarations() or "") + (
151
+ "wp_cli_version = config.wp_cli_version\n"
152
+ 'wp_cli_download_url = f"https://github.com/wp-cli/wp-cli/releases/download/v{wp_cli_version}/wp-cli-{wp_cli_version}.phar" if wp_cli_version else "https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar"\n'
153
+ )
154
+
155
+ def _wordpress_base_build_steps(self) -> list[str]:
156
+ steps = [
157
+ 'copy(wp_cli_download_url, "{}/wp-cli.phar".format(assets.path))',
158
+ 'copy("wordpress/install.sh", "{}/setup-wp.sh".format(assets.path), base="assets")',
159
+ ]
160
+ if self.config.wp_version:
161
+ version_args = [f"--version={self.config.wp_version}"]
162
+ if self.config.wp_locale:
163
+ version_args.append(f"--locale={self.config.wp_locale}")
164
+ version_flags = " ".join(version_args)
165
+ steps.append(
166
+ 'run("php -d memory_limit=512M {}/wp-cli.phar core '
167
+ "download --allow-root --path={} "
168
+ f'{version_flags}".format(assets.path, app.path))'
169
+ )
170
+ if self.config.phpix:
171
+ # We create the start script that creates the .htaccess symlink
172
+ # since phpix now supports .htaccess files.
173
+ steps.append(
174
+ 'copy("wordpress/start.php", "{}/start-wp.php".format(assets.path), base="assets")'
175
+ )
176
+ if self.extension or not _exists(self.path, "wp-config.php"):
177
+ steps.append(
178
+ 'copy("wordpress/wp-config.php", "{}/wp-config.php".format(app.path), base="assets")'
179
+ )
180
+ if self.extension or not _exists(self.path, ".htaccess"):
181
+ steps.append(
182
+ 'copy("wordpress/.htaccess", "{}/.htaccess".format(app.path), base="assets")'
183
+ )
184
+ return steps
185
+
186
+ def _wp_content_seed_steps(self) -> list[str]:
187
+ steps = []
188
+ if self.config.wp_version:
189
+ steps.append(
190
+ 'run("cp -R {}/wp-content/* {}".format(app.path, wpcontent_base.path))'
191
+ )
192
+ if _exists(self.path, "wp-content"):
193
+ steps.append('copy("wp-content", "{}".format(wpcontent_base.path))')
194
+ return steps
195
+
196
+ def _php_asset_steps(self) -> list[str]:
197
+ steps = [
198
+ 'workdir(app.path)',
199
+ ]
200
+ if _exists(self.path, "php.ini"):
201
+ steps.append('copy("php.ini", "{}/php.ini".format(assets.path))')
202
+ else:
203
+ steps.append(
204
+ 'copy("php/php.ini", "{}/php.ini".format(assets.path), base="assets")'
205
+ )
206
+ return steps
207
+
208
+ def _extension_build_steps(self, extension: WordPressExtension) -> list[str]:
209
+ target = (
210
+ f'"{{}}/{extension.content_dir}/{extension.slug}".format('
211
+ "wpcontent_base.path)"
212
+ )
213
+ ignore = [".git", ".source"]
214
+ if self.config.use_composer:
215
+ ignore.append("vendor")
216
+
217
+ steps = [
218
+ *self._wordpress_base_build_steps(),
219
+ *self._php_asset_steps(),
220
+ *self._wp_content_seed_steps(),
221
+ f'copy(".", {target}, ignore={json.dumps(ignore)})',
222
+ ]
223
+
224
+ if self.config.use_composer:
225
+ steps.extend(
226
+ [
227
+ f"workdir({target})",
228
+ (
229
+ 'env(COMPOSER_HOME="/tmp", COMPOSER_FUND="0", '
230
+ 'COMPOSER_ALLOW_SUPERUSER="1")'
231
+ ),
232
+ (
233
+ 'run("composer install --optimize-autoloader '
234
+ '--ignore-platform-reqs --no-scripts '
235
+ '--no-interaction", group="install")'
236
+ ),
237
+ ]
238
+ )
239
+ if self.config.composer_build_script:
240
+ steps.append(
241
+ f'run("composer run-script {self.config.composer_build_script}", '
242
+ 'outputs=["."], group="build")'
243
+ )
244
+
245
+ return steps
246
+
247
+ def build_steps(self) -> list[str]:
248
+ if self.extension:
249
+ return self._extension_build_steps(self.extension)
250
+
251
+ return (
252
+ self._wordpress_base_build_steps()
253
+ + super().build_steps_with_options(
254
+ extra_ignore=["wp-content"],
255
+ after_install=None,
256
+ after_build=None,
257
+ )
258
+ + self._wp_content_seed_steps()
259
+ )
260
+
261
+ def prepare_steps(self) -> Optional[list[str]]:
262
+ return super().prepare_steps()
263
+
264
+ def commands(self) -> Dict[str, str]:
265
+ commands = super().commands()
266
+ if self.config.phpix:
267
+ if "start" in commands:
268
+ commands["start"] = (
269
+ '"phpix --startup-script={}/start-wp.php -S localhost:{} '
270
+ '-t {}".format(assets.serve_path, PORT, app.serve_path)'
271
+ )
272
+ return {
273
+ "wp": '"php {}/wp-cli.phar --allow-root --path={}".format(assets.serve_path, app.serve_path)',
274
+ "after_deploy": '"bash {}/setup-wp.sh".format(assets.serve_path)',
275
+ **commands,
276
+ }
277
+
278
+ def mounts(self) -> list[MountSpec]:
279
+ return super().mounts() + [MountSpec("wpcontent_base")]
280
+
281
+ def volumes(self) -> list[VolumeSpec]:
282
+ return [
283
+ VolumeSpec(
284
+ name="wp-content",
285
+ serve_path='"{}/wp-content/".format(app.serve_path)',
286
+ var_name="wp_content",
287
+ )
288
+ ]
289
+
290
+ def env(self) -> Optional[Dict[str, str]]:
291
+ env = {
292
+ "PAGER": '"cat"',
293
+ "WPCONTENT_BASE_PATH": '"{}".format(wpcontent_base.serve_path)',
294
+ **(super().env() or {}),
295
+ }
296
+ if self.config.wp_locale:
297
+ env["WP_LOCALE"] = f'"{self.config.wp_locale}"'
298
+ if self.extension and self.extension.kind == "plugin":
299
+ env["WP_PLUGINS_ACTIVATE"] = f'"{self.extension.activate_target}"'
300
+ if self.extension and self.extension.kind == "theme":
301
+ env["WP_DEFAULT_THEME"] = f'"{self.extension.activate_target}"'
302
+ return env
303
+
304
+ def services(self) -> list[ServiceSpec]:
305
+ return [ServiceSpec(name="database", provider="mysql")]
@@ -149,28 +149,28 @@ class WasmerRunner:
149
149
  },
150
150
  "phpix": {
151
151
  "dependencies": {
152
- "latest": "phpix/phpix-83-32bit@=0.2.0-rc.1",
153
- "8.3": "phpix/phpix-83-32bit@=0.2.0-rc.1",
154
- "8.3.29": "phpix/phpix-83-32bit@=0.2.0-rc.1",
155
- "8.2": "phpix/phpix-82-32bit@=0.2.0-rc.1",
156
- "8.1": "phpix/phpix-81-32bit@=0.2.0-rc.1",
152
+ "latest": "phpix/phpix-83-32bit@=0.2.0-rc.2",
153
+ "8.3": "phpix/phpix-83-32bit@=0.2.0-rc.2",
154
+ "8.3.29": "phpix/phpix-83-32bit@=0.2.0-rc.2",
155
+ "8.2": "phpix/phpix-82-32bit@=0.2.0-rc.2",
156
+ "8.1": "phpix/phpix-81-32bit@=0.2.0-rc.2",
157
157
  "7.4": "php/php-32@=7.4.3301", # Note, we don't have PHPix + PHP 7.4 yet
158
158
  },
159
159
  "architecture_dependencies": {
160
160
  "64-bit": {
161
- "latest": "phpix/phpix-83-64bit@=0.2.0-rc.1",
162
- "8.3": "phpix/phpix-83-64bit@=0.2.0-rc.1",
163
- "8.3.29": "phpix/phpix-83-64bit@=0.2.0-rc.1",
164
- "8.2": "phpix/phpix-82-64bit@=0.2.0-rc.1",
165
- "8.1": "phpix/phpix-81-64bit@=0.2.0-rc.1",
161
+ "latest": "phpix/phpix-83-64bit@=0.2.0-rc.2",
162
+ "8.3": "phpix/phpix-83-64bit@=0.2.0-rc.2",
163
+ "8.3.29": "phpix/phpix-83-64bit@=0.2.0-rc.2",
164
+ "8.2": "phpix/phpix-82-64bit@=0.2.0-rc.2",
165
+ "8.1": "phpix/phpix-81-64bit@=0.2.0-rc.2",
166
166
  "7.4": "php/php-64@=7.4.3301",
167
167
  },
168
168
  "32-bit": {
169
- "latest": "phpix/phpix-83-32bit@=0.2.0-rc.1",
170
- "8.3": "phpix/phpix-83-32bit@=0.2.0-rc.1",
171
- "8.3.29": "phpix/phpix-83-32bit@=0.2.0-rc.1",
172
- "8.2": "phpix/phpix-82-32bit@=0.2.0-rc.1",
173
- "8.1": "phpix/phpix-81-32bit@=0.2.0-rc.1",
169
+ "latest": "phpix/phpix-83-32bit@=0.2.0-rc.2",
170
+ "8.3": "phpix/phpix-83-32bit@=0.2.0-rc.2",
171
+ "8.3.29": "phpix/phpix-83-32bit@=0.2.0-rc.2",
172
+ "8.2": "phpix/phpix-82-32bit@=0.2.0-rc.2",
173
+ "8.1": "phpix/phpix-81-32bit@=0.2.0-rc.2",
174
174
  "7.4": "php/php-32@=7.4.3301",
175
175
  },
176
176
  },
@@ -0,0 +1,5 @@
1
+ __all__ = ["version", "version_info"]
2
+
3
+
4
+ version = "0.19.9"
5
+ version_info = (0, 19, 9, "final", 0)
@@ -168,6 +168,41 @@ class E2ECase:
168
168
  ],
169
169
  build_modes=(BuildMode.Wasmer,),
170
170
  ),
171
+ # Full WordPress release archive, built and run through Wasmer only.
172
+ E2ECase(
173
+ path="examples/php-wordpress-empty",
174
+ serve_pattern=(
175
+ r"listening addr"
176
+ ),
177
+ http=[
178
+ HTTPRequest(
179
+ path="/",
180
+ expected_status=200,
181
+ body_match=r"WordPress",
182
+ )
183
+ ],
184
+ use_random_port=False,
185
+ env={
186
+ "DB_NAME": "test",
187
+ "DB_USERNAME": "root",
188
+ "DB_HOST": "127.0.0.1",
189
+ "DB_PORT": "3306",
190
+ "DB_PASSWORD": "",
191
+ "SHIPIT_PHPIX": "true",
192
+ "SHIPIT_WP_VERSION": "latest",
193
+ # "SHIPIT_WP_LOCALE": "en_US",
194
+ },
195
+ create_db=True,
196
+ create_wp_content_volume=True,
197
+ run_after_deploy=True,
198
+ commands=[
199
+ RunCommand(
200
+ "wp eval 'echo json_encode([\"status\" => \"ok\"]);'",
201
+ stdout_match=r'\{"status":"ok"\}',
202
+ )
203
+ ],
204
+ build_modes=(BuildMode.Wasmer,),
205
+ ),
171
206
  # WordPress skeleton in phpix mode (Wasmer only), validate memory cap.
172
207
  E2ECase(
173
208
  path="examples/php-wordpress",
@@ -18,12 +18,19 @@ _EXAMPLE_DIRS = _example_dirs_with_shipit()
18
18
  @pytest.mark.parametrize(
19
19
  "example_dir", _EXAMPLE_DIRS, ids=[p.name for p in _EXAMPLE_DIRS]
20
20
  )
21
- def test_generate_shipit_matches_example(example_dir: Path) -> None:
21
+ def test_generate_shipit_matches_example(
22
+ example_dir: Path,
23
+ monkeypatch: pytest.MonkeyPatch,
24
+ ) -> None:
22
25
  """Ensure generated Shipit content matches the checked-in file.
23
26
 
24
27
  This validates provider detection and the Shipit generator formatting for
25
28
  each example that includes a `Shipit` file.
26
29
  """
30
+ if example_dir.name == "php-wordpress-empty":
31
+ monkeypatch.setenv("SHIPIT_WP_VERSION", "latest")
32
+ monkeypatch.setenv("SHIPIT_PHPIX", "true")
33
+
27
34
  base_config = Config()
28
35
  base_config.commands.enrich_from_path(example_dir)
29
36
 
@@ -2,14 +2,45 @@ from pathlib import Path
2
2
 
3
3
  import yaml
4
4
 
5
- from shipit.generator import generate_shipit, load_provider_config
5
+ from shipit.generator import generate_shipit, load_provider, load_provider_config
6
6
  from shipit.providers.base import Config
7
- from shipit.providers.wordpress import WordPressProvider
7
+ from shipit.providers.wordpress import WordPressConfig, WordPressProvider
8
8
  from shipit.runners.wasmer import WasmerRunner
9
9
  from shipit.shipit_types import Mount, Package, Serve, Service
10
10
  from shipit.version import version as shipit_version
11
11
 
12
12
 
13
+ def _write_plugin(project_dir: Path, filename: str = "my-plugin.php") -> None:
14
+ project_dir.mkdir()
15
+ (project_dir / filename).write_text(
16
+ """<?php
17
+ /**
18
+ * Plugin Name: My Plugin
19
+ */
20
+ """
21
+ )
22
+
23
+
24
+ def _write_theme(project_dir: Path) -> None:
25
+ project_dir.mkdir()
26
+ (project_dir / "style.css").write_text(
27
+ """/*
28
+ Theme Name: My Theme
29
+ */
30
+ """
31
+ )
32
+ (project_dir / "index.php").write_text("<?php\n")
33
+
34
+
35
+ def _generate_for_path(project_dir: Path) -> tuple[type, WordPressConfig, str]:
36
+ base_config = Config()
37
+ base_config.commands.enrich_from_path(project_dir)
38
+ provider_cls = load_provider(project_dir, base_config)
39
+ provider_config = load_provider_config(provider_cls, project_dir, base_config)
40
+ provider = provider_cls(project_dir, provider_config)
41
+ return provider_cls, provider_config, generate_shipit(project_dir, provider)
42
+
43
+
13
44
  class DummyBuildBackend:
14
45
  def __init__(self, root: Path) -> None:
15
46
  self.root = root
@@ -29,6 +60,57 @@ class DummyBuildBackend:
29
60
  return None
30
61
 
31
62
 
63
+ def test_wordpress_provider_detects_plugin_and_generates_activation(
64
+ tmp_path: Path,
65
+ ) -> None:
66
+ project_dir = tmp_path / "my-plugin"
67
+ _write_plugin(project_dir)
68
+
69
+ provider_cls, provider_config, generated = _generate_for_path(project_dir)
70
+
71
+ assert provider_cls is WordPressProvider
72
+ assert provider_config.wp_version == "latest"
73
+ assert "--version=latest" in generated
74
+ assert (
75
+ 'copy(".", "{}/plugins/my-plugin".format(wpcontent_base.path), '
76
+ 'ignore=[".git", ".source"])'
77
+ ) in generated
78
+ assert '"WP_PLUGINS_ACTIVATE": "my-plugin/my-plugin.php"' in generated
79
+
80
+
81
+ def test_wordpress_provider_detects_theme_and_generates_activation(
82
+ tmp_path: Path,
83
+ ) -> None:
84
+ project_dir = tmp_path / "my-theme"
85
+ _write_theme(project_dir)
86
+
87
+ provider_cls, provider_config, generated = _generate_for_path(project_dir)
88
+
89
+ assert provider_cls is WordPressProvider
90
+ assert provider_config.wp_version == "latest"
91
+ assert "--version=latest" in generated
92
+ assert (
93
+ 'copy(".", "{}/themes/my-theme".format(wpcontent_base.path), '
94
+ 'ignore=[".git", ".source"])'
95
+ ) in generated
96
+ assert '"WP_DEFAULT_THEME": "my-theme"' in generated
97
+
98
+
99
+ def test_wordpress_extension_keeps_user_wp_version(tmp_path: Path) -> None:
100
+ project_dir = tmp_path / "my-plugin"
101
+ _write_plugin(project_dir)
102
+
103
+ base_config = Config()
104
+ provider_config = load_provider_config(
105
+ WordPressProvider,
106
+ project_dir,
107
+ base_config,
108
+ {"wp_version": "6.8.3"},
109
+ )
110
+
111
+ assert provider_config.wp_version == "6.8.3"
112
+
113
+
32
114
  def test_generate_shipit_wordpress_phpix_mode() -> None:
33
115
  repo_root = Path(__file__).resolve().parents[1]
34
116
  example_dir = repo_root / "examples" / "php-wordpress"
@@ -1,122 +0,0 @@
1
- from pathlib import Path
2
- from typing import Dict, Optional
3
-
4
- from .base import (
5
- DetectResult,
6
- DependencySpec,
7
- Provider,
8
- _exists,
9
- MountSpec,
10
- ServiceSpec,
11
- VolumeSpec,
12
- Config,
13
- )
14
- from .php import PhpConfig, PhpProvider
15
- from pydantic_settings import SettingsConfigDict
16
-
17
-
18
- class WordPressConfig(PhpConfig):
19
- model_config = SettingsConfigDict(extra="ignore", env_prefix="SHIPIT_")
20
-
21
- wp_cli_version: Optional[str] = None
22
-
23
-
24
- class WordPressProvider(PhpProvider):
25
- @classmethod
26
- def name(cls) -> str:
27
- return "wordpress"
28
-
29
- @classmethod
30
- def load_config(
31
- cls, path: Path, config: Config
32
- ) -> WordPressConfig:
33
- php_config = super().load_config(path, config)
34
- return WordPressConfig(**php_config.model_dump())
35
-
36
- @classmethod
37
- def detect(
38
- cls, path: Path, config: Config
39
- ) -> Optional[DetectResult]:
40
- if (
41
- _exists(path, "wp-content")
42
- and _exists(path, "index.php")
43
- and _exists(path, "wp-load.php")
44
- ):
45
- return DetectResult(cls.name(), 80)
46
- return None
47
-
48
- def dependencies(self) -> list[DependencySpec]:
49
- return [
50
- *super().dependencies(),
51
- DependencySpec("bash", use_in_build=False, use_in_serve=True),
52
- ]
53
-
54
- def declarations(self) -> Optional[str]:
55
- return (super().declarations() or "") + (
56
- "wp_cli_version = config.wp_cli_version\n"
57
- 'wp_cli_download_url = f"https://github.com/wp-cli/wp-cli/releases/download/v{wp_cli_version}/wp-cli-{wp_cli_version}.phar" if wp_cli_version else "https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar"\n'
58
- )
59
-
60
- def build_steps(self) -> list[str]:
61
- steps = [
62
- 'copy(wp_cli_download_url, "{}/wp-cli.phar".format(assets.path))',
63
- 'copy("wordpress/install.sh", "{}/setup-wp.sh".format(assets.path), base="assets")',
64
- ]
65
- if self.config.phpix:
66
- # We create the start script that creates the .htaccess symlink
67
- # since phpix now supports .htaccess files.
68
- steps.append(
69
- 'copy("wordpress/start.php", "{}/start-wp.php".format(assets.path), base="assets")'
70
- )
71
- if not _exists(self.path, "wp-config.php"):
72
- steps.append(
73
- 'copy("wordpress/wp-config.php", "{}/wp-config.php".format(app.path), base="assets")'
74
- )
75
- if not _exists(self.path, ".htaccess"):
76
- steps.append(
77
- 'copy("wordpress/.htaccess", "{}/.htaccess".format(app.path), base="assets")'
78
- )
79
- return steps + super().build_steps_with_options(
80
- extra_ignore=["wp-content"],
81
- after_install=None,
82
- after_build=None
83
- ) + ['copy("wp-content", "{}".format(wpcontent_base.path))']
84
-
85
- def prepare_steps(self) -> Optional[list[str]]:
86
- return super().prepare_steps()
87
-
88
- def commands(self) -> Dict[str, str]:
89
- commands = super().commands()
90
- if self.config.phpix:
91
- if "start" in commands:
92
- commands["start"] = (
93
- '"phpix --startup-script={}/start-wp.php -S localhost:{} '
94
- '-t {}".format(assets.serve_path, PORT, app.serve_path)'
95
- )
96
- return {
97
- "wp": '"php {}/wp-cli.phar --allow-root --path={}".format(assets.serve_path, app.serve_path)',
98
- "after_deploy": '"bash {}/setup-wp.sh".format(assets.serve_path)',
99
- **commands,
100
- }
101
-
102
- def mounts(self) -> list[MountSpec]:
103
- return super().mounts() + [MountSpec("wpcontent_base")]
104
-
105
- def volumes(self) -> list[VolumeSpec]:
106
- return [
107
- VolumeSpec(
108
- name="wp-content",
109
- serve_path='"{}/wp-content/".format(app.serve_path)',
110
- var_name="wp_content",
111
- )
112
- ]
113
-
114
- def env(self) -> Optional[Dict[str, str]]:
115
- return {
116
- "PAGER": '"cat"',
117
- "WPCONTENT_BASE_PATH": '"{}".format(wpcontent_base.serve_path)',
118
- **(super().env() or {}),
119
- }
120
-
121
- def services(self) -> list[ServiceSpec]:
122
- return [ServiceSpec(name="database", provider="mysql")]
@@ -1,5 +0,0 @@
1
- __all__ = ["version", "version_info"]
2
-
3
-
4
- version = "0.19.7"
5
- version_info = (0, 19, 7, "final", 0)
File without changes
File without changes