shipit-cli 0.22.1__tar.gz → 0.23.0__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 (176) hide show
  1. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/PKG-INFO +1 -1
  2. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/pyproject.toml +1 -1
  3. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/assets/wordpress/install.sh +5 -1
  4. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/node.py +31 -9
  5. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/python.py +82 -0
  6. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/staticfile.py +52 -1
  7. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/runners/wasmer.py +13 -19
  8. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/python.shipit +9 -44
  9. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/version.py +1 -1
  10. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_e2e.py +7 -7
  11. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_node_provider.py +26 -0
  12. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_node_static_provider.py +8 -3
  13. shipit_cli-0.23.0/tests/test_python_detection.py +245 -0
  14. shipit_cli-0.23.0/tests/test_staticfile_provider.py +99 -0
  15. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_wasmer_annotations.py +21 -9
  16. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_wordpress_phpix.py +18 -0
  17. shipit_cli-0.22.1/tests/test_python_detection.py +0 -96
  18. shipit_cli-0.22.1/tests/test_staticfile_provider.py +0 -46
  19. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/.gitignore +0 -0
  20. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/README.md +0 -0
  21. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/__init__.py +0 -0
  22. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/assets/node/optimize-node-modules.sh +0 -0
  23. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/assets/php/php.ini +0 -0
  24. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/assets/wordpress/.htaccess +0 -0
  25. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/assets/wordpress/start.php +0 -0
  26. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/assets/wordpress/wp-config.php +0 -0
  27. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/builders/__init__.py +0 -0
  28. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/builders/base.py +0 -0
  29. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/builders/docker.py +0 -0
  30. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/builders/local.py +0 -0
  31. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/cli.py +0 -0
  32. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/evaluator.py +0 -0
  33. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/generator.py +0 -0
  34. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/procfile.py +0 -0
  35. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/base.py +0 -0
  36. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/go.py +0 -0
  37. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/hugo.py +0 -0
  38. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/install_context.py +0 -0
  39. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/jekyll.py +0 -0
  40. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/laravel.py +0 -0
  41. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/mkdocs.py +0 -0
  42. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/node_static.py +0 -0
  43. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/php.py +0 -0
  44. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/registry.py +0 -0
  45. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/providers/wordpress.py +0 -0
  46. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/runners/__init__.py +0 -0
  47. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/runners/base.py +0 -0
  48. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/runners/local.py +0 -0
  49. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/shipit_types.py +0 -0
  50. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlark_loader.py +0 -0
  51. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/prelude.shipit +0 -0
  52. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/serve.shipit +0 -0
  53. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/go.shipit +0 -0
  54. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/hugo.shipit +0 -0
  55. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/jekyll.shipit +0 -0
  56. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/laravel.shipit +0 -0
  57. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/mkdocs.shipit +0 -0
  58. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/node.shipit +0 -0
  59. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/node_static.shipit +0 -0
  60. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/php.shipit +0 -0
  61. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/staticfile.shipit +0 -0
  62. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/starlib/tools/wordpress.shipit +0 -0
  63. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/ui.py +0 -0
  64. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/utils.py +0 -0
  65. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/src/shipit/volumes.py +0 -0
  66. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_helpers.py +0 -0
  67. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/go-graphql.json +0 -0
  68. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/go-simple.json +0 -0
  69. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/hugo.json +0 -0
  70. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/mkdocs-with-plugins.json +0 -0
  71. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/mkdocs-with-plugins__cross.json +0 -0
  72. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/mkdocs.json +0 -0
  73. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/mkdocs__cross.json +0 -0
  74. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-astro.json +0 -0
  75. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-elysia.json +0 -0
  76. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-express.json +0 -0
  77. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-fastify.json +0 -0
  78. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-h3.json +0 -0
  79. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-hono.json +0 -0
  80. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-hydrogen.json +0 -0
  81. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-koa.json +0 -0
  82. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-mastra.json +0 -0
  83. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-mcp.json +0 -0
  84. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-nestjs.json +0 -0
  85. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-next.json +0 -0
  86. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-nitro.json +0 -0
  87. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-npm-file-subdir.json +0 -0
  88. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-pnpm-workspace-subdir.json +0 -0
  89. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-react-router.json +0 -0
  90. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-remix.json +0 -0
  91. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-solidstart.json +0 -0
  92. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-tanstack-start.json +0 -0
  93. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node-xmcp.json +0 -0
  94. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/node.json +0 -0
  95. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-angular.json +0 -0
  96. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-assemble.json +0 -0
  97. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-astro.json +0 -0
  98. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-brunch.json +0 -0
  99. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-create-react-app.json +0 -0
  100. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-docusaurus-old.json +0 -0
  101. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-docusaurus.json +0 -0
  102. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-eleventy.json +0 -0
  103. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-ember.json +0 -0
  104. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-gatsby.json +0 -0
  105. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-harp.json +0 -0
  106. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-hexo.json +0 -0
  107. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-ionic-angular.json +0 -0
  108. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-ionic-react.json +0 -0
  109. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-metalsmith.json +0 -0
  110. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-next.json +0 -0
  111. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-nuxt.json +0 -0
  112. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-parcel.json +0 -0
  113. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-polymer.json +0 -0
  114. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-preact.json +0 -0
  115. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-remix.json +0 -0
  116. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-sanity.json +0 -0
  117. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-stencil.json +0 -0
  118. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-storybook.json +0 -0
  119. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-svelte.json +0 -0
  120. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-sveltekit.json +0 -0
  121. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-umijs.json +0 -0
  122. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-vite-react.json +0 -0
  123. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-vite.json +0 -0
  124. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-vitepress.json +0 -0
  125. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-vue.json +0 -0
  126. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/nodestatic-vuepress.json +0 -0
  127. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/php-api.json +0 -0
  128. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/php-laravel-react.json +0 -0
  129. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/php-nobuild.json +0 -0
  130. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/php-wordpress-empty.json +0 -0
  131. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/php-wordpress-plugin.json +0 -0
  132. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/php-wordpress-theme.json +0 -0
  133. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/php-wordpress.json +0 -0
  134. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-django.json +0 -0
  135. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-django__cross.json +0 -0
  136. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-fastapi.json +0 -0
  137. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-fastapi__cross.json +0 -0
  138. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-fasthtml.json +0 -0
  139. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-fasthtml__cross.json +0 -0
  140. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-ffmpeg.json +0 -0
  141. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-ffmpeg__cross.json +0 -0
  142. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-flask.json +0 -0
  143. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-flask__cross.json +0 -0
  144. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-mcp-chatgpt.json +0 -0
  145. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-mcp-chatgpt__cross.json +0 -0
  146. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-mcp.json +0 -0
  147. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-mcp__cross.json +0 -0
  148. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-pandoc.json +0 -0
  149. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-pandoc__cross.json +0 -0
  150. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-pillow.json +0 -0
  151. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-pillow__cross.json +0 -0
  152. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-procfile.json +0 -0
  153. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-procfile__cross.json +0 -0
  154. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-streamlit.json +0 -0
  155. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/python-streamlit__cross.json +0 -0
  156. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/static-htmlwithjs.json +0 -0
  157. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/static-nobuild.json +0 -0
  158. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/staticfile-redirects.json +0 -0
  159. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/staticfile.json +0 -0
  160. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/synthetic__go_subdir.json +0 -0
  161. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/synthetic__jekyll_bare.json +0 -0
  162. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/synthetic__jekyll_gemfile.json +0 -0
  163. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/synthetic__python_subdir.json +0 -0
  164. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/plan_snapshots/synthetic__static_subdir.json +0 -0
  165. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_cli_after_deploy.py +0 -0
  166. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_command_overrides.py +0 -0
  167. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_evaluator.py +0 -0
  168. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_generate_shipit_examples.py +0 -0
  169. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_install_context.py +0 -0
  170. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_php_provider.py +0 -0
  171. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_plan_snapshots.py +0 -0
  172. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_project_context.py +0 -0
  173. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_starlark_loader.py +0 -0
  174. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_subdir.py +0 -0
  175. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_version.py +0 -0
  176. {shipit_cli-0.22.1 → shipit_cli-0.23.0}/tests/test_volumes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shipit-cli
