mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 00:01:34 +00:00
Merge branch 'main' into frontdev
This commit is contained in:
@@ -44,6 +44,10 @@ RUN /usr/local/bin/python3 -m pip install pip-tools
|
||||
# Bun
|
||||
COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun
|
||||
|
||||
# Install windmill CLI
|
||||
RUN bun install -g windmill-cli \
|
||||
&& ln -s $(bun pm bin -g)/wmill /usr/bin/wmill
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Deno
|
||||
|
||||
@@ -19,7 +19,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
cargo_test:
|
||||
runs-on: ubicloud-standard-16
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
@@ -70,6 +70,16 @@ jobs:
|
||||
with:
|
||||
ruby-version: "3.3"
|
||||
bundler-cache: false
|
||||
- name: Install windmill CLI from source
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/cli
|
||||
bash gen_wm_client.sh
|
||||
bun install
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
printf '#!/bin/sh\nexec bun run "%s/cli/src/main.ts" "$@"\n' "$GITHUB_WORKSPACE" > "$HOME/.local/bin/wmill"
|
||||
chmod +x "$HOME/.local/bin/wmill"
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
working-directory: /
|
||||
- name: Install PowerShell, mold and clang
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y powershell mold clang libcurl4-openssl-dev
|
||||
@@ -78,6 +88,20 @@ jobs:
|
||||
with:
|
||||
cache: false
|
||||
toolchain: 1.93.0
|
||||
- name: Cache cargo target directory
|
||||
uses: useblacksmith/stickydisk@v1
|
||||
with:
|
||||
key: cargo-target
|
||||
path: ./backend/target
|
||||
- name: Cache cargo registry
|
||||
uses: useblacksmith/cache@v1
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: cargo-registry-${{ hashFiles('backend/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
cargo-registry-
|
||||
- name: Read EE repo commit hash
|
||||
run: |
|
||||
echo "ee_repo_ref=$(cat ./ee-repo-ref.txt)" >> "$GITHUB_ENV"
|
||||
@@ -165,6 +189,12 @@ jobs:
|
||||
fi
|
||||
|
||||
echo "NPM_TOKEN=${NPM_TOKEN}" >> $GITHUB_ENV
|
||||
{
|
||||
echo "TEST_NPMRC<<NPMRC_EOF"
|
||||
echo "@windmill-test:registry=http://localhost:4873/"
|
||||
echo "//localhost:4873/:_authToken=${NPM_TOKEN}"
|
||||
echo "NPMRC_EOF"
|
||||
} >> $GITHUB_ENV
|
||||
echo "Got NPM token successfully: ${NPM_TOKEN:0:10}..."
|
||||
|
||||
# Configure npm globally with the auth token
|
||||
@@ -199,7 +229,7 @@ jobs:
|
||||
fi
|
||||
echo "Verified: Package requires authentication for @windmill-test/private-pkg"
|
||||
- name: Cache DuckDB FFI module build
|
||||
uses: actions/cache@v3
|
||||
uses: useblacksmith/cache@v1
|
||||
with:
|
||||
path: ./backend/windmill-duckdb-ffi-internal/target
|
||||
key: ${{ runner.os }}-duckdb-ffi-${{ hashFiles('./backend/windmill-duckdb-ffi-internal/src/**/*.rs', './backend/windmill-duckdb-ffi-internal/Cargo.toml', './backend/windmill-duckdb-ffi-internal/Cargo.lock') }}
|
||||
@@ -215,6 +245,7 @@ jobs:
|
||||
RUST_LOG_STYLE: never
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
CARGO_BUILD_JOBS: 12
|
||||
CARGO_INCREMENTAL: 1
|
||||
WMDEBUG_FORCE_V0_WORKSPACE_DEPENDENCIES: 1
|
||||
WMDEBUG_FORCE_RUNNABLE_SETTINGS_V0: 1
|
||||
WMDEBUG_FORCE_NO_LEGACY_DEBOUNCING_COMPAT: 1
|
||||
|
||||
@@ -23,16 +23,16 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Generate Windmill client
|
||||
working-directory: cli
|
||||
run: ./gen_wm_client.sh
|
||||
@@ -69,11 +69,6 @@ jobs:
|
||||
cache: true
|
||||
cache-workspaces: backend
|
||||
|
||||
- name: Setup Deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -90,6 +85,10 @@ jobs:
|
||||
- name: Symlink Node to /usr/bin/node
|
||||
run: sudo ln -sf $(which node) /usr/bin/node
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: cli
|
||||
run: bun install
|
||||
|
||||
- name: Generate Windmill clients
|
||||
working-directory: cli
|
||||
run: |
|
||||
@@ -101,12 +100,10 @@ jobs:
|
||||
env:
|
||||
DATABASE_URL: postgres://postgres:changeme@localhost:5432
|
||||
CI_MINIMAL_FEATURES: "true"
|
||||
run: |
|
||||
deno test --no-check --allow-all test/ \
|
||||
--ignore=test/cargo_backend_example.test.ts
|
||||
run: bun test --timeout 120000 test/
|
||||
|
||||
test-windows:
|
||||
runs-on: windows-latest
|
||||
runs-on: blacksmith-16vcpu-windows-2025
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -126,11 +123,6 @@ jobs:
|
||||
cache: true
|
||||
cache-workspaces: backend
|
||||
|
||||
- name: Setup Deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -150,6 +142,10 @@ jobs:
|
||||
echo "BUN_PATH=$bunPath" >> $env:GITHUB_OUTPUT
|
||||
echo "NODE_BIN_PATH=$nodePath" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: cli
|
||||
run: bun install
|
||||
|
||||
- name: Generate Windmill clients
|
||||
working-directory: cli
|
||||
shell: bash
|
||||
@@ -165,9 +161,12 @@ jobs:
|
||||
CI_MINIMAL_FEATURES: "true"
|
||||
BUN_PATH: ${{ steps.runtime-paths.outputs.BUN_PATH }}
|
||||
NODE_BIN_PATH: ${{ steps.runtime-paths.outputs.NODE_BIN_PATH }}
|
||||
run: |
|
||||
deno test --no-check --allow-all test/ `
|
||||
--ignore=test/cargo_backend_example.test.ts
|
||||
run: bun test --timeout 120000 test/
|
||||
|
||||
- name: Keep runner alive for SSH debug
|
||||
if: failure()
|
||||
shell: pwsh
|
||||
run: Start-Sleep -Seconds 3600
|
||||
|
||||
# Combined summary job for branch protection
|
||||
test-summary:
|
||||
|
||||
@@ -25,9 +25,9 @@ jobs:
|
||||
with:
|
||||
node-version: "20.x"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- uses: denoland/setup-deno@v2
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
bun-version: latest
|
||||
- run: cd cli && ./build.sh && cd npm && npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
+20
-4
@@ -12,8 +12,24 @@ window_prefix: "wm-"
|
||||
|
||||
auto_name:
|
||||
model: "claude-sonnet-4.6"
|
||||
system_prompt: "Generate a kebab-case git branch name."
|
||||
background: true # Always run in background when using --auto-name
|
||||
system_prompt: |
|
||||
Generate a concise git branch name based on the task description.
|
||||
|
||||
Rules:
|
||||
- Use kebab-case (lowercase with hyphens)
|
||||
- Keep it short: 1-3 words, max 4 if necessary
|
||||
- Focus on the core task/feature, not implementation details
|
||||
- No prefixes like feat/, fix/, chore/
|
||||
|
||||
Examples of good branch names:
|
||||
- "Add dark mode toggle" → dark-mode
|
||||
- "Fix the search results not showing" → fix-search
|
||||
- "Refactor the authentication module" → auth-refactor
|
||||
- "Add CSV export to reports" → export-csv
|
||||
- "Shell completion is broken" → shell-completion
|
||||
|
||||
Output ONLY the branch name, nothing else.
|
||||
background: true
|
||||
|
||||
|
||||
# Commands to run in new worktree before tmux window opens.
|
||||
@@ -41,9 +57,9 @@ panes:
|
||||
When restarting backend or frontend, make sure to use the ports listed in .env.local.\n
|
||||
Because we are running backend with cargo watch, to verify your changes, just check the logs in the backend pane. No need for cargo check."
|
||||
focus: true
|
||||
- command: "[ -f .env.local ] && source .env.local; cd backend && PORT=${BACKEND_PORT:-8000} cargo watch -x run"
|
||||
- command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/backend" && PORT=${BACKEND_PORT:-8000} cargo watch -x run'
|
||||
split: horizontal
|
||||
- command: "[ -f .env.local ] && source .env.local; cd frontend && npm install && npm run generate-backend-client && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0"
|
||||
- command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/frontend" && npm install && npm run generate-backend-client && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0'
|
||||
split: vertical
|
||||
|
||||
files:
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
## [1.642.0](https://github.com/windmill-labs/windmill/compare/v1.641.0...v1.642.0) (2026-02-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **cli:** add consistent get/list/new subcommands for all item types ([#8047](https://github.com/windmill-labs/windmill/issues/8047)) ([4fedfdf](https://github.com/windmill-labs/windmill/commit/4fedfdfd11aa8ca7fff6f7aed5ae2b313888f878))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make WM_FLOW_PATH available in flow step previews ([#8042](https://github.com/windmill-labs/windmill/issues/8042)) ([a91c532](https://github.com/windmill-labs/windmill/commit/a91c532ecadce63cea965c497351fa1a6f39697a))
|
||||
* preserve debouncing settings for flows with preprocessors ([#8043](https://github.com/windmill-labs/windmill/issues/8043)) ([a00927b](https://github.com/windmill-labs/windmill/commit/a00927b3008a2d953fde1d461723a3c92f375eb4))
|
||||
|
||||
## [1.641.0](https://github.com/windmill-labs/windmill/compare/v1.640.0...v1.641.0) (2026-02-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add .npmrc support for private npm registries ([#8039](https://github.com/windmill-labs/windmill/issues/8039)) ([9eb1531](https://github.com/windmill-labs/windmill/commit/9eb15312f663aa6d700e8ac562d7b5c75c2221f7))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add created_by ownership check to update/delete saved inputs ([#8038](https://github.com/windmill-labs/windmill/issues/8038)) ([e8a13ed](https://github.com/windmill-labs/windmill/commit/e8a13edde7c0ba2ef80344ab7c7288e7bb2eb6b5))
|
||||
* run substitute_ee_code.sh after creating EE worktree ([b330f38](https://github.com/windmill-labs/windmill/commit/b330f388894ecd9cc6b64297420ac6f032d32f72))
|
||||
* tag bunnative dependency jobs as bun instead of nativets ([#8045](https://github.com/windmill-labs/windmill/issues/8045)) ([fd5ebc2](https://github.com/windmill-labs/windmill/commit/fd5ebc2fda589c022074c3bb4dcdb447c7f86cf0))
|
||||
|
||||
## [1.640.0](https://github.com/windmill-labs/windmill/compare/v1.639.0...v1.640.0) (2026-02-20)
|
||||
|
||||
|
||||
|
||||
@@ -258,6 +258,10 @@ COPY --from=denoland/deno:2.2.1 --chmod=755 /usr/bin/deno /usr/bin/deno
|
||||
|
||||
COPY --from=oven/bun:1.3.8 /usr/local/bin/bun /usr/bin/bun
|
||||
|
||||
# Install windmill CLI
|
||||
RUN bun install -g windmill-cli \
|
||||
&& ln -s $(bun pm bin -g)/wmill /usr/bin/wmill
|
||||
|
||||
COPY --from=php:8.3.7-cli /usr/local/bin/php /usr/bin/php
|
||||
COPY --from=composer:2.7.6 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag)\n SELECT unnest($1::uuid[]), 'test-workspace', now(), 'flow'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0681b850c033619e1b9498376263681f875a5aba22170ca50ec8b578f7fa478b"
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n WITH completed AS (\n INSERT INTO v2_job_completed\n (workspace_id, id, started_at, duration_ms, result,\n flow_status, workflow_as_code_status, status, worker)\n SELECT\n q.workspace_id, q.id, q.started_at,\n (EXTRACT('epoch' FROM now()) - EXTRACT('epoch' FROM COALESCE(q.started_at, now()))) * 1000,\n CASE WHEN q.running\n THEN $3::text::jsonb\n ELSE $4::text::jsonb\n END,\n s.flow_status,\n s.workflow_as_code_status,\n 'skipped'::job_status,\n q.worker\n FROM v2_job_queue q\n LEFT JOIN v2_job_status s ON s.id = q.id\n WHERE q.id = $1\n ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status, result = EXCLUDED.result\n RETURNING 1 AS x\n ), _deleted AS (\n DELETE FROM v2_job_queue WHERE id = $1\n ), _logged AS (\n INSERT INTO job_logs (logs, job_id, workspace_id)\n VALUES ($5, $1, $2)\n ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, EXCLUDED.logs)\n )\n SELECT x FROM completed\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "x",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "1437b432d2c23e30eb05443e83069cdb049f65ec299b0778ce14677728cf6346"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT result::text FROM v2_job_completed WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "result",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "18b6262a60400f2b58ab26615466c23b4c1a7805c66b70b0fcfb7d33b122a7bf"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag)\n VALUES ($1, $2, now(), 'flow')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1af6885dbc5055281acb82b3e57f7dba2e4b04d9535058fab695660a14bf8890"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job (id, kind, tag, created_by, permissioned_as, permissioned_as_email, workspace_id, runnable_path)\n VALUES ($1, 'flow', 'flow', 'test-user', 'u/test-user', 'test@windmill.dev', $2, $3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "27f70ebe788cca2e88732d8bf978883037bebca4cf75ba459858e4fb197f940b"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT debounce_batch FROM v2_job_debounce_batch WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "debounce_batch",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2a95f18e80c55a7e8178a4bd2b781d41fa47efd4da5bb9bc2d72b9aa1e33617f"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job (id, kind, tag, created_by, permissioned_as, permissioned_as_email, workspace_id, runnable_path)\n VALUES ($1, 'flow', 'flow', 'test-user', 'u/test-user', 'test@windmill.dev', 'ws2', 'f/test/flow')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4010328a9f1611064f497726b69c08625a55a4dab25c3d9b5ece07e44d14915b"
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO debounce_key (job_id, key)\n VALUES ($1, $2)\n ON CONFLICT (key)\n DO UPDATE SET\n previous_job_id = debounce_key.job_id,\n job_id = EXCLUDED.job_id, -- replace current job with new one \n debounced_times = debounce_key.debounced_times + 1 -- evaluated only if conflict,\n -- conflict means there is already existing value,\n -- which means overriding it will also imply adding new entry to v2_job_debounce_batch and thus debouncing the job\n -- so the counter should be incremented\n RETURNING\n debounced_times,\n first_started_at,\n previous_job_id AS job_id_to_debounce\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "debounced_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "first_started_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "job_id_to_debounce",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "454ace9ce391725ef4f4c129cd66e4c12a5c40f512b70551958178c8b4d6c183"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH ids AS (\n SELECT id as job_id FROM v2_job_debounce_batch WHERE debounce_batch = (\n SELECT debounce_batch FROM v2_job_debounce_batch WHERE id = $1\n )\n ) SELECT args->>'items' FROM ids LEFT JOIN v2_job ON v2_job.id = ids.job_id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "48536968f4173715d4ef8293683c2a3eb4bd22fbe18c34890a3dc4e96e4e6133"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag)\n SELECT unnest($1::uuid[]), 'test-workspace', now(), 'deno'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "539d661500254e2e346490710f5772cb88a1ab6bbddd97a77e06644ac0f61762"
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH job_result AS (\n SELECT result\n FROM v2_job_completed\n WHERE id = $1\n ),\n updated_queue AS (\n UPDATE v2_job_queue\n SET running = false,\n tag = COALESCE($3, tag)\n WHERE id = $2\n )\n UPDATE v2_job\n SET\n tag = COALESCE($3, tag),\n concurrent_limit = COALESCE($4, concurrent_limit),\n concurrency_time_window_s = COALESCE($5, concurrency_time_window_s),\n args = COALESCE(\n CASE\n WHEN job_result.result IS NULL THEN NULL\n WHEN jsonb_typeof(job_result.result) = 'object'\n THEN job_result.result\n WHEN jsonb_typeof(job_result.result) = 'null'\n THEN NULL\n ELSE jsonb_build_object('value', job_result.result)\n END,\n '{}'::jsonb\n ),\n preprocessed = TRUE\n FROM job_result\n WHERE v2_job.id = $2;\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Int4",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5b8c1803f0ccead11517fbc8a9bdc0227dc3922217fa18f0b71ff0484d65838c"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE debounce_key SET first_started_at = now() - interval '20 seconds' WHERE key = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "66342c32f7ae0238803cb1896d9f23a74b64573f77dd32189a25b6e8369f147b"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job (id, kind, tag, created_by, permissioned_as, permissioned_as_email, workspace_id, runnable_path)\n SELECT unnest($1::uuid[]), 'flow', 'flow', 'test-user', 'u/test-user', 'test@windmill.dev', 'test-workspace', 'f/test/flow'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "66faba2137791e0cb1353545c06f9f7c23a1559e7a761db7c2195736b8b30709"
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH job_result AS (\n SELECT result\n FROM v2_job_completed\n WHERE id = $1\n ),\n updated_queue AS (\n UPDATE v2_job_queue\n SET running = false,\n tag = COALESCE($3, tag),\n scheduled_for = COALESCE($6, scheduled_for)\n WHERE id = $2\n )\n UPDATE v2_job\n SET\n tag = COALESCE($3, tag),\n concurrent_limit = COALESCE($4, concurrent_limit),\n concurrency_time_window_s = COALESCE($5, concurrency_time_window_s),\n args = COALESCE(\n CASE\n WHEN job_result.result IS NULL THEN NULL\n WHEN jsonb_typeof(job_result.result) = 'object'\n THEN job_result.result\n WHEN jsonb_typeof(job_result.result) = 'null'\n THEN NULL\n ELSE jsonb_build_object('value', job_result.result)\n END,\n '{}'::jsonb\n ),\n preprocessed = TRUE\n FROM job_result\n WHERE v2_job.id = $2;\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Int4",
|
||||
"Int4",
|
||||
"Timestamptz"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "79b437ad31ddab94310989b8fb6a1c130b9be1ab4b6a100fffffd687677b9c92"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT logs as \"logs!\" FROM job_logs WHERE job_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "logs!",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "7ca599330c9913c7e66b27e2ffcfa18d53cbdd16e179749f0aea7980a901b23c"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag)\n VALUES ($1, $2, now(), 'deno')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7ca7dabfe360845a5b57552b0d02267d5dbbc488bc7ab990c0bda1594bf5ef3a"
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_debounce_batch (id, debounce_batch)\n -- if it the first one, nextval will be evaluated, otherwise take from the job we will debounce\n SELECT\n $2,\n COALESCE(\n (\n SELECT debounce_batch\n FROM v2_job_debounce_batch\n WHERE id = $1\n LIMIT 1\n ), -- maybe use current batch\n nextval('debounce_batch_seq')\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8360ab72d60f07dde6ecae599e6531b5b86862029ab51fdbdd44ec16239108e2"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_settings (workspace_id) VALUES ('ws2')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8cf5af21cde4e4de45f995efa2a9b56ce20c26869ca78d7e17b3504b92ae85b1"
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT job_id, previous_job_id, debounced_times FROM debounce_key WHERE key = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "job_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "previous_job_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "debounced_times",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "8f442110817244aa9533b014aa3d74a6582937dfe4759932b63b8e531984008e"
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n WITH dk AS (\n INSERT INTO debounce_key (job_id, key)\n VALUES ($1, $2)\n ON CONFLICT (key)\n DO UPDATE SET\n previous_job_id = debounce_key.job_id,\n job_id = EXCLUDED.job_id,\n debounced_times = debounce_key.debounced_times + 1\n RETURNING\n debounced_times,\n first_started_at,\n previous_job_id AS job_id_to_debounce\n ), _batch AS (\n INSERT INTO v2_job_debounce_batch (id, debounce_batch)\n SELECT\n $1,\n COALESCE(\n (SELECT debounce_batch FROM v2_job_debounce_batch WHERE id = dk.job_id_to_debounce LIMIT 1),\n nextval('debounce_batch_seq')\n )\n FROM dk\n )\n SELECT debounced_times, first_started_at, job_id_to_debounce FROM dk\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "debounced_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "first_started_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "job_id_to_debounce",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "98033aae3182bde22d5b2ff08ef6e8a4f8f3a9bf04238b33e9caf46836df73d9"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job (id, kind, tag, created_by, permissioned_as, permissioned_as_email, workspace_id)\n VALUES ($1, 'noop', 'deno', 'test-user', 'u/test-user', 'test@windmill.dev', $2)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9f50ec7681a1fcd11cb452c7aba7e8897e49a4b6affa4e9976680336b6bd3115"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace (id, name, owner) VALUES ('ws2', 'Workspace 2', 'test-user')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a057ff9f5998a162ae6de05f6127b7eefc826af7bf1bb89fd758f7c03c881033"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_runtime (id) VALUES ($1)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a68754521bf751450602f04dd4243199a18885e1739a5a0e7f6100eab6f3c803"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_queue SET runnable_settings_handle = $1 WHERE id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "abb56f78aa39c6b6ae8b0ccb7b724c1f80d717e7c9d76b287da63cb5ee8e8b25"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT 1 as x FROM v2_job_completed WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "x",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "adb98040c8039e5cc27fe0579941f723b38d1784bd2f1b16d8724f1f1612dcbf"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_runtime (id) SELECT unnest($1::uuid[])",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT 1 as x FROM v2_job_queue WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "x",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "b795dc228f93c8b9bedb4a3e7467d941819a7202214e0634580bdc2ec30f0b70"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag) VALUES ($1, 'ws2', now(), 'flow')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c1a1ae759ebb84fde3e6d2727991b2a1fcc73e520fee79653bb960dc00c3e2db"
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n WITH dk AS (\n INSERT INTO debounce_key (job_id, key)\n VALUES ($1, $2)\n ON CONFLICT (key)\n DO UPDATE SET\n previous_job_id = debounce_key.job_id,\n job_id = EXCLUDED.job_id,\n debounced_times = debounce_key.debounced_times + 1\n RETURNING\n debounced_times,\n first_started_at,\n previous_job_id AS job_id_to_debounce\n ), _batch AS (\n INSERT INTO v2_job_debounce_batch (id, debounce_batch)\n SELECT\n $1,\n COALESCE(\n (SELECT debounce_batch FROM v2_job_debounce_batch WHERE id = dk.job_id_to_debounce LIMIT 1),\n nextval('debounce_batch_seq')\n )\n FROM dk\n )\n SELECT debounced_times, first_started_at, job_id_to_debounce FROM dk\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "debounced_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "first_started_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "job_id_to_debounce",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "c2347460b73ae9d3167031c263032e97ebefb46be9e58bd3da9067748075311b"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) as \"count!\" FROM v2_job_queue WHERE id = ANY($1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count!",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "c63a1949247f1618f6b6acee9bf6b4d3081dfed2e6ff533dbff0bdfd52687cbb"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job (id, kind, tag, created_by, permissioned_as, permissioned_as_email, workspace_id)\n SELECT unnest($1::uuid[]), 'noop', 'deno', 'test-user', 'u/test-user', 'test@windmill.dev', 'test-workspace'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c6d963e5cefeea728414892df9f28a89f435fc0fb7e55f243b021200f33d2151"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n WITH _ AS (\n UPDATE debounce_key\n SET debounced_times = 0,\n first_started_at = now(),\n previous_job_id = NULL\n WHERE job_id = $1\n )\n UPDATE v2_job_debounce_batch\n SET debounce_batch = nextval('debounce_batch_seq')\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c9530931f670eab1208c4a284a55afdc3fcbb0eb5f98fd63e2ec89442becbfaa"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) as \"count!\" FROM v2_job_completed WHERE id = ANY($1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count!",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "cc309de42a3b630bb83d1b2437633ef0b98ce5e5fba1f5c1dda3ddd874ff3a39"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT debounce_batch FROM v2_job_debounce_batch WHERE id = ANY($1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "debounce_batch",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "ccfed494a8d89eb2c88d72738c341a4dd87701b0636eb9fa001cd1d9cbcd663b"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT debounce_batch FROM v2_job_debounce_batch WHERE id = ANY($1) ORDER BY debounce_batch",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "debounce_batch",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d9400849888dd021b0504b93004ab5e76296ed437c210942363ba06845f9f963"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job (id, kind, tag, created_by, permissioned_as, permissioned_as_email, workspace_id, runnable_path, args)\n VALUES ($1, 'flow', 'flow', 'test-user', 'u/test-user', 'test@windmill.dev', $2, $3, $4)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f8cec94b94098e752f7c71cbe5e9996410a07bacebc37ed21e0bedf1a33a8fdc"
|
||||
}
|
||||
+12
-1
@@ -44,11 +44,22 @@ Windmill uses a workspace-based architecture with multiple crates:
|
||||
## Enterprise Features
|
||||
|
||||
- Enterprise files use the `*_ee.rs` suffix
|
||||
- Enterprise source is in `windmill-ee-private` folder (sibling directory at `../../windmill-ee-private`), symlinked into each crate's `src/`
|
||||
- Enterprise source is in `windmill-ee-private` folder (sibling directory at `../../windmill-ee-private` or `~/windmill-ee-private`), symlinked into each crate's `src/`
|
||||
- The `_ee.rs` files are gitignored in the main repo — they are tracked only in the `windmill-ee-private` repo
|
||||
- You can and should modify `windmill-ee-private` directly when needed (e.g., when creating new crates that need EE code, mirror the package structure there)
|
||||
- Use feature flags: `#[cfg(feature = "enterprise")]`
|
||||
- Isolate enterprise code in separate modules
|
||||
|
||||
### EE PR Workflow (MUST DO when modifying `*_ee.rs` files)
|
||||
|
||||
When you modify any `*_ee.rs` file and create a PR on the windmill repo, you **MUST** also:
|
||||
|
||||
1. **Create a matching branch** in the `windmill-ee-private` repo (use the same branch name). If using worktrees, the EE worktree is at `~/windmill-ee-private__worktrees/<branch-name>/`
|
||||
2. **Commit and push** the `_ee.rs` changes in that branch
|
||||
3. **Create a PR** on `windmill-ee-private` with a link to the companion windmill PR
|
||||
4. **Update `ee-repo-ref.txt`**: Run `bash write_latest_ee_ref.sh` from `backend/` to write the latest EE commit hash. **Important**: the script may fall back to `~/windmill-ee-private` (main branch) instead of the worktree — verify it wrote the correct commit hash from your branch, not from main. If wrong, manually write the correct hash.
|
||||
5. **Commit `ee-repo-ref.txt`** in the windmill repo so CI picks up the correct EE ref
|
||||
|
||||
## Code Validation (MUST DO)
|
||||
|
||||
After making backend changes, you MUST run `cargo check` and fix all errors and warnings before considering the work done.
|
||||
|
||||
Generated
+71
-70
@@ -15725,7 +15725,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -15789,7 +15789,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-alerting"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15802,7 +15802,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -15940,7 +15940,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-agent-workers"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15963,7 +15963,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-assets"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15976,7 +15976,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-auth"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16002,7 +16002,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
@@ -16012,7 +16012,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-configs"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16029,7 +16029,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-debug"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"base64 0.22.1",
|
||||
@@ -16052,7 +16052,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-embeddings"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16075,7 +16075,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flow-conversations"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16091,7 +16091,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flows"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16111,7 +16111,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-groups"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16131,7 +16131,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-inputs"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16145,7 +16145,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-integration-tests"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -16171,7 +16171,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-jobs"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16196,10 +16196,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-npm-proxy"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"flate2",
|
||||
"reqwest 0.13.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
@@ -16212,7 +16213,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-openapi"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16233,7 +16234,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-schedule"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16253,7 +16254,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-scripts"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16283,7 +16284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-settings"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16310,7 +16311,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-sse"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"serde",
|
||||
@@ -16322,7 +16323,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-users"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum 0.7.9",
|
||||
@@ -16345,7 +16346,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workers"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16359,7 +16360,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workspaces"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16389,7 +16390,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
@@ -16403,7 +16404,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-autoscaling"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16422,7 +16423,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
@@ -16521,7 +16522,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-dep-map"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"itertools 0.14.0",
|
||||
@@ -16540,7 +16541,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -16555,7 +16556,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"astral-tokio-tar",
|
||||
@@ -16579,7 +16580,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-jseval"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -16596,7 +16597,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"lazy_static",
|
||||
@@ -16612,7 +16613,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-mcp"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16633,7 +16634,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-native-triggers"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16664,7 +16665,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-oauth"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-oauth2",
|
||||
@@ -16688,7 +16689,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-object-store"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -16722,7 +16723,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-operator"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -16740,7 +16741,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -16749,7 +16750,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16761,7 +16762,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-csharp"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16773,7 +16774,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -16785,7 +16786,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16797,7 +16798,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-java"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16809,7 +16810,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-nu"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"nu-parser",
|
||||
@@ -16820,7 +16821,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16831,7 +16832,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16844,7 +16845,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16868,7 +16869,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ruby"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16882,7 +16883,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -16899,7 +16900,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16914,7 +16915,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16933,7 +16934,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -16944,7 +16945,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16981,7 +16982,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-runtime-nativets"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"const_format",
|
||||
@@ -17019,7 +17020,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
@@ -17029,7 +17030,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-store"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -17058,7 +17059,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-test-utils"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -17081,7 +17082,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17114,7 +17115,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-email"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17134,7 +17135,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-gcp"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17168,7 +17169,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-http"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17203,7 +17204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-kafka"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17226,7 +17227,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-mqtt"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17250,7 +17251,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-nats"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -17274,7 +17275,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-postgres"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17309,7 +17310,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-sqs"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17337,7 +17338,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-websocket"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17360,7 +17361,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-types"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.4",
|
||||
@@ -17378,7 +17379,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-once-cell",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -76,7 +76,7 @@ members = [
|
||||
exclude = ["./windmill-duckdb-ffi-internal"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.640.0"
|
||||
version = "1.642.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
5f8105b808f3f0186fdf5132d2ee602d8a14aa17
|
||||
0fede4b1086bc1456be9cc55b203228c979c5c5e
|
||||
|
||||
@@ -54,7 +54,7 @@ use windmill_common::{
|
||||
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,
|
||||
NPMRC_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,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
@@ -89,9 +89,9 @@ use windmill_worker::{
|
||||
result_processor::handle_job_error, JobCompletedSender, JobIsolationLevel,
|
||||
OtelTracingProxySettings, SameWorkerSender, BUNFIG_INSTALL_SCOPES, CARGO_REGISTRIES,
|
||||
INSTANCE_PYTHON_VERSION, JAVA_HOME_DIR, JOB_DEFAULT_TIMEOUT, JOB_ISOLATION, KEEP_JOB_DIR,
|
||||
MAVEN_REPOS, MAVEN_SETTINGS_XML, NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NSJAIL_AVAILABLE,
|
||||
NUGET_CONFIG, OTEL_TRACING_PROXY_SETTINGS, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL,
|
||||
POWERSHELL_REPO_PAT, POWERSHELL_REPO_URL, UV_INDEX_STRATEGY,
|
||||
MAVEN_REPOS, MAVEN_SETTINGS_XML, NO_DEFAULT_MAVEN, NPMRC, NPM_CONFIG_REGISTRY,
|
||||
NSJAIL_AVAILABLE, NUGET_CONFIG, OTEL_TRACING_PROXY_SETTINGS, PIP_EXTRA_INDEX_URL,
|
||||
PIP_INDEX_URL, POWERSHELL_REPO_PAT, POWERSHELL_REPO_URL, UV_INDEX_STRATEGY,
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -330,6 +330,7 @@ pub async fn initial_load(
|
||||
reload_uv_index_strategy_setting(&conn).await;
|
||||
reload_npm_config_registry_setting(&conn).await;
|
||||
reload_bunfig_install_scopes_setting(&conn).await;
|
||||
reload_npmrc_setting(&conn).await;
|
||||
reload_instance_python_version_setting(&conn).await;
|
||||
reload_nuget_config_setting(&conn).await;
|
||||
reload_powershell_repo_url_setting(&conn).await;
|
||||
@@ -1306,6 +1307,10 @@ pub async fn reload_bunfig_install_scopes_setting(conn: &Connection) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reload_npmrc_setting(conn: &Connection) {
|
||||
reload_option_setting_with_tracing(conn, NPMRC_SETTING, "NPMRC", NPMRC.clone()).await;
|
||||
}
|
||||
|
||||
pub async fn reload_nuget_config_setting(conn: &Connection) {
|
||||
reload_option_setting_with_tracing(
|
||||
conn,
|
||||
|
||||
Executable
+474
@@ -0,0 +1,474 @@
|
||||
#!/usr/bin/env bash
|
||||
# End-to-end debounce tests against the running backend API
|
||||
# Usage: BACKEND_PORT=8030 ./test_debounce_e2e.sh
|
||||
set -uo pipefail
|
||||
|
||||
BASE="http://localhost:${BACKEND_PORT:-8030}/api"
|
||||
W="admins"
|
||||
EMAIL="admin@windmill.dev"
|
||||
PASSWORD="changeme"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
|
||||
log_pass() { echo -e "${GREEN}PASS${NC}: $1"; ((pass++)) || true; }
|
||||
log_fail() { echo -e "${RED}FAIL${NC}: $1 — $2"; ((fail++)) || true; }
|
||||
log_info() { echo -e "${YELLOW}INFO${NC}: $1"; }
|
||||
|
||||
# Unique suffix for idempotent re-runs
|
||||
TS=$(date +%s)
|
||||
|
||||
# --- Auth ---
|
||||
log_info "Logging in..."
|
||||
TOKEN=$(curl -s "$BASE/auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\"}")
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "Failed to login"; exit 1
|
||||
fi
|
||||
|
||||
AUTH="Authorization: Bearer $TOKEN"
|
||||
log_info "Logged in"
|
||||
|
||||
# --- Helpers ---
|
||||
api() {
|
||||
# Usage: api METHOD path [data]
|
||||
local method="$1" path="$2" data="${3:-}"
|
||||
if [ -n "$data" ]; then
|
||||
curl -s "$BASE/w/$W/$path" -X "$method" -H "$AUTH" -H 'Content-Type: application/json' -d "$data"
|
||||
else
|
||||
curl -s "$BASE/w/$W/$path" -X "$method" -H "$AUTH"
|
||||
fi
|
||||
}
|
||||
|
||||
wait_job() {
|
||||
local job_id="$1" max_wait="${2:-30}"
|
||||
for _ in $(seq 1 "$max_wait"); do
|
||||
local r
|
||||
r=$(api GET "jobs/completed/get_result_maybe/$job_id")
|
||||
if echo "$r" | jq -e '.completed == true' > /dev/null 2>&1; then
|
||||
echo "$r"; return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo '{"completed":false,"error":"timeout"}'; return 1
|
||||
}
|
||||
|
||||
BUN_EMPTY_LOCK=$'{"dependencies": {}}\n//bun.lock\n'
|
||||
|
||||
create_script() {
|
||||
# Usage: create_script path language content [extra_json_fields]
|
||||
# Note: lock must be non-empty; empty string ("") is treated as None by the backend
|
||||
# (scripts.rs:798-800), which triggers dependency resolution instead of direct deployment.
|
||||
# For bun scripts, the lock must contain "//bun.lock" as a split pattern.
|
||||
local path="$1" lang="$2" content="$3" extra="${4:-}"
|
||||
local json
|
||||
json=$(jq -n \
|
||||
--arg path "$path" \
|
||||
--arg lang "$lang" \
|
||||
--arg content "$content" \
|
||||
--arg summary "test" \
|
||||
--arg desc "test" \
|
||||
--arg lock "$BUN_EMPTY_LOCK" \
|
||||
'{path: $path, language: $lang, content: $content, summary: $summary, description: $desc, lock: $lock}')
|
||||
if [ -n "$extra" ]; then
|
||||
json=$(echo "$json" | jq ". + $extra")
|
||||
fi
|
||||
local hash
|
||||
hash=$(api POST "scripts/create" "$json")
|
||||
# Small delay for DB visibility after tx commit
|
||||
sleep 0.2
|
||||
echo "$hash"
|
||||
}
|
||||
|
||||
run_script() {
|
||||
# Usage: run_script path args_json
|
||||
api POST "jobs/run/p/$1" "$2"
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# TEST 1: Deploy a script and run it 5 times in close succession
|
||||
###############################################################################
|
||||
echo ""
|
||||
log_info "=== TEST 1: Deploy & run script 5 times rapidly ==="
|
||||
|
||||
P1="u/admin/e2e_simple_$TS"
|
||||
H1=$(create_script "$P1" "bun" 'export function main(x: number = 0) { return { result: x * 2 }; }')
|
||||
|
||||
if echo "$H1" | grep -qE '^[0-9a-f]{16}$'; then
|
||||
log_pass "Script created: $H1"
|
||||
else
|
||||
log_fail "Script creation" "$H1"
|
||||
fi
|
||||
|
||||
log_info "Running 5 times rapidly..."
|
||||
JOB_IDS=()
|
||||
for i in $(seq 1 5); do
|
||||
JID=$(run_script "$P1" "{\"x\": $i}")
|
||||
JOB_IDS+=("$JID")
|
||||
done
|
||||
log_info "Jobs: ${JOB_IDS[*]}"
|
||||
|
||||
log_info "Waiting for completion..."
|
||||
all_ok=true
|
||||
for i in "${!JOB_IDS[@]}"; do
|
||||
JID="${JOB_IDS[$i]}"
|
||||
R=$(wait_job "$JID" 30)
|
||||
success=$(echo "$R" | jq -r '.success // false')
|
||||
value=$(echo "$R" | jq -r '.result.result // "null"')
|
||||
expected=$(( (i + 1) * 2 ))
|
||||
if [ "$success" = "true" ] && [ "$value" = "$expected" ]; then
|
||||
log_pass "Job $((i+1)): x=$((i+1)) → $value (correct)"
|
||||
else
|
||||
log_fail "Job $((i+1))" "success=$success value=$value expected=$expected"
|
||||
all_ok=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$all_ok" = "true" ]; then
|
||||
log_pass "All 5 runs completed correctly (no debounce — different args)"
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# TEST 2: Redeploy script WITHOUT lock in close succession
|
||||
###############################################################################
|
||||
echo ""
|
||||
log_info "=== TEST 2: Redeploy without lock in rapid succession ==="
|
||||
|
||||
P2="u/admin/e2e_nolock_$TS"
|
||||
|
||||
# Deploy 5 versions of the same script without lock → triggers dependency jobs
|
||||
DEPLOY_HASHES=()
|
||||
for i in $(seq 1 5); do
|
||||
content="export function main(x: number = 0) { return { result: x * $i, version: $i }; }"
|
||||
parent_extra=""
|
||||
if [ "${#DEPLOY_HASHES[@]}" -gt 0 ]; then
|
||||
last_hash="${DEPLOY_HASHES[-1]}"
|
||||
parent_extra="{\"parent_hash\": \"$last_hash\"}"
|
||||
fi
|
||||
|
||||
# Deploy without lock (omit lock field entirely)
|
||||
json=$(jq -n \
|
||||
--arg path "$P2" \
|
||||
--arg content "$content" \
|
||||
--arg summary "v$i" \
|
||||
--arg desc "test" \
|
||||
'{path: $path, language: "bun", content: $content, summary: $summary, description: $desc}')
|
||||
if [ -n "$parent_extra" ]; then
|
||||
json=$(echo "$json" | jq ". + $parent_extra")
|
||||
fi
|
||||
|
||||
hash=$(api POST "scripts/create" "$json")
|
||||
if echo "$hash" | grep -qE '^[0-9a-f]{16}$'; then
|
||||
DEPLOY_HASHES+=("$hash")
|
||||
log_info "Deploy $i: $hash"
|
||||
else
|
||||
log_fail "Deploy $i" "$hash"
|
||||
# If path conflict, the script already exists from a previous version
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# Wait for dependency resolution
|
||||
log_info "Waiting 15s for dependency jobs..."
|
||||
sleep 15
|
||||
|
||||
# Check the latest script — should have lock resolved
|
||||
SCRIPT_INFO=$(api GET "scripts/get/p/$P2")
|
||||
LOCK=$(echo "$SCRIPT_INFO" | jq -r '.lock // "null"')
|
||||
if [ "$LOCK" != "null" ] && [ -n "$LOCK" ]; then
|
||||
log_pass "Latest version has lock resolved"
|
||||
else
|
||||
log_info "Lock not yet resolved: $LOCK"
|
||||
fi
|
||||
|
||||
# Run the latest version to verify it works
|
||||
sleep 0.5
|
||||
JID2=$(run_script "$P2" '{"x": 10}')
|
||||
if echo "$JID2" | grep -qE '^[0-9a-f-]{36}$'; then
|
||||
R2=$(wait_job "$JID2" 30)
|
||||
success=$(echo "$R2" | jq -r '.success // false')
|
||||
if [ "$success" = "true" ]; then
|
||||
version=$(echo "$R2" | jq -r '.result.version // "?"')
|
||||
log_pass "Latest version runs: version=$version"
|
||||
else
|
||||
err=$(echo "$R2" | jq -r '.result.error.message // "unknown"' 2>/dev/null)
|
||||
log_fail "Run latest version" "success=false err=$err"
|
||||
fi
|
||||
else
|
||||
log_fail "Run latest version" "bad job id: $JID2"
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# TEST 3: Script with debounce_delay_s — rapid runs with SAME args
|
||||
###############################################################################
|
||||
echo ""
|
||||
log_info "=== TEST 3: Debounce with same args (should debounce) ==="
|
||||
|
||||
P3="u/admin/e2e_debounce_$TS"
|
||||
H3=$(create_script "$P3" "bun" \
|
||||
'export function main(x: number = 0) { return { result: x }; }' \
|
||||
'{"debounce_delay_s": 3}')
|
||||
|
||||
if echo "$H3" | grep -qE '^[0-9a-f]{16}$'; then
|
||||
log_pass "Debounce script created: $H3"
|
||||
else
|
||||
log_fail "Debounce script creation" "$H3"
|
||||
fi
|
||||
|
||||
log_info "Running 5 times with same args {x: 42}..."
|
||||
DEB_IDS=()
|
||||
for i in $(seq 1 5); do
|
||||
JID=$(run_script "$P3" '{"x": 42}')
|
||||
DEB_IDS+=("$JID")
|
||||
log_info " Run $i: $JID"
|
||||
done
|
||||
|
||||
log_info "Waiting 10s for debounce delay (3s) + execution..."
|
||||
sleep 10
|
||||
|
||||
executed=0
|
||||
skipped=0
|
||||
for JID in "${DEB_IDS[@]}"; do
|
||||
if ! echo "$JID" | grep -qE '^[0-9a-f-]{36}$'; then
|
||||
log_info " Invalid job id: $JID"
|
||||
continue
|
||||
fi
|
||||
R=$(wait_job "$JID" 5 2>/dev/null || echo '{"completed":false}')
|
||||
completed=$(echo "$R" | jq -r '.completed // false')
|
||||
success=$(echo "$R" | jq -r '.success // false')
|
||||
if [ "$completed" = "true" ] && [ "$success" = "true" ]; then
|
||||
((executed++)) || true
|
||||
elif [ "$completed" = "true" ]; then
|
||||
((skipped++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Results: $executed executed, $skipped skipped out of ${#DEB_IDS[@]}"
|
||||
if [ "$executed" -eq 1 ] && [ "$skipped" -ge 3 ]; then
|
||||
log_pass "Debouncing perfect: 1 executed, $skipped skipped"
|
||||
elif [ "$executed" -le 2 ] && [ "$skipped" -ge 2 ]; then
|
||||
log_pass "Debouncing working: $executed executed, $skipped skipped"
|
||||
else
|
||||
log_fail "Debounce same args" "executed=$executed skipped=$skipped (want ~1 exec, ~4 skip)"
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# TEST 3b: Different args should NOT debounce against each other
|
||||
###############################################################################
|
||||
echo ""
|
||||
log_info "=== TEST 3b: Debounce with different args (should NOT debounce) ==="
|
||||
|
||||
DIFF_IDS=()
|
||||
for i in $(seq 1 3); do
|
||||
JID=$(run_script "$P3" "{\"x\": $((i * 100))}")
|
||||
DIFF_IDS+=("$JID")
|
||||
done
|
||||
|
||||
log_info "Waiting 8s..."
|
||||
sleep 8
|
||||
|
||||
diff_exec=0
|
||||
for JID in "${DIFF_IDS[@]}"; do
|
||||
if ! echo "$JID" | grep -qE '^[0-9a-f-]{36}$'; then continue; fi
|
||||
R=$(wait_job "$JID" 5 2>/dev/null || echo '{"completed":false}')
|
||||
success=$(echo "$R" | jq -r '.success // false')
|
||||
if [ "$success" = "true" ]; then ((diff_exec++)) || true; fi
|
||||
done
|
||||
|
||||
if [ "$diff_exec" -eq 3 ]; then
|
||||
log_pass "Different args: all 3 executed independently"
|
||||
else
|
||||
log_fail "Different args" "only $diff_exec/3 executed"
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# TEST 4: Custom debounce_key with $args interpolation
|
||||
###############################################################################
|
||||
echo ""
|
||||
log_info "=== TEST 4: Custom debounce key ==="
|
||||
|
||||
P4="u/admin/e2e_custom_key_$TS"
|
||||
H4=$(create_script "$P4" "bun" \
|
||||
'export function main(event_id: string = "", data: string = "") { return { event_id, data }; }' \
|
||||
'{"debounce_delay_s": 3, "debounce_key": "event#$args.event_id"}')
|
||||
|
||||
if echo "$H4" | grep -qE '^[0-9a-f]{16}$'; then
|
||||
log_pass "Custom key script created: $H4"
|
||||
else
|
||||
log_fail "Custom key script creation" "$H4"
|
||||
fi
|
||||
|
||||
# Same event_id → should debounce
|
||||
log_info "3 runs with same event_id..."
|
||||
SAME_IDS=()
|
||||
for i in $(seq 1 3); do
|
||||
JID=$(run_script "$P4" "{\"event_id\": \"evt_001\", \"data\": \"payload_$i\"}")
|
||||
SAME_IDS+=("$JID")
|
||||
done
|
||||
|
||||
# Different event_id → should NOT debounce
|
||||
JID_DIFF=$(run_script "$P4" '{"event_id": "evt_002", "data": "different"}')
|
||||
|
||||
log_info "Waiting 8s..."
|
||||
sleep 8
|
||||
|
||||
same_exec=0
|
||||
same_skip=0
|
||||
for JID in "${SAME_IDS[@]}"; do
|
||||
if ! echo "$JID" | grep -qE '^[0-9a-f-]{36}$'; then continue; fi
|
||||
R=$(wait_job "$JID" 5 2>/dev/null || echo '{"completed":false}')
|
||||
completed=$(echo "$R" | jq -r '.completed // false')
|
||||
success=$(echo "$R" | jq -r '.success // false')
|
||||
if [ "$completed" = "true" ] && [ "$success" = "true" ]; then
|
||||
data=$(echo "$R" | jq -r '.result.data // "?"')
|
||||
((same_exec++)) || true
|
||||
log_info " Executed: data=$data"
|
||||
elif [ "$completed" = "true" ]; then
|
||||
((same_skip++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Same event_id: $same_exec executed, $same_skip skipped"
|
||||
if [ "$same_exec" -eq 1 ] && [ "$same_skip" -ge 1 ]; then
|
||||
log_pass "Custom key debounce: same event_id debounced correctly"
|
||||
elif [ "$same_exec" -le 2 ]; then
|
||||
log_pass "Custom key debounce working: $same_exec executed, $same_skip skipped"
|
||||
else
|
||||
log_fail "Custom key debounce" "exec=$same_exec skip=$same_skip"
|
||||
fi
|
||||
|
||||
# Check different event_id ran independently
|
||||
if echo "$JID_DIFF" | grep -qE '^[0-9a-f-]{36}$'; then
|
||||
R_DIFF=$(wait_job "$JID_DIFF" 10 2>/dev/null || echo '{"completed":false}')
|
||||
diff_success=$(echo "$R_DIFF" | jq -r '.success // false')
|
||||
if [ "$diff_success" = "true" ]; then
|
||||
log_pass "Different event_id: executed independently"
|
||||
else
|
||||
log_info "Different event_id: success=$diff_success"
|
||||
fi
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# TEST 5: Git sync with bad target — debounced deployment callbacks
|
||||
###############################################################################
|
||||
echo ""
|
||||
log_info "=== TEST 5: Git sync debounce + aggregation ==="
|
||||
|
||||
# Create git repo resource
|
||||
api POST "resources/create?update_if_exists=true" '{
|
||||
"path": "u/admin/e2e_bad_git_repo",
|
||||
"description": "Bad git repo for testing",
|
||||
"resource_type": "git_repository",
|
||||
"value": {"url": "https://github.com/nonexistent/nope.git", "branch": "main", "token": "bad"}
|
||||
}' > /dev/null 2>&1
|
||||
log_info "Created git repo resource"
|
||||
|
||||
# Create a sync script at a folder path where the 2nd segment is a number >= 28103.
|
||||
# is_script_meets_min_version parses split("/").skip(1).next() as the version number.
|
||||
# This enables debounce_delay_s=5 and debounce_args_to_accumulate=["items"].
|
||||
api POST "folders/create" '{"name": "28103"}' > /dev/null 2>&1
|
||||
P5="f/28103/e2e_sync_$TS"
|
||||
H5=$(create_script "$P5" "bun" \
|
||||
'export function main(repo_url_resource_path: string = "", workspace_id: string = "", items: any[] = [], use_individual_branch: boolean = false, group_by_folder: boolean = false, parent_workspace_id: string = "") { return { synced: items.length, items }; }')
|
||||
|
||||
if echo "$H5" | grep -qE '^[0-9a-f]{16}$'; then
|
||||
log_pass "Sync script created: $H5"
|
||||
else
|
||||
log_fail "Sync script creation" "$H5"
|
||||
fi
|
||||
|
||||
# Configure git sync with include_path to match deployed scripts.
|
||||
# Without include_path, path_matches_filters returns false and no DeploymentCallback is created.
|
||||
api POST "workspaces/edit_git_sync_config" "{
|
||||
\"git_sync_settings\": {
|
||||
\"include_type\": [\"script\"],
|
||||
\"include_path\": [\"**\"],
|
||||
\"repositories\": [{
|
||||
\"script_path\": \"$P5\",
|
||||
\"git_repo_resource_path\": \"\$res:u/admin/e2e_bad_git_repo\",
|
||||
\"use_individual_branch\": false,
|
||||
\"group_by_folder\": false
|
||||
}]
|
||||
}
|
||||
}" > /dev/null 2>&1
|
||||
log_pass "Git sync configured with include_path and versioned folder script path"
|
||||
|
||||
# Deploy 5 scripts rapidly to trigger git sync.
|
||||
# Scripts are created with lock="" (via create_script), so handle_deployment_metadata
|
||||
# fires immediately after tx commit (not after dependency resolution).
|
||||
log_info "Deploying 5 scripts to trigger git sync..."
|
||||
for i in $(seq 1 5); do
|
||||
dp="u/admin/e2e_gitsync_${TS}_$i"
|
||||
create_script "$dp" "bun" "export function main() { return { v: $i }; }" > /dev/null
|
||||
log_info " Deployed $dp"
|
||||
done
|
||||
|
||||
# Wait for debounce delay (5s) + execution
|
||||
log_info "Waiting 15s for debounce (5s) + execution..."
|
||||
sleep 15
|
||||
|
||||
# Check deployment callback jobs for our sync script.
|
||||
# Debounced jobs have is_skipped=true (but success=true), so we use is_skipped to distinguish.
|
||||
SYNC_JOBS=$(api GET "jobs/completed/list?script_path_exact=$P5&job_kinds=deploymentcallback")
|
||||
SYNC_TOTAL=$(echo "$SYNC_JOBS" | jq 'length')
|
||||
SYNC_EXECUTED=$(echo "$SYNC_JOBS" | jq '[.[] | select(.is_skipped != true)] | length')
|
||||
SYNC_SKIPPED=$(echo "$SYNC_JOBS" | jq '[.[] | select(.is_skipped == true)] | length')
|
||||
|
||||
log_info "Sync jobs: total=$SYNC_TOTAL executed=$SYNC_EXECUTED skipped=$SYNC_SKIPPED"
|
||||
|
||||
if [ "$SYNC_TOTAL" -gt 0 ]; then
|
||||
# With debouncing (5s delay), rapid deploys should be consolidated.
|
||||
# All 5 jobs are created but most should be skipped (debounced).
|
||||
if [ "$SYNC_SKIPPED" -gt 0 ]; then
|
||||
log_pass "Git sync debouncing: $SYNC_EXECUTED executed, $SYNC_SKIPPED debounced out of $SYNC_TOTAL"
|
||||
else
|
||||
log_fail "Git sync debouncing" "No jobs were debounced ($SYNC_TOTAL all executed independently)"
|
||||
fi
|
||||
|
||||
# Check if items were aggregated in the executed (non-skipped) job(s)
|
||||
for idx in $(seq 0 $((SYNC_TOTAL - 1))); do
|
||||
is_skipped=$(echo "$SYNC_JOBS" | jq -r ".[$idx].is_skipped")
|
||||
[ "$is_skipped" = "true" ] && continue
|
||||
jid=$(echo "$SYNC_JOBS" | jq -r ".[$idx].id")
|
||||
r=$(api GET "jobs/completed/get_result/$jid")
|
||||
items_count=$(echo "$r" | jq '.items | length // 0')
|
||||
log_info " Executed sync job $jid: items=$items_count"
|
||||
if [ "$items_count" -gt 1 ]; then
|
||||
log_pass "Items aggregated: $items_count items in single sync job"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Check queued — jobs may still be pending debounce delay
|
||||
Q=$(api GET "jobs/queue/list?script_path_exact=$P5&job_kinds=deploymentcallback")
|
||||
QC=$(echo "$Q" | jq 'length')
|
||||
log_info "No completed sync jobs. $QC queued."
|
||||
if [ "$QC" -gt 0 ] && [ "$QC" -lt 5 ]; then
|
||||
log_pass "Git sync debouncing (queued): $QC jobs for 5 deploys"
|
||||
elif [ "$QC" -eq 0 ]; then
|
||||
log_fail "Git sync" "No deployment callback jobs found (completed or queued)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Cleanup git sync
|
||||
api POST "workspaces/edit_git_sync_config" '{"git_sync_settings": null}' > /dev/null 2>&1
|
||||
log_info "Git sync config cleared"
|
||||
|
||||
###############################################################################
|
||||
# Summary
|
||||
###############################################################################
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo -e "Results: ${GREEN}$pass passed${NC}, ${RED}$fail failed${NC}"
|
||||
echo "========================================="
|
||||
|
||||
if [ "$fail" -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
+155
-62
@@ -1,8 +1,8 @@
|
||||
use windmill_test_utils::*;
|
||||
use sqlx::postgres::Postgres;
|
||||
use sqlx::Pool;
|
||||
use windmill_common::jobs::{JobPayload, RawCode};
|
||||
use windmill_common::scripts::ScriptLang;
|
||||
use windmill_test_utils::*;
|
||||
|
||||
// ============================================================================
|
||||
// Basic Execution Tests
|
||||
@@ -27,8 +27,8 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -63,8 +63,8 @@ export function main(name: string, count: number) {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -104,8 +104,9 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default(
|
||||
)
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -135,8 +136,9 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default(
|
||||
)
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -167,8 +169,9 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default(
|
||||
)
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -207,8 +210,8 @@ export async function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -245,8 +248,9 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default(
|
||||
)
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -276,8 +280,9 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default(
|
||||
)
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -318,8 +323,8 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -358,8 +363,8 @@ export function notMain() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -398,8 +403,8 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -437,8 +442,8 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -474,8 +479,8 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -516,8 +521,8 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -613,8 +618,9 @@ export function main() {
|
||||
path: Some("f/nested/test_deep".to_string()),
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default(
|
||||
)
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -647,8 +653,9 @@ export function main() {
|
||||
path: Some("f/nested/test_deep_relative".to_string()),
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default(
|
||||
)
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -693,8 +700,8 @@ export function main() {
|
||||
path: Some("f/circular/test_both".to_string()),
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -741,8 +748,8 @@ export function main(x: number) {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -791,8 +798,8 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -836,8 +843,8 @@ export function main() {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -859,11 +866,11 @@ export function main() {
|
||||
// ============================================================================
|
||||
|
||||
mod dedicated_worker_protocol {
|
||||
use windmill_test_utils::{parse_dedicated_worker_line, DedicatedWorkerResult};
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::process::{Command, Stdio};
|
||||
use windmill_test_utils::{parse_dedicated_worker_line, DedicatedWorkerResult};
|
||||
use windmill_worker::{
|
||||
build_loader, generate_dedicated_worker_wrapper, BUN_DEDICATED_WORKER_ARGS, LoaderMode,
|
||||
build_loader, generate_dedicated_worker_wrapper, LoaderMode, BUN_DEDICATED_WORKER_ARGS,
|
||||
BUN_PATH, NODE_BIN_PATH,
|
||||
};
|
||||
|
||||
@@ -934,12 +941,8 @@ mod dedicated_worker_protocol {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
|
||||
// Create files and get the wrapper path (bundled for node, raw for bun)
|
||||
let wrapper_path = create_test_worker_files(
|
||||
temp_dir.path(),
|
||||
script,
|
||||
arg_names,
|
||||
runtime == "node",
|
||||
);
|
||||
let wrapper_path =
|
||||
create_test_worker_files(temp_dir.path(), script, arg_names, runtime == "node");
|
||||
let wrapper_str = wrapper_path.to_str().unwrap();
|
||||
|
||||
// Build args matching production behavior
|
||||
@@ -992,7 +995,10 @@ mod dedicated_worker_protocol {
|
||||
match parse_dedicated_worker_line(response.trim()) {
|
||||
DedicatedWorkerResult::Success(value) => results.push(Ok(value)),
|
||||
DedicatedWorkerResult::Error(err) => {
|
||||
let msg = err["message"].as_str().unwrap_or("Unknown error").to_string();
|
||||
let msg = err["message"]
|
||||
.as_str()
|
||||
.unwrap_or("Unknown error")
|
||||
.to_string();
|
||||
results.push(Err(msg));
|
||||
}
|
||||
other => panic!("Unexpected response: {:?}", other),
|
||||
@@ -1162,8 +1168,8 @@ export function main(name: string) {
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings:
|
||||
windmill_common::runnable_settings::ConcurrencySettings::default().into(),
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
@@ -1190,6 +1196,68 @@ export function main(name: string) {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that full .npmrc content works for bun jobs with private registries.
|
||||
/// Requires:
|
||||
/// - `TEST_NPMRC` environment variable set to the full .npmrc content
|
||||
#[cfg(feature = "private_registry_test")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_bun_job_private_npmrc(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
use windmill_worker::NPMRC;
|
||||
|
||||
let npmrc_content = std::env::var("TEST_NPMRC")
|
||||
.expect("TEST_NPMRC must be set when running private_registry_test");
|
||||
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
let port = server.addr.port();
|
||||
|
||||
{
|
||||
let mut npmrc = NPMRC.write().await;
|
||||
*npmrc = Some(npmrc_content.clone());
|
||||
}
|
||||
|
||||
let content = r#"
|
||||
import { greet } from "@windmill-test/private-pkg";
|
||||
|
||||
export function main(name: string) {
|
||||
return greet(name);
|
||||
}
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let job = JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
dedicated_worker: None,
|
||||
});
|
||||
|
||||
let result = RunJob::from(job)
|
||||
.arg("name", serde_json::json!("World"))
|
||||
.run_until_complete(&db, false, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let mut npmrc = NPMRC.write().await;
|
||||
*npmrc = None;
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
serde_json::json!("Hello from private package, World!")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests for RELATIVE_BUN_BUILDER (loader_builder.bun.js)
|
||||
/// These tests verify Bun's behavior for import scanning and package.json generation.
|
||||
/// Purpose: Catch regressions when upgrading Bun versions.
|
||||
@@ -1241,8 +1309,8 @@ mod bun_builder_tests {
|
||||
}
|
||||
|
||||
// Read generated package.json
|
||||
let package_json = std::fs::read_to_string(dir.join("package.json"))
|
||||
.expect("package.json not generated");
|
||||
let package_json =
|
||||
std::fs::read_to_string(dir.join("package.json")).expect("package.json not generated");
|
||||
|
||||
serde_json::from_str(&package_json).expect("Invalid JSON in package.json")
|
||||
}
|
||||
@@ -1257,7 +1325,10 @@ export function main() { return lodash; }
|
||||
let pkg = run_builder(main_ts);
|
||||
let deps = pkg["dependencies"].as_object().unwrap();
|
||||
|
||||
assert!(deps.contains_key("lodash"), "lodash should be in dependencies");
|
||||
assert!(
|
||||
deps.contains_key("lodash"),
|
||||
"lodash should be in dependencies"
|
||||
);
|
||||
assert_eq!(deps["lodash"], "latest");
|
||||
}
|
||||
|
||||
@@ -1271,7 +1342,10 @@ export function main() { return _; }
|
||||
let pkg = run_builder(main_ts);
|
||||
let deps = pkg["dependencies"].as_object().unwrap();
|
||||
|
||||
assert!(deps.contains_key("lodash"), "lodash should be in dependencies");
|
||||
assert!(
|
||||
deps.contains_key("lodash"),
|
||||
"lodash should be in dependencies"
|
||||
);
|
||||
assert_eq!(deps["lodash"], "4.17.21");
|
||||
}
|
||||
|
||||
@@ -1304,9 +1378,18 @@ export function main() { return { lodash, axios, dayjs }; }
|
||||
let pkg = run_builder(main_ts);
|
||||
let deps = pkg["dependencies"].as_object().unwrap();
|
||||
|
||||
assert!(deps.contains_key("lodash"), "lodash should be in dependencies");
|
||||
assert!(deps.contains_key("axios"), "axios should be in dependencies");
|
||||
assert!(deps.contains_key("dayjs"), "dayjs should be in dependencies");
|
||||
assert!(
|
||||
deps.contains_key("lodash"),
|
||||
"lodash should be in dependencies"
|
||||
);
|
||||
assert!(
|
||||
deps.contains_key("axios"),
|
||||
"axios should be in dependencies"
|
||||
);
|
||||
assert!(
|
||||
deps.contains_key("dayjs"),
|
||||
"dayjs should be in dependencies"
|
||||
);
|
||||
assert_eq!(deps.len(), 3, "Should have exactly 3 dependencies");
|
||||
}
|
||||
|
||||
@@ -1330,8 +1413,15 @@ export function main() { return { fs, path, lodash }; }
|
||||
!deps.contains_key("path"),
|
||||
"path (builtin) should NOT be in dependencies"
|
||||
);
|
||||
assert!(deps.contains_key("lodash"), "lodash should be in dependencies");
|
||||
assert_eq!(deps.len(), 1, "Should have exactly 1 dependency (lodash only)");
|
||||
assert!(
|
||||
deps.contains_key("lodash"),
|
||||
"lodash should be in dependencies"
|
||||
);
|
||||
assert_eq!(
|
||||
deps.len(),
|
||||
1,
|
||||
"Should have exactly 1 dependency (lodash only)"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test: semver.order() resolves version conflicts (picks lowest version)
|
||||
@@ -1347,7 +1437,10 @@ export function main() { return { a, b }; }
|
||||
let pkg = run_builder(main_ts);
|
||||
let deps = pkg["dependencies"].as_object().unwrap();
|
||||
|
||||
assert!(deps.contains_key("lodash"), "lodash should be in dependencies");
|
||||
assert!(
|
||||
deps.contains_key("lodash"),
|
||||
"lodash should be in dependencies"
|
||||
);
|
||||
// The builder sorts by semver and picks the first (lowest) version
|
||||
assert_eq!(
|
||||
deps["lodash"], "4.17.10",
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- Fixture for testing wmill CLI variable/resource get from bash scripts
|
||||
|
||||
INSERT INTO variable (workspace_id, path, value, is_secret, description, extra_perms)
|
||||
VALUES ('test-workspace', 'u/test-user/test_var', 'hello from variable', false, 'A test variable', '{"u/test-user": true}');
|
||||
|
||||
INSERT INTO resource_type (workspace_id, name, schema, description, created_by)
|
||||
VALUES ('test-workspace', 'test_object', '{}', 'Test object type', 'test-user');
|
||||
|
||||
INSERT INTO resource (workspace_id, path, value, description, resource_type, extra_perms, created_by)
|
||||
VALUES ('test-workspace', 'u/test-user/test_res', '{"host": "localhost", "port": 5432}', 'A test resource', 'test_object', '{"u/test-user": true}', 'test-user');
|
||||
@@ -993,6 +993,80 @@ echo "hello $msg"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base", "wmill_cli_test"))]
|
||||
async fn test_bash_wmill_variable_get(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
let port = server.addr.port();
|
||||
|
||||
// The bash script uses wmill CLI to get the variable value.
|
||||
// The worker sets WM_TOKEN, WM_WORKSPACE, and BASE_INTERNAL_URL as env vars,
|
||||
// and the CLI auto-configures from them when no workspace is explicitly set.
|
||||
// We point WMILL_CONFIG_DIR to a clean temp dir so no local active workspace interferes.
|
||||
let content = r#"
|
||||
export WMILL_CONFIG_DIR=$(mktemp -d)
|
||||
result=$(wmill variable get "u/test-user/test_var" --json | jq -r .value)
|
||||
echo "$result"
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let job = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
lock: None,
|
||||
language: ScriptLang::Bash,
|
||||
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(),
|
||||
}))
|
||||
.run_until_complete(&db, false, port)
|
||||
.await;
|
||||
assert_eq!(job.json_result(), Some(json!("hello from variable")));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base", "wmill_cli_test"))]
|
||||
async fn test_bash_wmill_resource_get(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
let port = server.addr.port();
|
||||
|
||||
// The bash script uses wmill CLI to get the resource value.
|
||||
// We point WMILL_CONFIG_DIR to a clean temp dir so no local active workspace interferes.
|
||||
let content = r#"
|
||||
export WMILL_CONFIG_DIR=$(mktemp -d)
|
||||
result=$(wmill resource get "u/test-user/test_res" --json | jq -c .value)
|
||||
echo "$result"
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let job = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
lock: None,
|
||||
language: ScriptLang::Bash,
|
||||
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(),
|
||||
}))
|
||||
.run_until_complete(&db, false, port)
|
||||
.await;
|
||||
// Bash echo outputs are returned as strings, so the JSON is a string value
|
||||
assert_eq!(
|
||||
job.json_result(),
|
||||
Some(json!("{\"host\":\"localhost\",\"port\":5432}"))
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "nu")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_nu_job(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
@@ -1592,6 +1666,66 @@ export async function main(a: Date) {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that full .npmrc content works for deno jobs with private registries.
|
||||
/// Requires:
|
||||
/// - `TEST_NPMRC` environment variable set to the full .npmrc content
|
||||
#[cfg(feature = "private_registry_test")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_deno_job_private_npmrc(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
use windmill_worker::NPMRC;
|
||||
|
||||
let npmrc_content = std::env::var("TEST_NPMRC")
|
||||
.expect("TEST_NPMRC must be set when running private_registry_test");
|
||||
|
||||
initialize_tracing().await;
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
let port = server.addr.port();
|
||||
|
||||
{
|
||||
let mut npmrc = NPMRC.write().await;
|
||||
*npmrc = Some(npmrc_content.clone());
|
||||
}
|
||||
|
||||
let content = r#"
|
||||
import { greet } from "npm:@windmill-test/private-pkg";
|
||||
|
||||
export function main(name: string) {
|
||||
return greet(name);
|
||||
}
|
||||
"#
|
||||
.to_owned();
|
||||
|
||||
let result = RunJob::from(JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content,
|
||||
path: None,
|
||||
language: ScriptLang::Deno,
|
||||
lock: None,
|
||||
concurrency_settings: windmill_common::runnable_settings::ConcurrencySettings::default()
|
||||
.into(),
|
||||
debouncing_settings: windmill_common::runnable_settings::DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
dedicated_worker: None,
|
||||
}))
|
||||
.arg("name", json!("World"))
|
||||
.run_until_complete(&db, false, port)
|
||||
.await
|
||||
.json_result()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let mut npmrc = NPMRC.write().await;
|
||||
*npmrc = None;
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
serde_json::json!("Hello from private package, World!")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn test_python_job_datetime_and_bytes(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE-AGPL for a copy of the license.
|
||||
*/
|
||||
|
||||
use windmill_api_auth::ApiAuthed;
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
routing::{get, post},
|
||||
@@ -20,6 +19,7 @@ use std::{
|
||||
fmt::{Display, Formatter},
|
||||
vec,
|
||||
};
|
||||
use windmill_api_auth::ApiAuthed;
|
||||
use windmill_common::{
|
||||
db::UserDB,
|
||||
error::JsonResult,
|
||||
@@ -352,11 +352,12 @@ async fn update_input(
|
||||
) -> JsonResult<String> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
sqlx::query("UPDATE input SET name = $1, is_public = $2 WHERE id = $3 and workspace_id = $4")
|
||||
sqlx::query("UPDATE input SET name = $1, is_public = $2 WHERE id = $3 and workspace_id = $4 AND created_by = $5")
|
||||
.bind(&input.name)
|
||||
.bind(&input.is_public)
|
||||
.bind(&input.id)
|
||||
.bind(&w_id)
|
||||
.bind(&authed.username)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
@@ -372,9 +373,10 @@ async fn delete_input(
|
||||
) -> JsonResult<String> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
sqlx::query("DELETE FROM input WHERE id = $1 and workspace_id = $2")
|
||||
sqlx::query("DELETE FROM input WHERE id = $1 and workspace_id = $2 AND created_by = $3")
|
||||
.bind(&i_id)
|
||||
.bind(&w_id)
|
||||
.bind(&authed.username)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ windmill-api-auth.workspace = true
|
||||
windmill-common = { workspace = true, default-features = false }
|
||||
axum.workspace = true
|
||||
flate2.workspace = true
|
||||
reqwest.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
sqlx.workspace = true
|
||||
|
||||
@@ -14,8 +14,10 @@ use std::collections::HashMap;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use windmill_common::{
|
||||
error::{Error, JsonResult, Result},
|
||||
global_settings::{load_value_from_global_settings, NPM_CONFIG_REGISTRY_SETTING},
|
||||
utils::StripPath,
|
||||
global_settings::{
|
||||
load_value_from_global_settings, NPMRC_SETTING, NPM_CONFIG_REGISTRY_SETTING,
|
||||
},
|
||||
utils::{parse_npmrc_registry, StripPath},
|
||||
};
|
||||
|
||||
use windmill_api_auth::ApiAuthed;
|
||||
@@ -129,6 +131,14 @@ pub fn workspaced_service() -> Router {
|
||||
)
|
||||
}
|
||||
|
||||
fn build_registry_request(url: &str, auth_token: &Option<String>) -> reqwest::RequestBuilder {
|
||||
let mut req = HTTP_CLIENT.get(url);
|
||||
if let Some(token) = auth_token {
|
||||
req = req.bearer_auth(token);
|
||||
}
|
||||
req
|
||||
}
|
||||
|
||||
/// Get package metadata (versions and tags) from the private registry
|
||||
async fn get_package_metadata(
|
||||
_authed: ApiAuthed,
|
||||
@@ -136,21 +146,14 @@ async fn get_package_metadata(
|
||||
Extension(db): Extension<sqlx::Pool<sqlx::Postgres>>,
|
||||
) -> JsonResult<PackageVersions> {
|
||||
let package = parse_package_name(package_path.to_path());
|
||||
let npm_registry = get_npm_registry(&db).await?;
|
||||
|
||||
if npm_registry.is_none() {
|
||||
return Err(Error::BadRequest(
|
||||
"No private npm registry configured".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let registry_url = npm_registry.unwrap();
|
||||
let (registry_url, auth_token) = get_npm_registry(&db)
|
||||
.await?
|
||||
.ok_or_else(|| Error::BadRequest("No private npm registry configured".to_string()))?;
|
||||
let package_url = format_registry_url(®istry_url, &package, None, None);
|
||||
|
||||
tracing::info!("Fetching package metadata from: {}", package_url);
|
||||
|
||||
let response = HTTP_CLIENT
|
||||
.get(&package_url)
|
||||
let response = build_registry_request(&package_url, &auth_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to fetch package metadata: {}", e)))?;
|
||||
@@ -167,7 +170,6 @@ async fn get_package_metadata(
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to parse package metadata: {}", e)))?;
|
||||
|
||||
// Extract versions and dist-tags from the package metadata
|
||||
let mut versions = Vec::new();
|
||||
let mut tags = HashMap::new();
|
||||
|
||||
@@ -194,22 +196,15 @@ async fn resolve_package_version(
|
||||
Extension(db): Extension<sqlx::Pool<sqlx::Postgres>>,
|
||||
) -> JsonResult<PackageVersion> {
|
||||
let package = parse_package_name(package_path.to_path());
|
||||
let npm_registry = get_npm_registry(&db).await?;
|
||||
|
||||
if npm_registry.is_none() {
|
||||
return Err(Error::BadRequest(
|
||||
"No private npm registry configured".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let registry_url = npm_registry.unwrap();
|
||||
let (registry_url, auth_token) = get_npm_registry(&db)
|
||||
.await?
|
||||
.ok_or_else(|| Error::BadRequest("No private npm registry configured".to_string()))?;
|
||||
let reference = query.tag.unwrap_or_else(|| "latest".to_string());
|
||||
let package_url = format_registry_url(®istry_url, &package, None, None);
|
||||
|
||||
tracing::info!("Resolving package version from: {}", package_url);
|
||||
|
||||
let response = HTTP_CLIENT
|
||||
.get(&package_url)
|
||||
let response = build_registry_request(&package_url, &auth_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to fetch package metadata: {}", e)))?;
|
||||
@@ -256,21 +251,14 @@ async fn get_package_filetree(
|
||||
Extension(db): Extension<sqlx::Pool<sqlx::Postgres>>,
|
||||
) -> JsonResult<PackageFiletree> {
|
||||
let (package, version) = parse_package_and_version(package_version_path.to_path())?;
|
||||
let npm_registry = get_npm_registry(&db).await?;
|
||||
|
||||
if npm_registry.is_none() {
|
||||
return Err(Error::BadRequest(
|
||||
"No private npm registry configured".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let registry_url = npm_registry.unwrap();
|
||||
let (registry_url, auth_token) = get_npm_registry(&db)
|
||||
.await?
|
||||
.ok_or_else(|| Error::BadRequest("No private npm registry configured".to_string()))?;
|
||||
let package_url = format_registry_url(®istry_url, &package, None, None);
|
||||
|
||||
tracing::info!("Fetching package filetree from: {}", package_url);
|
||||
|
||||
let response = HTTP_CLIENT
|
||||
.get(&package_url)
|
||||
let response = build_registry_request(&package_url, &auth_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to fetch package metadata: {}", e)))?;
|
||||
@@ -287,7 +275,6 @@ async fn get_package_filetree(
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to parse package metadata: {}", e)))?;
|
||||
|
||||
// Get the tarball URL for this version
|
||||
let tarball_url = package_json
|
||||
.get("versions")
|
||||
.and_then(|v| v.get(&version))
|
||||
@@ -296,9 +283,7 @@ async fn get_package_filetree(
|
||||
.and_then(|t| t.as_str())
|
||||
.ok_or_else(|| Error::NotFound(format!("Tarball not found for {}@{}", package, version)))?;
|
||||
|
||||
// Download and extract tarball to get file list
|
||||
let tarball_response = HTTP_CLIENT
|
||||
.get(tarball_url)
|
||||
let tarball_response = build_registry_request(tarball_url, &auth_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to download tarball: {}", e)))?;
|
||||
@@ -337,21 +322,14 @@ async fn get_package_file(
|
||||
Extension(db): Extension<sqlx::Pool<sqlx::Postgres>>,
|
||||
) -> Result<String> {
|
||||
let (package, version, filepath) = parse_package_version_and_file(full_path.to_path())?;
|
||||
let npm_registry = get_npm_registry(&db).await?;
|
||||
|
||||
if npm_registry.is_none() {
|
||||
return Err(Error::BadRequest(
|
||||
"No private npm registry configured".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let registry_url = npm_registry.unwrap();
|
||||
let (registry_url, auth_token) = get_npm_registry(&db)
|
||||
.await?
|
||||
.ok_or_else(|| Error::BadRequest("No private npm registry configured".to_string()))?;
|
||||
let package_url = format_registry_url(®istry_url, &package, None, None);
|
||||
|
||||
tracing::info!("Fetching package file from: {}", package_url);
|
||||
|
||||
let response = HTTP_CLIENT
|
||||
.get(&package_url)
|
||||
let response = build_registry_request(&package_url, &auth_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to fetch package metadata: {}", e)))?;
|
||||
@@ -368,7 +346,6 @@ async fn get_package_file(
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to parse package metadata: {}", e)))?;
|
||||
|
||||
// Get the tarball URL for this version
|
||||
let tarball_url = package_json
|
||||
.get("versions")
|
||||
.and_then(|v| v.get(&version))
|
||||
@@ -377,9 +354,7 @@ async fn get_package_file(
|
||||
.and_then(|t| t.as_str())
|
||||
.ok_or_else(|| Error::NotFound(format!("Tarball not found for {}@{}", package, version)))?;
|
||||
|
||||
// Download tarball
|
||||
let tarball_response = HTTP_CLIENT
|
||||
.get(tarball_url)
|
||||
let tarball_response = build_registry_request(tarball_url, &auth_token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to download tarball: {}", e)))?;
|
||||
@@ -402,13 +377,38 @@ async fn get_package_file(
|
||||
Ok(file_content)
|
||||
}
|
||||
|
||||
/// Get the npm registry URL from global settings
|
||||
async fn get_npm_registry(db: &sqlx::Pool<sqlx::Postgres>) -> Result<Option<String>> {
|
||||
/// Get the npm registry URL and optional auth token from global settings.
|
||||
/// Checks the `npmrc` setting first, then falls back to `npm_config_registry`.
|
||||
async fn get_npm_registry(
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
) -> Result<Option<(String, Option<String>)>> {
|
||||
let npmrc = load_value_from_global_settings(db, NPMRC_SETTING)
|
||||
.await?
|
||||
.and_then(|v| v.as_str().map(|s| s.to_string()));
|
||||
|
||||
if let Some(ref npmrc_content) = npmrc {
|
||||
if let Some(parsed) = parse_npmrc_registry(npmrc_content) {
|
||||
return Ok(Some(parsed));
|
||||
}
|
||||
}
|
||||
|
||||
let registry = load_value_from_global_settings(db, NPM_CONFIG_REGISTRY_SETTING)
|
||||
.await?
|
||||
.and_then(|v| v.as_str().map(|s| s.to_string()));
|
||||
|
||||
Ok(registry)
|
||||
if let Some(ref s) = registry {
|
||||
let (url, token) = if s.contains(":_authToken=") {
|
||||
let parts: Vec<&str> = s.split(":_authToken=").collect();
|
||||
let url = parts[0].to_string();
|
||||
let token = parts.get(1).map(|t| t.to_string());
|
||||
(url, token)
|
||||
} else {
|
||||
(s.clone(), None)
|
||||
};
|
||||
return Ok(Some((url, token)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Format a registry URL for a package
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.640.0
|
||||
version: 1.642.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -19671,6 +19671,8 @@ components:
|
||||
type: boolean
|
||||
lock:
|
||||
type: string
|
||||
flow_path:
|
||||
type: string
|
||||
required:
|
||||
- args
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ impl RawWebhookArgs {
|
||||
get_random_file_name, get_workspace_s3_resource, upload_file_internal,
|
||||
};
|
||||
use futures::TryStreamExt;
|
||||
use windmill_object_store::object_store_reexports::{Attribute, Attributes};
|
||||
use windmill_object_store::build_object_store_client;
|
||||
use windmill_object_store::object_store_reexports::{Attribute, Attributes};
|
||||
|
||||
let (_, s3_resource) = get_workspace_s3_resource(authed, db, None, w_id, None).await?;
|
||||
|
||||
@@ -106,20 +106,24 @@ impl RawWebhookArgs {
|
||||
Error::BadRequest(format!("Error reading multipart field: {}", e.body_text()))
|
||||
})? {
|
||||
if let Some(name) = field.name().map(|x| x.to_string()) {
|
||||
if let Some(content_type) = field.content_type() {
|
||||
if field.file_name().is_some() {
|
||||
let content_type = field
|
||||
.content_type()
|
||||
.unwrap_or("application/octet-stream")
|
||||
.to_string();
|
||||
let ext = field
|
||||
.file_name()
|
||||
.map(|x| x.split('.').last())
|
||||
.flatten()
|
||||
.and_then(|x| x.split('.').last())
|
||||
.map(|x| x.to_string());
|
||||
let filename = field.file_name().map(|x| x.to_string());
|
||||
|
||||
let file_key = get_random_file_name(ext);
|
||||
|
||||
let options = Attributes::from_iter(vec![
|
||||
(Attribute::ContentType, content_type.to_string()),
|
||||
(Attribute::ContentType, content_type),
|
||||
(
|
||||
Attribute::ContentDisposition,
|
||||
if let Some(filename) = field.file_name() {
|
||||
if let Some(filename) = filename {
|
||||
format!("inline; filename=\"{}\"", filename)
|
||||
} else {
|
||||
"inline".to_string()
|
||||
|
||||
@@ -42,8 +42,6 @@ use windmill_common::runnable_settings::{
|
||||
};
|
||||
#[cfg(feature = "inline_preview")]
|
||||
use windmill_common::runtime_assets::{register_runtime_asset, InsertRuntimeAssetParams};
|
||||
use windmill_types::s3::BundleFormat;
|
||||
use windmill_object_store::upload_artifact_to_store;
|
||||
use windmill_common::scripts::ScriptRunnableSettingsInline;
|
||||
use windmill_common::triggers::TriggerMetadata;
|
||||
use windmill_common::utils::{RunnableKind, WarnAfterExt};
|
||||
@@ -54,8 +52,10 @@ use windmill_common::workspace_dependencies::{
|
||||
use windmill_common::DYNAMIC_INPUT_CACHE;
|
||||
#[cfg(all(feature = "enterprise", feature = "smtp"))]
|
||||
use windmill_common::{email_oss::send_email_html, server::load_smtp_config};
|
||||
use windmill_object_store::upload_artifact_to_store;
|
||||
#[cfg(feature = "inline_preview")]
|
||||
use windmill_parser::asset_parser::AssetKind;
|
||||
use windmill_types::s3::BundleFormat;
|
||||
#[cfg(feature = "inline_preview")]
|
||||
use windmill_worker::get_worker_internal_server_inline_utils;
|
||||
|
||||
@@ -1386,7 +1386,8 @@ async fn get_logs_from_store(
|
||||
log_file_index: &Option<Vec<String>>,
|
||||
) -> Option<error::Result<Body>> {
|
||||
use futures::StreamExt;
|
||||
let stream = windmill_object_store::get_logs_from_store(log_offset, logs, log_file_index).await?;
|
||||
let stream =
|
||||
windmill_object_store::get_logs_from_store(log_offset, logs, log_file_index).await?;
|
||||
let header = bytes::Bytes::from(
|
||||
r#"to remove ansi colors, use: | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'
|
||||
"#
|
||||
@@ -2849,6 +2850,7 @@ struct Preview {
|
||||
dedicated_worker: Option<bool>,
|
||||
lock: Option<String>,
|
||||
format: Option<String>,
|
||||
flow_path: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "inline_preview")]
|
||||
@@ -4509,6 +4511,14 @@ async fn run_preview_script(
|
||||
check_tag_available_for_workspace(&db, &w_id, &tag, &authed).await?;
|
||||
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into());
|
||||
|
||||
let preview_args = preview.args.unwrap_or_default();
|
||||
let flow_path_extra = preview.flow_path.map(|fp| {
|
||||
let mut extra = HashMap::new();
|
||||
extra.insert("_FLOW_PATH".to_string(), to_raw_value(&fp));
|
||||
extra
|
||||
});
|
||||
let push_args = PushArgs { extra: flow_path_extra, args: &preview_args };
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
@@ -4532,7 +4542,7 @@ async fn run_preview_script(
|
||||
dedicated_worker: preview.dedicated_worker,
|
||||
}),
|
||||
},
|
||||
PushArgs::from(&preview.args.unwrap_or_default()),
|
||||
push_args,
|
||||
authed.display_username(),
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
@@ -5772,7 +5782,9 @@ async fn get_log_file(Path((_w_id, file_p)): Path<(String, String)>) -> error::R
|
||||
#[cfg(all(feature = "enterprise", feature = "parquet"))]
|
||||
if let Some(os) = windmill_object_store::get_object_store().await {
|
||||
let file = os
|
||||
.get(&windmill_object_store::object_store_reexports::Path::from(format!("logs/{file_p}")))
|
||||
.get(&windmill_object_store::object_store_reexports::Path::from(
|
||||
format!("logs/{file_p}"),
|
||||
))
|
||||
.await;
|
||||
if let Ok(file) = file {
|
||||
if let Ok(bytes) = file.bytes().await {
|
||||
|
||||
@@ -10,6 +10,7 @@ pub const REQUEST_SIZE_LIMIT_SETTING: &str = "request_size_limit_mb";
|
||||
pub const LICENSE_KEY_SETTING: &str = "license_key";
|
||||
pub const NPM_CONFIG_REGISTRY_SETTING: &str = "npm_config_registry";
|
||||
pub const BUNFIG_INSTALL_SCOPES_SETTING: &str = "bunfig_install_scopes";
|
||||
pub const NPMRC_SETTING: &str = "npmrc";
|
||||
pub const NUGET_CONFIG_SETTING: &str = "nuget_config";
|
||||
pub const POWERSHELL_REPO_URL_SETTING: &str = "powershell_repo_url";
|
||||
pub const POWERSHELL_REPO_PAT_SETTING: &str = "powershell_repo_pat";
|
||||
|
||||
@@ -261,6 +261,8 @@ pub struct GlobalSettings {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bunfig_install_scopes: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub npmrc: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub nuget_config: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub maven_repos: Option<String>,
|
||||
@@ -774,7 +776,11 @@ pub const PROTECTED_SETTINGS: &[&str] = &[
|
||||
/// Note: jwt_secret is intentionally NOT hidden — it is included in YAML exports so that
|
||||
/// operators can set it via ConfigMap. It is protected from deletion (PROTECTED_SETTINGS)
|
||||
/// and from being set to empty/null, and its value is partially redacted in log output.
|
||||
pub const HIDDEN_SETTINGS: &[&str] = &["uid", "min_keep_alive_version", "automate_username_creation"];
|
||||
pub const HIDDEN_SETTINGS: &[&str] = &[
|
||||
"uid",
|
||||
"min_keep_alive_version",
|
||||
"automate_username_creation",
|
||||
];
|
||||
|
||||
/// Top-level settings whose entire value is sensitive and must be fully redacted in logs.
|
||||
const SENSITIVE_SETTINGS: &[&str] = &[
|
||||
@@ -788,6 +794,7 @@ const SENSITIVE_SETTINGS: &[&str] = &[
|
||||
"pip_extra_index_url",
|
||||
"npm_config_registry",
|
||||
"bunfig_install_scopes",
|
||||
"npmrc",
|
||||
"maven_repos",
|
||||
"ruby_repos",
|
||||
"powershell_repo_pat",
|
||||
@@ -798,7 +805,10 @@ const SENSITIVE_SETTINGS: &[&str] = &[
|
||||
const NESTED_SENSITIVE_FIELDS: &[(&str, &[&str])] = &[
|
||||
("smtp_settings", &["smtp_password"]),
|
||||
("secret_backend", &["token"]),
|
||||
("object_store_cache_config", &["secret_key", "serviceAccountKey"]),
|
||||
(
|
||||
"object_store_cache_config",
|
||||
&["secret_key", "serviceAccountKey"],
|
||||
),
|
||||
];
|
||||
|
||||
fn redact_json_value(value: &serde_json::Value) -> serde_json::Value {
|
||||
@@ -2353,7 +2363,11 @@ mod tests {
|
||||
);
|
||||
|
||||
let diff = diff_global_settings(¤t, &desired, ApplyMode::Merge);
|
||||
assert_eq!(diff.upserts.len(), 1, "Same client with newer expiry should update even with different signature");
|
||||
assert_eq!(
|
||||
diff.upserts.len(),
|
||||
1,
|
||||
"Same client with newer expiry should update even with different signature"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1281,3 +1281,96 @@ mod tests {
|
||||
assert_eq!(parsed, serde_json::json!([[1], [2], [3], [4], [5]]));
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse .npmrc content to extract the default registry URL and its auth token.
|
||||
/// Returns `Some((registry_url, Option<auth_token>))` if a default registry is found.
|
||||
pub fn parse_npmrc_registry(npmrc_content: &str) -> Option<(String, Option<String>)> {
|
||||
let mut registry_url: Option<String> = None;
|
||||
let mut auth_tokens: Vec<(String, String)> = Vec::new();
|
||||
|
||||
for line in npmrc_content.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') || line.starts_with(';') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(url) = line.strip_prefix("registry=") {
|
||||
registry_url = Some(url.trim().to_string());
|
||||
}
|
||||
|
||||
if line.starts_with("//") {
|
||||
if let Some((prefix, token)) = line.split_once(":_authToken=") {
|
||||
auth_tokens.push((prefix.to_string(), token.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let url = registry_url?;
|
||||
let url_without_protocol = url.trim_start_matches("https:").trim_start_matches("http:");
|
||||
let url_prefix = url_without_protocol.trim_end_matches('/');
|
||||
|
||||
let token = auth_tokens
|
||||
.iter()
|
||||
.find(|(prefix, _)| {
|
||||
let p = prefix.trim_end_matches('/');
|
||||
p == url_prefix
|
||||
})
|
||||
.map(|(_, token)| token.clone());
|
||||
|
||||
Some((url, token))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod npmrc_tests {
|
||||
use super::parse_npmrc_registry;
|
||||
|
||||
#[test]
|
||||
fn test_parse_simple_registry() {
|
||||
let npmrc = "registry=https://registry.mycompany.com/\n//registry.mycompany.com/:_authToken=secret123\n";
|
||||
let result = parse_npmrc_registry(npmrc);
|
||||
assert_eq!(
|
||||
result,
|
||||
Some((
|
||||
"https://registry.mycompany.com/".to_string(),
|
||||
Some("secret123".to_string())
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_registry_without_auth() {
|
||||
let npmrc = "registry=https://registry.npmjs.org/\n";
|
||||
let result = parse_npmrc_registry(npmrc);
|
||||
assert_eq!(
|
||||
result,
|
||||
Some(("https://registry.npmjs.org/".to_string(), None))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_scoped_only_no_default() {
|
||||
let npmrc =
|
||||
"@myorg:registry=https://registry.myorg.com/\n//registry.myorg.com/:_authToken=tok\n";
|
||||
let result = parse_npmrc_registry(npmrc);
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_with_comments() {
|
||||
let npmrc = "# My registry\nregistry=https://r.example.com/\n; auth\n//r.example.com/:_authToken=tok\n";
|
||||
let result = parse_npmrc_registry(npmrc);
|
||||
assert_eq!(
|
||||
result,
|
||||
Some((
|
||||
"https://r.example.com/".to_string(),
|
||||
Some("tok".to_string())
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_empty_npmrc() {
|
||||
assert_eq!(parse_npmrc_registry(""), None);
|
||||
assert_eq!(parse_npmrc_registry("# just a comment"), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ fn format_pull_query(peek: String) -> String {
|
||||
j.same_worker, j.pre_run_error, j.visible_to_owner,
|
||||
j.tag, j.concurrent_limit, j.concurrency_time_window_s, j.flow_innermost_root_job, j.root_job,
|
||||
j.timeout, j.flow_step_id, j.cache_ttl, q.cache_ignore_s3_path, q.runnable_settings_handle, j.priority, j.raw_code, j.raw_lock, j.raw_flow,
|
||||
j.script_entrypoint_override, j.preprocessed, pj.runnable_path as parent_runnable_path,
|
||||
j.script_entrypoint_override, j.preprocessed, COALESCE(pj.runnable_path, j.args->>'_FLOW_PATH') as parent_runnable_path,
|
||||
COALESCE(p.email, j.permissioned_as_email) as permissioned_as_email, p.username as permissioned_as_username, p.is_admin as permissioned_as_is_admin,
|
||||
p.is_operator as permissioned_as_is_operator, p.groups as permissioned_as_groups, p.folders as permissioned_as_folders, p.end_user_email as permissioned_as_end_user_email
|
||||
FROM q, j
|
||||
|
||||
@@ -5321,7 +5321,11 @@ async fn push_inner<'c, 'd>(
|
||||
.as_ref()
|
||||
.map(|x| {
|
||||
let tag_lang = if x == &ScriptLang::Bunnative {
|
||||
ScriptLang::Nativets.as_str()
|
||||
if job_kind == JobKind::Dependencies {
|
||||
ScriptLang::Bun.as_str()
|
||||
} else {
|
||||
ScriptLang::Nativets.as_str()
|
||||
}
|
||||
} else {
|
||||
x.as_str()
|
||||
};
|
||||
@@ -5364,7 +5368,6 @@ async fn push_inner<'c, 'd>(
|
||||
job_id,
|
||||
&args,
|
||||
&mut tx,
|
||||
_db,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
@@ -6204,7 +6207,7 @@ pub async fn get_same_worker_job(
|
||||
v2_job.raw_code,
|
||||
v2_job.raw_lock,
|
||||
v2_job.raw_flow,
|
||||
pj.runnable_path as parent_runnable_path,
|
||||
COALESCE(pj.runnable_path, v2_job.args->>'_FLOW_PATH') as parent_runnable_path,
|
||||
p.email as permissioned_as_email, p.username as permissioned_as_username, p.is_admin as permissioned_as_is_admin,
|
||||
p.is_operator as permissioned_as_is_operator, p.groups as permissioned_as_groups, p.folders as permissioned_as_folders, p.end_user_email as permissioned_as_end_user_email
|
||||
FROM v2_job_queue
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,8 @@ use crate::{
|
||||
handle_child::handle_child,
|
||||
is_sandboxing_enabled, read_ee_registry, BUNFIG_INSTALL_SCOPES, BUN_BUNDLE_CACHE_DIR,
|
||||
BUN_CACHE_DIR, BUN_NO_CACHE, BUN_PATH, DISABLE_NUSER, HOME_ENV, NODE_BIN_PATH, NODE_PATH,
|
||||
NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS, TRACING_PROXY_CA_CERT_PATH,
|
||||
TZ_ENV,
|
||||
NPMRC, NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, PROXY_ENVS,
|
||||
TRACING_PROXY_CA_CERT_PATH, TZ_ENV,
|
||||
};
|
||||
use windmill_common::{
|
||||
client::AuthedClient,
|
||||
@@ -299,6 +299,20 @@ async fn gen_bunfig(
|
||||
w_id: &str,
|
||||
db: Option<&Connection>,
|
||||
) -> Result<()> {
|
||||
let npmrc = if let Some(conn) = db {
|
||||
read_ee_registry(NPMRC.read().await.clone(), "npmrc", job_id, w_id, conn).await
|
||||
} else {
|
||||
NPMRC.read().await.clone()
|
||||
};
|
||||
|
||||
if let Some(ref npmrc_content) = npmrc {
|
||||
if !npmrc_content.trim().is_empty() {
|
||||
tracing::debug!("Writing .npmrc for bun from npmrc setting");
|
||||
write_file(job_dir, ".npmrc", npmrc_content)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let (registry, bunfig_install_scopes) = if let Some(conn) = db {
|
||||
(
|
||||
read_ee_registry(
|
||||
@@ -402,39 +416,55 @@ pub async fn install_bun_lockfile(
|
||||
};
|
||||
|
||||
let has_file = if npm_mode {
|
||||
let registry = if let Some(conn) = db {
|
||||
read_ee_registry(
|
||||
NPM_CONFIG_REGISTRY.read().await.clone(),
|
||||
"npm registry",
|
||||
job_id,
|
||||
w_id,
|
||||
conn,
|
||||
)
|
||||
.await
|
||||
let npmrc = if let Some(conn) = db {
|
||||
read_ee_registry(NPMRC.read().await.clone(), "npmrc", job_id, w_id, conn).await
|
||||
} else {
|
||||
NPM_CONFIG_REGISTRY.read().await.clone()
|
||||
NPMRC.read().await.clone()
|
||||
};
|
||||
if let Some(registry) = registry {
|
||||
let content = registry
|
||||
.trim_start_matches("https:")
|
||||
.trim_start_matches("http:");
|
||||
|
||||
let mut splitted = registry.split(":_authToken=");
|
||||
let custom_registry = splitted.next().unwrap_or_default();
|
||||
npm_logs.push_str(&format!(
|
||||
"Using custom npm registry: {custom_registry} {}\n",
|
||||
if splitted.next().is_some() {
|
||||
"with authToken"
|
||||
} else {
|
||||
"without authToken"
|
||||
}
|
||||
));
|
||||
|
||||
child_cmd.env("NPM_CONFIG_REGISTRY", custom_registry);
|
||||
write_file(job_dir, ".npmrc", content)?;
|
||||
true
|
||||
if let Some(ref npmrc_content) = npmrc {
|
||||
if !npmrc_content.trim().is_empty() {
|
||||
npm_logs.push_str("Using .npmrc from instance settings\n");
|
||||
write_file(job_dir, ".npmrc", npmrc_content)?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
let registry = if let Some(conn) = db {
|
||||
read_ee_registry(
|
||||
NPM_CONFIG_REGISTRY.read().await.clone(),
|
||||
"npm registry",
|
||||
job_id,
|
||||
w_id,
|
||||
conn,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
NPM_CONFIG_REGISTRY.read().await.clone()
|
||||
};
|
||||
if let Some(registry) = registry {
|
||||
let content = registry
|
||||
.trim_start_matches("https:")
|
||||
.trim_start_matches("http:");
|
||||
|
||||
let mut splitted = registry.split(":_authToken=");
|
||||
let custom_registry = splitted.next().unwrap_or_default();
|
||||
npm_logs.push_str(&format!(
|
||||
"Using custom npm registry: {custom_registry} {}\n",
|
||||
if splitted.next().is_some() {
|
||||
"with authToken"
|
||||
} else {
|
||||
"without authToken"
|
||||
}
|
||||
));
|
||||
|
||||
child_cmd.env("NPM_CONFIG_REGISTRY", custom_registry);
|
||||
write_file(job_dir, ".npmrc", content)?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
@@ -446,9 +476,11 @@ pub async fn install_bun_lockfile(
|
||||
}
|
||||
}
|
||||
|
||||
let mut child_process = start_child_process(child_cmd, &*BUN_PATH, false).await?;
|
||||
if !has_file {
|
||||
gen_bunfig(job_dir, job_id, w_id, db).await?;
|
||||
}
|
||||
|
||||
gen_bunfig(job_dir, job_id, w_id, db).await?;
|
||||
let mut child_process = start_child_process(child_cmd, &*BUN_PATH, false).await?;
|
||||
if let Some(db) = db {
|
||||
handle_child(
|
||||
job_id,
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{
|
||||
},
|
||||
get_proxy_envs_for_lang,
|
||||
handle_child::handle_child,
|
||||
is_sandboxing_enabled, read_ee_registry, DENO_CACHE_DIR, DENO_PATH, HOME_ENV,
|
||||
is_sandboxing_enabled, read_ee_registry, DENO_CACHE_DIR, DENO_PATH, HOME_ENV, NPMRC,
|
||||
NPM_CONFIG_REGISTRY, PATH_ENV, TZ_ENV,
|
||||
};
|
||||
use windmill_common::client::AuthedClient;
|
||||
@@ -79,21 +79,29 @@ async fn get_common_deno_proc_envs(
|
||||
),
|
||||
]);
|
||||
|
||||
let registry = if let Some(conn) = conn {
|
||||
read_ee_registry(
|
||||
NPM_CONFIG_REGISTRY.read().await.clone(),
|
||||
"npm registry",
|
||||
job_id,
|
||||
w_id,
|
||||
conn,
|
||||
)
|
||||
.await
|
||||
let npmrc = if let Some(conn) = conn {
|
||||
read_ee_registry(NPMRC.read().await.clone(), "npmrc", job_id, w_id, conn).await
|
||||
} else {
|
||||
NPM_CONFIG_REGISTRY.read().await.clone()
|
||||
NPMRC.read().await.clone()
|
||||
};
|
||||
if let Some(ref s) = registry {
|
||||
let (url, _token_opt) = parse_npm_config(s);
|
||||
deno_envs.insert(String::from("NPM_CONFIG_REGISTRY"), url);
|
||||
|
||||
if npmrc.as_ref().map_or(true, |s| s.trim().is_empty()) {
|
||||
let registry = if let Some(conn) = conn {
|
||||
read_ee_registry(
|
||||
NPM_CONFIG_REGISTRY.read().await.clone(),
|
||||
"npm registry",
|
||||
job_id,
|
||||
w_id,
|
||||
conn,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
NPM_CONFIG_REGISTRY.read().await.clone()
|
||||
};
|
||||
if let Some(ref s) = registry {
|
||||
let (url, _token_opt) = parse_npm_config(s);
|
||||
deno_envs.insert(String::from("NPM_CONFIG_REGISTRY"), url);
|
||||
}
|
||||
}
|
||||
if DENO_CERT.len() > 0 {
|
||||
deno_envs.insert(String::from("DENO_CERT"), DENO_CERT.clone());
|
||||
@@ -390,6 +398,21 @@ try {{
|
||||
common_deno_proc_envs.insert("HOME".to_string(), job_dir.to_string());
|
||||
}
|
||||
|
||||
let npmrc = read_ee_registry(
|
||||
NPMRC.read().await.clone(),
|
||||
"npmrc",
|
||||
&job.id,
|
||||
&job.workspace_id,
|
||||
conn,
|
||||
)
|
||||
.await;
|
||||
if let Some(ref npmrc_content) = npmrc {
|
||||
if !npmrc_content.trim().is_empty() {
|
||||
write_file(job_dir, ".npmrc", npmrc_content)?;
|
||||
write_file(job_dir, "deno.json", "{}")?;
|
||||
}
|
||||
}
|
||||
|
||||
//do not cache local dependencies
|
||||
let child = {
|
||||
let reload = format!("--reload={base_internal_url}");
|
||||
|
||||
@@ -11,7 +11,6 @@ use tiberius::{
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::compat::TokioAsyncWriteCompatExt;
|
||||
use uuid::Uuid;
|
||||
use windmill_object_store::convert_json_line_stream;
|
||||
use windmill_common::utils::merge_raw_values_to_object;
|
||||
use windmill_common::worker::SqlResultCollectionStrategy;
|
||||
use windmill_common::{
|
||||
@@ -19,6 +18,7 @@ use windmill_common::{
|
||||
utils::empty_as_none,
|
||||
worker::{to_raw_value, Connection},
|
||||
};
|
||||
use windmill_object_store::convert_json_line_stream;
|
||||
use windmill_parser_sql::{parse_db_resource, parse_mssql_sig, parse_s3_mode};
|
||||
use windmill_queue::MiniPulledJob;
|
||||
use windmill_queue::{append_logs, CanceledBy};
|
||||
@@ -428,9 +428,7 @@ fn sql_to_json_value(val: ColumnData) -> Result<Box<RawValue>, Error> {
|
||||
}
|
||||
|
||||
fn numeric_to_raw_value(numeric: &tiberius::numeric::Numeric) -> Result<Box<RawValue>, Error> {
|
||||
// tiberius::Numeric::to_string is broken, don't use it
|
||||
|
||||
let sign = if numeric.int_part().is_negative() {
|
||||
let sign = if numeric.value().is_negative() {
|
||||
"-"
|
||||
} else {
|
||||
""
|
||||
@@ -468,6 +466,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tiberius::numeric::Numeric;
|
||||
|
||||
#[test]
|
||||
fn test_sql_to_json_value_numeric_null() {
|
||||
@@ -477,7 +476,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_sql_to_json_value_numeric_integer() {
|
||||
use tiberius::numeric::Numeric;
|
||||
let numeric = Numeric::new_with_scale(12345, 0);
|
||||
let result = sql_to_json_value(ColumnData::Numeric(Some(numeric))).unwrap();
|
||||
assert_eq!(result.get(), "12345");
|
||||
@@ -485,7 +483,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_sql_to_json_value_numeric_decimal() {
|
||||
use tiberius::numeric::Numeric;
|
||||
let numeric = Numeric::new_with_scale(123456, 2); // Represents 1234.56
|
||||
let result = sql_to_json_value(ColumnData::Numeric(Some(numeric))).unwrap();
|
||||
assert_eq!(result.get(), "1234.56");
|
||||
@@ -493,7 +490,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_sql_to_json_value_numeric_negative() {
|
||||
use tiberius::numeric::Numeric;
|
||||
let numeric = Numeric::new_with_scale(-98765, 2); // Represents -987.65
|
||||
let result = sql_to_json_value(ColumnData::Numeric(Some(numeric))).unwrap();
|
||||
assert_eq!(result.get(), "-987.65");
|
||||
@@ -501,7 +497,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_sql_to_json_value_numeric_negative_integer() {
|
||||
use tiberius::numeric::Numeric;
|
||||
let numeric = Numeric::new_with_scale(-98765, 0);
|
||||
let result = sql_to_json_value(ColumnData::Numeric(Some(numeric))).unwrap();
|
||||
assert_eq!(result.get(), "-98765");
|
||||
@@ -509,15 +504,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_sql_to_json_value_numeric_high_precision() {
|
||||
use tiberius::numeric::Numeric;
|
||||
let numeric = Numeric::new_with_scale(123456789012345, 10); // High precision
|
||||
let result = sql_to_json_value(ColumnData::Numeric(Some(numeric))).unwrap();
|
||||
assert_eq!(result.get(), "12345.6789012345");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sql_to_json_value_numeric_negative_fractional_only() {
|
||||
// -0.4: int_part() is 0, so old code lost the negative sign
|
||||
let numeric = Numeric::new_with_scale(-4, 1);
|
||||
let result = sql_to_json_value(ColumnData::Numeric(Some(numeric))).unwrap();
|
||||
assert_eq!(result.get(), "-0.4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sql_to_json_value_numeric_7_69() {
|
||||
use tiberius::numeric::Numeric;
|
||||
let numeric = Numeric::new_with_scale(769, 2);
|
||||
let result = sql_to_json_value(ColumnData::Numeric(Some(numeric))).unwrap();
|
||||
assert_eq!(result.get(), "7.69");
|
||||
|
||||
@@ -571,6 +571,7 @@ lazy_static::lazy_static! {
|
||||
|
||||
pub static ref NPM_CONFIG_REGISTRY: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref BUNFIG_INSTALL_SCOPES: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref NPMRC: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref BUN_NO_CACHE: bool = std::env::var("BUN_NO_CACHE")
|
||||
.ok()
|
||||
.and_then(|x| x.parse::<bool>().ok())
|
||||
|
||||
@@ -1328,29 +1328,30 @@ pub async fn update_flow_status_after_job_completion_internal(
|
||||
|
||||
if module_step.is_preprocessor_step() && success {
|
||||
let tag_and_concurrency_key = get_tag_and_concurrency(&flow, db).await;
|
||||
let require_args = tag_and_concurrency_key.as_ref().is_some_and(|x| {
|
||||
let has_debouncing = flow_value
|
||||
.debouncing_settings
|
||||
.debounce_delay_s
|
||||
.filter(|x| *x > 0)
|
||||
.is_some();
|
||||
let concurrency_requires_args = tag_and_concurrency_key.as_ref().is_some_and(|x| {
|
||||
x.tag.as_ref().is_some_and(|t| t.contains("$args"))
|
||||
|| x.concurrency_key
|
||||
.as_ref()
|
||||
.is_some_and(|ck| ck.contains("$args"))
|
||||
});
|
||||
let mut tag = tag_and_concurrency_key
|
||||
.as_ref()
|
||||
.map(|x| x.tag.clone())
|
||||
.flatten();
|
||||
let require_args = concurrency_requires_args || has_debouncing;
|
||||
let mut tag = tag_and_concurrency_key.as_ref().and_then(|x| x.tag.clone());
|
||||
let concurrency_key = tag_and_concurrency_key
|
||||
.as_ref()
|
||||
.map(|x| x.concurrency_key.clone())
|
||||
.flatten();
|
||||
.and_then(|x| x.concurrency_key.clone());
|
||||
let concurrent_limit = tag_and_concurrency_key
|
||||
.as_ref()
|
||||
.map(|x| x.concurrent_limit)
|
||||
.flatten();
|
||||
.and_then(|x| x.concurrent_limit);
|
||||
let concurrency_time_window_s = tag_and_concurrency_key
|
||||
.as_ref()
|
||||
.map(|x| x.concurrency_time_window_s)
|
||||
.flatten();
|
||||
if require_args {
|
||||
.and_then(|x| x.concurrency_time_window_s);
|
||||
|
||||
let fetched_args = if require_args {
|
||||
let args = sqlx::query_scalar!(
|
||||
"SELECT result as \"result: Json<HashMap<String, Box<RawValue>>>\"
|
||||
FROM v2_job_completed
|
||||
@@ -1362,8 +1363,13 @@ pub async fn update_flow_status_after_job_completion_internal(
|
||||
.map_err(|e| {
|
||||
Error::internal_err(format!("error while fetching preprocessing args: {e:#}"))
|
||||
})?;
|
||||
let args_hm = args.unwrap_or_default().0;
|
||||
let args = PushArgs::from(&args_hm);
|
||||
Some(args.unwrap_or_default().0)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if concurrency_requires_args {
|
||||
let args = PushArgs::from(fetched_args.as_ref().unwrap());
|
||||
if let Some(ck) = concurrency_key {
|
||||
insert_concurrency_key(
|
||||
&flow_job.workspace_id,
|
||||
@@ -1392,8 +1398,31 @@ pub async fn update_flow_status_after_job_completion_internal(
|
||||
.await?;
|
||||
}
|
||||
|
||||
// let tag = tag_and_concurrency_key.and_then(|tc| tc.tag.map(|t| interpolate_args(t.clone(), &args, &workspace_id)));
|
||||
// let concurrency_key = tag_and_concurrency_key.and_then(|tc| tc.concurrency_key.map(|ck| interpolate_args(&ck, &args, &workspace_id)));
|
||||
let scheduled_for: Option<chrono::DateTime<chrono::Utc>> = {
|
||||
#[cfg(feature = "private")]
|
||||
{
|
||||
if has_debouncing {
|
||||
let empty_hm = HashMap::new();
|
||||
let args = PushArgs::from(fetched_args.as_ref().unwrap_or(&empty_hm));
|
||||
windmill_queue::jobs_ee::maybe_debounce_post_preprocessing(
|
||||
&flow_value.debouncing_settings,
|
||||
&flow_job.runnable_path,
|
||||
&flow_job.workspace_id,
|
||||
flow,
|
||||
&args,
|
||||
db,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "private"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"WITH job_result AS (
|
||||
SELECT result
|
||||
@@ -1403,7 +1432,8 @@ pub async fn update_flow_status_after_job_completion_internal(
|
||||
updated_queue AS (
|
||||
UPDATE v2_job_queue
|
||||
SET running = false,
|
||||
tag = COALESCE($3, tag)
|
||||
tag = COALESCE($3, tag),
|
||||
scheduled_for = COALESCE($6, scheduled_for)
|
||||
WHERE id = $2
|
||||
)
|
||||
UPDATE v2_job
|
||||
@@ -1431,6 +1461,7 @@ pub async fn update_flow_status_after_job_completion_internal(
|
||||
tag,
|
||||
concurrent_limit,
|
||||
concurrency_time_window_s,
|
||||
scheduled_for,
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
|
||||
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
|
||||
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
|
||||
|
||||
export const VERSION = "v1.640.0";
|
||||
export const VERSION = "v1.642.0";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
@jsr:registry=https://npm.jsr.io
|
||||
@@ -0,0 +1,83 @@
|
||||
import { VERSION } from "./src/main.ts";
|
||||
import { readFileSync, writeFileSync, rmSync, cpSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
const outDir = "./npm";
|
||||
|
||||
// Parser npm packages — used as externals and added to generated package.json
|
||||
const parserPackages = [
|
||||
"windmill-parser-wasm-py", "windmill-parser-wasm-ts",
|
||||
"windmill-parser-wasm-regex", "windmill-parser-wasm-go",
|
||||
"windmill-parser-wasm-php", "windmill-parser-wasm-rust",
|
||||
"windmill-parser-wasm-yaml", "windmill-parser-wasm-csharp",
|
||||
"windmill-parser-wasm-nu", "windmill-parser-wasm-java",
|
||||
"windmill-parser-wasm-ruby",
|
||||
];
|
||||
const parserExternals = parserPackages.flatMap(p => ["--external", p]);
|
||||
|
||||
// Clean output directory
|
||||
rmSync(outDir, { recursive: true, force: true });
|
||||
|
||||
// Build with bun — bundle everything except esbuild (platform-specific binary),
|
||||
// svelte (optional, only needed for `wmill app bundle/dev`), and parser packages
|
||||
// (loaded at runtime via init() with readFileSync for the .wasm binary).
|
||||
console.log("Bundling with bun build...");
|
||||
const buildResult = Bun.spawnSync([
|
||||
"bun", "build", "src/main.ts",
|
||||
"--outdir", join(outDir, "esm"),
|
||||
"--target", "node",
|
||||
"--format", "esm",
|
||||
"--external", "esbuild",
|
||||
"--external", "svelte",
|
||||
"--external", "svelte/compiler",
|
||||
...parserExternals,
|
||||
], { cwd: import.meta.dir, stdout: "inherit", stderr: "inherit" });
|
||||
|
||||
if (buildResult.exitCode !== 0) {
|
||||
console.error("Build failed");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Add shebang to main.js
|
||||
const mainJsPath = join(outDir, "esm", "main.js");
|
||||
const mainJs = readFileSync(mainJsPath, "utf-8");
|
||||
writeFileSync(mainJsPath, "#!/usr/bin/env node\n" + mainJs, "utf-8");
|
||||
|
||||
// Copy LICENSE and README
|
||||
cpSync("../LICENSE", join(outDir, "LICENSE"));
|
||||
cpSync("README.md", join(outDir, "README.md"));
|
||||
|
||||
// Generate package.json
|
||||
const packageJson = {
|
||||
name: "windmill-cli",
|
||||
version: VERSION,
|
||||
description: "CLI for Windmill",
|
||||
license: "Apache 2.0",
|
||||
type: "module",
|
||||
main: "esm/main.js",
|
||||
bin: {
|
||||
wmill: "esm/main.js",
|
||||
},
|
||||
repository: {
|
||||
type: "git",
|
||||
url: "git+https://github.com/windmill-labs/windmill.git",
|
||||
},
|
||||
bugs: {
|
||||
url: "https://github.com/windmill-labs/windmill/issues",
|
||||
},
|
||||
dependencies: {
|
||||
esbuild: "^0.24.2",
|
||||
...Object.fromEntries(parserPackages.map(p => [p, "*"])),
|
||||
},
|
||||
optionalDependencies: {
|
||||
svelte: "^5.0.0",
|
||||
},
|
||||
};
|
||||
|
||||
writeFileSync(
|
||||
join(outDir, "package.json"),
|
||||
JSON.stringify(packageJson, null, 2) + "\n",
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
console.log(`Built npm package v${VERSION} to ${outDir}/`);
|
||||
+5
-6
@@ -9,12 +9,11 @@ set -e
|
||||
# Generate utils client files
|
||||
./windmill-utils-internal/gen_wm_client.sh
|
||||
|
||||
# Add .ts extensions to windmill-utils-internal
|
||||
./windmill-utils-internal/remove-ts-ext.sh -r
|
||||
# Install dependencies
|
||||
bun install
|
||||
|
||||
# Run dnt
|
||||
echo "Running dnt..."
|
||||
deno run -A dnt.ts
|
||||
# Build npm package with bun
|
||||
echo "Building npm package..."
|
||||
bun run build-npm.ts
|
||||
|
||||
echo "Build complete!"
|
||||
|
||||
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "windmill-cli-dev",
|
||||
"dependencies": {
|
||||
"@cliffy/ansi": "npm:@jsr/cliffy__ansi@1.0.0",
|
||||
"@cliffy/command": "npm:@jsr/cliffy__command@1.0.0",
|
||||
"@cliffy/prompt": "npm:@jsr/cliffy__prompt@1.0.0",
|
||||
"@cliffy/table": "npm:@jsr/cliffy__table@1.0.0",
|
||||
"@windmill-labs/shared-utils": "^1.0.12",
|
||||
"diff": "^5.2.0",
|
||||
"esbuild": "0.24.2",
|
||||
"get-port": "7.1.0",
|
||||
"jszip": "3.8.0",
|
||||
"minimatch": "^10.0.0",
|
||||
"open": "^10.0.0",
|
||||
"svelte": "^5.45.2",
|
||||
"tar-stream": "^3.1.7",
|
||||
"windmill-parser-wasm-csharp": "*",
|
||||
"windmill-parser-wasm-go": "*",
|
||||
"windmill-parser-wasm-java": "*",
|
||||
"windmill-parser-wasm-nu": "*",
|
||||
"windmill-parser-wasm-php": "*",
|
||||
"windmill-parser-wasm-py": "*",
|
||||
"windmill-parser-wasm-regex": "*",
|
||||
"windmill-parser-wasm-ruby": "*",
|
||||
"windmill-parser-wasm-rust": "*",
|
||||
"windmill-parser-wasm-ts": "*",
|
||||
"windmill-parser-wasm-yaml": "*",
|
||||
"windmill-yaml-validator": "1.1.1",
|
||||
"ws": "8.18.0",
|
||||
"yaml": "^2.7.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/diff": "^5.2.3",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/tar-stream": "^3.1.4",
|
||||
"@types/ws": "^8.5.0",
|
||||
"typescript": "^5.7.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@cliffy/ansi": ["@jsr/cliffy__ansi@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__ansi/1.0.0.tgz", { "dependencies": { "@jsr/cliffy__internal": "1.0.0", "@jsr/std__encoding": "^1.0.10", "@jsr/std__fmt": "^1.0.9", "@jsr/std__io": "~0.225.3" } }, "sha512-JesgTdgR0aW1mZv96VqvRHr2efzr4MgDFMnoT+hkhaiCpmyBz33sHM5peAoMJUbGVfEfQAsysIXvvgoFYoveYg=="],
|
||||
|
||||
"@cliffy/command": ["@jsr/cliffy__command@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__command/1.0.0.tgz", { "dependencies": { "@jsr/cliffy__flags": "1.0.0", "@jsr/cliffy__internal": "1.0.0", "@jsr/cliffy__table": "1.0.0", "@jsr/std__fmt": "^1.0.9", "@jsr/std__semver": "^1.0.8", "@jsr/std__text": "^1.0.17" } }, "sha512-oObplVtu1tvpkhgpuPDHZidx9g3axVOfRMQGmw7ZSGxp0+vZIJGiEtpcSvlN0XfuEhOG8neqfVBSSE9txrKanw=="],
|
||||
|
||||
"@cliffy/prompt": ["@jsr/cliffy__prompt@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__prompt/1.0.0.tgz", { "dependencies": { "@jsr/cliffy__ansi": "1.0.0", "@jsr/cliffy__internal": "1.0.0", "@jsr/cliffy__keycode": "1.0.0", "@jsr/std__assert": "^1.0.18", "@jsr/std__fmt": "^1.0.9", "@jsr/std__io": "~0.225.3", "@jsr/std__path": "^1.1.4", "@jsr/std__text": "^1.0.17" } }, "sha512-JDuHcCAjScV0IUj389brneF6AzJyyP0pK8mymsrGN5/PGQfqK8zr96QpFlo1wmo8BY/3JQAdNfy6NZkPCJ6VWA=="],
|
||||
|
||||
"@cliffy/table": ["@jsr/cliffy__table@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__table/1.0.0.tgz", { "dependencies": { "@jsr/std__fmt": "^1.0.9" } }, "sha512-VoLxH0DjofHWPWKUc5N+oCwXB6O6e+carnhp23yJTa7qokBb+SCrTIABEgQdIe/p0bxgmZhz17xt2efaAxXvbQ=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.24.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.24.2", "", { "os": "android", "cpu": "x64" }, "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.24.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.24.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.24.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.24.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.24.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.24.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.24.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.24.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.24.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.24.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.24.2", "", { "os": "none", "cpu": "arm64" }, "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.24.2", "", { "os": "none", "cpu": "x64" }, "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.24.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.24.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.24.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.24.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.24.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@jsr/cliffy__ansi": ["@jsr/cliffy__ansi@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__ansi/1.0.0.tgz", { "dependencies": { "@jsr/cliffy__internal": "1.0.0", "@jsr/std__encoding": "^1.0.10", "@jsr/std__fmt": "^1.0.9", "@jsr/std__io": "~0.225.3" } }, "sha512-JesgTdgR0aW1mZv96VqvRHr2efzr4MgDFMnoT+hkhaiCpmyBz33sHM5peAoMJUbGVfEfQAsysIXvvgoFYoveYg=="],
|
||||
|
||||
"@jsr/cliffy__flags": ["@jsr/cliffy__flags@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__flags/1.0.0.tgz", { "dependencies": { "@jsr/cliffy__internal": "1.0.0", "@jsr/std__text": "^1.0.17" } }, "sha512-j/v3J8MWu0tkYyisZ2w1HxELxxL/qg6vey9+fRkbTJ+S9J0GeLUn2joouikG7aXpULKCXHTjJ9XH9gQx+F3npw=="],
|
||||
|
||||
"@jsr/cliffy__internal": ["@jsr/cliffy__internal@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__internal/1.0.0.tgz", { "dependencies": { "@jsr/std__fmt": "^1.0.9" } }, "sha512-YPkbccbuu+kE55k+nia5jJx5Tu/IolBDXZTAgEA+YRGOzq8I1VkXajwykFXvSbXeVee3zQBU7y0HajVDB7ujQA=="],
|
||||
|
||||
"@jsr/cliffy__keycode": ["@jsr/cliffy__keycode@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__keycode/1.0.0.tgz", {}, "sha512-1ot+y8oZheBTpfgCazWjSOAK2Y2nOQD7NwMuiSAkcRuc1t7VizQZfDpZtBx97NlkYWgjn6ylArt2xyhiyLKRhA=="],
|
||||
|
||||
"@jsr/cliffy__table": ["@jsr/cliffy__table@1.0.0", "https://npm.jsr.io/~/11/@jsr/cliffy__table/1.0.0.tgz", { "dependencies": { "@jsr/std__fmt": "^1.0.9" } }, "sha512-VoLxH0DjofHWPWKUc5N+oCwXB6O6e+carnhp23yJTa7qokBb+SCrTIABEgQdIe/p0bxgmZhz17xt2efaAxXvbQ=="],
|
||||
|
||||
"@jsr/std__assert": ["@jsr/std__assert@1.0.19", "https://npm.jsr.io/~/11/@jsr/std__assert/1.0.19.tgz", { "dependencies": { "@jsr/std__internal": "^1.0.12" } }, "sha512-pEj6RPkGbqlgRmyKwATp4cUs6+ijxtdrv3bq8v1d2I2CEcMEyPaO8cVKro61wGRDH4cNg8Zx6haztvK/9m7gkA=="],
|
||||
|
||||
"@jsr/std__bytes": ["@jsr/std__bytes@1.0.6", "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.6.tgz", {}, "sha512-St6yKggjFGhxS52IFLJWvkchRFbAKg2Xh8UxA4S1EGz7GJ2Ui+ssDDldj/w2c8vCxvl6qgR0HaYbKeFJNqujmA=="],
|
||||
|
||||
"@jsr/std__encoding": ["@jsr/std__encoding@1.0.10", "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.10.tgz", {}, "sha512-WK2njnDTyKefroRNk2Ooq7GStp6Y0ccAvr4To+Z/zecRAGe7+OSvH9DbiaHpAKwEi2KQbmpWMOYsdNt+TsdmSw=="],
|
||||
|
||||
"@jsr/std__fmt": ["@jsr/std__fmt@1.0.9", "https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.9.tgz", {}, "sha512-YFJJMozmORj2K91c5J9opWeh0VUwrd+Mwb7Pr0FkVCAKVLu2UhT4LyvJqWiyUT+eF+MdfqQ9F7RtQj4bXn9Smw=="],
|
||||
|
||||
"@jsr/std__internal": ["@jsr/std__internal@1.0.12", "https://npm.jsr.io/~/11/@jsr/std__internal/1.0.12.tgz", {}, "sha512-6xReMW9p+paJgqoFRpOE2nogJFvzPfaLHLIlyADYjKMUcwDyjKZxryIbgcU+gxiTygn8yCjld1HoI0ET4/iZeA=="],
|
||||
|
||||
"@jsr/std__io": ["@jsr/std__io@0.225.3", "https://npm.jsr.io/~/11/@jsr/std__io/0.225.3.tgz", { "dependencies": { "@jsr/std__bytes": "^1.0.6" } }, "sha512-IDXY253ipW6FV34CJVxO+3ubfvSEEzw9N2W303KnLe9K/Y9+v/ID1dQYf9VsCCOFMpFtCmOLqzIZsRqv6yQnWw=="],
|
||||
|
||||
"@jsr/std__path": ["@jsr/std__path@1.1.4", "https://npm.jsr.io/~/11/@jsr/std__path/1.1.4.tgz", { "dependencies": { "@jsr/std__internal": "^1.0.12" } }, "sha512-SK4u9H6NVTfolhPdlvdYXfNFefy1W04AEHWJydryYbk+xqzNiVmr5o7TLJLJFqwHXuwMRhwrn+mcYeUfS0YFaA=="],
|
||||
|
||||
"@jsr/std__regexp": ["@jsr/std__regexp@1.0.1", "https://npm.jsr.io/~/11/@jsr/std__regexp/1.0.1.tgz", {}, "sha512-AnGeP//DHpPvhCWjI5dR4o013JhCQioD8yMF8drD7PWb0X4kvmO35hbZi+NZhfSolz4Ts2cpPzJY+DUpi2XE9A=="],
|
||||
|
||||
"@jsr/std__semver": ["@jsr/std__semver@1.0.8", "https://npm.jsr.io/~/11/@jsr/std__semver/1.0.8.tgz", {}, "sha512-YhkykPU2Majz66e+rQbP0okYc7kKv+U32aguLPCXZZAL+vEVmBA+khHjPHhLBpWR073gzU3WHqGRgB7a/aXCjg=="],
|
||||
|
||||
"@jsr/std__text": ["@jsr/std__text@1.0.17", "https://npm.jsr.io/~/11/@jsr/std__text/1.0.17.tgz", { "dependencies": { "@jsr/std__regexp": "^1.0.1" } }, "sha512-oZsihl1bcTy1Ixzven8rin8kjChj1zDJWqgpS0oSMGCJDzyB365gtIfAvcMmji+M+FcIWo3goDXfHcFYt+k/kg=="],
|
||||
|
||||
"@stoplight/ordered-object-literal": ["@stoplight/ordered-object-literal@1.0.5", "", {}, "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg=="],
|
||||
|
||||
"@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="],
|
||||
|
||||
"@stoplight/yaml": ["@stoplight/yaml@4.3.0", "", { "dependencies": { "@stoplight/ordered-object-literal": "^1.0.5", "@stoplight/types": "^14.1.1", "@stoplight/yaml-ast-parser": "0.0.50", "tslib": "^2.2.0" } }, "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w=="],
|
||||
|
||||
"@stoplight/yaml-ast-parser": ["@stoplight/yaml-ast-parser@0.0.50", "", {}, "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ=="],
|
||||
|
||||
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="],
|
||||
|
||||
"@types/diff": ["@types/diff@5.2.3", "", {}, "sha512-K0Oqlrq3kQMaO2RhfrNQX5trmt+XLyom88zS0u84nnIcLvFnRUMRRHmrGny5GSM+kNO9IZLARsdQHDzkhAgmrQ=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/node": ["@types/node@22.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="],
|
||||
|
||||
"@types/tar-stream": ["@types/tar-stream@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-921gW0+g29mCJX0fRvqeHzBlE/XclDaAG0Ousy1LCghsOhvaKacDeRGEVzQP9IPfKn8Vysy7FEXAIxycpc/CMg=="],
|
||||
|
||||
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"@windmill-labs/shared-utils": ["@windmill-labs/shared-utils@1.0.12", "", {}, "sha512-n68uEYv2B5q2Pp8J9syMS3qPZbppFEfeM7HIBEUfU5lGqi3hwnv4mPvgRUyb6K9im3frXC4gzdIdZdlrDpudXQ=="],
|
||||
|
||||
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||
|
||||
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||
|
||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
|
||||
"b4a": ["b4a@1.8.0", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg=="],
|
||||
|
||||
"balanced-match": ["balanced-match@4.0.3", "", {}, "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g=="],
|
||||
|
||||
"bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="],
|
||||
|
||||
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||
|
||||
"default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
|
||||
|
||||
"default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="],
|
||||
|
||||
"define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
|
||||
|
||||
"devalue": ["devalue@5.6.3", "", {}, "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg=="],
|
||||
|
||||
"diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="],
|
||||
|
||||
"esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="],
|
||||
|
||||
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
|
||||
|
||||
"esrap": ["esrap@2.2.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ=="],
|
||||
|
||||
"events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||
|
||||
"get-port": ["get-port@7.1.0", "", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="],
|
||||
|
||||
"immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
|
||||
|
||||
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
|
||||
|
||||
"is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
|
||||
|
||||
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"jszip": ["jszip@3.8.0", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "set-immediate-shim": "~1.0.1" } }, "sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw=="],
|
||||
|
||||
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
||||
|
||||
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"minimatch": ["minimatch@10.2.2", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw=="],
|
||||
|
||||
"open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
||||
|
||||
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
||||
|
||||
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
||||
|
||||
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"set-immediate-shim": ["set-immediate-shim@1.0.1", "", {}, "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ=="],
|
||||
|
||||
"streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
||||
"svelte": ["svelte@5.53.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.3", "esm-env": "^1.2.1", "esrap": "^2.2.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-WzxFHZhhD23Qzu7JCYdvm1rxvRSzdt9HtHO8TScMBX51bLRFTcJmATVqjqXG+6Ln6hrViGCo9DzwOhAasxwC/w=="],
|
||||
|
||||
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
||||
|
||||
"text-decoder": ["text-decoder@1.2.7", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"utility-types": ["utility-types@3.11.0", "", {}, "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw=="],
|
||||
|
||||
"windmill-parser-wasm-csharp": ["windmill-parser-wasm-csharp@1.510.1", "", {}, "sha512-qm09YmnbeYHLwYn1jUnObVzPhYO9NZKMlIO7nlo7zPJBXqksgG5fK/KCtwGw9rChrnz+DsvM9wP5FhrwRLMtwQ=="],
|
||||
|
||||
"windmill-parser-wasm-go": ["windmill-parser-wasm-go@1.510.1", "", {}, "sha512-HOkk6LXK0wrwvkn+zjm3Gxo90HmyL6TYqmLo2yp8fZuppy7GOngT27zwYeBtwONiPyvDKskzoqPQoEfd8VuUsQ=="],
|
||||
|
||||
"windmill-parser-wasm-java": ["windmill-parser-wasm-java@1.510.1", "", {}, "sha512-Zle+JZT/ZwUArUVacUudYlS+CaHp2lSnkqD/IhWaRUG+gcv26VbERnrrHPonqXbVMS+eA9ElfXrFM5j0ukaXUw=="],
|
||||
|
||||
"windmill-parser-wasm-nu": ["windmill-parser-wasm-nu@1.510.1", "", {}, "sha512-AJLFiUy6af+LpUe7CddDo4+JOmw3c0K/1iOWh8NdTwXcLDj90lL6089mdsVo1apyloLgrTbcuFDzZMXVGBgtCg=="],
|
||||
|
||||
"windmill-parser-wasm-php": ["windmill-parser-wasm-php@1.574.1", "", {}, "sha512-COyid6B1RYs+bpzUCInsA4HY/WZkpDLfkQ90+AqU/TVTpzYSbAC2JCbIwy0cRElBvlhI4bQ+9Wg6hSQKMpEkpA=="],
|
||||
|
||||
"windmill-parser-wasm-py": ["windmill-parser-wasm-py@1.628.3", "", {}, "sha512-TlluqknZpg8cZ+A3m6JFLPseY2PpKtDsxdj26fAnCUzKPtse8TxQR+n0dwC80rfW5TwdWSulvNGRDgcNuf7CTw=="],
|
||||
|
||||
"windmill-parser-wasm-regex": ["windmill-parser-wasm-regex@1.639.0", "", {}, "sha512-qvYM4sYxB6M0xrqwBljS2fWqOMk6rp++60TRltJnzZDzVaWQrKjTGwNMmfepGAIWy1OGVKp0SCVERhe2P+O6tQ=="],
|
||||
|
||||
"windmill-parser-wasm-ruby": ["windmill-parser-wasm-ruby@1.526.1", "", {}, "sha512-rMBQA8s21wmL2kA5ztRs/ZgVA3ckxe9/NLjxl3iQPL0CX6DlvfaUH0O+AnhpXXDMyBs1Y1SZIhcnbnvsHZ3R8g=="],
|
||||
|
||||
"windmill-parser-wasm-rust": ["windmill-parser-wasm-rust@1.558.1", "", {}, "sha512-21S7lm1KF8zO1187rbq14hzPHII2RdM2+D44MoAh1F6VoaScj+Puq0z5B1O/hwn/95R/a9jBlL2D8jbkXtlD1A=="],
|
||||
|
||||
"windmill-parser-wasm-ts": ["windmill-parser-wasm-ts@1.623.1", "", {}, "sha512-FBwi/zXxjhZcCvi04oFdNivazru1ynIqSbafHSArfaaBWesBO3nye9UO/WXUlWZm5a7BExbU+3R/eVJrGaornw=="],
|
||||
|
||||
"windmill-parser-wasm-yaml": ["windmill-parser-wasm-yaml@1.593.0", "", {}, "sha512-Gyx4aR2jsJYuDrD3mCNTmz7LWOQQXPw5yKNCC1xRgUOPfjsD/tINAFfsBLwVOSmlQQcFZO+wHm4KtDtXOcnGVw=="],
|
||||
|
||||
"windmill-yaml-validator": ["windmill-yaml-validator@1.1.1", "", { "dependencies": { "@stoplight/yaml": "^4.3.0", "ajv": "^8.17.1" } }, "sha512-CVgAwEoBdJhF39q2N012QffhlGPRIyIWd8gj7NnfG+/lMWgH2k5CBLtKIt6cPF8Bxz+6DGC3st1ARSsecDtbTg=="],
|
||||
|
||||
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
|
||||
|
||||
"wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||
|
||||
"yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
|
||||
|
||||
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
[test]
|
||||
preload = ["./test/setup.ts"]
|
||||
timeout = 60000
|
||||
root = "./test"
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"imports": {
|
||||
"@cliffy/ansi": "jsr:@windmill-labs/cliffy-ansi@^1.0.0-rc.5",
|
||||
"@cliffy/command": "jsr:@windmill-labs/cliffy-command@^1.0.0-rc.5",
|
||||
"@cliffy/prompt": "jsr:@windmill-labs/cliffy-prompt@^1.0.0-rc.6",
|
||||
"@cliffy/table": "jsr:@windmill-labs/cliffy-table@^1.0.0-rc.5",
|
||||
"@deno/dnt": "jsr:@deno/dnt@^0.41.3",
|
||||
"@std/encoding": "jsr:@std/encoding@^1.0.10",
|
||||
"@std/fs": "jsr:@std/fs@^1.0.21",
|
||||
"@std/io": "jsr:@std/io@^0.224.9",
|
||||
"@std/log": "jsr:@std/log@^0.224.14",
|
||||
"@std/net": "jsr:@std/net@^1.0.6",
|
||||
"@std/path": "jsr:@std/path@^1.1.4",
|
||||
"@std/streams": "jsr:@std/streams@^1.0.16",
|
||||
"@std/yaml": "jsr:@std/yaml@^1.0.10",
|
||||
"@types/diff": "npm:@types/diff@^5.2.3",
|
||||
"ws": "npm:ws@8.18.0"
|
||||
},
|
||||
"nodeModulesDir": "auto"
|
||||
}
|
||||
Generated
-1806
File diff suppressed because it is too large
Load Diff
-83
@@ -1,83 +0,0 @@
|
||||
// cliffy
|
||||
export { Command } from "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5";
|
||||
export { Table } from "jsr:@windmill-labs/cliffy-table@1.0.0-rc.5";
|
||||
export { colors } from "jsr:@windmill-labs/cliffy-ansi@1.0.0-rc.5/colors";
|
||||
export { Secret } from "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.6/secret";
|
||||
export { Select } from "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.6/select";
|
||||
export { Confirm } from "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.6/confirm";
|
||||
export { Input } from "jsr:@windmill-labs/cliffy-prompt@1.0.0-rc.6/input";
|
||||
export { UpgradeCommand } from "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5/upgrade";
|
||||
export { NpmProvider } from "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5/upgrade/provider/npm";
|
||||
export { Provider } from "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5/upgrade";
|
||||
|
||||
export { CompletionsCommand } from "jsr:@windmill-labs/cliffy-command@1.0.0-rc.5/completions";
|
||||
// std
|
||||
export { ensureDir } from "jsr:@std/fs";
|
||||
export { SEPARATOR as SEP } from "jsr:@std/path";
|
||||
export * as path from "jsr:@std/path";
|
||||
export { encodeHex } from "jsr:@std/encoding@1.0.4";
|
||||
export { writeAllSync } from "jsr:@std/io/write-all";
|
||||
export { copy } from "jsr:@std/io/copy";
|
||||
export { readAll } from "jsr:@std/io/read-all";
|
||||
|
||||
export * as log from "jsr:@std/log";
|
||||
export { stringify as yamlStringify } from "jsr:@std/yaml";
|
||||
|
||||
import { parse as yamlParse, ParseOptions } from "jsr:@std/yaml";
|
||||
|
||||
export async function yamlParseFile(path: string, options: ParseOptions = {}) {
|
||||
try {
|
||||
return yamlParse(await Deno.readTextFile(path), options);
|
||||
} catch (e) {
|
||||
throw new Error(`Error parsing yaml ${path}`, { cause: e });
|
||||
}
|
||||
}
|
||||
|
||||
export function yamlParseContent(
|
||||
path: string,
|
||||
content: string,
|
||||
options: ParseOptions = {},
|
||||
) {
|
||||
try {
|
||||
return yamlParse(content, options);
|
||||
} catch (e) {
|
||||
throw new Error(`Error parsing yaml ${path}`, { cause: e });
|
||||
}
|
||||
}
|
||||
|
||||
// other
|
||||
|
||||
export * as Diff from "npm:diff";
|
||||
export { minimatch } from "npm:minimatch";
|
||||
export { default as JSZip } from "npm:jszip@3.8.0";
|
||||
|
||||
export * as express from "npm:express";
|
||||
export * as http from "node:http";
|
||||
export { WebSocket, WebSocketServer } from "npm:ws";
|
||||
export * as getPort from "npm:get-port@7.1.0";
|
||||
export * as open from "npm:open";
|
||||
export * as esMain from "npm:es-main";
|
||||
export * as windmillUtils from "jsr:@windmill-labs/shared-utils@1.0.12";
|
||||
|
||||
// needed for dnt transform
|
||||
import * as wsTypes from "npm:@types/ws";
|
||||
|
||||
import { OpenAPI } from "./gen/index.ts";
|
||||
|
||||
export function setClient(token?: string, baseUrl?: string) {
|
||||
if (baseUrl === undefined) {
|
||||
baseUrl = getEnv("BASE_INTERNAL_URL") ??
|
||||
getEnv("BASE_URL") ??
|
||||
"http://localhost:8000";
|
||||
}
|
||||
if (token === undefined) {
|
||||
token = getEnv("WM_TOKEN") ?? "no_token";
|
||||
}
|
||||
OpenAPI.WITH_CREDENTIALS = true;
|
||||
OpenAPI.TOKEN = token;
|
||||
OpenAPI.BASE = baseUrl + "/api";
|
||||
}
|
||||
|
||||
const getEnv = (key: string) => {
|
||||
return Deno.env.get(key);
|
||||
};
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
// ex. scripts/build_npm.ts
|
||||
import { build, emptyDir } from "jsr:@deno/dnt@0.42.3";
|
||||
import { VERSION } from "./src/main.ts";
|
||||
await emptyDir("./npm");
|
||||
|
||||
await build({
|
||||
entryPoints: [
|
||||
"src/main.ts",
|
||||
{
|
||||
kind: "bin",
|
||||
name: "wmill", // command name
|
||||
path: "./src/main.ts",
|
||||
},
|
||||
],
|
||||
outDir: "./npm",
|
||||
test: false, // Disable all tests in npm build since they use Deno-specific APIs
|
||||
shims: {
|
||||
// see JS docs for overview and more options
|
||||
deno: true,
|
||||
// shims to only use in the tests
|
||||
customDev: [{
|
||||
// this is what `timers: "dev"` does internally
|
||||
package: {
|
||||
name: "@deno/shim-timers",
|
||||
version: "~0.1.0",
|
||||
},
|
||||
globalNames: ["setTimeout", "setInterval"],
|
||||
}],
|
||||
},
|
||||
scriptModule: false,
|
||||
filterDiagnostic(diagnostic) {
|
||||
if (
|
||||
diagnostic.file?.fileName.includes("node_modules/") ||
|
||||
diagnostic.file?.fileName.includes("src/deps/") ||
|
||||
diagnostic.file?.fileName.includes("src/deps.ts") ||
|
||||
diagnostic.file?.fileName.includes("src/utils/utils.ts")
|
||||
) {
|
||||
return false; // ignore all diagnostics in this file
|
||||
}
|
||||
// console.log(diagnostic.file?.fileName);
|
||||
return true;
|
||||
},
|
||||
declaration: "separate",
|
||||
package: {
|
||||
// package.json properties
|
||||
name: "windmill-cli",
|
||||
version: VERSION,
|
||||
description: "CLI for Windmill",
|
||||
license: "Apache 2.0",
|
||||
main: "esm/main.js",
|
||||
repository: {
|
||||
type: "git",
|
||||
url: "git+https://github.com/windmill-labs/windmill.git",
|
||||
},
|
||||
bugs: {
|
||||
url: "https://github.com/windmill-labs/windmill/issues",
|
||||
},
|
||||
},
|
||||
|
||||
postBuild() {
|
||||
// steps to run after building and before running the tests
|
||||
// add shebang to npm/esm/main.js
|
||||
const dirs = [
|
||||
"nu",
|
||||
"ts",
|
||||
"regex",
|
||||
"py",
|
||||
"go",
|
||||
"php",
|
||||
"rust",
|
||||
"yaml",
|
||||
"csharp",
|
||||
"java",
|
||||
"ruby",
|
||||
// for related places search: ADD_NEW_LANG
|
||||
];
|
||||
|
||||
for (const l of dirs) {
|
||||
Deno.copyFileSync(
|
||||
"wasm/" + l + "/windmill_parser_wasm_bg.wasm",
|
||||
"npm/esm/wasm/" + l + "/windmill_parser_wasm_bg.wasm"
|
||||
);
|
||||
}
|
||||
Deno.copyFileSync("../LICENSE", "npm/LICENSE");
|
||||
Deno.copyFileSync("README.md", "npm/README.md");
|
||||
},
|
||||
});
|
||||
@@ -6,8 +6,8 @@ rm -rf "${script_dirpath}/gen"
|
||||
|
||||
npx --yes @hey-api/openapi-ts@0.53.1 --input "${script_dirpath}/../backend/windmill-api/openapi.yaml" --output "${script_dirpath}/gen" --useOptions --client legacy/fetch --schemas false
|
||||
cat <<EOF - gen/core/OpenAPI.ts > temp_file && mv temp_file gen/core/OpenAPI.ts
|
||||
const getEnv = (key: string) => {
|
||||
return Deno.env.get(key)
|
||||
const getEnv = (key: string): string | undefined => {
|
||||
return process.env[key]
|
||||
};
|
||||
|
||||
const baseUrl = getEnv("BASE_INTERNAL_URL") ?? getEnv("BASE_URL") ?? "http://localhost:8000";
|
||||
|
||||
+48
-7
@@ -2,14 +2,55 @@
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
name="wmill"
|
||||
else
|
||||
name="$1"
|
||||
# Parse options
|
||||
USE_NODE=false
|
||||
name=""
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--node|-node|---node) USE_NODE=true ;;
|
||||
-*) echo "Unknown option: $arg"; echo "Usage: $0 [name] [--node]"; exit 1 ;;
|
||||
*) [ -z "$name" ] && name="$arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$name" ]; then
|
||||
name="wmill-dev"
|
||||
fi
|
||||
|
||||
./gen_wm_client.sh
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
./gen_wm_client.sh
|
||||
./windmill-utils-internal/gen_wm_client.sh
|
||||
|
||||
echo "Installing dev cli as $name (pass arg to override)"
|
||||
deno install -f -A -g src/main.ts --name $name --unstable
|
||||
bun install
|
||||
|
||||
INSTALL_DIR="$HOME/.local/bin"
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
if [ "$USE_NODE" = true ]; then
|
||||
echo "Building npm bundle..."
|
||||
bun run build-npm.ts
|
||||
|
||||
NPM_DIR="$SCRIPT_DIR/npm"
|
||||
cd "$NPM_DIR" && npm install
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
cat > "$INSTALL_DIR/$name" <<EOF
|
||||
#!/bin/sh
|
||||
exec node "$NPM_DIR/esm/main.js" "\$@"
|
||||
EOF
|
||||
else
|
||||
cat > "$INSTALL_DIR/$name" <<EOF
|
||||
#!/bin/sh
|
||||
exec bun run "$SCRIPT_DIR/src/main.ts" "\$@"
|
||||
EOF
|
||||
fi
|
||||
|
||||
chmod +x "$INSTALL_DIR/$name"
|
||||
|
||||
echo "Installed dev cli as '$name' at $INSTALL_DIR/$name"
|
||||
|
||||
if ! echo "$PATH" | tr ':' '\n' | grep -qx "$INSTALL_DIR"; then
|
||||
echo "Warning: $INSTALL_DIR is not in your PATH. Add it with:"
|
||||
echo " export PATH=\"$INSTALL_DIR:\$PATH\""
|
||||
fi
|
||||
|
||||
Generated
+1498
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "wmill-dev",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"wmill": "src/main.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bun run src/main.ts",
|
||||
"build": "./build.sh",
|
||||
"test": "bun test test/",
|
||||
"check": "bunx tsc --noEmit",
|
||||
"gen-client": "./gen_wm_client.sh && ./windmill-utils-internal/gen_wm_client.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cliffy/ansi": "npm:@jsr/cliffy__ansi@1.0.0",
|
||||
"@cliffy/command": "npm:@jsr/cliffy__command@1.0.0",
|
||||
"@cliffy/prompt": "npm:@jsr/cliffy__prompt@1.0.0",
|
||||
"@cliffy/table": "npm:@jsr/cliffy__table@1.0.0",
|
||||
"@windmill-labs/shared-utils": "^1.0.12",
|
||||
"diff": "^5.2.0",
|
||||
"esbuild": "0.24.2",
|
||||
"get-port": "7.1.0",
|
||||
"jszip": "3.8.0",
|
||||
"minimatch": "^10.0.0",
|
||||
"open": "^10.0.0",
|
||||
"svelte": "^5.45.2",
|
||||
"tar-stream": "^3.1.7",
|
||||
"windmill-parser-wasm-csharp": "*",
|
||||
"windmill-parser-wasm-go": "*",
|
||||
"windmill-parser-wasm-java": "*",
|
||||
"windmill-parser-wasm-nu": "*",
|
||||
"windmill-parser-wasm-php": "*",
|
||||
"windmill-parser-wasm-py": "*",
|
||||
"windmill-parser-wasm-regex": "*",
|
||||
"windmill-parser-wasm-ruby": "*",
|
||||
"windmill-parser-wasm-rust": "*",
|
||||
"windmill-parser-wasm-ts": "*",
|
||||
"windmill-parser-wasm-yaml": "*",
|
||||
"windmill-yaml-validator": "1.1.1",
|
||||
"ws": "8.18.0",
|
||||
"yaml": "^2.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/diff": "^5.2.3",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/tar-stream": "^3.1.4",
|
||||
"@types/ws": "^8.5.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
}
|
||||
+42
-17
@@ -1,15 +1,12 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
import { resolveWorkspace, validatePath } from "../../core/context.ts";
|
||||
import {
|
||||
colors,
|
||||
Command,
|
||||
log,
|
||||
SEP,
|
||||
Table,
|
||||
windmillUtils,
|
||||
yamlParseFile,
|
||||
} from "../../../deps.ts";
|
||||
import { Command } from "@cliffy/command";
|
||||
import { Table } from "@cliffy/table";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { sep as SEP } from "node:path";
|
||||
import * as windmillUtils from "@windmill-labs/shared-utils";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
import { ListableApp, Policy } from "../../../gen/types.gen.ts";
|
||||
|
||||
@@ -188,7 +185,7 @@ export async function generatingPolicy(
|
||||
}
|
||||
}
|
||||
|
||||
async function list(opts: GlobalOptions & { includeDraftOnly?: boolean }) {
|
||||
async function list(opts: GlobalOptions & { includeDraftOnly?: boolean; json?: boolean }) {
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
|
||||
@@ -209,12 +206,32 @@ async function list(opts: GlobalOptions & { includeDraftOnly?: boolean }) {
|
||||
}
|
||||
}
|
||||
|
||||
new Table()
|
||||
.header(["path", "summary"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(total.map((x) => [x.path, x.summary]))
|
||||
.render();
|
||||
if (opts.json) {
|
||||
console.log(JSON.stringify(total));
|
||||
} else {
|
||||
new Table()
|
||||
.header(["path", "summary"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(total.map((x) => [x.path, x.summary]))
|
||||
.render();
|
||||
}
|
||||
}
|
||||
|
||||
async function get(opts: GlobalOptions & { json?: boolean }, path: string) {
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
const a = await wmill.getAppByPath({
|
||||
workspace: workspace.workspaceId,
|
||||
path,
|
||||
});
|
||||
if (opts.json) {
|
||||
console.log(JSON.stringify(a));
|
||||
} else {
|
||||
console.log(colors.bold("Path:") + " " + a.path);
|
||||
console.log(colors.bold("Summary:") + " " + (a.summary ?? ""));
|
||||
console.log(colors.bold("Created by:") + " " + (a.created_by ?? ""));
|
||||
}
|
||||
}
|
||||
|
||||
async function push(opts: GlobalOptions, filePath: string, remotePath: string) {
|
||||
@@ -230,7 +247,15 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) {
|
||||
|
||||
const command = new Command()
|
||||
.description("app related commands")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(list as any)
|
||||
.command("list", "list all apps")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(list as any)
|
||||
.command("get", "get an app's details")
|
||||
.arguments("<path:string>")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(get as any)
|
||||
.command("push", "push a local app ")
|
||||
.arguments("<file_path:string> <remote_path:string>")
|
||||
.action(push as any)
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import path from "node:path";
|
||||
import {
|
||||
SEP,
|
||||
colors,
|
||||
log,
|
||||
yamlParseFile,
|
||||
yamlStringify,
|
||||
} from "../../../deps.ts";
|
||||
import { readFile, mkdir } from "node:fs/promises";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { sep as SEP } from "node:path";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import { stringify as yamlStringify } from "yaml";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import {
|
||||
checkifMetadataUptodate,
|
||||
@@ -86,7 +84,7 @@ async function generateAppHash(
|
||||
}
|
||||
} catch (error: any) {
|
||||
// If runnables folder doesn't exist, that's okay
|
||||
if (error.name !== "NotFound") {
|
||||
if (error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -351,7 +349,7 @@ async function updateRawAppRunnables(
|
||||
|
||||
// Ensure runnables folder exists
|
||||
try {
|
||||
await Deno.mkdir(runnablesFolder, { recursive: true });
|
||||
await mkdir(runnablesFolder, { recursive: true });
|
||||
} catch {
|
||||
// Folder may already exist
|
||||
}
|
||||
@@ -736,7 +734,7 @@ export async function inferRunnableSchemaFromFile(
|
||||
);
|
||||
let content: string;
|
||||
try {
|
||||
content = await Deno.readTextFile(fullFilePath);
|
||||
content = await readFile(fullFilePath, "utf-8");
|
||||
} catch {
|
||||
log.warn(colors.yellow(`Could not read file: ${fullFilePath}`));
|
||||
return undefined;
|
||||
@@ -786,7 +784,7 @@ export async function generateLocksCommand(
|
||||
const { generateAppLocksInternal } = await import("./app_metadata.ts");
|
||||
const { elementsToMap, FSFSElement } = await import("../sync/sync.ts");
|
||||
const { ignoreF } = await import("../sync/sync.ts");
|
||||
const { Confirm } = await import("../../../deps.ts");
|
||||
const { Confirm } = await import("@cliffy/prompt/confirm");
|
||||
|
||||
if (appPath == "") {
|
||||
appPath = undefined;
|
||||
@@ -813,7 +811,7 @@ export async function generateLocksCommand(
|
||||
// Generate metadata for all apps
|
||||
const ignore = await ignoreF(opts);
|
||||
const elems = await elementsToMap(
|
||||
await FSFSElement(Deno.cwd(), [], true),
|
||||
await FSFSElement(process.cwd(), [], true),
|
||||
(p, isD) => {
|
||||
return (
|
||||
ignore(p, isD) ||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import process from "node:process";
|
||||
import { spawn } from "node:child_process";
|
||||
import { log, colors } from "../../../deps.ts";
|
||||
import { windmillUtils } from "../../../deps.ts";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as windmillUtils from "@windmill-labs/shared-utils";
|
||||
export interface BundleOptions {
|
||||
entryPoint?: string;
|
||||
outDir?: string;
|
||||
@@ -66,7 +66,7 @@ function createSveltePlugin(appDir: string): any {
|
||||
setup(build: any) {
|
||||
build.onLoad({ filter: /\.svelte$/ }, async (args: any) => {
|
||||
// Import svelte compiler from the project's node_modules
|
||||
const svelte = await import("npm:svelte@5.45.2/compiler");
|
||||
const svelte = await import("svelte/compiler");
|
||||
|
||||
// Load the file from the file system
|
||||
const source = await fs.promises.readFile(args.path, "utf8");
|
||||
@@ -118,7 +118,7 @@ export async function createFrameworkPlugins(appDir: string): Promise<any[]> {
|
||||
log.info(colors.blue("🔧 Vue detected, adding vue plugin..."));
|
||||
throw new Error("Vue plugin not supported yet");
|
||||
// try {
|
||||
// const esbuildPluginVue = await import("npm:esbuild-plugin-vue3@0.5.1");
|
||||
// const esbuildPluginVue = await import("esbuild-plugin-vue3");
|
||||
// plugins.push(esbuildPluginVue.default());
|
||||
// } catch (error: any) {
|
||||
// log.warn(colors.yellow(`Failed to load vue plugin: ${error.message}`));
|
||||
@@ -164,7 +164,7 @@ export async function createBundle(
|
||||
options: BundleOptions = {}
|
||||
): Promise<BundleResult> {
|
||||
// Dynamically import esbuild
|
||||
const esbuild = await import("npm:esbuild@0.24.2");
|
||||
const esbuild = await import("esbuild");
|
||||
|
||||
// Detect frameworks to determine default entry point
|
||||
const frameworks = detectFrameworks(process.cwd());
|
||||
|
||||
+112
-135
@@ -1,14 +1,11 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import {
|
||||
colors,
|
||||
Command,
|
||||
getPort,
|
||||
log,
|
||||
open,
|
||||
SEP,
|
||||
windmillUtils,
|
||||
yamlParseFile,
|
||||
} from "../../../deps.ts";
|
||||
import { Command } from "@cliffy/command";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { sep as SEP } from "node:path";
|
||||
import * as windmillUtils from "@windmill-labs/shared-utils";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import * as getPort from "get-port";
|
||||
import * as open from "open";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import * as http from "node:http";
|
||||
import * as fs from "node:fs";
|
||||
@@ -16,7 +13,8 @@ import * as path from "node:path";
|
||||
import process from "node:process";
|
||||
import { Buffer } from "node:buffer";
|
||||
import { writeFileSync } from "node:fs";
|
||||
import { WebSocket, WebSocketServer } from "npm:ws";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { WebSocket, WebSocketServer } from "ws";
|
||||
import {
|
||||
createFrameworkPlugins,
|
||||
detectFrameworks,
|
||||
@@ -336,7 +334,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
log.error(colors.red(`Error: Directory not found: ${targetDir}`));
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +353,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
}' or specify one as argument.`,
|
||||
),
|
||||
);
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check for raw_app.yaml in target directory
|
||||
@@ -369,7 +367,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
} folder containing a raw_app.yaml file.`,
|
||||
),
|
||||
);
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Resolve workspace and authenticate (from original cwd to find wmill.yaml)
|
||||
@@ -387,7 +385,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
const appPath = rawApp?.custom_path ?? "u/unknown/newapp";
|
||||
|
||||
// Dynamically import esbuild only when the dev command is called
|
||||
const esbuild = await import("npm:esbuild@0.24.2");
|
||||
const esbuild = await import("esbuild");
|
||||
|
||||
const port = opts.port ??
|
||||
(await getPort.default({
|
||||
@@ -410,7 +408,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
`Entry point "${entryPoint}" not found. Please specify a valid entry point with --entry.`,
|
||||
),
|
||||
);
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Ensure node_modules exists
|
||||
@@ -525,99 +523,85 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
|
||||
// Watch runnables folder for changes
|
||||
const runnablesPath = path.join(process.cwd(), APP_BACKEND_FOLDER);
|
||||
let runnablesWatcher: Deno.FsWatcher | undefined;
|
||||
let runnablesWatcher: fs.FSWatcher | undefined;
|
||||
|
||||
if (fs.existsSync(runnablesPath)) {
|
||||
log.info(
|
||||
colors.blue(`👁️ Watching runnables folder at: ${runnablesPath}\n`),
|
||||
);
|
||||
runnablesWatcher = Deno.watchFs(runnablesPath);
|
||||
runnablesWatcher = fs.watch(runnablesPath, { recursive: true });
|
||||
|
||||
// Per-file debounce timeouts for schema inference (longer debounce for typing)
|
||||
const schemaInferenceTimeouts: Record<string, ReturnType<typeof setTimeout>> = {};
|
||||
const SCHEMA_DEBOUNCE_MS = 500; // Wait 500ms after last change before inferring schema
|
||||
|
||||
// Handle runnables file changes in the background
|
||||
(async () => {
|
||||
try {
|
||||
for await (const event of runnablesWatcher!) {
|
||||
// Process each changed path with individual debouncing
|
||||
for (const changedPath of event.paths) {
|
||||
const relativePath = path.relative(process.cwd(), changedPath);
|
||||
const relativeToRunnables = path.relative(
|
||||
runnablesPath,
|
||||
changedPath,
|
||||
);
|
||||
// Handle runnables file changes via callback
|
||||
runnablesWatcher.on("change", (_eventType, filename) => {
|
||||
if (!filename) return;
|
||||
const fileStr = typeof filename === "string" ? filename : filename.toString();
|
||||
const changedPath = path.join(runnablesPath, fileStr);
|
||||
const relativePath = path.relative(process.cwd(), changedPath);
|
||||
const relativeToRunnables = fileStr;
|
||||
|
||||
// Skip non-modify events for schema inference
|
||||
if (event.kind !== "modify" && event.kind !== "create") {
|
||||
continue;
|
||||
}
|
||||
// Skip lock files
|
||||
if (changedPath.endsWith(".lock")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip lock files
|
||||
if (changedPath.endsWith(".lock")) {
|
||||
continue;
|
||||
}
|
||||
// Log the change event
|
||||
log.info(
|
||||
colors.cyan(
|
||||
`📝 Runnable changed [${_eventType}]: ${relativePath}`,
|
||||
),
|
||||
);
|
||||
|
||||
// Log the change event
|
||||
// Debounce schema inference per file (wait for typing to finish)
|
||||
if (schemaInferenceTimeouts[changedPath]) {
|
||||
clearTimeout(schemaInferenceTimeouts[changedPath]);
|
||||
}
|
||||
|
||||
schemaInferenceTimeouts[changedPath] = setTimeout(async () => {
|
||||
delete schemaInferenceTimeouts[changedPath];
|
||||
|
||||
try {
|
||||
log.info(
|
||||
colors.cyan(
|
||||
`📝 Inferring schema for: ${relativeToRunnables}`,
|
||||
),
|
||||
);
|
||||
// Infer schema for this runnable (returns schema in memory, doesn't write to file)
|
||||
const result = await inferRunnableSchemaFromFile(
|
||||
process.cwd(),
|
||||
relativeToRunnables,
|
||||
);
|
||||
if (result) {
|
||||
// Store inferred schema in memory
|
||||
inferredSchemas[result.runnableId] = result.schema;
|
||||
log.info(
|
||||
colors.cyan(
|
||||
`📝 Runnable changed [${event.kind}]: ${relativePath}`,
|
||||
colors.green(
|
||||
` Inferred Schemas: ${
|
||||
JSON.stringify(
|
||||
inferredSchemas,
|
||||
null,
|
||||
2,
|
||||
)
|
||||
}`,
|
||||
),
|
||||
);
|
||||
|
||||
// Debounce schema inference per file (wait for typing to finish)
|
||||
if (schemaInferenceTimeouts[changedPath]) {
|
||||
clearTimeout(schemaInferenceTimeouts[changedPath]);
|
||||
}
|
||||
|
||||
schemaInferenceTimeouts[changedPath] = setTimeout(async () => {
|
||||
delete schemaInferenceTimeouts[changedPath];
|
||||
|
||||
try {
|
||||
log.info(
|
||||
colors.cyan(
|
||||
`📝 Inferring schema for: ${relativeToRunnables}`,
|
||||
),
|
||||
);
|
||||
// Infer schema for this runnable (returns schema in memory, doesn't write to file)
|
||||
const result = await inferRunnableSchemaFromFile(
|
||||
process.cwd(),
|
||||
relativeToRunnables,
|
||||
);
|
||||
if (result) {
|
||||
// log.info(colors.green(` Schema: ${JSON.stringify(result.schema, null, 2)}`));
|
||||
// log.info(colors.green(` Runnable ID: ${result.runnableId}`));
|
||||
// Store inferred schema in memory
|
||||
inferredSchemas[result.runnableId] = result.schema;
|
||||
log.info(
|
||||
colors.green(
|
||||
` Inferred Schemas: ${
|
||||
JSON.stringify(
|
||||
inferredSchemas,
|
||||
null,
|
||||
2,
|
||||
)
|
||||
}`,
|
||||
),
|
||||
);
|
||||
// Regenerate wmill.d.ts with updated schema from memory
|
||||
await genRunnablesTs(inferredSchemas);
|
||||
}
|
||||
} catch (error: any) {
|
||||
log.error(
|
||||
colors.red(`Error inferring schema: ${error.message}`),
|
||||
);
|
||||
}
|
||||
}, SCHEMA_DEBOUNCE_MS);
|
||||
// Regenerate wmill.d.ts with updated schema from memory
|
||||
await genRunnablesTs(inferredSchemas);
|
||||
}
|
||||
} catch (error: any) {
|
||||
log.error(
|
||||
colors.red(`Error inferring schema: ${error.message}`),
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.name !== "Interrupted") {
|
||||
log.error(colors.red(`Error watching runnables: ${error.message}`));
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, SCHEMA_DEBOUNCE_MS);
|
||||
});
|
||||
|
||||
runnablesWatcher.on("error", (error: Error) => {
|
||||
log.error(colors.red(`Error watching runnables: ${error.message}`));
|
||||
});
|
||||
} else {
|
||||
log.info(
|
||||
colors.gray(
|
||||
@@ -781,7 +765,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
const fileName = path.basename(filePath);
|
||||
|
||||
try {
|
||||
const sqlContent = await Deno.readTextFile(filePath);
|
||||
const sqlContent = await readFile(filePath, "utf-8");
|
||||
|
||||
if (!sqlContent.trim()) {
|
||||
log.info(colors.gray(`Skipping empty file: ${fileName}`));
|
||||
@@ -837,7 +821,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
// If there's a current SQL file being shown, send it to the new client
|
||||
if (currentSqlFile && fs.existsSync(currentSqlFile)) {
|
||||
try {
|
||||
const sqlContent = await Deno.readTextFile(currentSqlFile);
|
||||
const sqlContent = await readFile(currentSqlFile, "utf-8");
|
||||
const datatable = await getDatatableConfig();
|
||||
const fileName = path.basename(currentSqlFile);
|
||||
|
||||
@@ -1164,7 +1148,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
});
|
||||
|
||||
// Watch sql_to_apply folder for SQL migration files
|
||||
let sqlWatcher: Deno.FsWatcher | undefined;
|
||||
let sqlWatcher: fs.FSWatcher | undefined;
|
||||
|
||||
// Helper to scan for existing SQL files and add them to the queue
|
||||
async function scanExistingSqlFiles(): Promise<void> {
|
||||
@@ -1207,53 +1191,46 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
log.info(
|
||||
colors.blue(`🗃️ Watching sql_to_apply folder at: ${sqlToApplyPath}\n`),
|
||||
);
|
||||
sqlWatcher = Deno.watchFs(sqlToApplyPath);
|
||||
sqlWatcher = fs.watch(sqlToApplyPath, { recursive: true });
|
||||
|
||||
// Debounce timeout for SQL file changes
|
||||
const sqlDebounceTimeouts: Record<string, ReturnType<typeof setTimeout>> = {};
|
||||
const SQL_DEBOUNCE_MS = 300;
|
||||
|
||||
// Handle SQL file changes in the background
|
||||
(async () => {
|
||||
try {
|
||||
for await (const event of sqlWatcher!) {
|
||||
for (const changedPath of event.paths) {
|
||||
// Only handle .sql files
|
||||
if (!changedPath.endsWith(".sql")) {
|
||||
continue;
|
||||
}
|
||||
// Handle SQL file changes via callback
|
||||
sqlWatcher.on("change", (_eventType, filename) => {
|
||||
if (!filename) return;
|
||||
const fileStr = typeof filename === "string" ? filename : filename.toString();
|
||||
const changedPath = path.join(sqlToApplyPath, fileStr);
|
||||
|
||||
// Only handle modify and create events
|
||||
if (event.kind !== "modify" && event.kind !== "create") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fileName = path.basename(changedPath);
|
||||
|
||||
// Debounce per file
|
||||
if (sqlDebounceTimeouts[changedPath]) {
|
||||
clearTimeout(sqlDebounceTimeouts[changedPath]);
|
||||
}
|
||||
|
||||
sqlDebounceTimeouts[changedPath] = setTimeout(async () => {
|
||||
delete sqlDebounceTimeouts[changedPath];
|
||||
|
||||
log.info(colors.cyan(`📋 SQL file detected: ${fileName}`));
|
||||
|
||||
// Add to queue and process
|
||||
queueSqlFile(changedPath);
|
||||
await processNextSqlFile();
|
||||
}, SQL_DEBOUNCE_MS);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.name !== "Interrupted") {
|
||||
log.error(
|
||||
colors.red(`Error watching sql_to_apply: ${error.message}`),
|
||||
);
|
||||
}
|
||||
// Only handle .sql files
|
||||
if (!changedPath.endsWith(".sql")) {
|
||||
return;
|
||||
}
|
||||
})();
|
||||
|
||||
const fileName = path.basename(changedPath);
|
||||
|
||||
// Debounce per file
|
||||
if (sqlDebounceTimeouts[changedPath]) {
|
||||
clearTimeout(sqlDebounceTimeouts[changedPath]);
|
||||
}
|
||||
|
||||
sqlDebounceTimeouts[changedPath] = setTimeout(async () => {
|
||||
delete sqlDebounceTimeouts[changedPath];
|
||||
|
||||
log.info(colors.cyan(`📋 SQL file detected: ${fileName}`));
|
||||
|
||||
// Add to queue and process
|
||||
queueSqlFile(changedPath);
|
||||
await processNextSqlFile();
|
||||
}, SQL_DEBOUNCE_MS);
|
||||
});
|
||||
|
||||
sqlWatcher.on("error", (error: Error) => {
|
||||
log.error(
|
||||
colors.red(`Error watching sql_to_apply: ${error.message}`),
|
||||
);
|
||||
});
|
||||
|
||||
// Scan for existing SQL files after a delay (to let WebSocket clients connect)
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { colors, Command, log, yamlParseFile } from "../../../deps.ts";
|
||||
import * as fs from "node:fs";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
import { Command } from "@cliffy/command";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import { resolveWorkspace } from "../../core/context.ts";
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
import { DataTableSchema } from "../../../gen/types.gen.ts";
|
||||
import { generateAgentsDocumentation } from "../sync/sync.ts";
|
||||
import path from "node:path";
|
||||
import * as fs from "node:fs";
|
||||
import {
|
||||
getFolderSuffix,
|
||||
hasFolderSuffix,
|
||||
@@ -192,14 +198,14 @@ export async function regenerateAgentDocs(
|
||||
|
||||
// Generate and write AGENTS.md
|
||||
const agentsContent = generateAgentsDocumentation(localData);
|
||||
await Deno.writeTextFile(path.join(targetDir, "AGENTS.md"), agentsContent);
|
||||
await writeFile(path.join(targetDir, "AGENTS.md"), agentsContent, "utf-8");
|
||||
|
||||
// Generate and write CLAUDE.md referencing AGENTS.md
|
||||
await Deno.writeTextFile(path.join(targetDir, "CLAUDE.md"), `Instructions are in @AGENTS.md\n`);
|
||||
await writeFile(path.join(targetDir, "CLAUDE.md"), `Instructions are in @AGENTS.md\n`, "utf-8");
|
||||
|
||||
// Generate and write DATATABLES.md
|
||||
const datatablesContent = generateDatatablesMarkdown(schemas, localData);
|
||||
await Deno.writeTextFile(path.join(targetDir, "DATATABLES.md"), datatablesContent);
|
||||
await writeFile(path.join(targetDir, "DATATABLES.md"), datatablesContent, "utf-8");
|
||||
|
||||
if (!silent) {
|
||||
log.info(colors.green(`✓ Generated AGENTS.md, CLAUDE.md, and DATATABLES.md`));
|
||||
@@ -229,7 +235,7 @@ async function generateAgents(
|
||||
appFolder?: string
|
||||
) {
|
||||
// Resolve the app folder
|
||||
const cwd = Deno.cwd();
|
||||
const cwd = process.cwd();
|
||||
let targetDir = cwd;
|
||||
|
||||
if (appFolder) {
|
||||
@@ -252,7 +258,7 @@ async function generateAgents(
|
||||
)
|
||||
);
|
||||
log.info(colors.gray("Usage: wmill app generate-agents [app_folder]"));
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +268,7 @@ async function generateAgents(
|
||||
log.error(
|
||||
colors.red(`Error: raw_app.yaml not found in ${targetDir}`)
|
||||
);
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Resolve workspace and authenticate
|
||||
@@ -272,7 +278,6 @@ async function generateAgents(
|
||||
await regenerateAgentDocs(workspace.workspaceId, targetDir);
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const command = new Command()
|
||||
.description("regenerate AGENTS.md and DATATABLES.md from remote workspace")
|
||||
.arguments("[app_folder:string]")
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import process from "node:process";
|
||||
import { colors, Command, log, yamlParseFile } from "../../../deps.ts";
|
||||
import { Command } from "@cliffy/command";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import { createBundle } from "./bundle.ts";
|
||||
import { APP_BACKEND_FOLDER } from "./app_metadata.ts";
|
||||
@@ -224,7 +226,7 @@ async function lint(opts: LintOptions, appFolder?: string) {
|
||||
log.info(colors.red(` - ${error}`));
|
||||
});
|
||||
log.info(colors.red("\n❌ Lint failed\n"));
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log.info(colors.green("\n✅ All checks passed\n"));
|
||||
|
||||
+29
-32
@@ -1,13 +1,11 @@
|
||||
import {
|
||||
colors,
|
||||
Command,
|
||||
Confirm,
|
||||
ensureDir,
|
||||
Input,
|
||||
log,
|
||||
Select,
|
||||
yamlStringify,
|
||||
} from "../../../deps.ts";
|
||||
import { stat, writeFile, mkdir } from "node:fs/promises";
|
||||
import { Command } from "@cliffy/command";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import { Confirm } from "@cliffy/prompt/confirm";
|
||||
import { Input } from "@cliffy/prompt/input";
|
||||
import { Select } from "@cliffy/prompt/select";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { stringify as yamlStringify } from "yaml";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import { generateAgentsDocumentation, generateDatatablesDocumentation, yamlOptions } from "../sync/sync.ts";
|
||||
import { resolveWorkspace } from "../../core/context.ts";
|
||||
@@ -480,11 +478,11 @@ CREATE SCHEMA IF NOT EXISTS ${schemaName};
|
||||
|
||||
// Create the directory structure - preserve full path (e.g., f/foobar/x/y becomes f/foobar/x/y.raw_app)
|
||||
const folderName = buildFolderPath(appPath, "raw_app");
|
||||
const appDir = path.join(Deno.cwd(), folderName);
|
||||
const appDir = path.join(process.cwd(), folderName);
|
||||
|
||||
// Check if directory already exists
|
||||
try {
|
||||
await Deno.stat(appDir);
|
||||
await stat(appDir);
|
||||
const overwrite = await Confirm.prompt({
|
||||
message: `Directory '${folderName}' already exists. Overwrite?`,
|
||||
default: false,
|
||||
@@ -497,9 +495,9 @@ CREATE SCHEMA IF NOT EXISTS ${schemaName};
|
||||
// Directory doesn't exist, which is good
|
||||
}
|
||||
|
||||
await ensureDir(appDir);
|
||||
await ensureDir(path.join(appDir, "backend"));
|
||||
await ensureDir(path.join(appDir, "sql_to_apply"));
|
||||
await mkdir(appDir, { recursive: true });
|
||||
await mkdir(path.join(appDir, "backend"), { recursive: true });
|
||||
await mkdir(path.join(appDir, "sql_to_apply"), { recursive: true });
|
||||
|
||||
// Create raw_app.yaml with data configuration
|
||||
const rawAppConfig: Record<string, unknown> = {
|
||||
@@ -511,15 +509,15 @@ CREATE SCHEMA IF NOT EXISTS ${schemaName};
|
||||
rawAppConfig.data = dataConfig;
|
||||
}
|
||||
|
||||
await Deno.writeTextFile(
|
||||
await writeFile(
|
||||
path.join(appDir, "raw_app.yaml"),
|
||||
yamlStringify(rawAppConfig, yamlOptions)
|
||||
yamlStringify(rawAppConfig, yamlOptions), "utf-8"
|
||||
);
|
||||
|
||||
// Create template files
|
||||
for (const [filePath, content] of Object.entries(template.files)) {
|
||||
const fullPath = path.join(appDir, filePath.slice(1)); // Remove leading slash
|
||||
await Deno.writeTextFile(fullPath, content.trim() + "\n");
|
||||
await writeFile(fullPath, content.trim() + "\n", "utf-8");
|
||||
}
|
||||
|
||||
// Create AGENTS.md - main documentation for AI agents
|
||||
@@ -532,22 +530,22 @@ CREATE SCHEMA IF NOT EXISTS ${schemaName};
|
||||
: undefined;
|
||||
|
||||
const agentsContent = generateAgentsDocumentation(dataForDocs);
|
||||
await Deno.writeTextFile(
|
||||
await writeFile(
|
||||
path.join(appDir, "AGENTS.md"),
|
||||
agentsContent
|
||||
agentsContent, "utf-8"
|
||||
);
|
||||
|
||||
// Create CLAUDE.md referencing AGENTS.md
|
||||
await Deno.writeTextFile(
|
||||
await writeFile(
|
||||
path.join(appDir, "CLAUDE.md"),
|
||||
`Instructions are in @AGENTS.md\n`
|
||||
`Instructions are in @AGENTS.md\n`, "utf-8"
|
||||
);
|
||||
|
||||
// Create DATATABLES.md with the configured data
|
||||
const datatablesContent = generateDatatablesDocumentation(dataForDocs);
|
||||
await Deno.writeTextFile(
|
||||
await writeFile(
|
||||
path.join(appDir, "DATATABLES.md"),
|
||||
datatablesContent
|
||||
datatablesContent, "utf-8"
|
||||
);
|
||||
|
||||
// Create example backend runnable
|
||||
@@ -555,20 +553,20 @@ CREATE SCHEMA IF NOT EXISTS ${schemaName};
|
||||
type: "inline",
|
||||
path: undefined,
|
||||
};
|
||||
await Deno.writeTextFile(
|
||||
await writeFile(
|
||||
path.join(appDir, "backend", "a.yaml"),
|
||||
yamlStringify(exampleRunnable, yamlOptions)
|
||||
yamlStringify(exampleRunnable, yamlOptions), "utf-8"
|
||||
);
|
||||
await Deno.writeTextFile(
|
||||
await writeFile(
|
||||
path.join(appDir, "backend", "a.ts"),
|
||||
`export async function main(x: number): Promise<string> {
|
||||
return \`Hello from backend! x = \${x}\`;
|
||||
}
|
||||
`
|
||||
`, "utf-8"
|
||||
);
|
||||
|
||||
// Create sql_to_apply README
|
||||
await Deno.writeTextFile(
|
||||
await writeFile(
|
||||
path.join(appDir, "sql_to_apply", "README.md"),
|
||||
`# SQL Migrations Folder
|
||||
|
||||
@@ -601,9 +599,9 @@ This folder is for SQL migration files that will be applied to datatables during
|
||||
|
||||
// Create schema creation SQL file if a new schema was requested
|
||||
if (createSchemaSQL && schemaName) {
|
||||
await Deno.writeTextFile(
|
||||
await writeFile(
|
||||
path.join(appDir, "sql_to_apply", `000_create_schema_${schemaName}.sql`),
|
||||
createSchemaSQL
|
||||
createSchemaSQL, "utf-8"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -666,7 +664,6 @@ This folder is for SQL migration files that will be applied to datatables during
|
||||
log.info(colors.gray(" 4. wmill sync push (to deploy when ready)"));
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const command = new Command()
|
||||
.description("create a new raw app from a template")
|
||||
.action(newApp as any);
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
import { resolveWorkspace, validatePath } from "../../core/context.ts";
|
||||
import {
|
||||
colors,
|
||||
log,
|
||||
SEP,
|
||||
windmillUtils,
|
||||
yamlParseFile,
|
||||
yamlStringify,
|
||||
} from "../../../deps.ts";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { sep as SEP } from "node:path";
|
||||
import * as windmillUtils from "@windmill-labs/shared-utils";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import { stringify as yamlStringify } from "yaml";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
import { Policy } from "../../../gen/types.gen.ts";
|
||||
import path from "node:path";
|
||||
import { readFile, readdir } from "node:fs/promises";
|
||||
|
||||
import { GlobalOptions, isSuperset } from "../../types.ts";
|
||||
import { deepEqual } from "../../utils/utils.ts";
|
||||
@@ -67,8 +65,8 @@ async function findRunnableContentFile(
|
||||
// Check if this is a recognized extension
|
||||
if (EXTENSION_TO_LANGUAGE[ext]) {
|
||||
try {
|
||||
const content = await Deno.readTextFile(
|
||||
path.join(backendPath, fileName),
|
||||
const content = await readFile(
|
||||
path.join(backendPath, fileName), "utf-8",
|
||||
);
|
||||
return { ext, content };
|
||||
} catch {
|
||||
@@ -130,8 +128,9 @@ export async function loadRunnablesFromBackend(
|
||||
try {
|
||||
// First, collect all files in the backend folder
|
||||
const allFiles: string[] = [];
|
||||
for await (const entry of Deno.readDir(backendPath)) {
|
||||
if (entry.isFile) {
|
||||
const _entries = await readdir(backendPath, { withFileTypes: true });
|
||||
for (const entry of _entries) {
|
||||
if (entry.isFile()) {
|
||||
allFiles.push(entry.name);
|
||||
}
|
||||
}
|
||||
@@ -165,8 +164,9 @@ export async function loadRunnablesFromBackend(
|
||||
// Try to load lock file
|
||||
let lock: string | undefined;
|
||||
try {
|
||||
lock = await Deno.readTextFile(
|
||||
lock = await readFile(
|
||||
path.join(backendPath, `${runnableId}.lock`),
|
||||
"utf-8",
|
||||
);
|
||||
} catch {
|
||||
// No lock file, that's fine
|
||||
@@ -226,8 +226,8 @@ export async function loadRunnablesFromBackend(
|
||||
// Try to load lock file
|
||||
let lock: string | undefined;
|
||||
try {
|
||||
lock = await Deno.readTextFile(
|
||||
path.join(backendPath, `${runnableId}.lock`),
|
||||
lock = await readFile(
|
||||
path.join(backendPath, `${runnableId}.lock`), "utf-8",
|
||||
);
|
||||
} catch {
|
||||
// No lock file, that's fine
|
||||
@@ -245,7 +245,7 @@ export async function loadRunnablesFromBackend(
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.name !== "NotFound") {
|
||||
if (error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -291,11 +291,12 @@ async function collectAppFiles(
|
||||
const files: Record<string, string> = {};
|
||||
|
||||
async function readDirRecursive(dir: string, basePath: string = "/") {
|
||||
for await (const entry of Deno.readDir(dir)) {
|
||||
const dirEntries = await readdir(dir, { withFileTypes: true });
|
||||
for (const entry of dirEntries) {
|
||||
const fullPath = dir + entry.name;
|
||||
const relativePath = basePath + entry.name;
|
||||
|
||||
if (entry.isDirectory) {
|
||||
if (entry.isDirectory()) {
|
||||
// Skip the runnables, node_modules, and sql_to_apply subfolders
|
||||
if (
|
||||
entry.name === APP_BACKEND_FOLDER ||
|
||||
@@ -307,7 +308,7 @@ async function collectAppFiles(
|
||||
continue;
|
||||
}
|
||||
await readDirRecursive(fullPath + SEP, relativePath + "/");
|
||||
} else if (entry.isFile) {
|
||||
} else if (entry.isFile()) {
|
||||
// Skip generated/metadata files that shouldn't be part of the app
|
||||
if (
|
||||
entry.name === "raw_app.yaml" ||
|
||||
@@ -318,7 +319,7 @@ async function collectAppFiles(
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const content = await Deno.readTextFile(fullPath);
|
||||
const content = await readFile(fullPath, "utf-8");
|
||||
files[relativePath] = content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
import { resolveWorkspace } from "../../core/context.ts";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import { colors, Command, log } from "../../../deps.ts";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import { Command } from "@cliffy/command";
|
||||
import * as log from "../../core/log.ts";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
import fs from "node:fs";
|
||||
import { workspaceDependenciesPathToLanguageAndFilename } from "../../utils/metadata.ts";
|
||||
|
||||
+41
-37
@@ -1,15 +1,14 @@
|
||||
import {
|
||||
Command,
|
||||
SEP,
|
||||
WebSocketServer,
|
||||
express,
|
||||
getPort,
|
||||
http,
|
||||
log,
|
||||
open,
|
||||
WebSocket,
|
||||
yamlParseFile,
|
||||
} from "../../../deps.ts";
|
||||
import { Command } from "@cliffy/command";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { sep as SEP } from "node:path";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import { WebSocket, WebSocketServer } from "ws";
|
||||
|
||||
import * as getPort from "get-port";
|
||||
import * as http from "node:http";
|
||||
import * as open from "open";
|
||||
import { readFile, realpath } from "node:fs/promises";
|
||||
import { watch } from "node:fs";
|
||||
import { getTypeStrFromPath, GlobalOptions } from "../../types.ts";
|
||||
import { ignoreF } from "../sync/sync.ts";
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
@@ -40,25 +39,30 @@ async function dev(opts: GlobalOptions & SyncOptions) {
|
||||
const conf = await readConfigFile();
|
||||
let currentLastEdit: LastEditScript | LastEditFlow | undefined = undefined;
|
||||
|
||||
const watcher = Deno.watchFs(".");
|
||||
const base = await Deno.realPath(".");
|
||||
const fsWatcher = watch(".", { recursive: true });
|
||||
const base = await realpath(".");
|
||||
opts = await mergeConfigWithConfigFile(opts);
|
||||
const ignore = await ignoreF(opts);
|
||||
|
||||
const changesTimeouts: Record<string, number> = {};
|
||||
async function watchChanges() {
|
||||
for await (const event of watcher) {
|
||||
// console.log(">>>> event", event);
|
||||
const key = event.paths.join(",");
|
||||
if (changesTimeouts[key]) {
|
||||
clearTimeout(changesTimeouts[key]);
|
||||
}
|
||||
// @ts-ignore
|
||||
changesTimeouts[key] = setTimeout(async () => {
|
||||
delete changesTimeouts[key];
|
||||
await loadPaths(event.paths);
|
||||
}, 100);
|
||||
}
|
||||
const changesTimeouts: Record<string, ReturnType<typeof setTimeout>> = {};
|
||||
function watchChanges() {
|
||||
return new Promise<void>((_resolve, _reject) => {
|
||||
fsWatcher.on("change", (_eventType, filename) => {
|
||||
if (!filename) return;
|
||||
const filePath = typeof filename === "string" ? filename : filename.toString();
|
||||
const key = filePath;
|
||||
if (changesTimeouts[key]) {
|
||||
clearTimeout(changesTimeouts[key]);
|
||||
}
|
||||
changesTimeouts[key] = setTimeout(async () => {
|
||||
delete changesTimeouts[key];
|
||||
await loadPaths([filePath]);
|
||||
}, 100);
|
||||
});
|
||||
fsWatcher.on("error", (err) => {
|
||||
_reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const flowFolderSuffix = getFolderSuffixWithSep("flow");
|
||||
@@ -72,8 +76,9 @@ async function dev(opts: GlobalOptions & SyncOptions) {
|
||||
if (paths.length == 0) {
|
||||
return;
|
||||
}
|
||||
const cpath = (await Deno.realPath(paths[0])).replace(base + SEP, "");
|
||||
if (!ignore(cpath, false)) {
|
||||
const nativePath = (await realpath(paths[0])).replace(base + SEP, "");
|
||||
const cpath = nativePath.replaceAll("\\", "/");
|
||||
if (!ignore(nativePath, false)) {
|
||||
const typ = getTypeStrFromPath(cpath);
|
||||
log.info("Detected change in " + cpath + " (" + typ + ")");
|
||||
if (typ == "flow") {
|
||||
@@ -83,13 +88,11 @@ async function dev(opts: GlobalOptions & SyncOptions) {
|
||||
)) as FlowFile;
|
||||
await replaceInlineScripts(
|
||||
localFlow.value.modules,
|
||||
async (path: string) => await Deno.readTextFile(localPath + path),
|
||||
async (path: string) => await readFile(localPath + path, "utf-8"),
|
||||
log,
|
||||
localPath,
|
||||
SEP,
|
||||
undefined,
|
||||
// (path: string, newPath: string) => Deno.renameSync(path, newPath),
|
||||
// (path: string) => Deno.removeSync(path),
|
||||
);
|
||||
currentLastEdit = {
|
||||
type: "flow",
|
||||
@@ -99,7 +102,7 @@ async function dev(opts: GlobalOptions & SyncOptions) {
|
||||
log.info("Updated " + localPath);
|
||||
broadcastChanges(currentLastEdit);
|
||||
} else if (typ == "script") {
|
||||
const content = await Deno.readTextFile(cpath);
|
||||
const content = await readFile(cpath, "utf-8");
|
||||
const splitted = cpath.split(".");
|
||||
const wmPath = splitted[0];
|
||||
const lang = inferContentTypeFromFilePath(cpath, conf.defaultTs);
|
||||
@@ -150,8 +153,10 @@ async function dev(opts: GlobalOptions & SyncOptions) {
|
||||
}
|
||||
|
||||
async function startApp() {
|
||||
const app = express.default();
|
||||
const server = http.createServer(app);
|
||||
const server = http.createServer((_req, res) => {
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
});
|
||||
const wss = new WebSocketServer({ server });
|
||||
|
||||
// WebSocket server event listeners
|
||||
@@ -224,7 +229,6 @@ const command = new Command()
|
||||
"--includes <pattern...:string>",
|
||||
"Filter paths givena glob pattern or path"
|
||||
)
|
||||
// deno-lint-ignore no-explicit-any
|
||||
.action(dev as any);
|
||||
|
||||
export default command;
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { GlobalOptions, isSuperset } from "../../types.ts";
|
||||
import { Confirm, SEP, log, yamlStringify } from "../../../deps.ts";
|
||||
import { colors, Command, Table, yamlParseFile } from "../../../deps.ts";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import { Command } from "@cliffy/command";
|
||||
import { Confirm } from "@cliffy/prompt/confirm";
|
||||
import { Table } from "@cliffy/table";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { sep as SEP } from "node:path";
|
||||
import { stringify as yamlStringify } from "yaml";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
import { resolveWorkspace, validatePath } from "../../core/context.ts";
|
||||
@@ -51,7 +58,7 @@ export async function pushFlow(
|
||||
|
||||
await replaceInlineScripts(
|
||||
localFlow.value.modules,
|
||||
async (path: string) => await Deno.readTextFile(localPath + path),
|
||||
async (path: string) => await readFile(localPath + path, "utf-8"),
|
||||
log,
|
||||
localPath,
|
||||
SEP
|
||||
@@ -106,7 +113,7 @@ async function push(opts: Options, filePath: string, remotePath: string) {
|
||||
}
|
||||
|
||||
async function list(
|
||||
opts: GlobalOptions & { showArchived?: boolean; includeDraftOnly?: boolean }
|
||||
opts: GlobalOptions & { showArchived?: boolean; includeDraftOnly?: boolean; json?: boolean }
|
||||
) {
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
@@ -129,13 +136,35 @@ async function list(
|
||||
}
|
||||
}
|
||||
|
||||
new Table()
|
||||
.header(["path", "summary", "edited by"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(total.map((x) => [x.path, x.summary, x.edited_by]))
|
||||
.render();
|
||||
if (opts.json) {
|
||||
console.log(JSON.stringify(total));
|
||||
} else {
|
||||
new Table()
|
||||
.header(["path", "summary", "edited by"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(total.map((x) => [x.path, x.summary, x.edited_by]))
|
||||
.render();
|
||||
}
|
||||
}
|
||||
async function get(opts: GlobalOptions & { json?: boolean }, path: string) {
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
const f = await wmill.getFlowByPath({
|
||||
workspace: workspace.workspaceId,
|
||||
path,
|
||||
});
|
||||
if (opts.json) {
|
||||
console.log(JSON.stringify(f));
|
||||
} else {
|
||||
console.log(colors.bold("Path:") + " " + f.path);
|
||||
console.log(colors.bold("Summary:") + " " + (f.summary ?? ""));
|
||||
console.log(colors.bold("Description:") + " " + (f.description ?? ""));
|
||||
console.log(colors.bold("Edited by:") + " " + (f.edited_by ?? ""));
|
||||
console.log(colors.bold("Edited at:") + " " + (f.edited_at ?? ""));
|
||||
}
|
||||
}
|
||||
|
||||
async function run(
|
||||
opts: GlobalOptions & {
|
||||
data?: string;
|
||||
@@ -225,7 +254,7 @@ async function preview(
|
||||
// Replace inline scripts with their actual content
|
||||
await replaceInlineScripts(
|
||||
localFlow.value.modules,
|
||||
async (path: string) => await Deno.readTextFile(flowPath + path),
|
||||
async (path: string) => await readFile(flowPath + path, "utf-8"),
|
||||
log,
|
||||
flowPath,
|
||||
SEP
|
||||
@@ -286,7 +315,7 @@ async function generateLocks(
|
||||
const ignore = await ignoreF(opts);
|
||||
const elems = Object.keys(
|
||||
await elementsToMap(
|
||||
await FSFSElement(Deno.cwd(), [], true),
|
||||
await FSFSElement(process.cwd(), [], true),
|
||||
(p, isD) => {
|
||||
return (
|
||||
ignore(p, isD) ||
|
||||
@@ -348,7 +377,7 @@ export function bootstrap(
|
||||
}
|
||||
|
||||
const flowDirFullPath = `${flowPath}.flow`;
|
||||
Deno.mkdirSync(flowDirFullPath, { recursive: false });
|
||||
mkdirSync(flowDirFullPath, { recursive: false });
|
||||
|
||||
const newFlowDefinition = defaultFlowDefinition();
|
||||
if (opts.summary !== undefined) {
|
||||
@@ -363,13 +392,22 @@ export function bootstrap(
|
||||
);
|
||||
|
||||
const flowYamlPath = `${flowDirFullPath}/flow.yaml`;
|
||||
Deno.writeTextFile(flowYamlPath, newFlowDefinitionYaml, { createNew: true });
|
||||
writeFileSync(flowYamlPath, newFlowDefinitionYaml, { flag: "wx", encoding: "utf-8" });
|
||||
}
|
||||
|
||||
const command = new Command()
|
||||
.description("flow related commands")
|
||||
.option("--show-archived", "Enable archived scripts in output")
|
||||
.option("--show-archived", "Enable archived flows in output")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(list as any)
|
||||
.command("list", "list all flows")
|
||||
.option("--show-archived", "Enable archived flows in output")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(list as any)
|
||||
.command("get", "get a flow's details")
|
||||
.arguments("<path:string>")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(get as any)
|
||||
.command(
|
||||
"push",
|
||||
"push a local flow spec. This overrides any remote versions."
|
||||
@@ -416,10 +454,15 @@ const command = new Command()
|
||||
"Comma separated patterns to specify which file to NOT take into account."
|
||||
)
|
||||
.action(generateLocks as any)
|
||||
.command("bootstrap", "create a new empty flow")
|
||||
.command("new", "create a new empty flow")
|
||||
.arguments("<flow_path:string>")
|
||||
.option("--summary <summary:string>", "script summary")
|
||||
.option("--description <description:string>", "script description")
|
||||
.option("--summary <summary:string>", "flow summary")
|
||||
.option("--description <description:string>", "flow description")
|
||||
.action(bootstrap as any)
|
||||
.command("bootstrap", "create a new empty flow (alias for new)")
|
||||
.arguments("<flow_path:string>")
|
||||
.option("--summary <summary:string>", "flow summary")
|
||||
.option("--description <description:string>", "flow description")
|
||||
.action(bootstrap as any);
|
||||
|
||||
export default command;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import {
|
||||
SEP,
|
||||
colors,
|
||||
log,
|
||||
path,
|
||||
yamlParseFile,
|
||||
yamlStringify,
|
||||
} from "../../../deps.ts";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import * as path from "node:path";
|
||||
import { sep as SEP } from "node:path";
|
||||
import { stringify as yamlStringify } from "yaml";
|
||||
import { yamlParseFile } from "../../utils/yaml.ts";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import {
|
||||
readLockfile,
|
||||
@@ -37,7 +36,7 @@ async function generateFlowHash(
|
||||
folder: string,
|
||||
defaultTs: "bun" | "deno" | undefined
|
||||
) {
|
||||
const elems = await FSFSElement(path.join(Deno.cwd(), folder), [], true);
|
||||
const elems = await FSFSElement(path.join(process.cwd(), folder), [], true);
|
||||
const hashes: Record<string, string> = {};
|
||||
for await (const f of elems.getChildren()) {
|
||||
if (exts.some((e) => f.path.endsWith(e))) {
|
||||
@@ -124,13 +123,11 @@ export async function generateFlowLockInternal(
|
||||
log.info(`Recomputing locks of ${changedScripts.join(", ")} in ${folder}`);
|
||||
await replaceInlineScripts(
|
||||
flowValue.value.modules,
|
||||
async (path: string) => await Deno.readTextFile(folder + SEP + path),
|
||||
async (path: string) => await readFile(folder + SEP + path, "utf-8"),
|
||||
log,
|
||||
folder + SEP!,
|
||||
SEP,
|
||||
changedScripts
|
||||
// (path: string, newPath: string) => Deno.renameSync(path, newPath),
|
||||
// (path: string) => Deno.removeSync(path)
|
||||
);
|
||||
|
||||
//removeChangedLocks
|
||||
@@ -148,12 +145,12 @@ export async function generateFlowLockInternal(
|
||||
opts.defaultTs
|
||||
);
|
||||
inlineScripts.forEach((s) => {
|
||||
writeIfChanged(Deno.cwd() + SEP + folder + SEP + s.path, s.content);
|
||||
writeIfChanged(process.cwd() + SEP + folder + SEP + s.path, s.content);
|
||||
});
|
||||
|
||||
// Overwrite `flow.yaml` with the new lockfile references
|
||||
writeIfChanged(
|
||||
Deno.cwd() + SEP + folder + SEP + "flow.yaml",
|
||||
process.cwd() + SEP + folder + SEP + "flow.yaml",
|
||||
yamlStringify(flowValue as Record<string, any>)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { colors, Command, log, SEP, Table } from "../../../deps.ts";
|
||||
import { stat, writeFile, mkdir } from "node:fs/promises";
|
||||
import { stringify as yamlStringify } from "yaml";
|
||||
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import { Command } from "@cliffy/command";
|
||||
import { Table } from "@cliffy/table";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { sep as SEP } from "node:path";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
@@ -13,7 +19,7 @@ export interface FolderFile {
|
||||
display_name: string | undefined;
|
||||
}
|
||||
|
||||
async function list(opts: GlobalOptions) {
|
||||
async function list(opts: GlobalOptions & { json?: boolean }) {
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
|
||||
@@ -21,18 +27,60 @@ async function list(opts: GlobalOptions) {
|
||||
workspace: workspace.workspaceId,
|
||||
});
|
||||
|
||||
new Table()
|
||||
.header(["Name", "Owners", "Extra Perms"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(
|
||||
folders.map((x) => [
|
||||
x.name,
|
||||
x.owners?.join(",") ?? "-",
|
||||
JSON.stringify(x.extra_perms ?? {}),
|
||||
])
|
||||
)
|
||||
.render();
|
||||
if (opts.json) {
|
||||
console.log(JSON.stringify(folders));
|
||||
} else {
|
||||
new Table()
|
||||
.header(["Name", "Owners", "Extra Perms"])
|
||||
.padding(2)
|
||||
.border(true)
|
||||
.body(
|
||||
folders.map((x) => [
|
||||
x.name,
|
||||
x.owners?.join(",") ?? "-",
|
||||
JSON.stringify(x.extra_perms ?? {}),
|
||||
])
|
||||
)
|
||||
.render();
|
||||
}
|
||||
}
|
||||
|
||||
async function newFolder(opts: GlobalOptions, name: string) {
|
||||
const dirPath = `f${SEP}${name}`;
|
||||
const filePath = `${dirPath}${SEP}folder.meta.yaml`;
|
||||
try {
|
||||
await stat(filePath);
|
||||
throw new Error("File already exists: " + filePath);
|
||||
} catch (e: any) {
|
||||
if (e.message?.startsWith("File already exists")) throw e;
|
||||
}
|
||||
const template: Omit<FolderFile, "display_name"> = {
|
||||
owners: [],
|
||||
extra_perms: {},
|
||||
};
|
||||
await mkdir(dirPath, { recursive: true });
|
||||
await writeFile(filePath, yamlStringify(template as Record<string, any>), {
|
||||
flag: "wx",
|
||||
encoding: "utf-8",
|
||||
});
|
||||
log.info(colors.green(`Created ${filePath}`));
|
||||
}
|
||||
|
||||
async function get(opts: GlobalOptions & { json?: boolean }, name: string) {
|
||||
const workspace = await resolveWorkspace(opts);
|
||||
await requireLogin(opts);
|
||||
const f = await wmill.getFolder({
|
||||
workspace: workspace.workspaceId,
|
||||
name,
|
||||
});
|
||||
if (opts.json) {
|
||||
console.log(JSON.stringify(f));
|
||||
} else {
|
||||
console.log(colors.bold("Name:") + " " + f.name);
|
||||
console.log(colors.bold("Summary:") + " " + (f.summary ?? ""));
|
||||
console.log(colors.bold("Owners:") + " " + (f.owners?.join(", ") ?? "-"));
|
||||
console.log(colors.bold("Extra Perms:") + " " + JSON.stringify(f.extra_perms ?? {}));
|
||||
}
|
||||
}
|
||||
|
||||
export async function pushFolder(
|
||||
@@ -103,8 +151,8 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fstat = await Deno.stat(filePath);
|
||||
if (!fstat.isFile) {
|
||||
const fstat = await stat(filePath);
|
||||
if (!fstat.isFile()) {
|
||||
throw new Error("file path must refer to a file.");
|
||||
}
|
||||
|
||||
@@ -121,7 +169,18 @@ async function push(opts: GlobalOptions, filePath: string, remotePath: string) {
|
||||
|
||||
const command = new Command()
|
||||
.description("folder related commands")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(list as any)
|
||||
.command("list", "list all folders")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(list as any)
|
||||
.command("get", "get a folder's details")
|
||||
.arguments("<name:string>")
|
||||
.option("--json", "Output as JSON (for piping to jq)")
|
||||
.action(get as any)
|
||||
.command("new", "create a new folder locally")
|
||||
.arguments("<name:string>")
|
||||
.action(newFolder as any)
|
||||
.command(
|
||||
"push",
|
||||
"push a local folder spec. This overrides any remote versions."
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Command } from "../../../deps.ts";
|
||||
import { Command } from "@cliffy/command";
|
||||
import { pullGitSyncSettings } from "./pull.ts";
|
||||
import { pushGitSyncSettings } from "./push.ts";
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { colors, Confirm } from "../../../deps.ts";
|
||||
import process from "node:process";
|
||||
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import { Confirm } from "@cliffy/prompt/confirm";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
import { GitSyncRepository } from "./types.ts";
|
||||
|
||||
@@ -24,7 +27,7 @@ export async function handleLegacyRepositoryMigration(
|
||||
const workspaceIncludePath = gitSyncSettings.include_path;
|
||||
const workspaceIncludeType = gitSyncSettings.include_type;
|
||||
|
||||
if (Deno.stdout.isTerminal() && !opts.yes) {
|
||||
if (!!process.stdout.isTTY && !opts.yes) {
|
||||
// Interactive mode - show migration prompt
|
||||
console.log(colors.yellow('\n⚠️ Legacy git-sync settings detected!'));
|
||||
console.log(`\nRepository "${selectedRepo.git_repo_resource_path}" has legacy settings format.`);
|
||||
@@ -139,6 +142,6 @@ export async function handleLegacyRepositoryMigration(
|
||||
console.error('3. Push local settings to override backend settings:');
|
||||
console.error(' wmill gitsync-settings push\n');
|
||||
}
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import { colors, log, yamlStringify } from "../../../deps.ts";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { stringify as yamlStringify } from "yaml";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
import { resolveWorkspace } from "../../core/context.ts";
|
||||
import * as wmill from "../../../gen/services.gen.ts";
|
||||
import { SyncOptions, readConfigFile, getEffectiveSettings, DEFAULT_SYNC_OPTIONS, getWmillYamlPath } from "../../core/conf.ts";
|
||||
import { yamlOptions } from "../sync/sync.ts";
|
||||
import { deepEqual } from "../../utils/utils.ts";
|
||||
import { getCurrentGitBranch, isGitRepository } from "../../utils/git.ts";
|
||||
|
||||
@@ -173,7 +177,7 @@ export async function pullGitSyncSettings(
|
||||
}
|
||||
|
||||
// Write the new configuration
|
||||
await Deno.writeTextFile("wmill.yaml", yamlStringify(updatedConfig));
|
||||
await writeFile("wmill.yaml", yamlStringify(updatedConfig, yamlOptions), "utf-8");
|
||||
|
||||
if (opts.jsonOutput) {
|
||||
console.log(
|
||||
@@ -286,7 +290,7 @@ export async function pullGitSyncSettings(
|
||||
);
|
||||
const hasConflict = !deepEqual(gitSyncBackend, gitSyncCurrent);
|
||||
|
||||
if (hasConflict && !opts.yes && Deno.stdin.isTerminal()) {
|
||||
if (hasConflict && !opts.yes && !!process.stdin.isTTY) {
|
||||
// Show the diff first
|
||||
log.info("Changes that would be applied locally:");
|
||||
const changes = generateChanges(effectiveCurrentSettings, backendSyncOptions);
|
||||
@@ -295,7 +299,7 @@ export async function pullGitSyncSettings(
|
||||
}
|
||||
|
||||
// Interactive mode - ask user
|
||||
const { Select } = await import("../../../deps.ts");
|
||||
const { Select } = await import("@cliffy/prompt/select");
|
||||
const choice = await Select.prompt({
|
||||
message: "Settings conflict detected. How would you like to proceed?",
|
||||
options: [
|
||||
@@ -369,7 +373,7 @@ export async function pullGitSyncSettings(
|
||||
}
|
||||
|
||||
// Write updated configuration
|
||||
await Deno.writeTextFile("wmill.yaml", yamlStringify(updatedConfig));
|
||||
await writeFile("wmill.yaml", yamlStringify(updatedConfig, yamlOptions), "utf-8");
|
||||
|
||||
if (opts.jsonOutput) {
|
||||
console.log(
|
||||
@@ -446,7 +450,7 @@ export async function pullGitSyncSettings(
|
||||
}
|
||||
|
||||
// Write updated configuration
|
||||
await Deno.writeTextFile("wmill.yaml", yamlStringify(updatedConfig));
|
||||
await writeFile("wmill.yaml", yamlStringify(updatedConfig, yamlOptions), "utf-8");
|
||||
|
||||
if (opts.jsonOutput) {
|
||||
console.log(
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { colors, log, Confirm } from "../../../deps.ts";
|
||||
import process from "node:process";
|
||||
|
||||
import { colors } from "@cliffy/ansi/colors";
|
||||
import * as log from "../../core/log.ts";
|
||||
import { Confirm } from "@cliffy/prompt/confirm";
|
||||
import { GlobalOptions } from "../../types.ts";
|
||||
import { requireLogin } from "../../core/auth.ts";
|
||||
import { resolveWorkspace } from "../../core/context.ts";
|
||||
@@ -34,7 +38,7 @@ export async function pushGitSyncSettings(
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes("overrides")) {
|
||||
log.error(error.message);
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -51,7 +55,7 @@ export async function pushGitSyncSettings(
|
||||
"No wmill.yaml file found. Please run 'wmill init' first to create the configuration file.",
|
||||
),
|
||||
);
|
||||
Deno.exit(1);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read local configuration
|
||||
@@ -247,7 +251,7 @@ export async function pushGitSyncSettings(
|
||||
}
|
||||
|
||||
// Ask for confirmation unless --yes is passed or not in TTY
|
||||
if (!opts.yes && Deno.stdin.isTerminal()) {
|
||||
if (!opts.yes && !!process.stdin.isTTY) {
|
||||
const confirmed = await Confirm.prompt({
|
||||
message: `Do you want to apply these changes to the remote?`,
|
||||
default: true,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user