feat: ship php only on the full images (#11922)

* feat: ship php only on full images with a clear missing-php error

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: let a bare php or composer path resolve through PATH

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-10-21 18:01:38 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 2c6bcc09c9
commit b87486141d
6 changed files with 33 additions and 22 deletions
-3
View File
@@ -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/
+2 -1
View File
@@ -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
+23 -7
View File
@@ -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<String>,
occupancy_metrics: &mut OccupancyMetrics,
) -> Result<String> {
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<Box<RawValue>> {
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)?,
+4
View File
@@ -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
+4
View File
@@ -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
@@ -24,12 +24,6 @@ func main(x int) (interface{}, error) {
"python3": """
def main(x: int):
return x
""",
"php": """<?php
function main(int $x): int {
return $x;
}
""",
}
@@ -76,8 +70,3 @@ class TestIdentityScript(unittest.TestCase):
path = PATH_TEMPLATE.format(lang="python3")
result = self._client.run_sync(path, {"x": 5})
self.assertEqual(result, 5)
def test_php(self):
path = PATH_TEMPLATE.format(lang="php")
result = self._client.run_sync(path, {"x": 5})
self.assertEqual(result, 5)