3
- Version: 0.22.1
3
+ Version: 0.23.0
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.22.1"
3
+ version = "0.23.0"
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"
@@ -54,7 +54,11 @@ else
54
54
  --locale="$WP_LOCALE"
55
55
 
56
56
  echo "🔄 Setting permalinks"
57
- wp rewrite structure '/%year%/%monthnum%/%day%/%postname%/'
57
+ # Avoid `wp rewrite structure`: it relaunches wp-cli in a subprocess to flush
58
+ # rewrites, which fails under WASIX (PHP_BINARY is empty) and aborts this
59
+ # script via `set -e`. Setting the option directly is equivalent, since we
60
+ # flush rewrites with `wp rewrite flush --hard` at the end of this script.
61
+ wp option update permalink_structure '/%year%/%monthnum%/%day%/%postname%/'
58
62
  fi
59
63
 
60
64
  # Install plugins from WP_PLUGINS environment variable
@@ -9,6 +9,8 @@ from pydantic import Field
9
9
  from pydantic_settings import SettingsConfigDict
10
10
  from semantic_version import NpmSpec, Version
11
11
 
12
+ from shipit.ui import console
13
+
12
14
  from .base import Config, DetectResult
13
15
  from .install_context import discover_js_install_context
14
16
 
@@ -536,6 +538,12 @@ class NodeProvider:
536
538
  if config.commands.start and cls._is_node_command(config.commands.start):
