Compare commits

...

12 Commits

Author SHA1 Message Date
Ruben Fiszel
44e316a5d4 fix: extract debug files in same build_ee job to avoid double compile
Move the debuginfo extraction into the build_ee job, right after the
main build+push. Since it runs on the same depot runner with a warm
cache, the --target debuginfo step should be a cache hit (no recompile).
The separate attach_ee_debug_to_release job caused a full recompile
on v1.658.0 (~16 min per platform).

The .debug file stays out of the final image (zero image size increase).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 16:36:11 +00:00
Ruben Fiszel
ef6f0d7e66 fix: extract debug file from same image to ensure matching BuildID
The separate depot build for --target debuginfo produced a binary with
a different BuildID than the shipped binary. Extract the .debug file
from the same EE image instead — no extra CI build, guaranteed match.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 16:27:29 +00:00
Ruben Fiszel
140d08cf69 chore: extract debug info via separate Docker stage
Use a `FROM scratch AS debuginfo` stage instead of copying the .debug
file to the final image. This keeps the shipped image at exactly the
same size as before. CI extracts the .debug file using depot's
--target debuginfo with cache hits from the main build.

Also adds gnu_debuglink so gdb auto-discovers the debug file when
placed next to the binary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:27:52 +00:00
Ruben Fiszel
a74986ffff chore: split debug info for EE release builds
Generate line-table debug info in release builds and split it into
a separate .debug file. The shipped binary remains stripped (same
size as before), while the .debug files are attached to GitHub
releases for both amd64 and arm64 EE builds.

