feat: add native powershell support (#2025)

* feat: add powershell support

* fix: lang build
This commit is contained in:
HugoCasa
2023-08-10 13:19:50 +00:00
committed by GitHub
parent b7162c900a
commit 2f18b94b33
33 changed files with 307 additions and 71 deletions
@@ -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 'powershell';
@@ -8,7 +8,7 @@ use std::collections::HashMap;
use windmill_parser::{Arg, MainArgSignature, Typ};
pub fn parse_bash_sig(code: &str) -> anyhow::Result<MainArgSignature> {
let parsed = parse_file(&code)?;
let parsed = parse_bash_file(&code)?;
if let Some(x) = parsed {
let args = x;
Ok(MainArgSignature { star_args: false, star_kwargs: false, args })
@@ -17,13 +17,26 @@ pub fn parse_bash_sig(code: &str) -> anyhow::Result<MainArgSignature> {
}
}
lazy_static::lazy_static! {
static ref RE: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+):-(.*)\})"(?:([\t ]*#.*)?)$"#).unwrap();
pub fn parse_powershell_sig(code: &str) -> anyhow::Result<MainArgSignature> {
let parsed = parse_powershell_file(&code)?;
if let Some(x) = parsed {
let args = x;
Ok(MainArgSignature { star_args: false, star_kwargs: false, args })
} else {
Err(anyhow!("Error parsing powershell script".to_string()))
}
}
fn parse_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
lazy_static::lazy_static! {
static ref RE_BASH: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+):-(.*)\})"(?:[\t ]*)?(?:#.*)?$"#).unwrap();
static ref RE_POWERSHELL_PARAM: Regex = Regex::new(r#"(?m)param[\t ]*\(([^)]*)\)"#).unwrap();
static ref RE_POWERSHELL_ARGS: Regex = Regex::new(r#"(?:\[(\w+)\])?\$(\w+)[\t ]*(?:=[\t ]*(?:(?:(?:"|')([^"\n\r\$]*)(?:"|'))|([\d.]+)))?"#).unwrap();
}
fn parse_bash_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut hm: HashMap<i32, (String, Option<String>)> = HashMap::new();
for cap in RE.captures_iter(code) {
for cap in RE_BASH.captures_iter(code) {
hm.insert(
cap.get(2)
.or(cap.get(3))
@@ -53,6 +66,39 @@ fn parse_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
Ok(Some(args))
}
fn parse_powershell_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let param_wrapper = RE_POWERSHELL_PARAM.captures(code);
let mut args = vec![];
if let Some(param_wrapper) = param_wrapper {
let param_wrapper = param_wrapper.get(1).unwrap().as_str();
for cap in RE_POWERSHELL_ARGS.captures_iter(param_wrapper) {
let typ = cap
.get(1)
.map(|x| x.as_str().to_string())
.unwrap_or("string".to_string());
let name = cap.get(2).unwrap().as_str().to_string();
let default = cap
.get(3)
.or(cap.get(4))
.map(|x| json!(x.as_str().to_string()));
args.push(Arg {
name: name,
typ: match typ.as_str() {
"string" => Typ::Str(None),
"int" | "long" => Typ::Int,
"decimal" | "double" | "single" => Typ::Float,
_ => Typ::Str(None),
},
default: default.clone(),
otyp: None,
has_default: default.is_some(),
});
}
}
Ok(Some(args))
}
#[cfg(test)]
mod tests {
@@ -18,7 +18,6 @@ pub fn parse_graphql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
lazy_static::lazy_static! {
static ref RE_ARG_GRAPHQL: Regex = Regex::new(r#"\$(\w+)\s*:\s*(?:(\w+)!?|\[(\w+)!?\])!?\s*(?:=\s*(\w+)\s*)?"#).unwrap();
static ref RE_ARG_GRAPHQL_ARRAY: Regex = Regex::new(r#"^\[(\w+)!?\]!?$"#).unwrap();
}
fn parse_graphql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
@@ -3,7 +3,7 @@
"collaborators": [
"Ruben Fiszel <ruben@windmill.dev>"
],
"version": "1.141.0",
"version": "1.143.0",
"files": [
"windmill_parser_wasm_bg.wasm",
"windmill_parser_wasm.js",
@@ -14,6 +14,11 @@ export function parse_bash(code: string): string;
* @param {string} code
* @returns {string}
*/
export function parse_powershell(code: string): string;
/**
* @param {string} code
* @returns {string}
*/
export function parse_go(code: string): string;
/**
* @param {string} code
@@ -52,6 +57,7 @@ export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly parse_deno: (a: number, b: number, c: number) => void;
readonly parse_bash: (a: number, b: number, c: number) => void;
readonly parse_powershell: (a: number, b: number, c: number) => void;
readonly parse_go: (a: number, b: number, c: number) => void;
readonly parse_python: (a: number, b: number, c: number) => void;
readonly parse_sql: (a: number, b: number, c: number) => void;
@@ -243,6 +243,29 @@ export function parse_bash(code) {
}
}
/**
* @param {string} code
* @returns {string}
*/
export function parse_powershell(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_powershell(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);
}
}
/**
* @param {string} code
* @returns {string}
@@ -446,7 +469,7 @@ async function __wbg_load(module, imports) {
function __wbg_get_imports() {
const imports = {};
imports.wbg = {};
imports.wbg.__wbg_eval_9331e2bd3095f3d6 = function(arg0, arg1) {
imports.wbg.__wbg_eval_44bde4e76596166e = function(arg0, arg1) {
const ret = eval(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
@@ -3,6 +3,7 @@
export const memory: WebAssembly.Memory;
export function parse_deno(a: number, b: number, c: number): void;
export function parse_bash(a: number, b: number, c: number): void;
export function parse_powershell(a: number, b: number, c: number): void;
export function parse_go(a: number, b: number, c: number): void;
export function parse_python(a: number, b: number, c: number): void;
export function parse_sql(a: number, b: number, c: number): void;
@@ -20,6 +20,11 @@ pub fn parse_bash(code: &str) -> String {
wrap_sig(windmill_parser_bash::parse_bash_sig(code))
}
#[wasm_bindgen]
pub fn parse_powershell(code: &str) -> String {
wrap_sig(windmill_parser_bash::parse_powershell_sig(code))
}
#[wasm_bindgen]
pub fn parse_go(code: &str) -> String {
wrap_sig(windmill_parser_go::parse_go_sig(code))
+5
View File
@@ -5691,6 +5691,7 @@ components:
deno,
go,
bash,
powershell,
postgresql,
mysql,
bigquery,
@@ -5768,6 +5769,7 @@ components:
deno,
go,
bash,
powershell,
postgresql,
mysql,
bigquery,
@@ -5950,6 +5952,7 @@ components:
deno,
go,
bash,
powershell,
postgresql,
mysql,
bigquery,
@@ -6052,6 +6055,7 @@ components:
deno,
go,
bash,
powershell,
postgresql,
mysql,
bigquery,
@@ -6563,6 +6567,7 @@ components:
deno,
go,
bash,
powershell,
postgresql,
mysql,
bigquery,
+1
View File
@@ -1474,6 +1474,7 @@ async fn tarball_workspace(
ScriptLang::Deno => "ts",
ScriptLang::Go => "go",
ScriptLang::Bash => "sh",
ScriptLang::Powershell => "ps1",
ScriptLang::Postgresql => "pg.sql",
ScriptLang::Mysql => "my.sql",
ScriptLang::Bigquery => "bq.sql",
+2
View File
@@ -30,6 +30,7 @@ pub enum ScriptLang {
Python3,
Go,
Bash,
Powershell,
Postgresql,
Bun,
Mysql,
@@ -47,6 +48,7 @@ impl ScriptLang {
ScriptLang::Python3 => "python3",
ScriptLang::Go => "go",
ScriptLang::Bash => "bash",
ScriptLang::Powershell => "powershell",
ScriptLang::Postgresql => "postgresql",
ScriptLang::Mysql => "mysql",
ScriptLang::Bigquery => "bigquery",
+1
View File
@@ -69,6 +69,7 @@ lazy_static::lazy_static! {
"python3".to_string(),
"go".to_string(),
"bash".to_string(),
"powershell".to_string(),
"nativets".to_string(),
"mysql".to_string(),
"graphql".to_string(),
+103
View File
@@ -356,6 +356,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
Some(ScriptLang::Deno),
Some(ScriptLang::Go),
Some(ScriptLang::Bash),
Some(ScriptLang::Powershell),
Some(ScriptLang::Nativets),
Some(ScriptLang::Postgresql),
Some(ScriptLang::Mysql),
@@ -1509,6 +1510,21 @@ mount {{
)
.await
},
Some(ScriptLang::Powershell) => {
handle_powershell_job(
logs,
job,
db,
client,
&inner_content,
job_dir,
&shared_mount,
base_internal_url,
worker_name,
envs
)
.await
}
_ => panic!("unreachable, language is not supported: {language:#?}"),
};
tracing::info!(
@@ -1611,6 +1627,92 @@ async fn handle_bash_job(
.unwrap_or_else(String::new)))
}
#[tracing::instrument(level = "trace", skip_all)]
async fn handle_powershell_job(
logs: &mut String,
job: &QueuedJob,
db: &sqlx::Pool<sqlx::Postgres>,
client: &AuthedClientBackgroundTask,
content: &str,
job_dir: &str,
shared_mount: &str,
base_internal_url: &str,
worker_name: &str,
envs: HashMap<String, String>,
) -> Result<serde_json::Value, Error> {
logs.push_str("\n\n--- POWERSHELL CODE EXECUTION ---\n");
set_logs(logs, &job.id, db).await;
let hm: serde_json::Map<String, Value> = match job.args {
Some(Value::Object(ref hm)) => hm.clone(),
_ => serde_json::Map::new(),
};
let args_owned = windmill_parser_bash::parse_powershell_sig(&content)?
.args
.iter()
.map(|arg| {
(arg.name.clone(), hm.get(&arg.name)
.and_then(|v| match v {
Value::String(s) => Some(s.clone()),
_ => serde_json::to_string(v).ok(),
})
.unwrap_or_else(String::new))
})
.collect::<Vec<(String, String)>>();
let pwsh_args = args_owned.iter().map(|(n, v)| format!("--{n} {v}")).join(" ");
let content = content.replace('$', r"\$"); // escape powershell variables
write_file(job_dir, "main.sh", &format!("set -e\ncat > script.ps1 << EOF\n{content}\nEOF\npwsh -File script.ps1 {pwsh_args}\necho \"\"\nsleep 0.02")).await?;
let token = client.get_token().await;
let mut reserved_variables = get_reserved_variables(job, &token, db).await?;
reserved_variables.insert("RUST_LOG".to_string(), "info".to_string());
let child = if !*DISABLE_NSJAIL {
let _ = write_file(
job_dir,
"run.config.proto",
&NSJAIL_CONFIG_RUN_BASH_CONTENT
.replace("{JOB_DIR}", job_dir)
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{SHARED_MOUNT}", shared_mount),
)
.await?;
let cmd_args = vec!["--config", "run.config.proto", "--", "/bin/bash", "main.sh"];
Command::new(NSJAIL_PATH.as_str())
.current_dir(job_dir)
.env_clear()
.envs(reserved_variables)
.env("PATH", PATH_ENV.as_str())
.env("BASE_INTERNAL_URL", base_internal_url)
.args(cmd_args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?
} else {
let cmd_args = vec!["main.sh"];
Command::new("/bin/bash")
.current_dir(job_dir)
.env_clear()
.envs(envs)
.envs(reserved_variables)
.env("PATH", PATH_ENV.as_str())
.env("BASE_INTERNAL_URL", base_internal_url)
.env("HOME", HOME_ENV.as_str())
.args(cmd_args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?
};
handle_child(&job.id, db, logs, child, !*DISABLE_NSJAIL, worker_name, &job.workspace_id, "bash run", job.timeout).await?;
//for now bash jobs have an empty result object
Ok(serde_json::json!(logs
.lines()
.last()
.map(|x| x.to_string())
.unwrap_or_else(String::new)))
}
fn get_common_deno_proc_envs(token: &str, base_internal_url: &str) -> HashMap<String, String> {
let hostname_base = BASE_URL.split("://").last().unwrap_or("localhost");
let hostname_internal = base_internal_url.split("://").last().unwrap_or("localhost");
@@ -2250,6 +2352,7 @@ async fn capture_dependency_job(
ScriptLang::Snowflake => Ok("".to_owned()),
ScriptLang::Graphql => Ok("".to_owned()),
ScriptLang::Bash => Ok("".to_owned()),
ScriptLang::Powershell => Ok("".to_owned()),
ScriptLang::Nativets => Ok("".to_owned()),
}
+5 -1
View File
@@ -31,7 +31,9 @@ async function dev(opts: GlobalOptions & { filter?: string }) {
path.endsWith(".ts") ||
path.endsWith(".py") ||
path.endsWith(".sh") ||
path.endsWith(".sql")
path.endsWith(".sql") ||
path.endsWith(".gql") ||
path.endsWith(".ps1")
);
if (paths.length == 0) {
return;
@@ -56,6 +58,8 @@ async function dev(opts: GlobalOptions & { filter?: string }) {
? "go"
: ext == "sh"
? "bash"
: ext == "ps1"
? "powershell"
: ext == "sql"
? splitted.length > 2 && splitted[splitted.length - 2] == "my"
? "mysql"
+58 -17
View File
@@ -72,7 +72,10 @@ export async function handleFile(
(path.endsWith(".ts") ||
path.endsWith(".py") ||
path.endsWith(".go") ||
path.endsWith(".sh"))
path.endsWith(".sh") ||
path.endsWith(".sql") ||
path.endsWith(".gql") ||
path.endsWith(".ps1"))
) {
if (alreadySynced.includes(path)) {
return true;
@@ -181,16 +184,27 @@ export async function findContentFile(filePath: string) {
filePath.replace(".script.json", ".py"),
filePath.replace(".script.json", ".go"),
filePath.replace(".script.json", ".sh"),
filePath.replace(".script.json", ".sql"),
filePath.replace(".script.json", "pg.sql"),
filePath.replace(".script.json", "my.sql"),
filePath.replace(".script.json", "bq.sql"),
filePath.replace(".script.json", "sf.sql"),
filePath.replace(".script.json", ".fetch.ts"),
filePath.replace(".script.json", ".bun.ts"),
filePath.replace(".script.json", ".gql"),
filePath.replace(".script.json", ".ps1"),
]
: [
filePath.replace(".script.yaml", ".ts"),
filePath.replace(".script.yaml", ".py"),
filePath.replace(".script.yaml", ".go"),
filePath.replace(".script.yaml", ".sh"),
filePath.replace(".script.yaml", ".sql"),
filePath.replace(".script.yaml", "pg.sql"),
filePath.replace(".script.yaml", "bq.sql"),
filePath.replace(".script.yaml", "sf.sql"),
filePath.replace(".script.yaml", ".fetch.ts"),
filePath.replace(".script.yaml", ".bun.ts"),
filePath.replace(".script.yaml", ".gql"),
filePath.replace(".script.yaml", ".ps1"),
];
const validCandidates = (
await Promise.all(
@@ -220,21 +234,48 @@ export async function findContentFile(filePath: string) {
export function inferContentTypeFromFilePath(
contentPath: string
): "python3" | "deno" | "go" | "bash" {
let language = contentPath.substring(contentPath.lastIndexOf("."));
if (language == ".ts") language = "deno";
if (language == ".py") language = "python3";
if (language == ".sh") language = "bash";
if (language == ".go") language = "go";
if (
language != "python3" &&
language != "deno" &&
language != "go" &&
language != "bash"
) {
throw new Error("Invalid language: " + language);
):
| "python3"
| "deno"
| "bun"
| "nativets"
| "go"
| "bash"
| "powershell"
| "postgresql"
| "mysql"
| "bigquery"
| "snowflake"
| "graphql" {
if (contentPath.endsWith(".py")) {
return "python3";
} else if (contentPath.endsWith("fetch.ts")) {
return "nativets";
} else if (contentPath.endsWith("bun.ts")) {
return "bun";
} else if (contentPath.endsWith(".ts")) {
return "deno";
} else if (contentPath.endsWith(".go")) {
return "go";
} else if (contentPath.endsWith(".my.sql")) {
return "mysql";
} else if (contentPath.endsWith(".bq.sql")) {
return "bigquery";
} else if (contentPath.endsWith(".sf.sql")) {
return "snowflake";
} else if (contentPath.endsWith(".pg.sql")) {
return "postgresql";
} else if (contentPath.endsWith(".gql")) {
return "graphql";
} else if (contentPath.endsWith(".sh")) {
return "bash";
} else if (contentPath.endsWith(".ps1")) {
return "powershell";
} else {
throw new Error(
"Invalid language: " + contentPath.substring(contentPath.lastIndexOf("."))
);
}
return language;
}
async function list(opts: GlobalOptions & { showArchived?: boolean }) {
+1
View File
@@ -133,6 +133,7 @@ function ZipFSElement(zip: JSZip, useYaml: boolean): DynFSElement {
else if (language == "deno") ext = "ts";
else if (language == "go") ext = "go";
else if (language == "bash") ext = "sh";
else if (language == "powershell") ext = "ps1";
else if (language == "postgresql") ext = "pg.sql";
else if (language == "mysql") ext = "my.sql";
else if (language == "bigquery") ext = "bq.sql";
+1 -1
View File
@@ -60,7 +60,7 @@ services:
- KEEP_JOB_DIR=false
- METRICS_ADDR=false
# To handle all tags, remove the env variable altogether. If you do so, you can remove the windmill_worker_native containers.
- WORKER_TAGS=deno,python3,go,bash,dependency,flow,hub,other,bun
- WORKER_TAGS=deno,python3,go,bash,powershell,dependency,flow,hub,other,bun
# LICENSE_KEY is only needed for the enterprise edition
- LICENSE_KEY=${WM_LICENSE_KEY}
depends_on:
+7 -7
View File
@@ -43,7 +43,7 @@
"svelte-timezone-picker": "^2.0.3",
"tailwind-merge": "^1.13.2",
"vscode-ws-jsonrpc": "3.0.0",
"windmill-parser-wasm": "^1.141.0",
"windmill-parser-wasm": "^1.143.0",
"y-monaco": "^0.1.4",
"y-websocket": "^1.5.0",
"yjs": "^13.6.7"
@@ -9811,9 +9811,9 @@
}
},
"node_modules/windmill-parser-wasm": {
"version": "1.141.0",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.141.0.tgz",
"integrity": "sha512-2U4wAyKOMa7YTcWGUSCCqhNEfI+dVF9JvlXzPneOUO9nD7zm5uXRB+xaJLHKtYB5gmzLz1cQElp/FpbJaLLZsw=="
"version": "1.143.0",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.143.0.tgz",
"integrity": "sha512-sxkolDrUX/ywrE3tZpA3f9B4BONpW7PaHq+5qJBreCBHVkGnAx8iyXgyEvZhIpcTmuNhCy/osumoK/CjxGWWDw=="
},
"node_modules/wordwrap": {
"version": "1.0.0",
@@ -16782,9 +16782,9 @@
}
},
"windmill-parser-wasm": {
"version": "1.141.0",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.141.0.tgz",
"integrity": "sha512-2U4wAyKOMa7YTcWGUSCCqhNEfI+dVF9JvlXzPneOUO9nD7zm5uXRB+xaJLHKtYB5gmzLz1cQElp/FpbJaLLZsw=="
"version": "1.143.0",
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.143.0.tgz",
"integrity": "sha512-sxkolDrUX/ywrE3tZpA3f9B4BONpW7PaHq+5qJBreCBHVkGnAx8iyXgyEvZhIpcTmuNhCy/osumoK/CjxGWWDw=="
},
"wordwrap": {
"version": "1.0.0",
+1 -1
View File
@@ -107,7 +107,7 @@
"svelte-timezone-picker": "^2.0.3",
"tailwind-merge": "^1.13.2",
"vscode-ws-jsonrpc": "3.0.0",
"windmill-parser-wasm": "^1.141.0",
"windmill-parser-wasm": "^1.143.0",
"y-monaco": "^0.1.4",
"y-websocket": "^1.5.0",
"yjs": "^13.6.7"
@@ -40,7 +40,7 @@
export function setDiff(
original: string,
modified: string,
lang: 'typescript' | 'python' | 'go' | 'shell' | 'sql' | 'graphql' | 'javascript'
lang: 'typescript' | 'python' | 'go' | 'shell' | 'sql' | 'graphql' | 'javascript' | 'powershell'
): void {
diffEditor?.setModel({
original: meditor.createModel(original, lang),
+2 -1
View File
@@ -22,6 +22,7 @@
import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'
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/language/typescript/monaco.contribution'
import { MonacoLanguageClient, initServices } from 'monaco-languageclient'
import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'
@@ -48,7 +49,7 @@
let divEl: HTMLDivElement | null = null
let editor: meditor.IStandaloneCodeEditor
export let lang: 'typescript' | 'python' | 'go' | 'shell' | 'sql' | 'graphql'
export let lang: 'typescript' | 'python' | 'go' | 'shell' | 'sql' | 'graphql' | 'powershell'
export let deno: boolean
export let code: string = ''
export let cmdEnterAction: (() => void) | undefined = undefined
@@ -71,6 +71,7 @@
langs.push(['BigQuery', Script.language.BIGQUERY])
langs.push(['Snowflake', Script.language.SNOWFLAKE])
langs.push(['GraphQL', Script.language.GRAPHQL])
langs.push(['PowerShell', Script.language.POWERSHELL])
if (SCRIPT_SHOW_GO) {
langs.push(['Go', Script.language.GO])
}
@@ -365,22 +366,6 @@
>
<LanguageIcon lang="docker" /><span class="ml-2 py-2">Docker</span>
</Button>
<Button
size="sm"
variant="border"
color={template == 'powershell' ? 'blue' : 'light'}
btnClasses={template == 'powershell'
? '!border-2 !bg-blue-50/75 dark:!bg-frost-900/75'
: 'm-[1px]'}
disabled={lockedLanguage}
on:click={() => {
template = 'powershell'
initContent(Script.language.BASH, script.kind, template)
script.language = Script.language.BASH
}}
>
<LanguageIcon lang="powershell" /><span class="ml-2 py-2">Powershell</span>
</Button>
<Button
size="xs"
variant="border"
@@ -98,6 +98,7 @@
['python3', 'Python'],
['go', 'Go'],
['bash', 'Bash'],
['powershell', 'PowerShell'],
['nativets', 'REST'],
['postgresql', 'PostgreSQL'],
['mysql', 'MySQL'],
@@ -32,6 +32,7 @@
[Script.language.DENO]: 'TypeScript',
[Script.language.GO]: 'Go',
[Script.language.BASH]: 'Bash',
[Script.language.POWERSHELL]: 'PowerShell',
[Script.language.NATIVETS]: 'HTTP',
[Script.language.GRAPHQL]: 'GraphQL',
[Script.language.POSTGRESQL]: 'Postgresql',
@@ -224,10 +224,14 @@
/>
<FlowScriptPicker
label={`Powershell`}
lang="powershell"
label="PowerShell"
lang={Script.language.POWERSHELL}
on:click={() => {
dispatch('new', { language: RawScript.language.BASH, kind, subkind: 'powershell' })
dispatch('new', {
language: RawScript.language.POWERSHELL,
kind,
subkind: 'flow'
})
}}
/>
+2
View File
@@ -59,6 +59,8 @@ export function langToExt(lang: string): string {
return 'go'
case 'bash':
return 'sh'
case 'powershell':
return 'ps1'
case 'deno':
return 'ts'
case 'nativets':
+4 -1
View File
@@ -12,7 +12,8 @@ import init, {
parse_mysql,
parse_bigquery,
parse_snowflake,
parse_graphql
parse_graphql,
parse_powershell
} from 'windmill-parser-wasm'
import wasmUrl from 'windmill-parser-wasm/windmill_parser_wasm_bg.wasm?url'
import { workspaceStore } from './stores.js'
@@ -71,6 +72,8 @@ export async function inferArgs(
inferedSchema = JSON.parse(parse_go(code))
} else if (language == 'bash') {
inferedSchema = JSON.parse(parse_bash(code))
} else if (language == 'powershell') {
inferedSchema = JSON.parse(parse_powershell(code))
} else {
return
}
+8 -13
View File
@@ -260,17 +260,10 @@ docker pull $IMAGE
docker run --rm $IMAGE $COMMAND
`
export const POWERSHELL_INIT_CODE = `# shellcheck shell=bash
name="\${1:-Bill}"
export const POWERSHELL_INIT_CODE = `param($Msg, $Dflt = "default value", [int]$Nb = 3)
cat > script.ps1 << EOF
Write-Host -Object 'Hello'
Write-Host -Object 'From'
Write-Host -Object 'PowerShell, '
Write-Host -Object '$name!'
EOF
pwsh -File script.ps1`
# the last line of the stdout is the return value
Write-Output "Hello $Msg"`
const ALL_INITIAL_CODE = [
PYTHON_INIT_CODE,
@@ -285,7 +278,9 @@ const ALL_INITIAL_CODE = [
DENO_INIT_CODE_CLEAR,
PYTHON_INIT_CODE_CLEAR,
DENO_INIT_CODE_APPROVAL,
DENO_FAILURE_MODULE_CODE
DENO_FAILURE_MODULE_CODE,
BASH_INIT_CODE,
POWERSHELL_INIT_CODE
]
export function isInitialCode(content: string): boolean {
@@ -340,11 +335,11 @@ export function initialCode(
} else if (language == 'bash') {
if (subkind === 'docker') {
return DOCKER_INIT_CODE
} else if (subkind === 'powershell') {
return POWERSHELL_INIT_CODE
} else {
return BASH_INIT_CODE
}
} else if (language == 'powershell') {
return POWERSHELL_INIT_CODE
} else if (language == 'nativets') {
return NATIVETS_INIT_CODE
} else if (language == 'postgresql') {
+2
View File
@@ -24,6 +24,8 @@ export function scriptLangToEditorLang(lang: Script.language) {
return 'python'
} else if (lang == 'bash') {
return 'shell'
} else if (lang == 'powershell') {
return 'powershell'
} else if (lang == 'graphql') {
return 'graphql'
} else {
@@ -291,7 +291,7 @@
<Badge color="blue">{job.job_kind}</Badge>
</div>
{/if}
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'graphql', 'nativets', 'bash', 'other', 'dependency'].includes(job.tag)}
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'graphql', 'nativets', 'bash', 'powershell', 'other', 'dependency'].includes(job.tag)}
<div>
<Badge color="indigo">Worker group: {job.tag}</Badge>
</div>
+1
View File
@@ -192,6 +192,7 @@ components:
- python3
- go
- bash
- powershell
- postgresql
- mysql
- bigquery