Compare commits

..

22 Commits

Author SHA1 Message Date
Ruben Fiszel
7882e355eb tests 2025-02-24 12:25:52 +01:00
Ruben Fiszel
0b6d130efd all 2025-02-24 02:05:32 +01:00
Ruben Fiszel
31a8f24734 all 2025-02-24 02:04:30 +01:00
Ruben Fiszel
34587fea42 all 2025-02-24 02:02:20 +01:00
Ruben Fiszel
0577f999c2 all 2025-02-24 01:45:34 +01:00
Ruben Fiszel
25a49b74f9 all 2025-02-24 01:38:30 +01:00
Ruben Fiszel
1df9457bb1 all 2025-02-24 01:33:33 +01:00
Ruben Fiszel
fd1cfc0df5 bench.sh 2025-02-24 01:20:20 +01:00
Alexander Petric
f1c5b77d7a fix(frontend): make html app component content selectable (#5359) 2025-02-23 23:49:54 +01:00
Ruben Fiszel
089ba7ae00 fix npn check 2025-02-23 23:49:33 +01:00
Ruben Fiszel
66451d4da1 recycle audit logs 2025-02-23 23:38:23 +01:00
Ruben Fiszel
1a54e1ed45 delete audit logs stub older than 14days on CE 2025-02-23 23:33:47 +01:00
Ruben Fiszel
692de3e628 update bun to 1.2.3 2025-02-23 23:11:07 +01:00
Ruben Fiszel
f90841ac5a fix missing lock error logs 2025-02-23 09:18:50 +01:00
Ruben Fiszel
c81a13cdac bun to 1.2.2 while 1.2.3 is not released 2025-02-22 16:31:00 +01:00
Ruben Fiszel
05659816e7 fix: improve cancel/back behavior on editors 2025-02-22 16:28:28 +01:00
Ruben Fiszel
7c0152c353 add runs page links to history section 2025-02-22 15:22:53 +01:00
Ruben Fiszel
b8e6d0da79 feat: make flow lock deployment error visible in UI 2025-02-22 15:02:16 +01:00
Ruben Fiszel
20547493e0 add PRINT_NATIVE_ERRORS env variable 2025-02-22 11:06:42 +01:00
Ruben Fiszel
c6dbd239b4 update deno_core implementation to latest 2.2.1 2025-02-22 11:04:54 +01:00
Ruben Fiszel
b102ff4a46 fix: update deno 2.1.2->2.2.1 2025-02-22 09:25:47 +01:00
Ruben Fiszel
4e477d1f58 fix: update bun t.1.43->1.2.3 2025-02-22 09:23:33 +01:00
37 changed files with 3475 additions and 567 deletions

View File

@@ -40,7 +40,7 @@ RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VER
RUN /usr/local/bin/python3 -m pip install pip-tools
COPY --from=oven/bun:1.1.31 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.2.3 /usr/local/bin/bun /usr/bin/bun
ARG TARGETPLATFORM

View File

@@ -8,317 +8,22 @@ on:
jobs:
benchmark_single:
runs-on: ubicloud-standard-8
services:
postgres:
image: postgres
env:
POSTGRES_DB: windmill
POSTGRES_PASSWORD: changeme
POSTGRES_INITDB_ARGS: "-c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB"
options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s
--health-retries 5
--shm-size=2g
windmill:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always --health-interval 10s --health-timeout 5s
--health-retries 5 --health-cmd "curl
http://localhost:8000/api/version"
ports:
- 8000:8000
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
- name: benchmark
timeout-minutes: 30
run: deno run --unstable -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
- name: Save benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark_single
path: |
*.json
benchmark_dedicated:
runs-on: ubicloud-standard-8
services:
postgres:
image: postgres
env:
POSTGRES_DB: windmill
POSTGRES_PASSWORD: changeme
POSTGRES_INITDB_ARGS: "-c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB"
options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s
--health-retries 5
windmill:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
WORKER_GROUP: dedicated
DEDICATED_WORKER: "admins:f/benchmarks/dedicated"
options: >-
--pull always --restart unless-stopped --health-interval 10s --health-timeout 5s
--health-retries 5 --health-cmd "curl
http://localhost:8000/api/version"
ports:
- 8000:8000
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
- name: benchmark
timeout-minutes: 20
run: deno run --unstable -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
--no-warm-up -c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_dedicated.json
- name: Save benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark_dedicated
path: |
*.json
benchmark_4workers:
runs-on: ubicloud-standard-8
services:
postgres:
image: postgres
env:
POSTGRES_DB: windmill
POSTGRES_PASSWORD: changeme
POSTGRES_INITDB_ARGS: "-c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB"
options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s
--health-retries 5
windmill:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always --health-interval 10s --health-timeout 5s
--health-retries 5 --health-cmd "curl
http://localhost:8000/api/version"
ports:
- 8000:8000
windmill_1:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
windmill_2:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
windmill_3:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
- name: benchmark
timeout-minutes: 20
run: deno run --unstable -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
--workers 4
--factor 3
- name: Save benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark_4workers
path: |
*.json
benchmark_8workers:
runs-on: ubicloud-standard-8
services:
postgres:
image: postgres
env:
POSTGRES_DB: windmill
POSTGRES_PASSWORD: changeme
POSTGRES_INITDB_ARGS: "-c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB"
options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s
--health-retries 5
windmill:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always --health-interval 10s --health-timeout 5s
--health-retries 5 --health-cmd "curl
http://localhost:8000/api/version"
ports:
- 8000:8000
windmill_1:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
windmill_2:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
windmill_3:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
windmill_4:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
windmill_5:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
windmill_6:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
windmill_7:
image: ghcr.io/windmill-labs/windmill-ee:main
env:
DATABASE_URL: postgres://postgres:changeme@postgres:5432/windmill
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
MODE: worker
WORKER_GROUP: main
WORKER_TAGS: deno,bun,go,python3,bash,dependency,flow,nativets
options: >-
--pull always
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
- name: benchmark
timeout-minutes: 20
run: deno run --unstable -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json
--workers 8
--factor 3
- name: Save benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark_8workers
path: |
*.json
benchmark_graphs:
runs-on: ubicloud
needs:
- benchmark_single
- benchmark_dedicated
- benchmark_4workers
- benchmark_8workers
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: v1.x
- uses: actions/checkout@v4
with:
ref: benchmarks
- name: Download benchmark results
uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: graphs
run: deno run --unstable -A -r
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_graphs.ts
-c
https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/graphs_config.json
- name: Push changes
run: |
ls -la
pwd
git add .
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git commit -m "Update benchmarks"
git push
docker run -d --network=host -e POSTGRES_PASSWORD=changeme -e POSTGRES_USER=postgres -e POSTGRES_DB=windmill -e POSTGRES_INITDB_ARGS="-c log_duration=on -c log_statement=all -c log_min_duration_statement=0 -c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB -c shared_preload_libraries=auto_explain -c auto_explain.log_min_duration=5 -c auto_explain.log_analyze=on -c auto_explain.log_timing=on -c auto_explain.log_buffers=on -c auto_explain.log_verbose=on \
-c log_statement=all \
-c log_min_duration_statement=0 \
-c shared_buffers=2GB \
-c work_mem=32MB \
-c effective_cache_size=4GB" \
postgres
sleep 5
docker run -d -it --network=host -e DATABASE_URL=postgres://postgres:changeme@localhost/windmill ghcr.io/windmill-labs/windmill:main
sleep 10
deno run --unstable -A -r https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/benchmark_suite.ts -c https://raw.githubusercontent.com/windmill-labs/windmill/${GITHUB_REF##ref/head/}/benchmarks/suite_config.json

View File

@@ -189,9 +189,9 @@ ENV TZ=Etc/UTC
COPY --from=builder /frontend/build /static_frontend
COPY --from=builder /windmill/target/release/windmill ${APP}/windmill
COPY --from=denoland/deno:2.1.2 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=denoland/deno:2.2.1 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=oven/bun:1.1.43 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.2.3 /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

View File

@@ -130,28 +130,28 @@
},
{
"ordinal": 25,
"name": "ai_models",
"type_info": "VarcharArray"
},
{
"ordinal": 26,
"name": "code_completion_model",
"type_info": "Varchar"
},
{
"ordinal": 27,
"name": "teams_command_script",
"type_info": "Text"
},
{
"ordinal": 28,
"ordinal": 26,
"name": "teams_team_id",
"type_info": "Text"
},
{
"ordinal": 29,
"ordinal": 27,
"name": "teams_team_name",
"type_info": "Text"
},
{
"ordinal": 28,
"name": "ai_models",
"type_info": "VarcharArray"
},
{
"ordinal": 29,
"name": "code_completion_model",
"type_info": "Varchar"
}
],
"parameters": {
@@ -185,10 +185,10 @@
true,
true,
true,
true,
true,
true,
false,
true,
true,
true,
true
]
},

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow SET path = $1, summary = $2, description = $3,dependency_job = NULL, draft_only = NULL, tag = $4, dedicated_worker = $5, visible_to_runner_only = $6, on_behalf_of_email = $7, value = $8, schema = $9::text::json, edited_by = $10, edited_at = now()\n WHERE path = $11 AND workspace_id = $12",
"query": "UPDATE flow SET path = $1, summary = $2, description = $3,dependency_job = NULL, lock_error_logs = '', draft_only = NULL, tag = $4, dedicated_worker = $5, visible_to_runner_only = $6, on_behalf_of_email = $7, value = $8, schema = $9::text::json, edited_by = $10, edited_at = now()\n WHERE path = $11 AND workspace_id = $12",
"describe": {
"columns": [],
"parameters": {
@@ -21,5 +21,5 @@
},
"nullable": []
},
"hash": "ba8bde5018fdf7b12f85cd3a6557c4accfc78bf160c1277f35d9d8ddcd056963"
"hash": "2b9607ed838c8c62eb0f2856420389f7be648f52edbb875ff52c96219ed3ba84"
}

