Compare commits
28 Commits
v1.235.0
...
operator-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76266f3efb | ||
|
|
2cdda619de | ||
|
|
d340fbc02f | ||
|
|
bed2d1f417 | ||
|
|
4377cb3bcc | ||
|
|
269afe41f7 | ||
|
|
1d1f03691a | ||
|
|
6b075e92ef | ||
|
|
be28522eb7 | ||
|
|
5749a3083a | ||
|
|
2034aebb17 | ||
|
|
4384617f1e | ||
|
|
94493812ac | ||
|
|
2fc21d46fa | ||
|
|
2e023003da | ||
|
|
30a30f81e0 | ||
|
|
37744ff583 | ||
|
|
dbe14757d4 | ||
|
|
12bd4af3ee | ||
|
|
9cdaf9c201 | ||
|
|
ec094e8897 | ||
|
|
cf61e09b90 | ||
|
|
6a0ae83b49 | ||
|
|
2f4be74e94 | ||
|
|
28811023a4 | ||
|
|
e80534ca65 | ||
|
|
95be0155dc | ||
|
|
004781cd3d |
26
.github/workflows/docker-image.yml
vendored
26
.github/workflows/docker-image.yml
vendored
@@ -200,25 +200,25 @@ jobs:
|
||||
|
||||
- run: |
|
||||
# pulling docker image with desired arch so that actions-docker-extract doesn't do it
|
||||
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
|
||||
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
|
||||
- run: |
|
||||
# Checks the image is in docker prior to running actions-docker-extract. It fails if not
|
||||
# Also useful to visually check that the arch is the right opencontainers
|
||||
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
|
||||
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
id: extract
|
||||
with:
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
path: "/usr/src/app/windmill"
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
id: extract-ee
|
||||
with:
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
path: "/usr/src/app/windmill"
|
||||
|
||||
- name: Rename binary with corresponding architecture
|
||||
@@ -244,25 +244,25 @@ jobs:
|
||||
|
||||
- run: |
|
||||
# pulling docker image with desired arch so that actions-docker-extract doesn't do it
|
||||
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
|
||||
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
docker pull --platform "linux/$ARCH" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
|
||||
- run: |
|
||||
# Checks the image is in docker prior to running actions-docker-extract. It fails if not
|
||||
# Also useful to visually check that the arch is the right opencontainers
|
||||
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
|
||||
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
docker image inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
id: extract
|
||||
with:
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
path: "/usr/src/app/windmill"
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
id: extract-ee
|
||||
with:
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
path: "/usr/src/app/windmill"
|
||||
|
||||
- name: Rename binary with corresponding architecture
|
||||
@@ -307,7 +307,7 @@ jobs:
|
||||
git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${git_hash:0:7} \
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
echo "GIT_HASH=${git_hash:0:7}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
|
||||
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,6 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## [1.236.2](https://github.com/windmill-labs/windmill/compare/v1.236.1...v1.236.2) (2024-01-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle better non object result for app result loader ([269afe4](https://github.com/windmill-labs/windmill/commit/269afe41f7e02da8d3a364e649e5fef00847d24a))
|
||||
|
||||
## [1.236.1](https://github.com/windmill-labs/windmill/compare/v1.236.0...v1.236.1) (2024-01-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix app initialization ([6b075e9](https://github.com/windmill-labs/windmill/commit/6b075e92ef4e312e6a4ce582dc7c4d88456b2f5d))
|
||||
|
||||
## [1.236.0](https://github.com/windmill-labs/windmill/compare/v1.235.1...v1.236.0) (2024-01-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve git sync and recompute dependents when common python script is redeployed ([#2967](https://github.com/windmill-labs/windmill/issues/2967)) ([9449381](https://github.com/windmill-labs/windmill/commit/94493812ac0030cc6bba468d8ee24a14661716a8))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* reduce stack size needed for workflow transitions ([4384617](https://github.com/windmill-labs/windmill/commit/4384617f1ec4c27de88ef6c895d6ef060a79f6bd))
|
||||
|
||||
## [1.235.1](https://github.com/windmill-labs/windmill/compare/v1.235.0...v1.235.1) (2024-01-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix resultjobloader ([2e02300](https://github.com/windmill-labs/windmill/commit/2e023003da8bea9efa86c0fbc52bf9723c73907d))
|
||||
|
||||
## [1.235.0](https://github.com/windmill-labs/windmill/compare/v1.234.1...v1.235.0) (2024-01-06)
|
||||
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
22
backend/.sqlx/query-266741894b9a6ed650cf999b6e5b7c797424f8291517d3abf096fe45548a0273.json
generated
Normal file
22
backend/.sqlx/query-266741894b9a6ed650cf999b6e5b7c797424f8291517d3abf096fe45548a0273.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT git_sync FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "git_sync",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "266741894b9a6ed650cf999b6e5b7c797424f8291517d3abf096fe45548a0273"
|
||||
}
|
||||
@@ -42,6 +42,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
15
backend/.sqlx/query-30e9250edd0c4c6025322f51b758174435d6df4f835cb0883ba40897cb99a410.json
generated
Normal file
15
backend/.sqlx/query-30e9250edd0c4c6025322f51b758174435d6df4f835cb0883ba40897cb99a410.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM dependency_map\n WHERE importer_path = $1 AND importer_kind = 'script'\n AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "30e9250edd0c4c6025322f51b758174435d6df4f835cb0883ba40897cb99a410"
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
16
backend/.sqlx/query-5fbd5dfb8df9aecce023c5d482ee58efcbc23c8682a3f0ec94977eb56023ed72.json
generated
Normal file
16
backend/.sqlx/query-5fbd5dfb8df9aecce023c5d482ee58efcbc23c8682a3f0ec94977eb56023ed72.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO dependency_map (workspace_id, importer_path, importer_kind, imported_path)\n VALUES ($1, $2, 'script', $3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5fbd5dfb8df9aecce023c5d482ee58efcbc23c8682a3f0ec94977eb56023ed72"
|
||||
}
|
||||
@@ -62,6 +62,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "bool",
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
14
backend/.sqlx/query-76d73f2fe5ac75e5033fb93d60f2ef5ab2b0b73a80dcb6a250c17c3b2f15c6b4.json
generated
Normal file
14
backend/.sqlx/query-76d73f2fe5ac75e5033fb93d60f2ef5ab2b0b73a80dcb6a250c17c3b2f15c6b4.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM dependency_map WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "76d73f2fe5ac75e5033fb93d60f2ef5ab2b0b73a80dcb6a250c17c3b2f15c6b4"
|
||||
}
|
||||
@@ -42,6 +42,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
23
backend/.sqlx/query-aa44311f9bae8253c6c483d122597ce7e470c6ba1905e7e877dca35d9d68968f.json
generated
Normal file
23
backend/.sqlx/query-aa44311f9bae8253c6c483d122597ce7e470c6ba1905e7e877dca35d9d68968f.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT importer_path FROM dependency_map\n WHERE imported_path = $1 AND importer_kind = 'script'\n AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "importer_path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "aa44311f9bae8253c6c483d122597ce7e470c6ba1905e7e877dca35d9d68968f"
|
||||
}
|
||||
@@ -42,6 +42,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
23
backend/.sqlx/query-deb2e4c21d4f9d0b9439780ca6ebee13450609b578c469cb437c5420ab520258.json
generated
Normal file
23
backend/.sqlx/query-deb2e4c21d4f9d0b9439780ca6ebee13450609b578c469cb437c5420ab520258.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT content FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "content",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "deb2e4c21d4f9d0b9439780ca6ebee13450609b578c469cb437c5420ab520258"
|
||||
}
|
||||
@@ -42,6 +42,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun",
|
||||
"mysql",
|
||||
"bigquery",
|
||||
|
||||
332
backend/Cargo.lock
generated
332
backend/Cargo.lock
generated
@@ -266,7 +266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener 4.0.2",
|
||||
"event-listener 4.0.3",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@@ -313,7 +313,7 @@ dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand 2.0.1",
|
||||
"futures-lite 2.1.0",
|
||||
"futures-lite 2.2.0",
|
||||
"slab",
|
||||
]
|
||||
|
||||
@@ -328,7 +328,7 @@ dependencies = [
|
||||
"async-io 2.2.2",
|
||||
"async-lock 3.2.0",
|
||||
"blocking",
|
||||
"futures-lite 2.1.0",
|
||||
"futures-lite 2.2.0",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
@@ -362,7 +362,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"futures-io",
|
||||
"futures-lite 2.1.0",
|
||||
"futures-lite 2.2.0",
|
||||
"parking",
|
||||
"polling 3.3.1",
|
||||
"rustix 0.38.28",
|
||||
@@ -386,7 +386,7 @@ version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c"
|
||||
dependencies = [
|
||||
"event-listener 4.0.2",
|
||||
"event-listener 4.0.3",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
@@ -539,15 +539,6 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "2.0.0"
|
||||
@@ -1073,7 +1064,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -1224,7 +1215,7 @@ dependencies = [
|
||||
"async-task",
|
||||
"fastrand 2.0.1",
|
||||
"futures-io",
|
||||
"futures-lite 2.1.0",
|
||||
"futures-lite 2.2.0",
|
||||
"piper",
|
||||
"tracing",
|
||||
]
|
||||
@@ -1879,7 +1870,7 @@ dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -2010,7 +2001,7 @@ dependencies = [
|
||||
"hashbrown 0.14.3",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core 0.9.9",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2129,7 +2120,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"pin-project",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2352,22 +2343,13 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"dirs-sys 0.3.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys 0.4.1",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2380,17 +2362,6 @@ dependencies = [
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
@@ -2684,9 +2655,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "4.0.2"
|
||||
version = "4.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "218a870470cce1469024e9fb66b901aa983929d81304a1cdb299f28118e550d5"
|
||||
checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
@@ -2699,7 +2670,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
|
||||
dependencies = [
|
||||
"event-listener 4.0.2",
|
||||
"event-listener 4.0.3",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@@ -2945,17 +2916,6 @@ dependencies = [
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-intrusive"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"lock_api",
|
||||
"parking_lot 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-intrusive"
|
||||
version = "0.5.0"
|
||||
@@ -2964,7 +2924,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"lock_api",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2990,9 +2950,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143"
|
||||
checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba"
|
||||
dependencies = [
|
||||
"fastrand 2.0.1",
|
||||
"futures-core",
|
||||
@@ -3439,7 +3399,7 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b780635574b3d92f036890d8373433d6f9fc7abb320ee42a5c25897fc8ed732"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
"dirs",
|
||||
"indicatif",
|
||||
"log",
|
||||
"native-tls",
|
||||
@@ -3910,9 +3870,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.151"
|
||||
version = "0.2.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@@ -3977,9 +3937,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libz-ng-sys"
|
||||
version = "1.1.12"
|
||||
version = "1.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dd9f43e75536a46ee0f92b758f6b63846e594e86638c61a9251338a65baea63"
|
||||
checksum = "601c27491de2c76b43c9f52d639b2240bfb9b02112009d3b754bfa90d891492d"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
"libc",
|
||||
@@ -3987,9 +3947,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.12"
|
||||
version = "1.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
|
||||
checksum = "5f526fdd09d99e19742883e43de41e1aa9e36db0c7ab7f935165d611c5cccc66"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -4614,7 +4574,7 @@ dependencies = [
|
||||
"humantime",
|
||||
"hyper",
|
||||
"itertools 0.11.0",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"quick-xml 0.31.0",
|
||||
"rand 0.8.5",
|
||||
@@ -4753,17 +4713,6 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
@@ -4771,21 +4720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall 0.2.16",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4923,14 +4858,11 @@ dependencies = [
|
||||
"archiver-rs",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"dirs 5.0.1",
|
||||
"dirs",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"reqwest",
|
||||
"sqlx 0.6.3",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"zip",
|
||||
]
|
||||
|
||||
@@ -5177,7 +5109,7 @@ checksum = "dd503430a6d9779b07915d858865fe998317ef3cfef8973881f578ac5d4baae7"
|
||||
dependencies = [
|
||||
"ahash 0.8.7",
|
||||
"arrow-format",
|
||||
"atoi 2.0.0",
|
||||
"atoi",
|
||||
"atoi_simd",
|
||||
"bytemuck",
|
||||
"chrono",
|
||||
@@ -5445,7 +5377,7 @@ version = "0.35.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae56f79e9cedd617773c1c8f5ca84a31a8b1d593714959d5f799e7bdd98fe51"
|
||||
dependencies = [
|
||||
"atoi 2.0.0",
|
||||
"atoi",
|
||||
"chrono",
|
||||
"now",
|
||||
"once_cell",
|
||||
@@ -5764,7 +5696,7 @@ dependencies = [
|
||||
"fnv",
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -5870,7 +5802,7 @@ dependencies = [
|
||||
"ahash 0.8.7",
|
||||
"equivalent",
|
||||
"hashbrown 0.14.3",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6043,15 +5975,6 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
@@ -7294,82 +7217,19 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188"
|
||||
dependencies = [
|
||||
"sqlx-core 0.6.3",
|
||||
"sqlx-macros 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf"
|
||||
dependencies = [
|
||||
"sqlx-core 0.7.3",
|
||||
"sqlx-macros 0.7.3",
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029"
|
||||
dependencies = [
|
||||
"ahash 0.7.7",
|
||||
"atoi 1.0.0",
|
||||
"base64 0.13.1",
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"dirs 4.0.0",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"event-listener 2.5.3",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-intrusive 0.4.2",
|
||||
"futures-util",
|
||||
"hashlink",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"indexmap 1.9.3",
|
||||
"itoa",
|
||||
"libc",
|
||||
"log",
|
||||
"md-5 0.10.6",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"rustls 0.20.9",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"sha2 0.10.8",
|
||||
"smallvec",
|
||||
"sqlformat",
|
||||
"sqlx-rt",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"webpki-roots 0.22.6",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.7.3"
|
||||
@@ -7377,7 +7237,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
|
||||
dependencies = [
|
||||
"ahash 0.8.7",
|
||||
"atoi 2.0.0",
|
||||
"atoi",
|
||||
"bigdecimal 0.3.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@@ -7389,7 +7249,7 @@ dependencies = [
|
||||
"event-listener 2.5.3",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-intrusive 0.5.0",
|
||||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashlink",
|
||||
@@ -7416,25 +7276,6 @@ dependencies = [
|
||||
"webpki-roots 0.25.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"either",
|
||||
"heck",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sha2 0.10.8",
|
||||
"sqlx-core 0.6.3",
|
||||
"sqlx-rt",
|
||||
"syn 1.0.109",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.7.3"
|
||||
@@ -7443,7 +7284,7 @@ checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sqlx-core 0.7.3",
|
||||
"sqlx-core",
|
||||
"sqlx-macros-core",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
@@ -7465,7 +7306,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"sqlx-core 0.7.3",
|
||||
"sqlx-core",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
@@ -7481,7 +7322,7 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4"
|
||||
dependencies = [
|
||||
"atoi 2.0.0",
|
||||
"atoi",
|
||||
"base64 0.21.5",
|
||||
"bigdecimal 0.3.1",
|
||||
"bitflags 2.4.1",
|
||||
@@ -7512,7 +7353,7 @@ dependencies = [
|
||||
"sha1",
|
||||
"sha2 0.10.8",
|
||||
"smallvec",
|
||||
"sqlx-core 0.7.3",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
@@ -7526,7 +7367,7 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24"
|
||||
dependencies = [
|
||||
"atoi 2.0.0",
|
||||
"atoi",
|
||||
"base64 0.21.5",
|
||||
"bigdecimal 0.3.1",
|
||||
"bitflags 2.4.1",
|
||||
@@ -7555,7 +7396,7 @@ dependencies = [
|
||||
"sha1",
|
||||
"sha2 0.10.8",
|
||||
"smallvec",
|
||||
"sqlx-core 0.7.3",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
@@ -7563,36 +7404,25 @@ dependencies = [
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-rt"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"tokio",
|
||||
"tokio-rustls 0.23.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-sqlite"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490"
|
||||
dependencies = [
|
||||
"atoi 2.0.0",
|
||||
"atoi",
|
||||
"chrono",
|
||||
"flume",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-intrusive 0.5.0",
|
||||
"futures-intrusive",
|
||||
"futures-util",
|
||||
"libsqlite3-sys",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"sqlx-core 0.7.3",
|
||||
"sqlx-core",
|
||||
"tracing",
|
||||
"url",
|
||||
"urlencoding",
|
||||
@@ -7653,7 +7483,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
|
||||
dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"phf_shared 0.10.0",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
@@ -8399,7 +8229,7 @@ dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2 0.5.5",
|
||||
@@ -8453,7 +8283,7 @@ dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"log",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"phf 0.11.2",
|
||||
"pin-project-lite",
|
||||
@@ -8661,7 +8491,7 @@ dependencies = [
|
||||
"cookie",
|
||||
"futures-util",
|
||||
"http",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@@ -9171,9 +9001,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.4.3"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62ce5bb364b23e66b528d03168df78b38c0f7b6fe17386928f29d5ab2e7cb2f7"
|
||||
checksum = "dd65ff0de3304a013e6dfeb7324e1cd389a8a000e582e3c9c9fae82ced778e26"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
@@ -9442,7 +9272,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -9462,7 +9292,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"sqlx 0.7.3",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tokio-metrics",
|
||||
"tracing",
|
||||
@@ -9471,13 +9301,14 @@ dependencies = [
|
||||
"windmill-api",
|
||||
"windmill-api-client",
|
||||
"windmill-common",
|
||||
"windmill-git-sync",
|
||||
"windmill-queue",
|
||||
"windmill-worker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -9524,7 +9355,7 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"sha2 0.10.8",
|
||||
"sql-builder",
|
||||
"sqlx 0.7.3",
|
||||
"sqlx",
|
||||
"tempfile",
|
||||
"time",
|
||||
"tinyvector",
|
||||
@@ -9541,6 +9372,7 @@ dependencies = [
|
||||
"uuid 1.6.1",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
"windmill-git-sync",
|
||||
"windmill-parser",
|
||||
"windmill-parser-py-imports",
|
||||
"windmill-queue",
|
||||
@@ -9548,7 +9380,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"base64 0.21.5",
|
||||
"chrono",
|
||||
@@ -9566,20 +9398,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sql-builder",
|
||||
"sqlx 0.7.3",
|
||||
"sqlx",
|
||||
"tracing",
|
||||
"windmill-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aws-config",
|
||||
@@ -9600,7 +9432,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"sqlx 0.7.3",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -9610,9 +9442,22 @@ dependencies = [
|
||||
"uuid 1.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"rsmq_async",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tracing",
|
||||
"windmill-common",
|
||||
"windmill-queue",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -9620,7 +9465,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -9631,7 +9476,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -9643,7 +9488,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -9654,7 +9499,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.12.0",
|
||||
@@ -9665,7 +9510,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -9675,14 +9520,14 @@ dependencies = [
|
||||
"regex",
|
||||
"rustpython-parser",
|
||||
"serde_json",
|
||||
"sqlx 0.7.3",
|
||||
"sqlx",
|
||||
"windmill-common",
|
||||
"windmill-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -9693,7 +9538,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -9710,7 +9555,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wasm"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"getrandom 0.2.11",
|
||||
@@ -9728,7 +9573,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -9749,7 +9594,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sql-builder",
|
||||
"sqlx 0.7.3",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"ulid",
|
||||
@@ -9760,7 +9605,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -9800,7 +9645,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"sqlx 0.7.3",
|
||||
"sqlx",
|
||||
"tiberius",
|
||||
"tokio",
|
||||
"tokio-postgres",
|
||||
@@ -9810,6 +9655,7 @@ dependencies = [
|
||||
"uuid 1.6.1",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
"windmill-git-sync",
|
||||
"windmill-parser",
|
||||
"windmill-parser-bash",
|
||||
"windmill-parser-go",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -11,6 +11,7 @@ members = [
|
||||
"./windmill-worker",
|
||||
"./windmill-common",
|
||||
"./windmill-audit",
|
||||
"./windmill-git-sync",
|
||||
"./parsers/windmill-parser",
|
||||
"./parsers/windmill-parser-ts",
|
||||
"./parsers/windmill-parser-wasm",
|
||||
@@ -21,7 +22,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -34,7 +35,7 @@ opt-level = 0
|
||||
incremental = true
|
||||
|
||||
[features]
|
||||
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise"]
|
||||
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "windmill-git-sync/enterprise"]
|
||||
enterprise_saml = ["windmill-api/enterprise_saml"]
|
||||
benchmark = ["windmill-api/benchmark", "windmill-worker/benchmark", "windmill-queue/benchmark"]
|
||||
flamegraph = ["windmill-common/flamegraph", "windmill-worker/flamegraph"]
|
||||
@@ -46,6 +47,7 @@ anyhow.workspace = true
|
||||
tokio.workspace = true
|
||||
dotenv.workspace = true
|
||||
windmill-common = { workspace = true, features = ["tracing_init"] }
|
||||
windmill-git-sync.workspace = true
|
||||
windmill-api.workspace = true
|
||||
windmill-worker.workspace = true
|
||||
futures.workspace = true
|
||||
@@ -66,7 +68,7 @@ uuid.workspace = true
|
||||
gethostname.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
pg-embed = {git = "https://github.com/faokunega/pg-embed", optional = true}
|
||||
pg-embed = {git = "https://github.com/faokunega/pg-embed", optional = true, default-features = false}
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json.workspace = true
|
||||
@@ -83,6 +85,7 @@ windmill-queue = { path = "./windmill-queue" }
|
||||
windmill-worker = { path = "./windmill-worker" }
|
||||
windmill-common = { path = "./windmill-common" }
|
||||
windmill-audit = { path = "./windmill-audit" }
|
||||
windmill-git-sync = { path = "./windmill-git-sync" }
|
||||
windmill-parser = { path = "./parsers/windmill-parser" }
|
||||
windmill-parser-ts = { path = "./parsers/windmill-parser-ts" }
|
||||
windmill-parser-py = { path = "./parsers/windmill-parser-py" }
|
||||
@@ -153,7 +156,7 @@ swc_ecma_visit = "0.93.7"
|
||||
base64 = "0.21.0"
|
||||
hmac = "0.12.1"
|
||||
sha2 = "0.10.6"
|
||||
sqlx = { version = "^0", features = [
|
||||
sqlx = { version = "0.7.3", features = [
|
||||
"macros",
|
||||
"migrate",
|
||||
"uuid",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add down migration script here
|
||||
DROP TABLE dependency_map;
|
||||
14
backend/migrations/20231227014107_add_dependency_map.up.sql
Normal file
14
backend/migrations/20231227014107_add_dependency_map.up.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
-- Add up migration script here
|
||||
|
||||
CREATE TYPE IMPORTER_KIND AS ENUM ('script', 'flow', 'app');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dependency_map (
|
||||
workspace_id VARCHAR(50) NOT NULL,
|
||||
importer_path VARCHAR(510) NOT NULL,
|
||||
importer_kind IMPORTER_KIND NOT NULL,
|
||||
imported_path VARCHAR(510) NOT NULL,
|
||||
PRIMARY KEY (workspace_id, importer_path, importer_kind, imported_path)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS dependency_map_imported_path_idx ON dependency_map (workspace_id, imported_path);
|
||||
|
||||
@@ -63,7 +63,7 @@ lazy_static! {
|
||||
|
||||
fn process_import(module: Option<String>, path: &str, level: usize) -> Vec<String> {
|
||||
if level > 0 {
|
||||
let mut imports = vec!["requests".to_string()];
|
||||
let mut imports = vec![];
|
||||
let splitted_path = path.split("/");
|
||||
let base = splitted_path
|
||||
.clone()
|
||||
@@ -78,10 +78,7 @@ fn process_import(module: Option<String>, path: &str, level: usize) -> Vec<Strin
|
||||
} else if let Some(module) = module {
|
||||
let imprt = module.split('.').next().unwrap_or("").replace("_", "-");
|
||||
if imprt == "u" || imprt == "f" {
|
||||
vec![
|
||||
"requests".to_string(),
|
||||
format!("relative:{}", module.replace(".", "/")),
|
||||
]
|
||||
vec![format!("relative:{}", module.replace(".", "/"))]
|
||||
} else {
|
||||
vec![imprt]
|
||||
}
|
||||
@@ -90,6 +87,57 @@ fn process_import(module: Option<String>, path: &str, level: usize) -> Vec<Strin
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_relative_imports(code: &str, path: &str) -> error::Result<Vec<String>> {
|
||||
let nimports = parse_code_for_imports(code, path)?;
|
||||
return Ok(nimports
|
||||
.into_iter()
|
||||
.filter_map(|x| {
|
||||
if x.starts_with("relative:") {
|
||||
Some(x.replace("relative:", ""))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
|
||||
fn parse_code_for_imports(code: &str, path: &str) -> error::Result<Vec<String>> {
|
||||
let code = code.split(DEF_MAIN).next().unwrap_or("");
|
||||
let ast = Suite::parse(code, "main.py").map_err(|e| {
|
||||
error::Error::ExecutionErr(format!("Error parsing code: {}", e.to_string()))
|
||||
})?;
|
||||
let nimports: Vec<String> = ast
|
||||
.into_iter()
|
||||
.filter_map(|x| match x {
|
||||
Stmt::Import(StmtImport { names, .. }) => Some(
|
||||
names
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let name = x.name.to_string();
|
||||
process_import(Some(name), path, 0)
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<String>>(),
|
||||
),
|
||||
Stmt::ImportFrom(StmtImportFrom { level: Some(i), module, .. }) if i.to_u32() > 0 => {
|
||||
Some(process_import(
|
||||
module.map(|x| x.to_string()),
|
||||
path,
|
||||
i.to_usize(),
|
||||
))
|
||||
}
|
||||
Stmt::ImportFrom(StmtImportFrom { level: _, module, .. }) => {
|
||||
Some(process_import(module.map(|x| x.to_string()), path, 0))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.flatten()
|
||||
.filter(|x| !STDIMPORTS.contains(&x.as_str()))
|
||||
.unique()
|
||||
.collect();
|
||||
return Ok(nimports);
|
||||
}
|
||||
|
||||
#[async_recursion]
|
||||
pub async fn parse_python_imports(
|
||||
code: &str,
|
||||
@@ -127,41 +175,7 @@ pub async fn parse_python_imports(
|
||||
imports.extend(lines);
|
||||
}
|
||||
|
||||
let code = code.split(DEF_MAIN).next().unwrap_or("");
|
||||
let ast = Suite::parse(code, "main.py").map_err(|e| {
|
||||
error::Error::ExecutionErr(format!("Error parsing code: {}", e.to_string()))
|
||||
})?;
|
||||
let nimports: Vec<String> = ast
|
||||
.into_iter()
|
||||
.filter_map(|x| match x {
|
||||
Stmt::Import(StmtImport { names, .. }) => Some(
|
||||
names
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let name = x.name.to_string();
|
||||
process_import(Some(name), path, 0)
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<String>>(),
|
||||
),
|
||||
Stmt::ImportFrom(StmtImportFrom { level: Some(i), module, .. })
|
||||
if i.to_u32() > 0 =>
|
||||
{
|
||||
Some(process_import(
|
||||
module.map(|x| x.to_string()),
|
||||
path,
|
||||
i.to_usize(),
|
||||
))
|
||||
}
|
||||
Stmt::ImportFrom(StmtImportFrom { level: _, module, .. }) => {
|
||||
Some(process_import(module.map(|x| x.to_string()), path, 0))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.flatten()
|
||||
.filter(|x| !STDIMPORTS.contains(&x.as_str()))
|
||||
.unique()
|
||||
.collect();
|
||||
let nimports = parse_code_for_imports(code, path)?;
|
||||
for n in nimports.iter() {
|
||||
let nested = if n.starts_with("relative:") {
|
||||
let rpath = n.replace("relative:", "");
|
||||
|
||||
@@ -20,7 +20,7 @@ def main():
|
||||
";
|
||||
let r = parse_python_imports(code, "test-workspace", "f/foo/bar", &db).await?;
|
||||
// println!("{}", serde_json::to_string(&r)?);
|
||||
assert_eq!(r, vec!["matplotlib", "requests", "wmill", "zanzibar"]);
|
||||
assert_eq!(r, vec!["matplotlib", "wmill", "zanzibar"]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ def main():
|
||||
"numpy",
|
||||
"pandas",
|
||||
"pandas2",
|
||||
"requests"
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ use windmill_common::{
|
||||
BASE_URL, DB, METRICS_DEBUG_ENABLED, METRICS_ENABLED,
|
||||
};
|
||||
use windmill_worker::{
|
||||
create_token_for_owner, handle_job_error, AuthedClient, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR,
|
||||
NPM_CONFIG_REGISTRY, PIP_EXTRA_INDEX_URL, SCRIPT_TOKEN_EXPIRY,
|
||||
create_token_for_owner, handle_job_error, AuthedClient, SendResult, JOB_DEFAULT_TIMEOUT,
|
||||
KEEP_JOB_DIR, NPM_CONFIG_REGISTRY, PIP_EXTRA_INDEX_URL, SCRIPT_TOKEN_EXPIRY,
|
||||
};
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
@@ -757,6 +757,7 @@ async fn handle_zombie_jobs<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
|
||||
// since the job is unrecoverable, the same worker queue should never be sent anything
|
||||
let (same_worker_tx_never_used, _same_worker_rx_never_used) = mpsc::channel::<Uuid>(1);
|
||||
let (send_result_never_used, _send_result_rx_never_used) = mpsc::channel::<SendResult>(1);
|
||||
|
||||
let token = create_token_for_owner(
|
||||
&db,
|
||||
@@ -795,6 +796,7 @@ async fn handle_zombie_jobs<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
"",
|
||||
rsmq.clone(),
|
||||
worker_name,
|
||||
send_result_never_used,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -1805,7 +1805,7 @@ def main():
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_empty_loop(db: Pool<Postgres>) {
|
||||
async fn test_empty_loop_1(db: Pool<Postgres>) {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await;
|
||||
let port = server.addr.port();
|
||||
|
||||
@@ -9,7 +9,7 @@ name = "windmill_api"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
enterprise = ["windmill-queue/enterprise", "async-stripe", "windmill-audit/enterprise"]
|
||||
enterprise = ["windmill-queue/enterprise", "async-stripe", "windmill-audit/enterprise", "windmill-git-sync/enterprise"]
|
||||
enterprise_saml = ["samael"]
|
||||
benchmark = []
|
||||
|
||||
@@ -27,6 +27,7 @@ windmill-common = { workspace = true, features = [
|
||||
windmill-audit.workspace = true
|
||||
windmill-parser.workspace = true
|
||||
windmill-parser-py-imports.workspace = true
|
||||
windmill-git-sync.workspace = true
|
||||
tokio.workspace = true
|
||||
anyhow.workspace = true
|
||||
argon2.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.235.0
|
||||
version: 1.236.2
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
|
||||
@@ -9,7 +9,6 @@ use std::collections::HashMap;
|
||||
*/
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
deployment_metadata_helpers,
|
||||
users::{require_owner_of_path, OptAuthed},
|
||||
variables::build_crypt,
|
||||
webhook_util::{WebhookMessage, WebhookShared},
|
||||
@@ -588,12 +587,16 @@ async fn create_app(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let (dependency_job_uuid, new_tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::AppDependencies { path: app.path.clone(), version: v_id },
|
||||
JobPayload::AppDependencies {
|
||||
path: app.path.clone(),
|
||||
version: v_id,
|
||||
deployment_message: app.deployment_message,
|
||||
},
|
||||
PushArgs::empty(),
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
@@ -615,25 +618,7 @@ async fn create_app(
|
||||
.await?;
|
||||
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
|
||||
|
||||
tx = PushIsolationLevel::Transaction(new_tx);
|
||||
tx = deployment_metadata_helpers::handle_deployment_metadata(
|
||||
tx,
|
||||
&authed,
|
||||
&db,
|
||||
&w_id,
|
||||
deployment_metadata_helpers::DeployedObject::App { path: app.path.clone(), version: v_id },
|
||||
app.deployment_message,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match tx {
|
||||
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
|
||||
_ => {
|
||||
return Err(Error::InternalErr(
|
||||
"Expected a transaction here".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
new_tx.commit().await?;
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
@@ -849,14 +834,18 @@ async fn update_app(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut tx: PushIsolationLevel<'_, rsmq_async::MultiplexedRsmq> =
|
||||
let tx: PushIsolationLevel<'_, rsmq_async::MultiplexedRsmq> =
|
||||
PushIsolationLevel::Transaction(tx);
|
||||
if let Some(v_id) = v_id {
|
||||
let (dependency_job_uuid, new_tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::AppDependencies { path: npath.clone(), version: v_id },
|
||||
JobPayload::AppDependencies {
|
||||
path: npath.clone(),
|
||||
version: v_id,
|
||||
deployment_message: ns.deployment_message,
|
||||
},
|
||||
PushArgs::empty(),
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
@@ -877,26 +866,7 @@ async fn update_app(
|
||||
)
|
||||
.await?;
|
||||
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
|
||||
|
||||
tx = PushIsolationLevel::Transaction(new_tx);
|
||||
tx = deployment_metadata_helpers::handle_deployment_metadata(
|
||||
tx,
|
||||
&authed,
|
||||
&db,
|
||||
&w_id,
|
||||
deployment_metadata_helpers::DeployedObject::App { path: npath.clone(), version: v_id },
|
||||
ns.deployment_message,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
match tx {
|
||||
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
|
||||
_ => {
|
||||
return Err(Error::InternalErr(
|
||||
"Expected a transaction here".to_string(),
|
||||
));
|
||||
}
|
||||
new_tx.commit().await?;
|
||||
}
|
||||
|
||||
webhook.send_message(
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
use crate::deployment_metadata_helpers;
|
||||
use crate::{
|
||||
db::DB,
|
||||
schedule::clear_schedule,
|
||||
@@ -355,7 +354,7 @@ async fn create_flow(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let (dependency_job_uuid, mut new_tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
@@ -363,6 +362,7 @@ async fn create_flow(
|
||||
JobPayload::FlowDependencies {
|
||||
path: nf.path.clone(),
|
||||
dedicated_worker: nf.dedicated_worker,
|
||||
deployment_message: nf.deployment_message,
|
||||
},
|
||||
PushArgs::empty(),
|
||||
&authed.username,
|
||||
@@ -393,26 +393,7 @@ async fn create_flow(
|
||||
.execute(&mut new_tx)
|
||||
.await?;
|
||||
|
||||
tx = PushIsolationLevel::Transaction(new_tx);
|
||||
tx = deployment_metadata_helpers::handle_deployment_metadata(
|
||||
tx,
|
||||
&authed,
|
||||
&db,
|
||||
&w_id,
|
||||
deployment_metadata_helpers::DeployedObject::Flow { path: nf.path.clone() },
|
||||
nf.deployment_message,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match tx {
|
||||
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
|
||||
_ => {
|
||||
return Err(Error::InternalErr(
|
||||
"Expected a transaction here".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
new_tx.commit().await?;
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::CreateFlow { workspace: w_id.clone(), path: nf.path.clone() },
|
||||
@@ -585,7 +566,7 @@ async fn update_flow(
|
||||
},
|
||||
);
|
||||
|
||||
let mut tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
|
||||
let (dependency_job_uuid, mut new_tx) = push(
|
||||
&db,
|
||||
@@ -594,6 +575,7 @@ async fn update_flow(
|
||||
JobPayload::FlowDependencies {
|
||||
path: nf.path.clone(),
|
||||
dedicated_worker: nf.dedicated_worker,
|
||||
deployment_message: nf.deployment_message,
|
||||
},
|
||||
PushArgs::empty(),
|
||||
&authed.username,
|
||||
@@ -631,25 +613,7 @@ async fn update_flow(
|
||||
.await?;
|
||||
}
|
||||
|
||||
tx = PushIsolationLevel::Transaction(new_tx);
|
||||
tx = deployment_metadata_helpers::handle_deployment_metadata(
|
||||
tx,
|
||||
&authed,
|
||||
&db,
|
||||
&w_id,
|
||||
deployment_metadata_helpers::DeployedObject::Flow { path: nf.path.clone() },
|
||||
nf.deployment_message,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match tx {
|
||||
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
|
||||
_ => {
|
||||
return Err(Error::InternalErr(
|
||||
"Expected a transaction here".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
new_tx.commit().await?;
|
||||
|
||||
Ok(nf.path.to_string())
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ mod audit;
|
||||
mod capture;
|
||||
mod configs;
|
||||
mod db;
|
||||
mod deployment_metadata_helpers;
|
||||
mod drafts;
|
||||
pub mod ee;
|
||||
pub mod embeddings;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
deployment_metadata_helpers,
|
||||
schedule::clear_schedule,
|
||||
users::{maybe_refresh_folders, require_owner_of_path, AuthCache},
|
||||
webhook_util::{WebhookMessage, WebhookShared},
|
||||
@@ -47,6 +46,7 @@ use windmill_common::{
|
||||
not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath,
|
||||
},
|
||||
};
|
||||
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
|
||||
use windmill_queue::{
|
||||
self, schedule::push_scheduled_job, PushArgs, PushIsolationLevel, QueueTransaction,
|
||||
};
|
||||
@@ -325,7 +325,7 @@ async fn create_script(
|
||||
let script_path = ns.path.clone();
|
||||
let hash = ScriptHash(hash_script(&ns));
|
||||
let authed = maybe_refresh_folders(&ns.path, &w_id, authed, &db).await;
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq.clone(), user_db.begin(&authed).await?).into();
|
||||
|
||||
if sqlx::query_scalar!(
|
||||
"SELECT 1 FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
@@ -604,37 +604,29 @@ async fn create_script(
|
||||
);
|
||||
}
|
||||
|
||||
let mut tx = PushIsolationLevel::Transaction(tx);
|
||||
|
||||
let permissioned_as = username_to_permissioned_as(&authed.username);
|
||||
if needs_lock_gen {
|
||||
let dependencies = match ns.language {
|
||||
ScriptLang::Python3 => {
|
||||
windmill_parser_py_imports::parse_python_imports(&ns.content, &w_id, &ns.path, &db)
|
||||
.await?
|
||||
.join("\n")
|
||||
}
|
||||
_ => ns.content,
|
||||
};
|
||||
let tag = if ns.dedicated_worker.is_some_and(|x| x) {
|
||||
Some(format!("{}:{}", &w_id, &ns.path,))
|
||||
} else {
|
||||
ns.tag
|
||||
};
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let (_, new_tx) = windmill_queue::push(
|
||||
&db,
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::Dependencies {
|
||||
hash,
|
||||
dependencies,
|
||||
language: ns.language,
|
||||
path: ns.path,
|
||||
dedicated_worker: ns.dedicated_worker,
|
||||
deployment_message: ns.deployment_message,
|
||||
},
|
||||
PushArgs::empty(),
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
permissioned_as,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -650,26 +642,19 @@ async fn create_script(
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
tx = PushIsolationLevel::Transaction(new_tx);
|
||||
}
|
||||
|
||||
tx = deployment_metadata_helpers::handle_deployment_metadata(
|
||||
tx,
|
||||
&authed,
|
||||
&db,
|
||||
&w_id,
|
||||
deployment_metadata_helpers::DeployedObject::Script { hash: hash, path: script_path },
|
||||
ns.deployment_message,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match tx {
|
||||
PushIsolationLevel::Transaction(tx) => tx.commit().await?,
|
||||
_ => {
|
||||
return Err(Error::InternalErr(
|
||||
"Expected a transaction here".to_string(),
|
||||
));
|
||||
}
|
||||
new_tx.commit().await?;
|
||||
} else {
|
||||
handle_deployment_metadata(
|
||||
&authed.email,
|
||||
&authed.username,
|
||||
&db,
|
||||
&w_id,
|
||||
DeployedObject::Script { hash: hash, path: script_path },
|
||||
ns.deployment_message,
|
||||
rsmq,
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
}
|
||||
|
||||
Ok((StatusCode::CREATED, format!("{}", hash)))
|
||||
|
||||
@@ -44,6 +44,7 @@ use windmill_common::s3_helpers::LargeFileStorage;
|
||||
use windmill_common::schedule::Schedule;
|
||||
use windmill_common::users::username_to_permissioned_as;
|
||||
use windmill_common::worker::CLOUD_HOSTED;
|
||||
use windmill_common::workspaces::WorkspaceGitRepo;
|
||||
use windmill_common::{
|
||||
error::{to_anyhow, Error, JsonResult, Result},
|
||||
flows::Flow,
|
||||
@@ -1045,13 +1046,6 @@ struct EditGitSyncConfig {
|
||||
git_sync_settings: Option<WorkspaceGitRepo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct WorkspaceGitRepo {
|
||||
pub script_path: String,
|
||||
pub git_repo_resource_path: String,
|
||||
pub use_individual_branch: Option<bool>,
|
||||
}
|
||||
|
||||
async fn edit_git_sync_config(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
@@ -1505,6 +1499,9 @@ async fn delete_workspace(
|
||||
let mut tx = db.begin().await?;
|
||||
require_super_admin(&db, &email).await?;
|
||||
|
||||
sqlx::query!("DELETE FROM dependency_map WHERE workspace_id = $1", &w_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query!("DELETE FROM queue WHERE workspace_id = $1", &w_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
@@ -283,17 +283,19 @@ pub enum JobPayload {
|
||||
Dependencies {
|
||||
path: String,
|
||||
hash: ScriptHash,
|
||||
dependencies: String,
|
||||
language: ScriptLang,
|
||||
dedicated_worker: Option<bool>,
|
||||
deployment_message: Option<String>,
|
||||
},
|
||||
FlowDependencies {
|
||||
path: String,
|
||||
dedicated_worker: Option<bool>,
|
||||
deployment_message: Option<String>,
|
||||
},
|
||||
AppDependencies {
|
||||
path: String,
|
||||
version: i64,
|
||||
deployment_message: Option<String>,
|
||||
},
|
||||
Flow {
|
||||
path: String,
|
||||
|
||||
@@ -36,6 +36,7 @@ pub mod users;
|
||||
pub mod utils;
|
||||
pub mod variables;
|
||||
pub mod worker;
|
||||
pub mod workspaces;
|
||||
|
||||
#[cfg(feature = "tracing_init")]
|
||||
pub mod tracing_init;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
pub const SUPERADMIN_SECRET_EMAIL: &str = "superadmin_secret@windmill.dev";
|
||||
pub const SUPERADMIN_NOTIFICATION_EMAIL: &str = "superadmin_notification@windmill.dev";
|
||||
pub const SUPERADMIN_SYNC_EMAIL: &str = "superadmin_sync@windmill.dev";
|
||||
|
||||
pub fn username_to_permissioned_as(user: &str) -> String {
|
||||
if user.contains('@') {
|
||||
|
||||
8
backend/windmill-common/src/workspaces.rs
Normal file
8
backend/windmill-common/src/workspaces.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct WorkspaceGitRepo {
|
||||
pub script_path: String,
|
||||
pub git_repo_resource_path: String,
|
||||
pub use_individual_branch: Option<bool>,
|
||||
}
|
||||
22
backend/windmill-git-sync/Cargo.toml
Normal file
22
backend/windmill-git-sync/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "windmill-git-sync"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "windmill_git_sync"
|
||||
path = "./src/lib.rs"
|
||||
|
||||
[features]
|
||||
enterprise = ["windmill-queue/enterprise"]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
sqlx.workspace = true
|
||||
serde_json.workspace = true
|
||||
tracing.workspace = true
|
||||
windmill-common = { workspace = true, features = ["axum"] }
|
||||
windmill-queue.workspace = true
|
||||
rsmq_async.workspace = true
|
||||
@@ -8,17 +8,18 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
use crate::db::DB;
|
||||
use crate::workspaces::{WorkspaceGitRepo, WorkspaceSettings};
|
||||
use sqlx::{Pool, Postgres};
|
||||
use windmill_common::users::SUPERADMIN_SYNC_EMAIL;
|
||||
use windmill_common::workspaces::WorkspaceGitRepo;
|
||||
|
||||
use serde_json::json;
|
||||
use windmill_common::error::{Error, Result};
|
||||
use windmill_common::jobs::JobPayload;
|
||||
use windmill_common::scripts::ScriptHash;
|
||||
use windmill_common::users::username_to_permissioned_as;
|
||||
use windmill_queue::PushIsolationLevel;
|
||||
|
||||
pub type DB = Pool<Postgres>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum DeployedObject {
|
||||
Script { hash: ScriptHash, path: String },
|
||||
@@ -37,13 +38,14 @@ impl DeployedObject {
|
||||
}
|
||||
|
||||
pub async fn handle_deployment_metadata<'c, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
mut tx: PushIsolationLevel<'c, R>,
|
||||
authed: &ApiAuthed,
|
||||
email: &str,
|
||||
created_by: &str,
|
||||
db: &DB,
|
||||
w_id: &str,
|
||||
obj: DeployedObject,
|
||||
deployment_message: Option<String>,
|
||||
) -> Result<PushIsolationLevel<'c, R>> {
|
||||
rsmq: Option<R>,
|
||||
) -> Result<()> {
|
||||
let skip_git_sync = if obj.get_path().starts_with("u/") {
|
||||
tracing::debug!(
|
||||
"Ignoring {} from git sync as it's in a private user folder",
|
||||
@@ -54,10 +56,10 @@ pub async fn handle_deployment_metadata<'c, R: rsmq_async::RsmqConnection + Send
|
||||
false
|
||||
};
|
||||
|
||||
let workspace_git_repo_setting = sqlx::query_as::<_, WorkspaceSettings>(
|
||||
"SELECT * FROM workspace_settings WHERE workspace_id = $1",
|
||||
let workspace_git_repo_setting = sqlx::query_scalar!(
|
||||
"SELECT git_sync FROM workspace_settings WHERE workspace_id = $1",
|
||||
w_id
|
||||
)
|
||||
.bind(&w_id)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
|
||||
@@ -69,11 +71,10 @@ pub async fn handle_deployment_metadata<'c, R: rsmq_async::RsmqConnection + Send
|
||||
|
||||
let workspace_git_repo = workspace_git_repo_setting
|
||||
.unwrap()
|
||||
.git_sync
|
||||
.map(|conf| serde_json::from_value::<WorkspaceGitRepo>(conf).ok())
|
||||
.flatten();
|
||||
|
||||
let (git_sync_job_uuid, mut new_tx) = if !skip_git_sync && workspace_git_repo.is_some() {
|
||||
let git_sync_job_uuid = if !skip_git_sync && workspace_git_repo.is_some() {
|
||||
let workspace_git_repo = workspace_git_repo.unwrap();
|
||||
|
||||
let mut args: HashMap<String, serde_json::Value> = HashMap::new();
|
||||
@@ -84,39 +85,56 @@ pub async fn handle_deployment_metadata<'c, R: rsmq_async::RsmqConnection + Send
|
||||
.strip_prefix("$res:")),
|
||||
);
|
||||
|
||||
let default_commit_msg: String;
|
||||
match obj.clone() {
|
||||
let message = match obj.clone() {
|
||||
DeployedObject::Script { path, .. } => {
|
||||
args.insert("path".to_string(), json!(path.to_string()));
|
||||
default_commit_msg = format!("Script '{}' deployed", path);
|
||||
if deployment_message.as_ref().is_none()
|
||||
|| deployment_message.as_ref().is_some_and(|x| x.is_empty())
|
||||
{
|
||||
format!("Script '{}' deployed", path)
|
||||
} else {
|
||||
deployment_message.clone().unwrap()
|
||||
}
|
||||
}
|
||||
DeployedObject::Flow { path } => {
|
||||
args.insert("path".to_string(), json!(path.to_string()));
|
||||
default_commit_msg = format!("Flow '{}' deployed", path);
|
||||
if deployment_message.as_ref().is_none()
|
||||
|| deployment_message.as_ref().is_some_and(|x| x.is_empty())
|
||||
{
|
||||
format!("Flow '{}' deployed", path)
|
||||
} else {
|
||||
deployment_message.clone().unwrap()
|
||||
}
|
||||
}
|
||||
DeployedObject::App { path, .. } => {
|
||||
args.insert("path".to_string(), json!(path.to_string()));
|
||||
default_commit_msg = format!("App '{}' deployed", path);
|
||||
if deployment_message.as_ref().is_none()
|
||||
|| deployment_message.as_ref().is_some_and(|x| x.is_empty())
|
||||
{
|
||||
format!("App '{}' deployed", path)
|
||||
} else {
|
||||
deployment_message.clone().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
args.insert(
|
||||
"commit_msg".to_string(),
|
||||
json!(deployment_message.clone().unwrap_or(default_commit_msg)),
|
||||
);
|
||||
};
|
||||
|
||||
args.insert("commit_msg".to_string(), json!(message));
|
||||
args.insert(
|
||||
"use_individual_branch".to_string(),
|
||||
json!(workspace_git_repo.use_individual_branch.unwrap_or(false)),
|
||||
);
|
||||
|
||||
let tx: PushIsolationLevel<'_, R> = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
|
||||
|
||||
let (job_uuid, new_tx) = windmill_queue::push(
|
||||
&db,
|
||||
db,
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::DeploymentCallback { path: workspace_git_repo.script_path.clone() },
|
||||
args,
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
created_by,
|
||||
&email,
|
||||
SUPERADMIN_SYNC_EMAIL.to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -132,16 +150,10 @@ pub async fn handle_deployment_metadata<'c, R: rsmq_async::RsmqConnection + Send
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
(Some(job_uuid), new_tx)
|
||||
new_tx.commit().await?;
|
||||
Some(job_uuid)
|
||||
} else {
|
||||
let new_tx = match tx {
|
||||
PushIsolationLevel::Isolated(user_db, authed, rsmq) => {
|
||||
(rsmq, user_db.begin(&authed).await?).into()
|
||||
}
|
||||
PushIsolationLevel::IsolatedRoot(db, rsmq) => (rsmq, db.begin().await?).into(),
|
||||
PushIsolationLevel::Transaction(tx) => tx,
|
||||
};
|
||||
(None, new_tx)
|
||||
None
|
||||
};
|
||||
|
||||
// We're not persisting the default commit msg as it's pretty useless. We will persist the ones manually set by users
|
||||
@@ -153,28 +165,27 @@ pub async fn handle_deployment_metadata<'c, R: rsmq_async::RsmqConnection + Send
|
||||
if deployment_message.is_some() || job_uuids.len() > 0 {
|
||||
// if the git sync job hasn't been triggered, and there is not custom deployment message, there's not point adding an entry to the table
|
||||
match obj.clone() {
|
||||
DeployedObject::Script { path, hash, .. } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, script_hash, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
|
||||
w_id, path, hash.0, &job_uuids, deployment_message,
|
||||
)
|
||||
},
|
||||
DeployedObject::Flow { path } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path) WHERE script_hash IS NULL AND app_version IS NULL DO UPDATE SET callback_job_ids = $3, deployment_msg = $4",
|
||||
w_id, path, &job_uuids, deployment_message,
|
||||
)
|
||||
}
|
||||
DeployedObject::App { path, version } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, app_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
|
||||
w_id, path, version, &job_uuids, deployment_message,
|
||||
)
|
||||
}
|
||||
}.execute(&mut new_tx)
|
||||
.await?;
|
||||
DeployedObject::Script { path, hash, .. } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, script_hash, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
|
||||
w_id, path, hash.0, &job_uuids, deployment_message,
|
||||
)
|
||||
},
|
||||
DeployedObject::Flow { path } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path) WHERE script_hash IS NULL AND app_version IS NULL DO UPDATE SET callback_job_ids = $3, deployment_msg = $4",
|
||||
w_id, path, &job_uuids, deployment_message,
|
||||
)
|
||||
}
|
||||
DeployedObject::App { path, version } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, app_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)",
|
||||
w_id, path, version, &job_uuids, deployment_message,
|
||||
)
|
||||
}
|
||||
}.execute(db)
|
||||
.await?;
|
||||
}
|
||||
|
||||
tx = PushIsolationLevel::Transaction(new_tx);
|
||||
return Ok(tx);
|
||||
return Ok(());
|
||||
}
|
||||
@@ -2496,21 +2496,23 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection
|
||||
dedicated_worker,
|
||||
None,
|
||||
),
|
||||
JobPayload::Dependencies { hash, dependencies, language, path, dedicated_worker } => (
|
||||
Some(hash.0),
|
||||
Some(path),
|
||||
Some((dependencies, None)),
|
||||
JobKind::Dependencies,
|
||||
None,
|
||||
None,
|
||||
Some(language),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
dedicated_worker,
|
||||
None,
|
||||
),
|
||||
JobPayload::FlowDependencies { path, dedicated_worker } => {
|
||||
JobPayload::Dependencies { hash, language, path, dedicated_worker, deployment_message } => {
|
||||
(
|
||||
Some(hash.0),
|
||||
Some(path),
|
||||
Some((deployment_message.unwrap_or_else(String::new), None)),
|
||||
JobKind::Dependencies,
|
||||
None,
|
||||
None,
|
||||
Some(language),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
dedicated_worker,
|
||||
None,
|
||||
)
|
||||
}
|
||||
JobPayload::FlowDependencies { path, dedicated_worker, deployment_message } => {
|
||||
let value_json = fetch_scalar_isolated!(
|
||||
sqlx::query_scalar!(
|
||||
"SELECT value FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
@@ -2528,7 +2530,7 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection
|
||||
(
|
||||
None,
|
||||
Some(path),
|
||||
None,
|
||||
Some((deployment_message.unwrap_or_else(String::new), None)),
|
||||
JobKind::FlowDependencies,
|
||||
Some(value.clone()),
|
||||
Some(FlowStatus::new(&value)), // this is a new flow being pushed, flow_status is set to flow_value
|
||||
@@ -2540,10 +2542,10 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection
|
||||
None,
|
||||
)
|
||||
}
|
||||
JobPayload::AppDependencies { path, version } => (
|
||||
JobPayload::AppDependencies { path, version, deployment_message } => (
|
||||
Some(version),
|
||||
Some(path),
|
||||
None,
|
||||
Some((deployment_message.unwrap_or_else(String::new), None)),
|
||||
JobKind::AppDependencies,
|
||||
None,
|
||||
None,
|
||||
|
||||
@@ -10,7 +10,7 @@ path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
enterprise = ["windmill-queue/enterprise", "dep:gcp_auth", "dep:jsonwebtoken", "dep:pem", "dep:sha2", "dep:tiberius", "dep:tokio-util"]
|
||||
enterprise = ["windmill-queue/enterprise", "windmill-git-sync/enterprise", "dep:gcp_auth", "dep:jsonwebtoken", "dep:pem", "dep:sha2", "dep:tiberius", "dep:tokio-util"]
|
||||
benchmark = ["windmill-queue/benchmark"]
|
||||
flamegraph = []
|
||||
|
||||
@@ -31,6 +31,7 @@ windmill-parser-py-imports.workspace = true
|
||||
windmill-parser-bash.workspace = true
|
||||
windmill-parser-sql.workspace = true
|
||||
windmill-parser-graphql.workspace = true
|
||||
windmill-git-sync.workspace = true
|
||||
sqlx.workspace = true
|
||||
uuid.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
@@ -28,22 +28,23 @@ class WindmillFinder(MetaPathFinder):
|
||||
return ModuleSpec(name, WindmillLoader(name))
|
||||
elif l > 2:
|
||||
script_path = "/".join(splitted)
|
||||
import requests
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
headers = {"Authorization": f"Bearer {os.environ.get('WM_TOKEN')}"}
|
||||
url = f"{os.environ.get('BASE_INTERNAL_URL')}/api/w/{os.environ.get('WM_WORKSPACE')}/scripts/raw/p/{script_path}.py"
|
||||
|
||||
r = requests.get(
|
||||
url, headers={"Authorization": f"Bearer {os.environ.get('WM_TOKEN')}"}
|
||||
)
|
||||
|
||||
if r.status_code == 200:
|
||||
folder = os.getcwd() + "/tmp/" + "/".join(splitted[:-1])
|
||||
fullpath = folder + "/" + splitted[-1] + ".py"
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
with open(fullpath, "w+") as f:
|
||||
f.write(r.text)
|
||||
return ModuleSpec(name, SourceFileLoader(name, fullpath))
|
||||
else:
|
||||
req = urllib.request.Request(url, None, headers)
|
||||
try:
|
||||
with urllib.request.urlopen(req) as response:
|
||||
r = response.read().decode("utf-8")
|
||||
folder = os.getcwd() + "/tmp/" + "/".join(splitted[:-1])
|
||||
fullpath = folder + "/" + splitted[-1] + ".py"
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
with open(fullpath, "w+") as f:
|
||||
f.write(r)
|
||||
return ModuleSpec(name, SourceFileLoader(name, fullpath))
|
||||
except:
|
||||
# raise ImportError(f"Script {script_path} not found")
|
||||
return ModuleSpec(name, WindmillLoader(name))
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ use std::{
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
|
||||
use windmill_parser_py_imports::parse_relative_imports;
|
||||
|
||||
use uuid::Uuid;
|
||||
#[cfg(feature = "enterprise")]
|
||||
@@ -33,9 +35,9 @@ use windmill_common::{
|
||||
error::{self, to_anyhow, Error},
|
||||
flows::{FlowModule, FlowModuleValue, FlowValue},
|
||||
get_latest_deployed_hash_for_path,
|
||||
jobs::{JobKind, QueuedJob},
|
||||
jobs::{JobKind, JobPayload, QueuedJob},
|
||||
scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang},
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL, SUPERADMIN_SYNC_EMAIL},
|
||||
utils::{rd_string, StripPath},
|
||||
worker::{
|
||||
to_raw_value, to_raw_value_owned, update_ping, CLOUD_HOSTED, WORKER_CONFIG, WORKER_GROUP,
|
||||
@@ -149,7 +151,8 @@ pub async fn create_token_for_owner(
|
||||
.await?
|
||||
.unwrap_or(false)
|
||||
|| email == SUPERADMIN_SECRET_EMAIL
|
||||
|| email == SUPERADMIN_NOTIFICATION_EMAIL;
|
||||
|| email == SUPERADMIN_NOTIFICATION_EMAIL
|
||||
|| owner == SUPERADMIN_SYNC_EMAIL;
|
||||
|
||||
sqlx::query_scalar!(
|
||||
"INSERT INTO token
|
||||
@@ -472,6 +475,7 @@ async fn handle_receive_completed_job<
|
||||
worker_name: &str,
|
||||
worker_save_completed_job_duration: Option<Arc<prometheus::Histogram>>,
|
||||
worker_flow_transition_duration: Option<Arc<prometheus::Histogram>>,
|
||||
job_completed_tx: Sender<SendResult>,
|
||||
) {
|
||||
let token = jc.token.clone();
|
||||
let workspace = jc.job.workspace_id.clone();
|
||||
@@ -494,6 +498,7 @@ async fn handle_receive_completed_job<
|
||||
worker_name,
|
||||
worker_save_completed_job_duration,
|
||||
worker_flow_transition_duration,
|
||||
job_completed_tx.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -509,6 +514,7 @@ async fn handle_receive_completed_job<
|
||||
&worker_dir,
|
||||
rsmq.clone(),
|
||||
worker_name,
|
||||
job_completed_tx,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -516,7 +522,7 @@ async fn handle_receive_completed_job<
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct JobCompletedSender(
|
||||
Sender<JobCompleted>,
|
||||
Sender<SendResult>,
|
||||
Option<Arc<GenericGauge<AtomicI64>>>,
|
||||
Option<Arc<prometheus::Histogram>>,
|
||||
);
|
||||
@@ -525,12 +531,12 @@ impl JobCompletedSender {
|
||||
pub async fn send(
|
||||
&self,
|
||||
jc: JobCompleted,
|
||||
) -> Result<(), tokio::sync::mpsc::error::SendError<JobCompleted>> {
|
||||
) -> Result<(), tokio::sync::mpsc::error::SendError<SendResult>> {
|
||||
if let Some(wj) = self.1.as_ref() {
|
||||
wj.inc()
|
||||
}
|
||||
let timer = self.2.as_ref().map(|x| x.start_timer());
|
||||
let r = self.0.send(jc).await;
|
||||
let r = self.0.send(SendResult::JobCompleted(jc)).await;
|
||||
timer.map(|x| x.stop_and_record());
|
||||
r
|
||||
}
|
||||
@@ -876,7 +882,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
|
||||
let (same_worker_tx, mut same_worker_rx) = mpsc::channel::<Uuid>(5);
|
||||
|
||||
let (job_completed_tx, mut job_completed_rx) = mpsc::channel::<JobCompleted>(3);
|
||||
let (job_completed_tx, mut job_completed_rx) = mpsc::channel::<SendResult>(3);
|
||||
|
||||
let job_completed_tx = JobCompletedSender(
|
||||
job_completed_tx,
|
||||
@@ -964,119 +970,167 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
|
||||
let worker_name2 = worker_name.clone();
|
||||
let killpill_tx2 = killpill_tx.clone();
|
||||
let job_completed_sender = job_completed_tx.0.clone();
|
||||
let send_result = tokio::spawn(async move {
|
||||
while let Some(jc) = job_completed_rx.recv().await {
|
||||
if let Some(wj) = worker_job_completed_channel_queue2.as_ref() {
|
||||
wj.dec();
|
||||
}
|
||||
let base_internal_url2 = base_internal_url2.clone();
|
||||
let worker_dir2 = worker_dir2.clone();
|
||||
let db2 = db2.clone();
|
||||
let same_worker_tx2 = same_worker_tx2.clone();
|
||||
let rsmq2 = rsmq2.clone();
|
||||
let worker_name = worker_name2.clone();
|
||||
if matches!(jc.job.job_kind, JobKind::Noop) || is_dedicated_worker {
|
||||
thread_count.fetch_add(1, Ordering::SeqCst);
|
||||
let thread_count = thread_count.clone();
|
||||
|
||||
loop {
|
||||
if thread_count.load(Ordering::Relaxed) < 4 {
|
||||
break;
|
||||
while let Some(sr) = job_completed_rx.recv().await {
|
||||
match sr {
|
||||
SendResult::JobCompleted(jc) => {
|
||||
if let Some(wj) = worker_job_completed_channel_queue2.as_ref() {
|
||||
wj.dec();
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(3)).await;
|
||||
}
|
||||
let base_internal_url2 = base_internal_url2.clone();
|
||||
let worker_dir2 = worker_dir2.clone();
|
||||
let db2 = db2.clone();
|
||||
let same_worker_tx2 = same_worker_tx2.clone();
|
||||
let rsmq2 = rsmq2.clone();
|
||||
let worker_name = worker_name2.clone();
|
||||
if matches!(jc.job.job_kind, JobKind::Noop) || is_dedicated_worker {
|
||||
thread_count.fetch_add(1, Ordering::SeqCst);
|
||||
let thread_count = thread_count.clone();
|
||||
|
||||
#[cfg(feature = "benchmark")]
|
||||
let send_duration = send_duration2.clone();
|
||||
#[cfg(feature = "benchmark")]
|
||||
let process_duration = process_duration.clone();
|
||||
#[cfg(feature = "benchmark")]
|
||||
let completed_jobs = completed_jobs.clone();
|
||||
#[cfg(feature = "benchmark")]
|
||||
let main_duration = main_duration2.clone();
|
||||
|
||||
let worker_save_completed_job_duration2 =
|
||||
worker_save_completed_job_duration.clone();
|
||||
let worker_flow_transition_duration2 = worker_flow_transition_duration.clone();
|
||||
let killpill_tx = killpill_tx2.clone();
|
||||
tokio::spawn(async move {
|
||||
#[cfg(feature = "benchmark")]
|
||||
let process_start = Instant::now();
|
||||
|
||||
let is_dependency_job = matches!(
|
||||
jc.job.job_kind,
|
||||
JobKind::Dependencies | JobKind::FlowDependencies
|
||||
);
|
||||
handle_receive_completed_job(
|
||||
jc,
|
||||
base_internal_url2,
|
||||
db2.clone(),
|
||||
worker_dir2,
|
||||
same_worker_tx2,
|
||||
rsmq2,
|
||||
&worker_name,
|
||||
worker_save_completed_job_duration2.clone(),
|
||||
worker_flow_transition_duration2.clone(),
|
||||
)
|
||||
.await;
|
||||
#[cfg(feature = "benchmark")]
|
||||
{
|
||||
let n = completed_jobs.fetch_add(1, Ordering::SeqCst);
|
||||
if (n + 1) % 1000 == 0 || n == (jobs - 1) as usize {
|
||||
let duration_s = start.elapsed().as_secs_f64();
|
||||
let jobs_per_sec = n as f64 / duration_s;
|
||||
tracing::info!(
|
||||
"completed {} jobs in {}s, {} jobs/s",
|
||||
n + 1,
|
||||
duration_s,
|
||||
jobs_per_sec
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"main loop without send {}s",
|
||||
main_duration.load(Ordering::SeqCst) as f64 / 1000.0
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"send job completed / send dedicated job duration {}s",
|
||||
send_duration.load(Ordering::SeqCst) as f64 / 1000.0
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"job completed process duration {}s",
|
||||
process_duration.load(Ordering::SeqCst) as f64 / 1000.0
|
||||
);
|
||||
loop {
|
||||
if thread_count.load(Ordering::Relaxed) < 4 {
|
||||
break;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(3)).await;
|
||||
}
|
||||
|
||||
process_duration.fetch_add(
|
||||
process_start.elapsed().as_millis() as usize,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "benchmark")]
|
||||
let send_duration = send_duration2.clone();
|
||||
#[cfg(feature = "benchmark")]
|
||||
let process_duration = process_duration.clone();
|
||||
#[cfg(feature = "benchmark")]
|
||||
let completed_jobs = completed_jobs.clone();
|
||||
#[cfg(feature = "benchmark")]
|
||||
let main_duration = main_duration2.clone();
|
||||
|
||||
thread_count.fetch_sub(1, Ordering::SeqCst);
|
||||
if is_dependency_job && is_dedicated_worker {
|
||||
tracing::error!("Dedicated worker executed a dependency job, a new script has been deployed. Exiting expecting to be restarted.");
|
||||
sqlx::query!("UPDATE config SET config = config WHERE name = $1", format!("worker__{}", *WORKER_GROUP))
|
||||
let worker_save_completed_job_duration2 =
|
||||
worker_save_completed_job_duration.clone();
|
||||
let worker_flow_transition_duration2 =
|
||||
worker_flow_transition_duration.clone();
|
||||
let killpill_tx = killpill_tx2.clone();
|
||||
let job_completed_sender = job_completed_sender.clone();
|
||||
tokio::spawn(async move {
|
||||
#[cfg(feature = "benchmark")]
|
||||
let process_start = Instant::now();
|
||||
|
||||
let is_dependency_job = matches!(
|
||||
jc.job.job_kind,
|
||||
JobKind::Dependencies | JobKind::FlowDependencies
|
||||
);
|
||||
handle_receive_completed_job(
|
||||
jc,
|
||||
base_internal_url2,
|
||||
db2.clone(),
|
||||
worker_dir2,
|
||||
same_worker_tx2,
|
||||
rsmq2,
|
||||
&worker_name,
|
||||
worker_save_completed_job_duration2.clone(),
|
||||
worker_flow_transition_duration2.clone(),
|
||||
job_completed_sender.clone(),
|
||||
)
|
||||
.await;
|
||||
#[cfg(feature = "benchmark")]
|
||||
{
|
||||
let n = completed_jobs.fetch_add(1, Ordering::SeqCst);
|
||||
if (n + 1) % 1000 == 0 || n == (jobs - 1) as usize {
|
||||
let duration_s = start.elapsed().as_secs_f64();
|
||||
let jobs_per_sec = n as f64 / duration_s;
|
||||
tracing::info!(
|
||||
"completed {} jobs in {}s, {} jobs/s",
|
||||
n + 1,
|
||||
duration_s,
|
||||
jobs_per_sec
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"main loop without send {}s",
|
||||
main_duration.load(Ordering::SeqCst) as f64 / 1000.0
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"send job completed / send dedicated job duration {}s",
|
||||
send_duration.load(Ordering::SeqCst) as f64 / 1000.0
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"job completed process duration {}s",
|
||||
process_duration.load(Ordering::SeqCst) as f64 / 1000.0
|
||||
);
|
||||
}
|
||||
|
||||
process_duration.fetch_add(
|
||||
process_start.elapsed().as_millis() as usize,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
|
||||
thread_count.fetch_sub(1, Ordering::SeqCst);
|
||||
if is_dependency_job && is_dedicated_worker {
|
||||
tracing::error!("Dedicated worker executed a dependency job, a new script has been deployed. Exiting expecting to be restarted.");
|
||||
sqlx::query!("UPDATE config SET config = config WHERE name = $1", format!("worker__{}", *WORKER_GROUP))
|
||||
.execute(&db2)
|
||||
.await
|
||||
.expect("update config to trigger restart of all dedicated workers at that config");
|
||||
killpill_tx.send(()).unwrap_or_default();
|
||||
killpill_tx.send(()).unwrap_or_default();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
handle_receive_completed_job(
|
||||
jc,
|
||||
base_internal_url2,
|
||||
db2,
|
||||
worker_dir2,
|
||||
same_worker_tx2,
|
||||
rsmq2,
|
||||
&worker_name,
|
||||
worker_save_completed_job_duration2.clone(),
|
||||
worker_flow_transition_duration2.clone(),
|
||||
job_completed_sender.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
handle_receive_completed_job(
|
||||
jc,
|
||||
base_internal_url2,
|
||||
db2,
|
||||
worker_dir2,
|
||||
same_worker_tx2,
|
||||
rsmq2,
|
||||
&worker_name,
|
||||
worker_save_completed_job_duration2.clone(),
|
||||
worker_flow_transition_duration2.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
SendResult::UpdateFlow {
|
||||
flow,
|
||||
w_id,
|
||||
success,
|
||||
result,
|
||||
worker_dir,
|
||||
stop_early_override,
|
||||
token,
|
||||
} => {
|
||||
// let r;
|
||||
if let Err(e) = update_flow_status_after_job_completion(
|
||||
&db2,
|
||||
&AuthedClient {
|
||||
base_internal_url: base_internal_url2.to_string(),
|
||||
workspace: w_id.clone(),
|
||||
token: token.clone(),
|
||||
force_client: None,
|
||||
},
|
||||
flow,
|
||||
&Uuid::nil(),
|
||||
&w_id,
|
||||
success,
|
||||
&result,
|
||||
true,
|
||||
same_worker_tx2.clone(),
|
||||
&worker_dir,
|
||||
stop_early_override,
|
||||
rsmq2.clone(),
|
||||
&worker_name2,
|
||||
job_completed_sender.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!("Error updating flow status after job completion: {e}");
|
||||
}
|
||||
}
|
||||
SendResult::Kill => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1333,6 +1387,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
}
|
||||
}
|
||||
println!("received killpill for worker {}", i_worker);
|
||||
job_completed_tx.0.send(SendResult::Kill).await.unwrap();
|
||||
break
|
||||
},
|
||||
_ = copy_to_bucket_rx.recv() => {
|
||||
@@ -1600,6 +1655,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
&worker_dir,
|
||||
rsmq.clone(),
|
||||
&worker_name,
|
||||
(&job_completed_tx.0).clone(),
|
||||
)
|
||||
.await;
|
||||
};
|
||||
@@ -2083,6 +2139,7 @@ pub async fn process_completed_job<R: rsmq_async::RsmqConnection + Send + Sync +
|
||||
worker_name: &str,
|
||||
worker_save_completed_job_duration: Option<Arc<prometheus::Histogram>>,
|
||||
worker_flow_transition_duration: Option<Arc<prometheus::Histogram>>,
|
||||
job_completed_tx: Sender<SendResult>,
|
||||
) -> windmill_common::error::Result<()> {
|
||||
if success {
|
||||
// println!("bef completed job{:?}", SystemTime::now());
|
||||
@@ -2126,6 +2183,7 @@ pub async fn process_completed_job<R: rsmq_async::RsmqConnection + Send + Sync +
|
||||
None,
|
||||
rsmq.clone(),
|
||||
worker_name,
|
||||
job_completed_tx,
|
||||
)
|
||||
.await?;
|
||||
timer.map(|x| x.stop_and_record());
|
||||
@@ -2161,6 +2219,7 @@ pub async fn process_completed_job<R: rsmq_async::RsmqConnection + Send + Sync +
|
||||
None,
|
||||
rsmq,
|
||||
worker_name,
|
||||
job_completed_tx,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -2216,6 +2275,7 @@ pub async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clon
|
||||
worker_dir: &str,
|
||||
rsmq: Option<R>,
|
||||
worker_name: &str,
|
||||
job_completed_tx: Sender<SendResult>,
|
||||
) {
|
||||
let err = match err {
|
||||
Error::JsonErr(err) => err,
|
||||
@@ -2263,6 +2323,7 @@ pub async fn handle_job_error<R: rsmq_async::RsmqConnection + Send + Sync + Clon
|
||||
None,
|
||||
rsmq.clone(),
|
||||
worker_name,
|
||||
job_completed_tx.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -2305,6 +2366,34 @@ fn extract_error_value(log_lines: &str, i: i32) -> Box<RawValue> {
|
||||
);
|
||||
}
|
||||
|
||||
pub enum SendResult {
|
||||
JobCompleted(JobCompleted),
|
||||
UpdateFlow {
|
||||
flow: Uuid,
|
||||
w_id: String,
|
||||
success: bool,
|
||||
result: Box<RawValue>,
|
||||
worker_dir: String,
|
||||
stop_early_override: Option<bool>,
|
||||
token: String,
|
||||
},
|
||||
Kill,
|
||||
}
|
||||
|
||||
// db: &DB,
|
||||
// client: &AuthedClient,
|
||||
// flow: uuid::Uuid,
|
||||
// job_id_for_status: &Uuid,
|
||||
// w_id: &str,
|
||||
// success: bool,
|
||||
// result: &'a RawValue,
|
||||
// unrecoverable: bool,
|
||||
// same_worker_tx: Sender<Uuid>,
|
||||
// worker_dir: &str,
|
||||
// stop_early_override: Option<bool>,
|
||||
// rsmq: Option<R>,
|
||||
// worker_name: &str,
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JobCompleted {
|
||||
pub job: Arc<QueuedJob>,
|
||||
@@ -2467,7 +2556,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
rsmq,
|
||||
worker_name,
|
||||
job_completed_tx.0.clone(),
|
||||
)
|
||||
.await?;
|
||||
timer.map(|x| x.stop_and_record());
|
||||
@@ -2515,6 +2604,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
worker_dir,
|
||||
base_internal_url,
|
||||
&client.get_token().await,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -2529,6 +2619,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
worker_dir,
|
||||
base_internal_url,
|
||||
&client.get_token().await,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await
|
||||
.map(|()| serde_json::from_str("{}").unwrap()),
|
||||
@@ -2543,6 +2634,7 @@ async fn handle_queued_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
worker_dir,
|
||||
base_internal_url,
|
||||
&client.get_token().await,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await
|
||||
.map(|()| serde_json::from_str("{}").unwrap()),
|
||||
@@ -3044,7 +3136,7 @@ mount {{
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
async fn handle_dependency_job(
|
||||
async fn handle_dependency_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
job: &QueuedJob,
|
||||
logs: &mut String,
|
||||
mem_peak: &mut i32,
|
||||
@@ -3055,7 +3147,17 @@ async fn handle_dependency_job(
|
||||
worker_dir: &str,
|
||||
base_internal_url: &str,
|
||||
token: &str,
|
||||
rsmq: Option<R>,
|
||||
) -> error::Result<Box<RawValue>> {
|
||||
let raw_code = sqlx::query_scalar!(
|
||||
"SELECT content FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
&job.script_hash.unwrap_or(ScriptHash(0)).0,
|
||||
&job.workspace_id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
.unwrap_or_else(|| "No script found at this hash".to_string());
|
||||
let script_path = job.script_path();
|
||||
let content = capture_dependency_job(
|
||||
&job.id,
|
||||
job.language.as_ref().map(|v| Ok(v)).unwrap_or_else(|| {
|
||||
@@ -3063,10 +3165,7 @@ async fn handle_dependency_job(
|
||||
"Job Language required for dependency jobs".to_owned(),
|
||||
))
|
||||
})?,
|
||||
job.raw_code
|
||||
.as_ref()
|
||||
.map(|a| a.as_str())
|
||||
.unwrap_or_else(|| "no raw code"),
|
||||
&raw_code,
|
||||
logs,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
@@ -3077,19 +3176,83 @@ async fn handle_dependency_job(
|
||||
worker_dir,
|
||||
base_internal_url,
|
||||
token,
|
||||
job.script_path(),
|
||||
script_path,
|
||||
)
|
||||
.await;
|
||||
match content {
|
||||
Ok(content) => {
|
||||
let hash = job.script_hash.unwrap_or(ScriptHash(0));
|
||||
let w_id = &job.workspace_id;
|
||||
sqlx::query!(
|
||||
"UPDATE script SET lock = $1 WHERE hash = $2 AND workspace_id = $3",
|
||||
&content,
|
||||
&job.script_hash.unwrap_or(ScriptHash(0)).0,
|
||||
&job.workspace_id
|
||||
&hash.0,
|
||||
w_id
|
||||
)
|
||||
.execute(db)
|
||||
.await?;
|
||||
|
||||
let deployment_message = job.raw_code.as_ref().map(|x| x.clone());
|
||||
|
||||
if let Err(e) = handle_deployment_metadata(
|
||||
&job.email,
|
||||
&job.created_by,
|
||||
&db,
|
||||
&w_id,
|
||||
DeployedObject::Script { hash, path: script_path.to_string() },
|
||||
deployment_message.clone(),
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(%e, "error handling deployment metadata");
|
||||
}
|
||||
|
||||
if &job.language == &Some(ScriptLang::Python3) {
|
||||
if let Ok(relative_imports) = parse_relative_imports(&raw_code, script_path) {
|
||||
logs.push_str("\n--- RELATIVE IMPORTS ---\n\n");
|
||||
logs.push_str(&relative_imports.join("\n"));
|
||||
if !relative_imports.is_empty() {
|
||||
let mut tx = db.begin().await?;
|
||||
sqlx::query!(
|
||||
"DELETE FROM dependency_map
|
||||
WHERE importer_path = $1 AND importer_kind = 'script'
|
||||
AND workspace_id = $2",
|
||||
script_path,
|
||||
w_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
for import in relative_imports {
|
||||
sqlx::query!(
|
||||
"INSERT INTO dependency_map (workspace_id, importer_path, importer_kind, imported_path)
|
||||
VALUES ($1, $2, 'script', $3)",
|
||||
w_id,
|
||||
script_path,
|
||||
import
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
logs.push_str(&format!("{}\n", import));
|
||||
}
|
||||
tx.commit().await?;
|
||||
}
|
||||
if let Err(e) = trigger_python_dependents_to_recompute_dependencies(
|
||||
w_id,
|
||||
script_path,
|
||||
deployment_message,
|
||||
&job.email,
|
||||
&job.created_by,
|
||||
&job.permissioned_as,
|
||||
db,
|
||||
rsmq,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(%e, "error triggering python dependents to recompute dependencies");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(to_raw_value_owned(
|
||||
json!({ "success": "Successful lock file generation", "lock": content }),
|
||||
))
|
||||
@@ -3108,7 +3271,80 @@ async fn handle_dependency_job(
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_flow_dependency_job(
|
||||
async fn trigger_python_dependents_to_recompute_dependencies<
|
||||
R: rsmq_async::RsmqConnection + Send + Sync + Clone,
|
||||
>(
|
||||
w_id: &str,
|
||||
script_path: &str,
|
||||
deployment_message: Option<String>,
|
||||
email: &str,
|
||||
created_by: &str,
|
||||
permissioned_as: &str,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
rsmq: Option<R>,
|
||||
) -> error::Result<()> {
|
||||
let script_importers = sqlx::query_scalar!(
|
||||
"SELECT importer_path FROM dependency_map
|
||||
WHERE imported_path = $1 AND importer_kind = 'script'
|
||||
AND workspace_id = $2",
|
||||
script_path,
|
||||
w_id
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
for s in script_importers.iter() {
|
||||
let tx: PushIsolationLevel<'_, R> =
|
||||
PushIsolationLevel::IsolatedRoot(db.clone(), rsmq.clone());
|
||||
let r = get_latest_deployed_hash_for_path(db, w_id, s.as_str()).await;
|
||||
if let Ok(r) = r {
|
||||
let (job_uuid, new_tx) = windmill_queue::push(
|
||||
db,
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::Dependencies {
|
||||
path: s.clone(),
|
||||
hash: r.0,
|
||||
language: r.5,
|
||||
dedicated_worker: r.6,
|
||||
deployment_message: deployment_message.clone(),
|
||||
},
|
||||
PushArgs::empty(),
|
||||
&created_by,
|
||||
email,
|
||||
permissioned_as.to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
tracing::info!(
|
||||
"pushed dependency job due to common python path: {job_uuid} for path {path} with hash {hash}",
|
||||
path = s,
|
||||
hash = r.0
|
||||
);
|
||||
new_tx.commit().await?;
|
||||
} else {
|
||||
tracing::error!(
|
||||
"error getting latest deployed hash for path {path}: {err}",
|
||||
path = s,
|
||||
err = r.unwrap_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_flow_dependency_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
job: &QueuedJob,
|
||||
logs: &mut String,
|
||||
mem_peak: &mut i32,
|
||||
@@ -3119,6 +3355,7 @@ async fn handle_flow_dependency_job(
|
||||
worker_dir: &str,
|
||||
base_internal_url: &str,
|
||||
token: &str,
|
||||
rsmq: Option<R>,
|
||||
) -> error::Result<()> {
|
||||
let job_path = job.script_path.clone().ok_or_else(|| {
|
||||
error::Error::InternalErr(
|
||||
@@ -3170,6 +3407,21 @@ async fn handle_flow_dependency_job(
|
||||
)
|
||||
.execute(db)
|
||||
.await?;
|
||||
|
||||
if let Err(e) = handle_deployment_metadata(
|
||||
&job.email,
|
||||
&job.created_by,
|
||||
&db,
|
||||
&job.workspace_id,
|
||||
DeployedObject::Flow { path: job_path },
|
||||
job.raw_code.as_ref().map(|x| x.clone()),
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(%e, "error handling deployment metadata");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3295,22 +3547,11 @@ async fn lock_modules(
|
||||
new_flow_modules.push(e);
|
||||
continue;
|
||||
};
|
||||
// sync with windmill-api/scripts
|
||||
let dependencies = match language {
|
||||
ScriptLang::Python3 => windmill_parser_py_imports::parse_python_imports(
|
||||
&content,
|
||||
&job.workspace_id,
|
||||
&path.clone().unwrap_or_else(|| job_path.to_string()),
|
||||
&db,
|
||||
)
|
||||
.await?
|
||||
.join("\n"),
|
||||
_ => content.clone(),
|
||||
};
|
||||
|
||||
let new_lock = capture_dependency_job(
|
||||
&job.id,
|
||||
&language,
|
||||
&dependencies,
|
||||
&content,
|
||||
logs,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
@@ -3321,7 +3562,7 @@ async fn lock_modules(
|
||||
worker_dir,
|
||||
base_internal_url,
|
||||
token,
|
||||
job.script_path(),
|
||||
&path.clone().unwrap_or_else(|| job_path.to_string()),
|
||||
)
|
||||
.await;
|
||||
match new_lock {
|
||||
@@ -3396,24 +3637,11 @@ async fn lock_modules_app(
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
let dependencies = match language {
|
||||
ScriptLang::Python3 => {
|
||||
windmill_parser_py_imports::parse_python_imports(
|
||||
&content,
|
||||
&job.workspace_id,
|
||||
job_path,
|
||||
&db,
|
||||
)
|
||||
.await?
|
||||
.join("\n")
|
||||
}
|
||||
_ => content.clone(),
|
||||
};
|
||||
logs.push_str("Found lockable inline script. Generating lock...\n");
|
||||
let new_lock = capture_dependency_job(
|
||||
&job.id,
|
||||
&language,
|
||||
&dependencies,
|
||||
&content,
|
||||
logs,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
@@ -3498,7 +3726,7 @@ async fn lock_modules_app(
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_app_dependency_job(
|
||||
async fn handle_app_dependency_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
job: &QueuedJob,
|
||||
logs: &mut String,
|
||||
mem_peak: &mut i32,
|
||||
@@ -3509,6 +3737,7 @@ async fn handle_app_dependency_job(
|
||||
worker_dir: &str,
|
||||
base_internal_url: &str,
|
||||
token: &str,
|
||||
rsmq: Option<R>,
|
||||
) -> error::Result<()> {
|
||||
let job_path = job.script_path.clone().ok_or_else(|| {
|
||||
error::Error::InternalErr(
|
||||
@@ -3558,6 +3787,41 @@ async fn handle_app_dependency_job(
|
||||
sqlx::query!("UPDATE app_version SET value = $1 WHERE id = $2", value, id,)
|
||||
.execute(db)
|
||||
.await?;
|
||||
|
||||
if let Err(e) = handle_deployment_metadata(
|
||||
&job.email,
|
||||
&job.created_by,
|
||||
&db,
|
||||
&job.workspace_id,
|
||||
DeployedObject::App { path: job_path, version: id },
|
||||
job.raw_code.as_ref().map(|x| x.clone()),
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(%e, "error handling deployment metadata");
|
||||
}
|
||||
|
||||
// tx = PushIsolationLevel::Transaction(new_tx);
|
||||
// tx = handle_deployment_metadata(
|
||||
// tx,
|
||||
// &authed,
|
||||
// &db,
|
||||
// &w_id,
|
||||
// DeployedObject::App { path: app.path.clone(), version: v_id },
|
||||
// app.deployment_message,
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
// match tx {
|
||||
// PushIsolationLevel::Transaction(tx) => tx.commit().await?,
|
||||
// _ => {
|
||||
// return Err(Error::InternalErr(
|
||||
// "Expected a transaction here".to_string(),
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -3582,10 +3846,18 @@ async fn capture_dependency_job(
|
||||
) -> error::Result<String> {
|
||||
match job_language {
|
||||
ScriptLang::Python3 => {
|
||||
let reqs = windmill_parser_py_imports::parse_python_imports(
|
||||
job_raw_code,
|
||||
&w_id,
|
||||
script_path,
|
||||
&db,
|
||||
)
|
||||
.await?
|
||||
.join("\n");
|
||||
create_dependencies_dir(job_dir).await;
|
||||
let req: std::result::Result<String, Error> = pip_compile(
|
||||
job_id,
|
||||
job_raw_code,
|
||||
&reqs,
|
||||
logs,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
|
||||
@@ -15,9 +15,8 @@ use std::time::Duration;
|
||||
|
||||
use crate::common::{hash_args, save_in_cache};
|
||||
use crate::js_eval::{eval_timeout, IdContext};
|
||||
use crate::{AuthedClient, PreviousResult, KEEP_JOB_DIR};
|
||||
use crate::{AuthedClient, PreviousResult, SendResult, KEEP_JOB_DIR};
|
||||
use anyhow::Context;
|
||||
use async_recursion::async_recursion;
|
||||
use serde::Serialize;
|
||||
use serde_json::value::RawValue;
|
||||
use serde_json::{json, Value};
|
||||
@@ -70,8 +69,10 @@ pub async fn update_flow_status_after_job_completion<
|
||||
stop_early_override: Option<bool>,
|
||||
rsmq: Option<R>,
|
||||
worker_name: &str,
|
||||
job_completed_tx: Sender<SendResult>,
|
||||
) -> error::Result<()> {
|
||||
// this is manual tailrecursion because async_recursion blows up the stack
|
||||
// todo!();
|
||||
let mut rec = update_flow_status_after_job_completion_internal(
|
||||
db,
|
||||
client,
|
||||
@@ -87,6 +88,7 @@ pub async fn update_flow_status_after_job_completion<
|
||||
false,
|
||||
rsmq.clone(),
|
||||
worker_name,
|
||||
job_completed_tx.clone(),
|
||||
)
|
||||
.await?;
|
||||
while let Some(nrec) = rec {
|
||||
@@ -105,6 +107,7 @@ pub async fn update_flow_status_after_job_completion<
|
||||
nrec.skip_error_handler,
|
||||
rsmq.clone(),
|
||||
worker_name,
|
||||
job_completed_tx.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -125,6 +128,7 @@ pub async fn update_flow_status_after_job_completion<
|
||||
nrec.skip_error_handler,
|
||||
rsmq.clone(),
|
||||
worker_name,
|
||||
job_completed_tx.clone(),
|
||||
)
|
||||
.await?
|
||||
}
|
||||
@@ -167,6 +171,7 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
skip_error_handler: bool,
|
||||
rsmq: Option<R>,
|
||||
worker_name: &str,
|
||||
job_completed_tx: Sender<SendResult>,
|
||||
) -> error::Result<Option<RecUpdateFlowStatusAfterJobCompletion>> {
|
||||
let (
|
||||
should_continue_flow,
|
||||
@@ -575,6 +580,8 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
}
|
||||
|
||||
tx.commit().await?;
|
||||
tracing::debug!(id = %flow_job.id, root_id = %job_root, "flow status updated");
|
||||
|
||||
(
|
||||
should_continue_flow,
|
||||
flow_job,
|
||||
@@ -686,6 +693,7 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
}
|
||||
true
|
||||
} else {
|
||||
tracing::debug!(id = %flow_job.id, "start handle flow");
|
||||
match handle_flow(
|
||||
&flow_job,
|
||||
db,
|
||||
@@ -694,7 +702,7 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
same_worker_tx.clone(),
|
||||
worker_dir,
|
||||
rsmq.clone(),
|
||||
worker_name,
|
||||
job_completed_tx,
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -1037,7 +1045,7 @@ pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
same_worker_tx: Sender<Uuid>,
|
||||
worker_dir: &str,
|
||||
rsmq: Option<R>,
|
||||
worker_name: &str,
|
||||
job_completed_tx: Sender<SendResult>,
|
||||
) -> anyhow::Result<()> {
|
||||
let flow = flow_job
|
||||
.parse_raw_flow()
|
||||
@@ -1056,7 +1064,7 @@ pub async fn handle_flow<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
rsmq,
|
||||
worker_name,
|
||||
job_completed_tx,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
@@ -1089,7 +1097,7 @@ pub struct RawArgs {
|
||||
pub args: Option<Json<HashMap<String, Box<RawValue>>>>,
|
||||
}
|
||||
|
||||
#[async_recursion]
|
||||
// #[async_recursion]
|
||||
// #[instrument(level = "trace", skip_all)]
|
||||
async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>(
|
||||
flow_job: &QueuedJob,
|
||||
@@ -1101,7 +1109,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
same_worker_tx: Sender<Uuid>,
|
||||
worker_dir: &str,
|
||||
rsmq: Option<R>,
|
||||
worker_name: &str,
|
||||
job_completed_tx: Sender<SendResult>,
|
||||
) -> error::Result<()> {
|
||||
let job_root = flow_job
|
||||
.root_job
|
||||
@@ -1122,29 +1130,51 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
|
||||
// if this is an empty module of if the module has already been completed, successfully, update the parent flow
|
||||
if flow.modules.is_empty() || matches!(status_module, FlowStatusModule::Success { .. }) {
|
||||
let r;
|
||||
return update_flow_status_after_job_completion(
|
||||
db,
|
||||
client,
|
||||
flow_job.id,
|
||||
&Uuid::nil(),
|
||||
flow_job.workspace_id.as_str(),
|
||||
true,
|
||||
if flow.modules.is_empty() {
|
||||
r = to_raw_value(&flow_job_args);
|
||||
&r
|
||||
} else {
|
||||
// it has to be an empty for loop event
|
||||
serde_json::from_str("[]").unwrap()
|
||||
},
|
||||
true,
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
None,
|
||||
rsmq,
|
||||
worker_name,
|
||||
)
|
||||
.await;
|
||||
job_completed_tx
|
||||
.send(SendResult::UpdateFlow {
|
||||
flow: flow_job.id,
|
||||
success: true,
|
||||
result: if flow.modules.is_empty() {
|
||||
to_raw_value(&flow_job_args)
|
||||
} else {
|
||||
// it has to be an empty for loop event
|
||||
serde_json::from_str("[]").unwrap()
|
||||
},
|
||||
stop_early_override: None,
|
||||
w_id: flow_job.workspace_id.clone(),
|
||||
worker_dir: worker_dir.to_string(),
|
||||
token: client.token.clone(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::InternalErr(format!(
|
||||
"error sending update flow message to job completed channel: {e}"
|
||||
))
|
||||
})?;
|
||||
// let r;
|
||||
// return update_flow_status_after_job_completion(
|
||||
// db,
|
||||
// client,
|
||||
// flow_job.id,
|
||||
// &Uuid::nil(),
|
||||
// flow_job.workspace_id.as_str(),
|
||||
// true,
|
||||
// if flow.modules.is_empty() {
|
||||
// r = to_raw_value(&flow_job_args);
|
||||
// &r
|
||||
// } else {
|
||||
// // it has to be an empty for loop event
|
||||
// serde_json::from_str("[]").unwrap()
|
||||
// },
|
||||
// true,
|
||||
// same_worker_tx,
|
||||
// worker_dir,
|
||||
// None,
|
||||
// rsmq,
|
||||
// worker_name,
|
||||
// )
|
||||
// .await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let arc_flow_job_args = Arc::new(flow_job_args.clone());
|
||||
@@ -1166,25 +1196,45 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
flow_job.id
|
||||
).fetch_one(db).await?.unwrap_or(0);
|
||||
if count > 0 {
|
||||
return update_flow_status_after_job_completion(
|
||||
db,
|
||||
client,
|
||||
flow_job.id,
|
||||
&Uuid::nil(),
|
||||
flow_job.workspace_id.as_str(),
|
||||
true,
|
||||
serde_json::from_str(
|
||||
"\"not allowed to overlap, scheduling next iteration\"",
|
||||
)
|
||||
.unwrap(),
|
||||
true,
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
Some(true),
|
||||
rsmq,
|
||||
worker_name,
|
||||
)
|
||||
.await;
|
||||
job_completed_tx
|
||||
.send(SendResult::UpdateFlow {
|
||||
flow: flow_job.id,
|
||||
success: true,
|
||||
result: serde_json::from_str(
|
||||
"\"not allowed to overlap, scheduling next iteration\"",
|
||||
)
|
||||
.unwrap(),
|
||||
stop_early_override: Some(true),
|
||||
w_id: flow_job.workspace_id.clone(),
|
||||
worker_dir: worker_dir.to_string(),
|
||||
token: client.token.clone(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::InternalErr(format!(
|
||||
"error sending update flow message to job completed channel: {e}"
|
||||
))
|
||||
})?;
|
||||
// return update_flow_status_after_job_completion(
|
||||
// db,
|
||||
// client,
|
||||
// flow_job.id,
|
||||
// &Uuid::nil(),
|
||||
// flow_job.workspace_id.as_str(),
|
||||
// true,
|
||||
// serde_json::from_str(
|
||||
// "\"not allowed to overlap, scheduling next iteration\"",
|
||||
// )
|
||||
// .unwrap(),
|
||||
// true,
|
||||
// same_worker_tx,
|
||||
// worker_dir,
|
||||
// Some(true),
|
||||
// rsmq,
|
||||
// worker_name,
|
||||
// )
|
||||
// .await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1199,22 +1249,40 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
)
|
||||
.await?;
|
||||
if skip {
|
||||
return update_flow_status_after_job_completion(
|
||||
db,
|
||||
client,
|
||||
flow_job.id,
|
||||
&Uuid::nil(),
|
||||
flow_job.workspace_id.as_str(),
|
||||
true,
|
||||
serde_json::from_str("\"stopped early\"").unwrap(),
|
||||
true,
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
Some(true),
|
||||
rsmq,
|
||||
worker_name,
|
||||
)
|
||||
.await;
|
||||
job_completed_tx
|
||||
.send(SendResult::UpdateFlow {
|
||||
flow: flow_job.id,
|
||||
success: true,
|
||||
result: serde_json::from_str("\"stopped early\"").unwrap(),
|
||||
stop_early_override: Some(true),
|
||||
w_id: flow_job.workspace_id.clone(),
|
||||
worker_dir: worker_dir.to_string(),
|
||||
token: client.token.clone(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::InternalErr(format!(
|
||||
"error sending update flow message to job completed channel: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
// return update_flow_status_after_job_completion(
|
||||
// db,
|
||||
// client,
|
||||
// flow_job.id,
|
||||
// &Uuid::nil(),
|
||||
// flow_job.workspace_id.as_str(),
|
||||
// true,
|
||||
// serde_json::from_str("\"stopped early\"").unwrap(),
|
||||
// true,
|
||||
// same_worker_tx,
|
||||
// worker_dir,
|
||||
// Some(true),
|
||||
// rsmq,
|
||||
// worker_name,
|
||||
// )
|
||||
// .await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1436,22 +1504,39 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
.await?;
|
||||
if flow_job.is_flow_step {
|
||||
if let Some(parent_job) = flow_job.parent_job {
|
||||
update_flow_status_after_job_completion(
|
||||
db,
|
||||
client,
|
||||
parent_job,
|
||||
&flow_job.id,
|
||||
&flow_job.workspace_id,
|
||||
true,
|
||||
&to_raw_value(&result),
|
||||
false,
|
||||
same_worker_tx.clone(),
|
||||
&worker_dir,
|
||||
None,
|
||||
rsmq,
|
||||
worker_name,
|
||||
)
|
||||
.await?;
|
||||
job_completed_tx
|
||||
.send(SendResult::UpdateFlow {
|
||||
flow: parent_job,
|
||||
success: true,
|
||||
result: to_raw_value(&result),
|
||||
stop_early_override: Some(true),
|
||||
w_id: flow_job.workspace_id.clone(),
|
||||
worker_dir: worker_dir.to_string(),
|
||||
token: client.token.clone(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::InternalErr(format!(
|
||||
"error sending update flow message to job completed channel: {e}"
|
||||
))
|
||||
})?;
|
||||
// update_flow_status_after_job_completion(
|
||||
// db,
|
||||
// client,
|
||||
// parent_job,
|
||||
// &flow_job.id,
|
||||
// &flow_job.workspace_id,
|
||||
// true,
|
||||
// &to_raw_value(&result),
|
||||
// false,
|
||||
// same_worker_tx.clone(),
|
||||
// &worker_dir,
|
||||
// None,
|
||||
// rsmq,
|
||||
// worker_name,
|
||||
// )
|
||||
// .await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
@@ -1689,6 +1774,7 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
_ => Ok(flow_job_args),
|
||||
}
|
||||
};
|
||||
tracing::debug!(id = %flow_job.id, root_id = %job_root, "flow job args computed");
|
||||
|
||||
let next_flow_transform = compute_next_flow_transform(
|
||||
arc_flow_job_args.clone(),
|
||||
@@ -1889,6 +1975,8 @@ async fn push_next_flow_job<R: rsmq_async::RsmqConnection + Send + Sync + Clone>
|
||||
)
|
||||
.await?;
|
||||
|
||||
tracing::debug!(id = %flow_job.id, root_id = %job_root, "pushed next flow job: {uuid}");
|
||||
|
||||
if let FlowModuleValue::ForloopFlow { parallelism: Some(p), .. } = &module.value {
|
||||
if i as u16 >= *p {
|
||||
sqlx::query!(
|
||||
@@ -2211,6 +2299,8 @@ async fn compute_next_flow_transform(
|
||||
))
|
||||
};
|
||||
let delete_after_use = module.delete_after_use.unwrap_or(false);
|
||||
|
||||
tracing::debug!(id = %flow_job.id, "computing next flow transform for {:?}", &module.value);
|
||||
match &module.value {
|
||||
FlowModuleValue::Identity => trivial_next_job(JobPayload::Identity),
|
||||
FlowModuleValue::Flow { path, .. } => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
|
||||
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
|
||||
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
|
||||
|
||||
export const VERSION = "v1.235.0";
|
||||
export const VERSION = "v1.236.2";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
@@ -31,7 +31,7 @@ addEventListener("error", (event) => {
|
||||
}
|
||||
});
|
||||
|
||||
export const VERSION = "v1.235.0";
|
||||
export const VERSION = "v1.236.2";
|
||||
|
||||
let command: any = new Command()
|
||||
.name("wmill")
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.235.0",
|
||||
"version": "1.236.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.235.0",
|
||||
"version": "1.236.2",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.235.0",
|
||||
"version": "1.236.2",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
if (currentId === id || allowConcurentRequests) {
|
||||
job = { ...maybe_job, id }
|
||||
await tick()
|
||||
if ('error' in job.result ?? {}) {
|
||||
if (typeof job?.result == 'object' && 'error' in (job?.result ?? {})) {
|
||||
callbacks?.error()
|
||||
dispatch('doneError', {
|
||||
id,
|
||||
|
||||
@@ -373,7 +373,6 @@
|
||||
): Promise<string | undefined> {
|
||||
try {
|
||||
if (cancellableRun && !dynamicArgsOverride) {
|
||||
console.log('runComponent cancellable Run')
|
||||
await cancellableRun()
|
||||
} else {
|
||||
console.log('Run component')
|
||||
@@ -530,7 +529,6 @@
|
||||
|
||||
onMount(() => {
|
||||
cancellableRun = (inlineScript?: InlineScript, setRunnableJobEditorPanel?: boolean) => {
|
||||
console.log('cancellableRun', inlineScript)
|
||||
let rejectCb: (err: Error) => void
|
||||
let p: Partial<CancelablePromise<any>> = new Promise<void>((resolve, reject) => {
|
||||
rejectCb = reject
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
on:done
|
||||
on:doneError
|
||||
on:cancel
|
||||
on:setResult={() => (initializing = false)}
|
||||
on:resultSet={() => (initializing = false)}
|
||||
on:success={() => handleSideEffect(true)}
|
||||
on:handleError={(e) => handleSideEffect(false, e.detail)}
|
||||
{outputs}
|
||||
|
||||
@@ -710,6 +710,7 @@
|
||||
type="text"
|
||||
placeholder="App summary"
|
||||
class="text-sm w-full font-semibold"
|
||||
on:keydown|stopPropagation
|
||||
bind:value={$summary}
|
||||
on:keyup={() => {
|
||||
if (appPath == '' && $summary?.length > 0 && !dirtyPath) {
|
||||
@@ -766,6 +767,7 @@
|
||||
placeholder="App summary"
|
||||
class="text-sm w-full"
|
||||
bind:value={$summary}
|
||||
on:keydown|stopPropagation
|
||||
on:keyup={() => {
|
||||
if (appPath == '' && $summary?.length > 0 && !dirtyPath) {
|
||||
path?.setName(
|
||||
@@ -1180,6 +1182,7 @@
|
||||
placeholder="App summary"
|
||||
class="text-sm w-full font-semibold"
|
||||
bind:value={$summary}
|
||||
on:keydown|stopPropagation
|
||||
/>
|
||||
</div>
|
||||
<div class="flex gap-4 items-center justify-center">
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
<slot />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search workspace scripts"
|
||||
placeholder="Search workspace flows"
|
||||
on:keydown|stopPropagation
|
||||
bind:value={filter}
|
||||
class="text-2xl grow mb-4"
|
||||
/>
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<slot />
|
||||
<input
|
||||
type="text"
|
||||
on:keydown|stopPropagation
|
||||
placeholder="Search workspace scripts"
|
||||
bind:value={filter}
|
||||
class="text-2xl grow mb-4"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
export let placement: any = 'bottom-start'
|
||||
export let justifyEnd: boolean = false
|
||||
export let lightMode: boolean = false
|
||||
|
||||
const [floatingRef, floatingContent] = createFloatingActions({
|
||||
strategy: 'fixed',
|
||||
@@ -35,7 +36,10 @@
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<MenuItems
|
||||
class="border w-56 origin-top-right rounded-md bg-surface shadow-md focus:outline-none"
|
||||
class={twMerge(
|
||||
'border w-56 origin-top-right rounded-md shadow-md focus:outline-none',
|
||||
lightMode ? 'bg-surface-inverse' : 'bg-surface'
|
||||
)}
|
||||
>
|
||||
<div class="my-1">
|
||||
<slot />
|
||||
|
||||
@@ -29,7 +29,11 @@
|
||||
$: $workspaceStore && kind && loadItems()
|
||||
|
||||
async function loadItems(): Promise<void> {
|
||||
items = await ScriptService.listScripts({ workspace: $workspaceStore!, kinds: kind, isTemplate })
|
||||
items = await ScriptService.listScripts({
|
||||
workspace: $workspaceStore!,
|
||||
kinds: kind,
|
||||
isTemplate
|
||||
})
|
||||
}
|
||||
|
||||
let ownerFilter: string | undefined = undefined
|
||||
@@ -58,6 +62,7 @@
|
||||
|
||||
<input
|
||||
type="text"
|
||||
on:keydown|stopPropagation
|
||||
placeholder="Search Workspace Scripts"
|
||||
bind:value={filter}
|
||||
class="text-2xl grow"
|
||||
|
||||
@@ -83,9 +83,7 @@
|
||||
<Tabs bind:selected={viewTab}>
|
||||
<Tab size="xs" value="result">Result</Tab>
|
||||
<Tab size="xs" value="logs">Logs</Tab>
|
||||
{#if job?.job_kind == 'dependencies'}
|
||||
<Tab size="xs" value="code">Code</Tab>
|
||||
{:else if job?.job_kind == 'preview'}
|
||||
{#if job?.job_kind == 'preview'}
|
||||
<Tab size="xs" value="code">Code</Tab>
|
||||
{/if}
|
||||
</Tabs>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
import { clearStores } from '$lib/storeUtils'
|
||||
import { goto } from '$app/navigation'
|
||||
import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
$: mainMenuLinks = [
|
||||
{ label: 'Home', href: '/', icon: Home },
|
||||
@@ -150,20 +151,24 @@
|
||||
]
|
||||
|
||||
export let isCollapsed: boolean = false
|
||||
export let noGap: boolean = false
|
||||
|
||||
let leaveWorkspaceModal = false
|
||||
</script>
|
||||
|
||||
<nav
|
||||
class="grow flex flex-col overflow-x-hidden scrollbar-hidden px-2 md:pb-2 gap-16 justify-between"
|
||||
class={twMerge(
|
||||
'grow flex flex-col overflow-x-hidden scrollbar-hidden px-2 md:pb-2 justify-between',
|
||||
noGap ? 'gap-0' : 'gap-16'
|
||||
)}
|
||||
>
|
||||
<div class="space-y-1 pt-4 mb-6 md:mb-10">
|
||||
<div class={twMerge('space-y-1 pt-4 ', noGap ? 'md:mb-0 mb-0' : 'mb-6 md:mb-10')}>
|
||||
{#each mainMenuLinks as menuLink (menuLink.href ?? menuLink.label)}
|
||||
<MenuLink class="!text-xs" {...menuLink} {isCollapsed} />
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex flex-col h-full justify-end">
|
||||
<div class="space-y-0.5 mb-6 md:mb-10">
|
||||
<div class={twMerge('space-y-0.5 mb-6 md:mb-10', noGap ? 'md:mb-0 mb-0' : 'mb-6 md:mb-10')}>
|
||||
<UserMenu {isCollapsed} />
|
||||
{#each secondaryMenuLinks as menuLink (menuLink.href ?? menuLink.label)}
|
||||
{#if menuLink.subItems}
|
||||
@@ -176,7 +181,7 @@
|
||||
<div class="py-1" role="none">
|
||||
{#if subItem?.['action']}
|
||||
<button
|
||||
class="{subItem['class']} px-4 py-2 !text-2xs"
|
||||
class="text-secondary block px-4 py-2 text-xs hover:bg-surface-hover hover:text-primary"
|
||||
on:click={subItem?.['action']}
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
@@ -190,7 +195,9 @@
|
||||
{:else}
|
||||
<a
|
||||
href={subItem.href}
|
||||
class="text-secondary block px-4 py-2 text-2xs hover:bg-surface-hover hover:text-primary"
|
||||
class={twMerge(
|
||||
'text-secondary block px-4 py-2 text-2xs hover:bg-surface-hover hover:text-primary'
|
||||
)}
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
WorkspaceService
|
||||
} from '$lib/gen'
|
||||
import { classNames } from '$lib/utils'
|
||||
|
||||
import { slide } from 'svelte/transition'
|
||||
import WorkspaceMenu from '$lib/components/sidebar/WorkspaceMenu.svelte'
|
||||
import SidebarContent from '$lib/components/sidebar/SidebarContent.svelte'
|
||||
import {
|
||||
@@ -34,9 +34,11 @@
|
||||
import { SUPERADMIN_SETTINGS_HASH, USER_SETTINGS_HASH } from '$lib/components/sidebar/settings'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { syncTutorialsTodos } from '$lib/tutorialUtils'
|
||||
import { ArrowLeft } from 'lucide-svelte'
|
||||
import { ArrowLeft, Menu as MenuIcon } from 'lucide-svelte'
|
||||
import { getUserExt } from '$lib/user'
|
||||
import { workspacedOpenai } from '$lib/components/copilot/lib'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
OpenAPI.WITH_CREDENTIALS = true
|
||||
let menuOpen = false
|
||||
@@ -201,6 +203,8 @@
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
let menuSlide: boolean = false
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth />
|
||||
@@ -219,39 +223,206 @@
|
||||
<SuperadminSettings bind:this={superadminSettings} />
|
||||
{/if}
|
||||
<div>
|
||||
<div
|
||||
class={classNames(
|
||||
'relative md:hidden',
|
||||
menuOpen ? 'z-40' : 'pointer-events-none',
|
||||
devOnly ? 'hidden' : ''
|
||||
)}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
{#if !$userStore?.operator}
|
||||
<div
|
||||
class={classNames(
|
||||
'fixed inset-0 dark:bg-[#1e232e] bg-[#202125] dark:bg-opacity-75 bg-opacity-75 transition-opacity ease-linear duration-300 z-40 !dark',
|
||||
menuOpen ? 'opacity-100' : 'opacity-0'
|
||||
'relative md:hidden',
|
||||
menuOpen ? 'z-40' : 'pointer-events-none',
|
||||
devOnly ? 'hidden' : ''
|
||||
)}
|
||||
/>
|
||||
|
||||
<div class="fixed inset-0 flex z-40">
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'relative flex-1 flex flex-col max-w-min w-full bg-surface transition ease-in-out duration-300 transform',
|
||||
menuOpen ? 'translate-x-0' : '-translate-x-full'
|
||||
'fixed inset-0 dark:bg-[#1e232e] bg-[#202125] dark:bg-opacity-75 bg-opacity-75 transition-opacity ease-linear duration-300 z-40 !dark',
|
||||
|
||||
menuOpen ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
|
||||
<div class="fixed inset-0 flex z-40">
|
||||
<div
|
||||
class={classNames(
|
||||
'relative flex-1 flex flex-col max-w-min w-full bg-surface transition ease-in-out duration-300 transform',
|
||||
menuOpen ? 'translate-x-0' : '-translate-x-full'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'absolute top-0 right-0 -mr-12 pt-2 ease-in-out duration-300',
|
||||
menuOpen ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => {
|
||||
menuOpen = !menuOpen
|
||||
}}
|
||||
class="ml-1 flex items-center justify-center h-8 w-8 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white border border-white"
|
||||
>
|
||||
<svg
|
||||
class="h-6 w-6 text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dark:bg-[#1e232e] bg-[#202125] h-full !dark">
|
||||
<div
|
||||
class="flex gap-x-2 flex-shrink-0 p-4 font-semibold text-gray-200 w-10"
|
||||
class:w-40={!isCollapsed}
|
||||
>
|
||||
<WindmillIcon white={true} height="20px" width="20px" />
|
||||
{#if !isCollapsed}Windmill{/if}
|
||||
</div>
|
||||
|
||||
<div class="px-2 py-4 space-y-2 border-y border-gray-500">
|
||||
<WorkspaceMenu />
|
||||
<FavoriteMenu {favoriteLinks} />
|
||||
</div>
|
||||
|
||||
<SidebarContent {isCollapsed} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="sidebar"
|
||||
class={classNames(
|
||||
'hidden md:flex md:flex-col md:fixed md:inset-y-0 transition-all ease-in-out duration-200 shadow-md z-40 ',
|
||||
isCollapsed ? 'md:w-12' : 'md:w-40',
|
||||
devOnly ? '!hidden' : ''
|
||||
)}
|
||||
>
|
||||
<div
|
||||
class="flex-1 flex flex-col min-h-0 h-screen shadow-lg dark:bg-[#1e232e] bg-[#202125] !dark"
|
||||
>
|
||||
<button
|
||||
on:click={() => {
|
||||
goto('/')
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="flex-row flex-shrink-0 px-3.5 py-3.5 text-opacity-70 h-12 flex items-center gap-1.5"
|
||||
class:w-40={!isCollapsed}
|
||||
>
|
||||
<div class:mr-1={!isCollapsed}>
|
||||
<WindmillIcon white={true} height="20px" width="20px" />
|
||||
</div>
|
||||
{#if !isCollapsed}
|
||||
<div class="text-sm mt-0.5 text-white"> Windmill </div>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
<div class="px-2 py-4 space-y-2 border-y border-gray-700">
|
||||
<WorkspaceMenu {isCollapsed} />
|
||||
<FavoriteMenu {favoriteLinks} {isCollapsed} />
|
||||
</div>
|
||||
|
||||
<SidebarContent {isCollapsed} />
|
||||
|
||||
<div class="flex-shrink-0 flex px-4 pb-3.5">
|
||||
<button
|
||||
on:click={() => {
|
||||
isCollapsed = !isCollapsed
|
||||
}}
|
||||
>
|
||||
<ArrowLeft
|
||||
size={16}
|
||||
class={classNames(
|
||||
'flex-shrink-0 h-4 w-4 transition-all ease-in-out duration-200 text-white',
|
||||
isCollapsed ? 'rotate-180' : 'rotate-0'
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="relative">
|
||||
<div class="absolute top-2 left-2 z-50">
|
||||
<Button
|
||||
color="dark"
|
||||
on:click={() => {
|
||||
menuSlide = true
|
||||
}}
|
||||
startIcon={{ icon: MenuIcon }}
|
||||
iconOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if menuSlide}
|
||||
<div
|
||||
id="sidebar"
|
||||
class={classNames(
|
||||
'hidden md:flex md:flex-col md:fixed md:inset-y-0 transition-all ease-in-out duration-200 shadow-md z-40 ',
|
||||
isCollapsed ? 'md:w-12' : 'md:w-40',
|
||||
devOnly ? '!hidden' : '',
|
||||
'z5000'
|
||||
)}
|
||||
transition:slide={{ axis: 'x', duration: 100 }}
|
||||
>
|
||||
<div
|
||||
class="flex-1 flex flex-col min-h-0 h-screen shadow-lg dark:bg-[#1e232e] bg-[#202125] !dark"
|
||||
>
|
||||
<button
|
||||
on:click={() => {
|
||||
goto('/')
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="flex-row flex-shrink-0 px-3.5 py-3.5 text-opacity-70 h-12 flex items-center gap-1.5"
|
||||
class:w-40={!isCollapsed}
|
||||
>
|
||||
<div class:mr-1={!isCollapsed}>
|
||||
<WindmillIcon white={true} height="20px" width="20px" />
|
||||
</div>
|
||||
{#if !isCollapsed}
|
||||
<div class="text-sm mt-0.5 text-white"> Windmill </div>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
<div class="px-2 py-4 space-y-2 border-y border-gray-700">
|
||||
<WorkspaceMenu {isCollapsed} />
|
||||
<FavoriteMenu {favoriteLinks} {isCollapsed} />
|
||||
</div>
|
||||
|
||||
<SidebarContent {isCollapsed} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<div
|
||||
class={classNames(
|
||||
'fixed inset-0 dark:bg-[#1e232e] bg-[#202125] dark:bg-opacity-75 bg-opacity-75 transition-opacity ease-linear duration-300 !dark',
|
||||
menuSlide ? 'opacity-100 z-40' : 'opacity-0'
|
||||
)}
|
||||
>
|
||||
<div class={twMerge('fixed inset-0 flex ', menuSlide ? ' z-40' : '-z-0')}>
|
||||
<div
|
||||
class={classNames(
|
||||
'relative flex-1 flex flex-col max-w-min w-full bg-surface transition ease-in-out duration-100 transform',
|
||||
menuSlide ? 'translate-x-0' : '-translate-x-full'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'absolute top-0 right-0 -mr-12 pt-2 ease-in-out duration-300',
|
||||
menuOpen ? 'opacity-100' : 'opacity-0'
|
||||
'absolute top-0 right-0 -mr-12 pt-2 ease-in-out duration-100',
|
||||
menuSlide ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => {
|
||||
menuOpen = !menuOpen
|
||||
menuSlide = !menuSlide
|
||||
}}
|
||||
class="ml-1 flex items-center justify-center h-8 w-8 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white border border-white"
|
||||
>
|
||||
@@ -287,64 +458,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="sidebar"
|
||||
class={classNames(
|
||||
'hidden md:flex md:flex-col md:fixed md:inset-y-0 transition-all ease-in-out duration-200 shadow-md z-40 ',
|
||||
isCollapsed ? 'md:w-12' : 'md:w-40',
|
||||
devOnly ? '!hidden' : ''
|
||||
)}
|
||||
>
|
||||
<div
|
||||
class="flex-1 flex flex-col min-h-0 h-screen shadow-lg dark:bg-[#1e232e] bg-[#202125] !dark"
|
||||
>
|
||||
<button
|
||||
on:click={() => {
|
||||
goto('/')
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="flex-row flex-shrink-0 px-3.5 py-3.5 text-opacity-70 h-12 flex items-center gap-1.5"
|
||||
class:w-40={!isCollapsed}
|
||||
>
|
||||
<div class:mr-1={!isCollapsed}>
|
||||
<WindmillIcon white={true} height="20px" width="20px" />
|
||||
</div>
|
||||
{#if !isCollapsed}
|
||||
<div class="text-sm mt-0.5 text-white"> Windmill </div>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
<div class="px-2 py-4 space-y-2 border-y border-gray-700">
|
||||
<WorkspaceMenu {isCollapsed} />
|
||||
<FavoriteMenu {favoriteLinks} {isCollapsed} />
|
||||
</div>
|
||||
|
||||
<SidebarContent {isCollapsed} />
|
||||
|
||||
<div class="flex-shrink-0 flex px-4 pb-3.5">
|
||||
<button
|
||||
on:click={() => {
|
||||
isCollapsed = !isCollapsed
|
||||
}}
|
||||
>
|
||||
<ArrowLeft
|
||||
size={16}
|
||||
class={classNames(
|
||||
'flex-shrink-0 h-4 w-4 transition-all ease-in-out duration-200 text-white',
|
||||
isCollapsed ? 'rotate-180' : 'rotate-0'
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="content"
|
||||
class={classNames(
|
||||
'w-full flex flex-col flex-1 h-full',
|
||||
devOnly ? '!pl-0' : isCollapsed ? 'md:pl-12' : 'md:pl-40',
|
||||
devOnly || $userStore?.operator ? '!pl-0' : isCollapsed ? 'md:pl-12' : 'md:pl-40',
|
||||
'transition-all ease-in-out duration-200'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -59,8 +59,11 @@
|
||||
/>
|
||||
{#if can_write && !hideEditBtn}
|
||||
<div id="app-edit-btn" class="absolute bottom-4 z-50 right-4">
|
||||
<Button size="sm" startIcon={{ icon: Pen }} variant="border" href="/apps/edit/{app.path}"
|
||||
>Edit</Button
|
||||
<Button
|
||||
size="sm"
|
||||
startIcon={{ icon: Pen }}
|
||||
variant="border"
|
||||
href="/apps/edit/{app.path}?nodraft=true">Edit</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -512,9 +512,7 @@
|
||||
<Tab value="result">Result</Tab>
|
||||
<Tab value="logs">Logs</Tab>
|
||||
<Tab value="stats">Metrics</Tab>
|
||||
{#if job?.job_kind == 'dependencies'}
|
||||
<Tab value="code">Code</Tab>
|
||||
{:else if job?.job_kind == 'preview'}
|
||||
{#if job?.job_kind == 'preview'}
|
||||
<Tab value="code">Code</Tab>
|
||||
{/if}
|
||||
</Tabs>
|
||||
|
||||
@@ -136,12 +136,13 @@
|
||||
workspace.
|
||||
</p>
|
||||
{/if}
|
||||
{#each nonAdminWorkspaces as workspace}
|
||||
{#each nonAdminWorkspaces as workspace (workspace.id)}
|
||||
<label class="block pb-2">
|
||||
<button
|
||||
class="block w-full mx-auto py-1 px-2 rounded-md border
|
||||
shadow-sm text-sm font-normal mt-1 hover:ring-1 hover:ring-indigo-300"
|
||||
on:click={async () => {
|
||||
workspaceStore.set(undefined)
|
||||
workspaceStore.set(workspace.id)
|
||||
loading = true
|
||||
await goto(rd ?? '/')
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
): Promise<void> {
|
||||
if (!emptyString(gitRepoResourcePath)) {
|
||||
gitSyncSettings = {
|
||||
script_path: 'hub/7923/sync-script-to-git-repo-windmill',
|
||||
script_path: 'hub/7924/sync-script-to-git-repo-windmill',
|
||||
git_repo_resource_path: `$res:${gitRepoResourcePath.replace('$res:', '')}`,
|
||||
use_individual_branch: useIndividualBranch
|
||||
}
|
||||
@@ -328,7 +328,7 @@
|
||||
}
|
||||
let jobId = await JobService.runScriptByPath({
|
||||
workspace: $workspaceStore!,
|
||||
path: 'hub/7846/git-repo-test-read-write-windmill',
|
||||
path: 'hub/7925/git-repo-test-read-write-windmill',
|
||||
requestBody: {
|
||||
repo_url_resource_path: gitSyncSettings.git_repo_resource_path.replace('$res:', '')
|
||||
}
|
||||
@@ -389,8 +389,11 @@
|
||||
<Tab size="xs" value="users">
|
||||
<div class="flex gap-2 items-center my-1"> Users</div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="git_sync">
|
||||
<div class="flex gap-2 items-center my-1">Git Sync</div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="deploy_to">
|
||||
<div class="flex gap-2 items-center my-1"> Dev/Staging/Prod</div>
|
||||
<div class="flex gap-2 items-center my-1">Deployment UI</div>
|
||||
</Tab>
|
||||
{#if WORKSPACE_SHOW_SLACK_CMD}
|
||||
<Tab size="xs" value="slack">
|
||||
@@ -416,9 +419,6 @@
|
||||
<Tab size="xs" value="windmill_lfs">
|
||||
<div class="flex gap-2 items-center my-1"> S3 Storage </div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="git_sync">
|
||||
<div class="flex gap-2 items-center my-1"> Git sync </div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="export_delete">
|
||||
<div class="flex gap-2 items-center my-1"> Delete Workspace </div>
|
||||
</Tab>
|
||||
@@ -705,7 +705,7 @@
|
||||
/>
|
||||
</div>
|
||||
{:else if tab == 'windmill_lfs'}
|
||||
<PageHeader title="Windmill Large File Storage" primary={false} />
|
||||
<PageHeader title="S3 Storage" primary={false} />
|
||||
{#if !$enterpriseLicense}
|
||||
<Alert type="info" title="S3 storage it limited to 20 files in Windmill CE">
|
||||
Windmill S3 bucket browser will not work for buckets containing more than 20 files.
|
||||
@@ -755,6 +755,7 @@
|
||||
<Alert type="warning" title="Syncing workspace to Git is an EE feature">
|
||||
Automatically saving scripts to a Git repository on each deploy is a Windmill EE feature.
|
||||
</Alert>
|
||||
<div class="mb-1" />
|
||||
{/if}
|
||||
<Alert
|
||||
type="info"
|
||||
@@ -767,22 +768,24 @@
|
||||
Filtering out certain sensitive folders from the sync will be available soon.
|
||||
</Alert>
|
||||
<div class="flex mt-5 mb-1 gap-1">
|
||||
{#key s3ResourceInitialPath}
|
||||
<ResourcePicker
|
||||
resourceType="git_repository"
|
||||
initialValue={gitSyncSettings.git_repo_resource_path}
|
||||
on:change={(ev) => {
|
||||
editWindmillGitSyncSettings(ev.detail, gitSyncSettings.use_individual_branch)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
disabled={emptyString(gitSyncSettings.script_path)}
|
||||
btnClasses="w-32 text-center"
|
||||
color="dark"
|
||||
on:click={() => runGitSyncTestJob()}
|
||||
size="xs">Test connection</Button
|
||||
>
|
||||
{/key}
|
||||
{#if gitSyncSettings}
|
||||
{#key gitSyncSettings}
|
||||
<ResourcePicker
|
||||
resourceType="git_repository"
|
||||
initialValue={gitSyncSettings?.git_repo_resource_path}
|
||||
on:change={(ev) => {
|
||||
editWindmillGitSyncSettings(ev.detail, gitSyncSettings?.use_individual_branch)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
disabled={emptyString(gitSyncSettings?.script_path)}
|
||||
btnClasses="w-32 text-center"
|
||||
color="dark"
|
||||
on:click={() => runGitSyncTestJob()}
|
||||
size="xs">Test connection</Button
|
||||
>
|
||||
{/key}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex mb-5 text-normal text-2xs gap-1">
|
||||
{#if gitSyncTestJob !== undefined}
|
||||
@@ -801,18 +804,20 @@
|
||||
</div>
|
||||
|
||||
<div class="flex mt-5 mb-1 gap-1">
|
||||
<Toggle
|
||||
disabled={emptyString(gitSyncSettings.git_repo_resource_path)}
|
||||
bind:checked={gitSyncSettings.use_individual_branch}
|
||||
on:change={(ev) => {
|
||||
editWindmillGitSyncSettings(gitSyncSettings.git_repo_resource_path, ev.detail)
|
||||
}}
|
||||
options={{
|
||||
right: 'Create one branch per deployed script/flow/app',
|
||||
rightTooltip:
|
||||
"If set, Windmill will create a unique branch per script/flow/app being pushed, prefixed with 'wm_deploy/'."
|
||||
}}
|
||||
/>
|
||||
{#if gitSyncSettings}
|
||||
<Toggle
|
||||
disabled={emptyString(gitSyncSettings?.git_repo_resource_path)}
|
||||
bind:checked={gitSyncSettings.use_individual_branch}
|
||||
on:change={(ev) => {
|
||||
editWindmillGitSyncSettings(gitSyncSettings.git_repo_resource_path, ev.detail)
|
||||
}}
|
||||
options={{
|
||||
right: 'Create one branch per deployed script/flow/app',
|
||||
rightTooltip:
|
||||
"If set, Windmill will create a unique branch per script/flow/app being pushed, prefixed with 'wm_deploy/'."
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="bg-surface-disabled p-4 rounded-md flex flex-col gap-1">
|
||||
@@ -841,12 +846,12 @@
|
||||
|
||||
<pre class="overflow-auto max-h-screen"
|
||||
><code
|
||||
>> wmill workspace add WORKSPACE_NAME WORKSPACE_ID WINDMILL_URL
|
||||
> echo 'u/' > .wmillignore
|
||||
> wmill sync pull --raw --skip-variables --skip-secrets --skip-resources
|
||||
> git add -A
|
||||
> git commit -m 'Initial commit'
|
||||
> git push</code
|
||||
>wmill workspace add {$workspaceStore} {$workspaceStore} {`${$page.url.protocol}//${$page.url.hostname}/`}
|
||||
echo 'u/' > .wmillignore
|
||||
wmill sync pull --raw --skip-variables --skip-secrets --skip-resources
|
||||
git add -A
|
||||
git commit -m 'Initial commit'
|
||||
git push</code
|
||||
></pre
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.235.0"
|
||||
wmill_pg = ">=1.235.0"
|
||||
wmill = ">=1.236.2"
|
||||
wmill_pg = ">=1.236.2"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.235.0
|
||||
version: 1.236.2
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.235.0"
|
||||
version = "1.236.2"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.235.0",
|
||||
"version": "1.236.2",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.235.0
|
||||
1.236.2
|
||||
|
||||
Reference in New Issue
Block a user