mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-16 16:02:33 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1aac43977e | ||
|
|
05d4d6dd48 |
@@ -1,7 +1 @@
|
||||
use flake
|
||||
|
||||
# Per-worktree overrides (ports, DATABASE_URL, etc.) written by webmux/workmux
|
||||
# post-create hooks. Must come after `use flake` so they take precedence over
|
||||
# the flake's defaults.
|
||||
# shellcheck source=/dev/null
|
||||
[ -f .env.local ] && source .env.local
|
||||
|
||||
@@ -212,59 +212,6 @@ jobs:
|
||||
${{ steps.extract-ee.outputs.destination }}/*
|
||||
${{ steps.extract-duckdb-ffi-internal.outputs.destination }}/*
|
||||
|
||||
attach_ee_debug_to_release:
|
||||
needs: [build_ee]
|
||||
runs-on: ubicloud
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [linux/amd64, linux/arm64]
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
arch: amd64
|
||||
- platform: linux/arm64
|
||||
arch: arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- name: Read EE repo commit hash
|
||||
run: |
|
||||
echo "ee_repo_ref=$(cat ./backend/ee-repo-ref.txt)" >> "$GITHUB_ENV"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: windmill-labs/windmill-ee-private
|
||||
path: ./windmill-ee-private
|
||||
ref: ${{ env.ee_repo_ref }}
|
||||
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
|
||||
- name: Substitute EE code
|
||||
run: |
|
||||
./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private
|
||||
|
||||
- uses: depot/setup-action@v1
|
||||
|
||||
- name: Extract EE debug info from builder stage (depot cache hit)
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.platform }}
|
||||
target: debuginfo
|
||||
build-args: |
|
||||
features=ee
|
||||
outputs: type=local,dest=./debuginfo
|
||||
|
||||
- name: Rename debug file with corresponding architecture
|
||||
run: |
|
||||
mv ./debuginfo/windmill.debug ./debuginfo/windmill-ee-${{ matrix.arch }}.debug
|
||||
|
||||
- name: Attach debug file to release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: ./debuginfo/windmill-ee-${{ matrix.arch }}.debug
|
||||
|
||||
# attach_arm64_binary_to_release:
|
||||
# needs: [build, build_ee]
|
||||
# runs-on: ubicoud
|
||||
|
||||
@@ -106,19 +106,6 @@ jobs:
|
||||
git config --local user.name "windmill-internal-app[bot]"
|
||||
git config pull.rebase true
|
||||
git pull origin $BRANCH_NAME
|
||||
|
||||
# Checkout the correct windmill-ee-private commit from ee-repo-ref.txt
|
||||
if [ -f backend/ee-repo-ref.txt ]; then
|
||||
EE_REF=$(cat backend/ee-repo-ref.txt | tr -d '[:space:]')
|
||||
echo "Checking out windmill-ee-private at commit: $EE_REF"
|
||||
cd windmill-ee-private
|
||||
git fetch origin $EE_REF
|
||||
git checkout $EE_REF
|
||||
cd ..
|
||||
else
|
||||
echo "Warning: ee-repo-ref.txt not found, using default branch"
|
||||
fi
|
||||
|
||||
mkdir -p frontend/build
|
||||
cd backend
|
||||
cargo install sqlx-cli --version 0.8.5
|
||||
|
||||
@@ -16,7 +16,6 @@ rust-client/Cargo.toml
|
||||
|
||||
# Worktree-generated port isolation
|
||||
.env.local
|
||||
.webmux.local.yaml
|
||||
|
||||
# Worktree-specific Claude Code settings (generated by scripts/worktree-env)
|
||||
.claude/settings.local.json
|
||||
|
||||
@@ -1,39 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [1.658.0](https://github.com/windmill-labs/windmill/compare/v1.657.2...v1.658.0) (2026-03-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add GET /api/saml/metadata endpoint ([#8394](https://github.com/windmill-labs/windmill/issues/8394)) ([50b24cf](https://github.com/windmill-labs/windmill/commit/50b24cfdc8bf54656adbdc3315037aa773632076))
|
||||
* support custom headers in customai resource type ([#8364](https://github.com/windmill-labs/windmill/issues/8364)) ([5acb367](https://github.com/windmill-labs/windmill/commit/5acb367cf9b4b96ac7129c91df229d1a25258f5b))
|
||||
* support multiple secret variables during resource creation ([#8386](https://github.com/windmill-labs/windmill/issues/8386)) ([54841b7](https://github.com/windmill-labs/windmill/commit/54841b7549d5c9719d4dc3cb43e282ba057cd0f3))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* /updatesqlx now uses ee-repo-ref.txt commit hash ([#8387](https://github.com/windmill-labs/windmill/issues/8387)) ([a519d41](https://github.com/windmill-labs/windmill/commit/a519d4113086430ace1d7ac8795bd2c2a8cf99e9))
|
||||
* **native-triggers:** preserve API error response body in HttpRequestError ([#8392](https://github.com/windmill-labs/windmill/issues/8392)) ([1eee89d](https://github.com/windmill-labs/windmill/commit/1eee89d99fbf31751d6257a4015e0b22e3871372))
|
||||
* OutputPicker shows stale result after 'Test up to here' ([#8390](https://github.com/windmill-labs/windmill/issues/8390)) ([2907084](https://github.com/windmill-labs/windmill/commit/2907084ca653fc5540bb04a409d2789ddaeec05b))
|
||||
* propagate enterprise feature to windmill-api-schedule ([#8391](https://github.com/windmill-labs/windmill/issues/8391)) ([50ef9e7](https://github.com/windmill-labs/windmill/commit/50ef9e79fcef8ee2cccd789b5eb1aacf5647365f))
|
||||
* set nsjail time_limit from job timeout so configured defaults are respected ([#8389](https://github.com/windmill-labs/windmill/issues/8389)) ([65a92d9](https://github.com/windmill-labs/windmill/commit/65a92d98994dbe4ae90a5e554e55b3ab44463f86))
|
||||
* soft error on AI agent max iterations + rename retries tab to error handling ([#8366](https://github.com/windmill-labs/windmill/issues/8366)) ([1a1e8a1](https://github.com/windmill-labs/windmill/commit/1a1e8a164cccbfcc663b963cb062af9208ff51be))
|
||||
* use bookworm-based php image to fix glibc 2.38 incompatibility ([#8381](https://github.com/windmill-labs/windmill/issues/8381)) ([68fd900](https://github.com/windmill-labs/windmill/commit/68fd900076ecf8b20f6622cd5794f1b52c0f5cab))
|
||||
|
||||
## [1.657.2](https://github.com/windmill-labs/windmill/compare/v1.657.1...v1.657.2) (2026-03-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** Fix nonDottedPaths handling in cli flow lock generation ([#8375](https://github.com/windmill-labs/windmill/issues/8375)) ([eb03ebb](https://github.com/windmill-labs/windmill/commit/eb03ebbb0486b33c290fba3c34ea959e6e82fd13))
|
||||
|
||||
## [1.657.1](https://github.com/windmill-labs/windmill/compare/v1.657.0...v1.657.1) (2026-03-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* powershell WindmillClient module loading on Windows workers ([#8370](https://github.com/windmill-labs/windmill/issues/8370)) ([3a268a9](https://github.com/windmill-labs/windmill/commit/3a268a9cf16add2ea2530e6eab247120a4d4754e))
|
||||
|
||||
## [1.657.0](https://github.com/windmill-labs/windmill/compare/v1.656.0...v1.657.0) (2026-03-14)
|
||||
|
||||
|
||||
|
||||
+1
-13
@@ -118,18 +118,6 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --release --features "$features"
|
||||
|
||||
# Split debug info into a separate file, then strip the binary.
|
||||
# The .debug file can be extracted as a CI artifact for production debugging.
|
||||
# The debuglink allows gdb to auto-discover the debug file when placed next to the binary.
|
||||
RUN objcopy --only-keep-debug /windmill/target/release/windmill /windmill/target/release/windmill.debug \
|
||||
&& strip /windmill/target/release/windmill \
|
||||
&& objcopy --add-gnu-debuglink=/windmill/target/release/windmill.debug /windmill/target/release/windmill
|
||||
|
||||
# Standalone stage for extracting the .debug file without including it in the final image.
|
||||
# Build with: docker build --target debuginfo --output type=local,dest=./out .
|
||||
FROM scratch AS debuginfo
|
||||
COPY --from=builder /windmill/target/release/windmill.debug /windmill.debug
|
||||
|
||||
FROM ${DEBIAN_IMAGE}
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
@@ -280,7 +268,7 @@ RUN bun install -g windmill-cli \
|
||||
RUN curl -fsSL https://claude.ai/install.sh | bash \
|
||||
&& cp /root/.local/share/claude/versions/* /usr/bin/claude
|
||||
|
||||
COPY --from=php:8.3.30-cli-bookworm /usr/local/bin/php /usr/bin/php
|
||||
COPY --from=php:8.3.30-cli /usr/local/bin/php /usr/bin/php
|
||||
COPY --from=composer:2.9.5 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# add the docker client to call docker from a worker if enabled
|
||||
|
||||
+3
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO native_trigger (\n external_id,\n workspace_id,\n service_name,\n script_path,\n is_flow,\n webhook_token_prefix,\n service_config,\n summary\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8\n )\n ON CONFLICT (external_id, workspace_id, service_name)\n DO UPDATE SET script_path = $4, is_flow = $5, webhook_token_prefix = $6, service_config = $7, summary = $8, error = NULL, updated_at = NOW()\n ",
|
||||
"query": "\n INSERT INTO native_trigger (\n external_id,\n workspace_id,\n service_name,\n script_path,\n is_flow,\n webhook_token_prefix,\n service_config\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7\n )\n ON CONFLICT (external_id, workspace_id, service_name)\n DO UPDATE SET script_path = $4, is_flow = $5, webhook_token_prefix = $6, service_config = $7, error = NULL, updated_at = NOW()\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -21,11 +21,10 @@
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Varchar"
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "71327558c8f5f48f9da5d2585db9e4702ed7eddef41dbf0bb1bf66b5b567d481"
|
||||
"hash": "023cdbc77ea9e2c17a1aa92a5b9001f29e58e81b3f782887db6e0a627dd8ad75"
|
||||
}
|
||||
+3
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE native_trigger\n SET script_path = $1, is_flow = $2, webhook_token_prefix = $3, service_config = $4, summary = $8, error = NULL, updated_at = NOW()\n WHERE\n workspace_id = $5\n AND service_name = $6\n AND external_id = $7\n ",
|
||||
"query": "\n UPDATE native_trigger\n SET script_path = $1, is_flow = $2, webhook_token_prefix = $3, service_config = $4, error = NULL, updated_at = NOW()\n WHERE\n workspace_id = $5\n AND service_name = $6\n AND external_id = $7\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -21,11 +21,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Text",
|
||||
"Varchar"
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0eea6bb9d8d77fafbb4a0132a7b977bd37760e82efca322149814ad65a5be2bb"
|
||||
"hash": "27ada97cb533c8595f1d73987c7823d8e54c96889e06895c57cafae9ca27bf8b"
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2d6607b3c38fe72b5663c32de58dacbabed4c5ae28101e3ae2694f96fd055a91"
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM variable WHERE path = $1 AND workspace_id = $2 RETURNING path",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "3317484a9c09c07c2c9db9debaecc4a4d518093ab48e79365dbb808068e0b8ff"
|
||||
}
|
||||
+4
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n external_id,\n workspace_id,\n service_name AS \"service_name!: ServiceName\",\n script_path,\n is_flow,\n webhook_token_prefix,\n service_config,\n summary,\n error,\n created_at,\n updated_at\n FROM\n native_trigger\n WHERE\n workspace_id = $1\n AND service_name = $2\n AND external_id = $3\n ",
|
||||
"query": "\n SELECT\n external_id,\n workspace_id,\n service_name AS \"service_name!: ServiceName\",\n script_path,\n is_flow,\n webhook_token_prefix,\n service_config,\n error,\n created_at,\n updated_at\n FROM\n native_trigger\n WHERE\n workspace_id = $1\n AND service_name = $2\n AND external_id = $3\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -50,21 +50,16 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "summary",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "updated_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
@@ -95,10 +90,9 @@
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "88b27601cb7af37e3ddf79d308dbb2f6edb4c7c4214ee1e4ee0731e5175ed74b"
|
||||
"hash": "b615d73ddb43e9d655b86a0cf98f892bf40e629ee11ee4845199481755f2789d"
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "eba16eb819e2644284fb073c891706d78a6f24cb0e614d7d81ba1b643805bf06"
|
||||
|
||||
+4
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n nt.external_id,\n nt.workspace_id,\n nt.service_name AS \"service_name!: ServiceName\",\n nt.script_path,\n nt.is_flow,\n nt.webhook_token_prefix,\n nt.service_config,\n nt.summary,\n nt.error,\n nt.created_at,\n nt.updated_at\n FROM\n native_trigger nt\n WHERE\n nt.workspace_id = $1 AND\n nt.service_name = $2 AND\n ($5::text IS NULL OR nt.script_path = $5) AND\n ($6::bool IS NULL OR nt.is_flow = $6) AND\n (\n (nt.is_flow = false AND EXISTS (\n SELECT 1 FROM script s\n WHERE s.workspace_id = nt.workspace_id\n AND s.path = nt.script_path\n ))\n OR\n (nt.is_flow = true AND EXISTS (\n SELECT 1 FROM flow f\n WHERE f.workspace_id = nt.workspace_id\n AND f.path = nt.script_path\n ))\n )\n LIMIT $3\n OFFSET $4\n ",
|
||||
"query": "\n SELECT\n nt.external_id,\n nt.workspace_id,\n nt.service_name AS \"service_name!: ServiceName\",\n nt.script_path,\n nt.is_flow,\n nt.webhook_token_prefix,\n nt.service_config,\n nt.error,\n nt.created_at,\n nt.updated_at\n FROM\n native_trigger nt\n WHERE\n nt.workspace_id = $1 AND\n nt.service_name = $2 AND\n ($5::text IS NULL OR nt.script_path = $5) AND\n ($6::bool IS NULL OR nt.is_flow = $6) AND\n (\n (nt.is_flow = false AND EXISTS (\n SELECT 1 FROM script s\n WHERE s.workspace_id = nt.workspace_id\n AND s.path = nt.script_path\n ))\n OR\n (nt.is_flow = true AND EXISTS (\n SELECT 1 FROM flow f\n WHERE f.workspace_id = nt.workspace_id\n AND f.path = nt.script_path\n ))\n )\n LIMIT $3\n OFFSET $4\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -50,21 +50,16 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "summary",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "updated_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
@@ -98,10 +93,9 @@
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "bab3d5e34abc8e998ea9cf721da1c08cc01c313543c3bff90d7f41a4c51bcd5e"
|
||||
"hash": "ecab1af12a7afa685c056b9d0e526275203fc8ecddf83ca6d05c9fb77e46e7ee"
|
||||
}
|
||||
+4
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n external_id,\n workspace_id,\n service_name AS \"service_name!: ServiceName\",\n script_path,\n is_flow,\n webhook_token_prefix,\n service_config,\n summary,\n error,\n created_at,\n updated_at\n FROM\n native_trigger\n WHERE\n workspace_id = $1\n AND service_name = $2\n AND script_path = $3\n AND is_flow = $4\n LIMIT 1\n ",
|
||||
"query": "\n SELECT\n external_id,\n workspace_id,\n service_name AS \"service_name!: ServiceName\",\n script_path,\n is_flow,\n webhook_token_prefix,\n service_config,\n error,\n created_at,\n updated_at\n FROM\n native_trigger\n WHERE\n workspace_id = $1\n AND service_name = $2\n AND script_path = $3\n AND is_flow = $4\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -50,21 +50,16 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "summary",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "updated_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
@@ -96,10 +91,9 @@
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "1d1d70387f6da3181d2547146cd4897608932a6c0ff9bb9a2698c0d30c039665"
|
||||
"hash": "ee537def1ead8bee48bb9f5c1f57d42e7add6011c34d91761ba23e2c74c4032c"
|
||||
}
|
||||
Generated
+83
-84
@@ -8208,9 +8208,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lz4_flex"
|
||||
version = "0.11.6"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a"
|
||||
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
|
||||
dependencies = [
|
||||
"twox-hash 2.1.2",
|
||||
]
|
||||
@@ -9247,9 +9247,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.6"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
|
||||
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
"rustversion",
|
||||
@@ -9257,9 +9257,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.6"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
|
||||
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -14087,9 +14087,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.11.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
|
||||
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
@@ -15741,7 +15741,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -15808,7 +15808,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-alerting"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15821,7 +15821,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -15962,7 +15962,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-agent-workers"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15985,7 +15985,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-assets"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15998,7 +15998,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-auth"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16024,7 +16024,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
@@ -16034,7 +16034,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-configs"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16051,7 +16051,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-debug"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"base64 0.22.1",
|
||||
@@ -16074,7 +16074,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-embeddings"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16097,7 +16097,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flow-conversations"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16113,7 +16113,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flows"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16133,7 +16133,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-groups"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16153,7 +16153,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-inputs"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16167,7 +16167,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-integration-tests"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -16195,7 +16195,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-jobs"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16220,7 +16220,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-npm-proxy"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"flate2",
|
||||
@@ -16238,7 +16238,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-openapi"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16259,7 +16259,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-schedule"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16279,7 +16279,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-scripts"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16309,7 +16309,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-settings"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16336,7 +16336,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-sse"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"serde",
|
||||
@@ -16348,7 +16348,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-users"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum 0.7.9",
|
||||
@@ -16371,7 +16371,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workers"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16385,7 +16385,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workspaces"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16416,7 +16416,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
@@ -16430,7 +16430,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-autoscaling"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16449,7 +16449,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
@@ -16548,7 +16548,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-dep-map"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"itertools 0.14.0",
|
||||
@@ -16567,7 +16567,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -16582,7 +16582,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"astral-tokio-tar",
|
||||
@@ -16606,7 +16606,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-jseval"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -16623,7 +16623,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"lazy_static",
|
||||
@@ -16639,7 +16639,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-mcp"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16660,7 +16660,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-native-triggers"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16691,7 +16691,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-oauth"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-oauth2",
|
||||
@@ -16715,7 +16715,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-object-store"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -16749,7 +16749,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-operator"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -16767,7 +16767,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -16776,7 +16776,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16788,7 +16788,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-csharp"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16800,7 +16800,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -16812,7 +16812,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16824,7 +16824,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-java"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16836,7 +16836,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-nu"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"nu-parser",
|
||||
@@ -16847,7 +16847,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16858,7 +16858,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16870,7 +16870,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-asset"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustpython-ast",
|
||||
@@ -16881,7 +16881,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16905,7 +16905,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ruby"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16919,7 +16919,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -16936,7 +16936,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16950,7 +16950,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql-asset"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -16962,7 +16962,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16980,7 +16980,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts-asset"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde-wasm-bindgen",
|
||||
@@ -16996,7 +16996,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wac"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustpython-ast",
|
||||
@@ -17012,7 +17012,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -17023,7 +17023,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -17060,7 +17060,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-runtime-nativets"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"const_format",
|
||||
@@ -17098,7 +17098,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"wasm-bindgen",
|
||||
@@ -17109,7 +17109,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-store"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -17138,7 +17138,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-test-utils"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -17161,7 +17161,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17194,7 +17194,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-email"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17214,7 +17214,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-gcp"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17248,7 +17248,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-http"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17283,7 +17283,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-kafka"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17306,7 +17306,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-mqtt"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17330,7 +17330,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-nats"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -17354,7 +17354,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-postgres"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17389,7 +17389,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-sqs"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17417,7 +17417,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-websocket"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17440,7 +17440,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-types"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.4",
|
||||
@@ -17458,7 +17458,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-once-cell",
|
||||
@@ -17521,7 +17521,6 @@ dependencies = [
|
||||
"sha2 0.10.9",
|
||||
"sqlx",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"tiberius",
|
||||
"tokio",
|
||||
"tokio-postgres 0.7.13",
|
||||
@@ -17565,7 +17564,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker-volumes"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
|
||||
+2
-4
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -82,7 +82,7 @@ members = [
|
||||
exclude = ["./windmill-duckdb-ffi-internal"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -100,8 +100,6 @@ debug = false
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
debug = "line-tables-only"
|
||||
strip = "none"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -1 +1 @@
|
||||
b2fde51087d0d0ee0223c15cafb4e8badddd2d13
|
||||
c74c86b78a66b976fd9968b21f77903723e668ec
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE native_trigger DROP COLUMN summary;
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE native_trigger ADD COLUMN summary VARCHAR(512);
|
||||
@@ -1518,92 +1518,6 @@ Write-Output "hello $msg"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_powershell_param_block_with_attributes(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
let port = server.addr.port();
|
||||
|
||||
let content = r#"
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$Name,
|
||||
[int]$Count = 3
|
||||
)
|
||||
Write-Output "$Name-$Count"
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let job = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
lock: None,
|
||||
language: ScriptLang::Powershell,
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
dedicated_worker: None,
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
}))
|
||||
.arg("Name", json!("test"))
|
||||
.arg("Count", json!(7))
|
||||
.run_until_complete(&db, false, port)
|
||||
.await;
|
||||
assert_eq!(job.json_result(), Some(json!("test-7")));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_powershell_error_caught(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
let port = server.addr.port();
|
||||
|
||||
// Script with param block that throws an error — verifies the catch block works
|
||||
let content = r#"
|
||||
param($x)
|
||||
throw "intentional error"
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let job = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
lock: None,
|
||||
language: ScriptLang::Powershell,
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
dedicated_worker: None,
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
}))
|
||||
.arg("x", json!(1))
|
||||
.run_until_complete(&db, false, port)
|
||||
.await;
|
||||
assert!(!job.success, "job should fail on thrown error");
|
||||
let result_str = serde_json::to_string(&job.result).unwrap_or_default();
|
||||
assert!(
|
||||
result_str.contains("An error occurred:"),
|
||||
"catch block should output 'An error occurred:', got: {result_str}"
|
||||
);
|
||||
assert!(
|
||||
result_str.contains("intentional error"),
|
||||
"catch block should output the error message, got: {result_str}"
|
||||
);
|
||||
// Verify the catch block doesn't leak "Write-Output" as literal text
|
||||
// (regression from the old broken line continuation in strict_termination_end)
|
||||
let after_marker = result_str.split("An error occurred:").nth(1).unwrap_or("");
|
||||
assert!(
|
||||
!after_marker.starts_with("\\nWrite-Output"),
|
||||
"catch block should not output literal 'Write-Output' text, got: {result_str}"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "php")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_php_job(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
|
||||
@@ -422,7 +422,6 @@ async fn test_delete_integration_full_cascade(db: Pool<Postgres>) -> anyhow::Res
|
||||
"ext-1",
|
||||
&trigger_config,
|
||||
json!({"triggerType": "drive"}),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -512,7 +511,6 @@ async fn test_cleanup_preserves_triggers(db: Pool<Postgres>) -> anyhow::Result<(
|
||||
"ext-1",
|
||||
&trigger_config,
|
||||
json!({"triggerType": "drive"}),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ path = "src/lib.rs"
|
||||
[features]
|
||||
default = []
|
||||
private = ["windmill-audit/private", "windmill-common/private", "windmill-api-auth/private", "windmill-store/private", "windmill-api-users/private", "windmill-api-workspaces/private", "windmill-api-groups/private", "windmill-api-configs/private", "windmill-api-settings/private", "windmill-api-agent-workers?/private", "windmill-trigger-kafka?/private", "windmill-trigger-postgres?/private", "windmill-trigger-mqtt?/private", "windmill-trigger-websocket?/private", "windmill-trigger-nats?/private", "windmill-trigger-sqs?/private", "windmill-trigger-gcp?/private", "windmill-trigger-email?/private", "windmill-git-sync/private", "windmill-autoscaling?/private"]
|
||||
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker?/enterprise", "windmill-api-auth/enterprise", "windmill-store/enterprise", "windmill-api-jobs/enterprise", "windmill-api-scripts/enterprise", "windmill-api-flows/enterprise", "windmill-api-users/enterprise", "windmill-api-workspaces/enterprise", "windmill-api-groups/enterprise", "windmill-api-configs/enterprise", "windmill-api-settings/enterprise", "windmill-api-schedule/enterprise", "windmill-api-agent-workers?/enterprise", "windmill-trigger/enterprise", "windmill-trigger-kafka?/enterprise", "windmill-trigger-postgres?/enterprise", "windmill-trigger-mqtt?/enterprise", "windmill-trigger-websocket?/enterprise", "windmill-trigger-email?/enterprise", "windmill-trigger-nats?/enterprise", "windmill-trigger-sqs?/enterprise", "windmill-trigger-gcp?/enterprise", "windmill-trigger-http?/enterprise", "windmill-native-triggers?/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise"]
|
||||
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker?/enterprise", "windmill-api-auth/enterprise", "windmill-store/enterprise", "windmill-api-jobs/enterprise", "windmill-api-scripts/enterprise", "windmill-api-flows/enterprise", "windmill-api-users/enterprise", "windmill-api-workspaces/enterprise", "windmill-api-groups/enterprise", "windmill-api-configs/enterprise", "windmill-api-settings/enterprise", "windmill-api-agent-workers?/enterprise", "windmill-trigger/enterprise", "windmill-trigger-kafka?/enterprise", "windmill-trigger-postgres?/enterprise", "windmill-trigger-mqtt?/enterprise", "windmill-trigger-websocket?/enterprise", "windmill-trigger-email?/enterprise", "windmill-trigger-nats?/enterprise", "windmill-trigger-sqs?/enterprise", "windmill-trigger-gcp?/enterprise", "windmill-trigger-http?/enterprise", "windmill-native-triggers?/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise"]
|
||||
stripe = []
|
||||
run_inline = ["dep:windmill-worker", "windmill-api-configs/run_inline"]
|
||||
agent_worker_server = ["dep:windmill-worker", "dep:windmill-api-agent-workers"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.658.0
|
||||
version: 1.657.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -24326,10 +24326,6 @@ components:
|
||||
type: object
|
||||
description: Configuration for the trigger including event_type and service_config
|
||||
additionalProperties: true
|
||||
summary:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Short summary describing the purpose of this trigger
|
||||
error:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -24364,10 +24360,6 @@ components:
|
||||
type: object
|
||||
description: Configuration for the trigger including event_type and service_config
|
||||
additionalProperties: true
|
||||
summary:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Short summary describing the purpose of this trigger
|
||||
error:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -24464,10 +24456,6 @@ components:
|
||||
type: object
|
||||
description: Service-specific configuration (e.g., event types, filters)
|
||||
additionalProperties: true
|
||||
summary:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Short summary describing the purpose of this trigger
|
||||
required:
|
||||
- script_path
|
||||
- is_flow
|
||||
|
||||
@@ -169,9 +169,6 @@ struct AIStandardResource {
|
||||
/// Enable 1M context window for Anthropic
|
||||
#[serde(alias = "enable_1M_context", default)]
|
||||
enable_1m_context: bool,
|
||||
/// Custom HTTP headers to include in AI requests
|
||||
#[serde(default)]
|
||||
headers: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
@@ -203,7 +200,6 @@ struct AIRequestConfig {
|
||||
pub aws_session_token: Option<String>,
|
||||
pub platform: AIPlatform,
|
||||
pub enable_1m_context: bool,
|
||||
pub custom_headers: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl AIRequestConfig {
|
||||
@@ -225,13 +221,11 @@ impl AIRequestConfig {
|
||||
aws_session_token,
|
||||
platform,
|
||||
enable_1m_context,
|
||||
custom_headers,
|
||||
) = match resource {
|
||||
AIResource::Standard(resource) => {
|
||||
let region = resource.region.clone();
|
||||
let platform = resource.platform.clone();
|
||||
let enable_1m_context = resource.enable_1m_context;
|
||||
let custom_headers = resource.headers.clone();
|
||||
// Skip get_base_url for Bedrock - it uses SDK directly, not HTTP
|
||||
let base_url = if matches!(provider, AIProvider::AWSBedrock) {
|
||||
String::new()
|
||||
@@ -277,7 +271,6 @@ impl AIRequestConfig {
|
||||
aws_session_token,
|
||||
platform,
|
||||
enable_1m_context,
|
||||
custom_headers,
|
||||
)
|
||||
}
|
||||
AIResource::OAuth(resource) => {
|
||||
@@ -301,7 +294,6 @@ impl AIRequestConfig {
|
||||
None,
|
||||
AIPlatform::Standard,
|
||||
false,
|
||||
HashMap::new(),
|
||||
)
|
||||
}
|
||||
};
|
||||
@@ -318,7 +310,6 @@ impl AIRequestConfig {
|
||||
aws_session_token,
|
||||
platform,
|
||||
enable_1m_context,
|
||||
custom_headers,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -452,11 +443,6 @@ impl AIRequestConfig {
|
||||
request = request.header(header_name.as_str(), header_value.as_str());
|
||||
}
|
||||
|
||||
// Apply custom headers from the resource
|
||||
for (header_name, header_value) in &self.custom_headers {
|
||||
request = request.header(header_name.as_str(), header_value.as_str());
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
|
||||
@@ -57,13 +57,6 @@ pub fn extract_workspace_dependencies_annotated_refs(
|
||||
None,
|
||||
runnable_path,
|
||||
),
|
||||
Powershell => WorkspaceDependenciesAnnotatedRefs::parse(
|
||||
"#",
|
||||
"modules_json",
|
||||
code,
|
||||
None,
|
||||
runnable_path,
|
||||
),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,23 +548,6 @@ impl WorkspaceDependenciesPrefetched {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_powershell(&self) -> error::Result<Option<String>> {
|
||||
use WorkspaceDependenciesPrefetchedInternal::*;
|
||||
self.internal.assert_no_extra_mode().map_err(map_err)?;
|
||||
Ok(match &self.internal {
|
||||
Explicit(wdar @ WorkspaceDependenciesAnnotatedRefs { external, .. }) => {
|
||||
wdar.assert_no_inline().map_err(map_err)?;
|
||||
wdar.assert_external_less_than(2).map_err(map_err)?;
|
||||
external
|
||||
.get(0)
|
||||
.map(|wd| wd.content.clone())
|
||||
.or(Some(r#"{"modules": {}}"#.to_owned()))
|
||||
}
|
||||
Implicit { workspace_dependencies, .. } => Some(workspace_dependencies.content.clone()),
|
||||
None => Option::None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Is the runnable permitted to have external references
|
||||
pub fn is_external_references_permitted(runnable_path: &str) -> bool {
|
||||
!BLACKLIST.contains(runnable_path) && !runnable_path.starts_with("hub/")
|
||||
@@ -605,9 +588,7 @@ impl WorkspaceDependenciesPrefetched {
|
||||
use WorkspaceDependenciesPrefetchedInternal::*;
|
||||
match (self.language, &self.internal) {
|
||||
// These languages except for python had none of this functionality
|
||||
(Php | Bun | Bunnative | Go | Powershell, wdp) => {
|
||||
wdp.assert_no_workspace_dependencies()?
|
||||
}
|
||||
(Php | Bun | Bunnative | Go, wdp) => wdp.assert_no_workspace_dependencies()?,
|
||||
|
||||
// Python, had #(extra_)requirements:
|
||||
// but it had no external requirements.
|
||||
@@ -1280,130 +1261,6 @@ def main():
|
||||
assert!(result.external.is_empty());
|
||||
assert!(result.inline.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn test_parse_annotation_powershell_modules_json_manual_mode() {
|
||||
let code = r#"
|
||||
# modules_json: default
|
||||
param()
|
||||
Write-Host "Hello"
|
||||
"#;
|
||||
|
||||
let result = WorkspaceDependenciesAnnotatedRefs::<String>::parse(
|
||||
"#",
|
||||
"modules_json",
|
||||
code,
|
||||
None,
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(matches!(result.mode, Mode::manual));
|
||||
assert_eq!(result.external, vec!["default".to_owned()]);
|
||||
assert!(result.inline.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_annotation_powershell_modules_json_extra_mode() {
|
||||
let code = r#"
|
||||
# extra_modules_json: my_deps
|
||||
param()
|
||||
Write-Host "Hello"
|
||||
"#;
|
||||
|
||||
let result = WorkspaceDependenciesAnnotatedRefs::<String>::parse(
|
||||
"#",
|
||||
"modules_json",
|
||||
code,
|
||||
None,
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(matches!(result.mode, Mode::extra));
|
||||
assert_eq!(result.external, vec!["my_deps".to_owned()]);
|
||||
assert!(result.inline.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_annotation_powershell_modules_json_extra_hyphen() {
|
||||
let code = r#"
|
||||
# extra-modules_json: my_deps
|
||||
param()
|
||||
Write-Host "Hello"
|
||||
"#;
|
||||
|
||||
let result = WorkspaceDependenciesAnnotatedRefs::<String>::parse(
|
||||
"#",
|
||||
"modules_json",
|
||||
code,
|
||||
None,
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(matches!(result.mode, Mode::extra));
|
||||
assert_eq!(result.external, vec!["my_deps".to_owned()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_annotation_powershell_modules_json_multiple_refs() {
|
||||
let code = r#"
|
||||
# modules_json: default, extra_modules
|
||||
param()
|
||||
"#;
|
||||
|
||||
let result = WorkspaceDependenciesAnnotatedRefs::<String>::parse(
|
||||
"#",
|
||||
"modules_json",
|
||||
code,
|
||||
None,
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(matches!(result.mode, Mode::manual));
|
||||
assert_eq!(
|
||||
result.external,
|
||||
vec!["default".to_owned(), "extra_modules".to_owned()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_annotation_powershell_no_match() {
|
||||
let code = r#"
|
||||
param()
|
||||
Import-Module PSWriteColor
|
||||
Write-Host "Hello"
|
||||
"#;
|
||||
|
||||
let result = WorkspaceDependenciesAnnotatedRefs::<String>::parse(
|
||||
"#",
|
||||
"modules_json",
|
||||
code,
|
||||
None,
|
||||
"",
|
||||
);
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_annotation_powershell_modules_json_with_inline() {
|
||||
let code = r#"
|
||||
# modules_json: default
|
||||
#{ "modules": { "Extra": "1.0" } }
|
||||
param()
|
||||
"#;
|
||||
|
||||
let result = WorkspaceDependenciesAnnotatedRefs::<String>::parse(
|
||||
"#",
|
||||
"modules_json",
|
||||
code,
|
||||
None,
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(matches!(result.mode, Mode::manual));
|
||||
assert_eq!(result.external, vec!["default".to_owned()]);
|
||||
assert!(result.inline.is_some());
|
||||
assert!(result.inline.as_ref().unwrap().contains("Extra"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_annotation_blacklisted() {
|
||||
let code = r#"
|
||||
|
||||
@@ -181,7 +181,6 @@ async fn create_native_trigger<T: External>(
|
||||
&external_id,
|
||||
&config,
|
||||
service_config,
|
||||
data.summary.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -288,7 +287,6 @@ async fn update_native_trigger_handler<T: External>(
|
||||
&external_id,
|
||||
&config,
|
||||
service_config,
|
||||
data.summary.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -192,7 +192,6 @@ pub struct NativeTrigger {
|
||||
pub is_flow: bool,
|
||||
pub webhook_token_prefix: String,
|
||||
pub service_config: Option<serde_json::Value>,
|
||||
pub summary: Option<String>,
|
||||
pub error: Option<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
@@ -210,7 +209,6 @@ pub struct NativeTriggerData<C> {
|
||||
pub script_path: String,
|
||||
pub is_flow: bool,
|
||||
pub service_config: C,
|
||||
pub summary: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromRow, Serialize, Deserialize)]
|
||||
@@ -445,16 +443,10 @@ pub async fn make_http_request<T: DeserializeOwned + Send, B: Serialize>(
|
||||
request = request.json(body_content);
|
||||
}
|
||||
|
||||
let response = request.send().await?;
|
||||
let status = response.status();
|
||||
let bytes = response.bytes().await?;
|
||||
|
||||
if !status.is_success() {
|
||||
let body = String::from_utf8_lossy(&bytes);
|
||||
return Err(HttpRequestError::ApiError { status, body: body.into_owned() });
|
||||
}
|
||||
let response = request.send().await?.error_for_status()?;
|
||||
|
||||
// Handle empty responses (e.g. 204 No Content from Google channels/stop)
|
||||
let bytes = response.bytes().await?;
|
||||
if bytes.is_empty() {
|
||||
serde_json::from_str("null").map_err(HttpRequestError::Json)
|
||||
} else {
|
||||
@@ -466,7 +458,6 @@ pub async fn make_http_request<T: DeserializeOwned + Send, B: Serialize>(
|
||||
pub enum HttpRequestError {
|
||||
Reqwest(reqwest::Error),
|
||||
Json(serde_json::Error),
|
||||
ApiError { status: StatusCode, body: String },
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HttpRequestError {
|
||||
@@ -474,9 +465,6 @@ impl std::fmt::Display for HttpRequestError {
|
||||
match self {
|
||||
HttpRequestError::Reqwest(e) => write!(f, "{}", e),
|
||||
HttpRequestError::Json(e) => write!(f, "JSON decode error: {}", e),
|
||||
HttpRequestError::ApiError { status, body } => {
|
||||
write!(f, "HTTP {} error: {}", status.as_u16(), body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,7 +474,6 @@ impl std::error::Error for HttpRequestError {
|
||||
match self {
|
||||
HttpRequestError::Reqwest(e) => Some(e),
|
||||
HttpRequestError::Json(e) => Some(e),
|
||||
HttpRequestError::ApiError { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,7 +489,6 @@ impl HttpRequestError {
|
||||
match self {
|
||||
HttpRequestError::Reqwest(e) => e.status(),
|
||||
HttpRequestError::Json(_) => None,
|
||||
HttpRequestError::ApiError { status, .. } => Some(*status),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -779,7 +765,6 @@ pub async fn store_native_trigger<'c, E: sqlx::Executor<'c, Database = Postgres>
|
||||
external_id: &str,
|
||||
config: &NativeTriggerConfig,
|
||||
service_config: C,
|
||||
summary: Option<&str>,
|
||||
) -> Result<()> {
|
||||
// Store only the first 10 characters of the webhook token as a prefix
|
||||
let webhook_token_prefix: String = config.webhook_token.chars().take(10).collect();
|
||||
@@ -793,13 +778,12 @@ pub async fn store_native_trigger<'c, E: sqlx::Executor<'c, Database = Postgres>
|
||||
script_path,
|
||||
is_flow,
|
||||
webhook_token_prefix,
|
||||
service_config,
|
||||
summary
|
||||
service_config
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8
|
||||
$1, $2, $3, $4, $5, $6, $7
|
||||
)
|
||||
ON CONFLICT (external_id, workspace_id, service_name)
|
||||
DO UPDATE SET script_path = $4, is_flow = $5, webhook_token_prefix = $6, service_config = $7, summary = $8, error = NULL, updated_at = NOW()
|
||||
DO UPDATE SET script_path = $4, is_flow = $5, webhook_token_prefix = $6, service_config = $7, error = NULL, updated_at = NOW()
|
||||
"#,
|
||||
external_id,
|
||||
workspace_id,
|
||||
@@ -808,7 +792,6 @@ pub async fn store_native_trigger<'c, E: sqlx::Executor<'c, Database = Postgres>
|
||||
config.is_flow,
|
||||
webhook_token_prefix,
|
||||
sqlx::types::Json(service_config) as _,
|
||||
summary,
|
||||
)
|
||||
.execute(db)
|
||||
.await?;
|
||||
@@ -823,7 +806,6 @@ pub async fn update_native_trigger<'c, E: sqlx::Executor<'c, Database = Postgres
|
||||
external_id: &str,
|
||||
config: &NativeTriggerConfig,
|
||||
service_config: Option<&RawValue>,
|
||||
summary: Option<&str>,
|
||||
) -> Result<()> {
|
||||
// Store only the first 10 characters of the webhook token as a prefix
|
||||
let webhook_token_prefix: String = config.webhook_token.chars().take(10).collect();
|
||||
@@ -831,7 +813,7 @@ pub async fn update_native_trigger<'c, E: sqlx::Executor<'c, Database = Postgres
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE native_trigger
|
||||
SET script_path = $1, is_flow = $2, webhook_token_prefix = $3, service_config = $4, summary = $8, error = NULL, updated_at = NOW()
|
||||
SET script_path = $1, is_flow = $2, webhook_token_prefix = $3, service_config = $4, error = NULL, updated_at = NOW()
|
||||
WHERE
|
||||
workspace_id = $5
|
||||
AND service_name = $6
|
||||
@@ -844,7 +826,6 @@ pub async fn update_native_trigger<'c, E: sqlx::Executor<'c, Database = Postgres
|
||||
workspace_id,
|
||||
service_name as ServiceName,
|
||||
external_id,
|
||||
summary,
|
||||
)
|
||||
.execute(db)
|
||||
.await?;
|
||||
@@ -893,7 +874,6 @@ pub async fn get_native_trigger<'c, E: sqlx::Executor<'c, Database = Postgres>>(
|
||||
is_flow,
|
||||
webhook_token_prefix,
|
||||
service_config,
|
||||
summary,
|
||||
error,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -932,7 +912,6 @@ pub async fn get_native_trigger_by_script<'c, E: sqlx::Executor<'c, Database = P
|
||||
is_flow,
|
||||
webhook_token_prefix,
|
||||
service_config,
|
||||
summary,
|
||||
error,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -979,7 +958,6 @@ pub async fn list_native_triggers<'c, E: sqlx::Executor<'c, Database = Postgres>
|
||||
nt.is_flow,
|
||||
nt.webhook_token_prefix,
|
||||
nt.service_config,
|
||||
nt.summary,
|
||||
nt.error,
|
||||
nt.created_at,
|
||||
nt.updated_at
|
||||
|
||||
@@ -883,15 +883,6 @@ async fn delete_resource(
|
||||
}
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
// Fetch the resource value before deleting, so we can find linked $var: references
|
||||
let resource_value: Option<Option<serde_json::Value>> = sqlx::query_scalar(
|
||||
"SELECT value FROM resource WHERE path = $1 AND workspace_id = $2",
|
||||
)
|
||||
.bind(path)
|
||||
.bind(&w_id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?;
|
||||
|
||||
let deleted_path = sqlx::query_scalar!(
|
||||
"DELETE FROM resource WHERE path = $1 AND workspace_id = $2 RETURNING path",
|
||||
path,
|
||||
@@ -900,32 +891,13 @@ async fn delete_resource(
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?;
|
||||
not_found_if_none(deleted_path, "Resource", &path)?;
|
||||
|
||||
// Collect all $var: paths referenced in the resource value
|
||||
let mut linked_var_paths: Vec<String> = Vec::new();
|
||||
if let Some(Some(value)) = resource_value {
|
||||
collect_var_refs(&value, &mut linked_var_paths);
|
||||
}
|
||||
|
||||
// Delete linked variables that are actually referenced in the resource value
|
||||
let deleted_linked_variables: Vec<String> = if linked_var_paths.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let placeholders: Vec<String> = linked_var_paths
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _)| format!("${}", i + 2))
|
||||
.collect();
|
||||
let query = format!(
|
||||
"DELETE FROM variable WHERE workspace_id = $1 AND path IN ({}) RETURNING path",
|
||||
placeholders.join(", ")
|
||||
);
|
||||
let mut q = sqlx::query_scalar::<_, String>(&query).bind(&w_id);
|
||||
for var_path in &linked_var_paths {
|
||||
q = q.bind(var_path);
|
||||
}
|
||||
q.fetch_all(&mut *tx).await?
|
||||
};
|
||||
let deleted_linked_variable = sqlx::query_scalar!(
|
||||
"DELETE FROM variable WHERE path = $1 AND workspace_id = $2 RETURNING path",
|
||||
path,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?;
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
@@ -955,19 +927,19 @@ async fn delete_resource(
|
||||
WebhookMessage::DeleteResource { workspace: w_id.clone(), path: path.to_owned() },
|
||||
);
|
||||
|
||||
for var_path in &deleted_linked_variables {
|
||||
if deleted_linked_variable.is_some() {
|
||||
handle_deployment_metadata(
|
||||
&authed.email,
|
||||
&authed.username,
|
||||
&db,
|
||||
&w_id,
|
||||
DeployedObject::Variable {
|
||||
path: var_path.clone(),
|
||||
parent_path: Some(var_path.clone()),
|
||||
path: path.to_string(),
|
||||
parent_path: Some(path.to_string()),
|
||||
},
|
||||
Some(format!(
|
||||
"Variable '{}' deleted (linked resource deleted)",
|
||||
var_path
|
||||
path
|
||||
)),
|
||||
true,
|
||||
None,
|
||||
@@ -976,38 +948,13 @@ async fn delete_resource(
|
||||
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
WebhookMessage::DeleteVariable {
|
||||
workspace: w_id.clone(),
|
||||
path: var_path.clone(),
|
||||
},
|
||||
WebhookMessage::DeleteVariable { workspace: w_id, path: path.to_owned() },
|
||||
);
|
||||
}
|
||||
|
||||
Ok(format!("resource {} deleted", path))
|
||||
}
|
||||
|
||||
/// Recursively collect all `$var:path` references from a JSON value.
|
||||
fn collect_var_refs(value: &serde_json::Value, out: &mut Vec<String>) {
|
||||
match value {
|
||||
serde_json::Value::String(s) => {
|
||||
if let Some(var_path) = s.strip_prefix("$var:") {
|
||||
out.push(var_path.to_string());
|
||||
}
|
||||
}
|
||||
serde_json::Value::Object(m) => {
|
||||
for v in m.values() {
|
||||
collect_var_refs(v, out);
|
||||
}
|
||||
}
|
||||
serde_json::Value::Array(arr) => {
|
||||
for v in arr {
|
||||
collect_var_refs(v, out);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_resources_bulk(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
|
||||
@@ -96,7 +96,6 @@ impl ScriptLang {
|
||||
Python3 => "requirements.in",
|
||||
// Go => "go.mod",
|
||||
Php => "composer.json",
|
||||
Powershell => "modules.json",
|
||||
_ => return None,
|
||||
}
|
||||
.to_owned(),
|
||||
|
||||
@@ -143,8 +143,5 @@ hyper-tls = { workspace = true, optional = true }
|
||||
hyper-util = { workspace = true, optional = true }
|
||||
rcgen = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
libffi-sys = { workspace = true, optional = true }
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "ansible run script"
|
||||
mode: ONCE
|
||||
hostname: "ansible"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
rlimit_as: 4096
|
||||
rlimit_cpu: 1000
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "bash run script"
|
||||
mode: ONCE
|
||||
hostname: "bash"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "{LANG} run script"
|
||||
mode: ONCE
|
||||
hostname: "{LANG}"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "csharp run script"
|
||||
mode: ONCE
|
||||
hostname: "csharp"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "go run script"
|
||||
mode: ONCE
|
||||
hostname: "go"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "java run script"
|
||||
mode: ONCE
|
||||
hostname: "java"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "nu run script"
|
||||
mode: ONCE
|
||||
hostname: "nu"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "php run script"
|
||||
mode: ONCE
|
||||
hostname: "php"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "powershell run script"
|
||||
mode: ONCE
|
||||
hostname: "powershell"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "python run script"
|
||||
mode: ONCE
|
||||
hostname: "python"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
rlimit_as: 4096
|
||||
rlimit_cpu: 1000
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "ruby run script"
|
||||
mode: ONCE
|
||||
hostname: "ruby"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ name: "rust run script"
|
||||
mode: ONCE
|
||||
hostname: "rust"
|
||||
log_level: ERROR
|
||||
time_limit: {TIMEOUT}
|
||||
|
||||
disable_rl: true
|
||||
|
||||
|
||||
@@ -193,9 +193,6 @@ pub struct ProviderResource {
|
||||
/// Enable 1M context window for Anthropic
|
||||
#[serde(alias = "enable_1M_context", default)]
|
||||
pub enable_1m_context: bool,
|
||||
/// Custom HTTP headers to include in AI requests
|
||||
#[serde(default)]
|
||||
pub headers: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
@@ -247,10 +244,6 @@ impl ProviderWithResource {
|
||||
pub fn get_enable_1m_context(&self) -> bool {
|
||||
self.resource.enable_1m_context
|
||||
}
|
||||
|
||||
pub fn get_headers(&self) -> &HashMap<String, String> {
|
||||
&self.resource.headers
|
||||
}
|
||||
}
|
||||
|
||||
/// Token usage information from the AI provider
|
||||
|
||||
@@ -762,10 +762,8 @@ pub async fn run_agent(
|
||||
.map(|m| m.clamp(1, HARD_MAX_AGENT_ITERATIONS))
|
||||
.unwrap_or(DEFAULT_MAX_AGENT_ITERATIONS);
|
||||
|
||||
|
||||
// Main agent loop
|
||||
for i in 0..max_iterations {
|
||||
|
||||
if used_structured_output_tool {
|
||||
break;
|
||||
}
|
||||
@@ -832,8 +830,6 @@ pub async fn run_agent(
|
||||
.await
|
||||
.0;
|
||||
|
||||
let resource_headers = args.provider.get_headers();
|
||||
|
||||
// Helper to build HTTP request with headers
|
||||
let build_http_request = |body: String| {
|
||||
let mut req = HTTP_CLIENT
|
||||
@@ -849,10 +845,6 @@ pub async fn run_agent(
|
||||
req = req.header(header_name.as_str(), header_value.as_str());
|
||||
}
|
||||
|
||||
for (header_name, header_value) in resource_headers {
|
||||
req = req.header(header_name.as_str(), header_value.as_str());
|
||||
}
|
||||
|
||||
req.body(body)
|
||||
};
|
||||
|
||||
@@ -1038,13 +1030,10 @@ pub async fn run_agent(
|
||||
if tool_calls.is_empty() {
|
||||
break;
|
||||
} else if i == max_iterations - 1 {
|
||||
let partial_result = serde_json::to_string(&serde_json::json!({
|
||||
"messages": messages,
|
||||
})).unwrap_or_default();
|
||||
return Err(Error::internal_err(format!(
|
||||
"AI agent reached max iterations ({}), you can either increase max_iterations or enable the \"continue on error\" option from the advanced options of the step. Partial result:\n{}",
|
||||
max_iterations, partial_result
|
||||
)));
|
||||
return Err(Error::internal_err(
|
||||
"AI agent reached max iterations, but there are still tool calls"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
messages.push(OpenAIMessage {
|
||||
|
||||
@@ -30,8 +30,7 @@ use crate::{
|
||||
bash_executor::BIN_BASH,
|
||||
common::{
|
||||
build_command_with_isolation, check_executor_binary_exists, get_reserved_variables,
|
||||
read_and_check_result, resolve_nsjail_timeout, start_child_process, transform_json,
|
||||
OccupancyMetrics,
|
||||
read_and_check_result, start_child_process, transform_json, OccupancyMetrics,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
is_sandboxing_enabled,
|
||||
@@ -1181,8 +1180,6 @@ mount {{
|
||||
)
|
||||
})
|
||||
.join("\n");
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -1196,8 +1193,7 @@ mount {{
|
||||
.replace(
|
||||
"{ADDITIONAL_PYTHON_PATHS}",
|
||||
additional_python_paths_folders.as_str(),
|
||||
)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
),
|
||||
)?;
|
||||
} else {
|
||||
reserved_variables.insert("PYTHONPATH".to_string(), additional_python_paths_folders);
|
||||
|
||||
@@ -41,8 +41,7 @@ use crate::handle_child::run_future_with_polling_update_job_poller;
|
||||
use crate::{
|
||||
common::{
|
||||
build_args_map, build_command_with_isolation, get_reserved_variables, read_file,
|
||||
read_file_content, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
|
||||
DEV_CONF_NSJAIL,
|
||||
read_file_content, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
get_proxy_envs_for_lang,
|
||||
handle_child::handle_child,
|
||||
@@ -193,8 +192,6 @@ exit $exit_status
|
||||
// Use nsjail if globally enabled OR if script has #sandbox annotation
|
||||
let nsjail = (is_sandboxing_enabled() || annotation.sandbox) && is_regular_job;
|
||||
let child = if nsjail {
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -203,8 +200,7 @@ exit $exit_status
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
let mut cmd_args = vec![
|
||||
"--config",
|
||||
|
||||
@@ -15,9 +15,8 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob, PrecomputedAgentInf
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
|
||||
parse_npm_config, read_file, read_file_content, read_result, resolve_nsjail_timeout,
|
||||
start_child_process, write_file_binary, MaybeLock, OccupancyMetrics, StreamNotifier,
|
||||
DEV_CONF_NSJAIL,
|
||||
parse_npm_config, read_file, read_file_content, read_result, start_child_process,
|
||||
write_file_binary, MaybeLock, OccupancyMetrics, StreamNotifier, DEV_CONF_NSJAIL,
|
||||
},
|
||||
get_proxy_envs_for_lang,
|
||||
handle_child::handle_child,
|
||||
@@ -1775,8 +1774,6 @@ try {{
|
||||
|
||||
//do not cache local dependencies
|
||||
let child = if is_sandboxing_enabled() || annotation.sandbox {
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -1796,8 +1793,7 @@ try {{
|
||||
),
|
||||
)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
|
||||
@@ -805,17 +805,6 @@ pub async fn resolve_job_timeout(
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the nsjail timeout (in seconds) with a 15s buffer so handle_child fires first.
|
||||
pub async fn resolve_nsjail_timeout(
|
||||
conn: &Connection,
|
||||
w_id: &str,
|
||||
job_id: Uuid,
|
||||
custom_timeout: Option<i32>,
|
||||
) -> String {
|
||||
let (duration, _, _) = resolve_job_timeout(conn, w_id, job_id, custom_timeout).await;
|
||||
(duration.as_secs() + 15).to_string()
|
||||
}
|
||||
|
||||
async fn hash_args(
|
||||
#[allow(unused)] db: &DB,
|
||||
#[allow(unused)] client: &AuthedClient,
|
||||
|
||||
@@ -27,8 +27,7 @@ use windmill_queue::CanceledBy;
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file,
|
||||
get_reserved_variables, read_result, resolve_nsjail_timeout, start_child_process,
|
||||
DEV_CONF_NSJAIL,
|
||||
get_reserved_variables, read_result, start_child_process, DEV_CONF_NSJAIL,
|
||||
},
|
||||
get_proxy_envs_for_lang,
|
||||
handle_child::handle_child,
|
||||
@@ -583,8 +582,6 @@ pub async fn handle_csharp_job(
|
||||
get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?;
|
||||
|
||||
let child = if is_sandboxing_enabled() {
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -595,8 +592,7 @@ pub async fn handle_csharp_job(
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
nsjail_cmd
|
||||
|
||||
@@ -22,8 +22,7 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, capitalize, create_args_and_out_file, get_reserved_variables,
|
||||
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
|
||||
DEV_CONF_NSJAIL,
|
||||
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
is_sandboxing_enabled, read_ee_registry, DISABLE_NUSER, GOPRIVATE, GOPROXY, GO_BIN_CACHE_DIR,
|
||||
@@ -339,8 +338,6 @@ func Run(req Req) (interface{{}}, error){{
|
||||
get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?;
|
||||
|
||||
let child = if is_sandboxing_enabled() {
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -349,8 +346,7 @@ func Run(req Req) (interface{{}}, error){{
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
nsjail_cmd
|
||||
|
||||
@@ -23,7 +23,7 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
|
||||
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
|
||||
read_result, start_child_process, OccupancyMetrics,
|
||||
},
|
||||
handle_child, is_sandboxing_enabled, read_ee_registry,
|
||||
universal_pkg_installer::{par_install_language_dependencies_all_at_once, RequiredDependency},
|
||||
@@ -600,8 +600,6 @@ async fn run<'a>(
|
||||
)
|
||||
.await;
|
||||
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -610,8 +608,7 @@ async fn run<'a>(
|
||||
.replace("{CACHE_DIR}", &*JAVA_CACHE_DIR)
|
||||
.replace("{SHARED_MOUNT}", &shared_mount)
|
||||
// .replace("{CACHED_TARGET}", &shared_mount)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()),
|
||||
)?;
|
||||
let mut cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
cmd.env_clear()
|
||||
|
||||
@@ -14,8 +14,7 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
|
||||
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
|
||||
DEV_CONF_NSJAIL,
|
||||
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
get_proxy_envs_for_lang, handle_child, is_sandboxing_enabled, DISABLE_NUSER, NSJAIL_PATH,
|
||||
PATH_ENV, TRACING_PROXY_CA_CERT_PATH,
|
||||
@@ -246,8 +245,6 @@ async fn run<'a>(
|
||||
)
|
||||
.await;
|
||||
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -257,8 +254,7 @@ async fn run<'a>(
|
||||
.replace("{SHARED_MOUNT}", &shared_mount)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
nsjail_cmd
|
||||
|
||||
@@ -19,8 +19,7 @@ use windmill_queue::{append_logs, CanceledBy};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file,
|
||||
get_reserved_variables, read_result, resolve_nsjail_timeout, start_child_process,
|
||||
MaybeLock, OccupancyMetrics,
|
||||
get_reserved_variables, read_result, start_child_process, MaybeLock, OccupancyMetrics,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
is_sandboxing_enabled, COMPOSER_CACHE_DIR, COMPOSER_PATH, DISABLE_NUSER, NSJAIL_PATH, PHP_PATH,
|
||||
@@ -295,16 +294,13 @@ try {{
|
||||
let (reserved_variables, _) = tokio::try_join!(reserved_variables_args_out_f, write_wrapper_f)?;
|
||||
|
||||
let child = if is_sandboxing_enabled() {
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
&NSJAIL_CONFIG_RUN_PHP_CONTENT
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("{SHARED_MOUNT}", shared_mount),
|
||||
)?;
|
||||
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
|
||||
@@ -18,14 +18,13 @@ const NSJAIL_CONFIG_RUN_POWERSHELL_CONTENT: &str =
|
||||
include_str!("../nsjail/run.powershell.config.proto");
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref RE_POWERSHELL_IMPORTS: Regex = Regex::new(r#"^\s*Import-Module\s+(?:-Name\s+)?"?([^\s"]+)"?(?:\s+-RequiredVersion\s+"?([^\s"]+)"?)?"#).unwrap();
|
||||
static ref RE_POWERSHELL_IMPORTS: Regex = Regex::new(r#"^Import-Module\s+(?:-Name\s+)?"?([^\s"]+)"?(?:\s+-RequiredVersion\s+"?([^\s"]+)"?)?"#).unwrap();
|
||||
}
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
build_args_map, build_command_with_isolation, get_reserved_variables, read_file,
|
||||
read_file_content, resolve_nsjail_timeout, start_child_process, MaybeLock,
|
||||
OccupancyMetrics,
|
||||
read_file_content, start_child_process, OccupancyMetrics,
|
||||
},
|
||||
handle_child::handle_child,
|
||||
is_sandboxing_enabled, read_ee_registry, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV,
|
||||
@@ -197,41 +196,17 @@ async fn get_module_versions(module_path: &str) -> Result<Vec<String>, Error> {
|
||||
.to_string();
|
||||
|
||||
// Check if this looks like a version (contains dots and numbers)
|
||||
// and verify a module manifest (.psd1) or script (.psm1) actually exists
|
||||
if version.chars().any(|c| c.is_numeric()) && version.contains('.') {
|
||||
let has_module_files = fs::read_dir(&version_path)
|
||||
.map(|entries| {
|
||||
entries.filter_map(|e| e.ok()).any(|e| {
|
||||
let name = e.file_name();
|
||||
let name = name.to_string_lossy();
|
||||
name.ends_with(".psd1") || name.ends_with(".psm1")
|
||||
})
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if has_module_files {
|
||||
versions.push(version);
|
||||
}
|
||||
versions.push(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no version subdirectories found, check if module files exist directly
|
||||
// in the module directory (flat/single-version installation)
|
||||
// If no version subdirectories found, treat as single version installation
|
||||
if versions.is_empty() {
|
||||
let has_module_files = fs::read_dir(module_path)
|
||||
.map(|entries| {
|
||||
entries.filter_map(|e| e.ok()).any(|e| {
|
||||
let name = e.file_name();
|
||||
let name = name.to_string_lossy();
|
||||
name.ends_with(".psd1") || name.ends_with(".psm1")
|
||||
})
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if has_module_files {
|
||||
versions.push("unknown".to_string());
|
||||
}
|
||||
versions.push("unknown".to_string());
|
||||
}
|
||||
|
||||
Ok(versions)
|
||||
@@ -262,67 +237,8 @@ struct ModuleRequest {
|
||||
version: Option<String>,
|
||||
}
|
||||
|
||||
/// Parse Import-Module statements from PowerShell code into module requests.
|
||||
fn parse_script_imports(code: &str) -> Vec<ModuleRequest> {
|
||||
let mut modules = Vec::new();
|
||||
for line in code.lines() {
|
||||
for cap in RE_POWERSHELL_IMPORTS.captures_iter(line) {
|
||||
let name = cap.get(1).unwrap().as_str().to_string();
|
||||
let version = cap.get(2).map(|m| m.as_str().to_string());
|
||||
modules.push(ModuleRequest { name, version });
|
||||
}
|
||||
}
|
||||
modules
|
||||
}
|
||||
|
||||
/// Parse a modules.json workspace dependencies content into module requests.
|
||||
/// Format: { "modules": { "ModuleName": "1.0.0", "Another": null } }
|
||||
fn parse_modules_json(content: &str) -> Result<Vec<ModuleRequest>, Error> {
|
||||
let parsed: serde_json::Value = serde_json::from_str(content).map_err(|e| {
|
||||
Error::internal_err(format!("Failed to parse PowerShell modules.json: {e}"))
|
||||
})?;
|
||||
let modules = parsed
|
||||
.get("modules")
|
||||
.and_then(|m| m.as_object())
|
||||
.ok_or_else(|| {
|
||||
Error::internal_err(
|
||||
"PowerShell modules.json must have a \"modules\" object".to_string(),
|
||||
)
|
||||
})?;
|
||||
let mut result = Vec::new();
|
||||
for (name, version) in modules {
|
||||
let version = match version {
|
||||
serde_json::Value::String(v) if v != "*" => Some(v.clone()),
|
||||
_ => None,
|
||||
};
|
||||
result.push(ModuleRequest { name: name.clone(), version });
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Merge workspace dependency modules with script import modules.
|
||||
/// Workspace dependency versions take precedence on overlap.
|
||||
fn merge_module_requests(
|
||||
workspace_modules: Vec<ModuleRequest>,
|
||||
script_modules: Vec<ModuleRequest>,
|
||||
) -> Vec<ModuleRequest> {
|
||||
let mut seen: HashMap<String, ModuleRequest> = HashMap::new();
|
||||
// Script imports first (lower priority)
|
||||
for m in script_modules {
|
||||
let key = m.name.to_lowercase();
|
||||
seen.entry(key).or_insert(m);
|
||||
}
|
||||
// Workspace deps override
|
||||
for m in workspace_modules {
|
||||
let key = m.name.to_lowercase();
|
||||
seen.insert(key, m);
|
||||
}
|
||||
seen.into_values().collect()
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub async fn handle_powershell_job(
|
||||
maybe_lock: MaybeLock,
|
||||
mem_peak: &mut i32,
|
||||
canceled_by: &mut Option<CanceledBy>,
|
||||
job: &MiniPulledJob,
|
||||
@@ -387,29 +303,18 @@ pub async fn handle_powershell_job(
|
||||
.join(" ")
|
||||
};
|
||||
|
||||
// Resolve modules from workspace dependencies and/or script imports
|
||||
let all_modules = match &maybe_lock {
|
||||
MaybeLock::Resolved { lock } if !lock.is_empty() => {
|
||||
// Deployed script with lock: parse workspace deps from lock, merge with script imports
|
||||
let ws_modules = parse_modules_json(lock)?;
|
||||
let script_modules = parse_script_imports(content);
|
||||
merge_module_requests(ws_modules, script_modules)
|
||||
// First, collect all imported modules
|
||||
let mut imported_modules: Vec<(String, Option<String>)> = Vec::new();
|
||||
for line in content.lines() {
|
||||
for cap in RE_POWERSHELL_IMPORTS.captures_iter(line) {
|
||||
let module_name = cap.get(1).unwrap().as_str().to_string();
|
||||
let required_version = cap.get(2).map(|m| m.as_str().to_string());
|
||||
imported_modules.push((module_name, required_version));
|
||||
}
|
||||
MaybeLock::Unresolved { workspace_dependencies } => {
|
||||
let script_modules = parse_script_imports(content);
|
||||
match workspace_dependencies.get_powershell()? {
|
||||
Some(modules_json) => {
|
||||
let ws_modules = parse_modules_json(&modules_json)?;
|
||||
merge_module_requests(ws_modules, script_modules)
|
||||
}
|
||||
None => script_modules,
|
||||
}
|
||||
}
|
||||
_ => parse_script_imports(content),
|
||||
};
|
||||
}
|
||||
|
||||
// Only scan the top-level cache directory if there are modules to check
|
||||
let module_dirs = if !all_modules.is_empty() {
|
||||
let module_dirs = if !imported_modules.is_empty() {
|
||||
scan_module_directories().await?
|
||||
} else {
|
||||
HashMap::new()
|
||||
@@ -418,20 +323,19 @@ pub async fn handle_powershell_job(
|
||||
let mut modules_to_install: Vec<ModuleRequest> = Vec::new();
|
||||
let mut logs1 = String::new();
|
||||
|
||||
for module_req in all_modules {
|
||||
for (module_name, required_version) in imported_modules {
|
||||
// Check if this specific module is already installed, only scanning versions if needed
|
||||
let (is_installed, installed_versions) = check_module_installed(
|
||||
&module_dirs,
|
||||
&module_req.name,
|
||||
module_req.version.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
let (is_installed, installed_versions) =
|
||||
check_module_installed(&module_dirs, &module_name, required_version.as_deref()).await?;
|
||||
|
||||
if !is_installed {
|
||||
modules_to_install.push(module_req);
|
||||
modules_to_install.push(ModuleRequest {
|
||||
name: module_name.clone(),
|
||||
version: required_version.clone(),
|
||||
});
|
||||
} else {
|
||||
// Log what versions are actually installed
|
||||
let version_info = if let Some(version) = &module_req.version {
|
||||
let version_info = if let Some(version) = &required_version {
|
||||
format!(" version {} found in cache", version)
|
||||
} else if installed_versions.len() == 1 {
|
||||
format!(" (version {}) found in cache", installed_versions[0])
|
||||
@@ -443,7 +347,7 @@ pub async fn handle_powershell_job(
|
||||
} else {
|
||||
" found in cache".to_string()
|
||||
};
|
||||
logs1.push_str(&format!("\n{}{}", module_req.name, version_info));
|
||||
logs1.push_str(&format!("\n{}{}", module_name, version_info));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,8 +466,8 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
|
||||
|
||||
let strict_termination_end = "\n\
|
||||
} catch {\n\
|
||||
Write-Output \"An error occurred:\"\n\
|
||||
Write-Output $_\n\
|
||||
Write-Output \"An error occurred:\n\"\
|
||||
Write-Output $_
|
||||
exit 1\n\
|
||||
}\n";
|
||||
|
||||
@@ -614,8 +518,6 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
|
||||
|
||||
let nsjail = is_sandboxing_enabled() && is_regular_job;
|
||||
let child = if nsjail {
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(db, &job.workspace_id, job.id, job.timeout).await;
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -623,8 +525,7 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{CACHE_DIR}", &*POWERSHELL_CACHE_DIR)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("{CACHE_DIR}", &*POWERSHELL_CACHE_DIR),
|
||||
)?;
|
||||
let cmd_args = vec![
|
||||
"--config",
|
||||
@@ -771,317 +672,3 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
|
||||
"No result.out, result2.out or result.json found"
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use tempfile::TempDir;
|
||||
|
||||
// --- RE_POWERSHELL_IMPORTS regex tests ---
|
||||
|
||||
fn match_import(line: &str) -> Option<(String, Option<String>)> {
|
||||
RE_POWERSHELL_IMPORTS.captures(line).map(|cap| {
|
||||
let name = cap.get(1).unwrap().as_str().to_string();
|
||||
let version = cap.get(2).map(|m| m.as_str().to_string());
|
||||
(name, version)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_module_basic() {
|
||||
let (name, version) = match_import("Import-Module WindmillClient").unwrap();
|
||||
assert_eq!(name, "WindmillClient");
|
||||
assert_eq!(version, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_module_with_leading_whitespace() {
|
||||
let (name, _) = match_import(" Import-Module WindmillClient").unwrap();
|
||||
assert_eq!(name, "WindmillClient");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_module_with_tab_indent() {
|
||||
let (name, _) = match_import("\tImport-Module WindmillClient").unwrap();
|
||||
assert_eq!(name, "WindmillClient");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_module_with_name_flag() {
|
||||
let (name, _) = match_import("Import-Module -Name WindmillClient").unwrap();
|
||||
assert_eq!(name, "WindmillClient");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_module_with_required_version() {
|
||||
let (name, version) =
|
||||
match_import(r#"Import-Module WindmillClient -RequiredVersion "1.655.0""#).unwrap();
|
||||
assert_eq!(name, "WindmillClient");
|
||||
assert_eq!(version, Some("1.655.0".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_module_quoted_name() {
|
||||
let (name, _) = match_import(r#"Import-Module "WindmillClient""#).unwrap();
|
||||
assert_eq!(name, "WindmillClient");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_module_name_flag_quoted_with_version() {
|
||||
let (name, version) =
|
||||
match_import(r#"Import-Module -Name "WindmillClient" -RequiredVersion "2.0.0""#)
|
||||
.unwrap();
|
||||
assert_eq!(name, "WindmillClient");
|
||||
assert_eq!(version, Some("2.0.0".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_module_indented_with_version() {
|
||||
let (name, version) =
|
||||
match_import(r#" Import-Module WindmillClient -RequiredVersion 1.0.0"#).unwrap();
|
||||
assert_eq!(name, "WindmillClient");
|
||||
assert_eq!(version, Some("1.0.0".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_commented_import_not_matched() {
|
||||
assert!(match_import("# Import-Module WindmillClient").is_none());
|
||||
}
|
||||
|
||||
// --- get_module_versions / check_module_installed tests ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_empty_module_dir_not_installed() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("WindmillClient");
|
||||
fs::create_dir(&module_dir).unwrap();
|
||||
|
||||
let versions = get_module_versions(module_dir.to_str().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(versions.is_empty(), "empty dir should have no versions");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_empty_version_subdir_not_installed() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("WindmillClient");
|
||||
let version_dir = module_dir.join("1.655.0");
|
||||
fs::create_dir_all(&version_dir).unwrap();
|
||||
|
||||
let versions = get_module_versions(module_dir.to_str().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
versions.is_empty(),
|
||||
"version dir without .psd1/.psm1 should not count"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_valid_versioned_module_detected() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("WindmillClient");
|
||||
let version_dir = module_dir.join("1.655.0");
|
||||
fs::create_dir_all(&version_dir).unwrap();
|
||||
fs::write(version_dir.join("WindmillClient.psd1"), "# manifest").unwrap();
|
||||
fs::write(version_dir.join("WindmillClient.psm1"), "# module").unwrap();
|
||||
|
||||
let versions = get_module_versions(module_dir.to_str().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(versions, vec!["1.655.0"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_flat_module_with_files_detected() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("MyModule");
|
||||
fs::create_dir(&module_dir).unwrap();
|
||||
fs::write(module_dir.join("MyModule.psm1"), "# module").unwrap();
|
||||
|
||||
let versions = get_module_versions(module_dir.to_str().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(versions, vec!["unknown"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_flat_module_without_files_not_detected() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("MyModule");
|
||||
fs::create_dir(&module_dir).unwrap();
|
||||
fs::write(module_dir.join("readme.txt"), "not a module").unwrap();
|
||||
|
||||
let versions = get_module_versions(module_dir.to_str().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(versions.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_module_installed_empty_dir_returns_false() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("WindmillClient");
|
||||
fs::create_dir(&module_dir).unwrap();
|
||||
|
||||
let mut dirs = HashMap::new();
|
||||
dirs.insert(
|
||||
"windmillclient".to_string(),
|
||||
module_dir.to_str().unwrap().to_string(),
|
||||
);
|
||||
|
||||
let (installed, _) = check_module_installed(&dirs, "WindmillClient", None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
!installed,
|
||||
"empty module dir should not be considered installed"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_module_installed_valid_module_returns_true() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("WindmillClient");
|
||||
let version_dir = module_dir.join("1.655.0");
|
||||
fs::create_dir_all(&version_dir).unwrap();
|
||||
fs::write(version_dir.join("WindmillClient.psd1"), "# manifest").unwrap();
|
||||
|
||||
let mut dirs = HashMap::new();
|
||||
dirs.insert(
|
||||
"windmillclient".to_string(),
|
||||
module_dir.to_str().unwrap().to_string(),
|
||||
);
|
||||
|
||||
let (installed, versions) = check_module_installed(&dirs, "WindmillClient", None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(installed);
|
||||
assert_eq!(versions, vec!["1.655.0"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_check_module_installed_wrong_version_returns_false() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("WindmillClient");
|
||||
let version_dir = module_dir.join("1.0.0");
|
||||
fs::create_dir_all(&version_dir).unwrap();
|
||||
fs::write(version_dir.join("WindmillClient.psd1"), "# manifest").unwrap();
|
||||
|
||||
let mut dirs = HashMap::new();
|
||||
dirs.insert(
|
||||
"windmillclient".to_string(),
|
||||
module_dir.to_str().unwrap().to_string(),
|
||||
);
|
||||
|
||||
let (installed, _) = check_module_installed(&dirs, "WindmillClient", Some("2.0.0"))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!installed, "wrong version should not match");
|
||||
}
|
||||
|
||||
// --- parse_modules_json tests ---
|
||||
|
||||
#[test]
|
||||
fn test_parse_modules_json_basic() {
|
||||
let json = r#"{"modules": {"PSWriteColor": "1.0.0", "ImportExcel": null}}"#;
|
||||
let modules = parse_modules_json(json).unwrap();
|
||||
assert_eq!(modules.len(), 2);
|
||||
let by_name: HashMap<String, Option<String>> =
|
||||
modules.into_iter().map(|m| (m.name, m.version)).collect();
|
||||
assert_eq!(by_name["PSWriteColor"], Some("1.0.0".to_string()));
|
||||
assert_eq!(by_name["ImportExcel"], None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_modules_json_wildcard_treated_as_none() {
|
||||
let json = r#"{"modules": {"Mod": "*"}}"#;
|
||||
let modules = parse_modules_json(json).unwrap();
|
||||
assert_eq!(modules[0].version, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_modules_json_empty() {
|
||||
let json = r#"{"modules": {}}"#;
|
||||
let modules = parse_modules_json(json).unwrap();
|
||||
assert!(modules.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_modules_json_missing_modules_key() {
|
||||
let json = r#"{"deps": {}}"#;
|
||||
assert!(parse_modules_json(json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_modules_json_invalid_json() {
|
||||
assert!(parse_modules_json("not json").is_err());
|
||||
}
|
||||
|
||||
// --- parse_script_imports tests ---
|
||||
|
||||
#[test]
|
||||
fn test_parse_script_imports() {
|
||||
let code = r#"Import-Module PSWriteColor
|
||||
Import-Module ImportExcel -RequiredVersion "7.8.6"
|
||||
# Import-Module Commented
|
||||
Write-Host "Hello""#;
|
||||
let modules = parse_script_imports(code);
|
||||
assert_eq!(modules.len(), 2);
|
||||
assert_eq!(modules[0].name, "PSWriteColor");
|
||||
assert_eq!(modules[0].version, None);
|
||||
assert_eq!(modules[1].name, "ImportExcel");
|
||||
assert_eq!(modules[1].version, Some("7.8.6".to_string()));
|
||||
}
|
||||
|
||||
// --- merge_module_requests tests ---
|
||||
|
||||
#[test]
|
||||
fn test_merge_workspace_overrides_script() {
|
||||
let ws = vec![ModuleRequest { name: "Mod".to_string(), version: Some("2.0".to_string()) }];
|
||||
let script =
|
||||
vec![ModuleRequest { name: "Mod".to_string(), version: Some("1.0".to_string()) }];
|
||||
let merged = merge_module_requests(ws, script);
|
||||
assert_eq!(merged.len(), 1);
|
||||
assert_eq!(merged[0].version, Some("2.0".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_combines_distinct_modules() {
|
||||
let ws = vec![ModuleRequest { name: "WsMod".to_string(), version: None }];
|
||||
let script = vec![ModuleRequest { name: "ScriptMod".to_string(), version: None }];
|
||||
let merged = merge_module_requests(ws, script);
|
||||
assert_eq!(merged.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_case_insensitive() {
|
||||
let ws =
|
||||
vec![ModuleRequest { name: "MyModule".to_string(), version: Some("2.0".to_string()) }];
|
||||
let script =
|
||||
vec![ModuleRequest { name: "mymodule".to_string(), version: Some("1.0".to_string()) }];
|
||||
let merged = merge_module_requests(ws, script);
|
||||
assert_eq!(merged.len(), 1);
|
||||
// Workspace version wins
|
||||
assert_eq!(merged[0].version, Some("2.0".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_multiple_versions_detected() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let module_dir = tmp.path().join("WindmillClient");
|
||||
for ver in &["1.0.0", "1.655.0"] {
|
||||
let version_dir = module_dir.join(ver);
|
||||
fs::create_dir_all(&version_dir).unwrap();
|
||||
fs::write(version_dir.join("WindmillClient.psd1"), "# manifest").unwrap();
|
||||
}
|
||||
|
||||
let mut versions = get_module_versions(module_dir.to_str().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
versions.sort();
|
||||
assert_eq!(versions, vec!["1.0.0", "1.655.0"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +128,7 @@ use windmill_object_store::OBJECT_STORE_SETTINGS;
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables, read_file,
|
||||
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics, StreamNotifier,
|
||||
DEV_CONF_NSJAIL,
|
||||
read_result, start_child_process, OccupancyMetrics, StreamNotifier, DEV_CONF_NSJAIL,
|
||||
},
|
||||
get_proxy_envs_for_lang,
|
||||
handle_child::handle_child,
|
||||
@@ -876,8 +875,6 @@ mount {{
|
||||
)
|
||||
})
|
||||
.join("\n");
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -894,8 +891,7 @@ mount {{
|
||||
additional_python_paths_folders.as_str(),
|
||||
)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL),
|
||||
)?;
|
||||
} else {
|
||||
reserved_variables.insert("PYTHONPATH".to_string(), additional_python_paths_folders);
|
||||
|
||||
@@ -23,8 +23,7 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
|
||||
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
|
||||
DEV_CONF_NSJAIL,
|
||||
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
},
|
||||
get_proxy_envs_for_lang,
|
||||
handle_child::{self},
|
||||
@@ -793,8 +792,6 @@ mount {{
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -804,8 +801,7 @@ mount {{
|
||||
.replace("{SHARED_DEPENDENCIES}", &shared_deps)
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()),
|
||||
)?;
|
||||
let mut cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
cmd.env_clear()
|
||||
|
||||
@@ -23,8 +23,8 @@ use windmill_queue::{append_logs, CanceledBy};
|
||||
use crate::{
|
||||
common::{
|
||||
build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file,
|
||||
get_reserved_variables, read_result, resolve_nsjail_timeout, start_child_process,
|
||||
OccupancyMetrics, DEV_CONF_NSJAIL,
|
||||
get_reserved_variables, read_result, start_child_process, OccupancyMetrics,
|
||||
DEV_CONF_NSJAIL,
|
||||
},
|
||||
get_proxy_envs_for_lang,
|
||||
handle_child::handle_child,
|
||||
@@ -682,8 +682,6 @@ pub async fn handle_rust_job(
|
||||
append_logs(&job.id, &job.workspace_id, logs2, conn).await;
|
||||
|
||||
let child = if is_sandboxing_enabled() {
|
||||
let nsjail_timeout =
|
||||
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
|
||||
let _ = write_file(
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
@@ -694,8 +692,7 @@ pub async fn handle_rust_job(
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
|
||||
.replace("#{DEV}", DEV_CONF_NSJAIL)
|
||||
.replace("{SHARED_MOUNT}", shared_mount)
|
||||
.replace("{TIMEOUT}", &nsjail_timeout),
|
||||
.replace("{SHARED_MOUNT}", shared_mount),
|
||||
)?;
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
nsjail_cmd
|
||||
|
||||
@@ -4562,17 +4562,7 @@ mount {{
|
||||
"Inline execution is not yet supported for this language".to_string(),
|
||||
));
|
||||
}
|
||||
let maybe_lock = resolve_maybe_lock(
|
||||
&lock,
|
||||
&code,
|
||||
language,
|
||||
&job.workspace_id,
|
||||
job.runnable_path(),
|
||||
conn.clone(),
|
||||
)
|
||||
.await?;
|
||||
Box::pin(handle_powershell_job(
|
||||
maybe_lock,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
job,
|
||||
|
||||
+1
-1
@@ -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.658.0";
|
||||
export const VERSION = "v1.657.0";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
@@ -252,16 +252,6 @@ async function initAction(opts: InitOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
// Read nonDottedPaths from config to specialize generated skills
|
||||
let nonDottedPaths = true; // default for new inits
|
||||
try {
|
||||
const { readConfigFile } = await import("../../core/conf.ts");
|
||||
const config = await readConfigFile();
|
||||
nonDottedPaths = config.nonDottedPaths ?? true;
|
||||
} catch {
|
||||
// If config can't be read, use default
|
||||
}
|
||||
|
||||
// Create guidance files (AGENTS.md, CLAUDE.md, and Claude skills)
|
||||
try {
|
||||
// Generate skills reference section for AGENTS.md
|
||||
@@ -300,20 +290,6 @@ async function initAction(opts: InitOptions) {
|
||||
|
||||
let skillContent = SKILL_CONTENT[skill.name];
|
||||
if (skillContent) {
|
||||
// Replace placeholders with actual suffixes based on nonDottedPaths
|
||||
if (nonDottedPaths) {
|
||||
skillContent = skillContent
|
||||
.replaceAll("{{FLOW_SUFFIX}}", "__flow")
|
||||
.replaceAll("{{APP_SUFFIX}}", "__app")
|
||||
.replaceAll("{{RAW_APP_SUFFIX}}", "__raw_app")
|
||||
.replaceAll("{{INLINE_SCRIPT_NAMING}}", "Inline script files should NOT include `.inline_script.` in their names (e.g. use `a.ts`, not `a.inline_script.ts`).");
|
||||
} else {
|
||||
skillContent = skillContent
|
||||
.replaceAll("{{FLOW_SUFFIX}}", ".flow")
|
||||
.replaceAll("{{APP_SUFFIX}}", ".app")
|
||||
.replaceAll("{{RAW_APP_SUFFIX}}", ".raw_app")
|
||||
.replaceAll("{{INLINE_SCRIPT_NAMING}}", "Inline script files use the `.inline_script.` naming convention (e.g. `a.inline_script.ts`).");
|
||||
}
|
||||
// Check if this skill has schemas that need to be appended
|
||||
const schemaMappings = SCHEMA_MAPPINGS[skill.name];
|
||||
if (schemaMappings && schemaMappings.length > 0) {
|
||||
|
||||
@@ -230,7 +230,6 @@ export async function pushNativeTrigger(
|
||||
script_path: result.script_path,
|
||||
is_flow: result.is_flow,
|
||||
service_config: result.service_config,
|
||||
summary: result.summary,
|
||||
error: result.error,
|
||||
};
|
||||
log.debug(`Native trigger ${serviceName}/${externalId} exists on remote`);
|
||||
@@ -244,7 +243,6 @@ export async function pushNativeTrigger(
|
||||
script_path: localTrigger.script_path,
|
||||
is_flow: localTrigger.is_flow,
|
||||
service_config: localTrigger.service_config,
|
||||
summary: localTrigger.summary || undefined,
|
||||
};
|
||||
|
||||
if (remoteTrigger) {
|
||||
@@ -253,13 +251,11 @@ export async function pushNativeTrigger(
|
||||
script_path: localTrigger.script_path,
|
||||
is_flow: localTrigger.is_flow,
|
||||
service_config: localTrigger.service_config,
|
||||
summary: localTrigger.summary,
|
||||
};
|
||||
const remoteCompare = {
|
||||
script_path: remoteTrigger.script_path,
|
||||
is_flow: remoteTrigger.is_flow,
|
||||
service_config: remoteTrigger.service_config,
|
||||
summary: remoteTrigger.summary,
|
||||
};
|
||||
|
||||
if (isSuperset(localCompare, remoteCompare)) {
|
||||
|
||||
@@ -4236,10 +4236,10 @@ description: MUST use when creating flows.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Create a folder ending with \`{{FLOW_SUFFIX}}\` and add a \`flow.yaml\` file with the flow definition.
|
||||
For rawscript modules, use \`!inline path/to/script.ts\` for the content key. {{INLINE_SCRIPT_NAMING}}
|
||||
Create a folder ending with \`.flow\` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use \`!inline path/to/script.ts\` for the content key.
|
||||
After writing, tell the user they can run:
|
||||
- \`wmill flow generate-locks <path_to_flow_folder> --yes\` - Generate lock files for the specific flow you modified (e.g. \`wmill flow generate-locks f/my_folder/my_flow{{FLOW_SUFFIX}} --yes\`)
|
||||
- \`wmill flow generate-locks <path_to_flow_folder> --yes\` - Generate lock files for the specific flow you modified (e.g. \`wmill flow generate-locks f/my_folder/my_flow.flow --yes\`)
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
@@ -4375,7 +4375,7 @@ This interactive command creates a complete app structure with your choice of fr
|
||||
## App Structure
|
||||
|
||||
\`\`\`
|
||||
my_app{{RAW_APP_SUFFIX}}/
|
||||
my_app.raw_app/
|
||||
├── AGENTS.md # AI agent instructions (auto-generated)
|
||||
├── DATATABLES.md # Database schemas (run 'wmill app generate-agents' to refresh)
|
||||
├── raw_app.yaml # App configuration (summary, path, data settings)
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ export {
|
||||
workspaceAdd,
|
||||
};
|
||||
|
||||
export const VERSION = "1.658.0";
|
||||
export const VERSION = "1.657.0";
|
||||
|
||||
// Re-exported from constants.ts to maintain backwards compatibility
|
||||
export { WM_FORK_PREFIX } from "./core/constants.ts";
|
||||
|
||||
@@ -328,7 +328,6 @@ const LANG_ANNOTATION_CONFIG: Partial<
|
||||
nativets: { comment: "//", keyword: "package_json" },
|
||||
go: { comment: "//", keyword: "go_mod" },
|
||||
php: { comment: "//", keyword: "composer_json" },
|
||||
powershell: { comment: "#", keyword: "modules_json" },
|
||||
};
|
||||
|
||||
export function extractWorkspaceDepsAnnotation(
|
||||
@@ -506,8 +505,7 @@ async function updateScriptLock(
|
||||
): Promise<void> {
|
||||
if (
|
||||
!(
|
||||
(workspaceDependenciesLanguages.some((l) => l.language == language) &&
|
||||
language !== "powershell") ||
|
||||
workspaceDependenciesLanguages.some((l) => l.language == language) ||
|
||||
language == "deno" ||
|
||||
language == "rust" ||
|
||||
language == "ansible"
|
||||
|
||||
@@ -30,15 +30,13 @@ export type WorkspaceDependenciesLanguage =
|
||||
| { language: "bun", filename /** (raw requirements filename) */: "package.json" }
|
||||
| { language: "python3", filename: "requirements.in" }
|
||||
| { language: "php", filename: "composer.json" }
|
||||
| { language: "go", filename: "go.mod" }
|
||||
| { language: "powershell", filename: "modules.json" };
|
||||
| { language: "go", filename: "go.mod" };
|
||||
|
||||
export const workspaceDependenciesLanguages: WorkspaceDependenciesLanguage[] = [
|
||||
{ language: "bun", filename: "package.json" },
|
||||
{ language: "python3", filename: "requirements.in" },
|
||||
{ language: "php", filename: "composer.json" },
|
||||
{ language: "go", filename: "go.mod" },
|
||||
{ language: "powershell", filename: "modules.json" },
|
||||
] as const;
|
||||
|
||||
/**
|
||||
@@ -47,8 +45,7 @@ export const workspaceDependenciesLanguages: WorkspaceDependenciesLanguage[] = [
|
||||
*/
|
||||
export function languageNeedsLock(language: ScriptLanguage | string): boolean {
|
||||
return (
|
||||
(workspaceDependenciesLanguages.some((l) => l.language === language) &&
|
||||
language !== "powershell") ||
|
||||
workspaceDependenciesLanguages.some((l) => l.language === language) ||
|
||||
language === "deno" ||
|
||||
language === "rust" ||
|
||||
language === "ansible"
|
||||
|
||||
@@ -229,20 +229,13 @@
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
devEnvVars = {
|
||||
DATABASE_URL = "postgres://postgres:changeme@127.0.0.1:5432/windmill?sslmode=disable";
|
||||
REMOTE = "http://127.0.0.1:8000";
|
||||
REMOTE_LSP = "http://127.0.0.1:3001";
|
||||
NODE_ENV = "development";
|
||||
NODE_OPTIONS = "--max-old-space-size=16384";
|
||||
};
|
||||
|
||||
# Connection-specific defaults — set via shellHook so they respect
|
||||
# pre-existing values (e.g. from webmux runtime.env / .env.local).
|
||||
# Nix attrs are injected unconditionally and would override per-worktree
|
||||
# values set by webmux before the interactive shell starts.
|
||||
devShellHook = ''
|
||||
export DATABASE_URL="''${DATABASE_URL:-postgres://postgres:changeme@127.0.0.1:5432/windmill?sslmode=disable}"
|
||||
export REMOTE="''${REMOTE:-http://127.0.0.1:8000}"
|
||||
export REMOTE_LSP="''${REMOTE_LSP:-http://127.0.0.1:3001}"
|
||||
'';
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Helper scripts — base set (default + full)
|
||||
# ---------------------------------------------------------------
|
||||
@@ -409,7 +402,6 @@
|
||||
# =============================================================
|
||||
|
||||
devShells.default = pkgs.mkShell (buildEnvVars // commonRuntimeVars // devEnvVars // browserVars // {
|
||||
shellHook = devShellHook;
|
||||
buildInputs = coreBuildInputs;
|
||||
|
||||
packages = helperScriptsBase ++ [ playwrightWrapper ];
|
||||
@@ -421,7 +413,6 @@
|
||||
# =============================================================
|
||||
|
||||
devShells.full = pkgs.mkShell (buildEnvVars // commonRuntimeVars // extraRuntimeVars // devEnvVars // browserVars // {
|
||||
shellHook = devShellHook;
|
||||
buildInputs = coreBuildInputs ++ extraRuntimes ++ (with pkgs; [
|
||||
# Python extras
|
||||
poetry
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.658.0",
|
||||
"version": "1.657.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.658.0",
|
||||
"version": "1.657.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.658.0",
|
||||
"version": "1.657.0",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
resourceType: string
|
||||
resourceTypeInfo: ResourceType | undefined
|
||||
args?: Record<string, any> | any
|
||||
linkedSecrets?: string[]
|
||||
linkedSecret?: string | undefined
|
||||
isValid?: boolean
|
||||
linkedSecretCandidates?: string[] | undefined
|
||||
description?: string | undefined
|
||||
@@ -31,7 +31,7 @@
|
||||
resourceType,
|
||||
resourceTypeInfo,
|
||||
args = $bindable({}),
|
||||
linkedSecrets = $bindable([]),
|
||||
linkedSecret = $bindable(undefined),
|
||||
isValid = $bindable(true),
|
||||
linkedSecretCandidates = undefined,
|
||||
description = $bindable(undefined)
|
||||
@@ -152,7 +152,7 @@
|
||||
/>
|
||||
<ResourceGen
|
||||
bind:args
|
||||
{resourceType}
|
||||
resourceType={resourceType}
|
||||
resourceSchema={notFound ? undefined : schema}
|
||||
isFileset={resourceTypeInfo?.is_fileset ?? false}
|
||||
/>
|
||||
@@ -246,7 +246,9 @@
|
||||
{/await}
|
||||
</div>
|
||||
{:else if resourceTypeInfo?.is_fileset}
|
||||
<h5 class="mt-1 inline-flex items-center gap-4"> Fileset </h5>
|
||||
<h5 class="mt-1 inline-flex items-center gap-4">
|
||||
Fileset
|
||||
</h5>
|
||||
<FilesetEditor bind:args />
|
||||
{:else if resourceTypeInfo?.format_extension}
|
||||
<h5 class="mt-4 inline-flex items-center gap-4">
|
||||
@@ -271,7 +273,7 @@
|
||||
onlyMaskPassword
|
||||
noDelete
|
||||
{linkedSecretCandidates}
|
||||
bind:linkedSecrets
|
||||
bind:linkedSecret
|
||||
isValid
|
||||
{schema}
|
||||
bind:args
|
||||
|
||||
@@ -84,22 +84,14 @@
|
||||
)
|
||||
}
|
||||
|
||||
let linkedSecrets: string[] = $state([])
|
||||
let linkedSecret: string | undefined = $state(undefined)
|
||||
let linkedSecretCandidates: string[] | undefined = $state(undefined)
|
||||
function computeDefaultLinkedSecrets(
|
||||
resourceType: string,
|
||||
argsKeys: string[],
|
||||
passwords: string[]
|
||||
): string[] {
|
||||
function computeLinkedSecret(resourceType: string, argsKeys: string[], passwords: string[]) {
|
||||
linkedSecretCandidates = computeCandidates(resourceType, argsKeys, passwords)
|
||||
const forced = forceSecretValue(resourceType)
|
||||
if (forced) {
|
||||
return [forced]
|
||||
}
|
||||
const best = linkedSecretCandidates?.sort(
|
||||
(ua, ub) => linkedSecretValue(ub) - linkedSecretValue(ua)
|
||||
)?.[0]
|
||||
return best ? [best] : []
|
||||
return (
|
||||
forceSecretValue(resourceType) ??
|
||||
linkedSecretCandidates?.sort((ua, ub) => linkedSecretValue(ub) - linkedSecretValue(ua))?.[0]
|
||||
)
|
||||
}
|
||||
|
||||
let scopes: string[] = $state([])
|
||||
@@ -202,7 +194,7 @@
|
||||
args['password'] == '' &&
|
||||
args['api_key'] == '' &&
|
||||
args['key'] == '' &&
|
||||
linkedSecrets.length > 0
|
||||
linkedSecret != undefined
|
||||
: false)) ||
|
||||
step == 3 ||
|
||||
(step == 4 && pathError != '') ||
|
||||
@@ -325,13 +317,13 @@
|
||||
const passwords = newArgsKeys.filter((x) => {
|
||||
return props?.[x]?.password
|
||||
})
|
||||
if (linkedSecrets.length === 0) {
|
||||
linkedSecrets = computeDefaultLinkedSecrets(resourceType, newArgsKeys, passwords)
|
||||
if (!linkedSecret) {
|
||||
linkedSecret = computeLinkedSecret(resourceType, newArgsKeys, passwords)
|
||||
}
|
||||
}
|
||||
export async function next() {
|
||||
if (step == 1) {
|
||||
linkedSecrets = []
|
||||
linkedSecret = undefined
|
||||
if (manual) {
|
||||
getResourceTypeInfo()
|
||||
args = {}
|
||||
@@ -416,30 +408,14 @@
|
||||
if (step == 2) return
|
||||
throw Error('Path is not set')
|
||||
}
|
||||
// Check if variable paths already exist
|
||||
if (!manual || linkedSecrets.length <= 1) {
|
||||
const exists = await VariableService.existsVariable({
|
||||
workspace: $workspaceStore!,
|
||||
path
|
||||
})
|
||||
if (exists) {
|
||||
throw Error(`Variable at path ${path} already exists. Delete it or pick another path`)
|
||||
}
|
||||
} else {
|
||||
for (const secretField of linkedSecrets) {
|
||||
const varPath = `${path}_${secretField}`
|
||||
const exists = await VariableService.existsVariable({
|
||||
workspace: $workspaceStore!,
|
||||
path: varPath
|
||||
})
|
||||
if (exists) {
|
||||
throw Error(
|
||||
`Variable at path ${varPath} already exists. Delete it or pick another path`
|
||||
)
|
||||
}
|
||||
}
|
||||
let exists = await VariableService.existsVariable({
|
||||
workspace: $workspaceStore!,
|
||||
path
|
||||
})
|
||||
if (exists) {
|
||||
throw Error(`Variable at path ${path} already exists. Delete it or pick another path`)
|
||||
}
|
||||
let exists = await ResourceService.existsResource({
|
||||
exists = await ResourceService.existsResource({
|
||||
workspace: $workspaceStore!,
|
||||
path
|
||||
})
|
||||
@@ -486,65 +462,25 @@
|
||||
|
||||
const resourceValue = args
|
||||
|
||||
let savedVariableCount = 0
|
||||
if (!manual) {
|
||||
// OAuth flow: single secret variable for the token
|
||||
if (typeof value == 'string' && value != '' && !value.startsWith('$var:')) {
|
||||
savedVariableCount++
|
||||
await VariableService.createVariable({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path,
|
||||
value: value,
|
||||
is_secret: true,
|
||||
description: emptyString(description)
|
||||
? `OAuth token for ${resourceType}`
|
||||
: description,
|
||||
is_oauth: true,
|
||||
account: account
|
||||
}
|
||||
})
|
||||
resourceValue['token'] = `$var:${path}`
|
||||
}
|
||||
} else if (linkedSecrets.length === 1) {
|
||||
// Single secret: use the resource path as variable name (original behavior)
|
||||
const secretField = linkedSecrets[0]
|
||||
const v = args[secretField]
|
||||
let saveVariable = false
|
||||
if (!manual || linkedSecret != undefined) {
|
||||
let v = manual ? args[linkedSecret ?? ''] : value
|
||||
if (typeof v == 'string' && v != '' && !v.startsWith('$var:')) {
|
||||
savedVariableCount++
|
||||
saveVariable = true
|
||||
await VariableService.createVariable({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path,
|
||||
value: v,
|
||||
is_secret: true,
|
||||
description: emptyString(description) ? `Token for ${resourceType}` : description,
|
||||
is_oauth: false
|
||||
description: emptyString(description)
|
||||
? `${manual ? 'Token' : 'OAuth token'} for ${resourceType}`
|
||||
: description,
|
||||
is_oauth: !manual,
|
||||
account: account
|
||||
}
|
||||
})
|
||||
resourceValue[secretField] = `$var:${path}`
|
||||
}
|
||||
} else if (linkedSecrets.length > 1) {
|
||||
// Multiple secrets: append _field_name to each variable path
|
||||
for (const secretField of linkedSecrets) {
|
||||
const v = args[secretField]
|
||||
if (typeof v == 'string' && v != '' && !v.startsWith('$var:')) {
|
||||
const varPath = `${path}_${secretField}`
|
||||
savedVariableCount++
|
||||
await VariableService.createVariable({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path: varPath,
|
||||
value: v,
|
||||
is_secret: true,
|
||||
description: emptyString(description)
|
||||
? `${secretField} for ${resourceType}`
|
||||
: description,
|
||||
is_oauth: false
|
||||
}
|
||||
})
|
||||
resourceValue[secretField] = `$var:${varPath}`
|
||||
}
|
||||
resourceValue[linkedSecret ?? 'token'] = `$var:${path}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,9 +495,7 @@
|
||||
})
|
||||
dispatch('refresh', path)
|
||||
dispatch('close')
|
||||
sendUserToast(
|
||||
`Saved resource${savedVariableCount > 0 ? ` and ${savedVariableCount} variable${savedVariableCount > 1 ? 's' : ''}` : ''} path: ${path}`
|
||||
)
|
||||
sendUserToast(`Saved resource${saveVariable ? ' and variable' : ''} path: ${path}`)
|
||||
step = 1
|
||||
resourceType = ''
|
||||
}
|
||||
@@ -804,7 +738,7 @@
|
||||
|
||||
{#key resourceTypeInfo}
|
||||
<ApiConnectForm
|
||||
bind:linkedSecrets
|
||||
bind:linkedSecret
|
||||
bind:description
|
||||
{linkedSecretCandidates}
|
||||
{resourceType}
|
||||
|
||||
@@ -652,13 +652,6 @@
|
||||
onDone={async ({ job: completedJob }) => {
|
||||
isRunning = false
|
||||
$executionCount = $executionCount + 1
|
||||
// Reset 'initial' flags for modules that were part of this flow test,
|
||||
// so OutputPicker no longer shows "Run loaded from history"
|
||||
for (const mod of completedJob.flow_status?.modules ?? []) {
|
||||
if (mod.id) {
|
||||
stepHistoryLoader?.resetInitial(mod.id)
|
||||
}
|
||||
}
|
||||
if (flowRecording.active) {
|
||||
lastRecording = flowRecording.stop()
|
||||
setActiveRecording(undefined)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
defaultValues?: Record<string, any>
|
||||
shouldHideNoInputs?: boolean
|
||||
compact?: boolean
|
||||
linkedSecrets?: string[]
|
||||
linkedSecret?: string | undefined
|
||||
linkedSecretCandidates?: string[] | undefined
|
||||
noVariablePicker?: boolean
|
||||
flexWrap?: boolean
|
||||
@@ -86,7 +86,7 @@
|
||||
defaultValues = {},
|
||||
shouldHideNoInputs = false,
|
||||
compact = false,
|
||||
linkedSecrets = $bindable([]),
|
||||
linkedSecret = $bindable(undefined),
|
||||
linkedSecretCandidates = undefined,
|
||||
noVariablePicker = false,
|
||||
flexWrap = false,
|
||||
@@ -333,7 +333,7 @@
|
||||
{variableEditor}
|
||||
{itemPicker}
|
||||
{pickForField}
|
||||
password={linkedSecrets.includes(argName)}
|
||||
password={linkedSecret == argName}
|
||||
extra={formerProperty}
|
||||
{showSchemaExplorer}
|
||||
simpleTooltip={schemaFieldTooltip[argName]}
|
||||
@@ -398,24 +398,22 @@
|
||||
customErrorMessage={prop?.customErrorMessage}
|
||||
bind:properties={
|
||||
() => prop?.properties,
|
||||
(v) => {
|
||||
if (prop) prop.properties = v
|
||||
}
|
||||
(v) => { if (prop) prop.properties = v }
|
||||
}
|
||||
bind:order={
|
||||
() => prop?.order,
|
||||
(v) => {
|
||||
if (prop) prop.order = v
|
||||
}
|
||||
(v) => { if (prop) prop.order = v }
|
||||
}
|
||||
nestedRequired={prop?.required}
|
||||
itemsType={prop?.items}
|
||||
disabled={disabledArgs.includes(argName) || disabled || prop?.disabled}
|
||||
disabled={disabledArgs.includes(argName) ||
|
||||
disabled ||
|
||||
prop?.disabled}
|
||||
{compact}
|
||||
{variableEditor}
|
||||
{itemPicker}
|
||||
bind:pickForField
|
||||
password={linkedSecrets.includes(argName)}
|
||||
password={linkedSecret == argName}
|
||||
extra={prop}
|
||||
{showSchemaExplorer}
|
||||
simpleTooltip={schemaFieldTooltip[argName]}
|
||||
@@ -442,14 +440,12 @@
|
||||
{#if linkedSecretCandidates?.includes(argName)}
|
||||
<div class="relative">
|
||||
<ToggleButtonGroup
|
||||
selected={linkedSecrets.includes(argName) ? 'secret' : 'inlined'}
|
||||
selected={linkedSecret == argName ? 'secret' : 'inlined'}
|
||||
on:selected={(e) => {
|
||||
if (e.detail === 'secret') {
|
||||
if (!linkedSecrets.includes(argName)) {
|
||||
linkedSecrets = [...linkedSecrets, argName]
|
||||
}
|
||||
} else {
|
||||
linkedSecrets = linkedSecrets.filter((s) => s !== argName)
|
||||
linkedSecret = argName
|
||||
} else if (linkedSecret == argName) {
|
||||
linkedSecret = undefined
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -40,19 +40,6 @@
|
||||
return deps.name || `Default (${deps.language})`
|
||||
}
|
||||
|
||||
function getEditorLang(language: ScriptLang): string {
|
||||
switch (language) {
|
||||
case 'bun':
|
||||
case 'php':
|
||||
case 'powershell':
|
||||
return 'json'
|
||||
case 'python3':
|
||||
return 'plaintext'
|
||||
default:
|
||||
return 'markdown'
|
||||
}
|
||||
}
|
||||
|
||||
export function getFileExtension(language: ScriptLang): string | null {
|
||||
switch (language) {
|
||||
case 'python3':
|
||||
@@ -63,8 +50,6 @@
|
||||
// return 'go.mod'
|
||||
case 'php':
|
||||
return 'composer.json'
|
||||
case 'powershell':
|
||||
return 'modules.json'
|
||||
default:
|
||||
return null
|
||||
}
|
||||
@@ -128,8 +113,7 @@
|
||||
{ value: 'python3', label: 'Python' },
|
||||
{ value: 'bun', label: 'TypeScript (Bun/Bunnative)' },
|
||||
// { value: 'go', label: 'Go' },
|
||||
{ value: 'php', label: 'PHP' },
|
||||
{ value: 'powershell', label: 'PowerShell' }
|
||||
{ value: 'php', label: 'PHP' }
|
||||
]
|
||||
|
||||
// Default templates for each language
|
||||
@@ -173,13 +157,6 @@ numpy>=1.24.0
|
||||
"vlucas/phpdotenv": "^5.6",
|
||||
"symfony/console": "^6.4"
|
||||
}
|
||||
}`,
|
||||
|
||||
powershell: `{
|
||||
"modules": {
|
||||
"PSWriteColor": "*",
|
||||
"ImportExcel": "7.8.6"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
@@ -520,7 +497,7 @@ numpy>=1.24.0
|
||||
<Module.default
|
||||
bind:this={editor}
|
||||
autoHeight
|
||||
lang={getEditorLang(workspaceDependencies.language)}
|
||||
lang="markdown"
|
||||
code={workspaceDependencies.content}
|
||||
on:change={(e) => handleEditorChange(e.detail)}
|
||||
fixedOverflowWidgets={false}
|
||||
@@ -528,15 +505,6 @@ numpy>=1.24.0
|
||||
/>
|
||||
{/await}
|
||||
</div>
|
||||
{#if workspaceDependencies.language === 'powershell'}
|
||||
<div class="text-sm text-tertiary mt-2">
|
||||
JSON object with a <code>"modules"</code> key mapping module names to versions. Use
|
||||
<code>"*"</code>
|
||||
or <code>null</code> for latest version, or a specific version string to pin. These
|
||||
modules are merged with script-level
|
||||
<code>Import-Module</code> statements at runtime (workspace versions take precedence).
|
||||
</div>
|
||||
{/if}
|
||||
</Section>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1111,8 +1111,8 @@
|
||||
<Tabs bind:selected={advancedSelected} wrapperClass="shrink-0">
|
||||
<Tab
|
||||
value="retries"
|
||||
active={flowModule.retry !== undefined || flowModule.continue_on_error}
|
||||
label="Error handling"
|
||||
active={flowModule.retry !== undefined}
|
||||
label="Retries"
|
||||
/>
|
||||
{#if !selectedId.includes('failure')}
|
||||
<Tab value="runtime" label="Runtime" />
|
||||
@@ -1152,22 +1152,6 @@
|
||||
{/if}
|
||||
<div class="flex-1 overflow-auto p-4">
|
||||
{#if advancedSelected === 'retries'}
|
||||
<Section label="Continue on error">
|
||||
{#snippet header()}
|
||||
<Tooltip>
|
||||
When enabled, the flow will continue to the next step even if this step fails (after exhausting all retries, if any). This enables to process the error in a branch one for instance.
|
||||
</Tooltip>
|
||||
{/snippet}
|
||||
<Toggle
|
||||
size="xs"
|
||||
bind:checked={flowModule.continue_on_error}
|
||||
options={{
|
||||
left: 'Stop on error and propagate error up',
|
||||
right: "Continue on error with error as step's return"
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
<div class="mt-4"></div>
|
||||
<Section label="Retries">
|
||||
{#snippet header()}
|
||||
<Tooltip
|
||||
@@ -1177,6 +1161,19 @@
|
||||
maximum number of attempts as defined below.
|
||||
</Tooltip>
|
||||
{/snippet}
|
||||
<Label label="After all retries attempts have been exhausted:">
|
||||
<Toggle
|
||||
size="xs"
|
||||
bind:checked={flowModule.continue_on_error}
|
||||
options={{
|
||||
left: 'Stop on error and propagate error up',
|
||||
right: "Continue on error with error as step's return",
|
||||
rightTooltip:
|
||||
'When enabled, the flow will continue to the next step after going through all the retries (if any) even if this step fails. This enables to process the error in a branch one for instance.'
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<div class="my-8"></div>
|
||||
<FlowRetries bind:flowModuleRetry={flowModule.retry} bind:flowModule />
|
||||
</Section>
|
||||
{:else if advancedSelected === 'runtime' && advancedRuntimeSelected === 'concurrency'}
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
const u32Max = 4294967295
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="h-full flex flex-col gap-4">
|
||||
<ToggleButtonGroup
|
||||
bind:selected={delayType}
|
||||
class={`${disabled ? 'disabled' : ''}`}
|
||||
@@ -216,7 +216,6 @@
|
||||
</Section>
|
||||
{/if}
|
||||
|
||||
{#if delayType === 'constant' || delayType === 'exponential'}
|
||||
<div class="flex h-[calc(100%-22px)]">
|
||||
<div class="w-1/2 h-full overflow-auto pr-2">
|
||||
{#if delayType === 'constant'}
|
||||
@@ -297,65 +296,66 @@
|
||||
</div>
|
||||
<div class="w-1/2 h-full overflow-auto pl-2">
|
||||
{#if true}
|
||||
{@const { attempts: cAttempts, seconds: cSeconds } = flowModuleRetry?.constant || {}}
|
||||
{@const {
|
||||
attempts: eAttempts,
|
||||
seconds: eSeconds,
|
||||
multiplier,
|
||||
random_factor
|
||||
} = flowModuleRetry?.exponential || {}}
|
||||
{@const cArray = Array.from({ length: Math.min(cAttempts || 0, 100) }, () => cSeconds)}
|
||||
{@const eArray = Array.from(
|
||||
{ length: Math.min(eAttempts || 0, 100) },
|
||||
(_, i) => (multiplier || 0) * (eSeconds || 0) ** (i + cArray.length + 1)
|
||||
)}
|
||||
{@const array = [...cArray, ...eArray]}
|
||||
<div class="bg-surface-secondary border rounded px-4 py-2">
|
||||
<div class="text-xs font-medium mb-2">Retry attempts</div>
|
||||
{#if array.length > 0}
|
||||
<table class="text-xs">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="font-semibold pr-1 pb-1">1:</td>
|
||||
<td class="pb-1"
|
||||
>After {array[0]} second{array[0] === 1 ? '' : 's'}
|
||||
{#if (random_factor ?? 0) > 0}(+/- {((array[0] ?? 0) * (random_factor ?? 0)) /
|
||||
100}
|
||||
seconds){/if}</td
|
||||
>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each array.slice(1, 100) as delay, i}
|
||||
{@const index = i + 2}
|
||||
{@const { attempts: cAttempts, seconds: cSeconds } = flowModuleRetry?.constant || {}}
|
||||
{@const {
|
||||
attempts: eAttempts,
|
||||
seconds: eSeconds,
|
||||
multiplier,
|
||||
random_factor
|
||||
} = flowModuleRetry?.exponential || {}}
|
||||
{@const cArray = Array.from({ length: Math.min(cAttempts || 0, 100) }, () => cSeconds)}
|
||||
{@const eArray = Array.from(
|
||||
{ length: Math.min(eAttempts || 0, 100) },
|
||||
(_, i) => (multiplier || 0) * (eSeconds || 0) ** (i + cArray.length + 1)
|
||||
)}
|
||||
{@const array = [...cArray, ...eArray]}
|
||||
<div class="bg-surface-secondary border rounded px-4 py-2">
|
||||
<div class="text-xs font-medium mb-2">Retry attempts</div>
|
||||
{#if array.length > 0}
|
||||
<table class="text-xs">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="font-semibold pr-1 align-top">{index}:</td>
|
||||
<td class="pb-1 whitespace-nowrap">
|
||||
{delay} second{delay === 1 ? '' : 's'}
|
||||
{#if (random_factor ?? 0) > 0}(+/- {((delay ?? 0) * (random_factor ?? 0)) /
|
||||
<td class="font-semibold pr-1 pb-1">1:</td>
|
||||
<td class="pb-1"
|
||||
>After {array[0]} second{array[0] === 1 ? '' : 's'}
|
||||
{#if (random_factor ?? 0) > 0}(+/- {((array[0] ?? 0) * (random_factor ?? 0)) /
|
||||
100}
|
||||
seconds){/if}
|
||||
after attempt #{index - 1}
|
||||
{#if i > cArray.length - 2}
|
||||
<span class="text-gray-400 pl-2">
|
||||
({multiplier} * {eSeconds}<sup>{index}</sup>)
|
||||
</span>
|
||||
{/if}
|
||||
</td>
|
||||
seconds){/if}</td
|
||||
>
|
||||
</tr>
|
||||
{/each}
|
||||
{#if (cAttempts ?? 0) > 100 || (eAttempts ?? 0) > 100}
|
||||
<tr>
|
||||
<td class="font-semibold pr-1 align-top">...</td>
|
||||
<td class="pb-1">...</td>
|
||||
</tr>
|
||||
{/if}
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
</div>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each array.slice(1, 100) as delay, i}
|
||||
{@const index = i + 2}
|
||||
<tr>
|
||||
<td class="font-semibold pr-1 align-top">{index}:</td>
|
||||
<td class="pb-1 whitespace-nowrap">
|
||||
{delay} second{delay === 1 ? '' : 's'}
|
||||
{#if (random_factor ?? 0) > 0}(+/- {((delay ?? 0) * (random_factor ?? 0)) /
|
||||
100}
|
||||
seconds){/if}
|
||||
after attempt #{index - 1}
|
||||
{#if i > cArray.length - 2}
|
||||
<span class="text-gray-400 pl-2">
|
||||
({multiplier} * {eSeconds}<sup>{index}</sup>)
|
||||
</span>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
{#if (cAttempts ?? 0) > 100 || (eAttempts ?? 0) > 100}
|
||||
<tr>
|
||||
<td class="font-semibold pr-1 align-top">...</td>
|
||||
<td class="pb-1">...</td>
|
||||
</tr>
|
||||
{/if}
|
||||
</tbody>
|
||||
</table>
|
||||
{:else}
|
||||
<div class="text-xs">No retries</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -204,8 +204,7 @@
|
||||
}
|
||||
|
||||
function updateLastJob() {
|
||||
// Prefer testJob only when actively running/streaming (individual step test in progress)
|
||||
if (testJob && (testJob.result_stream || testJob.type === 'QueuedJob')) {
|
||||
if (testJob) {
|
||||
return testJob
|
||||
}
|
||||
if (
|
||||
@@ -215,8 +214,6 @@
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Use flowStateStore as source of truth — it's updated by both individual step tests
|
||||
// (ModuleTest.jobDone) and flow tests (FlowStatusViewerInner.onJobsLoadedInner)
|
||||
return {
|
||||
id: flowStateStore.val[moduleId]?.previewJobId ?? '',
|
||||
result: flowStateStore.val[moduleId]?.previewResult,
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
escapeBehavior: untrack(() => escapeBehavior),
|
||||
openFocus: untrack(() => openFocus),
|
||||
onOpenChange: ({ curr, next }) => {
|
||||
console.log('Popover open state changed:', { curr, next })
|
||||
if (curr != next) {
|
||||
dispatch('openChange', next)
|
||||
if (!next) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
import { Loader2, Save } from 'lucide-svelte'
|
||||
import ScriptPicker from '$lib/components/ScriptPicker.svelte'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import Required from '$lib/components/Required.svelte'
|
||||
import NextcloudTriggerForm from './services/nextcloud/NextcloudTriggerForm.svelte'
|
||||
import GoogleTriggerForm from './services/google/GoogleTriggerForm.svelte'
|
||||
@@ -97,7 +96,6 @@
|
||||
let isFlow = $state(false)
|
||||
let externalId = $state<string | null>(null)
|
||||
let can_write = $state(true)
|
||||
let summary = $state('')
|
||||
let originalConfig = $state<Record<string, any> | undefined>(undefined)
|
||||
let initialConfig = $state<Record<string, any> | undefined>(undefined)
|
||||
|
||||
@@ -123,7 +121,6 @@
|
||||
loadingConfig = false
|
||||
loadingForm = false
|
||||
can_write = true
|
||||
summary = ''
|
||||
originalConfig = undefined
|
||||
initialConfig = undefined
|
||||
}
|
||||
@@ -147,7 +144,6 @@
|
||||
loadingConfig = false
|
||||
loadingForm = false
|
||||
can_write = true
|
||||
summary = nativeTrigger.summary ?? ''
|
||||
originalConfig = undefined
|
||||
initialConfig = undefined
|
||||
}
|
||||
@@ -186,7 +182,6 @@
|
||||
scriptPath = fullTrigger.script_path
|
||||
initialScriptPath = fullTrigger.script_path
|
||||
can_write = canWrite(fullTrigger.script_path, {}, $userStore)
|
||||
summary = fullTrigger.summary ?? ''
|
||||
externalData = fullTrigger.external_data
|
||||
|
||||
// Apply default values if provided (for draft triggers)
|
||||
@@ -208,8 +203,7 @@
|
||||
return {
|
||||
script_path: scriptPath,
|
||||
is_flow: isFlow,
|
||||
service_config: serviceConfig,
|
||||
summary: summary || undefined
|
||||
service_config: serviceConfig
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,18 +422,6 @@
|
||||
</Section>
|
||||
{/if}
|
||||
|
||||
<Section label="Metadata">
|
||||
<Label label="Summary">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Short summary to be displayed when listed"
|
||||
class="text-sm w-full"
|
||||
bind:value={summary}
|
||||
disabled={!can_write}
|
||||
/>
|
||||
</Label>
|
||||
</Section>
|
||||
|
||||
{#if loadingConfig}
|
||||
<Section label="{serviceInfo?.serviceDisplayName} configuration">
|
||||
<div class="flex items-center gap-2 text-secondary text-xs">
|
||||
|
||||
@@ -199,8 +199,7 @@ export async function saveNativeTriggerFromCfg(
|
||||
const requestBody: NativeTriggerData = {
|
||||
script_path: triggerCfg.script_path,
|
||||
is_flow: triggerCfg.is_flow,
|
||||
service_config: triggerCfg.service_config,
|
||||
summary: triggerCfg.summary || undefined
|
||||
service_config: triggerCfg.service_config
|
||||
}
|
||||
|
||||
const serviceName = NATIVE_TRIGGER_SERVICES[service].serviceDisplayName
|
||||
|
||||
@@ -481,15 +481,14 @@ export function getLightConfig(
|
||||
} else if (triggerType === 'email') {
|
||||
return { local_part: trigger.local_part }
|
||||
} else if (triggerType === 'nextcloud') {
|
||||
return { event: trigger.service_config?.event ?? trigger.event, summary: trigger.summary }
|
||||
return { event: trigger.service_config?.event ?? trigger.event }
|
||||
} else if (triggerType === 'google') {
|
||||
return {
|
||||
trigger_type: trigger.service_config?.triggerType ?? trigger.trigger_type,
|
||||
resource_id: trigger.service_config?.resourceId ?? trigger.resource_id,
|
||||
resource_name: trigger.service_config?.resourceName ?? trigger.resource_name,
|
||||
calendar_id: trigger.service_config?.calendarId ?? trigger.calendar_id,
|
||||
calendar_name: trigger.service_config?.calendarName ?? trigger.calendar_name,
|
||||
summary: trigger.summary
|
||||
calendar_name: trigger.service_config?.calendarName ?? trigger.calendar_name
|
||||
}
|
||||
} else {
|
||||
return undefined
|
||||
@@ -525,12 +524,8 @@ export function getTriggerLabel(trigger: Trigger): string {
|
||||
return `${config?.url}`
|
||||
} else if (type === 'email' && config?.local_part) {
|
||||
return `${config?.local_part}`
|
||||
} else if (type === 'nextcloud' && config?.summary) {
|
||||
return `${config.summary}`
|
||||
} else if (type === 'nextcloud' && path) {
|
||||
return `${path}`
|
||||
} else if (type === 'google' && config?.summary) {
|
||||
return `${config.summary}`
|
||||
} else if (type === 'google' && path) {
|
||||
const triggerType = config?.trigger_type ?? config?.triggerType
|
||||
if (triggerType === 'calendar') {
|
||||
|
||||
@@ -179,10 +179,11 @@
|
||||
|
||||
for (const serviceName of Object.keys(supportedServices)) {
|
||||
try {
|
||||
const available = await WorkspaceIntegrationService.checkInstanceSharingAvailable({
|
||||
workspace: $workspaceStore,
|
||||
serviceName: serviceName as NativeServiceName
|
||||
})
|
||||
const available =
|
||||
await WorkspaceIntegrationService.checkInstanceSharingAvailable({
|
||||
workspace: $workspaceStore,
|
||||
serviceName: serviceName as NativeServiceName
|
||||
})
|
||||
instanceSharingAvailable[serviceName] = available
|
||||
} catch {
|
||||
instanceSharingAvailable[serviceName] = false
|
||||
@@ -388,10 +389,7 @@
|
||||
<span class="font-semibold">Connected</span>
|
||||
</div>
|
||||
<Button
|
||||
onclick={() =>
|
||||
integration?.oauth_data?.instance_shared
|
||||
? connectWithInstanceCredentials(serviceName)
|
||||
: connectService(serviceName, getRedirectUri(serviceName))}
|
||||
onclick={() => connectService(serviceName, getRedirectUri(serviceName))}
|
||||
disabled={isConnecting}
|
||||
startIcon={{ icon: Plug }}
|
||||
>
|
||||
|
||||
@@ -29,12 +29,6 @@ func main(x int) (interface{}, error) {
|
||||
"python3": """
|
||||
def main(x: int):
|
||||
return x
|
||||
""",
|
||||
"php": """<?php
|
||||
|
||||
function main(int $x): int {
|
||||
return $x;
|
||||
}
|
||||
""",
|
||||
}
|
||||
|
||||
@@ -86,8 +80,3 @@ class TestIdentityScript(unittest.TestCase):
|
||||
path = PATH_TEMPLATE.format(lang="python3")
|
||||
result = self._client.run_sync(path, {"x": 5})
|
||||
self.assertEqual(result, 5)
|
||||
|
||||
def test_php(self):
|
||||
path = PATH_TEMPLATE.format(lang="php")
|
||||
result = self._client.run_sync(path, {"x": 5})
|
||||
self.assertEqual(result, 5)
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.658.0"
|
||||
wmill = ">=1.657.0"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: '3.0.3'
|
||||
|
||||
info:
|
||||
version: 1.658.0
|
||||
version: 1.657.0
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
RootModule = 'WindmillClient.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.658.0'
|
||||
ModuleVersion = '1.657.0'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.658.0"
|
||||
version = "1.657.0"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Create a folder ending with `__flow` and add a `flow.yaml` file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key. Inline script files should NOT include `.inline_script.` in their names (e.g. use `a.ts`, not `a.inline_script.ts`).
|
||||
Create a folder ending with `.flow` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key.
|
||||
After writing, tell the user they can run:
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow__flow --yes`)
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow.flow --yes`)
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
@@ -33,10 +33,10 @@ export const FLOW_BASE = `# Windmill Flow Building Guide
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Create a folder ending with \`__flow\` and add a \`flow.yaml\` file with the flow definition.
|
||||
For rawscript modules, use \`!inline path/to/script.ts\` for the content key. Inline script files should NOT include \`.inline_script.\` in their names (e.g. use \`a.ts\`, not \`a.inline_script.ts\`).
|
||||
Create a folder ending with \`.flow\` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use \`!inline path/to/script.ts\` for the content key.
|
||||
After writing, tell the user they can run:
|
||||
- \`wmill flow generate-locks <path_to_flow_folder> --yes\` - Generate lock files for the specific flow you modified (e.g. \`wmill flow generate-locks f/my_folder/my_flow__flow --yes\`)
|
||||
- \`wmill flow generate-locks <path_to_flow_folder> --yes\` - Generate lock files for the specific flow you modified (e.g. \`wmill flow generate-locks f/my_folder/my_flow.flow --yes\`)
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
@@ -18,7 +18,7 @@ This interactive command creates a complete app structure with your choice of fr
|
||||
## App Structure
|
||||
|
||||
```
|
||||
my_app__raw_app/
|
||||
my_app.raw_app/
|
||||
├── AGENTS.md # AI agent instructions (auto-generated)
|
||||
├── DATATABLES.md # Database schemas (run 'wmill app generate-agents' to refresh)
|
||||
├── raw_app.yaml # App configuration (summary, path, data settings)
|
||||
|
||||
@@ -7,10 +7,10 @@ description: MUST use when creating flows.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Create a folder ending with `__flow` and add a `flow.yaml` file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key. Inline script files should NOT include `.inline_script.` in their names (e.g. use `a.ts`, not `a.inline_script.ts`).
|
||||
Create a folder ending with `.flow` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key.
|
||||
After writing, tell the user they can run:
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow__flow --yes`)
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow.flow --yes`)
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Create a folder ending with `__flow` and add a `flow.yaml` file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key. Inline script files should NOT include `.inline_script.` in their names (e.g. use `a.ts`, not `a.inline_script.ts`).
|
||||
Create a folder ending with `.flow` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key.
|
||||
After writing, tell the user they can run:
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow__flow --yes`)
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow.flow --yes`)
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
@@ -13,7 +13,7 @@ This interactive command creates a complete app structure with your choice of fr
|
||||
## App Structure
|
||||
|
||||
```
|
||||
my_app__raw_app/
|
||||
my_app.raw_app/
|
||||
├── AGENTS.md # AI agent instructions (auto-generated)
|
||||
├── DATATABLES.md # Database schemas (run 'wmill app generate-agents' to refresh)
|
||||
├── raw_app.yaml # App configuration (summary, path, data settings)
|
||||
|
||||
@@ -1094,20 +1094,6 @@ export function getFlowPrompt(): string {
|
||||
|
||||
# Generate skills TypeScript export for CLI
|
||||
skills_ts = generate_skills_ts_export(skills, schema_yaml_content)
|
||||
|
||||
# Replace hardcoded path conventions with placeholders for CLI runtime resolution.
|
||||
# init.ts resolves these based on the nonDottedPaths setting in wmill.yaml.
|
||||
# (Frontend auto-generated files keep the default non-dotted conventions.)
|
||||
skills_ts = (skills_ts
|
||||
.replace("\\`__flow\\`", "\\`{{FLOW_SUFFIX}}\\`")
|
||||
.replace(
|
||||
"Inline script files should NOT include \\`.inline_script.\\`"
|
||||
" in their names (e.g. use \\`a.ts\\`, not \\`a.inline_script.ts\\`).",
|
||||
"{{INLINE_SCRIPT_NAMING}}"
|
||||
)
|
||||
.replace("my_flow__flow", "my_flow{{FLOW_SUFFIX}}")
|
||||
.replace("my_app__raw_app/", "my_app{{RAW_APP_SUFFIX}}/")
|
||||
)
|
||||
(CLI_GUIDANCE_DIR / "skills.ts").write_text(skills_ts)
|
||||
|
||||
print(f"\nGenerated files:")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@windmill/windmill",
|
||||
"version": "1.658.0",
|
||||
"version": "1.657.0",
|
||||
"exports": "./src/index.ts",
|
||||
"publish": {
|
||||
"exclude": ["!src", "./s3Types.ts", "./sqlUtils.ts", "./client.ts"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.658.0",
|
||||
"version": "1.657.0",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"sideEffects": false,
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.658.0
|
||||
1.657.0
|
||||
|
||||
Reference in New Issue
Block a user