537
539
  return DetectResult(cls.name(), 35)
538
540
 
541
+ package_json = cls.parse_package_json(path)
542
+ package_start = (
543
+ cls.package_scripts(package_json).get("start", "").strip()
544
+ )
545
+ package_score = 30 if package_start else 10
546
+
539
547
  if config.commands.install:
540
548
  install_commands = {
541
549
  "npm install",
@@ -552,17 +560,17 @@ class NodeProvider:
552
560
  "bun i",
553
561
  }
554
562
  if config.commands.install in install_commands:
555
- return DetectResult(cls.name(), 30)
563
+ return DetectResult(cls.name(), package_score)
556
564
 
557
- package_json = cls.parse_package_json(path)
558
565
  found_deps = cls._check_package_json_deps(
559
566
  package_json, *cls.FRAMEWORK_DEPENDENCIES
560
567
  )
561
568
  if cls.detect_framework(package_json, found_deps, path):
562
- return DetectResult(cls.name(), 45)
569
+ framework_score = 45 if package_start else 10
570
+ return DetectResult(cls.name(), framework_score)
563
571
 
564
572
  if (path / "package.json").is_file():
565
- return DetectResult(cls.name(), 30)
573
+ return DetectResult(cls.name(), package_score)
566
574
 
567
575
  if cls._common_entry_file(path, require_node_evidence=True):
568
576
  return DetectResult(cls.name(), 30)
@@ -917,28 +925,42 @@ class NodeProvider:
917
925
 
918
926
  @classmethod
919
927
  def infer_start_command(
920
- cls, path: Path, package_json: Optional[Dict[str, Any]]
928
+ cls,
929
+ path: Path,
930
+ package_json: Optional[Dict[str, Any]],
931
+ *,
932
+ warn: bool = True,
921
933
  ) -> Optional[str]:
922
934
  scripts = cls.package_scripts(package_json)
923
935
  start_script = scripts.get("start")
924
- if start_script:
925
- return start_script.strip() or None
936
+ if start_script and start_script.strip():
937
+ return start_script.strip()
926
938
 
927
939
  if package_json:
928
940
  main = package_json.get("main")
929
941
  if isinstance(main, str) and main.strip():
930
942
  return cls._node_entry_command(main.strip())
931
943
 
944
+ if warn and (path / "package.json").is_file():
945
+ console.print(
946
+ "[bold yellow]Warning:[/bold yellow] "
947
+ "no start or main script found in package.json"
948
+ )
949
+
932
950
  entry_file = cls._common_entry_file(
933
951
  path,
934
952
  require_node_evidence=not (path / "package.json").is_file(),
935
953
  )
936
954
  if entry_file:
937
955
  return cls._node_entry_command(entry_file)
938
-
956
+ if warn:
957
+ tried = ", ".join(cls.COMMON_ENTRY_FILES)
958
+ console.print(
959
+ "[bold yellow]Warning:[/bold yellow] "
960
+ f"no entry file found for Node project (tried: {tried})"
961
+ )
939
962
  return None
