Compare commits
5 Commits
v1.634.1
...
rf/testbac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9bac09b14 | ||
|
|
607a861b80 | ||
|
|
5ecfa92ac3 | ||
|
|
a3d5ae5425 | ||
|
|
d363fa6098 |
16
.github/workflows/backend-test.yml
vendored
16
.github/workflows/backend-test.yml
vendored
@@ -19,7 +19,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
cargo_test:
|
||||
runs-on: ubicloud-standard-16
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
@@ -50,9 +50,9 @@ jobs:
|
||||
- uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.21.5
|
||||
go-version: 1.25.0
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.8
|
||||
@@ -72,11 +72,13 @@ jobs:
|
||||
bundler-cache: false
|
||||
- name: Install PowerShell, mold and clang
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y powershell mold clang libcurl4-openssl-dev
|
||||
dotnet tool install --global PowerShell
|
||||
echo "$HOME/.dotnet/tools" >> $GITHUB_PATH
|
||||
sudo apt-get update && sudo apt-get install -y mold clang libcurl4-openssl-dev
|
||||
working-directory: /
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: false
|
||||
cache: true
|
||||
toolchain: 1.93.0
|
||||
- name: Read EE repo commit hash
|
||||
run: |
|
||||
@@ -214,7 +216,7 @@ jobs:
|
||||
RUST_LOG: "off"
|
||||
RUST_LOG_STYLE: never
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
CARGO_BUILD_JOBS: 12
|
||||
CARGO_BUILD_JOBS: 16
|
||||
WMDEBUG_FORCE_V0_WORKSPACE_DEPENDENCIES: 1
|
||||
WMDEBUG_FORCE_RUNNABLE_SETTINGS_V0: 1
|
||||
WMDEBUG_FORCE_NO_LEGACY_DEBOUNCING_COMPAT: 1
|
||||
@@ -222,4 +224,4 @@ jobs:
|
||||
run: |
|
||||
deno --version && bun -v && node --version && go version && python3 --version && php --version && ruby --version && pwsh --version && dotnet --version
|
||||
cd windmill-duckdb-ffi-internal && ./build_dev.sh && cd ..
|
||||
DENO_PATH=$(which deno) BUN_PATH=$(which bun) NODE_BIN_PATH=$(which node) GO_PATH=$(which go) UV_PATH=$(which uv) PHP_PATH=$(which php) COMPOSER_PATH=$(which composer) RUBY_PATH=$(which ruby) RUBY_BUNDLE_PATH=$(which bundle) RUBY_GEM_PATH=$(which gem) POWERSHELL_PATH=$(which pwsh) DOTNET_PATH=$(which dotnet) cargo test --features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,private_registry_test,csharp,php,ruby,mysql,quickjs,mcp --all -- --nocapture --test-threads=10
|
||||
DENO_PATH=$(which deno) BUN_PATH=$(which bun) NODE_BIN_PATH=$(which node) GO_PATH=$(which go) UV_PATH=$(which uv) PHP_PATH=$(which php) COMPOSER_PATH=$(which composer) RUBY_PATH=$(which ruby) RUBY_BUNDLE_PATH=$(which bundle) RUBY_GEM_PATH=$(which gem) POWERSHELL_PATH=$(which pwsh) DOTNET_PATH=$(which dotnet) cargo test --features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,private_registry_test,csharp,php,ruby,mysql,quickjs,mcp --all -- --nocapture --test-threads=12
|
||||
|
||||
23
.github/workflows/check-org-membership.yml
vendored
23
.github/workflows/check-org-membership.yml
vendored
@@ -4,10 +4,9 @@ on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
commenter:
|
||||
required: false
|
||||
required: true
|
||||
type: string
|
||||
default: ''
|
||||
description: 'The username to check. Auto-detected from the event context if not provided.'
|
||||
description: 'The username to check for organization membership'
|
||||
organization:
|
||||
required: false
|
||||
type: string
|
||||
@@ -33,27 +32,11 @@ jobs:
|
||||
outputs:
|
||||
is_member: ${{ steps.check-membership.outputs.is_member }}
|
||||
steps:
|
||||
- name: Determine commenter
|
||||
id: determine-commenter
|
||||
run: |
|
||||
COMMENTER="${{ inputs.commenter }}"
|
||||
if [[ -z "$COMMENTER" ]]; then
|
||||
if [[ "${{ github.event_name }}" == "issue_comment" || \
|
||||
"${{ github.event_name }}" == "pull_request_review_comment" ]]; then
|
||||
COMMENTER="${{ github.event.comment.user.login }}"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request_review" ]]; then
|
||||
COMMENTER="${{ github.event.review.user.login }}"
|
||||
else
|
||||
COMMENTER="${{ github.event.issue.user.login }}"
|
||||
fi
|
||||
fi
|
||||
echo "commenter=$COMMENTER" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check organization membership
|
||||
id: check-membership
|
||||
env:
|
||||
ORG_ACCESS_TOKEN: ${{ secrets.access_token }}
|
||||
COMMENTER: ${{ steps.determine-commenter.outputs.commenter }}
|
||||
COMMENTER: ${{ inputs.commenter }}
|
||||
ORG: ${{ inputs.organization }}
|
||||
TRUSTED_BOT: ${{ inputs.trusted_bot }}
|
||||
run: |
|
||||
|
||||
26
.github/workflows/claude-fast.yml
vendored
26
.github/workflows/claude-fast.yml
vendored
@@ -11,18 +11,40 @@ on:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
check-membership:
|
||||
determine-commenter:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '/ai-fast')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/ai-fast')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '/ai-fast')) ||
|
||||
(github.event_name == 'issues' && contains(github.event.issue.body, '/ai-fast'))
|
||||
runs-on: ubicloud-standard-2
|
||||
outputs:
|
||||
commenter: ${{ steps.determine-commenter.outputs.commenter }}
|
||||
steps:
|
||||
- name: Determine commenter
|
||||
id: determine-commenter
|
||||
run: |
|
||||
# Work out who wrote the comment / review
|
||||
if [[ "${{ github.event_name }}" == "issue_comment" || \
|
||||
"${{ github.event_name }}" == "pull_request_review_comment" ]]; then
|
||||
COMMENTER="${{ github.event.comment.user.login }}"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request_review" ]]; then
|
||||
COMMENTER="${{ github.event.review.user.login }}"
|
||||
else
|
||||
COMMENTER="${{ github.event.issue.user.login }}"
|
||||
fi
|
||||
echo "commenter=$COMMENTER" >> $GITHUB_OUTPUT
|
||||
|
||||
check-membership:
|
||||
needs: determine-commenter
|
||||
uses: ./.github/workflows/check-org-membership.yml
|
||||
with:
|
||||
commenter: ${{ needs.determine-commenter.outputs.commenter }}
|
||||
secrets:
|
||||
access_token: ${{ secrets.ORG_ACCESS_TOKEN }}
|
||||
|
||||
claude-code-action:
|
||||
needs: check-membership
|
||||
needs: [determine-commenter, check-membership]
|
||||
if: |
|
||||
needs.check-membership.outputs.is_member == 'true'
|
||||
runs-on: ubicloud-standard-8
|
||||
|
||||
26
.github/workflows/claude-plan.yml
vendored
26
.github/workflows/claude-plan.yml
vendored
@@ -11,18 +11,40 @@ on:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
check-membership:
|
||||
determine-commenter:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '/plan')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/plan')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '/plan')) ||
|
||||
(github.event_name == 'issues' && contains(github.event.issue.body, '/plan'))
|
||||
runs-on: ubicloud-standard-2
|
||||
outputs:
|
||||
commenter: ${{ steps.determine-commenter.outputs.commenter }}
|
||||
steps:
|
||||
- name: Determine commenter
|
||||
id: determine-commenter
|
||||
run: |
|
||||
# Work out who wrote the comment / review
|
||||
if [[ "${{ github.event_name }}" == "issue_comment" || \
|
||||
"${{ github.event_name }}" == "pull_request_review_comment" ]]; then
|
||||
COMMENTER="${{ github.event.comment.user.login }}"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request_review" ]]; then
|
||||
COMMENTER="${{ github.event.review.user.login }}"
|
||||
else
|
||||
COMMENTER="${{ github.event.issue.user.login }}"
|
||||
fi
|
||||
echo "commenter=$COMMENTER" >> $GITHUB_OUTPUT
|
||||
|
||||
check-membership:
|
||||
needs: determine-commenter
|
||||
uses: ./.github/workflows/check-org-membership.yml
|
||||
with:
|
||||
commenter: ${{ needs.determine-commenter.outputs.commenter }}
|
||||
secrets:
|
||||
access_token: ${{ secrets.ORG_ACCESS_TOKEN }}
|
||||
|
||||
claude-plan-action:
|
||||
needs: check-membership
|
||||
needs: [determine-commenter, check-membership]
|
||||
if: |
|
||||
needs.check-membership.outputs.is_member == 'true'
|
||||
runs-on: ubicloud-standard-4
|
||||
|
||||
26
.github/workflows/claude.yml
vendored
26
.github/workflows/claude.yml
vendored
@@ -11,18 +11,40 @@ on:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
check-membership:
|
||||
determine-commenter:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '/ai')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/ai')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '/ai')) ||
|
||||
(github.event_name == 'issues' && contains(github.event.issue.body, '/ai'))
|
||||
runs-on: ubicloud-standard-2
|
||||
outputs:
|
||||
commenter: ${{ steps.determine-commenter.outputs.commenter }}
|
||||
steps:
|
||||
- name: Determine commenter
|
||||
id: determine-commenter
|
||||
run: |
|
||||
# Work out who wrote the comment / review
|
||||
if [[ "${{ github.event_name }}" == "issue_comment" || \
|
||||
"${{ github.event_name }}" == "pull_request_review_comment" ]]; then
|
||||
COMMENTER="${{ github.event.comment.user.login }}"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request_review" ]]; then
|
||||
COMMENTER="${{ github.event.review.user.login }}"
|
||||
else
|
||||
COMMENTER="${{ github.event.issue.user.login }}"
|
||||
fi
|
||||
echo "commenter=$COMMENTER" >> $GITHUB_OUTPUT
|
||||
|
||||
check-membership:
|
||||
needs: determine-commenter
|
||||
uses: ./.github/workflows/check-org-membership.yml
|
||||
with:
|
||||
commenter: ${{ needs.determine-commenter.outputs.commenter }}
|
||||
secrets:
|
||||
access_token: ${{ secrets.ORG_ACCESS_TOKEN }}
|
||||
|
||||
claude-code-action:
|
||||
needs: check-membership
|
||||
needs: [determine-commenter, check-membership]
|
||||
if: |
|
||||
needs.check-membership.outputs.is_member == 'true'
|
||||
runs-on: ubicloud-standard-8
|
||||
|
||||
39
.github/workflows/create-docs.yml
vendored
Normal file
39
.github/workflows/create-docs.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
check-membership:
|
||||
if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/docs') }}
|
||||
uses: ./.github/workflows/check-org-membership.yml
|
||||
with:
|
||||
commenter: ${{ github.event.comment.user.login }}
|
||||
secrets:
|
||||
access_token: ${{ secrets.ORG_ACCESS_TOKEN }}
|
||||
|
||||
generate-token:
|
||||
needs: check-membership
|
||||
if: ${{ needs.check-membership.outputs.is_member == 'true' }}
|
||||
runs-on: ubicloud-standard-2
|
||||
outputs:
|
||||
app_token: ${{ steps.app.outputs.token }}
|
||||
steps:
|
||||
- name: Generate an installation token
|
||||
id: app
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ vars.INTERNAL_APP_ID }}
|
||||
private-key: ${{ secrets.INTERNAL_APP_KEY }}
|
||||
owner: windmill-labs
|
||||
|
||||
trigger-docs:
|
||||
needs: [generate-token, check-membership]
|
||||
if: ${{ needs.check-membership.outputs.is_member == 'true' }}
|
||||
uses: windmill-labs/windmilldocs/.github/workflows/create-docs.yml@main
|
||||
with:
|
||||
pr_number: ${{ github.event.issue.number }}
|
||||
repo: ${{ github.event.repository.name }}
|
||||
comment_text: ${{ github.event.comment.body }}
|
||||
secrets:
|
||||
DOCS_TOKEN: ${{ needs.generate-token.outputs.app_token }}
|
||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
53
.github/workflows/git-commands.yaml
vendored
53
.github/workflows/git-commands.yaml
vendored
@@ -5,21 +5,8 @@ on:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
check-membership:
|
||||
if: >-
|
||||
github.event.issue.pull_request && (
|
||||
startsWith(github.event.comment.body, '/updatesqlx') ||
|
||||
startsWith(github.event.comment.body, '/demo') ||
|
||||
startsWith(github.event.comment.body, '/eeref') ||
|
||||
startsWith(github.event.comment.body, '/docs')
|
||||
)
|
||||
uses: ./.github/workflows/check-org-membership.yml
|
||||
secrets:
|
||||
access_token: ${{ secrets.ORG_ACCESS_TOKEN }}
|
||||
|
||||
update-sqlx:
|
||||
needs: check-membership
|
||||
if: needs.check-membership.outputs.is_member == 'true' && startsWith(github.event.comment.body, '/updatesqlx')
|
||||
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/updatesqlx')
|
||||
runs-on: ubicloud-standard-8
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -134,8 +121,7 @@ jobs:
|
||||
})
|
||||
|
||||
demo:
|
||||
needs: check-membership
|
||||
if: needs.check-membership.outputs.is_member == 'true' && startsWith(github.event.comment.body, '/demo')
|
||||
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/demo')
|
||||
runs-on: ubicloud-standard-2
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -214,8 +200,7 @@ jobs:
|
||||
fi
|
||||
|
||||
update-ee-ref:
|
||||
needs: check-membership
|
||||
if: needs.check-membership.outputs.is_member == 'true' && startsWith(github.event.comment.body, '/eeref')
|
||||
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/eeref')
|
||||
runs-on: ubicloud-standard-2
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -298,35 +283,3 @@ jobs:
|
||||
repo: context.repo.repo,
|
||||
body: 'Successfully updated ee-repo-ref.txt'
|
||||
})
|
||||
|
||||
update-docs:
|
||||
needs: check-membership
|
||||
if: needs.check-membership.outputs.is_member == 'true' && startsWith(github.event.comment.body, '/docs')
|
||||
runs-on: ubicloud-standard-2
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app
|
||||
with:
|
||||
app-id: ${{ vars.INTERNAL_APP_ID }}
|
||||
private-key: ${{ secrets.INTERNAL_APP_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: |
|
||||
windmilldocs
|
||||
|
||||
- name: Trigger docs update
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app.outputs.token }}
|
||||
COMMENT_TEXT: ${{ github.event.comment.body }}
|
||||
run: |
|
||||
jq -n \
|
||||
--argjson pr_number ${{ github.event.issue.number }} \
|
||||
--arg repo "${{ github.event.repository.name }}" \
|
||||
--arg comment "$COMMENT_TEXT" \
|
||||
'{event_type: "create-docs", client_payload: {pr_number: $pr_number, repo: $repo, comment_text: $comment}}' | \
|
||||
gh api repos/windmill-labs/windmilldocs/dispatches \
|
||||
--method POST \
|
||||
--input -
|
||||
|
||||
26
.github/workflows/spawn-ephemeral-backend.yml
vendored
26
.github/workflows/spawn-ephemeral-backend.yml
vendored
@@ -13,16 +13,38 @@ on:
|
||||
type: number
|
||||
|
||||
jobs:
|
||||
check-membership:
|
||||
determine-commenter:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '/spawnbackend')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '/spawnbackend'))
|
||||
runs-on: ubicloud-standard-2
|
||||
outputs:
|
||||
commenter: ${{ steps.determine-commenter.outputs.commenter }}
|
||||
steps:
|
||||
- name: Determine commenter
|
||||
id: determine-commenter
|
||||
run: |
|
||||
# Work out who wrote the comment / review
|
||||
if [[ "${{ github.event_name }}" == "issue_comment" || \
|
||||
"${{ github.event_name }}" == "pull_request_review_comment" ]]; then
|
||||
COMMENTER="${{ github.event.comment.user.login }}"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request_review" ]]; then
|
||||
COMMENTER="${{ github.event.review.user.login }}"
|
||||
else
|
||||
COMMENTER="${{ github.event.issue.user.login }}"
|
||||
fi
|
||||
echo "commenter=$COMMENTER" >> $GITHUB_OUTPUT
|
||||
|
||||
check-membership:
|
||||
needs: determine-commenter
|
||||
uses: ./.github/workflows/check-org-membership.yml
|
||||
with:
|
||||
commenter: ${{ needs.determine-commenter.outputs.commenter }}
|
||||
secrets:
|
||||
access_token: ${{ secrets.ORG_ACCESS_TOKEN }}
|
||||
|
||||
spawn-backend:
|
||||
needs: check-membership
|
||||
needs: [determine-commenter, check-membership]
|
||||
# Only run on PR comments that contain /spawn-backend, or manual dispatch
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
|
||||
49
CHANGELOG.md
49
CHANGELOG.md
@@ -1,54 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [1.634.1](https://github.com/windmill-labs/windmill/compare/v1.634.0...v1.634.1) (2026-02-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* conditionally skip relock on dep job ([#7860](https://github.com/windmill-labs/windmill/issues/7860)) ([d6c72df](https://github.com/windmill-labs/windmill/commit/d6c72df99a0a500bdd925fcdcba8abd8bbe537f5))
|
||||
* improve on-boarding experience ([4e38a4f](https://github.com/windmill-labs/windmill/commit/4e38a4f1083d880b0814e336d5e27cb40187fc28))
|
||||
|
||||
## [1.634.0](https://github.com/windmill-labs/windmill/compare/v1.633.1...v1.634.0) (2026-02-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add force_sandboxing global setting and #sandbox bash annotation ([#7816](https://github.com/windmill-labs/windmill/issues/7816)) ([2646629](https://github.com/windmill-labs/windmill/commit/2646629194f260d0be3a809be421bbab1307f927))
|
||||
* support for datatables in App Db studio ([#7930](https://github.com/windmill-labs/windmill/issues/7930)) ([6cee34a](https://github.com/windmill-labs/windmill/commit/6cee34a81da389faebb1474957a3989c4aadb00f))
|
||||
|
||||
## [1.633.1](https://github.com/windmill-labs/windmill/compare/v1.633.0...v1.633.1) (2026-02-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add private registries support for RUST + java home nit ([e2c28e4](https://github.com/windmill-labs/windmill/commit/e2c28e42dbda0f7bf119efc8d587da0d30636a44))
|
||||
|
||||
## [1.633.0](https://github.com/windmill-labs/windmill/compare/v1.632.0...v1.633.0) (2026-02-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* /health endpoints ([#7727](https://github.com/windmill-labs/windmill/issues/7727)) ([7df4aa4](https://github.com/windmill-labs/windmill/commit/7df4aa4fec021fc728950fc10db4d0401a205087))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* save deployment msg in CE ([#7923](https://github.com/windmill-labs/windmill/issues/7923)) ([e9be616](https://github.com/windmill-labs/windmill/commit/e9be616d3c079a0c6fd98733c66560f2cc1ee40d))
|
||||
|
||||
## [1.632.0](https://github.com/windmill-labs/windmill/compare/v1.631.2...v1.632.0) (2026-02-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ai:** add AWS bedrock session token support ([#7908](https://github.com/windmill-labs/windmill/issues/7908)) ([d95e4db](https://github.com/windmill-labs/windmill/commit/d95e4db8f31e70a645a5f41e287557933b257db8))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add kafka kerberos runtime packages ([#7918](https://github.com/windmill-labs/windmill/issues/7918)) ([22f22c2](https://github.com/windmill-labs/windmill/commit/22f22c26612b904c2b82b415ec9337213ef593c3))
|
||||
* **frontend:** redesign instance settings ([#7916](https://github.com/windmill-labs/windmill/issues/7916)) ([dd419ad](https://github.com/windmill-labs/windmill/commit/dd419ade94a992073dfd1a979bfeb8a5fadfb051))
|
||||
* hash long dedicated worker tags ([#7914](https://github.com/windmill-labs/windmill/issues/7914)) ([aaa1b92](https://github.com/windmill-labs/windmill/commit/aaa1b92300bdc0794de5356ef6750bab5d8d81a0))
|
||||
|
||||
## [1.631.2](https://github.com/windmill-labs/windmill/compare/v1.631.1...v1.631.2) (2026-02-11)
|
||||
|
||||
|
||||
|
||||
21
CLAUDE.md
21
CLAUDE.md
@@ -17,32 +17,19 @@ When implementing new features in Windmill, follow these best practices:
|
||||
|
||||
## Language-Specific Guides
|
||||
|
||||
- Backend (Rust): see `backend/CLAUDE.md` and the `rust-backend` skill: `.claude/skills/rust-backend/SKILL.md`
|
||||
- Frontend (Svelte 5): see `frontend/CLAUDE.md` and the `svelte-frontend` skill: `.claude/skills/svelte-frontend/SKILL.md`
|
||||
|
||||
## Code Validation (MUST DO)
|
||||
|
||||
After making code changes, you MUST run the appropriate checks and fix all errors before considering the work done:
|
||||
|
||||
- **Backend**: Run `cargo check` from the `backend/` directory. Only enable the feature flags needed for the code you changed — check `backend/Cargo.toml` `[features]` section to identify which flags gate the crates/modules you modified. For example: `cargo check --features enterprise,parquet` if you only touched enterprise and parquet code.
|
||||
- **Frontend**: Run `npm run check` from the `frontend/` directory.
|
||||
- Backend (Rust): @backend/rust-best-practices.mdc + @backend/summarized_schema.txt
|
||||
- Frontend (Svelte 5): @frontend/svelte5-best-practices.mdc
|
||||
|
||||
## Querying the Database
|
||||
|
||||
`backend/summarized_schema.txt` provides a compact overview of all tables, columns, types, ENUMs, and foreign keys. Use it to quickly understand the data model and relationships. Note: this file is a simplified summary — it omits indexes, constraints details, and other metadata.
|
||||
|
||||
For exact table definitions (indexes, constraints, column defaults, etc.), query the database directly:
|
||||
To query the database directly, use psql with the following connection string:
|
||||
|
||||
```bash
|
||||
psql postgres://postgres:changeme@localhost:5432/windmill
|
||||
```
|
||||
|
||||
Useful psql commands:
|
||||
- `\d <table_name>` — full table definition with indexes and constraints
|
||||
- `\di <table_name>*` — list indexes for a table
|
||||
- `\d+ <table_name>` — extended table info including storage and descriptions
|
||||
This can be helpful for:
|
||||
|
||||
This is also helpful for:
|
||||
- Inspecting database state during development
|
||||
- Testing queries before implementing them in Rust
|
||||
- Debugging data-related issues
|
||||
|
||||
@@ -132,7 +132,6 @@ ARG WITH_POWERSHELL=true
|
||||
ARG WITH_KUBECTL=true
|
||||
ARG WITH_HELM=true
|
||||
ARG WITH_GIT=true
|
||||
ARG features=""
|
||||
|
||||
# To change latest stable version:
|
||||
# 1. Change placeholder in instanceSettings.ts
|
||||
@@ -150,8 +149,7 @@ ENV PATH /usr/local/bin:/root/.local/bin:/tmp/.local/bin:$PATH
|
||||
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends netbase tzdata ca-certificates wget curl jq unzip build-essential unixodbc xmlsec1 software-properties-common tini \
|
||||
&& if echo "$features" | grep -q "ee"; then apt-get install -y --no-install-recommends libsasl2-modules-gssapi-mit krb5-user; fi \
|
||||
&& apt-get install -y --no-install-recommends netbase tzdata ca-certificates wget curl jq unzip build-essential unixodbc xmlsec1 software-properties-common tini libsasl2-modules-gssapi-mit \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ Scripts are turned into sharable UIs automatically, and can be composed together
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://app.windmill.dev">Try it</a> - <a href="https://www.windmill.dev/">Website</a> - <a href="https://www.windmill.dev/docs/intro/">Docs</a> - <a href="https://discord.gg/V7PM2YHsPB">Discord</a> - <a href="https://hub.windmill.dev">Hub</a> - <a href="https://www.windmill.dev/docs/misc/contributing">Contributor's guide</a>
|
||||
<a href="https://app.windmill.dev">Try it</a> - <a href="https://www.windmill.dev/docs/intro/">Docs</a> - <a href="https://discord.gg/V7PM2YHsPB">Discord</a> - <a href="https://hub.windmill.dev">Hub</a> - <a href="https://www.windmill.dev/docs/misc/contributing">Contributor's guide</a>
|
||||
</p>
|
||||
|
||||
# Windmill - Developer platform for APIs, background jobs, workflows and UIs
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +122,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM resource_type WHERE workspace_id = 'admins' AND name = $1 AND schema IS NOT DISTINCT FROM $2 AND description IS NOT DISTINCT FROM $3)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Jsonb",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "1ea97f9085ec018f779e77e0fdbda3d4ecd67b3fbee9a58228ef577f846607ae"
|
||||
}
|
||||
@@ -34,8 +34,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -68,8 +67,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT logs FROM job_logs WHERE created_at > $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "logs",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Timestamptz"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3cc7ecab48c379cd845b22012ef1fe1573fc332c9f1c44960084f3784ddb3f54"
|
||||
}
|
||||
@@ -16,8 +16,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -52,8 +51,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO resource_type (workspace_id, name, schema, description, edited_at)\n VALUES ('admins', $1, $2, $3, now())\n ON CONFLICT (workspace_id, name) DO UPDATE\n SET schema = EXCLUDED.schema, description = EXCLUDED.description, edited_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4b93550c7836fd3643180ade3548faa875e471d3f9ca37fc669f359e7a1818bb"
|
||||
}
|
||||
@@ -37,8 +37,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -71,8 +70,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO dependency_map (workspace_id, importer_path, importer_kind, imported_path, importer_node_id, imported_lockfile_hash)\n VALUES ($1, $2, $3::text::IMPORTER_KIND, $4, $5, $6)\n ON CONFLICT (workspace_id, importer_node_id, importer_kind, importer_path, imported_path)\n DO UPDATE SET imported_lockfile_hash = EXCLUDED.imported_lockfile_hash",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "55a7460901c8ccdb478a2b7a3d4bd9d367838fe80844b5a14d2ef66452f36297"
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55"
|
||||
|
||||
@@ -245,8 +245,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT hash FROM script WHERE path = 'f/rel/leaf_2' AND workspace_id = 'test-workspace' AND archived = false ORDER BY created_at DESC LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "hash",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "5f6d0d1b24693db22230ec8322b3174ecc195242a1205131f5d0d32148c0280a"
|
||||
}
|
||||
@@ -35,8 +35,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n WITH ins AS (\n INSERT INTO workspace_dependencies(name, workspace_id, content, language, description)\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id\n ), lock_ins AS (\n INSERT INTO lock_hash (workspace_id, path, lockfile_hash)\n VALUES ($2, $6, $7)\n ON CONFLICT (workspace_id, path) DO UPDATE SET lockfile_hash = $7\n )\n SELECT id FROM ins\n ",
|
||||
"query": "\n INSERT INTO workspace_dependencies(name, workspace_id, content, language, description)\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -46,14 +46,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Int8"
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "8acc8c47123af84d2308042dcd34d04b941e928c7621e66d3bdc510c335f8c20"
|
||||
"hash": "6c10c38a5d5e560d8d76159ce0ae118cf7191b06d72285e71256dd6d70c19451"
|
||||
}
|
||||
@@ -29,8 +29,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n COUNT(*) > 0\n AND BOOL_AND(\n CASE\n -- For dependencies/: use IS NOT DISTINCT FROM (NULL = NULL is true for legacy)\n -- And the reason for this, is that for every script we also add dependency on default workspace dependencies by default\n -- that default/unnamed workspace dependencies may not exist, but we still do this.\n -- it is needed for windmill to know what to redeploy when default workspace dependencies are being added\n\n -- naturally for non-existant entries we have no hash of it\n -- so if we compared hash with '=' (instead of IS NOT DISTINCT FROM), it would give false on NULL = NULL,\n -- which would mean that this entire query returns false, which means relock skip cannot happen\n\n -- The solution is to say if both: referenced and current hash are NULLs we treat it as true, so it becomes no longer a blocker from skip.\n --\n -- It is backed up by the fact that server is responsible for deploying new workspace dependencies\n -- so if one was to deploy a new wdeps, server would assign it new hash, and this expression would be invalid and would not approve skip\n WHEN dm.imported_path LIKE 'dependencies/%'\n THEN dm.imported_lockfile_hash IS NOT DISTINCT FROM lh.lockfile_hash\n\n -- For scripts: use = with COALESCE (NULL = NULL becomes false)\n -- unlike w deps, we can't do IS NOT DISTINCT FROM here\n -- the reason is that scripts deployments are issued by other workers instead of the server\n -- which would mean that there is no guarantee that new d job will also write it's lock's hash to the `lock_hash`\n -- which could lead to false positives\n ELSE COALESCE(dm.imported_lockfile_hash = lh.lockfile_hash, false)\n END\n )\n FROM dependency_map dm\n LEFT JOIN lock_hash lh\n ON lh.workspace_id = dm.workspace_id\n AND lh.path = dm.imported_path\n WHERE dm.workspace_id = $1\n AND dm.importer_path = $2\n AND dm.importer_node_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "9ca267c5a88f0cf3a4821e7514fe585131a42136c522a0fdde47c63cc0c6bc5a"
|
||||
}
|
||||
@@ -30,8 +30,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET deployment_msg = EXCLUDED.deployment_msg",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9f07510019ebe6f0c5fa17bf31c2d14755474cba82b3b388a47585a8bb325b1a"
|
||||
}
|
||||
@@ -155,8 +155,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,8 +185,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,8 +160,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -72,8 +71,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM worker_ping WHERE ping_at > now() - interval '5 minutes'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "bbf166552ba15ce7cd76f812cdf7223414fa5c8a4860f3fe829d649baa1c5465"
|
||||
}
|
||||
@@ -105,8 +105,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT lock FROM script WHERE path = $1 AND workspace_id = $2 AND lock IS NOT NULL\n AND deleted = false ORDER BY created_at DESC LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "lock",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "cbbe20745c2b743acc814e8e77c0e4e154b511af676e085bbe88e72e7d14aee3"
|
||||
}
|
||||
@@ -11,8 +11,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +105,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
16
backend/.sqlx/query-d697b7311430e7bd5375ec5494179f4071e3bfe123d9600249cfa80c1103edd8.json
generated
Normal file
16
backend/.sqlx/query-d697b7311430e7bd5375ec5494179f4071e3bfe123d9600249cfa80c1103edd8.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE script SET lock = $1 WHERE hash = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d697b7311430e7bd5375ec5494179f4071e3bfe123d9600249cfa80c1103edd8"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "e004ebd5b5532a4b85984a62f8ad48a81aa3460c1ca07701f386135d72cdecf5"
|
||||
}
|
||||
18
backend/.sqlx/query-e32d6c6ae4e0d824c4cf19128182d67f36d1fd87fe4cf4f002e18367088c497c.json
generated
Normal file
18
backend/.sqlx/query-e32d6c6ae4e0d824c4cf19128182d67f36d1fd87fe4cf4f002e18367088c497c.json
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO dependency_map (workspace_id, importer_path, importer_kind, imported_path, importer_node_id)\n VALUES ($1, $2, $3::text::IMPORTER_KIND, $4, $5) ON CONFLICT DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e32d6c6ae4e0d824c4cf19128182d67f36d1fd87fe4cf4f002e18367088c497c"
|
||||
}
|
||||
@@ -185,8 +185,7 @@
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n DELETE FROM dependency_map\n WHERE workspace_id = $1\n AND importer_path = $2\n AND importer_kind = $3::text::IMPORTER_KIND\n AND importer_node_id = $4\n AND imported_path = $5\n AND imported_lockfile_hash IS NOT DISTINCT FROM $6 -- we don't want to delete other entries with other lockfile hash.\n ",
|
||||
"query": "\n DELETE FROM dependency_map\n WHERE workspace_id = $1\n AND importer_path = $2\n AND importer_kind = $3::text::IMPORTER_KIND\n AND importer_node_id = $4\n AND imported_path = $5\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -9,11 +9,10 @@
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Int8"
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c8dee3bd1ce03d8ee3c05345a0bdd5bdbd75396fe5bc24fd2d42edc79261169c"
|
||||
"hash": "ead84a63cb965e36155605434c9e3670e50344f39b0d61eef8e60c2cdb57ae1f"
|
||||
}
|
||||
@@ -21,8 +21,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -72,8 +71,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
"mqtt",
|
||||
"gcp",
|
||||
"default_email",
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -72,8 +71,7 @@
|
||||
"name": "native_trigger_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"nextcloud",
|
||||
"google"
|
||||
"nextcloud"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH update_lock AS (\n UPDATE script SET lock = $1 WHERE hash = $2 AND workspace_id = $3\n )\n INSERT INTO lock_hash (workspace_id, path, lockfile_hash)\n VALUES ($3, $4, $5)\n ON CONFLICT (workspace_id, path) DO UPDATE SET lockfile_hash = $5",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f8c08997d7fd0158b4d3cc093fb0a2aadd4473e98873d5a7873f4fa944b99d58"
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n worker_group,\n wm_version\n FROM worker_ping\n WHERE ping_at > now() - interval '5 minutes'\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "worker_group",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "wm_version",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f912b91c940900b9961920ac0f71b7ba1e8cd50ce90c0c17336e9dd9c684f348"
|
||||
}
|
||||
@@ -49,24 +49,6 @@ Windmill uses a workspace-based architecture with multiple crates:
|
||||
- Use feature flags: `#[cfg(feature = "enterprise")]`
|
||||
- Isolate enterprise code in separate modules
|
||||
|
||||
## Code Validation (MUST DO)
|
||||
|
||||
After making backend changes, you MUST run `cargo check` and fix all errors and warnings before considering the work done.
|
||||
|
||||
Only enable the feature flags relevant to your changes — do NOT use `all_sqlx_features` as it compiles the entire codebase and is very slow. Check the `[features]` section in `Cargo.toml` to identify which flags gate the crates/modules you modified.
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
# Changed core code (no feature-gated modules)
|
||||
cargo check
|
||||
|
||||
# Changed code behind the enterprise feature
|
||||
cargo check --features enterprise
|
||||
|
||||
# Changed kafka trigger code
|
||||
cargo check --features kafka
|
||||
```
|
||||
|
||||
## Git Workflow
|
||||
|
||||
- **Never push directly to main** — always create a branch and open a pull request
|
||||
|
||||
411
backend/Cargo.lock
generated
411
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.634.1"
|
||||
version = "1.631.2"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -74,7 +74,7 @@ members = [
|
||||
exclude = ["./windmill-duckdb-ffi-internal"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.634.1"
|
||||
version = "1.631.2"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -95,8 +95,8 @@ lto = "thin"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
private = ["windmill-api/private", "windmill-api-agent-workers?/private", "windmill-autoscaling/private", "windmill-common/private", "windmill-git-sync/private", "windmill-indexer/private", "windmill-queue/private", "windmill-worker/private", "windmill-test-utils/private"]
|
||||
agent_worker_server = ["windmill-api/agent_worker_server", "dep:windmill-api-agent-workers", "windmill-test-utils/agent_worker_server"]
|
||||
private = ["windmill-api/private", "windmill-api-agent-workers?/private", "windmill-autoscaling/private", "windmill-common/private", "windmill-git-sync/private", "windmill-indexer/private", "windmill-queue/private", "windmill-worker/private"]
|
||||
agent_worker_server = ["windmill-api/agent_worker_server", "dep:windmill-api-agent-workers"]
|
||||
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "windmill-api-agent-workers?/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise"]
|
||||
local_reports = ["windmill-common/local_reports"]
|
||||
enterprise_saml = ["windmill-api/enterprise_saml", "oauth2"]
|
||||
@@ -112,7 +112,7 @@ cloud = ["windmill-queue/cloud", "windmill-worker/cloud", "windmill-common/cloud
|
||||
jemalloc = ["windmill-common/jemalloc", "dep:tikv-jemallocator", "dep:tikv-jemalloc-sys", "dep:tikv-jemalloc-ctl"]
|
||||
tantivy = ["dep:windmill-indexer", "windmill-api/tantivy", "windmill-indexer/enterprise", "windmill-indexer/parquet", "windmill-common/tantivy", "enterprise", "parquet"]
|
||||
sqlx = ["windmill-worker/sqlx"]
|
||||
deno_core = ["windmill-worker/deno_core", "dep:windmill-runtime-nativets", "windmill-test-utils/deno_core"]
|
||||
deno_core = ["windmill-worker/deno_core", "dep:windmill-runtime-nativets"]
|
||||
deno_core_mac = ["deno_core", "windmill-worker/libffi_mac"]
|
||||
kafka = ["windmill-api/kafka"]
|
||||
kafka-gssapi = ["windmill-api/kafka-gssapi"]
|
||||
@@ -137,7 +137,7 @@ scoped_cache = ["windmill-common/scoped_cache"]
|
||||
no_auth = ["windmill-api/no_auth"]
|
||||
private_registry_test = []
|
||||
# Languages
|
||||
python = ["windmill-worker/python", "windmill-api/python", "windmill-test-utils/python"]
|
||||
python = ["windmill-worker/python", "windmill-api/python"]
|
||||
rust = ["windmill-worker/rust"]
|
||||
mysql = ["windmill-worker/mysql"]
|
||||
oracledb = ["windmill-worker/oracledb"]
|
||||
@@ -181,8 +181,7 @@ ee_windows = ["ce_core", "ee_core", "all_languages_windows"]
|
||||
all_sqlx_features = ["all_languages", "enterprise", "enterprise_saml", "embedding", "parquet", "prometheus", "flow_testing",
|
||||
"openidconnect", "cloud", "jemalloc", "tantivy", "sqlx", "kafka", "kafka-gssapi", "nats", "otel", "dind", "websocket", "http_trigger",
|
||||
"postgres_trigger", "mcp", "mqtt_trigger", "sqs_trigger", "gcp_trigger", "smtp", "stripe",
|
||||
"license", "oauth2", "zip", "static_frontend", "scoped_cache", "agent_worker_server", "bedrock", "native_trigger", "quickjs",
|
||||
"windmill-git-sync/all_sqlx_features"]
|
||||
"license", "oauth2", "zip", "static_frontend", "scoped_cache", "agent_worker_server", "bedrock", "native_trigger", "quickjs"]
|
||||
|
||||
[patch.crates-io]
|
||||
object_store = { git = "https://github.com/apache/arrow-rs-object-store", rev = "36752c975d4f29e20b57c91f81a10872dcd48ae7" }
|
||||
|
||||
@@ -1 +1 @@
|
||||
e7f80bca9320580e1cb96b4f4ca9942649abce7f
|
||||
ab082a1a65830bf362a32b30fa86741999da091d
|
||||
@@ -1,3 +0,0 @@
|
||||
DROP TABLE IF EXISTS lock_hash;
|
||||
DROP INDEX IF EXISTS dependency_map_importer_path_idx;
|
||||
ALTER TABLE dependency_map DROP COLUMN IF EXISTS imported_lockfile_hash;
|
||||
@@ -1,14 +0,0 @@
|
||||
-- Add column to track lockfile hash of imported scripts
|
||||
-- Used to skip re-locking when imports' lockfiles haven't changed
|
||||
ALTER TABLE dependency_map ADD COLUMN imported_lockfile_hash BIGINT;
|
||||
|
||||
-- Index for queries filtering by importer (skip-relock check, load(), clear_map_for_item)
|
||||
CREATE INDEX IF NOT EXISTS dependency_map_importer_path_idx ON dependency_map (workspace_id, importer_path);
|
||||
|
||||
-- Stores lockfile/content hashes to detect when imports' locks have changed
|
||||
CREATE TABLE lock_hash (
|
||||
workspace_id VARCHAR(50) NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
|
||||
path VARCHAR(255) NOT NULL,
|
||||
lockfile_hash BIGINT NOT NULL,
|
||||
PRIMARY KEY (workspace_id, path)
|
||||
);
|
||||
@@ -1,3 +0,0 @@
|
||||
-- The setup_app was a complex app created via multiple migrations.
|
||||
-- Restoring it would require re-running the original creation and update migrations.
|
||||
-- This is a no-op down migration since the app is no longer needed.
|
||||
@@ -1 +0,0 @@
|
||||
DELETE FROM app WHERE workspace_id = 'admins' AND path = 'g/all/setup_app';
|
||||
@@ -328,7 +328,6 @@ pub static FULL_IMPORTS_MAP: PyMap = phf_map! {
|
||||
"azure.keyvault.secrets" => "azure-keyvault-secrets",
|
||||
"azure.storage.blob" => "azure-storage-blob",
|
||||
"azure.storage.filedatalake" => "azure-storage-file-datalake",
|
||||
"azure.identity" => "azure-identity",
|
||||
// Add new entry here ^
|
||||
};
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ use windmill_common::{
|
||||
CRITICAL_ERROR_CHANNELS_SETTING, CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING,
|
||||
DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
JOB_ISOLATION_SETTING, HUB_API_SECRET_SETTING, HUB_BASE_URL_SETTING, INDEXER_SETTING,
|
||||
HUB_API_SECRET_SETTING, HUB_BASE_URL_SETTING, INDEXER_SETTING,
|
||||
INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING,
|
||||
MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NO_DEFAULT_MAVEN_SETTING,
|
||||
@@ -99,9 +99,8 @@ use crate::monitor::{
|
||||
reload_app_workspaced_route_setting, reload_base_url_setting,
|
||||
reload_bunfig_install_scopes_setting, reload_critical_alert_mute_ui_setting,
|
||||
reload_critical_error_channels_setting, reload_extra_pip_index_url_setting,
|
||||
reload_job_isolation_setting, reload_hub_api_secret_setting, reload_hub_base_url_setting,
|
||||
reload_job_default_timeout_setting, reload_jwt_secret_setting, reload_license_key,
|
||||
reload_npm_config_registry_setting,
|
||||
reload_hub_api_secret_setting, reload_hub_base_url_setting, reload_job_default_timeout_setting,
|
||||
reload_jwt_secret_setting, reload_license_key, reload_npm_config_registry_setting,
|
||||
reload_otel_tracing_proxy_setting, reload_pip_index_url_setting,
|
||||
reload_retention_period_setting, reload_scim_token_setting, reload_smtp_config,
|
||||
reload_uv_index_strategy_setting, reload_worker_config, MonitorIteration,
|
||||
@@ -1217,15 +1216,11 @@ Windmill Community Edition {GIT_VERSION}
|
||||
last_settings_reload = Instant::now();
|
||||
}
|
||||
|
||||
let monitor_start = Instant::now();
|
||||
let warn_handle = if server_mode {
|
||||
Some(tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
tracing::warn!("monitor task has been running for more than 5s");
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if server_mode {
|
||||
if !*windmill_common::QUIET_LOGS {
|
||||
tracing::info!("monitor task started");
|
||||
}
|
||||
}
|
||||
monitor_db(
|
||||
&conn,
|
||||
&base_internal_url,
|
||||
@@ -1240,12 +1235,10 @@ Windmill Community Edition {GIT_VERSION}
|
||||
)
|
||||
.await;
|
||||
monitor_iteration += 1;
|
||||
if let Some(handle) = warn_handle {
|
||||
handle.abort();
|
||||
}
|
||||
let elapsed = monitor_start.elapsed();
|
||||
if server_mode && elapsed >= Duration::from_secs(5) {
|
||||
tracing::info!("monitor task finished in {elapsed:.1?}");
|
||||
if server_mode {
|
||||
if !*windmill_common::QUIET_LOGS {
|
||||
tracing::info!("monitor task finished");
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1562,7 +1555,6 @@ async fn process_notify_event(
|
||||
reload_delete_logs_periodically_setting(conn).await
|
||||
}
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING => reload_job_default_timeout_setting(conn).await,
|
||||
JOB_ISOLATION_SETTING => reload_job_isolation_setting(conn).await,
|
||||
#[cfg(feature = "parquet")]
|
||||
OBJECT_STORE_CONFIG_SETTING => {
|
||||
if !disable_s3_store {
|
||||
|
||||
@@ -54,14 +54,13 @@ use windmill_common::{
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
HUB_API_SECRET_SETTING, HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING,
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING, JOB_ISOLATION_SETTING, JWT_SECRET_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING,
|
||||
NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OTEL_SETTING,
|
||||
OTEL_TRACING_PROXY_SETTING, PIP_INDEX_URL_SETTING, POWERSHELL_REPO_PAT_SETTING,
|
||||
POWERSHELL_REPO_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING,
|
||||
LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING,
|
||||
NUGET_CONFIG_SETTING, OTEL_SETTING, OTEL_TRACING_PROXY_SETTING, PIP_INDEX_URL_SETTING,
|
||||
UV_INDEX_STRATEGY_SETTING,
|
||||
POWERSHELL_REPO_PAT_SETTING, POWERSHELL_REPO_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
UV_INDEX_STRATEGY_SETTING,
|
||||
},
|
||||
indexer::load_indexer_config,
|
||||
jwt::JWT_SECRET,
|
||||
@@ -86,10 +85,9 @@ use windmill_common::{
|
||||
use windmill_common::{client::AuthedClient, global_settings::APP_WORKSPACED_ROUTE_SETTING};
|
||||
use windmill_queue::{cancel_job, get_queued_job_v2, SameWorkerPayload};
|
||||
use windmill_worker::{
|
||||
result_processor::handle_job_error, JobCompletedSender, JobIsolationLevel,
|
||||
OtelTracingProxySettings, SameWorkerSender, BUNFIG_INSTALL_SCOPES, CARGO_REGISTRIES,
|
||||
INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, JOB_ISOLATION, KEEP_JOB_DIR, MAVEN_REPOS,
|
||||
NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NSJAIL_AVAILABLE, NUGET_CONFIG,
|
||||
result_processor::handle_job_error, JobCompletedSender, OtelTracingProxySettings,
|
||||
SameWorkerSender, BUNFIG_INSTALL_SCOPES, INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT,
|
||||
KEEP_JOB_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NUGET_CONFIG,
|
||||
OTEL_TRACING_PROXY_SETTINGS, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, POWERSHELL_REPO_PAT,
|
||||
POWERSHELL_REPO_URL, UV_INDEX_STRATEGY,
|
||||
};
|
||||
@@ -318,7 +316,6 @@ pub async fn initial_load(
|
||||
|
||||
if worker_mode {
|
||||
reload_job_default_timeout_setting(&conn).await;
|
||||
reload_job_isolation_setting(&conn).await;
|
||||
reload_extra_pip_index_url_setting(&conn).await;
|
||||
reload_pip_index_url_setting(&conn).await;
|
||||
reload_uv_index_strategy_setting(&conn).await;
|
||||
@@ -331,7 +328,6 @@ pub async fn initial_load(
|
||||
reload_maven_repos_setting(&conn).await;
|
||||
reload_no_default_maven_setting(&conn).await;
|
||||
reload_ruby_repos_setting(&conn).await;
|
||||
reload_cargo_registries_setting(&conn).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1362,16 +1358,6 @@ pub async fn reload_ruby_repos_setting(conn: &Connection) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reload_cargo_registries_setting(conn: &Connection) {
|
||||
reload_option_setting_with_tracing(
|
||||
conn,
|
||||
windmill_common::global_settings::CARGO_REGISTRIES_SETTING,
|
||||
"CARGO_REGISTRIES",
|
||||
CARGO_REGISTRIES.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reload_hub_api_secret_setting(conn: &Connection) {
|
||||
reload_option_setting_with_tracing(
|
||||
conn,
|
||||
@@ -1421,32 +1407,6 @@ pub async fn reload_job_default_timeout_setting(conn: &Connection) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reload_job_isolation_setting(conn: &Connection) {
|
||||
let value =
|
||||
match load_value_from_global_settings_with_conn(conn, JOB_ISOLATION_SETTING, true).await {
|
||||
Ok(Some(v)) => JobIsolationLevel::from_str(v.as_str().unwrap_or("")),
|
||||
Ok(None) => JobIsolationLevel::Undefined,
|
||||
Err(e) => {
|
||||
tracing::error!("Error reloading job_isolation setting: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let old_value = JobIsolationLevel::from_u8(JOB_ISOLATION.swap(value as u8, Ordering::Relaxed));
|
||||
if old_value != value {
|
||||
tracing::info!(
|
||||
"job_isolation setting changed from {:?} to {:?}",
|
||||
old_value,
|
||||
value
|
||||
);
|
||||
}
|
||||
if value == JobIsolationLevel::NsjailSandboxing && NSJAIL_AVAILABLE.is_none() {
|
||||
tracing::error!(
|
||||
"job_isolation is set to nsjail_sandboxing but nsjail is not available on this worker. \
|
||||
All jobs will fail until nsjail is installed or the setting is changed."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reload_request_size(conn: &Connection) {
|
||||
if let Err(e) = reload_setting(
|
||||
conn,
|
||||
|
||||
@@ -11,10 +11,12 @@ EE_CODE_DIR="../windmill-ee-private/"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-r|--revert)
|
||||
# Removes all _ee.rs files from the backend directory. Symlinks are deleted.
|
||||
# Regular files with content differing from windmill-ee-private are moved back
|
||||
# to the EE repo so nothing is lost.
|
||||
# If EE files have been substituted, this will revert them to their initial content.
|
||||
# This relies on `git restore` so the EE files must not be committed to the repo for
|
||||
# this to work (commit hooks should prevent this from happening, as well as the fact
|
||||
# that we're using symlinks by default).
|
||||
REVERT="YES"
|
||||
MOVE_NEW_FILES="YES"
|
||||
shift
|
||||
;;
|
||||
-c|--copy)
|
||||
@@ -64,28 +66,12 @@ if [ ! -d "${EE_CODE_DIR}" ]; then
|
||||
fi
|
||||
|
||||
if [ "$REVERT" == "YES" ]; then
|
||||
backend_dirpath="${root_dirpath}/backend/"
|
||||
for ce_file in $(find "${root_dirpath}/backend" -name "*_ee.rs"); do
|
||||
if [ -L "${ce_file}" ]; then
|
||||
rm "${ce_file}"
|
||||
echo "Deleted symlink '${ce_file}'"
|
||||
else
|
||||
ee_file="${ce_file/${backend_dirpath}/}"
|
||||
ee_file="${EE_CODE_DIR}${ee_file}"
|
||||
if [ ! -f "${ee_file}" ] || ! diff -q "${ce_file}" "${ee_file}" > /dev/null 2>&1; then
|
||||
mkdir -p "$(dirname "${ee_file}")"
|
||||
mv "${ce_file}" "${ee_file}"
|
||||
echo "Moved '${ce_file}' -->> '${ee_file}'"
|
||||
else
|
||||
rm "${ce_file}"
|
||||
echo "Deleted '${ce_file}' (identical to EE)"
|
||||
fi
|
||||
fi
|
||||
for ee_file in $(find ${EE_CODE_DIR} -name "*ee.rs"); do
|
||||
ce_file="${ee_file/${EE_CODE_DIR}/}"
|
||||
ce_file="${root_dirpath}/backend/${ce_file}"
|
||||
rm ${ce_file} || true
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$MOVE_NEW_FILES" == "NO" ]; then
|
||||
elif [ "$MOVE_NEW_FILES" == "NO" ]; then
|
||||
# This replaces all files in current repo with alternative EE files in windmill-ee-private
|
||||
for ee_file in $(find "${EE_CODE_DIR}" -name "*ee.rs"); do
|
||||
ce_file="${ee_file/${EE_CODE_DIR}/}"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# This script is used to summarize the database schema.
|
||||
# You can use pg_dump to dump the schema to a file.
|
||||
# pg_dump --file "schema.sql" --format=p --large-objects --schema-only --no-owner --no-privileges --no-tablespaces --no-unlogged-table-data --no-comments --no-publications --no-subscriptions --no-security-labels --no-toast-compression --no-table-access-method --verbose --schema "public" "postgresql://postgres:changeme@localhost:5432/windmill"
|
||||
# pg_dump --file "schema.sql" --host "localhost" --port "5432" --username "postgres" --no-password --format=c --large-objects --schema-only --no-owner --no-privileges --no-tablespaces --no-unlogged-table-data --no-comments --no-publications --no-subscriptions --no-security-labels --no-toast-compression --no-table-access-method --verbose --schema "public" "windmill"
|
||||
# Then you can run python summarize_schema.py schema.sql to get the summarized schema.
|
||||
|
||||
import re
|
||||
@@ -17,7 +17,6 @@ def summarize_schema(file_path):
|
||||
# Use state variables to parse multi-line definitions
|
||||
current_table = None
|
||||
current_enum = None
|
||||
pending_alter_table = None # For multi-line ALTER TABLE ... ADD CONSTRAINT
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
@@ -55,9 +54,6 @@ def summarize_schema(file_path):
|
||||
match_column = re.match(r'^"?(\w+)"?\s+([\w\d\.\[\]\(\)]+)', line)
|
||||
if match_column:
|
||||
col_name = match_column.group(1)
|
||||
# Skip CONSTRAINT definitions (e.g., CHECK, UNIQUE) mistakenly matched as columns
|
||||
if col_name.upper() == 'CONSTRAINT':
|
||||
continue
|
||||
col_type = match_column.group(2)
|
||||
tables[current_table]['columns'].append(f"{col_name} ({col_type})")
|
||||
|
||||
@@ -69,22 +65,16 @@ def summarize_schema(file_path):
|
||||
tables[current_table]['pks'].update(pk_cols)
|
||||
continue
|
||||
|
||||
# --- Parse Foreign Keys (defined outside CREATE TABLE, may span 2 lines) ---
|
||||
match_alter = re.match(r"ALTER TABLE ONLY public\.(\w+)$", line)
|
||||
if match_alter:
|
||||
pending_alter_table = match_alter.group(1)
|
||||
continue
|
||||
|
||||
if pending_alter_table:
|
||||
match_fk = re.match(r"ADD CONSTRAINT \w+ FOREIGN KEY \(([\w,\s\"]+)\) REFERENCES public\.(\w+)\(([\w,\s\"]+)\)", line)
|
||||
if match_fk:
|
||||
from_cols, to_table, to_cols = match_fk.groups()
|
||||
from_cols_clean = ', '.join([c.strip().strip('"') for c in from_cols.split(',')])
|
||||
to_cols_clean = ', '.join([c.strip().strip('"') for c in to_cols.split(',')])
|
||||
fk_string = f"({from_cols_clean}) -> {to_table}({to_cols_clean})"
|
||||
tables[pending_alter_table]['fks'].append(fk_string)
|
||||
pending_alter_table = None
|
||||
continue
|
||||
# --- Parse Foreign Keys (defined outside CREATE TABLE) ---
|
||||
match_fk = re.match(r"ALTER TABLE ONLY public\.(\w+)\s+ADD CONSTRAINT \w+ FOREIGN KEY \(([\w,\s\"]+)\) REFERENCES public\.(\w+)\(([\w,\s\"]+)\);", line)
|
||||
if match_fk:
|
||||
from_table, from_cols, to_table, to_cols = match_fk.groups()
|
||||
# Clean up column names
|
||||
from_cols_clean = ', '.join([c.strip().strip('"') for c in from_cols.split(',')])
|
||||
to_cols_clean = ', '.join([c.strip().strip('"') for c in to_cols.split(',')])
|
||||
|
||||
fk_string = f"({from_cols_clean}) -> {to_table}({to_cols_clean})"
|
||||
tables[from_table]['fks'].append(fk_string)
|
||||
|
||||
# --- Parse Index definitions ---
|
||||
match_index = re.match(r"CREATE (UNIQUE )?INDEX (\w+) ON public\.(\w+) USING (\w+) \((.+)\);", line)
|
||||
@@ -104,73 +94,44 @@ def summarize_schema(file_path):
|
||||
|
||||
return enums, tables
|
||||
|
||||
TYPE_ABBREVIATIONS = {
|
||||
'character': 'char',
|
||||
'integer': 'int',
|
||||
'bigint': 'bigint',
|
||||
'smallint': 'smallint',
|
||||
'boolean': 'bool',
|
||||
'timestamp': 'ts',
|
||||
'bytea': 'bytes',
|
||||
'real': 'float',
|
||||
'json': 'json',
|
||||
'jsonb': 'jsonb',
|
||||
'text': 'text',
|
||||
'uuid': 'uuid',
|
||||
'bit(64)': 'bit64',
|
||||
}
|
||||
|
||||
def shorten_type(col_str):
|
||||
"""Shorten a 'name (type)' string to 'name(short_type)'."""
|
||||
match = re.match(r'^(\w+) \((.+)\)$', col_str)
|
||||
if not match:
|
||||
return col_str
|
||||
name, typ = match.group(1), match.group(2)
|
||||
# Strip public. prefix from enum types
|
||||
typ = re.sub(r'^public\.', '', typ)
|
||||
# Apply abbreviations (prefix-based to handle parametrized types like character(64) and array types like integer[])
|
||||
for prefix, abbr in TYPE_ABBREVIATIONS.items():
|
||||
if typ.startswith(prefix):
|
||||
typ = abbr + typ[len(prefix):]
|
||||
break
|
||||
return f"{name}({typ})"
|
||||
|
||||
def format_output(enums, tables):
|
||||
"""
|
||||
Formats the parsed schema data into a compact, LLM-friendly string.
|
||||
Formats the parsed schema data into a clean, readable string.
|
||||
"""
|
||||
output = []
|
||||
|
||||
output.append("# Database Schema")
|
||||
output.append("")
|
||||
output.append("## ENUMs")
|
||||
|
||||
output.append("### Simplified Database Schema ###")
|
||||
output.append("\n--- Custom Data Types (ENUMs) ---\n")
|
||||
if not enums:
|
||||
output.append("(none)")
|
||||
output.append("No custom ENUM types found.")
|
||||
else:
|
||||
for name, values in sorted(enums.items()):
|
||||
output.append(f"{name}: {', '.join(values)}")
|
||||
output.append(f"{name}:")
|
||||
for v in values:
|
||||
output.append(f" - {v}")
|
||||
output.append("")
|
||||
|
||||
output.append("")
|
||||
output.append("## Tables")
|
||||
output.append("\n--- Tables and Relationships ---\n")
|
||||
if not tables:
|
||||
output.append("(none)")
|
||||
output.append("No tables found.")
|
||||
else:
|
||||
for name, data in sorted(tables.items()):
|
||||
# Columns: inline, comma-separated, with PK marker and shortened types
|
||||
cols = []
|
||||
output.append(f"TABLE: {name}")
|
||||
for col in data['columns']:
|
||||
col_short = shorten_type(col)
|
||||
col_name = col.split(' ')[0]
|
||||
if col_name in data['pks']:
|
||||
col_short += " PK"
|
||||
cols.append(col_short)
|
||||
output.append(f"{name}: {', '.join(cols)}")
|
||||
|
||||
# Foreign keys on one indented line
|
||||
marker = " (PK)" if col_name in data['pks'] else ""
|
||||
output.append(f" - {col}{marker}")
|
||||
|
||||
if data['fks']:
|
||||
output.append(f" FK: {' | '.join(data['fks'])}")
|
||||
|
||||
# Indexes omitted for brevity — query the database for exact index info
|
||||
output.append(" Relationships:")
|
||||
for fk in data['fks']:
|
||||
output.append(f" - {fk}")
|
||||
|
||||
if data['indexes']:
|
||||
output.append(" Indexes:")
|
||||
for idx in data['indexes']:
|
||||
output.append(f" - {idx}")
|
||||
output.append("-" * 20)
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,378 +0,0 @@
|
||||
use sqlx::{Pool, Postgres};
|
||||
use tokio_stream::StreamExt;
|
||||
use windmill_api_client::types::NewScript;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
mod relock_skip {
|
||||
use super::*;
|
||||
|
||||
fn quick_ns(
|
||||
content: &str,
|
||||
language: windmill_api_client::types::ScriptLang,
|
||||
path: &str,
|
||||
lock: Option<String>,
|
||||
parent_hash: Option<String>,
|
||||
) -> NewScript {
|
||||
NewScript {
|
||||
content: content.into(),
|
||||
language,
|
||||
lock,
|
||||
parent_hash,
|
||||
path: path.into(),
|
||||
concurrent_limit: None,
|
||||
concurrency_time_window_s: None,
|
||||
cache_ttl: None,
|
||||
dedicated_worker: None,
|
||||
description: "".to_string(),
|
||||
draft_only: None,
|
||||
envs: vec![],
|
||||
is_template: None,
|
||||
kind: None,
|
||||
summary: "".to_string(),
|
||||
tag: None,
|
||||
schema: std::collections::HashMap::new(),
|
||||
ws_error_handler_muted: Some(false),
|
||||
priority: None,
|
||||
delete_after_use: None,
|
||||
timeout: None,
|
||||
restart_unless_cancelled: None,
|
||||
deployment_message: None,
|
||||
concurrency_key: None,
|
||||
visible_to_runner_only: None,
|
||||
no_main_func: None,
|
||||
codebase: None,
|
||||
has_preprocessor: None,
|
||||
on_behalf_of_email: None,
|
||||
assets: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
async fn init(db: Pool<Postgres>) -> (windmill_api_client::Client, u16, ApiServer) {
|
||||
init_client(db).await
|
||||
}
|
||||
|
||||
/// Counts occurrences of a pattern in job logs for all jobs created after a given time
|
||||
async fn count_pattern_in_job_logs(
|
||||
db: &Pool<Postgres>,
|
||||
pattern: &str,
|
||||
after: chrono::DateTime<chrono::Utc>,
|
||||
) -> i64 {
|
||||
let logs = sqlx::query_scalar!(
|
||||
"SELECT logs FROM job_logs WHERE created_at > $1",
|
||||
after
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
logs.iter()
|
||||
.filter_map(|l| l.as_ref())
|
||||
.map(|l| l.matches(pattern).count() as i64)
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Waits for exactly N jobs to complete. Returns the timestamp before waiting.
|
||||
async fn wait_for_jobs(
|
||||
completed: &mut (impl futures::Stream<Item = uuid::Uuid> + Unpin),
|
||||
count: usize,
|
||||
) -> chrono::DateTime<chrono::Utc> {
|
||||
let before = chrono::Utc::now();
|
||||
for _ in 0..count {
|
||||
completed.next().await;
|
||||
}
|
||||
before
|
||||
}
|
||||
|
||||
/// Waits for at least N jobs to complete, then drains any additional jobs
|
||||
/// that complete within a short timeout. Returns the timestamp before waiting.
|
||||
async fn wait_for_jobs_ge(
|
||||
completed: &mut (impl futures::Stream<Item = uuid::Uuid> + Unpin),
|
||||
min_count: usize,
|
||||
) -> chrono::DateTime<chrono::Utc> {
|
||||
let before = chrono::Utc::now();
|
||||
for _ in 0..min_count {
|
||||
completed.next().await;
|
||||
}
|
||||
// Drain any additional jobs that complete within 5 seconds
|
||||
loop {
|
||||
match tokio::time::timeout(std::time::Duration::from_secs(1), completed.next()).await {
|
||||
Ok(Some(_)) => continue,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
before
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "dependency_map"))]
|
||||
async fn relock_skip_on_script_redeployment(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
std::env::set_var("DEPENDENCY_JOB_DEBOUNCE_DELAY", "0");
|
||||
let (client, port, _s) = init(db.clone()).await;
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
|
||||
in_test_worker(&db, async {
|
||||
// Step 1: Redeploy leaf_1 - first time, no hashes exist, all should relock
|
||||
let before = chrono::Utc::now();
|
||||
client
|
||||
.create_script(
|
||||
"test-workspace",
|
||||
&quick_ns(
|
||||
"
|
||||
def main():
|
||||
return 'leaf1'
|
||||
",
|
||||
windmill_api_client::types::ScriptLang::Python3,
|
||||
"f/rel/leaf_1",
|
||||
None,
|
||||
Some("0000000000051658".into()),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// leaf_1(1) + branch(1) + root_script(2: leaf_1+branch) + root_flow(2) + root_app(2) = 8 jobs
|
||||
wait_for_jobs(&mut completed, 8).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
let relocking_count = count_pattern_in_job_logs(&db, "Relocking", before).await;
|
||||
assert_eq!(skipping_count, 0, "First deployment should not skip");
|
||||
assert!(relocking_count > 0, "First deployment should have relocking jobs");
|
||||
|
||||
// Step 2: Redeploy leaf_2 - first time for leaf_2, should relock
|
||||
let before = chrono::Utc::now();
|
||||
client
|
||||
.create_script(
|
||||
"test-workspace",
|
||||
&quick_ns(
|
||||
"
|
||||
def main():
|
||||
return 'leaf2'
|
||||
",
|
||||
windmill_api_client::types::ScriptLang::Python3,
|
||||
"f/rel/leaf_2",
|
||||
None,
|
||||
Some("0000000000051659".into()),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// leaf_2(1) + root_script(1) + root_flow(1) + root_app(1) = 4 jobs (no cascade, branch doesn't depend on leaf_2)
|
||||
wait_for_jobs(&mut completed, 4).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
let relocking_count = count_pattern_in_job_logs(&db, "Relocking", before).await;
|
||||
assert_eq!(skipping_count, 0, "leaf_2 first deployment should not skip");
|
||||
assert!(relocking_count > 0, "leaf_2 first deployment should have relocking jobs");
|
||||
|
||||
// Step 3: Redeploy leaf_2 with trivial change (comment) - lock stays same, should SKIP
|
||||
// Get current parent hash for leaf_2
|
||||
let leaf2_hash = sqlx::query_scalar!(
|
||||
"SELECT hash FROM script WHERE path = 'f/rel/leaf_2' AND workspace_id = 'test-workspace' AND archived = false ORDER BY created_at DESC LIMIT 1"
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let before = chrono::Utc::now();
|
||||
client
|
||||
.create_script(
|
||||
"test-workspace",
|
||||
&quick_ns(
|
||||
"
|
||||
# comment to change hash but not lock
|
||||
def main():
|
||||
return 'leaf2'
|
||||
",
|
||||
windmill_api_client::types::ScriptLang::Python3,
|
||||
"f/rel/leaf_2",
|
||||
None,
|
||||
Some(format!("{:016X}", leaf2_hash)),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Same as leaf_2 first deployment: 4 jobs
|
||||
wait_for_jobs(&mut completed, 4).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
assert!(skipping_count > 0, "Trivial change (comment only) should skip - lock unchanged");
|
||||
|
||||
// Step 4: Redeploy leaf_2 with actual dependency change (add tiny via comment) - should NOT skip
|
||||
let leaf2_hash = sqlx::query_scalar!(
|
||||
"SELECT hash FROM script WHERE path = 'f/rel/leaf_2' AND workspace_id = 'test-workspace' AND archived = false ORDER BY created_at DESC LIMIT 1"
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let before = chrono::Utc::now();
|
||||
client
|
||||
.create_script(
|
||||
"test-workspace",
|
||||
&quick_ns(
|
||||
"
|
||||
# requirements:
|
||||
# tiny
|
||||
|
||||
def main():
|
||||
return 'leaf2 with tiny'
|
||||
",
|
||||
windmill_api_client::types::ScriptLang::Python3,
|
||||
"f/rel/leaf_2",
|
||||
None,
|
||||
Some(format!("{:016X}", leaf2_hash)),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Same as leaf_2 first deployment: 4 jobs
|
||||
wait_for_jobs(&mut completed, 4).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
assert_eq!(skipping_count, 0, "Changed dependencies should not skip");
|
||||
}, port).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "dependency_map"))]
|
||||
async fn relock_skip_on_workspace_deps_redeployment(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
use windmill_dep_map::workspace_dependencies::NewWorkspaceDependencies;
|
||||
|
||||
std::env::set_var("DEPENDENCY_JOB_DEBOUNCE_DELAY", "0");
|
||||
std::env::set_var("EXISTS_CACHE_TIMEOUT_MS", "0");
|
||||
|
||||
let (_client, port, _s) = init(db.clone()).await;
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
|
||||
// Step 1: Redeploy default (unnamed) workspace deps - first time, should relock
|
||||
let before = chrono::Utc::now();
|
||||
NewWorkspaceDependencies {
|
||||
workspace_id: "test-workspace".into(),
|
||||
language: ScriptLang::Python3,
|
||||
content: "".into(),
|
||||
name: None, // Default/unnamed
|
||||
description: None,
|
||||
}
|
||||
.create(("".to_owned(), "".to_owned(), "".to_owned()), db.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
in_test_worker(&db, wait_for_jobs_ge(&mut completed, 10), port).await;
|
||||
|
||||
// Note: within a cascade, the same script may be triggered multiple times.
|
||||
// After the first trigger relocks and stores the hash, subsequent triggers skip.
|
||||
// We allow up to 3 skips from cascade re-triggers.
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
let relocking_count = count_pattern_in_job_logs(&db, "Relocking", before).await;
|
||||
assert!(skipping_count <= 3, "First deployment should have at most 3 skips from cascade");
|
||||
assert!(relocking_count >= 3, "First deployment should have at least 3 relocking jobs");
|
||||
|
||||
// Step 2: Redeploy default workspace deps again - should SKIP
|
||||
let before = chrono::Utc::now();
|
||||
NewWorkspaceDependencies {
|
||||
workspace_id: "test-workspace".into(),
|
||||
language: ScriptLang::Python3,
|
||||
content: "".into(),
|
||||
name: None,
|
||||
description: None,
|
||||
}
|
||||
.create(("".to_owned(), "".to_owned(), "".to_owned()), db.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
in_test_worker(&db, wait_for_jobs_ge(&mut completed, 10), port).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
assert!(skipping_count >= 3, "Second deployment of same content should skip at least 3 times");
|
||||
|
||||
// Step 3: Redeploy default workspace deps with different content - should NOT skip
|
||||
let before = chrono::Utc::now();
|
||||
NewWorkspaceDependencies {
|
||||
workspace_id: "test-workspace".into(),
|
||||
language: ScriptLang::Python3,
|
||||
content: "tiny".into(),
|
||||
name: None,
|
||||
description: None,
|
||||
}
|
||||
.create(("".to_owned(), "".to_owned(), "".to_owned()), db.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
in_test_worker(&db, wait_for_jobs_ge(&mut completed, 10), port).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
let relocking_count = count_pattern_in_job_logs(&db, "Relocking", before).await;
|
||||
assert!(skipping_count <= 4, "Changed content should have at most 3 skips from cascade");
|
||||
assert!(relocking_count >= 3, "Changed content should trigger at least 3 relocking jobs");
|
||||
|
||||
// Step 4: Deploy named workspace deps first time - should relock (no hash exists yet)
|
||||
// Named deps trigger exactly 3 independent objects with no cascade
|
||||
let before = chrono::Utc::now();
|
||||
NewWorkspaceDependencies {
|
||||
workspace_id: "test-workspace".into(),
|
||||
language: ScriptLang::Python3,
|
||||
content: "".into(),
|
||||
name: Some("test".to_owned()),
|
||||
description: None,
|
||||
}
|
||||
.create(("".to_owned(), "".to_owned(), "".to_owned()), db.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
in_test_worker(&db, wait_for_jobs(&mut completed, 3), port).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
let relocking_count = count_pattern_in_job_logs(&db, "Relocking", before).await;
|
||||
assert_eq!(skipping_count, 0, "Named workspace deps first deployment should not skip");
|
||||
assert!(relocking_count > 0, "Named workspace deps first deployment should relock");
|
||||
|
||||
// Step 5: Deploy named workspace deps again with no change - should SKIP
|
||||
let before = chrono::Utc::now();
|
||||
NewWorkspaceDependencies {
|
||||
workspace_id: "test-workspace".into(),
|
||||
language: ScriptLang::Python3,
|
||||
content: "".into(),
|
||||
name: Some("test".to_owned()),
|
||||
description: None,
|
||||
}
|
||||
.create(("".to_owned(), "".to_owned(), "".to_owned()), db.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
in_test_worker(&db, wait_for_jobs(&mut completed, 3), port).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
let relocking_count = count_pattern_in_job_logs(&db, "Relocking", before).await;
|
||||
|
||||
assert!(skipping_count > 0, "Named workspace deps second deployment should skip");
|
||||
assert_eq!(relocking_count, 0, "Named workspace deps second deployment should not relock");
|
||||
|
||||
// Step 6: Deploy named workspace deps with small change - should NOT skip
|
||||
let before = chrono::Utc::now();
|
||||
NewWorkspaceDependencies {
|
||||
workspace_id: "test-workspace".into(),
|
||||
language: ScriptLang::Python3,
|
||||
content: "tiny".into(),
|
||||
name: Some("test".to_owned()),
|
||||
description: None,
|
||||
}
|
||||
.create(("".to_owned(), "".to_owned(), "".to_owned()), db.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
in_test_worker(&db, wait_for_jobs(&mut completed, 3), port).await;
|
||||
|
||||
let skipping_count = count_pattern_in_job_logs(&db, "Skipping relock", before).await;
|
||||
let relocking_count = count_pattern_in_job_logs(&db, "Relocking", before).await;
|
||||
assert_eq!(skipping_count, 0, "Named workspace deps with change should not skip");
|
||||
assert!(relocking_count > 0, "Named workspace deps with change should relock");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,9 @@ if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Run cargo sqlx prepare with deno_core_mac
|
||||
echo "Running cargo sqlx prepare with deno_core_mac..."
|
||||
cargo sqlx prepare --workspace -- --workspace --all-targets --features all_sqlx_features,private,deno_core_mac,deno_core,enterprise,mcp
|
||||
cargo sqlx prepare --workspace -- --all-targets --features all_sqlx_features,private,deno_core_mac
|
||||
else
|
||||
# Run cargo sqlx prepare
|
||||
echo "Running cargo sqlx prepare..."
|
||||
cargo sqlx prepare --workspace -- --workspace --all-targets --features all_sqlx_features,ee,deno_core,private,enterprise,mcp
|
||||
cargo sqlx prepare --workspace -- --all-targets --features all_sqlx_features,ee
|
||||
fi
|
||||
|
||||
@@ -12,10 +12,7 @@ use windmill_api_auth::{
|
||||
check_scopes, maybe_refresh_folders, require_owner_of_path, ApiAuthed,
|
||||
};
|
||||
use windmill_common::{
|
||||
utils::{BulkDeleteRequest, WithStarredInfoQuery, HTTP_CLIENT},
|
||||
webhook::{WebhookMessage, WebhookShared},
|
||||
workspaces::{check_user_against_rule, ProtectionRuleKind, RuleCheckResult},
|
||||
DB,
|
||||
utils::{BulkDeleteRequest, WithStarredInfoQuery, HTTP_CLIENT}, webhook::{WebhookMessage, WebhookShared}, workspaces::{check_user_against_rule, ProtectionRuleKind, RuleCheckResult}, DB
|
||||
};
|
||||
use windmill_queue::schedule::clear_schedule;
|
||||
|
||||
@@ -862,13 +859,10 @@ async fn create_script_internal<'c>(
|
||||
}
|
||||
};
|
||||
|
||||
let runnable_settings_handle = windmill_common::runnable_settings::insert_rs(
|
||||
RunnableSettings {
|
||||
debouncing_settings: ns.debouncing_settings.insert_cached(&db).await?,
|
||||
concurrency_settings: ns.concurrency_settings.insert_cached(&db).await?,
|
||||
},
|
||||
&db,
|
||||
)
|
||||
let runnable_settings_handle = windmill_common::runnable_settings::insert_rs(RunnableSettings {
|
||||
debouncing_settings: ns.debouncing_settings.insert_cached(&db).await?,
|
||||
concurrency_settings: ns.concurrency_settings.insert_cached(&db).await?,
|
||||
}, &db)
|
||||
.await?;
|
||||
|
||||
let (
|
||||
@@ -1078,9 +1072,7 @@ async fn create_script_internal<'c>(
|
||||
}
|
||||
if needs_lock_gen {
|
||||
let tag = if ns.dedicated_worker.is_some_and(|x| x) {
|
||||
Some(windmill_common::worker::dedicated_worker_tag(
|
||||
&w_id, &ns.path,
|
||||
))
|
||||
Some(format!("{}:{}", &w_id, &ns.path,))
|
||||
} else if ns.tag.as_ref().is_some_and(|x| x.contains("$args[")) {
|
||||
None
|
||||
} else {
|
||||
@@ -1847,9 +1839,7 @@ async fn get_script_by_hash(
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Json(
|
||||
windmill_common::scripts::prefetch_cached_script_with_starred(r, &db).await?,
|
||||
))
|
||||
Ok(Json(windmill_common::scripts::prefetch_cached_script_with_starred(r, &db).await?))
|
||||
}
|
||||
|
||||
async fn raw_script_by_hash(
|
||||
@@ -2052,9 +2042,7 @@ async fn archive_script_by_hash(
|
||||
WebhookMessage::DeleteScript { workspace: w_id, hash: hash.to_string() },
|
||||
);
|
||||
|
||||
Ok(Json(
|
||||
windmill_common::scripts::prefetch_cached_script(script, &db).await?,
|
||||
))
|
||||
Ok(Json(windmill_common::scripts::prefetch_cached_script(script, &db).await?))
|
||||
}
|
||||
|
||||
async fn delete_script_by_hash(
|
||||
@@ -2109,9 +2097,7 @@ async fn delete_script_by_hash(
|
||||
WebhookMessage::DeleteScript { workspace: w_id, hash: hash.to_string() },
|
||||
);
|
||||
|
||||
Ok(Json(
|
||||
windmill_common::scripts::prefetch_cached_script(script, &db).await?,
|
||||
))
|
||||
Ok(Json(windmill_common::scripts::prefetch_cached_script(script, &db).await?))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user