mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-07 16:02:27 +00:00
@@ -173,6 +173,9 @@ COPY --from=downloader --chmod=755 /deno /usr/bin/deno
|
||||
|
||||
COPY --from=oven/bun:1.1.7 /usr/local/bin/bun /usr/bin/bun
|
||||
|
||||
COPY --from=php:8.3.7-cli /usr/local/bin/php /usr/bin/php
|
||||
COPY --from=composer:2.7.6 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# add the docker client to call docker from a worker if enabled
|
||||
COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/
|
||||
|
||||
|
||||
+2
-1
@@ -54,7 +54,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -48,7 +48,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -68,7 +68,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -48,7 +48,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -48,7 +48,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -74,7 +74,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -48,7 +48,8 @@
|
||||
"snowflake",
|
||||
"graphql",
|
||||
"powershell",
|
||||
"mssql"
|
||||
"mssql",
|
||||
"php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+44
@@ -208,6 +208,15 @@ dependencies = [
|
||||
"password-hash 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ariadne"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
|
||||
dependencies = [
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "array-init"
|
||||
version = "2.1.0"
|
||||
@@ -5097,6 +5106,18 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "php-parser-rs"
|
||||
version = "0.1.3"
|
||||
source = "git+https://github.com/php-rust-tools/parser?rev=ec4cb411dec09450946ef57920b7ffced7f6495d#ec4cb411dec09450946ef57920b7ffced7f6495d"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"clap",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.5"
|
||||
@@ -9876,6 +9897,20 @@ dependencies = [
|
||||
"windmill-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.327.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"php-parser-rs",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"windmill-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.329.0"
|
||||
@@ -9946,6 +9981,7 @@ dependencies = [
|
||||
"windmill-parser-bash",
|
||||
"windmill-parser-go",
|
||||
"windmill-parser-graphql",
|
||||
"windmill-parser-php",
|
||||
"windmill-parser-py",
|
||||
"windmill-parser-sql",
|
||||
"windmill-parser-ts",
|
||||
@@ -10006,6 +10042,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
"const_format",
|
||||
"convert_case 0.6.0",
|
||||
"deno_ast",
|
||||
"deno_console",
|
||||
"deno_core",
|
||||
@@ -10056,6 +10093,7 @@ dependencies = [
|
||||
"windmill-parser-bash",
|
||||
"windmill-parser-go",
|
||||
"windmill-parser-graphql",
|
||||
"windmill-parser-php",
|
||||
"windmill-parser-py",
|
||||
"windmill-parser-py-imports",
|
||||
"windmill-parser-sql",
|
||||
@@ -10300,6 +10338,12 @@ dependencies = [
|
||||
"lzma-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.3"
|
||||
|
||||
@@ -107,6 +107,7 @@ windmill-parser-go = { path = "./parsers/windmill-parser-go" }
|
||||
windmill-parser-bash = { path = "./parsers/windmill-parser-bash" }
|
||||
windmill-parser-sql = { path = "./parsers/windmill-parser-sql" }
|
||||
windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" }
|
||||
windmill-parser-php = { path = "./parsers/windmill-parser-php" }
|
||||
windmill-api-client = { path = "./windmill-api-client" }
|
||||
|
||||
axum = { version = "^0.7", features = ["multipart"] }
|
||||
@@ -139,6 +140,7 @@ rand_core = { version = "^0", features = ["std"] }
|
||||
magic-crypt = "^3"
|
||||
git-version = "^0"
|
||||
rustpython-parser = { git = "https://github.com/RustPython/Parser", rev = "9ce55aefdeb35e2f706ce0b02d5a2dfe6295fc57" }
|
||||
php-parser-rs = { git = "https://github.com/php-rust-tools/parser", rev = "ec4cb411dec09450946ef57920b7ffced7f6495d" }
|
||||
cron = "^0"
|
||||
mail-send = { version = "0.4.0", features = ["builder"], default-features=false }
|
||||
urlencoding = "^2"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add up migration script here
|
||||
ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'php';
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add up migration script here
|
||||
UPDATE config set config = '{"worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun", "php"]}'::jsonb where name = 'worker__default' and config @> '{"worker_tags": ["deno", "python3", "go", "bash", "powershell", "dependency", "flow", "hub", "other", "bun"]}'::jsonb;
|
||||
UPDATE workspace_settings SET default_scripts = jsonb_set(default_scripts, '{order}', default_scripts->'order' || '["php"]'::jsonb) WHERE default_scripts IS NOT NULL AND default_scripts->'order' IS NOT NULL AND NOT default_scripts->'order' @> '["php"]'::jsonb;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "windmill-parser-php"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "windmill_parser_php"
|
||||
path = "./src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
windmill-parser.workspace = true
|
||||
itertools.workspace = true
|
||||
serde_json.workspace = true
|
||||
anyhow.workspace = true
|
||||
php-parser-rs.workspace = true
|
||||
convert_case.workspace = true
|
||||
lazy_static.workspace = true
|
||||
regex.workspace = true
|
||||
@@ -0,0 +1,166 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use regex::Regex;
|
||||
use serde_json::Value;
|
||||
use windmill_parser::{Arg, MainArgSignature, Typ};
|
||||
|
||||
use php_parser_rs::parser::{
|
||||
self,
|
||||
ast::{
|
||||
data_type::Type,
|
||||
functions::{FunctionParameterList, FunctionStatement},
|
||||
literals::Literal,
|
||||
Expression, Statement,
|
||||
},
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref RE_SNK_CASE: Regex = Regex::new(r"_(\d)").unwrap();
|
||||
}
|
||||
|
||||
fn to_snake_case(s: String) -> String {
|
||||
let r = s.to_case(Case::Snake);
|
||||
|
||||
// s_3 => s3
|
||||
RE_SNK_CASE.replace_all(&r, "$1").to_string()
|
||||
}
|
||||
|
||||
fn parse_php_type(e: Type) -> Typ {
|
||||
match e {
|
||||
Type::Float(_) => Typ::Float,
|
||||
Type::Boolean(_) => Typ::Bool,
|
||||
Type::Integer(_) => Typ::Int,
|
||||
Type::String(_) => Typ::Str(None),
|
||||
Type::Array(_) => Typ::List(Box::new(Typ::Str(None))),
|
||||
Type::Object(_) => Typ::Object(vec![]),
|
||||
Type::Named(_, name) => Typ::Resource(to_snake_case(name.to_string())),
|
||||
_ => Typ::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_default_expr(e: Expression) -> Option<Value> {
|
||||
match e {
|
||||
Expression::Literal(l) => match l {
|
||||
Literal::String(s) => Some(Value::String(s.value.to_string())),
|
||||
Literal::Integer(i) => match i.value.to_string().parse() {
|
||||
Ok(i) => Some(Value::Number(i)),
|
||||
Err(_) => None,
|
||||
},
|
||||
Literal::Float(f) => match f.value.to_string().parse() {
|
||||
Ok(i) => Some(Value::Number(i)),
|
||||
Err(_) => None,
|
||||
},
|
||||
},
|
||||
Expression::Bool(b) => Some(Value::Bool(b.value)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_php_signature(
|
||||
code: &str,
|
||||
override_main: Option<String>,
|
||||
) -> anyhow::Result<MainArgSignature> {
|
||||
let main_name = override_main.unwrap_or("main".to_string());
|
||||
|
||||
let ast = parser::parse(code)
|
||||
.map_err(|e| anyhow::anyhow!("Error parsing code: {}", e.to_string()))?;
|
||||
|
||||
let params = ast.into_iter().find_map(|x| match x {
|
||||
Statement::Function(FunctionStatement {
|
||||
name,
|
||||
parameters: FunctionParameterList { parameters, .. },
|
||||
..
|
||||
}) if name.to_string() == main_name => Some(parameters),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if let Some(params) = params {
|
||||
let args = params
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let typ = x.data_type.map_or(Typ::Unknown, |e| parse_php_type(e));
|
||||
let default = x.default.map_or(None, |e| parse_default_expr(e));
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: x.name.to_string().trim_start_matches('$').to_string(),
|
||||
typ,
|
||||
has_default: default.is_some(),
|
||||
default,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args })
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"main function was not findable".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use serde_json::Number;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_php_sig() -> anyhow::Result<()> {
|
||||
let code = "
|
||||
<?php
|
||||
|
||||
class Stripe {}
|
||||
|
||||
function main(string $input1 = \"hey\", bool $input2 = false, int $input3 = 3, float $input4 = 4.5, Stripe $resource) {
|
||||
echo 'hello';
|
||||
}
|
||||
|
||||
";
|
||||
assert_eq!(
|
||||
parse_php_signature(code, None)?,
|
||||
MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: vec![
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "input1".to_string(),
|
||||
typ: Typ::Str(None),
|
||||
has_default: true,
|
||||
default: Some(Value::String("hey".to_string()))
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "input2".to_string(),
|
||||
typ: Typ::Bool,
|
||||
has_default: true,
|
||||
default: Some(Value::Bool(false))
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "input3".to_string(),
|
||||
typ: Typ::Int,
|
||||
has_default: true,
|
||||
default: Some(Value::Number(Number::from(3)))
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "input4".to_string(),
|
||||
typ: Typ::Float,
|
||||
has_default: true,
|
||||
default: Some(Value::Number(Number::from_f64(f64::from(4.5)).unwrap()))
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "resource".to_string(),
|
||||
typ: Typ::Resource("stripe".to_string()),
|
||||
has_default: false,
|
||||
default: None
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ windmill-parser-bash.workspace = true
|
||||
windmill-parser-sql.workspace = true
|
||||
windmill-parser-py.workspace = true
|
||||
windmill-parser-ts.workspace = true
|
||||
windmill-parser-php.workspace = true
|
||||
windmill-parser-graphql.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"collaborators": [
|
||||
"Ruben Fiszel <ruben@windmill.dev>"
|
||||
],
|
||||
"version": "1.318.0",
|
||||
"version": "1.327.0",
|
||||
"files": [
|
||||
"windmill_parser_wasm_bg.wasm",
|
||||
"windmill_parser_wasm.js",
|
||||
|
||||
@@ -70,6 +70,11 @@ export function parse_db_resource(code: string): string | undefined;
|
||||
* @returns {string}
|
||||
*/
|
||||
export function parse_graphql(code: string): string;
|
||||
/**
|
||||
* @param {string} code
|
||||
* @returns {string}
|
||||
*/
|
||||
export function parse_php(code: string): string;
|
||||
|
||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
@@ -89,6 +94,7 @@ export interface InitOutput {
|
||||
readonly parse_mssql: (a: number, b: number, c: number) => void;
|
||||
readonly parse_db_resource: (a: number, b: number, c: number) => void;
|
||||
readonly parse_graphql: (a: number, b: number, c: number) => void;
|
||||
readonly parse_php: (a: number, b: number, c: number) => void;
|
||||
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
||||
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||
|
||||
@@ -97,15 +97,6 @@ function getInt32Memory0() {
|
||||
return cachedInt32Memory0;
|
||||
}
|
||||
|
||||
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
|
||||
|
||||
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
@@ -115,6 +106,15 @@ function addHeapObject(obj) {
|
||||
return idx;
|
||||
}
|
||||
|
||||
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
|
||||
|
||||
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
let cachedFloat64Memory0 = null;
|
||||
|
||||
function getFloat64Memory0() {
|
||||
@@ -519,6 +519,29 @@ export function parse_graphql(code) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
* @returns {string}
|
||||
*/
|
||||
export function parse_php(code) {
|
||||
let deferred2_0;
|
||||
let deferred2_1;
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passStringToWasm0(code, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.parse_php(retptr, ptr0, len0);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
deferred2_0 = r0;
|
||||
deferred2_1 = r1;
|
||||
return getStringFromWasm0(r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(f, args) {
|
||||
try {
|
||||
return f.apply(this, args);
|
||||
@@ -564,6 +587,10 @@ function __wbg_get_imports() {
|
||||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbg_eval_f2b8ae7add53626d = function(arg0, arg1) {
|
||||
const ret = eval(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
@@ -572,10 +599,6 @@ function __wbg_get_imports() {
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len1;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
|
||||
};
|
||||
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
|
||||
const ret = new Error(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_boolean_get = function(arg0) {
|
||||
const v = getObject(arg0);
|
||||
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||
@@ -597,6 +620,10 @@ function __wbg_get_imports() {
|
||||
const ret = BigInt.asUintN(64, arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
|
||||
const ret = new Error(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'number' ? obj : undefined;
|
||||
@@ -612,10 +639,6 @@ function __wbg_get_imports() {
|
||||
const ret = getObject(arg0) in getObject(arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_eval_33c4985197d1feaf = function(arg0, arg1) {
|
||||
const ret = eval(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_jsval_loose_eq = function(arg0, arg1) {
|
||||
const ret = getObject(arg0) == getObject(arg1);
|
||||
return ret;
|
||||
|
||||
Binary file not shown.
@@ -15,6 +15,7 @@ export function parse_snowflake(a: number, b: number, c: number): void;
|
||||
export function parse_mssql(a: number, b: number, c: number): void;
|
||||
export function parse_db_resource(a: number, b: number, c: number): void;
|
||||
export function parse_graphql(a: number, b: number, c: number): void;
|
||||
export function parse_php(a: number, b: number, c: number): void;
|
||||
export function __wbindgen_malloc(a: number, b: number): number;
|
||||
export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
|
||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
||||
|
||||
@@ -92,3 +92,8 @@ pub fn parse_db_resource(code: &str) -> Option<String> {
|
||||
pub fn parse_graphql(code: &str) -> String {
|
||||
wrap_sig(windmill_parser_graphql::parse_graphql_sig(code))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_php(code: &str) -> String {
|
||||
wrap_sig(windmill_parser_php::parse_php_signature(code, None))
|
||||
}
|
||||
|
||||
@@ -8406,6 +8406,7 @@ components:
|
||||
graphql,
|
||||
nativets,
|
||||
bun,
|
||||
php,
|
||||
]
|
||||
kind:
|
||||
type: string
|
||||
@@ -8501,6 +8502,7 @@ components:
|
||||
graphql,
|
||||
nativets,
|
||||
bun,
|
||||
php,
|
||||
]
|
||||
kind:
|
||||
type: string
|
||||
@@ -8719,6 +8721,7 @@ components:
|
||||
graphql,
|
||||
nativets,
|
||||
bun,
|
||||
php,
|
||||
]
|
||||
email:
|
||||
type: string
|
||||
@@ -8829,6 +8832,7 @@ components:
|
||||
graphql,
|
||||
nativets,
|
||||
bun,
|
||||
php,
|
||||
]
|
||||
is_skipped:
|
||||
type: boolean
|
||||
@@ -9343,6 +9347,7 @@ components:
|
||||
graphql,
|
||||
nativets,
|
||||
bun,
|
||||
php
|
||||
]
|
||||
tag:
|
||||
type: string
|
||||
@@ -10380,6 +10385,7 @@ components:
|
||||
graphql,
|
||||
nativets,
|
||||
bun,
|
||||
php
|
||||
]
|
||||
required:
|
||||
- raw_code
|
||||
|
||||
@@ -557,7 +557,8 @@ async fn create_script_internal<'c>(
|
||||
let lock = if !(ns.language == ScriptLang::Python3
|
||||
|| ns.language == ScriptLang::Go
|
||||
|| ns.language == ScriptLang::Bun
|
||||
|| ns.language == ScriptLang::Deno)
|
||||
|| ns.language == ScriptLang::Deno
|
||||
|| ns.language == ScriptLang::Php)
|
||||
{
|
||||
Some(String::new())
|
||||
} else {
|
||||
|
||||
@@ -2473,6 +2473,7 @@ async fn tarball_workspace(
|
||||
"bun.ts"
|
||||
}
|
||||
}
|
||||
ScriptLang::Php => "php",
|
||||
};
|
||||
archive
|
||||
.write_to_archive(&script.content, &format!("{}.{}", script.path, ext))
|
||||
|
||||
@@ -39,6 +39,7 @@ pub enum ScriptLang {
|
||||
Snowflake,
|
||||
Graphql,
|
||||
Mssql,
|
||||
Php,
|
||||
}
|
||||
|
||||
impl ScriptLang {
|
||||
@@ -57,6 +58,7 @@ impl ScriptLang {
|
||||
ScriptLang::Snowflake => "snowflake",
|
||||
ScriptLang::Mssql => "mssql",
|
||||
ScriptLang::Graphql => "graphql",
|
||||
ScriptLang::Php => "php",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ windmill-parser-py-imports.workspace = true
|
||||
windmill-parser-bash.workspace = true
|
||||
windmill-parser-sql.workspace = true
|
||||
windmill-parser-graphql.workspace = true
|
||||
windmill-parser-php.workspace = true
|
||||
windmill-git-sync.workspace = true
|
||||
sqlx.workspace = true
|
||||
uuid.workspace = true
|
||||
@@ -80,6 +81,7 @@ tokio-util = { workspace = true, optional = true }
|
||||
openidconnect = { workspace = true, optional = true}
|
||||
tar = { workspace = true, optional = true}
|
||||
object_store = { workspace = true, optional = true}
|
||||
convert_case.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
deno_fetch.workspace = true
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
name: "php run script"
|
||||
|
||||
mode: ONCE
|
||||
hostname: "php"
|
||||
log_level: ERROR
|
||||
|
||||
disable_rl: true
|
||||
|
||||
cwd: "/tmp"
|
||||
|
||||
clone_newnet: false
|
||||
clone_newuser: {CLONE_NEWUSER}
|
||||
|
||||
keep_caps: false
|
||||
keep_env: true
|
||||
mount_proc: true
|
||||
|
||||
mount {
|
||||
src: "/bin"
|
||||
dst: "/bin"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "/lib"
|
||||
dst: "/lib"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
|
||||
mount {
|
||||
src: "/lib64"
|
||||
dst: "/lib64"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
|
||||
mount {
|
||||
src: "/usr"
|
||||
dst: "/usr"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "/dev/null"
|
||||
dst: "/dev/null"
|
||||
is_bind: true
|
||||
rw: true
|
||||
}
|
||||
|
||||
mount {
|
||||
dst: "/tmp"
|
||||
fstype: "tmpfs"
|
||||
rw: true
|
||||
options: "size=800000000"
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/main.php"
|
||||
dst: "/tmp/main.php"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/wrapper.php"
|
||||
dst: "/tmp/wrapper.php"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "/etc"
|
||||
dst: "/etc"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "/dev/random"
|
||||
dst: "/dev/random"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "/dev/urandom"
|
||||
dst: "/dev/urandom"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/args.json"
|
||||
dst: "/tmp/args.json"
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/result.json"
|
||||
dst: "/tmp/result.json"
|
||||
rw: true
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/composer.json"
|
||||
dst: "/tmp/composer.json"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/composer.lock"
|
||||
dst: "/tmp/composer.lock"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "{JOB_DIR}/vendor"
|
||||
dst: "/tmp/vendor"
|
||||
is_bind: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
iface_no_lo: true
|
||||
|
||||
mount {
|
||||
src: "{CACHE_DIR}"
|
||||
dst: "/tmp/windmill/cache/php"
|
||||
is_bind: true
|
||||
rw: true
|
||||
mandatory: false
|
||||
}
|
||||
|
||||
{SHARED_MOUNT}
|
||||
|
||||
envar: "HOME=/tmp"
|
||||
@@ -18,6 +18,7 @@ mod graphql_executor;
|
||||
mod js_eval;
|
||||
mod mysql_executor;
|
||||
mod pg_executor;
|
||||
mod php_executor;
|
||||
mod python_executor;
|
||||
mod worker;
|
||||
mod worker_flow;
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
use serde_json::value::RawValue;
|
||||
use std::{collections::HashMap, process::Stdio};
|
||||
use tokio::{fs::File, io::AsyncReadExt, process::Command};
|
||||
use uuid::Uuid;
|
||||
use windmill_common::{
|
||||
error::{self, to_anyhow, Result},
|
||||
jobs::QueuedJob,
|
||||
};
|
||||
use windmill_parser::Typ;
|
||||
use windmill_queue::{append_logs, CanceledBy};
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
create_args_and_out_file, get_main_override, get_reserved_variables, handle_child,
|
||||
read_result, start_child_process, write_file,
|
||||
},
|
||||
AuthedClientBackgroundTask, COMPOSER_CACHE_DIR, COMPOSER_PATH, DISABLE_NSJAIL, DISABLE_NUSER,
|
||||
NSJAIL_PATH, PHP_PATH,
|
||||
};
|
||||
|
||||
const NSJAIL_CONFIG_RUN_PHP_CONTENT: &str = include_str!("../nsjail/run.php.config.proto");
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"^//\s?(\S+)\s*$").unwrap();
|
||||
}
|
||||
|
||||
const COMPOSER_LOCK_SPLIT: &str = "\nLOCK\n";
|
||||
|
||||
pub fn parse_php_imports(code: &str) -> anyhow::Result<Option<String>> {
|
||||
let find_requirements = code
|
||||
.lines()
|
||||
.find_position(|x| x.starts_with("//require:") || x.starts_with("// require:"));
|
||||
|
||||
if let Some((pos, _)) = find_requirements {
|
||||
let requirements = code
|
||||
.lines()
|
||||
.skip(pos + 1)
|
||||
.map_while(|x| {
|
||||
RE.captures(x).map(|x| {
|
||||
match x.get(1).unwrap().as_str().split("@").collect_vec()[..] {
|
||||
[path, version] => (path.to_string(), version.to_string()),
|
||||
[path] | [path, ..] => (path.to_string(), "*".to_string()),
|
||||
[] => unreachable!(),
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let composer_json =
|
||||
serde_json::to_string_pretty(&HashMap::from([("require", requirements)]))
|
||||
.map_err(to_anyhow)?;
|
||||
|
||||
Ok(Some(composer_json))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn composer_install(
|
||||
mem_peak: &mut i32,
|
||||
canceled_by: &mut Option<CanceledBy>,
|
||||
job_id: &Uuid,
|
||||
w_id: &str,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
job_dir: &str,
|
||||
worker_name: &str,
|
||||
requirements: String,
|
||||
lock: Option<String>,
|
||||
) -> Result<String> {
|
||||
write_file(job_dir, "composer.json", &requirements).await?;
|
||||
|
||||
if let Some(lock) = lock.as_ref() {
|
||||
write_file(job_dir, "composer.lock", lock).await?;
|
||||
}
|
||||
|
||||
let mut child_cmd = Command::new(&*COMPOSER_PATH);
|
||||
let args = vec!["install", "--no-dev", "--no-progress"];
|
||||
child_cmd
|
||||
.current_dir(job_dir)
|
||||
.env("COMPOSER_HOME", &*COMPOSER_CACHE_DIR)
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
let child_process = start_child_process(child_cmd, &*COMPOSER_PATH).await?;
|
||||
|
||||
handle_child(
|
||||
job_id,
|
||||
db,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
child_process,
|
||||
false,
|
||||
worker_name,
|
||||
w_id,
|
||||
"composer install",
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match lock {
|
||||
Some(lock) => Ok(format!("{requirements}{COMPOSER_LOCK_SPLIT}{lock}")),
|
||||
None => {
|
||||
let mut lock_content = "".to_string();
|
||||
let mut lock_file = File::open(format!("{job_dir}/composer.lock")).await?;
|
||||
lock_file.read_to_string(&mut lock_content).await?;
|
||||
Ok(format!("{requirements}{COMPOSER_LOCK_SPLIT}{lock_content}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_resource_class(rt_name: &str, arg_name: &str) -> String {
|
||||
let rt_name = rt_name.to_case(Case::Pascal);
|
||||
format!(
|
||||
"#[AllowDynamicProperties]
|
||||
class {rt_name} {{
|
||||
public function __construct($data) {{
|
||||
foreach ($data AS $key => $value) $this->{{$key}} = $value;
|
||||
}}
|
||||
}}
|
||||
$args->{arg_name} = new {rt_name}($args->{arg_name});"
|
||||
)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub async fn handle_php_job(
|
||||
requirements_o: Option<String>,
|
||||
mem_peak: &mut i32,
|
||||
canceled_by: &mut Option<CanceledBy>,
|
||||
job: &QueuedJob,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
client: &AuthedClientBackgroundTask,
|
||||
job_dir: &str,
|
||||
inner_content: &String,
|
||||
base_internal_url: &str,
|
||||
worker_name: &str,
|
||||
envs: HashMap<String, String>,
|
||||
shared_mount: &str,
|
||||
) -> error::Result<Box<RawValue>> {
|
||||
let (composer_json, composer_lock) = match requirements_o {
|
||||
Some(reqs_and_lock) if !reqs_and_lock.is_empty() => {
|
||||
let splitted = reqs_and_lock.split(COMPOSER_LOCK_SPLIT).collect_vec();
|
||||
if splitted.len() != 2 {
|
||||
return Err(error::Error::ExecutionErr(
|
||||
format!("Invalid requirements, expected to find LOCK split pattern in reqs. Found: |{reqs_and_lock}|")
|
||||
));
|
||||
}
|
||||
(Some(splitted[0].to_string()), Some(splitted[1].to_string()))
|
||||
}
|
||||
_ => (parse_php_imports(inner_content)?, None),
|
||||
};
|
||||
|
||||
let autoload_line = if let Some(composer_json) = composer_json {
|
||||
let logs1 = "\n\n--- COMPOSER INSTALL ---\n".to_string();
|
||||
append_logs(job.id, job.workspace_id.clone(), logs1, db).await;
|
||||
|
||||
composer_install(
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
&job.id,
|
||||
&job.workspace_id,
|
||||
db,
|
||||
job_dir,
|
||||
worker_name,
|
||||
composer_json,
|
||||
composer_lock,
|
||||
)
|
||||
.await?;
|
||||
"require './vendor/autoload.php';"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let init_logs = "\n\n--- PHP CODE EXECUTION ---\n".to_string();
|
||||
|
||||
append_logs(job.id.clone(), job.workspace_id.to_string(), init_logs, db).await;
|
||||
|
||||
let _ = write_file(job_dir, "main.php", inner_content).await?;
|
||||
|
||||
let main_override = get_main_override(job.args.as_ref());
|
||||
|
||||
let write_wrapper_f = async {
|
||||
let args =
|
||||
windmill_parser_php::parse_php_signature(inner_content, main_override.clone())?.args;
|
||||
|
||||
let args_to_include = args
|
||||
.iter()
|
||||
.filter(|x| {
|
||||
!x.has_default || job.args.as_ref().is_some_and(|a| a.contains_key(&x.name))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let func_args_str = args_to_include
|
||||
.iter()
|
||||
.map(|x| format!("$args->{}", x.name))
|
||||
.collect::<Vec<String>>()
|
||||
.join(",");
|
||||
|
||||
let resource_classes = args_to_include
|
||||
.iter()
|
||||
.filter_map(|x| match &x.typ {
|
||||
Typ::Resource(name) => Some((name, &x.name)),
|
||||
_ => None,
|
||||
})
|
||||
.unique()
|
||||
.map(|(rt_name, arg_name)| generate_resource_class(rt_name, arg_name))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n\n");
|
||||
|
||||
let main_name = main_override.unwrap_or("main".to_string());
|
||||
|
||||
let wrapper_content: String = format!(
|
||||
r#"
|
||||
<?php
|
||||
{autoload_line}
|
||||
require './main.php';
|
||||
|
||||
$args_str = file_get_contents('./args.json');
|
||||
$args = json_decode($args_str);
|
||||
|
||||
{resource_classes}
|
||||
|
||||
try {{
|
||||
$res = {main_name}({func_args_str});
|
||||
file_put_contents("result.json", json_encode($res));
|
||||
}} catch (Exception $e) {{
|
||||
$err = [
|
||||
"message" => $e->getMessage(),
|
||||
"name" => $e->getName(),
|
||||
"stack" => $e->getTraceAsString()
|
||||
];
|
||||
$step_id = getenv('WM_FLOW_STEP_ID');
|
||||
if ($step_id) {{
|
||||
$err["step_id"] = $step_id;
|
||||
}}
|
||||
file_put_contents("result.json", json_encode($err));
|
||||
exit(1);
|
||||
}}
|
||||
"#,
|
||||
);
|
||||
write_file(job_dir, "wrapper.php", &wrapper_content).await?;
|
||||
Ok(()) as error::Result<()>
|
||||
};
|
||||
|
||||
let reserved_variables_args_out_f = async {
|
||||
let args_and_out_f = async {
|
||||
create_args_and_out_file(&client, job, job_dir, db).await?;
|
||||
Ok(()) as Result<()>
|
||||
};
|
||||
let reserved_variables_f = async {
|
||||
let client = client.get_authed().await;
|
||||
let vars = get_reserved_variables(job, &client.token, db).await?;
|
||||
Ok(vars) as Result<HashMap<String, String>>
|
||||
};
|
||||
let (_, reserved_variables) = tokio::try_join!(args_and_out_f, reserved_variables_f)?;
|
||||
Ok(reserved_variables) as error::Result<HashMap<String, String>>
|
||||
};
|
||||
|
||||
let (reserved_variables, _) = tokio::try_join!(reserved_variables_args_out_f, write_wrapper_f)?;
|
||||
|
||||
let child = if !*DISABLE_NSJAIL {
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
&NSJAIL_CONFIG_RUN_PHP_CONTENT
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
let args = vec![
|
||||
"--config",
|
||||
"run.config.proto",
|
||||
"--",
|
||||
&PHP_PATH,
|
||||
"/tmp/wrapper.php",
|
||||
];
|
||||
nsjail_cmd
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.env("COMPOSER_HOME", &*COMPOSER_CACHE_DIR)
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
start_child_process(nsjail_cmd, NSJAIL_PATH.as_str()).await?
|
||||
} else {
|
||||
let cmd = {
|
||||
let script_path = format!("{job_dir}/wrapper.php");
|
||||
|
||||
let mut php_cmd = Command::new(&*PHP_PATH);
|
||||
let args = vec![&script_path];
|
||||
php_cmd
|
||||
.current_dir(job_dir)
|
||||
.env_clear()
|
||||
.envs(envs)
|
||||
.envs(reserved_variables)
|
||||
.env("COMPOSER_HOME", &*COMPOSER_CACHE_DIR)
|
||||
.env("BASE_INTERNAL_URL", base_internal_url)
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
php_cmd
|
||||
};
|
||||
start_child_process(cmd, &*PHP_PATH).await?
|
||||
};
|
||||
|
||||
handle_child(
|
||||
&job.id,
|
||||
db,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
child,
|
||||
false,
|
||||
worker_name,
|
||||
&job.workspace_id,
|
||||
"php run",
|
||||
job.timeout,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
read_result(job_dir).await
|
||||
}
|
||||
@@ -100,6 +100,7 @@ use crate::{
|
||||
js_eval::{eval_fetch_timeout, transpile_ts},
|
||||
mysql_executor::do_mysql,
|
||||
pg_executor::do_postgresql,
|
||||
php_executor::{composer_install, handle_php_job, parse_php_imports},
|
||||
python_executor::{
|
||||
create_dependencies_dir, handle_python_job, handle_python_reqs, pip_compile,
|
||||
},
|
||||
@@ -203,6 +204,7 @@ pub const BUN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "bun");
|
||||
pub const HUB_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "hub");
|
||||
pub const GO_BIN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "gobin");
|
||||
pub const POWERSHELL_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "powershell");
|
||||
pub const COMPOSER_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "composer");
|
||||
|
||||
const NUM_SECS_PING: u64 = 5;
|
||||
|
||||
@@ -273,6 +275,8 @@ lazy_static::lazy_static! {
|
||||
pub static ref NPM_PATH: String = std::env::var("NPM_PATH").unwrap_or_else(|_| "/usr/bin/npm".to_string());
|
||||
pub static ref NODE_PATH: String = std::env::var("NODE_PATH").unwrap_or_else(|_| "/usr/bin/node".to_string());
|
||||
pub static ref POWERSHELL_PATH: String = std::env::var("POWERSHELL_PATH").unwrap_or_else(|_| "/usr/bin/pwsh".to_string());
|
||||
pub static ref PHP_PATH: String = std::env::var("PHP_PATH").unwrap_or_else(|_| "/usr/bin/php".to_string());
|
||||
pub static ref COMPOSER_PATH: String = std::env::var("COMPOSER_PATH").unwrap_or_else(|_| "/usr/bin/composer".to_string());
|
||||
pub static ref NSJAIL_PATH: String = std::env::var("NSJAIL_PATH").unwrap_or_else(|_| "nsjail".to_string());
|
||||
pub static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new());
|
||||
pub static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
||||
@@ -3431,6 +3435,23 @@ mount {{
|
||||
)
|
||||
.await
|
||||
}
|
||||
Some(ScriptLang::Php) => {
|
||||
handle_php_job(
|
||||
requirements_o,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
job,
|
||||
db,
|
||||
client,
|
||||
job_dir,
|
||||
&inner_content,
|
||||
base_internal_url,
|
||||
worker_name,
|
||||
envs,
|
||||
&shared_mount,
|
||||
)
|
||||
.await
|
||||
}
|
||||
_ => panic!("unreachable, language is not supported: {language:#?}"),
|
||||
};
|
||||
tracing::info!(
|
||||
@@ -4357,6 +4378,34 @@ async fn capture_dependency_job(
|
||||
.await?;
|
||||
Ok(req.unwrap_or_else(String::new))
|
||||
}
|
||||
ScriptLang::Php => {
|
||||
let reqs = if raw_deps {
|
||||
if job_raw_code.is_empty() {
|
||||
return Ok("".to_string());
|
||||
}
|
||||
job_raw_code.to_string()
|
||||
} else {
|
||||
match parse_php_imports(job_raw_code)? {
|
||||
Some(reqs) => reqs,
|
||||
None => {
|
||||
return Ok("".to_string());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
composer_install(
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
job_id,
|
||||
w_id,
|
||||
db,
|
||||
job_dir,
|
||||
worker_name,
|
||||
reqs,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
ScriptLang::Postgresql => Ok("".to_owned()),
|
||||
ScriptLang::Mysql => Ok("".to_owned()),
|
||||
ScriptLang::Bigquery => Ok("".to_owned()),
|
||||
|
||||
+14
-4
@@ -30,7 +30,7 @@ import { generateHash } from "./utils.ts";
|
||||
export async function generateAllMetadata() {}
|
||||
|
||||
function findClosestRawReqs(
|
||||
lang: "bun" | "python3" | undefined,
|
||||
lang: "bun" | "python3" | "php" | undefined,
|
||||
remotePath: string,
|
||||
globalDeps: GlobalDeps
|
||||
): string | undefined {
|
||||
@@ -53,6 +53,15 @@ function findClosestRawReqs(
|
||||
bestCandidate = { k, v };
|
||||
}
|
||||
});
|
||||
} else if (lang == "php") {
|
||||
Object.entries(globalDeps.composers).forEach(([k, v]) => {
|
||||
if (
|
||||
remotePath.startsWith(k) &&
|
||||
k.length >= (bestCandidate?.k ?? "").length
|
||||
) {
|
||||
bestCandidate = { k, v };
|
||||
}
|
||||
});
|
||||
}
|
||||
// @ts-ignore
|
||||
return bestCandidate?.v;
|
||||
@@ -77,14 +86,14 @@ export async function generateMetadataInternal(
|
||||
const language = inferContentTypeFromFilePath(scriptPath, opts.defaultTs);
|
||||
|
||||
const rawReqs = findClosestRawReqs(
|
||||
language as "bun" | "python3" | undefined,
|
||||
language as "bun" | "python3" | "php" | undefined,
|
||||
scriptPath,
|
||||
globalDeps
|
||||
);
|
||||
if (rawReqs) {
|
||||
log.info(
|
||||
colors.blue(
|
||||
`Found raw requirements (package.json/requirements.txt) for ${scriptPath}, using it`
|
||||
`Found raw requirements (package.json/requirements.txt/composer.json) for ${scriptPath}, using it`
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -184,7 +193,8 @@ async function updateScriptLock(
|
||||
language == "bun" ||
|
||||
language == "python3" ||
|
||||
language == "go" ||
|
||||
language == "deno"
|
||||
language == "deno" ||
|
||||
language == "php"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
|
||||
+11
-2
@@ -671,17 +671,24 @@ async function bootstrap(
|
||||
export type GlobalDeps = {
|
||||
pkgs: Record<string, string>;
|
||||
reqs: Record<string, string>;
|
||||
composers: Record<string, string>;
|
||||
};
|
||||
export async function findGlobalDeps(
|
||||
codebases: SyncCodebase[]
|
||||
): Promise<GlobalDeps> {
|
||||
const pkgs: { [key: string]: string } = {};
|
||||
const reqs: { [key: string]: string } = {};
|
||||
const composers: { [key: string]: string } = {};
|
||||
const els = await FSFSElement(Deno.cwd(), codebases);
|
||||
for await (const entry of readDirRecursiveWithIgnore((p, isDir) => {
|
||||
p = "/" + p;
|
||||
return (
|
||||
!isDir && !(p.endsWith("/package.json") || p.endsWith("requirements.txt"))
|
||||
!isDir &&
|
||||
!(
|
||||
p.endsWith("/package.json") ||
|
||||
p.endsWith("requirements.txt") ||
|
||||
p.endsWith("composer.json")
|
||||
)
|
||||
);
|
||||
}, els)) {
|
||||
if (entry.isDirectory || entry.ignored) continue;
|
||||
@@ -690,9 +697,11 @@ export async function findGlobalDeps(
|
||||
pkgs[entry.path.substring(0, entry.path.length - 12)] = content;
|
||||
} else if (entry.path.endsWith("requirements.txt")) {
|
||||
reqs[entry.path.substring(0, entry.path.length - 16)] = content;
|
||||
} else if (entry.path.endsWith("composer.json")) {
|
||||
composers[entry.path.substring(0, entry.path.length - 13)] = content;
|
||||
}
|
||||
}
|
||||
return { pkgs, reqs };
|
||||
return { pkgs, reqs, composers };
|
||||
}
|
||||
async function generateMetadata(
|
||||
opts: GlobalOptions & {
|
||||
|
||||
Generated
+4
-4
@@ -53,7 +53,7 @@
|
||||
"vscode-languageclient": "~9.0.1",
|
||||
"vscode-uri": "~3.0.8",
|
||||
"vscode-ws-jsonrpc": "~3.1.0",
|
||||
"windmill-parser-wasm": "^1.318.0",
|
||||
"windmill-parser-wasm": "^1.327.0",
|
||||
"windmill-sql-datatype-parser-wasm": "^1.318.0",
|
||||
"y-monaco": "^0.1.4",
|
||||
"y-websocket": "^1.5.0",
|
||||
@@ -10310,9 +10310,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/windmill-parser-wasm": {
|
||||
"version": "1.318.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.318.0.tgz",
|
||||
"integrity": "sha512-uxiluG0/EIwqwmR4FTppxa2eHZ0hrTijjWmf5L1vJYAgdCotI+glQhjdD2EMNtQRwX9+Pjj8QoVYckrNwJ/2yg=="
|
||||
"version": "1.327.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.327.0.tgz",
|
||||
"integrity": "sha512-lliouG5syBSwaPDN3C5fd2whqZmD5kW7S+gXBtunnMwIydPfXiHqWTL4F2zvbHhcHhiIgN04ATxL7gpxcMP1wg=="
|
||||
},
|
||||
"node_modules/windmill-sql-datatype-parser-wasm": {
|
||||
"version": "1.318.0",
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
"vscode-languageclient": "~9.0.1",
|
||||
"vscode-uri": "~3.0.8",
|
||||
"vscode-ws-jsonrpc": "~3.1.0",
|
||||
"windmill-parser-wasm": "^1.318.0",
|
||||
"windmill-parser-wasm": "^1.327.0",
|
||||
"windmill-sql-datatype-parser-wasm": "^1.318.0",
|
||||
"y-monaco": "^0.1.4",
|
||||
"y-websocket": "^1.5.0",
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/graphql/graphql.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/powershell/powershell.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/php/php.contribution'
|
||||
import 'monaco-editor/esm/vs/language/typescript/monaco.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/css/css.contribution'
|
||||
|
||||
@@ -84,6 +85,7 @@
|
||||
| 'sql'
|
||||
| 'graphql'
|
||||
| 'powershell'
|
||||
| 'php'
|
||||
| 'css'
|
||||
| 'javascript'
|
||||
export let code: string = ''
|
||||
@@ -166,6 +168,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
export function backspace(): void {
|
||||
if (editor) {
|
||||
editor.trigger('keyboard', 'deleteLeft', {})
|
||||
}
|
||||
}
|
||||
|
||||
export function insertAtBeginning(code: string): void {
|
||||
if (editor) {
|
||||
const range = { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }
|
||||
|
||||
@@ -86,11 +86,19 @@
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'nativets'
|
||||
'nativets',
|
||||
'php'
|
||||
].includes(lang ?? '')
|
||||
$: showVarPicker = [
|
||||
'python3',
|
||||
'bash',
|
||||
'powershell',
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'nativets',
|
||||
'php'
|
||||
].includes(lang ?? '')
|
||||
$: showVarPicker = ['python3', 'bash', 'powershell', 'go', 'deno', 'bun', 'nativets'].includes(
|
||||
lang ?? ''
|
||||
)
|
||||
$: showResourcePicker = [
|
||||
'python3',
|
||||
'bash',
|
||||
@@ -98,10 +106,13 @@
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'nativets'
|
||||
'nativets',
|
||||
'php'
|
||||
].includes(lang ?? '')
|
||||
$: showResourceTypePicker =
|
||||
['typescript', 'javascript'].includes(scriptLangToEditorLang(lang)) || lang === 'python3'
|
||||
['typescript', 'javascript'].includes(scriptLangToEditorLang(lang)) ||
|
||||
lang === 'python3' ||
|
||||
lang === 'php'
|
||||
|
||||
let codeViewer: Drawer
|
||||
let codeObj: { language: SupportedLanguage; content: string } | undefined = undefined
|
||||
@@ -187,12 +198,47 @@
|
||||
if (!code.includes('from typing import TypedDict')) {
|
||||
editor.insertAtBeginning('from typing import TypedDict\n')
|
||||
}
|
||||
} else if (lang === 'php') {
|
||||
const phpSchema = phpCompile(resourceType.schema as any)
|
||||
const rtName = toCamel(capitalize(name))
|
||||
editor.insertAtCursor(`if (!class_exists('${rtName}')) {\nclass ${rtName} {\n${phpSchema}\n`)
|
||||
editor.backspace()
|
||||
editor.insertAtCursor('}')
|
||||
} else {
|
||||
const tsSchema = compile(resourceType.schema as any)
|
||||
editor.insertAtCursor(`type ${toCamel(capitalize(name))} = ${tsSchema}\n`)
|
||||
editor.insertAtCursor(`type ${toCamel(capitalize(name))} = ${tsSchema}`)
|
||||
}
|
||||
sendUserToast(`${name} inserted at cursor`)
|
||||
}
|
||||
|
||||
function phpCompile(schema: Schema) {
|
||||
let res = ' '
|
||||
const entries = Object.entries(schema.properties)
|
||||
if (entries.length === 0) {
|
||||
return 'array'
|
||||
}
|
||||
let i = 0
|
||||
for (let [name, prop] of entries) {
|
||||
let typ = 'array'
|
||||
if (prop.type === 'array') {
|
||||
typ = 'array'
|
||||
} else if (prop.type === 'string') {
|
||||
typ = 'string'
|
||||
} else if (prop.type === 'number') {
|
||||
typ = 'float'
|
||||
} else if (prop.type === 'integer') {
|
||||
typ = 'int'
|
||||
} else if (prop.type === 'boolean') {
|
||||
typ = 'bool'
|
||||
}
|
||||
res += `public ${typ} $${name};`
|
||||
i++
|
||||
if (i < entries.length) {
|
||||
res += '\n'
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
function pythonCompile(schema: Schema) {
|
||||
let res = ''
|
||||
const entries = Object.entries(schema.properties)
|
||||
@@ -285,6 +331,8 @@
|
||||
editor.insertAtCursor(`$${name}`)
|
||||
} else if (lang == 'powershell') {
|
||||
editor.insertAtCursor(`$Env:${name}`)
|
||||
} else if (lang == 'php') {
|
||||
editor.insertAtCursor(`getenv('${name}');`)
|
||||
}
|
||||
sendUserToast(`${name} inserted at cursor`)
|
||||
}}
|
||||
@@ -336,6 +384,11 @@
|
||||
editor.insertAtBeginning(`import * as wmill from "./windmill.ts"\n`)
|
||||
}
|
||||
editor.insertAtCursor(`(await wmill.getVariable('${path}'))`)
|
||||
} else if (lang == 'php') {
|
||||
editor.insertAtCursor(`$ch = curl_init(getenv('BASE_INTERNAL_URL') . '/api/w/' . getenv('WM_WORKSPACE') . '/variables/get_value/${path}');
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . getenv('WM_TOKEN')));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$var = json_decode(curl_exec($ch));`)
|
||||
}
|
||||
sendUserToast(`${name} inserted at cursor`)
|
||||
}}
|
||||
@@ -401,6 +454,11 @@
|
||||
editor.insertAtBeginning(`import * as wmill from "./windmill.ts"\n`)
|
||||
}
|
||||
editor.insertAtCursor(`(await wmill.getResource('${path}'))`)
|
||||
} else if (lang == 'php') {
|
||||
editor.insertAtCursor(`$ch = curl_init(getenv('BASE_INTERNAL_URL') . '/api/w/' . getenv('WM_WORKSPACE') . '/resources/get_value_interpolated/${path}');
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . getenv('WM_TOKEN')));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$res = json_decode(curl_exec($ch));`)
|
||||
}
|
||||
sendUserToast(`${path} inserted at cursor`)
|
||||
}}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import javascript from 'svelte-highlight/languages/javascript'
|
||||
import sql from 'svelte-highlight/languages/sql'
|
||||
import powershell from 'svelte-highlight/languages/powershell'
|
||||
import php from 'svelte-highlight/languages/php'
|
||||
import type { Script } from '$lib/gen'
|
||||
import { Button } from './common'
|
||||
import { copyToClipboard } from '$lib/utils'
|
||||
@@ -45,6 +46,8 @@
|
||||
return sql
|
||||
case 'powershell':
|
||||
return powershell
|
||||
case 'php':
|
||||
return php
|
||||
|
||||
default:
|
||||
return typescript
|
||||
|
||||
@@ -93,7 +93,8 @@
|
||||
'flow',
|
||||
'hub',
|
||||
'other',
|
||||
'bun'
|
||||
'bun',
|
||||
'php'
|
||||
]
|
||||
const nativeTags = [
|
||||
'nativets',
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import BunIcon from '$lib/components/icons/BunIcon.svelte'
|
||||
import DenoIcon from '$lib/components/icons/DenoIcon.svelte'
|
||||
import type { Script } from '$lib/gen'
|
||||
import PHPIcon from '$lib/components/icons/PHPIcon.svelte'
|
||||
|
||||
export let lang:
|
||||
| SupportedLanguage
|
||||
@@ -42,7 +43,8 @@
|
||||
snowflake: 'Snowflake',
|
||||
mysql: 'MySQL',
|
||||
mssql: 'MS SQL Server',
|
||||
bun: 'TypeScript'
|
||||
bun: 'TypeScript',
|
||||
php: 'PHP'
|
||||
}
|
||||
|
||||
const langToComponent: Record<
|
||||
@@ -66,7 +68,8 @@
|
||||
powershell: PowershellIcon,
|
||||
postgresql: PostgresIcon,
|
||||
nativets: RestIcon,
|
||||
graphql: GraphqlIcon
|
||||
graphql: GraphqlIcon,
|
||||
php: PHPIcon
|
||||
}
|
||||
|
||||
let subIconScale = width === 30 ? 0.6 : 0.8
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<script lang="ts">
|
||||
export let height = '24px'
|
||||
export let width = '24px'
|
||||
</script>
|
||||
|
||||
<svg
|
||||
{height}
|
||||
{width}
|
||||
id="svg3430"
|
||||
version="1.1"
|
||||
viewBox="0 0 711.20123 383.5975"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title id="title3510">Official PHP Logo</title>
|
||||
<defs id="defs3434">
|
||||
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath3444">
|
||||
<path
|
||||
d="M 11.52,162 C 11.52,81.677 135.307,16.561 288,16.561 l 0,0 c 152.693,0 276.481,65.116 276.481,145.439 l 0,0 c 0,80.322 -123.788,145.439 -276.481,145.439 l 0,0 C 135.307,307.439 11.52,242.322 11.52,162"
|
||||
id="path3446"
|
||||
/>
|
||||
</clipPath>
|
||||
<radialGradient
|
||||
cx="0"
|
||||
cy="0"
|
||||
fx="0"
|
||||
fy="0"
|
||||
gradientTransform="matrix(363.05789,0,0,-363.05789,177.52002,256.30713)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3452"
|
||||
r="1"
|
||||
spreadMethod="pad"
|
||||
>
|
||||
<stop id="stop3454" offset="0" style="stop-opacity:1;stop-color:#aeb2d5" />
|
||||
<stop id="stop3456" offset="0.3" style="stop-opacity:1;stop-color:#aeb2d5" />
|
||||
<stop id="stop3458" offset="0.75" style="stop-opacity:1;stop-color:#484c89" />
|
||||
<stop id="stop3460" offset="1" style="stop-opacity:1;stop-color:#484c89" />
|
||||
</radialGradient>
|
||||
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath3468">
|
||||
<path d="M 0,324 576,324 576,0 0,0 0,324 Z" id="path3470" />
|
||||
</clipPath>
|
||||
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath3480">
|
||||
<path d="M 0,324 576,324 576,0 0,0 0,324 Z" id="path3482" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="g3438" transform="matrix(1.25,0,0,-1.25,-4.4,394.29875)">
|
||||
<g id="g3440">
|
||||
<g clip-path="url(#clipPath3444)" id="g3442">
|
||||
<g id="g3448">
|
||||
<g id="g3450">
|
||||
<path
|
||||
d="M 11.52,162 C 11.52,81.677 135.307,16.561 288,16.561 l 0,0 c 152.693,0 276.481,65.116 276.481,145.439 l 0,0 c 0,80.322 -123.788,145.439 -276.481,145.439 l 0,0 C 135.307,307.439 11.52,242.322 11.52,162"
|
||||
id="path3462"
|
||||
style="fill:url(#radialGradient3452);stroke:none"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g3464">
|
||||
<g clip-path="url(#clipPath3468)" id="g3466">
|
||||
<g id="g3472" transform="translate(288,27.3594)">
|
||||
<path
|
||||
d="M 0,0 C 146.729,0 265.68,60.281 265.68,134.641 265.68,209 146.729,269.282 0,269.282 -146.729,269.282 -265.68,209 -265.68,134.641 -265.68,60.281 -146.729,0 0,0"
|
||||
id="path3474"
|
||||
style="fill:#777bb3;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g3476">
|
||||
<g clip-path="url(#clipPath3480)" id="g3478">
|
||||
<g id="g3484" transform="translate(161.7344,145.3066)">
|
||||
<path
|
||||
d="m 0,0 c 12.065,0 21.072,2.225 26.771,6.611 5.638,4.341 9.532,11.862 11.573,22.353 1.903,9.806 1.178,16.653 -2.154,20.348 C 32.783,53.086 25.417,55 14.297,55 L -4.984,55 -15.673,0 0,0 Z m -63.063,-67.75 c -0.895,0 -1.745,0.4 -2.314,1.092 -0.57,0.691 -0.801,1.601 -0.63,2.48 L -37.679,81.573 C -37.405,82.982 -36.17,84 -34.734,84 L 26.32,84 C 45.508,84 59.79,78.79 68.767,68.513 77.792,58.182 80.579,43.741 77.05,25.592 75.614,18.198 73.144,11.331 69.709,5.183 66.27,-0.972 61.725,-6.667 56.198,-11.747 49.582,-17.939 42.094,-22.429 33.962,-25.071 25.959,-27.678 15.681,-29 3.414,-29 l -24.722,0 -7.06,-36.322 c -0.274,-1.41 -1.508,-2.428 -2.944,-2.428 l -31.751,0 z"
|
||||
id="path3486"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
/>
|
||||
</g>
|
||||
<g id="g3488" transform="translate(159.2236,197.3071)">
|
||||
<path
|
||||
d="m 0,0 16.808,0 c 13.421,0 18.083,-2.945 19.667,-4.7 2.628,-2.914 3.124,-9.058 1.435,-17.767 C 36.012,-32.217 32.494,-39.13 27.452,-43.012 22.29,-46.986 13.898,-49 2.511,-49 L -9.523,-49 0,0 Z m 28.831,35 -61.055,0 c -2.872,0 -5.341,-2.036 -5.889,-4.855 l -28.328,-145.751 c -0.342,-1.759 0.12,-3.578 1.259,-4.961 1.14,-1.383 2.838,-2.183 4.63,-2.183 l 31.75,0 c 2.873,0 5.342,2.036 5.89,4.855 l 6.588,33.895 22.249,0 c 12.582,0 23.174,1.372 31.479,4.077 8.541,2.775 16.399,7.48 23.354,13.984 5.752,5.292 10.49,11.232 14.08,17.657 3.591,6.427 6.171,13.594 7.668,21.302 3.715,19.104 0.697,34.402 -8.969,45.466 C 63.965,29.444 48.923,35 28.831,35 m -45.633,-90 19.313,0 c 12.801,0 22.336,2.411 28.601,7.234 6.266,4.824 10.492,12.875 12.688,24.157 2.101,10.832 1.144,18.476 -2.871,22.929 C 36.909,3.773 28.87,6 16.808,6 L -4.946,6 -16.802,-55 M 28.831,29 C 47.198,29 60.597,24.18 69.019,14.539 77.44,4.898 79.976,-8.559 76.616,-25.836 75.233,-32.953 72.894,-39.46 69.601,-45.355 66.304,-51.254 61.999,-56.648 56.679,-61.539 50.339,-67.472 43.296,-71.7 35.546,-74.218 27.796,-76.743 17.925,-78 5.925,-78 l -27.196,0 -7.531,-38.75 -31.75,0 28.328,145.75 61.055,0"
|
||||
id="path3490"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
/>
|
||||
</g>
|
||||
<g id="g3492" transform="translate(311.583,116.3066)">
|
||||
<path
|
||||
d="m 0,0 c -0.896,0 -1.745,0.4 -2.314,1.092 -0.571,0.691 -0.802,1.6 -0.631,2.48 L 9.586,68.061 C 10.778,74.194 10.484,78.596 8.759,80.456 7.703,81.593 4.531,83.5 -4.848,83.5 L -27.55,83.5 -43.305,2.428 C -43.579,1.018 -44.814,0 -46.25,0 l -31.5,0 c -0.896,0 -1.745,0.4 -2.315,1.092 -0.57,0.691 -0.801,1.601 -0.63,2.48 l 28.328,145.751 c 0.274,1.409 1.509,2.427 2.945,2.427 l 31.5,0 c 0.896,0 1.745,-0.4 2.315,-1.091 0.57,-0.692 0.801,-1.601 0.63,-2.481 L -21.813,113 2.609,113 c 18.605,0 31.221,-3.28 38.569,-10.028 7.49,-6.884 9.827,-17.891 6.947,-32.719 L 34.945,2.428 C 34.671,1.018 33.437,0 32,0 L 0,0 Z"
|
||||
id="path3494"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
/>
|
||||
</g>
|
||||
<g id="g3496" transform="translate(293.6611,271.0571)">
|
||||
<path
|
||||
d="m 0,0 -31.5,0 c -2.873,0 -5.342,-2.036 -5.89,-4.855 l -28.328,-145.751 c -0.342,-1.759 0.12,-3.578 1.26,-4.961 1.14,-1.383 2.838,-2.183 4.63,-2.183 l 31.5,0 c 2.872,0 5.342,2.036 5.89,4.855 l 15.283,78.645 20.229,0 c 9.363,0 11.328,-2 11.407,-2.086 0.568,-0.611 1.315,-3.441 0.082,-9.781 l -12.531,-64.489 c -0.342,-1.759 0.12,-3.578 1.26,-4.961 1.14,-1.383 2.838,-2.183 4.63,-2.183 l 32,0 c 2.872,0 5.342,2.036 5.89,4.855 l 13.179,67.825 c 3.093,15.921 0.447,27.864 -7.861,35.5 -7.928,7.281 -21.208,10.82 -40.599,10.82 l -20.784,0 6.143,31.605 C 6.231,-5.386 5.77,-3.566 4.63,-2.184 3.49,-0.801 1.792,0 0,0 m 0,-6 -7.531,-38.75 28.062,0 c 17.657,0 29.836,-3.082 36.539,-9.238 6.703,-6.16 8.711,-16.141 6.032,-29.938 l -13.18,-67.824 -32,0 12.531,64.488 c 1.426,7.336 0.902,12.34 -1.574,15.008 -2.477,2.668 -7.746,4.004 -15.805,4.004 l -25.176,0 -16.226,-83.5 -31.5,0 L -31.5,-6 0,-6"
|
||||
id="path3498"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
/>
|
||||
</g>
|
||||
<g id="g3500" transform="translate(409.5498,145.3066)">
|
||||
<path
|
||||
d="m 0,0 c 12.065,0 21.072,2.225 26.771,6.611 5.638,4.34 9.532,11.861 11.574,22.353 1.903,9.806 1.178,16.653 -2.155,20.348 C 32.783,53.086 25.417,55 14.297,55 L -4.984,55 -15.673,0 0,0 Z m -63.062,-67.75 c -0.895,0 -1.745,0.4 -2.314,1.092 -0.57,0.691 -0.802,1.601 -0.631,2.48 L -37.679,81.573 C -37.404,82.982 -36.17,84 -34.733,84 L 26.32,84 C 45.509,84 59.79,78.79 68.768,68.513 77.793,58.183 80.579,43.742 77.051,25.592 75.613,18.198 73.144,11.331 69.709,5.183 66.27,-0.972 61.725,-6.667 56.198,-11.747 49.582,-17.939 42.094,-22.429 33.962,-25.071 25.959,-27.678 15.681,-29 3.414,-29 l -24.723,0 -7.057,-36.322 c -0.275,-1.41 -1.509,-2.428 -2.946,-2.428 l -31.75,0 z"
|
||||
id="path3502"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
/>
|
||||
</g>
|
||||
<g id="g3504" transform="translate(407.0391,197.3071)">
|
||||
<path
|
||||
d="M 0,0 16.808,0 C 30.229,0 34.891,-2.945 36.475,-4.7 39.104,-7.614 39.6,-13.758 37.91,-22.466 36.012,-32.217 32.493,-39.13 27.452,-43.012 22.29,-46.986 13.898,-49 2.511,-49 L -9.522,-49 0,0 Z m 28.831,35 -61.054,0 c -2.872,0 -5.341,-2.036 -5.889,-4.855 L -66.44,-115.606 c -0.342,-1.759 0.12,-3.578 1.259,-4.961 1.14,-1.383 2.838,-2.183 4.63,-2.183 l 31.75,0 c 2.872,0 5.342,2.036 5.89,4.855 l 6.587,33.895 22.249,0 c 12.582,0 23.174,1.372 31.479,4.077 8.541,2.775 16.401,7.481 23.356,13.986 5.752,5.291 10.488,11.23 14.078,17.655 3.591,6.427 6.171,13.594 7.668,21.302 3.715,19.105 0.697,34.403 -8.969,45.467 C 63.965,29.444 48.924,35 28.831,35 m -45.632,-90 19.312,0 c 12.801,0 22.336,2.411 28.601,7.234 6.267,4.824 10.492,12.875 12.688,24.157 2.102,10.832 1.145,18.476 -2.871,22.929 C 36.909,3.773 28.87,6 16.808,6 L -4.946,6 -16.801,-55 M 28.831,29 C 47.198,29 60.597,24.18 69.019,14.539 77.441,4.898 79.976,-8.559 76.616,-25.836 75.233,-32.953 72.894,-39.46 69.601,-45.355 66.304,-51.254 61.999,-56.648 56.679,-61.539 50.339,-67.472 43.296,-71.7 35.546,-74.218 27.796,-76.743 17.925,-78 5.925,-78 l -27.196,0 -7.53,-38.75 -31.75,0 28.328,145.75 61.054,0"
|
||||
id="path3506"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -58,6 +58,8 @@ export function langToExt(lang: string): string {
|
||||
return 'sh'
|
||||
case 'powershell':
|
||||
return 'ps1'
|
||||
case 'php':
|
||||
return 'php'
|
||||
case 'deno':
|
||||
return 'ts'
|
||||
case 'nativets':
|
||||
|
||||
@@ -17,7 +17,8 @@ import init, {
|
||||
parse_outputs,
|
||||
parse_mssql,
|
||||
parse_ts_imports,
|
||||
parse_db_resource
|
||||
parse_db_resource,
|
||||
parse_php
|
||||
} from 'windmill-parser-wasm'
|
||||
import wasmUrl from 'windmill-parser-wasm/windmill_parser_wasm_bg.wasm?url'
|
||||
import { workspaceStore } from './stores.js'
|
||||
@@ -120,6 +121,8 @@ export async function inferArgs(
|
||||
inferedSchema = JSON.parse(parse_bash(code))
|
||||
} else if (language == 'powershell') {
|
||||
inferedSchema = JSON.parse(parse_powershell(code))
|
||||
} else if (language == 'php') {
|
||||
inferedSchema = JSON.parse(parse_php(code))
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -190,6 +190,26 @@ export const GRAPHQL_INIT_CODE = `query($name4: String, $name2: Int, $name3: [St
|
||||
}
|
||||
`
|
||||
|
||||
export const PHP_INIT_CODE = `<?php
|
||||
|
||||
// remove the first // of the following lines to specify packages to install using composer
|
||||
// // require:
|
||||
// // monolog/monolog@3.6.0
|
||||
// // stripe/stripe-php
|
||||
|
||||
function main(
|
||||
// Postgresql $a,
|
||||
// array $b,
|
||||
// object $c,
|
||||
int $d = 123,
|
||||
string $e = "default value",
|
||||
float $f = 3.5,
|
||||
bool $g = true,
|
||||
) {
|
||||
return $d;
|
||||
}
|
||||
`
|
||||
|
||||
export const FETCH_INIT_CODE = `export async function main(
|
||||
url: string | undefined,
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' = 'GET',
|
||||
@@ -378,7 +398,8 @@ const ALL_INITIAL_CODE = [
|
||||
BUN_INIT_CODE_CLEAR,
|
||||
BUN_INIT_CODE_APPROVAL,
|
||||
BASH_INIT_CODE,
|
||||
POWERSHELL_INIT_CODE
|
||||
POWERSHELL_INIT_CODE,
|
||||
PHP_INIT_CODE
|
||||
]
|
||||
|
||||
export function isInitialCode(content: string): boolean {
|
||||
@@ -454,6 +475,8 @@ export function initialCode(
|
||||
return MSSQL_INIT_CODE
|
||||
} else if (language == 'graphql') {
|
||||
return GRAPHQL_INIT_CODE
|
||||
} else if (language == 'php') {
|
||||
return PHP_INIT_CODE
|
||||
} else if (language == 'bun') {
|
||||
if (kind === 'approval') {
|
||||
return BUN_INIT_CODE_APPROVAL
|
||||
|
||||
@@ -28,6 +28,8 @@ export function scriptLangToEditorLang(lang: Script['language'] | undefined) {
|
||||
return 'shell'
|
||||
} else if (lang == 'powershell') {
|
||||
return 'powershell'
|
||||
} else if (lang == 'php') {
|
||||
return 'php'
|
||||
} else if (lang == 'graphql') {
|
||||
return 'graphql'
|
||||
} else if (lang == undefined) {
|
||||
@@ -103,6 +105,7 @@ const scriptLanguagesArray: [SupportedLanguage | 'docker', string][] = [
|
||||
['mssql', 'MS SQL Server'],
|
||||
['graphql', 'GraphQL'],
|
||||
['powershell', 'PowerShell'],
|
||||
['php', 'PHP'],
|
||||
['docker', 'Docker']
|
||||
]
|
||||
export const defaultScriptLanguages = Object.fromEntries(scriptLanguagesArray)
|
||||
|
||||
@@ -628,7 +628,7 @@
|
||||
<Badge color="blue">priority: {job.priority}</Badge>
|
||||
</div>
|
||||
{/if}
|
||||
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'mssql', 'graphql', 'nativets', 'bash', 'powershell', 'other', 'dependency'].includes(job.tag)}
|
||||
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'mssql', 'graphql', 'nativets', 'bash', 'powershell', 'php', 'other', 'dependency'].includes(job.tag)}
|
||||
<div>
|
||||
<Badge color="indigo">Tag: {job.tag}</Badge>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user