From f081fb10705cadf99e99dfa786d1cc2ebf0447db Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 8 Sep 2026 11:23:35 +0000 Subject: [PATCH] feat: recognize `// volume:` mounts in PHP scripts (#11018) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: recognize `// volume:` mounts in PHP scripts Volume annotations were parsed for every language but PHP, so a PHP script could not mount a workspace volume. Two things stood in the way: PHP had no entry in the comment-prefix maps, and a PHP script opens with ` Claude-Session: https://claude.ai/code/session_01T3FR7iS9nRhpFt615cnuQ7 * fix: tolerate a PHP opener that carries code, drop the inert CLI hunk The open-tag skip matched ` Claude-Session: https://claude.ai/code/session_01T3FR7iS9nRhpFt615cnuQ7 * docs: correct the CLI mirror comment, state the own-line annotation rule Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01T3FR7iS9nRhpFt615cnuQ7 --------- Co-authored-by: Claude Opus 5 (1M context) --- .../src/asset_inference.rs | 23 +++++++++++++--- backend/windmill-worker-volumes/src/lib.rs | 26 ++++++++++++++++++- backend/windmill-worker/src/worker.rs | 3 ++- cli/src/commands/pipeline/localGraph.ts | 8 +++++- frontend/src/lib/infer.ts | 8 +++++- 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/backend/windmill-api-scripts/src/asset_inference.rs b/backend/windmill-api-scripts/src/asset_inference.rs index 9c503ae1ae..1c0bc6d57f 100644 --- a/backend/windmill-api-scripts/src/asset_inference.rs +++ b/backend/windmill-api-scripts/src/asset_inference.rs @@ -78,20 +78,26 @@ fn comment_prefix(lang: &ScriptLang) -> Option<&'static str> { | ScriptLang::Bun | ScriptLang::Bunnative | ScriptLang::Nativets - | ScriptLang::Go => Some("//"), + | ScriptLang::Go + | ScriptLang::Php => Some("//"), _ => None, } } /// Mirror of the frontend `parseVolumeAnnotations` (infer.ts): ` /// volume: ` lines in the leading comment block, each an `rw` volume -/// asset. Scanning stops at the first non-comment line (blank lines are -/// skipped), exactly like the frontend. +/// asset. Scanning stops at the first non-comment line; blank lines and PHP's +/// opening tag line are skipped whole (the tag may carry code, so an annotation +/// must sit on its own line below it), exactly like the frontend. fn parse_volume_annotations(content: &str, prefix: &str) -> Vec { let mut volumes = Vec::new(); for line in content.lines() { let trimmed = line.trim(); - if trimmed.is_empty() { + if trimmed.is_empty() + || trimmed + .get(..5) + .is_some_and(|p| p.eq_ignore_ascii_case(" = got.iter().filter(|a| a.kind == AssetKind::Volume).collect(); + assert_eq!(vols.len(), 1); + assert_eq!(vols[0].path, "my_vol"); + } } diff --git a/backend/windmill-worker-volumes/src/lib.rs b/backend/windmill-worker-volumes/src/lib.rs index a9003a3712..e2e4ab866b 100644 --- a/backend/windmill-worker-volumes/src/lib.rs +++ b/backend/windmill-worker-volumes/src/lib.rs @@ -179,11 +179,24 @@ pub fn interpolate_volume_name( result } +/// PHP's opening tag, which is case-insensitive and may be followed by code. +fn is_php_open_tag(trimmed_line: &str) -> bool { + trimmed_line + .get(..5) + .is_some_and(|p| p.eq_ignore_ascii_case(" volume: ` lines, +/// stopping at the first line that is neither blank nor a comment. A PHP script +/// opens with ` Vec { let mut volumes = Vec::new(); for line in content.lines() { let trimmed = line.trim(); - if trimmed.is_empty() { + if trimmed.is_empty() || is_php_open_tag(trimmed) { continue; } if !trimmed.starts_with(comment_prefix) { @@ -230,6 +243,17 @@ mod tests { ); } + #[test] + fn parse_php_volume_after_open_tag() { + let content = + " "//", + | ScriptLang::Go + | ScriptLang::Php => "//", _ => "", }; let raw_mounts = windmill_worker_volumes::parse_volume_annotations(&code, comment_prefix); diff --git a/cli/src/commands/pipeline/localGraph.ts b/cli/src/commands/pipeline/localGraph.ts index 5f9e713f42..f6173a28bb 100644 --- a/cli/src/commands/pipeline/localGraph.ts +++ b/cli/src/commands/pipeline/localGraph.ts @@ -413,7 +413,13 @@ export function parseMuteAnnotations(content: string): { // Comment prefix for `volume:` annotations. Deliberately NOT `commentPrefix` // above (which returns `--` for SQL): volume annotations are only recognized for // the languages the backend/frontend recognize them for — mirrors -// `asset_inference.rs:comment_prefix` and `infer.ts:getCommentPrefix` (SQL → none). +// `asset_inference.rs:comment_prefix` and `infer.ts:getCommentPrefix` (SQL → none), +// minus `php`. Those two recognize PHP, but a PHP script cannot reach this map: it +// has no wasm asset parser, so `fallbackParse` handles it and that scan breaks on +// the mandatory ` } // e.g { id: "number", name: "text" } | { error: string; columns?: undefined } // error message if preparation failed +// Scans the leading comment block, stopping at the first line that is neither blank +// nor a comment. A PHP script opens with `