View File

@@ -130,28 +130,28 @@
},
{
"ordinal": 25,
"name": "ai_models",
"type_info": "VarcharArray"
},
{
"ordinal": 26,
"name": "code_completion_model",
"type_info": "Varchar"
},
{
"ordinal": 27,
"name": "teams_command_script",
"type_info": "Text"
},
{
"ordinal": 28,
"ordinal": 26,
"name": "teams_team_id",
"type_info": "Text"
},
{
"ordinal": 29,
"ordinal": 27,
"name": "teams_team_name",
"type_info": "Text"
},
{
"ordinal": 28,
"name": "ai_models",
"type_info": "VarcharArray"
},
{
"ordinal": 29,
"name": "code_completion_model",
"type_info": "Varchar"
}
],
"parameters": {
@@ -185,10 +185,10 @@
true,
true,
true,
true,
true,
true,
false,
true,
true,
true,
true
]
},

View File

@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "SELECT lock_error_logs FROM flow WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "lock_error_logs",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
true
]
},
"hash": "97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3"
}

View File

@@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow SET lock_error_logs = $1 WHERE path = $2 AND workspace_id = $3",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "ba285edd1c1b1e400e85168ff4f05cf5281fd341096d433c7c0e5712e7726fb0"
}

View File

@@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "UPDATE flow SET lock_error_logs = NULL WHERE path = $1 AND workspace_id = $2",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": []
},
"hash": "d1876c46c0b1aba168efaebd3a056e999c400998eb699d862d718e7ab4c1f427"
}