940
963
 
941
964
  @classmethod
942
965
  def _node_entry_command(cls, entry_file: str) -> str:
943
966
  return f"node {shlex.quote(entry_file)}"
944
-
@@ -6,6 +6,8 @@ from typing import List, Optional, Set
6
6
  from pydantic import Field
7
7
  from pydantic_settings import SettingsConfigDict
8
8
 
9
+ from shipit.ui import console
10
+
9
11
  from .install_context import discover_python_dependency_files, discover_python_install_context
10
12
  from .base import DetectResult, _exists, Config
11
13
 
@@ -156,8 +158,88 @@ class PythonProvider:
156
158
  path, config.framework, config.main_file
157
159
  )
158
160
 
161
+ if not config.commands.start:
162
+ config.commands.start = cls.infer_start_command(config)
163
+
159
164
  return config
160
165
 
166
+ @staticmethod
167
+ def infer_start_command(
168
+ config: PythonConfig,
169
+ *,
170
+ warn: bool = True,
171
+ ) -> Optional[str]:
172
+ def cannot_infer(reason: str) -> Optional[str]:
173
+ if warn:
174
+ console.print(
175
+ f"[bold yellow]Warning:[/bold yellow] {reason}"
176
+ )
177
+ return None
178
+
179
+ def missing_main_file() -> Optional[str]:
180
+ if config.framework:
181
+ return cannot_infer(
182
+ "no main file was detected for the "
183
+ f"{config.framework.name} framework"
184
+ )
185
+ return cannot_infer(
186
+ "no main file was detected for Python project"
187
+ )
188
+
189
+ main_file = config.main_file
190
+ asgi = config.asgi_application
191
+ wsgi = config.wsgi_application
192
+
193
+ if config.server == PythonServer.Daphne:
194
+ if asgi:
195
+ return f"daphne {asgi} --bind 0.0.0.0 --port $PORT"
196
+ return cannot_infer(
197
+ "no ASGI application was detected for the Daphne server"
198
+ )
199
+ if config.server == PythonServer.Uvicorn:
200
+ if asgi:
201
+ return f"uvicorn {asgi} --host 0.0.0.0 --port $PORT"
202
+ if wsgi:
203
+ return (
204
+ f"uvicorn {wsgi} --interface=wsgi "
205
+ "--host 0.0.0.0 --port $PORT"
206
+ )
207
+ if not main_file:
208
+ if config.framework:
209
+ return missing_main_file()
210
+ return cannot_infer(
211
+ "no main file, ASGI application, or WSGI application "
212
+ "was detected for the Uvicorn server"
213
+ )
214
+ elif config.server == PythonServer.Hypercorn:
215
+ if asgi:
216
+ return f"hypercorn {asgi} --bind 0.0.0.0:$PORT"
217
+ return cannot_infer(
218
+ "no ASGI application was detected for the Hypercorn server"
219
+ )
220
+ elif config.framework == PythonFramework.Streamlit:
221
+ if main_file:
222
+ return (
223
+ f"streamlit run {main_file} --server.port $PORT "
224
+ "--server.address 0.0.0.0 --server.headless true"
225
+ )
226
+ return missing_main_file()
227
+ elif config.framework == PythonFramework.MCP:
228
+ if not main_file:
229
+ return missing_main_file()
230
+ if config.mcp_self_running:
231
+ return f"python {main_file}"
232
+ return (
233
+ f"python $VIRTUAL_ENV/bin/mcp run {main_file} "
234
+ "--transport=streamable-http"
235
+ )
236
+ elif config.framework == PythonFramework.Django:
237
+ return "python manage.py runserver 0.0.0.0:$PORT"
238
+
239
+ if main_file:
240
+ return f"python {main_file}"
241
+ return missing_main_file()
242
+
161
243
  @classmethod
162
244
  def check_deps(cls, path: Path, *deps: str) -> Set[str]:
163
245
  pending_deps = {dep.lower() for dep in deps}
@@ -88,14 +88,65 @@ class StaticFileProvider:
88
88
  if _exists(
89
89
  path, "index.html", "index.htm", "public/index.htm", "public/index.html"
90
90
  ):