This enables production debugging with gdb/perf by copying the
matching .debug file into a running pod.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:18:03 +00:00
Ruben Fiszel
68fd900076 fix: use bookworm-based php image to fix glibc 2.38 incompatibility (#8381)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-15 19:03:09 +00:00
Ruben Fiszel
82bfa9613c chore(main): release 1.657.2 (#8376)
* chore(main): release 1.657.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-15 05:05:49 +00:00
Ruben Fiszel
b1b9c984e3 make wmill init generated skills respect nonDottedPaths config (#8377)
* docs: add nonDottedPaths convention to CLAUDE.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(cli): update generated skills to use non-dotted path conventions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): make generated skills respect nonDottedPaths config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): inject nonDottedPaths placeholders in generate.py for skills.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: default system prompts to non-dotted path conventions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 05:05:31 +00:00
Travis Pew
eb03ebbb04 fix(cli): Fix nonDottedPaths handling in cli flow lock generation (#8375)
* fix(cli): preserve non-dotted flow lock filenames

* test(cli): add non-dotted path tests for generate-metadata and sync pull

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 04:36:04 +00:00
Ruben Fiszel
5296adeddf test: add powershell module detection and execution tests (#8373)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 04:11:43 +00:00
Ruben Fiszel
1a061892e9 chore(main): release 1.657.1 (#8372)
* chore(main): release 1.657.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2026-03-14 23:24:31 +00:00
Ruben Fiszel
daade374b3 restore flat module detection with file existence check (#8371)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 23:11:23 +00:00
Ruben Fiszel
3a268a9cf1 fix: powershell WindmillClient module loading on Windows workers (#8370)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 23:09:00 +00:00
33 changed files with 796 additions and 124 deletions

View File

@@ -162,6 +162,31 @@ jobs:
${{ steps.meta-ee-public.outputs.labels }}
org.opencontainers.image.licenses=Windmill-Enterprise-License
- name: Extract EE debug files from cached builder layer
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
uses: depot/build-push-action@v1
with:
context: .
platforms: linux/amd64,linux/arm64
target: debuginfo
build-args: |
features=ee
outputs: type=local,dest=./debuginfo
- name: Rename EE debug files
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
mv ./debuginfo/linux_amd64/windmill.debug ./debuginfo/windmill-ee-amd64.debug
mv ./debuginfo/linux_arm64/windmill.debug ./debuginfo/windmill-ee-arm64.debug
- name: Upload EE debug files
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
uses: softprops/action-gh-release@v2
with:
files: |
./debuginfo/windmill-ee-amd64.debug
./debuginfo/windmill-ee-arm64.debug
attach_amd64_binary_to_release:
needs: [build, build_ee]
runs-on: ubicloud

View File

@@ -1,5 +1,19 @@
# Changelog
## [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)

View File

@@ -118,6 +118,18 @@ 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
# Lightweight stage for extracting the .debug file without bloating the final image.
# Usage: 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
@@ -268,7 +280,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 /usr/local/bin/php /usr/bin/php
COPY --from=php:8.3.30-cli-bookworm /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

159
backend/Cargo.lock generated
View File

@@ -8208,9 +8208,9 @@ dependencies = [
[[package]]
name = "lz4_flex"
version = "0.11.5"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a"
dependencies = [
"twox-hash 2.1.2",
]
@@ -14087,9 +14087,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.10.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
@@ -15741,7 +15741,7 @@ dependencies = [
[[package]]
name = "windmill"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-nats",
@@ -15808,7 +15808,7 @@ dependencies = [
[[package]]
name = "windmill-alerting"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -15821,7 +15821,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"argon2",
@@ -15962,7 +15962,7 @@ dependencies = [
[[package]]
name = "windmill-api-agent-workers"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -15985,7 +15985,7 @@ dependencies = [
[[package]]
name = "windmill-api-assets"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -15998,7 +15998,7 @@ dependencies = [
[[package]]
name = "windmill-api-auth"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -16024,7 +16024,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"reqwest 0.12.28",
"serde",
@@ -16034,7 +16034,7 @@ dependencies = [
[[package]]
name = "windmill-api-configs"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16051,7 +16051,7 @@ dependencies = [
[[package]]
name = "windmill-api-debug"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"base64 0.22.1",
@@ -16074,7 +16074,7 @@ dependencies = [
[[package]]
name = "windmill-api-embeddings"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -16097,7 +16097,7 @@ dependencies = [
[[package]]
name = "windmill-api-flow-conversations"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16113,7 +16113,7 @@ dependencies = [
[[package]]
name = "windmill-api-flows"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16133,7 +16133,7 @@ dependencies = [
[[package]]
name = "windmill-api-groups"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16153,7 +16153,7 @@ dependencies = [
[[package]]
name = "windmill-api-inputs"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16167,7 +16167,7 @@ dependencies = [
[[package]]
name = "windmill-api-integration-tests"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-nats",
@@ -16195,7 +16195,7 @@ dependencies = [
[[package]]
name = "windmill-api-jobs"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -16220,7 +16220,7 @@ dependencies = [
[[package]]
name = "windmill-api-npm-proxy"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"flate2",
@@ -16238,7 +16238,7 @@ dependencies = [
[[package]]
name = "windmill-api-openapi"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -16259,7 +16259,7 @@ dependencies = [
[[package]]
name = "windmill-api-schedule"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16279,7 +16279,7 @@ dependencies = [
[[package]]
name = "windmill-api-scripts"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16309,7 +16309,7 @@ dependencies = [
[[package]]
name = "windmill-api-settings"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -16336,7 +16336,7 @@ dependencies = [
[[package]]
name = "windmill-api-sse"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"lazy_static",
"serde",
@@ -16348,7 +16348,7 @@ dependencies = [
[[package]]
name = "windmill-api-users"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"argon2",
"axum 0.7.9",
@@ -16371,7 +16371,7 @@ dependencies = [
[[package]]
name = "windmill-api-workers"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16385,7 +16385,7 @@ dependencies = [
[[package]]
name = "windmill-api-workspaces"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -16416,7 +16416,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"chrono",
"lazy_static",
@@ -16430,7 +16430,7 @@ dependencies = [
[[package]]
name = "windmill-autoscaling"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -16449,7 +16449,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"aes-gcm",
"anyhow",
@@ -16548,7 +16548,7 @@ dependencies = [
[[package]]
name = "windmill-dep-map"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"chrono",
"itertools 0.14.0",
@@ -16567,7 +16567,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"regex",
"serde",
@@ -16582,7 +16582,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"astral-tokio-tar",
@@ -16606,7 +16606,7 @@ dependencies = [
[[package]]
name = "windmill-jseval"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"futures",
@@ -16623,7 +16623,7 @@ dependencies = [
[[package]]
name = "windmill-macros"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"itertools 0.14.0",
"lazy_static",
@@ -16639,7 +16639,7 @@ dependencies = [
[[package]]
name = "windmill-mcp"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -16660,7 +16660,7 @@ dependencies = [
[[package]]
name = "windmill-native-triggers"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -16691,7 +16691,7 @@ dependencies = [
[[package]]
name = "windmill-oauth"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-oauth2",
@@ -16715,7 +16715,7 @@ dependencies = [
[[package]]
name = "windmill-object-store"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-stream",
@@ -16749,7 +16749,7 @@ dependencies = [
[[package]]
name = "windmill-operator"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"futures",
@@ -16767,7 +16767,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"convert_case 0.6.0",
"serde",
@@ -16776,7 +16776,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -16788,7 +16788,7 @@ dependencies = [
[[package]]
name = "windmill-parser-csharp"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"serde_json",
@@ -16800,7 +16800,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"gosyn",
@@ -16812,7 +16812,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -16824,7 +16824,7 @@ dependencies = [
[[package]]
name = "windmill-parser-java"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"serde_json",
@@ -16836,7 +16836,7 @@ dependencies = [
[[package]]
name = "windmill-parser-nu"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"nu-parser",
@@ -16847,7 +16847,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -16858,7 +16858,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -16870,7 +16870,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-asset"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"rustpython-ast",
@@ -16881,7 +16881,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-recursion",
@@ -16905,7 +16905,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ruby"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -16919,7 +16919,7 @@ dependencies = [
[[package]]
name = "windmill-parser-rust"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -16936,7 +16936,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -16950,7 +16950,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql-asset"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"serde",
@@ -16962,7 +16962,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"lazy_static",
@@ -16980,7 +16980,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts-asset"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"serde-wasm-bindgen",
@@ -16996,7 +16996,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wac"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"rustpython-ast",
@@ -17012,7 +17012,7 @@ dependencies = [
[[package]]
name = "windmill-parser-yaml"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"serde",
@@ -17023,7 +17023,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-recursion",
@@ -17060,7 +17060,7 @@ dependencies = [
[[package]]
name = "windmill-runtime-nativets"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"const_format",
@@ -17098,7 +17098,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"getrandom 0.3.4",
"wasm-bindgen",
@@ -17109,7 +17109,7 @@ dependencies = [
[[package]]
name = "windmill-store"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-recursion",
@@ -17138,7 +17138,7 @@ dependencies = [
[[package]]
name = "windmill-test-utils"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"axum 0.7.9",
@@ -17161,7 +17161,7 @@ dependencies = [
[[package]]
name = "windmill-trigger"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17194,7 +17194,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-email"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17214,7 +17214,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-gcp"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17248,7 +17248,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-http"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17283,7 +17283,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-kafka"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17306,7 +17306,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-mqtt"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17330,7 +17330,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-nats"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-nats",
@@ -17354,7 +17354,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-postgres"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17389,7 +17389,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-sqs"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17417,7 +17417,7 @@ dependencies = [
[[package]]
name = "windmill-trigger-websocket"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-trait",
@@ -17440,7 +17440,7 @@ dependencies = [
[[package]]
name = "windmill-types"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"bitflags 2.9.4",
@@ -17458,7 +17458,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"anyhow",
"async-once-cell",
@@ -17521,6 +17521,7 @@ dependencies = [
"sha2 0.10.9",
"sqlx",
"tar",
"tempfile",
"tiberius",
"tokio",
"tokio-postgres 0.7.13",
@@ -17564,7 +17565,7 @@ dependencies = [
[[package]]
name = "windmill-worker-volumes"
version = "1.657.0"
version = "1.657.2"
dependencies = [
"bytes",
"futures",

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.657.0"
version = "1.657.2"
authors.workspace = true
edition.workspace = true
@@ -82,7 +82,7 @@ members = [
exclude = ["./windmill-duckdb-ffi-internal"]
[workspace.package]
version = "1.657.0"
version = "1.657.2"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -100,6 +100,8 @@ debug = false
[profile.release]
lto = "thin"
debug = "line-tables-only"
strip = "none"
[features]
default = []

View File

@@ -1518,6 +1518,92 @@ 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<()> {

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.657.0
version: 1.657.2
title: Windmill API
contact:

View File

@@ -143,5 +143,8 @@ 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 }

View File

@@ -18,7 +18,7 @@ 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#"^Import-Module\s+(?:-Name\s+)?"?([^\s"]+)"?(?:\s+-RequiredVersion\s+"?([^\s"]+)"?)?"#).unwrap();
static ref RE_POWERSHELL_IMPORTS: Regex = Regex::new(r#"^\s*Import-Module\s+(?:-Name\s+)?"?([^\s"]+)"?(?:\s+-RequiredVersion\s+"?([^\s"]+)"?)?"#).unwrap();
}
use crate::{
@@ -196,17 +196,41 @@ 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('.') {
versions.push(version);
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);
}
}
}
}
}
}
// If no version subdirectories found, treat as single version installation
// If no version subdirectories found, check if module files exist directly
// in the module directory (flat/single-version installation)
if versions.is_empty() {
versions.push("unknown".to_string());
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());
}
}
Ok(versions)
@@ -466,8 +490,8 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
let strict_termination_end = "\n\
} catch {\n\
Write-Output \"An error occurred:\n\"\
Write-Output $_
Write-Output \"An error occurred:\"\n\
Write-Output $_\n\
exit 1\n\
}\n";
@@ -672,3 +696,231 @@ $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");
}
#[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"]);
}
}

View File

@@ -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.657.0";
export const VERSION = "v1.657.2";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({

View File

@@ -29,7 +29,10 @@ import { FlowFile } from "./flow.ts";
import { FlowValue } from "../../../gen/types.gen.ts";
import { replaceInlineScripts } from "../../../windmill-utils-internal/src/inline-scripts/replacer.ts";
import { workspaceDependenciesLanguages } from "../../utils/script_common.ts";
import { extractNameFromFolder, getFolderSuffix } from "../../utils/resource_folders.ts";
import {
extractNameFromFolder,
getNonDottedPaths,
} from "../../utils/resource_folders.ts";
const TOP_HASH = "__flow_hash";
async function generateFlowHash(
@@ -157,7 +160,9 @@ export async function generateFlowLockInternal(
filteredDeps
);
const lockAssigner = newPathAssigner(opts.defaultTs ?? "bun");
const lockAssigner = newPathAssigner(opts.defaultTs ?? "bun", {
skipInlineScriptSuffix: getNonDottedPaths(),
});
const inlineScripts = extractInlineScriptsForFlows(
flowValue.value.modules,
{},

View File

@@ -252,6 +252,16 @@ 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
@@ -290,6 +300,20 @@ 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) {

View File

@@ -4236,10 +4236,10 @@ description: MUST use when creating flows.
## CLI Commands
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.
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}}
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_SUFFIX}} --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/
my_app{{RAW_APP_SUFFIX}}/
├── 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)

View File

@@ -68,7 +68,7 @@ export {
workspaceAdd,
};
export const VERSION = "1.657.0";
export const VERSION = "1.657.2";
// Re-exported from constants.ts to maintain backwards compatibility
export { WM_FORK_PREFIX } from "./core/constants.ts";

View File

@@ -1768,6 +1768,66 @@ excludes: []
});
});
test("Integration: Sync pull with nonDottedPaths uses non-dotted inline script filenames", async () => {
await withTestBackend(async (backend, tempDir) => {
// Push a flow using default dotted paths
await writeFile(
`${tempDir}/wmill.yaml`,
`defaultTs: bun
includes:
- "**"
excludes: []
`,
"utf-8",
);
const uniqueId = Date.now();
const flowName = `f/test/nondot_pull_inline_${uniqueId}`;
const flowFixture = createFlowFixture(flowName);
await mkdir(`${tempDir}/f/test/nondot_pull_inline_${uniqueId}${getFolderSuffix("flow")}`, { recursive: true });
for (const file of Object.values(flowFixture)) {
await writeFile(`${tempDir}/${file.path}`, file.content, "utf-8");
}
const pushResult = await backend.runCLICommand(
["sync", "push", "--yes", "--includes", `f/test/nondot_pull_inline_${uniqueId}*/**`],
tempDir,
);
expect(pushResult.code).toEqual(0);
// Pull into a fresh directory with nonDottedPaths enabled
const tempDir2 = await mkdtemp(join(tmpdir(), "wmill_nondot_inline_"));
try {
await writeFile(
`${tempDir2}/wmill.yaml`,
`defaultTs: bun
nonDottedPaths: true
includes:
- "**"
excludes: []
`,
"utf-8",
);
const pullResult = await backend.runCLICommand(["sync", "pull", "--yes"], tempDir2);
expect(pullResult.code).toEqual(0);
// Verify pulled files use non-dotted inline script naming
const files = await listFilesRecursive(tempDir2);
const flowFiles = files.filter((f) => f.includes(`nondot_pull_inline_${uniqueId}`));
expect(flowFiles.length > 0).toBeTruthy();
// Should use __flow folder, not .flow
expect(flowFiles.some((f) => f.includes("__flow/"))).toBeTruthy();
// No files should have .inline_script. in their name
const dottedInlineFiles = flowFiles.filter((f) => f.includes(".inline_script."));
expect(dottedInlineFiles.length).toEqual(0);
} finally {
await cleanupTempDir(tempDir2);
}
});
});
// =============================================================================
// ws_error_handler_muted Persistence Tests
// =============================================================================

View File

@@ -8,7 +8,7 @@
import { expect, test, describe } from "bun:test";
import { withTestBackend } from "./test_backend.ts";
import { addWorkspace } from "../workspace.ts";
import { writeFile } from "node:fs/promises";
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
import {
createLocalScript,
createLocalFlow,
@@ -19,7 +19,12 @@ import {
/**
* Helper to set up a workspace with wmill.yaml
*/
async function setupWorkspace(backend: any, tempDir: string, workspaceName: string) {
async function setupWorkspace(
backend: any,
tempDir: string,
workspaceName: string,
nonDottedPaths = false
) {
const testWorkspace = {
remote: backend.baseUrl,
workspaceId: backend.workspace,
@@ -29,11 +34,88 @@ async function setupWorkspace(backend: any, tempDir: string, workspaceName: stri
await addWorkspace(testWorkspace, { force: true, configDir: backend.testConfigDir });
await writeFile(`${tempDir}/wmill.yaml`, `defaultTs: bun
includes:
${nonDottedPaths ? "nonDottedPaths: true\n" : ""}includes:
- "**"
excludes: []`, "utf-8");
}
async function createLocalNonDottedFlow(tempDir: string, name: string) {
const flowDir = `${tempDir}/f/test/${name}__flow`;
await mkdir(flowDir, { recursive: true });
await writeFile(
`${flowDir}/a.ts`,
`export async function main() {\n return "Hello from flow ${name}";\n}`,
"utf-8"
);
await writeFile(
`${flowDir}/flow.yaml`,
`summary: "${name} flow"
description: "A flow for testing"
value:
modules:
- id: a
value:
type: rawscript
content: "!inline a.ts"
language: bun
input_transforms: {}
schema:
$schema: "https://json-schema.org/draft/2020-12/schema"
type: object
properties: {}
required: []
`,
"utf-8"
);
}
async function createLocalNonDottedApp(tempDir: string, name: string) {
const appDir = `${tempDir}/f/test/${name}__app`;
await mkdir(appDir, { recursive: true });
await writeFile(
`${appDir}/app.yaml`,
`summary: "${name} app"
value:
type: app
grid:
- id: button1
data:
type: buttoncomponent
componentInput:
type: runnable
runnable:
type: runnableByName
inlineScript:
content: |
export async function main() {
return "hello from app";
}
language: bun
hiddenInlineScripts: []
css: {}
norefreshbar: false
policy:
on_behalf_of: null
on_behalf_of_email: null
triggerables: {}
execution_mode: viewer
`,
"utf-8"
);
}
async function fileExists(filePath: string): Promise<boolean> {
try {
await stat(filePath);
return true;
} catch {
return false;
}
}
// =============================================================================
// Main test: processes scripts, flows, and apps together
// =============================================================================
@@ -156,6 +238,87 @@ describe("generate-metadata flags", () => {
});
});
test("--lock-only preserves non-dotted flow filenames", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspace(backend, tempDir, "lock_only_non_dotted_test", true);
await createLocalNonDottedFlow(tempDir, "my_flow");
const result = await backend.runCLICommand(
["generate-metadata", "--yes", "--lock-only"],
tempDir,
"lock_only_non_dotted_test"
);
expect(result.code).toEqual(0);
const flowDir = `${tempDir}/f/test/my_flow__flow`;
const flowYaml = await readFile(`${flowDir}/flow.yaml`, "utf-8");
expect(flowYaml).toContain("!inline a.ts");
expect(flowYaml).toContain("!inline a.lock");
expect(flowYaml).not.toContain(".inline_script.");
expect(await fileExists(`${flowDir}/a.lock`)).toEqual(true);
expect(await fileExists(`${flowDir}/a.inline_script.ts`)).toEqual(false);
expect(await fileExists(`${flowDir}/a.inline_script.lock`)).toEqual(false);
});
});
test("generate-metadata preserves non-dotted flow inline script filenames", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspace(backend, tempDir, "full_gen_non_dotted_flow_test", true);
await createLocalNonDottedFlow(tempDir, "my_flow");
const result = await backend.runCLICommand(
["generate-metadata", "--yes"],
tempDir,
"full_gen_non_dotted_flow_test"
);
expect(result.code).toEqual(0);
const flowDir = `${tempDir}/f/test/my_flow__flow`;
const flowYaml = await readFile(`${flowDir}/flow.yaml`, "utf-8");
// Inline script references should use non-dotted naming
expect(flowYaml).toContain("!inline a.ts");
expect(flowYaml).toContain("!inline a.lock");
expect(flowYaml).not.toContain(".inline_script.");
expect(await fileExists(`${flowDir}/a.ts`)).toEqual(true);
expect(await fileExists(`${flowDir}/a.lock`)).toEqual(true);
expect(await fileExists(`${flowDir}/a.inline_script.ts`)).toEqual(false);
expect(await fileExists(`${flowDir}/a.inline_script.lock`)).toEqual(false);
});
});
test("generate-metadata uses non-dotted app inline script filenames", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspace(backend, tempDir, "non_dotted_app_gen_test", true);
await createLocalNonDottedApp(tempDir, "my_app");
const result = await backend.runCLICommand(
["generate-metadata", "--yes"],
tempDir,
"non_dotted_app_gen_test"
);
expect(result.code).toEqual(0);
const appDir = `${tempDir}/f/test/my_app__app`;
const appYaml = await readFile(`${appDir}/app.yaml`, "utf-8");
// Inline script references should use non-dotted naming
expect(appYaml).not.toContain(".inline_script.");
// Verify no dotted inline script files were created
const { readdir: readdirAsync } = await import("node:fs/promises");
const files = await readdirAsync(appDir);
const dottedFiles = files.filter((f: string) => f.includes(".inline_script."));
expect(dottedFiles.length).toEqual(0);
});
});
test("--schema-only only processes scripts (skips flows and apps)", async () => {
await withTestBackend(async (backend, tempDir) => {
await setupWorkspace(backend, tempDir, "schema_only_test");

View File

@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.657.0",
"version": "1.657.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.657.0",
"version": "1.657.2",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.657.0",
"version": "1.657.2",
"scripts": {
"dev": "vite dev",
"build": "vite build",

View File

@@ -29,6 +29,12 @@ func main(x int) (interface{}, error) {
"python3": """
def main(x: int):
return x
""",
"php": """<?php
function main(int $x): int {
return $x;
}
""",
}
@@ -80,3 +86,8 @@ 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)

View File

@@ -4,7 +4,7 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.657.0"
wmill = ">=1.657.2"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"

View File

@@ -1,7 +1,7 @@
openapi: '3.0.3'
info:
version: 1.657.0
version: 1.657.2
title: OpenFlow Spec
contact:
name: Ruben Fiszel

View File

@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.657.0'
ModuleVersion = '1.657.2'
# Supported PSEditions
# CompatiblePSEditions = @()

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.657.0"
version = "1.657.2"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"

View File

@@ -2,10 +2,10 @@
## CLI Commands
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.
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`).
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.

View File

@@ -33,10 +33,10 @@ export const FLOW_BASE = `# Windmill Flow Building Guide
## CLI Commands
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.
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\`).
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.

View File

@@ -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)

View File

@@ -7,10 +7,10 @@ description: MUST use when creating flows.
## CLI Commands
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.
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`).
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.

View File

@@ -2,10 +2,10 @@
## CLI Commands
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.
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`).
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.

View File

@@ -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)

View File

@@ -1094,6 +1094,20 @@ 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:")

View File

@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.657.0",
"version": "1.657.2",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./sqlUtils.ts", "./client.ts"]

View File

@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.657.0",
"version": "1.657.2",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"sideEffects": false,

View File

@@ -1 +1 @@
1.657.0
1.657.2