View File

@@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "DELETE FROM audit WHERE timestamp <= now() - ($1::bigint::text || ' s')::interval",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": []
},
"hash": "d8186f0cee285aa50db7626409aec7e0504b068ffa8bb185d9384ce1422fd3d1"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO flow (workspace_id, path, summary, description, dependency_job, draft_only, tag, dedicated_worker, visible_to_runner_only, on_behalf_of_email, value, schema, edited_by, edited_at) \n VALUES ($1, $2, $3, $4, NULL, $5, $6, $7, $8, $9, $10, $11::text::json, $12, now())",
"query": "INSERT INTO flow (workspace_id, path, summary, description, dependency_job, lock_error_logs, draft_only, tag, dedicated_worker, visible_to_runner_only, on_behalf_of_email, value, schema, edited_by, edited_at) \n VALUES ($1, $2, $3, $4, NULL, '', $5, $6, $7, $8, $9, $10, $11::text::json, $12, now())",
"describe": {
"columns": [],
"parameters": {
@@ -21,5 +21,5 @@
},
"nullable": []
},
"hash": "4205d237c123d8d1c9ff2d61118027a80ccc8cd75e9703cb1d014b45f57c2be6"
"hash": "e4f1ee1568ce3c186b569421c6c8a3039f73d04fc53c67c70e67371f06416ef3"
}

View File

@@ -41,11 +41,11 @@
]
},
"nullable": [
true,
true,
true,
true,
true,
false,
false,
false,
false,
false,
true
]
},

3156
backend/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -160,7 +160,6 @@ windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" }
windmill-parser-php = { path = "./parsers/windmill-parser-php" }
windmill-api-client = { path = "./windmill-api-client" }
v8 = "=130.0.7" # Exact version
memchr = "2.7.4"
axum = { version = "^0.7", features = ["multipart"] }
headers = "^0"
@@ -212,22 +211,28 @@ itertools = "^0"
regex = "^1"
semver = "^1"
deno_fetch = "0.203.0"
deno_tls = "0.166.0"
deno_console = "0.179.0"
deno_url = "0.179.0"
deno_webidl = "0.179.0"
deno_web = "0.210.0"
deno_net = "0.171.0"
deno_core = "0.321.0"
deno_ast = { version = "=0.43.3", features = ["transpiling"] }
deno_permissions = "0.39.0"
v8 = "=134.4.0" # Exact version
deno_fetch = "0.216.0"
deno_tls = "0.179.0"
deno_console = "0.192.0"
deno_url = "0.192.0"
deno_webidl = "0.192.0"
deno_web = "0.223.0"
deno_io = "0.102.0"
deno_net = "0.184.0"
deno_core = "0.338.0"
deno_ast = { version = "=0.44.0", features = ["transpiling"] }
deno_permissions = "0.51.0"
deno_runtime = { version = "0.200.0", features = ["transpile"] }
deno_telemetry = "0.14.0"
deno_error = "=0.5.5"
swc_common = "=0.37.5"
swc_ecma_parser = "=0.149.1"
swc_ecma_ast = "=0.118.2"
swc_ecma_visit = "=0.104.8"
async-recursion = "^1"
base64 = "^0"
@@ -264,9 +269,9 @@ once_cell = "1.17.1"
gosyn = "0.2.6"
bytes = "1.4.0"
gethostname = "0.4.3"
wasm-bindgen = "=0.2.92"
serde-wasm-bindgen = "0.6.5"
wasm-bindgen-test = "0.3.42"
wasm-bindgen = "^0"
serde-wasm-bindgen = "^0"
wasm-bindgen-test = "^0"
convert_case = "0.6.0"
getrandom = "0.2"
tokio-postgres = {version = "^0.7", features = ["array-impls", "with-serde_json-1", "with-chrono-0_4", "with-uuid-1", "with-bit-vec-0_6"]}

View File

@@ -0,0 +1,2 @@
-- Add down migration script here
ALTER TABLE flow DROP COLUMN lock_error_logs;

View File

@@ -0,0 +1,2 @@
-- Add up migration script here
ALTER TABLE flow ADD COLUMN lock_error_logs TEXT;

View File