91
- return DetectResult(cls.name(), 10)
91
+ return DetectResult(cls.name(), 15)
92
92
  return DetectResult(cls.name(), 10)
93
93
  if config.commands.start and config.commands.start.startswith(
94
94
  "static-web-server "
95
95
  ):
96
96
  return DetectResult(cls.name(), 70)
97
+ if cls._is_unbuilt_node_static_site(path, config):
98
+ return DetectResult(cls.name(), 15)
97
99
  return None
98
100
 
101
+ @classmethod
102
+ def _is_unbuilt_node_static_site(
103
+ cls,
104
+ path: Path,
105
+ config: Config,
106
+ ) -> bool:
107
+ if not _exists(path, "index.html", "index.htm"):
108
+ return False
109
+
110
+ # Local imports avoid a module cycle: node-static composes this
111
+ # provider for its serving behavior.
112
+ from .node import NodeProvider
113
+ from .node_static import NodeStaticProvider
114
+
115
+ package_json = NodeProvider.parse_package_json(path)
116
+ if package_json is None:
117
+ return False
118
+
119
+ scripts = NodeProvider.package_scripts(package_json)
120
+ if scripts.get("start", "").strip():
121
+ return False
122
+ build_scripts = ("build", "generate", "export", "docs:build")
123
+ if config.commands.build or any(
124
+ scripts.get(name, "").strip() for name in build_scripts
125
+ ):
126
+ return False
127
+ if NodeStaticProvider.detect(path, config):
128
+ return False
129
+
130
+ found_runtime_deps = NodeProvider._check_package_json_deps(
131
+ package_json,
132
+ *NodeProvider.FRAMEWORK_DEPENDENCIES,
133
+ )
134
+ if NodeProvider.detect_framework(
135
+ package_json,
136
+ found_runtime_deps,
137
+ path,
138
+ ):
139
+ return False
140
+
141
+ return (
142
+ NodeProvider.infer_start_command(
143
+ path,
144
+ package_json,
145
+ warn=False,
146
+ )
147
+ is None
148
+ )
149
+
99
150
  @classmethod
100
151
  def _load_redirect_rules(cls, redirects_path: Path) -> list[RedirectRule]:
101
152
  rules: list[RedirectRule] = []
@@ -31,8 +31,8 @@ SHIPIT_VERSION_ANNOTATION = "shipitcli.com/version"
31
31
  WASMER_APP_KIND_ANNOTATION = "wasmer.io/app-kind"
32
32
  WASMER_VERSION_ANNOTATION = "wasmer.io/version"
33
33
  BUILD_ANNOTATIONS_FILENAME = "build-annotations.yaml"
34
- EDGEJS_QUICKJS_DEPENDENCY = "wasmer/edgejs-quickjs@=0.0.7"
35
- PHPIX_VERSION = "0.3.0-rc.2"
34
+ EDGEJS_QUICKJS_DEPENDENCY = "wasmer/edgejs-quickjs@=0.1.0"
35
+ PHPIX_VERSION = "0.3.0-rc.3"
36
36
 
37
37
 
38
38
  def _phpix_dependency(php_version: str, bits: int) -> str:
@@ -260,9 +260,9 @@ class WasmerRunner:
260
260
  return Path("/opt") / name
261
261
 
262
262
  def prepare_config(self, provider_config: Any) -> Any:
263
- from shipit.providers.python import PythonConfig
264
- from shipit.providers.php import PhpConfig
265
263
  from shipit.providers.node import NodeConfig
264
+ from shipit.providers.php import PhpConfig
265
+ from shipit.providers.python import PythonConfig
266
266
 
267
267
  # Plan-visible overrides: the Shipit plan intentionally reacts to
268
268
  # these at evaluation time (cross-platform wheel builds etc.).
@@ -272,21 +272,15 @@ class WasmerRunner:
272
272
  )
273
273
  provider_config.cross_platform = "wasix_wasm32"
274
274
  provider_config.precompile_python = True
