feat: support bash as 4th language (#865)

* bash support

* bash backend working

* frontend part

* bash backend working
This commit is contained in:
Ruben Fiszel
2022-11-06 23:48:18 +01:00
committed by GitHub
parent 5bee890032
commit 7c97fac746
35 changed files with 475 additions and 50 deletions
+15
View File
@@ -4086,6 +4086,7 @@ dependencies = [
"windmill-audit",
"windmill-common",
"windmill-parser",
"windmill-parser-bash",
"windmill-parser-go",
"windmill-parser-py",
"windmill-parser-ts",
@@ -4152,6 +4153,19 @@ dependencies = [
"serde_json",
]
[[package]]
name = "windmill-parser-bash"
version = "1.44.0"
dependencies = [
"anyhow",
"itertools",
"phf 0.11.1",
"regex",
"unicode-general-category",
"windmill-common",
"windmill-parser",
]
[[package]]
name = "windmill-parser-go"
version = "1.44.0"
@@ -4240,6 +4254,7 @@ dependencies = [
"windmill-audit",
"windmill-common",
"windmill-parser",
"windmill-parser-bash",
"windmill-parser-go",
"windmill-parser-py",
"windmill-parser-ts",
+1
View File
@@ -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"] }
@@ -0,0 +1 @@
-- Add down migration script here
@@ -0,0 +1,2 @@
-- Add up migration script here
ALTER TYPE SCRIPT_LANG ADD VALUE 'bash';
@@ -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
@@ -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
View File
@@ -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;
+1
View File
@@ -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
+27 -6
View File
@@ -1671,7 +1671,7 @@ paths:
schema: {}
language:
type: string
enum: [deno, python3, go]
enum: [deno, python3, go, bash]
summary:
type: string
required:
@@ -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]
@@ -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
@@ -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]
@@ -3677,7 +3698,7 @@ components:
type: boolean
language:
type: string
enum: [python3, deno, go]
enum: [python3, deno, go, bash]
required:
- id
- running
@@ -3755,7 +3776,7 @@ components:
type: boolean
language:
type: string
enum: [python3, deno, go]
enum: [python3, deno, go, bash]
is_skipped:
type: boolean
required:
@@ -4126,7 +4147,7 @@ components:
$ref: "#/components/schemas/ScriptArgs"
language:
type: string
enum: [python3, deno, go]
enum: [python3, deno, go, bash]
required:
- content
+7
View File
@@ -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))
@@ -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
View File
@@ -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,
+2
View File
@@ -28,6 +28,7 @@ pub enum ScriptLang {
Deno,
Python3,
Go,
Bash,
}
impl ScriptLang {
@@ -36,6 +37,7 @@ impl ScriptLang {
ScriptLang::Deno => "deno",
ScriptLang::Python3 => "python3",
ScriptLang::Go => "go",
ScriptLang::Bash => "bash",
}
}
}
+1
View File
@@ -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
@@ -0,0 +1,105 @@
name: "bash run script"
mode: ONCE
hostname: "bash"
log_level: ERROR
time_limit: 300
rlimit_as: 2048
rlimit_cpu: 1000
rlimit_fsize: 1024
rlimit_nofile: 64
cwd: "/tmp"
clone_newnet: false
clone_newuser: {CLONE_NEWUSER}
keep_caps: false
keep_env: 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=500000000"
}
mount {
src: "{JOB_DIR}/main.sh"
dst: "/tmp/main.sh"
is_bind: true
mandatory: false
}
mount {
src: "/etc/ssl"
dst: "/etc/ssl"
is_bind: true
}
mount {
src: "/etc/pki"
dst: "/etc/pki"
is_bind: true
mandatory: false
}
mount {
src: "/etc/resolv.conf"
dst: "/etc/resolv.conf"
is_bind: true
}
mount {
src: "/dev/random"
dst: "/dev/random"
is_bind: true
}
iface_no_lo: true
mount {
src: "{CACHE_DIR}"
dst: "/tmp/.cache/go"
is_bind: true
rw: true
mandatory: false
}
{SHARED_MOUNT}
+100 -1
View File
@@ -139,8 +139,8 @@ const DEFAULT_HEAVY_DEPS: [&str; 18] = [
const INCLUDE_DEPS_PY_SH_CONTENT: &str = include_str!("../nsjail/download_deps.py.sh");
const NSJAIL_CONFIG_DOWNLOAD_PY_CONTENT: &str = include_str!("../nsjail/download.py.config.proto");
const NSJAIL_CONFIG_RUN_PYTHON3_CONTENT: &str = include_str!("../nsjail/run.python3.config.proto");
const NSJAIL_CONFIG_RUN_GO_CONTENT: &str = include_str!("../nsjail/run.go.config.proto");
const NSJAIL_CONFIG_RUN_BASH_CONTENT: &str = include_str!("../nsjail/run.bash.config.proto");
const NSJAIL_CONFIG_RUN_DENO_CONTENT: &str = include_str!("../nsjail/run.deno.config.proto");
const MAX_LOG_SIZE: u32 = 200000;
@@ -904,6 +904,21 @@ mount {{
)
.await
}
Some(ScriptLang::Bash) => {
handle_bash_job(
worker_config,
envs,
logs,
job,
db,
token,
&inner_content,
timeout,
job_dir,
&shared_mount,
)
.await
}
};
tracing::info!(
worker_name = %worker_name,
@@ -1100,6 +1115,90 @@ func Run(req Req) (interface{{}}, error){{
read_result(job_dir).await
}
#[tracing::instrument(level = "trace", skip_all)]
async fn handle_bash_job(
WorkerConfig { base_internal_url, disable_nuser, disable_nsjail, base_url, .. }: &WorkerConfig,
Envs { nsjail_path, path_env, home_env, .. }: &Envs,
logs: &mut String,
job: &QueuedJob,
db: &sqlx::Pool<sqlx::Postgres>,
token: String,
content: &str,
timeout: i32,
job_dir: &str,
shared_mount: &str,
) -> Result<serde_json::Value, Error> {
logs.push_str("\n\n--- BASH CODE EXECUTION ---\n");
set_logs(logs, job.id, db).await;
write_file(job_dir, "main.sh", content).await?;
let mut reserved_variables = get_reserved_variables(job, &token, &base_url, db).await?;
reserved_variables.insert("RUST_LOG".to_string(), "info".to_string());
let hm = match job.args {
Some(Value::Object(ref hm)) => hm.clone(),
_ => serde_json::Map::new(),
};
let args_owned = windmill_parser_bash::parse_bash_sig(&content)?
.args
.iter()
.map(|arg| {
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>>();
let args = args_owned.iter().map(|s| &s[..]).collect::<Vec<&str>>();
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 mut cmd_args = vec!["--config", "run.config.proto", "--", "/bin/sh", "main.sh"];
cmd_args.extend(args);
Command::new(nsjail_path)
.current_dir(job_dir)
.env_clear()
.envs(reserved_variables)
.env("PATH", path_env)
.env("BASE_INTERNAL_URL", base_internal_url)
.args(cmd_args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?
} else {
let mut cmd_args = vec!["main.sh"];
cmd_args.extend(&args);
Command::new("/bin/sh")
.current_dir(job_dir)
.env_clear()
.envs(reserved_variables)
.env("PATH", path_env)
.env("BASE_INTERNAL_URL", base_internal_url)
.env("HOME", home_env)
.args(cmd_args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?
};
handle_child(&job.id, db, logs, timeout, child).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 capitalize(s: &str) -> String {
let mut c = s.chars();
match c.next() {
View File
+4 -2
View File
@@ -33,7 +33,7 @@
import getMessageServiceOverride from 'vscode/service-override/messages'
import { StandaloneServices } from 'vscode/services'
import {
DENO_INIT_CODE,
BASH_INIT_CODE,
DENO_INIT_CODE_CLEAR,
GO_INIT_CODE,
PYTHON_INIT_CODE_CLEAR
@@ -57,7 +57,7 @@
let divEl: HTMLDivElement | null = null
let editor: monaco.editor.IStandaloneCodeEditor
export let lang: 'typescript' | 'python' | 'go'
export let lang: 'typescript' | 'python' | 'go' | 'shell'
export let code: string = ''
export let hash: string = randomHash()
export let cmdEnterAction: (() => void) | undefined = undefined
@@ -147,6 +147,8 @@
setCode(PYTHON_INIT_CODE_CLEAR)
} else if (lang == 'go') {
setCode(GO_INIT_CODE)
} else if (lang == 'shell') {
setCode(BASH_INIT_CODE)
}
}
}
+13 -4
View File
@@ -25,10 +25,8 @@
import HighlightCode from './HighlightCode.svelte'
import DrawerContent from './common/drawer/DrawerContent.svelte'
import { Drawer } from './common'
import Icon from 'svelte-awesome'
import Popover from './Popover.svelte'
export let lang: 'python3' | 'deno' | 'go'
export let lang: 'python3' | 'deno' | 'go' | 'bash'
export let editor: Editor
export let websocketAlive: { pyright: boolean; black: boolean; deno: boolean; go: boolean }
export let iconOnly: boolean = false
@@ -42,7 +40,7 @@
let resourceEditor: ResourceEditor
let codeViewer: Drawer
let codeLang: 'python3' | 'deno' | 'go' = 'deno'
let codeLang: 'python3' | 'deno' | 'go' | 'bash' = 'deno'
let codeContent: string = ''
function addEditorActions() {
@@ -109,6 +107,13 @@
editor.insertAtBeginning('import os\n')
}
editor.insertAtCursor(`os.environ.get("${name}")`)
} else if (lang == 'go') {
if (!editor.getCode().includes('"os"')) {
editor.insertAtLine('import "os"\n', 2)
}
editor.insertAtCursor(`os.Getenv("${name}")`)
} else if (lang == 'bash') {
editor.insertAtCursor(`$${name}`)
}
sendUserToast(`${name} inserted at cursor`)
}}
@@ -137,6 +142,8 @@
editor.insertAtLine('import wmill "github.com/windmill-labs/windmill-go-client"\n\n', 3)
}
editor.insertAtCursor(`v, _ := wmill.GetVariable("${path}")`)
} else if (lang == 'bash') {
sendUserToast('Not supported yet', true)
}
sendUserToast(`${name} inserted at cursor`)
}}
@@ -181,6 +188,8 @@
editor.insertAtLine('import wmill "github.com/windmill-labs/windmill-go-client"\n\n', 3)
}
editor.insertAtCursor(`r, _ := wmill.GetResource("${path}")`)
} else if (lang == 'bash') {
sendUserToast('Not supported yet', true)
}
sendUserToast(`${path} inserted at cursor`)
}}
@@ -3,9 +3,10 @@
import python from 'svelte-highlight/languages/python'
import typescript from 'svelte-highlight/languages/typescript'
import go from 'svelte-highlight/languages/go'
import shell from 'svelte-highlight/languages/shell'
export let code: string = ''
export let language: 'python3' | 'deno' | 'go' | undefined
export let language: 'python3' | 'deno' | 'go' | 'bash' | undefined
function getLang(lang: string | undefined) {
switch (lang) {
@@ -15,8 +16,10 @@
return typescript
case 'go':
return go
case 'bash':
return shell
default:
return python
return typescript
}
}
@@ -41,7 +41,7 @@
}
function initContent(
language: 'deno' | 'python3' | 'go',
language: 'deno' | 'python3' | 'go' | 'bash',
kind: Script.kind,
template: 'pgsql' | 'script'
) {
@@ -193,7 +193,8 @@
options={[
['Typescript (Deno)', 'deno'],
['Python 3.10', 'python3'],
['Go', 'go']
['Go', 'go'],
['Bash', 'bash']
]}
on:change={(e) => initContent(e.detail, script.kind, template)}
bind:value={script.language}
@@ -275,11 +276,12 @@
</div>
{/if}
{/if}
<Toggle
bind:checked={script.is_template}
options={{ right: 'Save as a workspace template' }}
/>
<div class="ml-3">
<Toggle
bind:checked={script.is_template}
options={{ right: 'Save as a workspace template' }}
/>
</div>
</div>
</CenteredPage>
{:else if step === 2}
@@ -21,7 +21,7 @@
let itemPicker: ItemPicker
let drawerViewer: Drawer
let code: string = ''
let lang: 'deno' | 'python3' | 'go' | undefined
let lang: 'deno' | 'python3' | 'go' | 'bash' | undefined
let options: [[string, any]] = [['Script', 'script']]
allowHub && options.unshift(['Hub', 'hub'])
@@ -10,9 +10,17 @@
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
let drawer: Drawer
export function openDrawer() {
loadVersion()
listUsers()
drawer?.openDrawer()
}
export function toggleDrawer() {
drawer?.toggleDrawer()
}
let version: string | undefined
let users: GlobalUserInfo[] = []
let filteredUsers: GlobalUserInfo[] | undefined
@@ -37,9 +45,6 @@
users = await UserService.listUsersAsSuperAdmin({ perPage: 100000 })
fuse?.setCollection(users)
}
loadVersion()
listUsers()
</script>
<Drawer bind:this={drawer} on:open={listUsers} size="900px">
@@ -20,7 +20,7 @@
export async function runPreview(
path: string | undefined,
code: string,
lang: 'deno' | 'go' | 'python3',
lang: 'deno' | 'go' | 'python3' | 'bash',
args: Record<string, any>
): Promise<void> {
try {
@@ -23,12 +23,15 @@
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
let drawer: Drawer
export function openDrawer() {
loadVersion()
loadLoginType()
listTokens()
drawer?.openDrawer()
}
export function toggleDrawer() {
if (!drawer || !drawer?.isOpen()) {
loadVersion()
loadLoginType()
listTokens()
}
drawer?.toggleDrawer()
}
@@ -5,7 +5,7 @@
export let path: string
let code: string
let language: 'deno' | 'python3' | 'go'
let language: 'deno' | 'python3' | 'go' | 'bash'
async function loadCode(path: string) {
const script = await getScriptByPath(path!)
@@ -16,7 +16,7 @@
import DrawerContent from '../common/drawer/DrawerContent.svelte'
import HighlightCode from '../HighlightCode.svelte'
import LogViewer from '../LogViewer.svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import { Pane } from 'svelte-splitpanes'
import SplitPanesWrapper from '../splitPanes/SplitPanesWrapper.svelte'
export let path: string | undefined
@@ -59,7 +59,7 @@
class="overflow-x-auto break-words relative h-full m-2 text-xs bg-white shadow-inner p-2">
{drawerContent?.content}
</pre>
{:else if drawerContent?.mode === 'deno' || drawerContent?.mode === 'python3' || drawerContent?.mode === 'go'}
{:else if drawerContent?.mode === 'deno' || drawerContent?.mode === 'python3' || drawerContent?.mode === 'go' || drawerContent?.mode === 'bash'}
<HighlightCode language={drawerContent?.mode} code={drawerContent?.content} />
{/if}
</DrawerContent>
@@ -82,8 +82,8 @@
</Pane>
<Pane class="!duration-[0ms]">
{#if previewJob != undefined && 'result' in previewJob && previewJob.result != undefined}
<pre class="overflow-x-auto break-words relative h-full px-2">
<DisplayResult result={previewJob.result} />
<pre class="overflow-x-auto break-words relative h-full p-2"
><DisplayResult result={previewJob.result} />
</pre>
{:else}
<div class="text-sm text-gray-600 p-2">
+2
View File
@@ -43,6 +43,8 @@ export function langToExt(lang: string): string {
return 'py'
case 'go':
return 'go'
case 'bash':
return 'sh'
default:
return 'unknown'
}
+5 -1
View File
@@ -6,7 +6,7 @@ const loadSchemaLastRun = writable<[string | undefined, MainArgSignature | undef
export async function inferArgs(
language: 'python3' | 'deno' | 'go',
language: 'python3' | 'deno' | 'go' | 'bash',
code: string,
schema: Schema
): Promise<void> {
@@ -28,6 +28,10 @@ export async function inferArgs(
inferedSchema = await ScriptService.goToJsonschema({
requestBody: code
})
} else if (language == 'bash') {
inferedSchema = await ScriptService.bashToJsonschema({
requestBody: code
})
} else {
return
}
+12 -1
View File
@@ -143,6 +143,15 @@ export async function main(
return query.rows;
}`
export const BASH_INIT_CODE = `
# arguments of the form X="$I" are parsed as parameters X of type string
msg="$1"
# the last line of the stdout is the return value
echo "Hello $msg"
`
export const DENO_INIT_CODE_TRIGGER = `import * as wmill from "https://deno.land/x/windmill@v${__pkg__.version}/mod.ts"
export async function main() {
@@ -181,7 +190,7 @@ export function isInitialCode(content: string): boolean {
return false
}
export function initialCode(language: 'deno' | 'python3' | 'go', kind: Script.kind, subkind: 'pgsql' | 'flow' | 'script' | undefined): string {
export function initialCode(language: 'deno' | 'python3' | 'go' | 'bash', kind: Script.kind, subkind: 'pgsql' | 'flow' | 'script' | undefined): string {
if (language === 'deno') {
if (kind === 'trigger') {
return DENO_INIT_CODE_TRIGGER
@@ -210,6 +219,8 @@ export function initialCode(language: 'deno' | 'python3' | 'go', kind: Script.ki
} else {
return PYTHON_INIT_CODE
}
} else if (language == 'bash') {
return BASH_INIT_CODE
} else {
if (kind === 'failure') {
return GO_FAILURE_MODULE_CODE
+4 -2
View File
@@ -490,7 +490,7 @@ export function scriptPathToHref(path: string): string {
export async function getScriptByPath(path: string): Promise<{
content: string
language: 'deno' | 'python3' | 'go',
language: 'deno' | 'python3' | 'go' | 'bash',
schema: any
}> {
if (path.startsWith('hub/')) {
@@ -594,11 +594,13 @@ export function classNames(...classes: Array<string | undefined>): string {
return classes.filter(Boolean).join(' ')
}
export function scriptLangToEditorLang(lang: Script.language): 'typescript' | 'python' | 'go' {
export function scriptLangToEditorLang(lang: Script.language): 'typescript' | 'python' | 'go' | 'shell' {
if (lang == 'deno') {
return 'typescript'
} else if (lang == 'python3') {
return 'python'
} else if (lang == 'bash') {
return 'shell'
} else {
return lang
}
+4 -4
View File
@@ -111,8 +111,8 @@
<div class="px-2 py-4 space-y-2 border-y border-blue-400">
<WorkspaceMenu />
<UserMenu
on:user-settings={() => userSettings.toggleDrawer()}
on:superadmin-settings={() => superadminSettings.toggleDrawer()}
on:user-settings={() => userSettings.openDrawer()}
on:superadmin-settings={() => superadminSettings.openDrawer()}
/>
</div>
@@ -146,8 +146,8 @@
<div class="px-2 py-4 space-y-2 border-y border-blue-400">
<WorkspaceMenu {isCollapsed} />
<UserMenu
on:user-settings={userSettings.toggleDrawer}
on:superadmin-settings={() => superadminSettings.toggleDrawer()}
on:user-settings={userSettings.openDrawer}
on:superadmin-settings={() => superadminSettings.openDrawer()}
{isCollapsed}
/>
</div>
@@ -246,7 +246,7 @@
>
Copy
</Button>
<Button size="xs" on:click={userSettings.toggleDrawer}>Create token</Button>
<Button size="xs" on:click={userSettings.openDrawer}>Create token</Button>
</div>
</div>
{#if schedule}
+1 -1
View File
@@ -69,7 +69,7 @@
let codeViewer: Drawer
let codeViewerContent: string = ''
let codeViewerLanguage: 'deno' | 'python3' | 'go' = 'deno'
let codeViewerLanguage: 'deno' | 'python3' | 'go' | 'bash' = 'deno'
let codeViewerPath: string = ''
$: filteredScripts =
@@ -432,7 +432,7 @@
{/each}
</ul>
<div class="flex flex-row-reverse mt-2">
<Button size="xs" on:click={userSettings.toggleDrawer}>Create token</Button>
<Button size="xs" on:click={userSettings.openDrawer}>Create token</Button>
</div>
</TabContent>
{/each}
@@ -160,11 +160,11 @@
{/each}
<div class="flex justify-between items-center mt-10">
{#if $superadmin}
<Button variant="border" size="sm" on:click={superadminSettings.toggleDrawer}>
<Button variant="border" size="sm" on:click={superadminSettings.openDrawer}>
<Icon data={faCrown} class="mr-1" scale={1} />Superadmin settings</Button
>
{/if}
<Button variant="border" size="sm" on:click={userSettings.toggleDrawer}>
<Button variant="border" size="sm" on:click={userSettings.openDrawer}>
<Icon data={faUserCog} class="mr-1" scale={1} />User settings</Button
>
<Button