@@ -731,6 +731,22 @@ pub async fn delete_expired_items(db: &DB) -> () {
Err(e) => tracing::error!("Error deleting log file: {:?}", e),
}
#[cfg(not(feature = "enterprise"))]
let audit_retention_secs = 1 * 60 * 60 * 24 * 14;
#[cfg(feature = "enterprise")]
let audit_retention_secs = 1 * 60 * 60 * 24 * 365;
if let Err(e) = sqlx::query_scalar!(
"DELETE FROM audit WHERE timestamp <= now() - ($1::bigint::text || ' s')::interval",
audit_retention_secs,
)
.fetch_all(db)
.await
{
tracing::error!("Error deleting audit log on CE: {:?}", e);
}
let job_retention_secs = *JOB_RETENTION_SECS.read().await;
if job_retention_secs > 0 {
match db.begin().await {

View File

@@ -2900,7 +2900,7 @@ async fn test_flow_lock_all(db: Pool<Postgres>) {
.await
.unwrap()
.into_inner()
.subtype_0
.open_flow
.value
.modules;
modules.into_iter()

View File

@@ -5366,6 +5366,27 @@ paths:
schema:
$ref: "#/components/schemas/Flow"
/w/{workspace}/flows/deployment_status/p/{path}:
get:
summary: get flow deployment status
operationId: getFlowDeploymentStatus
tags:
- flow
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: flow status
content:
application/json:
schema:
type: object
properties:
lock_error_logs:
type: string
/w/{workspace}/flows/get_triggers_count/{path}:
get:
summary: get triggers count of flow
@@ -14336,6 +14357,10 @@ components:
allOf:
- $ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow"
- $ref: "#/components/schemas/FlowMetadata"
- type: object
properties:
lock_error_logs:
type: string
ExtraPerms:
type: object

View File

@@ -422,7 +422,7 @@ impl TryFrom<&str> for AIProvider {
#[derive(Deserialize, Debug)]
pub struct AIResource {
pub path: String,
pub path: Option<String>,
pub provider: AIProvider,
}
@@ -485,22 +485,26 @@ async fn proxy(
let ai_resource = serde_json::from_value::<AIResource>(ai_resource.unwrap())
.map_err(|e| Error::BadRequest(e.to_string()))?;
let path = ai_resource.path.unwrap_or("".to_string());
if path.is_empty() {
return Err(Error::BadRequest("Resource path is empty".to_string()));
}
let resource = sqlx::query_scalar!(
"SELECT value
FROM resource
WHERE path = $1 AND workspace_id = $2",
&ai_resource.path,
&path,
&w_id
)
.fetch_optional(&db)
.await?
.ok_or_else(|| {
Error::NotFound(format!(
"Could not find the {:?} resource at path {}, update the resource path in the workspace settings", ai_resource.provider, ai_resource.path
"Could not find the {:?} resource at path {}, update the resource path in the workspace settings", ai_resource.provider, path
))
})?;
(resource, ai_resource.path, ai_resource.provider)
(resource, path, ai_resource.provider)
};
if resource.is_none() {

View File

@@ -59,6 +59,7 @@ pub fn workspaced_service() -> Router {
.route("/get_triggers_count/*path", get(get_triggers_count))
.route("/list_tokens/*path", get(list_tokens))
.route("/get/*path", get(get_flow_by_path))
.route("/deployment_status/p/*path", get(get_deployment_status))
.route("/get/draft/*path", get(get_flow_by_path_w_draft))
.route("/exists/*path", get(exists_flow_by_path))
.route("/list_paths", get(list_paths))
@@ -356,8 +357,8 @@ async fn create_flow(
sqlx::query!(
"INSERT INTO flow (workspace_id, path, summary, description, \
dependency_job, draft_only, tag, dedicated_worker, visible_to_runner_only, on_behalf_of_email, value, schema, edited_by, edited_at)
VALUES ($1, $2, $3, $4, NULL, $5, $6, $7, $8, $9, $10, $11::text::json, $12, now())",
dependency_job, lock_error_logs, draft_only, tag, dedicated_worker, visible_to_runner_only, on_behalf_of_email, value, schema, edited_by, edited_at)
VALUES ($1, $2, $3, $4, NULL, '', $5, $6, $7, $8, $9, $10, $11::text::json, $12, now())",
w_id,
nf.path,
nf.summary,
@@ -683,7 +684,7 @@ async fn update_flow(
sqlx::query!(
"UPDATE flow SET path = $1, summary = $2, description = $3,\
dependency_job = NULL, draft_only = NULL, tag = $4, dedicated_worker = $5, visible_to_runner_only = $6, on_behalf_of_email = $7, \
dependency_job = NULL, lock_error_logs = '', draft_only = NULL, tag = $4, dedicated_worker = $5, visible_to_runner_only = $6, on_behalf_of_email = $7, \
value = $8, schema = $9::text::json, edited_by = $10, edited_at = now()
WHERE path = $11 AND workspace_id = $12",
if is_new_path { flow_path } else { &nf.path }, // if new path, do not rename directly (to avoid flow_version foreign key constraint)
@@ -950,6 +951,31 @@ async fn list_tokens(
list_tokens_internal(&db, &w_id, &path, true).await
}
#[derive(FromRow, Serialize)]
struct DeploymentStatus {
lock_error_logs: Option<String>,
}
async fn get_deployment_status(
Extension(db): Extension<DB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<DeploymentStatus> {
let path = path.to_path();
let mut tx = db.begin().await?;
let status_o: Option<DeploymentStatus> = sqlx::query_as!(
DeploymentStatus,
"SELECT lock_error_logs FROM flow WHERE path = $1 AND workspace_id = $2",
path,
w_id,
)
.fetch_optional(&mut *tx)
.await?;
let status = not_found_if_none(status_o, "DeploymentStatus", path)?;
tx.commit().await?;
Ok(Json(status))
}
async fn get_flow_by_path(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -961,7 +987,7 @@ async fn get_flow_by_path(
let flow_o = if query.with_starred_info.unwrap_or(false) {
sqlx::query_as::<_, FlowWithStarred>(
"SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, favorite.path IS NOT NULL as starred
"SELECT flow.workspace_id, flow.path, flow.lock_error_logs, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, favorite.path IS NOT NULL as starred
FROM flow
LEFT JOIN favorite
ON favorite.favorite_kind = 'flow'
@@ -978,7 +1004,7 @@ async fn get_flow_by_path(
.await?
} else {
sqlx::query_as::<_, FlowWithStarred>(
"SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, NULL as starred
"SELECT flow.workspace_id, flow.path, flow.lock_error_logs, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow.on_behalf_of_email, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, NULL as starred
FROM flow
LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
WHERE flow.path = $1 AND flow.workspace_id = $2"

View File

@@ -732,7 +732,8 @@ async fn edit_copilot_config(
.await?;
if let Some(cached) = AI_KEY_CACHE.get(&w_id) {
if cached.path != parsed_ai_resource.path {
if parsed_ai_resource.path.is_none() || parsed_ai_resource.path.unwrap() != cached.path
{
AI_KEY_CACHE.remove(&w_id);
}
}
@@ -1362,7 +1363,7 @@ struct UsedTriggers {
pub kafka_used: bool,
pub nats_used: bool,
pub postgres_used: bool,
pub sqs_used: bool
pub sqs_used: bool,
}
async fn get_used_triggers(

View File

@@ -60,6 +60,8 @@ pub struct FlowWithStarred {
pub flow: Flow,
#[serde(skip_serializing_if = "Option::is_none")]
pub starred: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lock_error_logs: Option<String>,
}
fn is_none_or_false(b: &Option<bool>) -> bool {

View File

@@ -20,7 +20,7 @@ flow_testing = []
cloud = []
sqlx = []
deno_core = ["dep:deno_fetch", "dep:deno_webidl", "dep:deno_web", "dep:deno_net", "dep:deno_console", "dep:deno_url", "dep:deno_core",
"dep:deno_ast", "dep:deno_tls", "dep:deno_permissions"]
"dep:deno_ast", "dep:deno_tls", "dep:deno_permissions", "dep:deno_io", "dep:deno_runtime", "dep:deno_telemetry", "dep:deno_error"]
otel = ["windmill-common/otel", "dep:opentelemetry"]
dind = ["dep:bollard"]
php = ["dep:windmill-parser-php"]
@@ -70,6 +70,7 @@ dyn-iter.workspace = true
once_cell.workspace = true
tokio-postgres.workspace = true
bit-vec.workspace = true
deno_telemetry = { workspace = true, optional = true }
deno_fetch = { workspace = true, optional = true }
deno_webidl = { workspace = true, optional = true }
deno_web = { workspace = true, optional = true }
@@ -80,6 +81,8 @@ deno_core = { workspace = true, optional = true }
deno_ast = { workspace = true, optional = true }
deno_tls = { workspace = true, optional = true }
deno_permissions = { workspace = true, optional = true }
deno_io = { workspace = true, optional = true }
deno_error = { workspace = true, optional = true }
postgres-native-tls.workspace = true
native-tls.workspace = true
@@ -118,3 +121,6 @@ deno_core = { workspace = true, optional = true }
deno_ast = { workspace = true, optional = true }
deno_tls = { workspace = true, optional = true }
deno_permissions = { workspace = true, optional = true }
deno_io = { workspace = true, optional = true }
deno_runtime = { workspace = true, optional = true }
deno_telemetry = { workspace = true, optional = true }

View File

@@ -32,9 +32,10 @@ impl FetchPermissions for PermissionsContainer {
#[inline(always)]
fn check_read<'a>(
&mut self,
_resolved: bool,
_p: &'a std::path::Path,
_api_name: &str,
) -> Result<Cow<'a, Path>, deno_permissions::PermissionCheckError> {
) -> Result<Cow<'a, std::path::Path>, deno_io::fs::FsError> {
unreachable!("snapshotting")
}
}
@@ -95,7 +96,7 @@ fn main() {
println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap());
let exts = vec![
// deno_telemetry::deno_telemetry::init_ops_and_esm(),
deno_telemetry::deno_telemetry::init_ops_and_esm(),
deno_webidl::deno_webidl::init_ops_and_esm(),
deno_url::deno_url::init_ops_and_esm(),
deno_console::deno_console::init_ops_and_esm(),
@@ -117,7 +118,9 @@ fn main() {
deno_core::snapshot::CreateSnapshotOptions {
cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"),
startup_snapshot: None,
extension_transpiler: None,
extension_transpiler: Some(std::rc::Rc::new(|specifier, source| {
deno_runtime::transpile::maybe_transpile_source(specifier, source)
})),
extensions: exts,
with_runtime_cb: None,
skip_op_registration: false,

View File

@@ -108,9 +108,10 @@ impl FetchPermissions for PermissionsContainer {
#[inline(always)]
fn check_read<'a>(
&mut self,
_resolved: bool,
p: &'a std::path::Path,
_api_name: &str,
) -> Result<Cow<'a, std::path::Path>, deno_permissions::PermissionCheckError> {
) -> Result<Cow<'a, std::path::Path>, deno_io::fs::FsError> {
Ok(Cow::Borrowed(p))
}
}
@@ -554,12 +555,17 @@ function get_from_env(name) {{
async fn op_variable(
op_state: Rc<RefCell<OpState>>,
#[string] path: String,
) -> Result<String, anyhow::Error> {
) -> Result<String, deno_error::JsErrorBox> {
let client = op_state.borrow().borrow::<OptAuthedClient>().0.clone();
if let Some(client) = client {
Ok(client.get_variable_value(&path).await?)
Ok(client
.get_variable_value(&path)
.await
.map_err(|e| deno_error::JsErrorBox::generic(e.to_string()))?)
} else {
anyhow::bail!("No client found in op state");
Err(deno_error::JsErrorBox::generic(
"No client found in op state",
))
}
}
@@ -569,16 +575,18 @@ async fn op_variable(
async fn op_get_result(
op_state: Rc<RefCell<OpState>>,
#[string] id: String,
) -> Result<String, anyhow::Error> {
) -> Result<String, deno_error::JsErrorBox> {
let client = op_state.borrow().borrow::<OptAuthedClient>().0.clone();
if let Some(client) = client {
let result = client
client
.get_completed_job_result::<Box<RawValue>>(&id, None)
.await?
.clone();
Ok(result.get().to_string())
.await
.map_err(|e| deno_error::JsErrorBox::generic(e.to_string()))
.map(|x| x.get().to_string())
} else {
anyhow::bail!("No client found in op state");
Err(deno_error::JsErrorBox::generic(
"No client found in op state",
))
}
}
@@ -589,7 +597,7 @@ async fn op_get_id(
op_state: Rc<RefCell<OpState>>,
#[string] flow_job_id: String,
#[string] node_id: String,
) -> Result<Option<String>, anyhow::Error> {
) -> Result<Option<String>, deno_error::JsErrorBox> {
let client = op_state.borrow().borrow::<OptAuthedClient>().0.clone();
if let Some(client) = client {
let result = client
@@ -602,7 +610,9 @@ async fn op_get_id(
Ok(None)
}
} else {
anyhow::bail!("No client found in op state");
Err(deno_error::JsErrorBox::generic(
"No client found in op state",
))
}
}
@@ -612,15 +622,18 @@ async fn op_get_id(
async fn op_resource(
op_state: Rc<RefCell<OpState>>,
#[string] path: String,
) -> Result<Option<String>, anyhow::Error> {
) -> Result<Option<String>, deno_error::JsErrorBox> {
let client = op_state.borrow().borrow::<OptAuthedClient>().0.clone();
if let Some(client) = client {
client
.get_resource_value_interpolated::<Option<Box<RawValue>>>(&path, None)
.await
.map(|x| x.map(|x| x.get().to_string()))
.map_err(|e| deno_error::JsErrorBox::generic(e.to_string()))
} else {
anyhow::bail!("No client found in op state");
Err(deno_error::JsErrorBox::generic(
"No client found in op state",
))
}
}
@@ -822,6 +835,7 @@ pub async fn eval_fetch_timeout(
};
let exts: Vec<Extension> = vec![
deno_telemetry::deno_telemetry::init_ops(),
deno_webidl::deno_webidl::init_ops(),
deno_url::deno_url::init_ops(),
deno_console::deno_console::init_ops(),
@@ -953,6 +967,9 @@ fn write_error_expr(expr: &str, uuid: &Uuid) {
}
};
if std::env::var("PRINT_NATIVE_ERRORS").is_ok() {
tracing::info!("native error for job {uuid}: {expr}");
}
if dir_entries >= 100 {
tracing::info!("Too many error files in {ERROR_DIR}, skipping write");
return;

View File

@@ -621,7 +621,8 @@ pub async fn handle_flow_dependency_job(
tx = clear_dependency_parent_path(&parent_path, &job_path, &job.workspace_id, "flow", tx)
.await?;
let modified_ids;
(flow.modules, tx, modified_ids) = lock_modules(
let errors;
(flow.modules, tx, modified_ids, errors) = lock_modules(
flow.modules,
job,
mem_peak,
@@ -638,6 +639,43 @@ pub async fn handle_flow_dependency_job(
occupancy_metrics,
)
.await?;
if !errors.is_empty() {
let error_message = errors
.iter()
.map(|e| format!("{}: {}", e.id, e.error))
.collect::<Vec<String>>()
.join("\n");
let logs2 = sqlx::query_scalar!(
"SELECT logs FROM job_logs WHERE job_id = $1 AND workspace_id = $2",
&job.id,
&job.workspace_id
)
.fetch_optional(db)
.await?
.flatten()
.unwrap_or_else(|| "no logs".to_string());
sqlx::query!(
"UPDATE flow SET lock_error_logs = $1 WHERE path = $2 AND workspace_id = $3",
&format!("{logs2}\n{error_message}"),
&job.script_path(),
&job.workspace_id
)
.execute(db)
.await?;
return Err(Error::ExecutionErr(format!(
"Error locking flow modules:\n{}\n\nlogs:\n{}",
error_message,
remove_ansi_codes(&logs2)
)));
} else {
sqlx::query!(
"UPDATE flow SET lock_error_logs = NULL WHERE path = $1 AND workspace_id = $2",
&job.script_path(),
&job.workspace_id
)
.execute(db)
.await?;
}
let new_flow_value = Json(serde_json::value::to_raw_value(&flow).map_err(to_anyhow)?);
// Re-check cancellation to ensure we don't accidentally override a flow.
@@ -772,6 +810,7 @@ async fn lock_modules<'c>(
Vec<FlowModule>,
sqlx::Transaction<'c, sqlx::Postgres>,
Vec<String>,
Vec<LockModuleError>,
)> {
let mut new_flow_modules = Vec::new();
let mut modified_ids = Vec::new();
@@ -802,7 +841,7 @@ async fn lock_modules<'c>(
parallelism,
} => {
let nmodules;
(nmodules, tx, nmodified_ids) = Box::pin(lock_modules(
(nmodules, tx, modified_ids, errors) = Box::pin(lock_modules(
modules,
job,
mem_peak,
@@ -835,7 +874,8 @@ async fn lock_modules<'c>(
for mut b in branches {
let nmodules;
let inner_modified_ids;
(nmodules, tx, inner_modified_ids) = Box::pin(lock_modules(
let inner_errors;
(nmodules, tx, inner_modified_ids, inner_errors) = Box::pin(lock_modules(
b.modules,
job,
mem_peak,
@@ -853,6 +893,7 @@ async fn lock_modules<'c>(
))
.await?;
nmodified_ids.extend(inner_modified_ids);
errors.extend(inner_errors);
b.modules = nmodules;
nbranches.push(b)
}
@@ -860,7 +901,7 @@ async fn lock_modules<'c>(
}
FlowModuleValue::WhileloopFlow { modules, modules_node, skip_failures } => {
let nmodules;
(nmodules, tx, nmodified_ids) = Box::pin(lock_modules(
(nmodules, tx, nmodified_ids, errors) = Box::pin(lock_modules(
modules,
job,
mem_peak,
@@ -890,8 +931,8 @@ async fn lock_modules<'c>(
for mut b in branches {
let nmodules;
let inner_modified_ids;
(nmodules, tx, inner_modified_ids) = Box::pin(lock_modules(
let inner_errors;
(nmodules, tx, inner_modified_ids, inner_errors) = Box::pin(lock_modules(
b.modules,
job,
mem_peak,
@@ -909,11 +950,13 @@ async fn lock_modules<'c>(
))
.await?;
nmodified_ids.extend(inner_modified_ids);
errors.extend(inner_errors);
b.modules = nmodules;
nbranches.push(b)
}
let ndefault;
(ndefault, tx, nmodified_ids) = Box::pin(lock_modules(
let ninner_errors;
(ndefault, tx, nmodified_ids, ninner_errors) = Box::pin(lock_modules(
default,
job,
mem_peak,
@@ -930,6 +973,7 @@ async fn lock_modules<'c>(
occupancy_metrics,
))
.await?;
errors.extend(ninner_errors);
e.value = FlowModuleValue::BranchOne {
branches: nbranches,
default: ndefault,
@@ -1048,29 +1092,8 @@ async fn lock_modules<'c>(
new_flow_modules.push(e);
continue;
}
if !errors.is_empty() {
let error_message = errors
.iter()
.map(|e| format!("{}: {}", e.id, e.error))
.collect::<Vec<String>>()
.join("\n");
let logs2 = sqlx::query_scalar!(
"SELECT logs FROM job_logs WHERE job_id = $1 AND workspace_id = $2",
&job.id,
&job.workspace_id
)
.fetch_optional(db)
.await?
.flatten()
.unwrap_or_else(|| "no logs".to_string());
return Err(Error::ExecutionErr(format!(
"Error locking flow modules:\n{}\n\nlogs:\n{}",
error_message,
remove_ansi_codes(&logs2)
)));
}
Ok((new_flow_modules, tx, modified_ids))
Ok((new_flow_modules, tx, modified_ids, errors))
}
async fn insert_flow_node<'c>(

43
benchmarks/bench.sh Normal file
View File

@@ -0,0 +1,43 @@
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
mkdir pg_logs
chmod 777 pg_logs
sudo docker run --network=host -e POSTGRES_PASSWORD=changeme -e POSTGRES_USER=postgres -e POSTGRES_DB=windmill -e POSTGRES_INITDB_ARGS="-c log_duration=on -c log_statement=all -c log_min_duration_statement=0 -c shared_buffers=2GB -c work_mem=32MB -c effective_cache_size=4GB -c shared_preload_libraries=auto_explain -c auto_explain.log_min_duration=5 -c auto_explain.log_analyze=on -c auto_explain.log_timing=on -c auto_explain.log_buffers=on -c auto_explain.log_verbose=on \
-c log_statement=all \
-c log_min_duration_statement=0 \
-c shared_buffers=2GB \
-c work_mem=32MB \
-c effective_cache_size=4GB \
-c shared_preload_libraries=auto_explain \
-c auto_explain.log_min_duration=5 \
-c auto_explain.log_analyze=on \
-c auto_explain.log_timing=on \
-c auto_explain.log_buffers=on \
-c auto_explain.log_verbose=on \
-c auto_explain.log_nested_statements=on \
-c logging_collector=on \
-c log_directory='/var/log/postgresql' \
-c log_filename='postgresql.log'" \
-v ~/pg_logs:/var/log/postgresql \
postgres
docker run -it --network=host -e DATABASE_URL=postgres://postgres:changeme@localhost/windmill ghcr.io/windmill-labs/windmill:main
curl -fsSL https://deno.land/install.sh | sh
cat <<EOF > suite.json
[
{
"kind": "noop",
"jobs": 90000
}
]
EOF
deno run --unstable -A -r https://raw.githubusercontent.com/windmill-labs/windmill/main/benchmarks/benchmark_suite.ts -c suite.json

View File

@@ -1,4 +0,0 @@
FROM ghcr.io/windmill-labs/windmill-ee-nsjail:main
RUN apt-get update
RUN apt-get install -y chromium

View File

@@ -20,7 +20,7 @@ RUN /usr/local/bin/python3 -m pip install pip-tools
# Install UV
RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.5.15/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv
COPY --from=oven/bun:1.1.43 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.2.3 /usr/local/bin/bun /usr/bin/bun
# add the docker client to call docker from a worker if enabled
COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/

View File

@@ -19,7 +19,7 @@ RUN /usr/local/bin/python3 -m pip install pip-tools
# Install UV
RUN curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.5.15/uv-installer.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv
COPY --from=oven/bun:1.1.43 /usr/local/bin/bun /usr/bin/bun
COPY --from=oven/bun:1.2.3 /usr/local/bin/bun /usr/bin/bun
# add the docker client to call docker from a worker if enabled
COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/

View File

@@ -6,6 +6,8 @@
import { Pane, Splitpanes } from 'svelte-splitpanes'
import Section from '$lib/components/Section.svelte'
import SaveInputsButton from '$lib/components/SaveInputsButton.svelte'
import { Button } from './common'
import { ExternalLink } from 'lucide-svelte'
const dispatch = createEventDispatcher()
export let scriptHash: string | null = null
@@ -89,6 +91,19 @@
<Pane class="px-4 py-4 h-full">
<Section label="History" wrapperClass="h-full" small={true}>
<svelte:fragment slot="action">
<Button
size="xs2"
color="light"
btnClasses="!text-tertiary"
endIcon={{ icon: ExternalLink }}
on:click={() => {
window.open(`/runs/${runnableId}`, '_blank')
}}
>
All runs
</Button>
</svelte:fragment>
<HistoricInputs
bind:this={historicInputs}
{runnableId}

View File

@@ -13,7 +13,7 @@
export let customCss: ComponentCustomCSS<'htmlcomponent'> | undefined = undefined
export let render: boolean
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
const { app, worldStore, mode } = getContext<AppViewerContext>('AppViewerContext')
const outputs = initOutput($worldStore, id, {
result: undefined,
@@ -38,7 +38,9 @@
{#if render}
<div
on:pointerdown={(e) => {
e?.preventDefault()
if ($mode !== 'preview') {
e?.preventDefault()
}
}}
class="h-full w-full"
>

View File

@@ -23,7 +23,6 @@
let goingTo: URL | undefined = undefined
beforeNavigate(async (newNavigationState) => {
// console.log('beforeNavigate', newNavigationState, bypassBeforeNavigate)
if (
!bypassBeforeNavigate &&
newNavigationState.to &&
@@ -32,9 +31,13 @@
) {
// console.log('going to', newNavigationState.to.url)
goingTo = newNavigationState.to.url
newNavigationState.cancel()
if (newNavigationState.type != 'popstate') {
await tick() // make sure saved value is updated when clicking on save draft or deploy
async function openModal() {
newNavigationState.cancel()
if (newNavigationState.type != 'popstate') {
await tick() // make sure saved value is updated when clicking on save draft or deploy
}
open = true
}
if (savedValue && modifiedValue) {
const draftOrDeployed = cleanValueProperties({
@@ -48,12 +51,11 @@
) {
bypassBeforeNavigate = true
additionalExitAction?.()
gotoUrl(goingTo)
} else {
open = true
await openModal()
}
} else {
open = true
await openModal()
}
} else if (bypassBeforeNavigate) {
bypassBeforeNavigate = false

View File

@@ -29,7 +29,6 @@
Trash,
ChevronUpSquare,
Share,
Badge,
Loader2,
GitFork,
Play,
@@ -67,18 +66,22 @@
import PostgresTriggersPanel from '$lib/components/triggers/postgres/PostgresTriggersPanel.svelte'
import Toggle from '$lib/components/Toggle.svelte'
import SqsTriggerPanel from '$lib/components/triggers/sqs/SqsTriggerPanel.svelte'
import { onDestroy } from 'svelte'
import LogViewer from '$lib/components/LogViewer.svelte'
let flow: Flow | undefined
let can_write = false
$: path = $page.params.path
let shareModal: ShareModal
let deploymentInProgress = false
let scheduledForStr: string | undefined = undefined
let invisible_to_owner: boolean | undefined = undefined
let overrideTag: string | undefined = undefined
let inputSelected: 'saved' | 'history' | undefined = undefined
let jsonView = false
let deploymentInProgress = false
let intervalId: NodeJS.Timeout | undefined = undefined
const triggersCount = writable<TriggersCount | undefined>(undefined)
@@ -129,12 +132,31 @@
if (!flow.path.startsWith(`u/${$userStore?.username}`) && flow.path.split('/').length > 2) {
invisible_to_owner = flow.visible_to_runner_only
}
intervalId && clearInterval(intervalId)
deploymentInProgress = flow.lock_error_logs == ''
if (deploymentInProgress) {
intervalId = setInterval(syncer, 500)
}
can_write = canWrite(flow.path, flow.extra_perms!, $userStore)
}
let isValid = true
let loading = false
async function syncer(): Promise<void> {
if (flow) {
const status = await FlowService.getFlowDeploymentStatus({
workspace: $workspaceStore!,
path: flow.path
})
if (status.lock_error_logs == undefined || status.lock_error_logs != '') {
deploymentInProgress = false
flow.lock_error_logs = status.lock_error_logs
clearInterval(intervalId)
}
}
}
async function runFlow(
scheduledForStr: string | undefined,
args: Record<string, any>,
@@ -198,7 +220,7 @@
}
buttons.push({
label: `View runs`,
label: `Runs`,
buttonProps: {
href: `${base}/runs/${flow.path}`,
size: 'xs',
@@ -328,6 +350,10 @@
return menuItems
}
onDestroy(() => {
intervalId && clearInterval(intervalId)
})
function onKeyDown(event: KeyboardEvent) {
switch (event.key) {
case 'Enter':
@@ -442,10 +468,17 @@
</div>
{#if deploymentInProgress}
<Badge color="yellow">
<HeaderBadge color="yellow">
<Loader2 size={12} class="inline animate-spin mr-1" />
Deployment in progress
</Badge>
</HeaderBadge>
{/if}
{#if flow.lock_error_logs && flow.lock_error_logs != ''}
<div class="bg-red-100 dark:bg-red-700 border-l-4 border-red-500 p-4" role="alert">
<p class="font-bold">Error deploying this flow</p>
<p> This flow has not been deployed successfully because of the following errors: </p>
<LogViewer content={flow.lock_error_logs} isLoading={false} tag={undefined} />
</div>
{/if}
<div class="flex flex-col align-left">
@@ -587,7 +620,7 @@
<PostgresTriggersPanel path={flow.path ?? ''} isFlow />
</div>
</svelte:fragment>
<svelte:fragment slot="nats">
<div class="p-2">
<NatsTriggersPanel path={flow.path ?? ''} isFlow />

View File

@@ -288,7 +288,7 @@
}
buttons.push({
label: `View runs`,
label: `Runs`,
buttonProps: {
href: `${base}/runs/${script.path}`,
size: 'xs',

View File

@@ -39,7 +39,7 @@ RUN /usr/local/go/bin/go install -v golang.org/x/tools/gopls@latest
RUN pip3 install tornado python-lsp-jsonrpc ruff-lsp
COPY --from=denoland/deno:2.1.2 --chmod=755 /usr/bin/deno /usr/bin/deno
COPY --from=denoland/deno:2.2.1 --chmod=755 /usr/bin/deno /usr/bin/deno
RUN mkdir -p /pyls/.cache