275
-
276
- # Runner-only overrides: these feed app.yaml/wasmer.toml metadata
277
- # (app kind, memory caps, runtime selection) but must NOT change the
278
- # evaluated plan — with providers evaluated from config, a shared
279
- # mutation here would silently flip e.g. php serving to phpix.
280
- # phpix stays opt-in via project config/SHIPIT_PHPIX.
281
- runner_config = provider_config.model_copy(deep=True)
282
- if isinstance(runner_config, PhpConfig):
283
- runner_config.phpix = True
284
- if isinstance(runner_config, NodeConfig):
285
- runner_config.use_edgejs = True
286
- if runner_config.precompile_edgejs is None:
287
- runner_config.precompile_edgejs = True
288
- runner_config.remove_native_binaries = True
289
- self.provider_config = runner_config
275
+ if isinstance(provider_config, PhpConfig):
276
+ provider_config.phpix = True
277
+ if isinstance(provider_config, NodeConfig):
278
+ provider_config.use_edgejs = True
279
+ if provider_config.precompile_edgejs is None:
280
+ provider_config.precompile_edgejs = True
281
+ provider_config.remove_native_binaries = True
282
+
283
+ self.provider_config = provider_config.model_copy(deep=True)
290
284
  return provider_config
291
285
 
292
286
  def prepare_build_steps(self, build_steps: List["Step"]) -> List["Step"]:
@@ -155,55 +155,18 @@ def python_build(config, source = None, app = None, venv = None, serving = True)
155
155
  local_venv = local_venv,
156
156
  )
157
157
 
158
- def python_commands(config, venv):
159
- """start/after_deploy commands for the detected server and framework."""
160
- asgi = config.asgi_application
161
- wsgi = config.wsgi_application
162
- main_file = config.main_file
163
-
164
- start = None
165
- if config.server == "daphne":
166
- if not asgi:
167
- fail("No ASGI application found for Daphne")
168
- start = f"daphne {asgi} --bind 0.0.0.0 --port {PORT}"
169
- elif config.server == "uvicorn":
170
- if not main_file and not asgi and not wsgi:
171
- fail("No ASGI or WSGI application found for Uvicorn and no main file found")
172
- if asgi:
173
- start = f"uvicorn {asgi} --host 0.0.0.0 --port {PORT}"
174
- elif wsgi:
175
- start = f"uvicorn {wsgi} --interface=wsgi --host 0.0.0.0 --port {PORT}"
176
- elif config.server == "hypercorn":
177
- if not asgi:
178
- fail("No ASGI application found for Hypercorn")
179
- start = f"hypercorn {asgi} --bind 0.0.0.0:{PORT}"
180
- elif config.framework == "streamlit":
181
- if not main_file:
182
- fail("No main file found for Streamlit")
183
- start = f"streamlit run {main_file} --server.port {PORT} --server.address 0.0.0.0 --server.headless true"
184
- elif config.framework == "mcp":
185
- if not main_file:
186
- fail("No main file found for MCP")
187
- if config.mcp_self_running:
188
- start = "python " + main_file
189
- else:
190
- start = "python {}/bin/mcp run {} --transport=streamable-http".format(venv.serve_path, main_file)
191
- elif config.framework == "django":
192
- start = f"python manage.py runserver 0.0.0.0:{PORT}"
193
-
194
- if not start and main_file:
195
- start = "python " + main_file
196
-
158
+ def python_commands(config):
159
+ """Commands resolved from the Python provider configuration."""
197
160
  commands = {}
198
- if start:
199
- commands["start"] = start
161
+ if config.commands.start:
162
+ commands["start"] = config.commands.start
200
163
  if config.migration_strategy == "django":
201
164
  commands["after_deploy"] = "python manage.py migrate"
202
165
  elif config.migration_strategy == "alembic":
203
166
  commands["after_deploy"] = "alembic upgrade head"
204
167
  return commands
205
168
 
206
- def python_env(config, app, site_packages):
169
+ def python_env(config, app, venv, site_packages):
207
170
  app_path = app.serve_path
208
171
  if config.main_file and config.main_file.startswith("src/"):
209
172
  pythonpath = "{}:{}/src:{}".format(app_path, app_path, site_packages)
@@ -215,6 +178,8 @@ def python_env(config, app, site_packages):
215
178
  elif config.framework == "mcp":
216
179
  env_vars["FASTMCP_HOST"] = "0.0.0.0"
