diff --git a/Dockerfile b/Dockerfile index 8b4adebcba..ef37868d0d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -305,9 +305,6 @@ RUN bun install -g windmill-cli \ RUN curl -fsSL https://claude.ai/install.sh | bash \ && cp /root/.local/share/claude/versions/* /usr/bin/claude -COPY --from=php:8.3.33-cli-trixie /usr/local/bin/php /usr/bin/php -COPY --from=composer:2.9.5 /usr/bin/composer /usr/bin/composer - # add the docker client to call docker from a worker if enabled COPY --from=docker:29-dind /usr/local/bin/docker /usr/local/bin/ diff --git a/README.md b/README.md index e9ed6381f1..597a9142b2 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,8 @@ You can run scripts locally by passing the right environment variables for the ` - **Runtimes**: - TypeScript/JavaScript: Bun (default); Deno only on the `windmill-full` images - Python: python3 with uv for dependency management - - Go, Bash, PowerShell, PHP, Rust, C#, Java, Ansible + - PHP: only on the `windmill-full` images + - Go, Bash, PowerShell, Rust, C#, Java, Ansible ## Fastest Self-Hostable Workflow Engine diff --git a/backend/windmill-worker/src/php_executor.rs b/backend/windmill-worker/src/php_executor.rs index 26559e8b37..bc4583d11e 100644 --- a/backend/windmill-worker/src/php_executor.rs +++ b/backend/windmill-worker/src/php_executor.rs @@ -2,7 +2,7 @@ use convert_case::{Case, Casing}; use itertools::Itertools; use regex::Regex; use serde_json::value::RawValue; -use std::{collections::HashMap, process::Stdio}; +use std::{collections::HashMap, path::Path, process::Stdio}; use tokio::{fs::File, io::AsyncReadExt, process::Command}; use uuid::Uuid; use windmill_common::{ @@ -20,10 +20,9 @@ use windmill_queue::{append_logs, CanceledBy}; use crate::common::DEV_CONF_NSJAIL; use crate::{ common::{ - build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file, - get_reserved_variables, read_result, resolve_nsjail_timeout, - resolve_nsjail_tmp_mount_block, script_envars, start_child_process, MaybeLock, - OccupancyMetrics, + build_command_with_isolation, create_args_and_out_file, get_reserved_variables, + read_result, resolve_nsjail_timeout, resolve_nsjail_tmp_mount_block, script_envars, + start_child_process, MaybeLock, OccupancyMetrics, }, handle_child::handle_child, is_sandboxing_enabled, COMPOSER_CACHE_DIR, COMPOSER_PATH, DISABLE_NUSER, NSJAIL_PATH, PATH_ENV, @@ -44,6 +43,22 @@ lazy_static::lazy_static! { const COMPOSER_LOCK_SPLIT: &str = "\nLOCK\n"; +fn ensure_php_tool_installed(tool: &str, path: &str) -> Result<()> { + // A bare name is resolved through PATH when the process starts, not here. + let path_ref = Path::new(path); + if !path_ref.is_absolute() || path_ref.exists() { + return Ok(()); + } + Err(error::Error::ExecutionErr(format!( + "{tool} is not installed on this worker (looked for it at {path}). PHP and Composer are no \ + longer included in the standard windmill and windmill-ee images: they ship only in the \ + full images. To run PHP scripts, switch the workers that run PHP jobs to \ + ghcr.io/windmill-labs/windmill-full (Community Edition) or \ + ghcr.io/windmill-labs/windmill-ee-full (Enterprise Edition), or install PHP and Composer \ + on them and point PHP_PATH and COMPOSER_PATH at them." + ))) +} + static PHP_PARSER_SLOT: tokio::sync::Semaphore = tokio::sync::Semaphore::const_new(1); pub(crate) async fn parse_php_signature( @@ -123,7 +138,8 @@ pub async fn composer_install( lock: Option, occupancy_metrics: &mut OccupancyMetrics, ) -> Result { - check_executor_binary_exists("php", PHP_PATH.as_str(), "php")?; + ensure_php_tool_installed("PHP", PHP_PATH.as_str())?; + ensure_php_tool_installed("Composer", COMPOSER_PATH.as_str())?; // When no lock is provided (previews), try to reuse a previously resolved // lockfile from the DB so we can hit the same vendor cache as deployed scripts. @@ -415,7 +431,7 @@ pub async fn handle_php_job( shared_mount: &str, occupancy_metrics: &mut OccupancyMetrics, ) -> error::Result> { - check_executor_binary_exists("php", PHP_PATH.as_str(), "php")?; + ensure_php_tool_installed("PHP", PHP_PATH.as_str())?; let (composer_json, composer_lock) = match &maybe_lock { MaybeLock::Resolved { lock } if !lock.is_empty() => split_reqs_and_lock(lock)?, diff --git a/docker/DockerfileFull b/docker/DockerfileFull index c3a82c8312..61bde89216 100644 --- a/docker/DockerfileFull +++ b/docker/DockerfileFull @@ -25,6 +25,10 @@ RUN wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh \ && ./dotnet-install.sh --channel 9.0 --install-dir /usr/share/dotnet \ && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet \ && rm dotnet-install.sh +# PHP +COPY --from=php:8.3.33-cli-trixie /usr/local/bin/php /usr/bin/php +COPY --from=composer:2.9.5 /usr/bin/composer /usr/bin/composer + # Nushell COPY --from=ghcr.io/nushell/nushell:0.101.0-bookworm /usr/bin/nu /usr/bin/nu diff --git a/docker/DockerfileFullEe b/docker/DockerfileFullEe index bb83195f62..fc7df76987 100644 --- a/docker/DockerfileFullEe +++ b/docker/DockerfileFullEe @@ -53,6 +53,10 @@ RUN apt-get -y update && apt-get install -y libaio1t64 \ && ln -s "$(basename "$libaio")" "$(dirname "$libaio")/libaio.so.1" RUN echo /opt/oracle/23/lib > /etc/ld.so.conf.d/oracle-instantclient.conf && ldconfig +# PHP +COPY --from=php:8.3.33-cli-trixie /usr/local/bin/php /usr/bin/php +COPY --from=composer:2.9.5 /usr/bin/composer /usr/bin/composer + # Nushell COPY --from=ghcr.io/nushell/nushell:0.101.0-bookworm /usr/bin/nu /usr/bin/nu diff --git a/integration_tests/test/identity_script_test.py b/integration_tests/test/identity_script_test.py index 638919fb22..c4689e1874 100644 --- a/integration_tests/test/identity_script_test.py +++ b/integration_tests/test/identity_script_test.py @@ -24,12 +24,6 @@ func main(x int) (interface{}, error) { "python3": """ def main(x: int): return x -""", - "php": """