Skip to content

Commit

Permalink
feat: support bash as 4th language (#865)
Browse files Browse the repository at this point in the history
* bash support

* bash backend working

* frontend part

* bash backend working
  • Loading branch information
rubenfiszel committed Nov 6, 2022
1 parent 3333713 commit 3c09275
Show file tree
Hide file tree
Showing 35 changed files with 475 additions and 50 deletions.
15 changes: 15 additions & 0 deletions backend/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/Cargo.toml
Expand Up @@ -59,6 +59,7 @@ windmill-parser = { path = "./parsers/windmill-parser" }
windmill-parser-ts = { path = "./parsers/windmill-parser-ts" }
windmill-parser-py = { path = "./parsers/windmill-parser-py" }
windmill-parser-go = { path = "./parsers/windmill-parser-go" }
windmill-parser-bash = { path = "./parsers/windmill-parser-bash" }
axum = { version = "^0", features = ["headers"] }
headers = "^0"
hyper = { version = "^0", features = ["full"] }
Expand Down
1 change: 1 addition & 0 deletions backend/migrations/20221106081530_bash.down.sql
@@ -0,0 +1 @@
-- Add down migration script here
2 changes: 2 additions & 0 deletions backend/migrations/20221106081530_bash.up.sql
@@ -0,0 +1,2 @@
-- Add up migration script here
ALTER TYPE SCRIPT_LANG ADD VALUE 'bash';
18 changes: 18 additions & 0 deletions backend/parsers/windmill-parser-bash/Cargo.toml
@@ -0,0 +1,18 @@
[package]
name = "windmill-parser-bash"
version.workspace = true
edition.workspace = true
authors.workspace = true

[lib]
name = "windmill_parser_bash"
path = "./src/lib.rs"

[dependencies]
windmill-parser.workspace = true
windmill-common.workspace = true
phf.workspace = true
unicode-general-category.workspace = true
itertools.workspace = true
anyhow.workspace = true
regex.workspace = true
84 changes: 84 additions & 0 deletions backend/parsers/windmill-parser-bash/src/lib.rs
@@ -0,0 +1,84 @@
#![allow(non_snake_case)] // TODO: switch to parse_* function naming

use regex::Regex;

use std::collections::HashMap;
use windmill_parser::{Arg, MainArgSignature, Typ};

pub fn parse_bash_sig(code: &str) -> windmill_common::error::Result<MainArgSignature> {
let parsed = parse_file(&code)?;
if let Some(x) = parsed {
let args = x;
Ok(MainArgSignature { star_args: false, star_kwargs: false, args })
} else {
Err(windmill_common::error::Error::BadRequest(
"Error parsing bash script".to_string(),
))
}
}

fn parse_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut hm = HashMap::new();
let re = Regex::new(r#"(?m)^(\w+)="\$(\d+)"$"#).unwrap();
for cap in re.captures_iter(code) {
hm.insert(cap[2].parse::<i32>()?, cap[1].to_string());
}
let mut args = vec![];
for i in 1..20 {
if hm.contains_key(&i) {
args.push(Arg {
name: hm[&i].clone(),
typ: Typ::Str(None),
default: None,
otyp: None,
has_default: false,
});
} else {
break;
}
}
Ok(Some(args))
}

#[cfg(test)]
mod tests {

use super::*;

#[test]
fn test_parse_bash_sig() -> anyhow::Result<()> {
let code = r#"
token="$1"
image="$2"
digest="${3:-latest}"
foo="$4"
"#;
//println!("{}", serde_json::to_string()?);
assert_eq!(
parse_bash_sig(code)?,
MainArgSignature {
star_args: false,
star_kwargs: false,
args: vec![
Arg {
otyp: None,
name: "token".to_string(),
typ: Typ::Str(None),
default: None,
has_default: false
},
Arg {
otyp: None,
name: "image".to_string(),
typ: Typ::Str(None),
default: None,
has_default: false
}
]
}
);

Ok(())
}
}
24 changes: 24 additions & 0 deletions backend/tests/worker.rs
Expand Up @@ -1515,6 +1515,30 @@ func main(derp string) (string, error) {
assert_eq!(result, serde_json::json!("hello world"));
}

#[sqlx::test(fixtures("base"))]
async fn test_bash_job(db: Pool<Postgres>) {
initialize_tracing().await;
let server = ApiServer::start(db.clone()).await;
let port = server.addr.port();

let content = r#"
msg="$1"
echo "hello $msg"
"#
.to_owned();

let job = RunJob::from(JobPayload::Code(RawCode {
content,
path: None,
language: ScriptLang::Bash,
}))
.arg("msg", json!("world"))
.run_until_complete(&db, port)
.await;

assert_eq!(job.result, Some(json!("hello world")));
}

#[sqlx::test(fixtures("base"))]
async fn test_python_job(db: Pool<Postgres>) {
initialize_tracing().await;
Expand Down
1 change: 1 addition & 0 deletions backend/windmill-api/Cargo.toml
Expand Up @@ -28,6 +28,7 @@ windmill-parser.workspace = true
windmill-parser-ts.workspace = true
windmill-parser-go.workspace = true
windmill-parser-py.workspace = true
windmill-parser-bash.workspace = true
tokio.workspace = true
anyhow.workspace = true
argon2.workspace = true
Expand Down
33 changes: 27 additions & 6 deletions backend/windmill-api/openapi.yaml
Expand Up @@ -1671,7 +1671,7 @@ paths:
schema: {}
language:
type: string
enum: [deno, python3, go]
enum: [deno, python3, go, bash]
summary:
type: string
required:
Expand Down Expand Up @@ -1794,7 +1794,7 @@ paths:
type: string
language:
type: string
enum: [python3, deno, go]
enum: [python3, deno, go, bash]
kind:
type: string
enum: [script, failure, trigger, command, approval]
Expand Down Expand Up @@ -1854,6 +1854,27 @@ paths:
schema:
$ref: "#/components/schemas/MainArgSignature"

/scripts/bash/tojsonschema:
post:
summary: inspect bash code to infer jsonschema of arguments
operationId: bashToJsonschema
tags:
- script
requestBody:
description: bash code with the main function
required: true
content:
application/json:
schema:
type: string
responses:
"200":
description: parsed args
content:
application/json:
schema:
$ref: "#/components/schemas/MainArgSignature"

/scripts/go/tojsonschema:
post:
summary: inspect go code to infer jsonschema of arguments
Expand Down Expand Up @@ -3585,7 +3606,7 @@ components:
type: string
language:
type: string
enum: [python3, deno, go]
enum: [python3, deno, go, bash]
kind:
type: string
enum: [script, failure, trigger, command, approval]
Expand Down Expand Up @@ -3677,7 +3698,7 @@ components:
type: boolean
language:
type: string
enum: [python3, deno, go]
enum: [python3, deno, go, bash]
required:
- id
- running
Expand Down Expand Up @@ -3755,7 +3776,7 @@ components:
type: boolean
language:
type: string
enum: [python3, deno, go]
enum: [python3, deno, go, bash]
is_skipped:
type: boolean
required:
Expand Down Expand Up @@ -4126,7 +4147,7 @@ components:
$ref: "#/components/schemas/ScriptArgs"
language:
type: string
enum: [python3, deno, go]
enum: [python3, deno, go, bash]

required:
- content
Expand Down
7 changes: 7 additions & 0 deletions backend/windmill-api/src/scripts.rs
Expand Up @@ -50,6 +50,7 @@ pub fn global_service() -> Router {
)
.route("/deno/tojsonschema", post(parse_deno_code_to_jsonschema))
.route("/go/tojsonschema", post(parse_go_code_to_jsonschema))
.route("/bash/tojsonschema", post(parse_bash_code_to_jsonschema))
.route("/hub/list", get(list_hub_scripts))
.route("/hub/get/*path", get(get_hub_script_by_path))
.route("/hub/get_full/*path", get(get_full_hub_script_by_path))
Expand Down Expand Up @@ -661,3 +662,9 @@ async fn parse_go_code_to_jsonschema(
) -> JsonResult<windmill_parser::MainArgSignature> {
windmill_parser_go::parse_go_sig(&code).map(Json)
}

async fn parse_bash_code_to_jsonschema(
Json(code): Json<String>,
) -> JsonResult<windmill_parser::MainArgSignature> {
windmill_parser_bash::parse_bash_sig(&code).map(Json)
}
1 change: 1 addition & 0 deletions backend/windmill-api/src/workspaces.rs
Expand Up @@ -562,6 +562,7 @@ async fn tarball_workspace(
ScriptLang::Python3 => "py",
ScriptLang::Deno => "ts",
ScriptLang::Go => "go",
ScriptLang::Bash => "sh",
};
write_to_archive(
script.content,
Expand Down
2 changes: 2 additions & 0 deletions backend/windmill-common/src/scripts.rs
Expand Up @@ -28,6 +28,7 @@ pub enum ScriptLang {
Deno,
Python3,
Go,
Bash,
}

impl ScriptLang {
Expand All @@ -36,6 +37,7 @@ impl ScriptLang {
ScriptLang::Deno => "deno",
ScriptLang::Python3 => "python3",
ScriptLang::Go => "go",
ScriptLang::Bash => "bash",
}
}
}
Expand Down
1 change: 1 addition & 0 deletions backend/windmill-worker/Cargo.toml
Expand Up @@ -23,6 +23,7 @@ windmill-parser.workspace = true
windmill-parser-ts.workspace = true
windmill-parser-go.workspace = true
windmill-parser-py.workspace = true
windmill-parser-bash.workspace = true
sqlx.workspace = true
uuid.workspace = true
tracing.workspace = true
Expand Down

0 comments on commit 3c09275

Please sign in to comment.