217
180
  env_vars["FASTMCP_PORT"] = PORT
181
+ if not config.mcp_self_running:
182
+ env_vars["VIRTUAL_ENV"] = venv.serve_path
218
183
  return env_vars
219
184
 
220
185
  def python_prepare(config, site_packages, app_serve_path):
@@ -256,8 +221,8 @@ def python_serve(
256
221
  name = name,
257
222
  cwd = app.serve_path,
258
223
  prepare = prepare if prepare != None else python_prepare(config, site_packages, app.serve_path),
259
- env = python_env(config, app, site_packages),
260
- commands = python_commands(config, venv),
224
+ env = python_env(config, app, venv, site_packages),
225
+ commands = python_commands(config),
261
226
  services = services if services != None else python_services(config),
262
227
  mounts = [app, venv],
263
228
  **overrides
@@ -1,7 +1,7 @@
1
1
  __all__ = ["version", "version_info"]
2
2
 
3
3
 
4
- version = "0.22.1" # x-release-please-version
4
+ version = "0.23.0" # x-release-please-version
5
5
  _version_parts = version.split(".")
6
6
  version_info = (
7
7
  int(_version_parts[0]),
@@ -201,7 +201,7 @@ def _e2e_case_params(cases: list[E2ECase]) -> list[object]:
201
201
  E2ECase(
202
202
  path="examples/php-nobuild",
203
203
  serve_pattern=(
204
- r"PHP 8\.3\.[0-9]+ Development Server \(http://localhost:[\d]+\) started"
204
+ r"listening addr"
205
205
  ),
206
206
  http=[HTTPRequest(path="/", body_match=r"PHP Version 8\.3\.[0-9]+")],
207
207
  ),
@@ -209,7 +209,7 @@ def _e2e_case_params(cases: list[E2ECase]) -> list[object]:
209
209
  E2ECase(
210
210
  path="examples/php-nobuild",
211
211
  serve_pattern=(
212
- r"PHP 8\.3\.[0-9]+ Development Server \(http://localhost:[\d]+\) started"
212
+ r"listening addr"
213
213
  ),
214
214
  http=[HTTPRequest(path="/", body_match=r"PHP Version 8\.3\.[0-9]+")],
215
215
  ),
@@ -217,7 +217,7 @@ def _e2e_case_params(cases: list[E2ECase]) -> list[object]:
217
217
  E2ECase(
218
218
  path="examples/php-api",
219
219
  serve_pattern=(
220
- r"PHP 8\.3\.[0-9]+ Development Server \(http://localhost:[\d]+\) started"
220
+ r"listening addr"
221
221
  ),
222
222
  http=[
223
223
  HTTPRequest(
@@ -231,7 +231,7 @@ def _e2e_case_params(cases: list[E2ECase]) -> list[object]:
231
231
  E2ECase(
232
232
  path="examples/php-wordpress",
233
233
  serve_pattern=(
234
- r"PHP 8\.3\.[0-9]+ Development Server \(http://localhost:[\d]+\) started"
234
+ r"listening addr"
235
235
  ),
236
236
  http=[HTTPRequest(path="/", body_match=r"WordPress")],
237
237
  ),
@@ -342,7 +342,7 @@ def _e2e_case_params(cases: list[E2ECase]) -> list[object]:
342
342
  E2ECase(
343
343
  path="examples/php-wordpress",
344
344
  serve_pattern=(
345
- r"PHP 8\.3\.[0-9]+ Development Server \(http://localhost:[\d]+\) started"
345
+ r"listening addr"
346
346
  ),
347
347
  http=[HTTPRequest(path="/", body_match=r"WordPress")],
348
348
  extra_env={"SHIPIT_PHPIX": "true"},
@@ -352,7 +352,7 @@ def _e2e_case_params(cases: list[E2ECase]) -> list[object]:
352
352
  E2ECase(
353
353
  path="examples/php-nobuild",
354
354
  serve_pattern=(
355
- r"PHP 8\.3\.[0-9]+ Development Server \(http://localhost:[\d]+\) started"
355
+ r"listening addr"
356
356
  ),
357
357
  http=[HTTPRequest(path="/", body_match=r"PHP Version 8\.3\.[0-9]+")],
358
358
  extra_env={"SHIPIT_PHPIX": "true"},
@@ -534,7 +534,7 @@ def _e2e_case_params(cases: list[E2ECase]) -> list[object]:
534
534
  path="examples/node-astro",
535
535
  serve_pattern=r"Node|Astro|Listening|ready",
536
536
  http=[HTTPRequest(path="/", body_match=r"Astro Node Example")],
537
- build_modes=(BuildMode.Local,),
537
+ build_modes=(BuildMode.Local, BuildMode.Wasmer),
538
538
  ),
539
539
  # Hugo static site (built via Hugo, served with static-web-server)
540
540
  E2ECase(
@@ -341,6 +341,18 @@ def test_node_start_command_uses_package_main(tmp_path: Path) -> None:
341
341
  assert provider_config.commands.start == "node src/server.js"
342
342
 
343
343
 
344
+ def test_node_start_command_ignores_empty_script_and_uses_package_main(
345
+ tmp_path: Path,
346
+ ) -> None:
347
+ (tmp_path / "package.json").write_text(
348
+ '{"scripts": {"start": " "}, "main": "src/server.js"}\n'
349
+ )
350
+
351
+ provider_config = NodeProvider.load_config(tmp_path, Config())
352
+
353
+ assert provider_config.commands.start == "node src/server.js"
354
+
355
+
344
356
  def test_node_start_command_uses_common_entry_file(tmp_path: Path) -> None:
345
357
  (tmp_path / "server.js").write_text(
346
358
  """const http = require("http");
@@ -356,6 +368,20 @@ http.createServer((_req, res) => {
356
368
  assert provider_config.commands.start == "node server.js"
357
369
 
358
370
 
371
+ def test_node_provider_warns_when_start_command_is_missing(
372
+ tmp_path: Path,
373
+ capsys,
374
+ ) -> None:
375
+ (tmp_path / "package.json").write_text('{"name": "missing-start"}\n')
376
+
377
+ provider_config = NodeProvider.load_config(tmp_path, Config())
378
+
379
+ assert provider_config.commands.start is None
380
+ warning = capsys.readouterr().err
381
+ assert "Warning: no start or main script found in package.json" in warning
382
+ assert "Warning: no entry file found for Node project" in warning
383
+
384
+
359
385
  def test_node_build_steps_optimize_deps_prunes_then_node_modules(
360
386
  tmp_path: Path,
361
387
  ) -> None:
@@ -6,7 +6,7 @@ from pydantic import ValidationError
6
6
 
7
7
  from shipit.generator import load_provider, load_provider_config
8
8
  from shipit.providers.base import Config
9
- from shipit.providers.node import NodeFramework, PackageManager
9
+ from shipit.providers.node import NodeFramework, NodeProvider, PackageManager
10
10
  from shipit.providers.node_static import (
11
11
  NodeStaticConfig,
12
12
  NodeStaticProvider,
@@ -245,7 +245,9 @@ def test_pure_static_dependency_keeps_priority_with_package_script_command() ->
245
245
  assert detect_result.score == 60
246
246
 
247
247
 
248
- def test_explicit_next_build_command_uses_node_provider(tmp_path: Path) -> None:
248
+ def test_next_build_without_start_command_uses_node_static(
249
+ tmp_path: Path,
250
+ ) -> None:
249
251
  (tmp_path / "package.json").write_text(
250
252
  """{
251
253
  "scripts": {
@@ -261,10 +263,13 @@ def test_explicit_next_build_command_uses_node_provider(tmp_path: Path) -> None:
261
263
  base_config.commands.build = "next build"
262
264
 
263
265
  detect_result = NodeStaticProvider.detect(tmp_path, base_config)
266
+ node_result = NodeProvider.detect(tmp_path, base_config)
264
267
 
265
268
  assert detect_result is not None
266
269
  assert detect_result.score == 20
267
- assert load_provider(tmp_path, base_config) is not NodeStaticProvider
270
+ assert node_result is not None
271
+ assert node_result.score < detect_result.score
272
+ assert load_provider(tmp_path, base_config) is NodeStaticProvider
268
273
 
269
274
 
270
275
  def test_explicit_next_export_command_stays_node_static(tmp_path: Path) -> None: