Compare commits
79 Commits
glm/improv
...
v1.409.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64e2334c2c | ||
|
|
77702268d2 | ||
|
|
c3c2fe462c | ||
|
|
b237873a87 | ||
|
|
b7ad19bb75 | ||
|
|
45bf59ba32 | ||
|
|
1466da3999 | ||
|
|
3174024d8e | ||
|
|
4e6c0d1e80 | ||
|
|
6abb346013 | ||
|
|
79100f8d0a | ||
|
|
28f6f6b067 | ||
|
|
d24e153065 | ||
|
|
8df2cec40a | ||
|
|
a8c4ea2334 | ||
|
|
20e8be15fe | ||
|
|
c3b536b1b8 | ||
|
|
c06c42a1ed | ||
|
|
01b95ebd8d | ||
|
|
99814c7ab0 | ||
|
|
10414df4a7 | ||
|
|
91a3d06529 | ||
|
|
f38b3d14e8 | ||
|
|
e736572db1 | ||
|
|
7126ba12c7 | ||
|
|
29cb954639 | ||
|
|
411bce7e13 | ||
|
|
99c5b3ecda | ||
|
|
9eb1ecc9f3 | ||
|
|
dcdbf1afb4 | ||
|
|
34c39ce0ab | ||
|
|
288dfe7c70 | ||
|
|
8867260e43 | ||
|
|
4112eb6072 | ||
|
|
9d2cb91f9c | ||
|
|
a323d2b29a | ||
|
|
fa642c8db1 | ||
|
|
42fe31f804 | ||
|
|
242a251ab1 | ||
|
|
7a92a152d1 | ||
|
|
fad4785b0a | ||
|
|
296aa97bd8 | ||
|
|
d77412cef6 | ||
|
|
685eb5d471 | ||
|
|
fcdf82af36 | ||
|
|
d2c4d3fa20 | ||
|
|
e06c845ed4 | ||
|
|
f82f091290 | ||
|
|
9f7edbaf1a | ||
|
|
2dab068fd3 | ||
|
|
7e754a1e50 | ||
|
|
be112408e7 | ||
|
|
568cc66932 | ||
|
|
0915968eba | ||
|
|
982dde2b9d | ||
|
|
7db74eecb9 | ||
|
|
81b0ff1ad1 | ||
|
|
b133f19333 | ||
|
|
d9bdc5a5b0 | ||
|
|
ba17dfc841 | ||
|
|
7ca5bf2fae | ||
|
|
cf8d443603 | ||
|
|
0e61f8db78 | ||
|
|
c3a5736641 | ||
|
|
26b11a0015 | ||
|
|
7dbe49b77b | ||
|
|
2e9312216e | ||
|
|
7b0503332d | ||
|
|
25e417538f | ||
|
|
6280ed3524 | ||
|
|
ffaf756e9a | ||
|
|
21cd7a63ae | ||
|
|
d90d6c2b89 | ||
|
|
111bfc6a65 | ||
|
|
257f0971f8 | ||
|
|
f831b9b1fe | ||
|
|
b579db61ec | ||
|
|
e44decb9c2 | ||
|
|
644dd87c50 |
27
.github/DockerfileBackendTests
vendored
27
.github/DockerfileBackendTests
vendored
@@ -1,22 +1,6 @@
|
||||
ARG DEBIAN_IMAGE=debian:bookworm-slim
|
||||
ARG RUST_IMAGE=rust:1.80-slim-bookworm
|
||||
ARG PYTHON_IMAGE=python:3.11.4-slim-bookworm
|
||||
|
||||
FROM ${DEBIAN_IMAGE} as downloader
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
RUN apt update -y
|
||||
RUN apt install -y unzip curl
|
||||
|
||||
RUN [ "$TARGETPLATFORM" == "linux/amd64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.46.3/deno-x86_64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.46.3/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
|
||||
|
||||
RUN unzip deno.zip && rm deno.zip
|
||||
|
||||
|
||||
FROM ${RUST_IMAGE} as builder
|
||||
|
||||
@@ -31,7 +15,7 @@ ENV SQLX_OFFLINE=true
|
||||
|
||||
RUN mkdir -p /frontend/build
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates tzdata libpq5 cmake\
|
||||
&& apt-get install -y ca-certificates tzdata libpq5 cmake unzip\
|
||||
make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev \
|
||||
libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libxml2-dev \
|
||||
libxmlsec1-dev libffi-dev liblzma-dev mecab-ipadic-utf8 libgdbm-dev libc6-dev git libprotobuf-dev libnl-route-3-dev \
|
||||
@@ -56,13 +40,14 @@ RUN wget https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VER
|
||||
|
||||
RUN /usr/local/bin/python3 -m pip install pip-tools
|
||||
|
||||
COPY --from=oven/bun:1.1.27 /usr/local/bin/bun /usr/bin/bun
|
||||
COPY --from=oven/bun:1.1.30 /usr/local/bin/bun /usr/bin/bun
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN [ "$TARGETPLATFORM" == "linux/amd64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.41.0/deno-x86_64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v1.41.0/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
RUN curl -Lsf https://github.com/denoland/deno/releases/download/v2.0.0/deno-x86_64-unknown-linux-gnu.zip -o deno.zip
|
||||
# RUN [ "$TARGETPLATFORM" == "linux/arm64" ] && curl -Lsf https://github.com/denoland/deno/releases/download/v2.0.0/deno-aarch64-unknown-linux-gnu.zip -o deno.zip || true
|
||||
|
||||
COPY --from=downloader --chmod=755 /deno /usr/bin/deno
|
||||
RUN unzip deno.zip && rm deno.zip && mv deno /usr/bin/deno
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y postgresql-client --allow-unauthenticated
|
||||
|
||||
4
.github/workflows/backend-test.yml
vendored
4
.github/workflows/backend-test.yml
vendored
@@ -41,6 +41,10 @@ jobs:
|
||||
- name: cargo test
|
||||
timeout-minutes: 15
|
||||
run:
|
||||
/usr/bin/deno --version &&
|
||||
/usr/bin/bun -v &&
|
||||
go version &&
|
||||
/usr/local/bin/python3 --version &&
|
||||
mkdir frontend/build && cd backend && touch
|
||||
windmill-api/openapi-deref.yaml &&
|
||||
DATABASE_URL=postgres://postgres:changeme@postgres:5432/windmill
|
||||
|
||||
128
.github/workflows/build-publish-rh-image.yml
vendored
Normal file
128
.github/workflows/build-publish-rh-image.yml
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
name: Build and publish windmill for RHEL9
|
||||
on:
|
||||
workflow_dispatch
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
build_ee:
|
||||
runs-on: ubicloud
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Read EE repo commit hash
|
||||
run: |
|
||||
echo "ee_repo_ref=$(cat ./backend/ee-repo-ref.txt)" >> "$GITHUB_ENV"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: windmill-labs/windmill-ee-private
|
||||
path: ./windmill-ee-private
|
||||
ref: ${{ env.ee_repo_ref }}
|
||||
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
fetch-depth: 0
|
||||
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v2
|
||||
- uses: depot/setup-action@v1
|
||||
|
||||
- name: Docker meta
|
||||
id: meta-ee-public
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-rhel9
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=sha
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Substitute EE code
|
||||
run: |
|
||||
./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private
|
||||
|
||||
- name: Copy RHEL9 Dockerfile
|
||||
run: |
|
||||
cp ./docker/RHEL9/Dockerfile ./Dockerfile
|
||||
|
||||
- name: Build and push publicly ee amd64
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
tags: |
|
||||
${{ steps.meta-ee-public.outputs.tags }}-amd64
|
||||
labels: |
|
||||
${{ steps.meta-ee-public.outputs.labels }}-amd64
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
|
||||
- name: Build and push publicly ee arm64
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,deno_core
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
tags: |
|
||||
${{ steps.meta-ee-public.outputs.tags }}-arm64
|
||||
labels: |
|
||||
${{ steps.meta-ee-public.outputs.labels }}-arm64
|
||||
org.opencontainers.image.licenses=Windmill-Enterprise-License
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
id: extract-ee-amd64
|
||||
with:
|
||||
image: ${{ steps.meta-ee-public.outputs.tags}}-amd64
|
||||
path: "/windmill/target/release/windmill"
|
||||
|
||||
- uses: shrink/actions-docker-extract@v3
|
||||
id: extract-ee-arm64
|
||||
with:
|
||||
image: ${{ steps.meta-ee-public.outputs.tags}}-arm64
|
||||
path: "/windmill/target/release/windmill"
|
||||
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
mv "${{ steps.extract-ee-amd64.outputs.destination }}/windmill" "${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9"
|
||||
mv "${{ steps.extract-ee-arm64.outputs.destination }}/windmill" "${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9"
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RHEL9-amd64 build
|
||||
path: ${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RHEL9-arm64 build
|
||||
path: ${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9
|
||||
|
||||
# - name: Attach binary to release
|
||||
# uses: softprops/action-gh-release@v2
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# with:
|
||||
# files: |
|
||||
# ${{ steps.extract-ee-arm64.outputs.destination }}/windmill-ee-arm64-rhel9
|
||||
# ${{ steps.extract-ee-amd64.outputs.destination }}/windmill-ee-amd64-rhel9
|
||||
12
.github/workflows/docker-image.yml
vendored
12
.github/workflows/docker-image.yml
vendored
@@ -394,7 +394,7 @@ jobs:
|
||||
verify_ee_image_vulnerabilities:
|
||||
runs-on: ubicloud
|
||||
needs: [tag_latest_ee]
|
||||
# if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -589,8 +589,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-cuda
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
@@ -635,8 +633,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-slim
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
@@ -680,8 +676,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-slim
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
@@ -726,8 +720,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-full
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
@@ -771,8 +763,6 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee-full
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
106
CHANGELOG.md
106
CHANGELOG.md
@@ -1,5 +1,111 @@
|
||||
# Changelog
|
||||
|
||||
## [1.409.3](https://github.com/windmill-labs/windmill/compare/v1.409.2...v1.409.3) (2024-10-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not delete primary schedule of script/flow on redeploy even if schedule wasn't loaded ([c3c2fe4](https://github.com/windmill-labs/windmill/commit/c3c2fe462c52f48795a55025414b16f8d3d98fe0))
|
||||
* **nsjail:** improve memory reading when using nsjail ([b7ad19b](https://github.com/windmill-labs/windmill/commit/b7ad19bb75bd885a254ebac778349c7d88af8326))
|
||||
|
||||
## [1.409.2](https://github.com/windmill-labs/windmill/compare/v1.409.1...v1.409.2) (2024-10-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add extra args support for exception to bun scripts ([1466da3](https://github.com/windmill-labs/windmill/commit/1466da3999add0238b9c42ca13df52194f082fc0))
|
||||
* fix script persistence in url + add support for extra error args in python ([3174024](https://github.com/windmill-labs/windmill/commit/3174024d8e6ecbe9f8c9e1ea055d611f652c0057))
|
||||
|
||||
## [1.409.1](https://github.com/windmill-labs/windmill/compare/v1.409.0...v1.409.1) (2024-10-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **apidocs:** fix generated openapi files ([d24e153](https://github.com/windmill-labs/windmill/commit/d24e1530655d27ffda9bb4c19471dc1431124cc2))
|
||||
* **git-sync:** propagate update of folders with git sync ([6abb346](https://github.com/windmill-labs/windmill/commit/6abb346013da4a907a860713a8a67642985b8025))
|
||||
|
||||
## [1.409.0](https://github.com/windmill-labs/windmill/compare/v1.408.1...v1.409.0) (2024-10-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** unify all triggers UX and simplify flow settings ([#4259](https://github.com/windmill-labs/windmill/issues/4259)) ([91a3d06](https://github.com/windmill-labs/windmill/commit/91a3d065298cce7a882464fa0cd31d8f1ae9dda2))
|
||||
* Scroll to element in virtual list when clicking on graph point ([#4532](https://github.com/windmill-labs/windmill/issues/4532)) ([7126ba1](https://github.com/windmill-labs/windmill/commit/7126ba12c7eb52d2cfbe8d83311b5592a5707bce))
|
||||
* **sso:** adding the ability to define a custom display name for sso ([#4529](https://github.com/windmill-labs/windmill/issues/4529)) ([99c5b3e](https://github.com/windmill-labs/windmill/commit/99c5b3ecdacb1158c2cba5b891c4c3b8b70c3b6a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add indexer backup lock to fit the deployment model ([#4531](https://github.com/windmill-labs/windmill/issues/4531)) ([411bce7](https://github.com/windmill-labs/windmill/commit/411bce7e13aabfa53db80d55261ea4511f6d1ae9))
|
||||
* **app:** accept connecting to non yet existing state output for convenience ([9eb1ecc](https://github.com/windmill-labs/windmill/commit/9eb1ecc9f3017e2f4284a827a2bcd21f36b1b8ac))
|
||||
* **app:** improve absolute url handling in download button and downloadFile ([dcdbf1a](https://github.com/windmill-labs/windmill/commit/dcdbf1afb4d5a18e00b9bbb1eb0bef129ea5667f))
|
||||
* **app:** make s3 uploads persistent across tabs change ([c3b536b](https://github.com/windmill-labs/windmill/commit/c3b536b1b8069898131768867a187b186b21e537))
|
||||
* canceled jobs button reporting 0 jobs cancelled ([#4534](https://github.com/windmill-labs/windmill/issues/4534)) ([e736572](https://github.com/windmill-labs/windmill/commit/e736572db10929ae5e123c4f6cef73b8e90fc29b))
|
||||
* **python-client:** improve get_job_status for running jobs ([a8c4ea2](https://github.com/windmill-labs/windmill/commit/a8c4ea2334d2535fa7d5d43f58d65565afe8f3e5))
|
||||
* **ui:** dark mode support for queue metrics based critical alert ([#4535](https://github.com/windmill-labs/windmill/issues/4535)) ([f38b3d1](https://github.com/windmill-labs/windmill/commit/f38b3d14e8092ae58817511aea91a4e77725ead6))
|
||||
|
||||
## [1.408.1](https://github.com/windmill-labs/windmill/compare/v1.408.0...v1.408.1) (2024-10-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix deno cache --allow-import on deno 2 ([42fe31f](https://github.com/windmill-labs/windmill/commit/42fe31f804c9e6643cd90167494e45270831e013))
|
||||
|
||||
## [1.408.0](https://github.com/windmill-labs/windmill/compare/v1.407.2...v1.408.0) (2024-10-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **app builder:** file download helper ([#4511](https://github.com/windmill-labs/windmill/issues/4511)) ([f82f091](https://github.com/windmill-labs/windmill/commit/f82f09129096cfff975370d8bb7b6d832a2b8f9f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** handle case where 'toString' is a schema field ([568cc66](https://github.com/windmill-labs/windmill/commit/568cc66932fb0470f5e89de7b02d94dba4050638))
|
||||
* **frontend:** s3 file uploader works on public apps too ([982dde2](https://github.com/windmill-labs/windmill/commit/982dde2b9dfe6d9eda300c683af729d97a03cb4d))
|
||||
* **frontend:** set unused schema property fields to null ([be11240](https://github.com/windmill-labs/windmill/commit/be112408e7c4601314726e9517c37daaeaa1bf09))
|
||||
* improve workflow as code row-lock on db to handle more concurrency ([d2c4d3f](https://github.com/windmill-labs/windmill/commit/d2c4d3fa207379cb0b8ac180f6ccc759045580e8))
|
||||
|
||||
## [1.407.2](https://github.com/windmill-labs/windmill/compare/v1.407.1...v1.407.2) (2024-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve default properties of new nodes of flows (suspend, branchone, branchall) ([d9bdc5a](https://github.com/windmill-labs/windmill/commit/d9bdc5a5b08dd4d0381304656af097315398c9d4))
|
||||
|
||||
## [1.407.1](https://github.com/windmill-labs/windmill/compare/v1.407.0...v1.407.1) (2024-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve handling of empty lock files on deno 2.0 ([7ca5bf2](https://github.com/windmill-labs/windmill/commit/7ca5bf2faeff44a7543b1afa9369c140fcb71dfc))
|
||||
|
||||
## [1.407.0](https://github.com/windmill-labs/windmill/compare/v1.406.0...v1.407.0) (2024-10-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* upgrade to deno 2 ([26b11a0](https://github.com/windmill-labs/windmill/commit/26b11a00150acbe101abe4bb542f24379da0cc56))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update internal deno runtime to latest (deno 2.0) ([c3a5736](https://github.com/windmill-labs/windmill/commit/c3a57366419882ea2de1938bea592c795b1a1d03))
|
||||
|
||||
## [1.406.0](https://github.com/windmill-labs/windmill/compare/v1.405.5...v1.406.0) (2024-10-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **frontend:** components can be moved inside containers by holding ctrl/cmd ([111bfc6](https://github.com/windmill-labs/windmill/commit/111bfc6a659037ae7029e8f557256e2fffcf979b))
|
||||
* **monitoring:** Critical Alerts for Jobs Waiting in Queue [enterprise] ([#4491](https://github.com/windmill-labs/windmill/issues/4491)) ([d90d6c2](https://github.com/windmill-labs/windmill/commit/d90d6c2b896c5f99e00681656f376b180901f272))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** instance sync push does not require sync pull ([257f097](https://github.com/windmill-labs/windmill/commit/257f0971f86938da71b879f32d93473976eaa920))
|
||||
* remove monaco-editor for app preview code path for faster app loads ([7b05033](https://github.com/windmill-labs/windmill/commit/7b0503332d1bdd7f5999a5ef99150f8c9f6f18be))
|
||||
|
||||
## [1.405.5](https://github.com/windmill-labs/windmill/compare/v1.405.4...v1.405.5) (2024-10-04)
|
||||
|
||||
|
||||
|
||||
@@ -175,9 +175,9 @@ RUN /usr/local/bin/python3 -m pip install pip-tools
|
||||
COPY --from=builder /frontend/build /static_frontend
|
||||
COPY --from=builder /windmill/target/release/windmill ${APP}/windmill
|
||||
|
||||
COPY --from=denoland/deno:1.46.3 --chmod=755 /usr/bin/deno /usr/bin/deno
|
||||
COPY --from=denoland/deno:2.0.0 --chmod=755 /usr/bin/deno /usr/bin/deno
|
||||
|
||||
COPY --from=oven/bun:1.1.27 /usr/local/bin/bun /usr/bin/bun
|
||||
COPY --from=oven/bun:1.1.30 /usr/local/bin/bun /usr/bin/bun
|
||||
|
||||
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
backend/.sqlx/query-0ee63ef2dd5c88edba2a1f56d31f29876724922f148dad7af35b36efbf70207a.json
generated
Normal file
14
backend/.sqlx/query-0ee63ef2dd5c88edba2a1f56d31f29876724922f148dad7af35b36efbf70207a.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM healthchecks WHERE check_type = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0ee63ef2dd5c88edba2a1f56d31f29876724922f148dad7af35b36efbf70207a"
|
||||
}
|
||||
15
backend/.sqlx/query-14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d.json
generated
Normal file
15
backend/.sqlx/query-14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO concurrency_locks (id, last_locked_at, owner)\n VALUES ($1, now(), $2)\n ON CONFLICT (id)\n DO UPDATE SET\n last_locked_at = now(),\n owner = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d"
|
||||
}
|
||||
24
backend/.sqlx/query-1ca5bc2d35c0498b587fd0618434def64233dc4f8fc3344d8d74be8e96ded659.json
generated
Normal file
24
backend/.sqlx/query-1ca5bc2d35c0498b587fd0618434def64233dc4f8fc3344d8d74be8e96ded659.json
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM schedule WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "1ca5bc2d35c0498b587fd0618434def64233dc4f8fc3344d8d74be8e96ded659"
|
||||
}
|
||||
12
backend/.sqlx/query-2041526bc58872d71f91f7698144039bd67f8e37895befa94a15b7e4019e114b.json
generated
Normal file
12
backend/.sqlx/query-2041526bc58872d71f91f7698144039bd67f8e37895befa94a15b7e4019e114b.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM healthchecks WHERE healthy = true AND created_at < NOW() - INTERVAL '14 days'",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2041526bc58872d71f91f7698144039bd67f8e37895befa94a15b7e4019e114b"
|
||||
}
|
||||
14
backend/.sqlx/query-27920aaa55666ffc14a36a247f89ff7994ee40d3953b9f772d0e0ab999bccb7b.json
generated
Normal file
14
backend/.sqlx/query-27920aaa55666ffc14a36a247f89ff7994ee40d3953b9f772d0e0ab999bccb7b.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO healthchecks (check_type, healthy) VALUES ($1, false)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "27920aaa55666ffc14a36a247f89ff7994ee40d3953b9f772d0e0ab999bccb7b"
|
||||
}
|
||||
24
backend/.sqlx/query-31bc3dcea29be9cc0242771d25a232f173446d29c08fc29ddb8d55294f2c070e.json
generated
Normal file
24
backend/.sqlx/query-31bc3dcea29be9cc0242771d25a232f173446d29c08fc29ddb8d55294f2c070e.json
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM http_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "31bc3dcea29be9cc0242771d25a232f173446d29c08fc29ddb8d55294f2c070e"
|
||||
}
|
||||
22
backend/.sqlx/query-34a45763bb4d14162f4cd3fa07cd8020f1f6085f4ee85f5eab3458637edf26cd.json
generated
Normal file
22
backend/.sqlx/query-34a45763bb4d14162f4cd3fa07cd8020f1f6085f4ee85f5eab3458637edf26cd.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT created_at FROM healthchecks WHERE check_type = $1 ORDER BY created_at DESC LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "34a45763bb4d14162f4cd3fa07cd8020f1f6085f4ee85f5eab3458637edf26cd"
|
||||
}
|
||||
29
backend/.sqlx/query-3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30.json
generated
Normal file
29
backend/.sqlx/query-3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30.json
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT COUNT(*) as count, \n MIN(scheduled_for) as oldest_job\n FROM queue \n WHERE tag = $1 \n AND scheduled_for <= NOW() - $2::interval \n AND running = false\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "oldest_job",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Interval"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "3ecb25b05d6c14b499f9b00af42ae74134728899f6b59c68b246979bc5143e30"
|
||||
}
|
||||
15
backend/.sqlx/query-57e270e032e8c04dda7b5c1ca949861756b3ad367a4a500728332a7cb91560a4.json
generated
Normal file
15
backend/.sqlx/query-57e270e032e8c04dda7b5c1ca949861756b3ad367a4a500728332a7cb91560a4.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE concurrency_locks SET\n last_locked_at = now()\n WHERE id = $1 AND owner = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "57e270e032e8c04dda7b5c1ca949861756b3ad367a4a500728332a7cb91560a4"
|
||||
}
|
||||
23
backend/.sqlx/query-5fc6b4a4dbb7875bdec76f876c18543435a95b019b20081f52f6ed6f4457e3c7.json
generated
Normal file
23
backend/.sqlx/query-5fc6b4a4dbb7875bdec76f876c18543435a95b019b20081f52f6ed6f4457e3c7.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5fc6b4a4dbb7875bdec76f876c18543435a95b019b20081f52f6ed6f4457e3c7"
|
||||
}
|
||||
22
backend/.sqlx/query-5fd70c70ce52cbc51fa9124cb05f82b5951f17d1b7eade53c6d89253d55f8b9f.json
generated
Normal file
22
backend/.sqlx/query-5fd70c70ce52cbc51fa9124cb05f82b5951f17d1b7eade53c6d89253d55f8b9f.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT owner FROM concurrency_locks WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "owner",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5fd70c70ce52cbc51fa9124cb05f82b5951f17d1b7eade53c6d89253d55f8b9f"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO windmill_migrations (name) VALUES ('bypassrls_1-2')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "722a3096f03d25ef94292d53801d41037de4bc69dd434232029c731cbbcbc22f"
|
||||
}
|
||||
14
backend/.sqlx/query-900ac59515e4283f4b57516210575dfe92f74a7220ed69e61899a6e0f053d9cd.json
generated
Normal file
14
backend/.sqlx/query-900ac59515e4283f4b57516210575dfe92f74a7220ed69e61899a6e0f053d9cd.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO concurrency_locks (id, last_locked_at) VALUES ($1, NOW()) ON CONFLICT (id) DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "900ac59515e4283f4b57516210575dfe92f74a7220ed69e61899a6e0f053d9cd"
|
||||
}
|
||||
23
backend/.sqlx/query-a7f5431e3b8960e9dc46fae69dd4391516d8b169186548ec44528c84078b80d8.json
generated
Normal file
23
backend/.sqlx/query-a7f5431e3b8960e9dc46fae69dd4391516d8b169186548ec44528c84078b80d8.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "a7f5431e3b8960e9dc46fae69dd4391516d8b169186548ec44528c84078b80d8"
|
||||
}
|
||||
14
backend/.sqlx/query-ad42118ccf6a9d2d1e072c4df064ddf964a5b3cd088fc162d0d8222325d4a5ea.json
generated
Normal file
14
backend/.sqlx/query-ad42118ccf6a9d2d1e072c4df064ddf964a5b3cd088fc162d0d8222325d4a5ea.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE healthchecks SET healthy = true WHERE check_type = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ad42118ccf6a9d2d1e072c4df064ddf964a5b3cd088fc162d0d8222325d4a5ea"
|
||||
}
|
||||
@@ -18,8 +18,8 @@
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
false
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"
|
||||
|
||||
@@ -52,7 +52,8 @@
|
||||
"trigger",
|
||||
"failure",
|
||||
"command",
|
||||
"approval"
|
||||
"approval",
|
||||
"preprocessor"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
24
backend/.sqlx/query-c060b8bbc5af7d2e7d0aaff64f0f62ec9db58611a99b0ba7f0375638b128ab89.json
generated
Normal file
24
backend/.sqlx/query-c060b8bbc5af7d2e7d0aaff64f0f62ec9db58611a99b0ba7f0375638b128ab89.json
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT schedule FROM schedule WHERE path = $1 AND script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "schedule",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "c060b8bbc5af7d2e7d0aaff64f0f62ec9db58611a99b0ba7f0375638b128ab89"
|
||||
}
|
||||
23
backend/.sqlx/query-c4e1873bfc7b905e7299a021f4baa2a97e95f4797c5e11f37822e19828422b7e.json
generated
Normal file
23
backend/.sqlx/query-c4e1873bfc7b905e7299a021f4baa2a97e95f4797c5e11f37822e19828422b7e.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE concurrency_locks SET last_locked_at = NOW() WHERE id = $1 AND last_locked_at < NOW() - INTERVAL '1 second' * $2 RETURNING 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Float8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "c4e1873bfc7b905e7299a021f4baa2a97e95f4797c5e11f37822e19828422b7e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin, scopes)\n VALUES ($1, $2, $3, $4, $5, $6)",
|
||||
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin, scopes, workspace_id)\n VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -10,10 +10,11 @@
|
||||
"Varchar",
|
||||
"Timestamptz",
|
||||
"Bool",
|
||||
"TextArray"
|
||||
"TextArray",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "34ad8a2a5bd89b9b8e25847a7e5e94ef99e35a178ad6328c1bcde2a6d6f88cb5"
|
||||
"hash": "c624f15f3e321b1eecf123da9bf0b18e8c1d16ef25ffb9d04e5447d0d583d55c"
|
||||
}
|
||||
59
backend/.sqlx/query-c7ee7ce64686cef41cebd99ad7ef31572fc1bf12e6ae473fd58fafb025989965.json
generated
Normal file
59
backend/.sqlx/query-c7ee7ce64686cef41cebd99ad7ef31572fc1bf12e6ae473fd58fafb025989965.json
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "label",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token_prefix",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "expiration",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "last_used_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "scopes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "c7ee7ce64686cef41cebd99ad7ef31572fc1bf12e6ae473fd58fafb025989965"
|
||||
}
|
||||
22
backend/.sqlx/query-cecf1addc4aecb087a14786b2a9165895ca61ef042947c7314f66514d7f29edc.json
generated
Normal file
22
backend/.sqlx/query-cecf1addc4aecb087a14786b2a9165895ca61ef042947c7314f66514d7f29edc.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT last_locked_at\n FROM concurrency_locks\n WHERE id = $1\n FOR UPDATE",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "last_locked_at",
|
||||
"type_info": "Timestamp"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "cecf1addc4aecb087a14786b2a9165895ca61ef042947c7314f66514d7f29edc"
|
||||
}
|
||||
23
backend/.sqlx/query-d7a0f19f9e18d2ea49316012375ad78b69292ba091d69880945e42bebe890d66.json
generated
Normal file
23
backend/.sqlx/query-d7a0f19f9e18d2ea49316012375ad78b69292ba091d69880945e42bebe890d66.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "d7a0f19f9e18d2ea49316012375ad78b69292ba091d69880945e42bebe890d66"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT name FROM windmill_migrations WHERE name = 'bypassrls_1-2')",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM healthchecks WHERE check_type = $1 AND healthy = false)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -10,11 +10,13 @@
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "eb1f916f9beea3eea83ce359f5305d0cfb0d6cdba9cc56c6139f57e46345f843"
|
||||
"hash": "eb932b613a6dbb2cdff97e5512d42b538ba83115c0ea798be00b01659600f45a"
|
||||
}
|
||||
59
backend/.sqlx/query-eff32aeac25a75d06f73e08c26dd3fd25f6b85cbea870505751c6a82457ae1da.json
generated
Normal file
59
backend/.sqlx/query-eff32aeac25a75d06f73e08c26dd3fd25f6b85cbea870505751c6a82457ae1da.json
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "label",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token_prefix",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "expiration",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "last_used_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "scopes",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "eff32aeac25a75d06f73e08c26dd3fd25f6b85cbea870505751c6a82457ae1da"
|
||||
}
|
||||
23
backend/.sqlx/query-f06e0e4fa358b26792df22fff48b71a6fcfa1e5603ea472892917c1accd1aafb.json
generated
Normal file
23
backend/.sqlx/query-f06e0e4fa358b26792df22fff48b71a6fcfa1e5603ea472892917c1accd1aafb.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "f06e0e4fa358b26792df22fff48b71a6fcfa1e5603ea472892917c1accd1aafb"
|
||||
}
|
||||
763
backend/Cargo.lock
generated
763
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.405.5"
|
||||
version = "1.409.3"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -14,6 +14,7 @@ members = [
|
||||
"./windmill-audit",
|
||||
"./windmill-git-sync",
|
||||
"./windmill-indexer",
|
||||
"./windmill-macros",
|
||||
"./parsers/windmill-parser",
|
||||
"./parsers/windmill-parser-ts",
|
||||
"./parsers/windmill-parser-wasm",
|
||||
@@ -23,11 +24,11 @@ members = [
|
||||
"./parsers/windmill-parser-py",
|
||||
"./parsers/windmill-parser-py-imports",
|
||||
"./parsers/windmill-sql-datatype-parser-wasm",
|
||||
"./parsers/windmill-parser-yaml",
|
||||
"./parsers/windmill-parser-yaml", "windmill-macros",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.405.5"
|
||||
version = "1.409.3"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -39,6 +40,9 @@ path = "./src/main.rs"
|
||||
opt-level = 0
|
||||
incremental = true
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise", "windmill-indexer/enterprise"]
|
||||
@@ -113,6 +117,7 @@ windmill-common = { path = "./windmill-common", default-features = false }
|
||||
windmill-audit = { path = "./windmill-audit" }
|
||||
windmill-git-sync = { path = "./windmill-git-sync" }
|
||||
windmill-indexer = {path = "./windmill-indexer"}
|
||||
windmill-macros = {path = "./windmill-macros"}
|
||||
windmill-parser = { path = "./parsers/windmill-parser" }
|
||||
windmill-parser-ts = { path = "./parsers/windmill-parser-ts" }
|
||||
windmill-parser-py = { path = "./parsers/windmill-parser-py" }
|
||||
@@ -172,20 +177,24 @@ tokio-util = { version = "^0", features = ["io"] }
|
||||
json-pointer = "^0"
|
||||
itertools = "^0"
|
||||
regex = "^1"
|
||||
deno_fetch = "0.187.0"
|
||||
deno_tls = "0.150.0"
|
||||
deno_console = "0.163.0"
|
||||
deno_url = "0.163.0"
|
||||
deno_webidl = "0.163.0"
|
||||
deno_web = "0.194.0"
|
||||
deno_net = "0.155.0"
|
||||
deno_core = "0.299.0"
|
||||
deno_ast = { version = "=0.40.0", features = ["transpiling"] }
|
||||
|
||||
deno_fetch = "0.195.0"
|
||||
deno_tls = "0.158.0"
|
||||
deno_console = "0.171.0"
|
||||
deno_url = "0.171.0"
|
||||
deno_webidl = "0.171.0"
|
||||
deno_web = "0.202.0"
|
||||
deno_net = "0.163.0"
|
||||
deno_core = "0.311.0"
|
||||
deno_ast = { version = "=0.42.2", features = ["transpiling"] }
|
||||
|
||||
swc_common = "=0.37.5"
|
||||
swc_ecma_parser = "=0.149.1"
|
||||
swc_ecma_ast = "=0.118.2"
|
||||
swc_ecma_visit = "=0.104.8"
|
||||
|
||||
async-recursion = "^1"
|
||||
swc_common = "=0.33.26"
|
||||
swc_ecma_parser = "=0.144.3"
|
||||
swc_ecma_ast = "=0.113.7"
|
||||
swc_ecma_visit = "=0.99.1"
|
||||
|
||||
base64 = "0.21.0"
|
||||
base32 = "^0"
|
||||
hmac = "0.12.1"
|
||||
@@ -270,11 +279,12 @@ tikv-jemallocator = { version = "0.5" }
|
||||
tikv-jemalloc-sys = { version = "^0.5" }
|
||||
tikv-jemalloc-ctl = { version = "^0.5" }
|
||||
|
||||
# 0.1.12 broken (nested dependency of swc_common)
|
||||
triomphe = "<0.1.12"
|
||||
triomphe = "^0"
|
||||
|
||||
tantivy = "0.22.0"
|
||||
|
||||
# Macro-related
|
||||
proc-macro2 = "1.0"
|
||||
pulldown-cmark = "0.9"
|
||||
toml = "0.7"
|
||||
syn = { version = "2.0.74", features = ["full"] }
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
CREATE POLICY admin_policy ON account TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON app TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON audit TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON capture TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON completed_job TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON flow TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON folder TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON queue TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON raw_app TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON resource TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON schedule TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON script TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON usr_to_group TO windmill_admin USING (true);
|
||||
CREATE POLICY admin_policy ON variable TO windmill_admin USING (true);
|
||||
@@ -1 +1 @@
|
||||
0f5f42d2f8f5f1af05c8086f3dc7ad38d83750df
|
||||
0428068e4fbbd1380a4d8bbaab5c8e7955decdb8
|
||||
|
||||
1
backend/migrations/20241006144414_admin_policy.down.sql
Normal file
1
backend/migrations/20241006144414_admin_policy.down.sql
Normal file
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
24
backend/migrations/20241006144414_admin_policy.up.sql
Normal file
24
backend/migrations/20241006144414_admin_policy.up.sql
Normal file
@@ -0,0 +1,24 @@
|
||||
-- Add up migration script here
|
||||
DO
|
||||
$$
|
||||
DECLARE
|
||||
tbl_name text;
|
||||
policy_exists boolean;
|
||||
tbl_names text[] := ARRAY['account', 'app', 'audit', 'capture', 'completed_job', 'flow', 'folder', 'http_trigger', 'queue', 'raw_app', 'resource', 'schedule', 'script', 'usr_to_group', 'variable'];
|
||||
BEGIN
|
||||
FOR tbl_name IN SELECT unnest(tbl_names)
|
||||
LOOP
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_policies
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename = tbl_name
|
||||
AND policyname = 'admin_policy'
|
||||
) INTO policy_exists;
|
||||
|
||||
IF NOT policy_exists THEN
|
||||
EXECUTE format('CREATE POLICY admin_policy ON %I TO windmill_admin USING (true);', tbl_name);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Drop the alert_locks table
|
||||
DROP TABLE IF EXISTS concurrency_locks;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Create the alert_locks table
|
||||
CREATE TABLE concurrency_locks (
|
||||
id VARCHAR PRIMARY KEY,
|
||||
last_locked_at TIMESTAMP NOT NULL,
|
||||
owner VARCHAR NULL
|
||||
);
|
||||
@@ -15,13 +15,13 @@ use windmill_parser::{
|
||||
|
||||
use swc_common::{sync::Lrc, FileName, SourceMap, SourceMapper, Span, Spanned};
|
||||
use swc_ecma_ast::{
|
||||
ArrayLit, AssignPat, BigInt, BindingIdent, Bool, Decl, ExportDecl, Expr, FnDecl, Ident, Lit,
|
||||
MemberExpr, MemberProp, ModuleDecl, ModuleItem, Number, ObjectLit, ObjectPat, Param, Pat, Str,
|
||||
TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType, TsOptionalType,
|
||||
TsParenthesizedType, TsPropertySignature, TsType, TsTypeAnn, TsTypeElement, TsTypeLit,
|
||||
TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
|
||||
ArrayLit, AssignPat, BigInt, BindingIdent, Bool, Decl, ExportDecl, Expr, FnDecl, Ident,
|
||||
IdentName, Lit, MemberExpr, MemberProp, ModuleDecl, ModuleItem, Number, ObjectLit, ObjectPat,
|
||||
Param, Pat, Str, TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType,
|
||||
TsOptionalType, TsParenthesizedType, TsPropertySignature, TsType, TsTypeAnn, TsTypeElement,
|
||||
TsTypeLit, TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
|
||||
};
|
||||
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax, TsConfig};
|
||||
use swc_ecma_parser::{lexer::Lexer, EsSyntax, Parser, StringInput, Syntax, TsSyntax};
|
||||
|
||||
use regex::Regex;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -48,9 +48,9 @@ impl Visit for ImportsFinder {
|
||||
|
||||
pub fn parse_expr_for_imports(code: &str) -> anyhow::Result<Vec<String>> {
|
||||
let cm: Lrc<SourceMap> = Default::default();
|
||||
let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()), code.into());
|
||||
let fm = cm.new_source_file(FileName::Custom("main.d.ts".into()).into(), code.into());
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Typescript(TsConfig::default()),
|
||||
Syntax::Typescript(TsSyntax::default()),
|
||||
// EsVersion defaults to es5
|
||||
Default::default(),
|
||||
StringInput::from(&*fm),
|
||||
@@ -69,7 +69,7 @@ pub fn parse_expr_for_imports(code: &str) -> anyhow::Result<Vec<String>> {
|
||||
})?;
|
||||
|
||||
let mut visitor = ImportsFinder { imports: HashSet::new() };
|
||||
swc_ecma_visit::visit_module(&mut visitor, &expr);
|
||||
visitor.visit_module(&expr);
|
||||
|
||||
Ok(visitor.imports.into_iter().collect())
|
||||
}
|
||||
@@ -87,7 +87,7 @@ impl Visit for OutputFinder {
|
||||
c.visit_with(self);
|
||||
}
|
||||
match m {
|
||||
MemberExpr { obj, prop: MemberProp::Ident(Ident { sym, .. }), .. } => {
|
||||
MemberExpr { obj, prop: MemberProp::Ident(IdentName { sym, .. }), .. } => {
|
||||
match *obj.to_owned() {
|
||||
Expr::Ident(Ident { sym: sym_i, .. }) => {
|
||||
self.idents.insert((sym_i.to_string(), sym.to_string()));
|
||||
@@ -102,10 +102,10 @@ impl Visit for OutputFinder {
|
||||
|
||||
pub fn parse_expr_for_ids(code: &str) -> anyhow::Result<Vec<(String, String)>> {
|
||||
let cm: Lrc<SourceMap> = Default::default();
|
||||
let fm = cm.new_source_file(FileName::Custom("main.ts".into()), code.into());
|
||||
let fm = cm.new_source_file(FileName::Custom("main.ts".into()).into(), code.into());
|
||||
let lexer = Lexer::new(
|
||||
// We want to parse ecmascript
|
||||
Syntax::Es(EsConfig { jsx: false, ..Default::default() }),
|
||||
Syntax::Es(EsSyntax { jsx: false, ..Default::default() }),
|
||||
// EsVersion defaults to es5
|
||||
Default::default(),
|
||||
StringInput::from(&*fm),
|
||||
@@ -124,7 +124,7 @@ pub fn parse_expr_for_ids(code: &str) -> anyhow::Result<Vec<(String, String)>> {
|
||||
})?;
|
||||
|
||||
let mut visitor = OutputFinder { idents: HashSet::new() };
|
||||
swc_ecma_visit::visit_module(&mut visitor, &expr);
|
||||
visitor.visit_module(&expr);
|
||||
|
||||
Ok(visitor.idents.into_iter().collect())
|
||||
}
|
||||
@@ -135,10 +135,10 @@ pub fn parse_deno_signature(
|
||||
main_override: Option<String>,
|
||||
) -> anyhow::Result<MainArgSignature> {
|
||||
let cm: Lrc<SourceMap> = Default::default();
|
||||
let fm = cm.new_source_file(FileName::Custom("main.ts".into()), code.into());
|
||||
let fm = cm.new_source_file(FileName::Custom("main.ts".into()).into(), code.into());
|
||||
let lexer = Lexer::new(
|
||||
// We want to parse ecmascript
|
||||
Syntax::Typescript(TsConfig::default()),
|
||||
Syntax::Typescript(TsSyntax::default()),
|
||||
// EsVersion defaults to es5
|
||||
Default::default(),
|
||||
StringInput::from(&*fm),
|
||||
|
||||
@@ -116,7 +116,7 @@ where
|
||||
|
||||
pub fn main() -> anyhow::Result<()> {
|
||||
#[cfg(feature = "deno_core")]
|
||||
deno_core::JsRuntime::init_platform(None);
|
||||
deno_core::JsRuntime::init_platform(None, false);
|
||||
create_and_run_current_thread_inner(windmill_main())
|
||||
}
|
||||
|
||||
@@ -476,7 +476,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
|
||||
#[cfg(feature = "tantivy")]
|
||||
let (index_reader, index_writer) = if should_index_jobs {
|
||||
let (r, w) = windmill_indexer::indexer_ee::init_index().await?;
|
||||
let (r, w) = windmill_indexer::indexer_ee::init_index(&db).await?;
|
||||
(Some(r), Some(w))
|
||||
} else {
|
||||
(None, None)
|
||||
|
||||
@@ -27,7 +27,7 @@ use windmill_api::{
|
||||
DEFAULT_BODY_LIMIT, IS_SECURE, OAUTH_CLIENTS, REQUEST_SIZE_LIMIT, SAML_METADATA, SCIM_TOKEN,
|
||||
};
|
||||
#[cfg(feature = "enterprise")]
|
||||
use windmill_common::ee::worker_groups_alerts;
|
||||
use windmill_common::ee::{worker_groups_alerts, jobs_waiting_alerts};
|
||||
use windmill_common::{
|
||||
auth::JWT_SECRET,
|
||||
ee::CriticalErrorChannel,
|
||||
@@ -1061,12 +1061,20 @@ pub async fn monitor_db(
|
||||
}
|
||||
};
|
||||
|
||||
let jobs_waiting_alerts_f = async {
|
||||
#[cfg(feature = "enterprise")]
|
||||
if server_mode {
|
||||
jobs_waiting_alerts(&db).await;
|
||||
}
|
||||
};
|
||||
|
||||
join!(
|
||||
expired_items_f,
|
||||
zombie_jobs_f,
|
||||
expose_queue_metrics_f,
|
||||
verify_license_key_f,
|
||||
worker_groups_alerts_f
|
||||
worker_groups_alerts_f,
|
||||
jobs_waiting_alerts_f,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2745,7 +2745,7 @@ async fn test_flow_lock_all(db: Pool<Postgres>) {
|
||||
"lock": null,
|
||||
"path": null,
|
||||
"type": "rawscript",
|
||||
"content": "import * as wmill from \"https://deno.land/x/windmill@v1.50.0/mod.ts\"\n\nexport async function main() {\n return \"Hello\"\n}\n",
|
||||
"content": "import * as wmill from \"https://deno.land/x/windmill@v1.50.0/mod.ts\"\n\nexport async function main() {\n return wmill\n}\n",
|
||||
"language": "deno",
|
||||
"input_transforms": {}
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.405.5
|
||||
version: 1.409.3
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -2789,7 +2789,14 @@ paths:
|
||||
oauth:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
display_name:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
saml:
|
||||
type: string
|
||||
required:
|
||||
@@ -4013,6 +4020,42 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Script"
|
||||
|
||||
/w/{workspace}/scripts/get_triggers_count/{path}:
|
||||
get:
|
||||
summary: get triggers count of script
|
||||
operationId: getTriggersCountOfScript
|
||||
tags:
|
||||
- script
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: triggers count
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TriggersCount"
|
||||
|
||||
/w/{workspace}/scripts/list_tokens/{path}:
|
||||
get:
|
||||
summary: get tokens with script scope
|
||||
operationId: listTokensOfScript
|
||||
tags:
|
||||
- script
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: tokens list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TruncatedToken"
|
||||
|
||||
/w/{workspace}/scripts/get/draft/{path}:
|
||||
get:
|
||||
summary: get script by path with draft
|
||||
@@ -4617,6 +4660,43 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Flow"
|
||||
|
||||
/w/{workspace}/flows/get_triggers_count/{path}:
|
||||
get:
|
||||
summary: get triggers count of flow
|
||||
operationId: getTriggersCountOfFlow
|
||||
tags:
|
||||
- flow
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: triggers count
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TriggersCount"
|
||||
|
||||
/w/{workspace}/flows/list_tokens/{path}:
|
||||
get:
|
||||
summary: get tokens with flow scope
|
||||
operationId: listTokensOfFlow
|
||||
tags:
|
||||
- flow
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
responses:
|
||||
"200":
|
||||
description: tokens list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TruncatedToken"
|
||||
|
||||
|
||||
/w/{workspace}/flows/toggle_workspace_error_handler/{path}:
|
||||
post:
|
||||
summary: Toggle ON and OFF the workspace error handler for a given flow
|
||||
@@ -10247,6 +10327,8 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
required:
|
||||
- token_prefix
|
||||
- created_at
|
||||
@@ -10264,6 +10346,8 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
workspace_id:
|
||||
type: string
|
||||
|
||||
NewTokenImpersonate:
|
||||
type: object
|
||||
@@ -10275,6 +10359,8 @@ components:
|
||||
format: date-time
|
||||
impersonate_email:
|
||||
type: string
|
||||
workspace_id:
|
||||
type: string
|
||||
required:
|
||||
- impersonate_email
|
||||
|
||||
@@ -11111,6 +11197,23 @@ components:
|
||||
- requires_auth
|
||||
- http_method
|
||||
|
||||
TriggersCount:
|
||||
type: object
|
||||
properties:
|
||||
primary_schedule:
|
||||
type: object
|
||||
properties:
|
||||
schedule:
|
||||
type: string
|
||||
schedule_count:
|
||||
type: number
|
||||
http_routes_count:
|
||||
type: number
|
||||
webhook_count:
|
||||
type: number
|
||||
email_count:
|
||||
type: number
|
||||
|
||||
Group:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -199,11 +199,6 @@ pub async fn migrate(db: &DB) -> Result<(), Error> {
|
||||
Err(err) => Err(err),
|
||||
}?;
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
if let Err(e) = windmill_migrations(&mut custom_migrator, db).await {
|
||||
tracing::error!("Could not apply windmill custom migrations: {e:#}")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -497,33 +492,6 @@ async fn fix_job_completed_index(db: &DB) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
async fn windmill_migrations(migrator: &mut CustomMigrator, db: &DB) -> Result<(), Error> {
|
||||
if std::env::var("MIGRATION_NO_BYPASSRLS").is_ok() {
|
||||
migrator.lock().await?;
|
||||
let has_done_migration = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT name FROM windmill_migrations WHERE name = 'bypassrls_1-2')",
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
|
||||
if !has_done_migration {
|
||||
let query = include_str!("../../custom_migrations/bypassrls_1.sql");
|
||||
tracing::info!("Applying bypassrls_1.sql");
|
||||
let mut tx: sqlx::Transaction<'_, Postgres> = db.begin().await?;
|
||||
tx.execute(query).await?;
|
||||
tracing::info!("Applied bypassrls_1.sql");
|
||||
sqlx::query!("INSERT INTO windmill_migrations (name) VALUES ('bypassrls_1-2')")
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
}
|
||||
migrator.unlock().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ApiAuthed {
|
||||
pub email: String,
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
use crate::triggers::{
|
||||
get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail,
|
||||
};
|
||||
use crate::utils::WithStarredInfoQuery;
|
||||
use crate::{
|
||||
db::DB,
|
||||
@@ -53,6 +56,8 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/update/*path", post(update_flow))
|
||||
.route("/archive/*path", post(archive_flow_by_path))
|
||||
.route("/delete/*path", delete(delete_flow_by_path))
|
||||
.route("/get_triggers_count/*path", get(get_triggers_count))
|
||||
.route("/list_tokens/*path", get(list_tokens))
|
||||
.route("/get/*path", get(get_flow_by_path))
|
||||
.route("/get/draft/*path", get(get_flow_by_path_w_draft))
|
||||
.route("/exists/*path", get(exists_flow_by_path))
|
||||
@@ -874,6 +879,22 @@ async fn update_flow(
|
||||
Ok(nf.path.to_string())
|
||||
}
|
||||
|
||||
async fn get_triggers_count(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<TriggersCount> {
|
||||
let path = path.to_path();
|
||||
get_triggers_count_internal(&db, &w_id, &path, true).await
|
||||
}
|
||||
|
||||
async fn list_tokens(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
|
||||
let path = path.to_path();
|
||||
list_tokens_internal(&db, &w_id, &path, true).await
|
||||
}
|
||||
|
||||
async fn get_flow_by_path(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
|
||||
@@ -275,8 +275,10 @@ pub fn require_is_owner(authed: &ApiAuthed, name: &str) -> Result<()> {
|
||||
|
||||
async fn update_folder(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Extension(webhook): Extension<WebhookShared>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, name)): Path<(String, String)>,
|
||||
Json(mut ng): Json<UpdateFolder>,
|
||||
) -> Result<String> {
|
||||
@@ -367,6 +369,18 @@ async fn update_folder(
|
||||
}
|
||||
}
|
||||
|
||||
handle_deployment_metadata(
|
||||
&authed.email,
|
||||
&authed.username,
|
||||
&db,
|
||||
&w_id,
|
||||
DeployedObject::Folder { path: format!("f/{}", name) },
|
||||
Some(format!("Folder '{}' updated", name)),
|
||||
rsmq,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
|
||||
@@ -24,7 +24,7 @@ use windmill_common::jobs::{
|
||||
format_completed_job_result, format_result, CompletedJobWithFormattedResult, FormattedResult,
|
||||
ENTRYPOINT_OVERRIDE,
|
||||
};
|
||||
use windmill_common::worker::TMP_DIR;
|
||||
use windmill_common::worker::{CLOUD_HOSTED, TMP_DIR};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "parquet"))]
|
||||
use windmill_common::scripts::PREVIEW_IS_CODEBASE_HASH;
|
||||
@@ -81,7 +81,7 @@ use windmill_common::{METRICS_DEBUG_ENABLED, METRICS_ENABLED};
|
||||
use windmill_common::{get_latest_deployed_hash_for_path, BASE_URL};
|
||||
use windmill_queue::{
|
||||
cancel_job, get_queued_job, get_result_by_id_from_running_flow, job_is_complete, push,
|
||||
DecodeQueries, PushArgs, PushArgsOwned, PushIsolationLevel, QueueTransaction,
|
||||
DecodeQueries, PushArgs, PushArgsOwned, PushIsolationLevel,
|
||||
};
|
||||
|
||||
#[cfg(feature = "prometheus")]
|
||||
@@ -544,8 +544,8 @@ pub async fn get_path_for_hash<'c>(
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub async fn get_path_tag_limits_cache_for_hash<'c, R: rsmq_async::RsmqConnection + Send>(
|
||||
tx: &mut QueueTransaction<'c, R>,
|
||||
pub async fn get_path_tag_limits_cache_for_hash(
|
||||
tx: &DB,
|
||||
w_id: &str,
|
||||
hash: i64,
|
||||
) -> error::Result<(
|
||||
@@ -1469,6 +1469,8 @@ async fn cancel_jobs(
|
||||
}
|
||||
}
|
||||
|
||||
uuids.extend(trivial_jobs);
|
||||
|
||||
Ok(Json(uuids))
|
||||
}
|
||||
|
||||
@@ -2810,7 +2812,6 @@ pub async fn run_flow_by_path_inner(
|
||||
let flow_path = flow_path.to_path();
|
||||
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (tag, dedicated_worker, has_preprocessor) = sqlx::query!(
|
||||
"SELECT tag, dedicated_worker, flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor
|
||||
@@ -2821,7 +2822,7 @@ pub async fn run_flow_by_path_inner(
|
||||
flow_path,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.map(|x| (x.tag, x.dedicated_worker, x.has_preprocessor))
|
||||
.ok_or_else(|| {
|
||||
@@ -2834,7 +2835,7 @@ pub async fn run_flow_by_path_inner(
|
||||
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
tx,
|
||||
@@ -2906,14 +2907,13 @@ pub async fn restart_flow(
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
check_license_key_valid().await?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let completed_job = sqlx::query_as::<_, CompletedJob>(
|
||||
"SELECT *, result->'wm_labels' as labels from completed_job WHERE id = $1 and workspace_id = $2",
|
||||
)
|
||||
.bind(job_id)
|
||||
.bind(&w_id)
|
||||
.fetch_optional(&mut tx)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.with_context(|| "Unable to find completed job with the given job UUID")?;
|
||||
|
||||
@@ -2931,7 +2931,7 @@ pub async fn restart_flow(
|
||||
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3007,16 +3007,16 @@ pub async fn run_script_by_path_inner(
|
||||
|
||||
check_scopes(&authed, || format!("run:script/{script_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, _delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3049,6 +3049,11 @@ pub async fn run_script_by_path_inner(
|
||||
Ok((StatusCode::CREATED, uuid.to_string()))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WorkflowAsCodeQuery {
|
||||
pub skip_update: Option<bool>,
|
||||
}
|
||||
|
||||
pub async fn run_workflow_as_code(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
@@ -3056,15 +3061,38 @@ pub async fn run_workflow_as_code(
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path((w_id, job_id, entrypoint)): Path<(String, Uuid, String)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
Query(wkflow_query): Query<WorkflowAsCodeQuery>,
|
||||
Json(task): Json<WorkflowTask>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
|
||||
let mut i = 1;
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
check_license_key_valid().await?;
|
||||
check_tag_available_for_workspace(&w_id, &run_query.tag).await?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
let job = get_queued_job(&job_id, &w_id, &db).await?;
|
||||
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
let job = not_found_if_none(job, "Queued Job", &job_id.to_string())?;
|
||||
let (job_payload, tag, _delete_after_use, timeout) = match job.job_kind {
|
||||
JobKind::Preview => (
|
||||
@@ -3089,7 +3117,7 @@ pub async fn run_workflow_as_code(
|
||||
JobKind::Script => {
|
||||
script_path_to_payload(
|
||||
job.script_path(),
|
||||
&mut tx,
|
||||
&db,
|
||||
&w_id,
|
||||
run_query.skip_preprocessor,
|
||||
)
|
||||
@@ -3098,6 +3126,12 @@ pub async fn run_workflow_as_code(
|
||||
_ => return Err(anyhow::anyhow!("Not supported").into()),
|
||||
};
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let mut extra = HashMap::new();
|
||||
extra.insert(ENTRYPOINT_OVERRIDE.to_string(), to_raw_value(&entrypoint));
|
||||
|
||||
@@ -3106,7 +3140,21 @@ pub async fn run_workflow_as_code(
|
||||
|
||||
let tag = run_query.tag.clone().or(tag).or(Some(job.tag));
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
let (uuid, mut tx) = push(
|
||||
&db,
|
||||
@@ -3133,14 +3181,39 @@ pub async fn run_workflow_as_code(
|
||||
Some(&authed.clone().into()),
|
||||
)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"UPDATE queue SET flow_status = jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], jsonb_set(jsonb_set('{}'::jsonb, '{scheduled_for}', to_jsonb(now()::text)), '{name}', to_jsonb($4::text))) WHERE id = $2 AND workspace_id = $3",
|
||||
uuid.to_string(),
|
||||
job_id,
|
||||
w_id,
|
||||
entrypoint
|
||||
).execute(&mut tx).await?;
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if !wkflow_query.skip_update.unwrap_or(false) {
|
||||
sqlx::query!(
|
||||
"UPDATE queue SET flow_status = jsonb_set(COALESCE(flow_status, '{}'::jsonb), array[$1], jsonb_set(jsonb_set('{}'::jsonb, '{scheduled_for}', to_jsonb(now()::text)), '{name}', to_jsonb($4::text))) WHERE id = $2 AND workspace_id = $3",
|
||||
uuid.to_string(),
|
||||
job_id,
|
||||
w_id,
|
||||
entrypoint
|
||||
).execute(&mut tx).await?;
|
||||
} else {
|
||||
tracing::info!("Skipping update of flow status for job {job_id} in workspace {w_id}");
|
||||
}
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
tracing::info!("workflow_as_code_tracing id {i} ");
|
||||
}
|
||||
|
||||
Ok((StatusCode::CREATED, uuid.to_string()))
|
||||
}
|
||||
|
||||
@@ -3504,15 +3577,13 @@ pub async fn run_wait_result_job_by_path_get(
|
||||
let script_path = script_path.to_path();
|
||||
check_scopes(&authed, || format!("run:script/{script_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3629,15 +3700,13 @@ pub async fn run_wait_result_script_by_path_internal(
|
||||
let script_path = script_path.to_path();
|
||||
check_scopes(&authed, || format!("run:script/{script_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let (job_payload, tag, delete_after_use, timeout) =
|
||||
script_path_to_payload(script_path, &mut tx, &w_id, run_query.skip_preprocessor).await?;
|
||||
script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?;
|
||||
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3689,8 +3758,6 @@ pub async fn run_wait_result_script_by_hash(
|
||||
|
||||
check_queue_too_long(&db, run_query.queue_limit).await?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let hash = script_hash.0;
|
||||
let (
|
||||
path,
|
||||
@@ -3705,7 +3772,7 @@ pub async fn run_wait_result_script_by_hash(
|
||||
delete_after_use,
|
||||
timeout,
|
||||
has_preprocessor,
|
||||
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
|
||||
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
|
||||
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
|
||||
cache_ttl = Some(run_query_cache_ttl);
|
||||
}
|
||||
@@ -3714,7 +3781,7 @@ pub async fn run_wait_result_script_by_hash(
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -3796,7 +3863,6 @@ pub async fn run_wait_result_flow_by_path_internal(
|
||||
let flow_path = flow_path.to_path();
|
||||
check_scopes(&authed, || format!("run:flow/{flow_path}"))?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let scheduled_for = run_query.get_scheduled_for(&db).await?;
|
||||
|
||||
@@ -3809,7 +3875,7 @@ pub async fn run_wait_result_flow_by_path_internal(
|
||||
flow_path,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.fetch_optional(&db)
|
||||
.await?
|
||||
.map(|x| (x.tag, x.dedicated_worker, x.early_return, x.has_preprocessor))
|
||||
.ok_or_else(|| {
|
||||
@@ -3821,7 +3887,7 @@ pub async fn run_wait_result_flow_by_path_internal(
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
@@ -4127,10 +4193,10 @@ async fn run_dependencies_job(
|
||||
JsonRawValue::from_string("true".to_string()).unwrap(),
|
||||
);
|
||||
if language == ScriptLang::Bun {
|
||||
let annotation = windmill_common::worker::get_annotation_ts(&raw_code);
|
||||
let annotation = windmill_common::worker::TypeScriptAnnotations::parse(&raw_code);
|
||||
hm.insert(
|
||||
"npm_mode".to_string(),
|
||||
JsonRawValue::from_string(annotation.npm_mode.to_string()).unwrap(),
|
||||
JsonRawValue::from_string(annotation.npm.to_string()).unwrap(),
|
||||
);
|
||||
}
|
||||
(PushArgs { extra: Some(hm), args: &ehm }, deps)
|
||||
@@ -4293,7 +4359,6 @@ async fn add_batch_jobs(
|
||||
}
|
||||
}
|
||||
"flow" => {
|
||||
let mut tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
|
||||
|
||||
let mut uuids: Vec<Uuid> = Vec::new();
|
||||
let payload = if let Some(ref fv) = batch_info.flow_value {
|
||||
@@ -4311,6 +4376,7 @@ async fn add_batch_jobs(
|
||||
))?
|
||||
}
|
||||
};
|
||||
let mut tx = PushIsolationLevel::IsolatedRoot(db.clone(), rsmq);
|
||||
for _ in 0..n {
|
||||
let ehm = HashMap::new();
|
||||
let (uuid, ntx) = push(
|
||||
@@ -4511,7 +4577,6 @@ pub async fn run_job_by_hash_inner(
|
||||
#[cfg(feature = "enterprise")]
|
||||
check_license_key_valid().await?;
|
||||
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
|
||||
let hash = script_hash.0;
|
||||
let (
|
||||
@@ -4527,7 +4592,7 @@ pub async fn run_job_by_hash_inner(
|
||||
_delete_after_use, // not taken into account in async endpoints
|
||||
timeout,
|
||||
has_preprocessor,
|
||||
) = get_path_tag_limits_cache_for_hash(&mut tx, &w_id, hash).await?;
|
||||
) = get_path_tag_limits_cache_for_hash(&db, &w_id, hash).await?;
|
||||
check_scopes(&authed, || format!("run:script/{path}"))?;
|
||||
if let Some(run_query_cache_ttl) = run_query.cache_ttl {
|
||||
cache_ttl = Some(run_query_cache_ttl);
|
||||
@@ -4536,7 +4601,7 @@ pub async fn run_job_by_hash_inner(
|
||||
let tag = run_query.tag.clone().or(tag);
|
||||
|
||||
check_tag_available_for_workspace(&w_id, &tag).await?;
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq);
|
||||
|
||||
let (uuid, tx) = push(
|
||||
&db,
|
||||
|
||||
@@ -82,6 +82,7 @@ pub mod smtp_server_ee;
|
||||
mod static_assets;
|
||||
mod stripe_ee;
|
||||
mod tracing_init;
|
||||
mod triggers;
|
||||
mod users;
|
||||
mod utils;
|
||||
mod variables;
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
schedule::clear_schedule,
|
||||
triggers::{
|
||||
get_triggers_count_internal, list_tokens_internal, TriggersCount, TruncatedTokenWithEmail,
|
||||
},
|
||||
users::{maybe_refresh_folders, require_owner_of_path, AuthCache},
|
||||
utils::WithStarredInfoQuery,
|
||||
webhook_util::{WebhookMessage, WebhookShared},
|
||||
@@ -53,7 +56,7 @@ use windmill_common::{
|
||||
utils::{
|
||||
not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath,
|
||||
},
|
||||
worker::{get_annotation_ts, to_raw_value},
|
||||
worker::to_raw_value,
|
||||
HUB_BASE_URL,
|
||||
};
|
||||
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
|
||||
@@ -132,6 +135,8 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/archive/p/*path", post(archive_script_by_path))
|
||||
.route("/get/draft/*path", get(get_script_by_path_w_draft))
|
||||
.route("/get/p/*path", get(get_script_by_path))
|
||||
.route("/get_triggers_count/*path", get(get_triggers_count))
|
||||
.route("/list_tokens/*path", get(list_tokens))
|
||||
.route("/raw/p/*path", get(raw_script_by_path))
|
||||
.route("/raw_unpinned/p/*path", get(raw_script_by_path_unpinned))
|
||||
.route("/exists/p/*path", get(exists_script_by_path))
|
||||
@@ -601,8 +606,8 @@ async fn create_script_internal<'c>(
|
||||
};
|
||||
|
||||
let lang = if &ns.language == &ScriptLang::Bun || &ns.language == &ScriptLang::Bunnative {
|
||||
let anns = get_annotation_ts(&ns.content);
|
||||
if anns.native_mode {
|
||||
let anns = windmill_common::worker::TypeScriptAnnotations::parse(&ns.content);
|
||||
if anns.native {
|
||||
ScriptLang::Bunnative
|
||||
} else {
|
||||
ScriptLang::Bun
|
||||
@@ -874,6 +879,22 @@ async fn get_script_by_path(
|
||||
Ok(Json(script))
|
||||
}
|
||||
|
||||
async fn list_tokens(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
|
||||
let path = path.to_path();
|
||||
list_tokens_internal(&db, &w_id, &path, false).await
|
||||
}
|
||||
|
||||
async fn get_triggers_count(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<TriggersCount> {
|
||||
let path = path.to_path();
|
||||
get_triggers_count_internal(&db, &w_id, &path, false).await
|
||||
}
|
||||
|
||||
async fn get_script_by_path_w_draft(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
use ::tracing::{field, Span};
|
||||
use hyper::Response;
|
||||
use tower_http::trace::{MakeSpan, OnFailure, OnResponse};
|
||||
use uuid::Uuid;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref LOG_REQUESTS: bool = std::env::var("LOG_REQUESTS")
|
||||
@@ -45,17 +46,28 @@ impl<B> OnFailure<B> for MyOnFailure {
|
||||
// tracing::error!(latency = latency.as_millis(), "response")
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref TRACING_HEADER: String = std::env::var("TRACING_HEADER")
|
||||
.ok().unwrap_or_else(|| "x-tracing-id".to_string());
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct MyMakeSpan {}
|
||||
|
||||
impl<B> MakeSpan<B> for MyMakeSpan {
|
||||
fn make_span(&mut self, request: &hyper::Request<B>) -> Span {
|
||||
let tracing_id = request
|
||||
.headers()
|
||||
.get(TRACING_HEADER.as_str())
|
||||
.and_then(|x| x.to_str().map(|x| x.to_string()).ok())
|
||||
.unwrap_or(Uuid::new_v4().to_string());
|
||||
tracing::info_span!(
|
||||
"request",
|
||||
method = %request.method(),
|
||||
uri = %request.uri(),
|
||||
username = field::Empty,
|
||||
workspace_id = field::Empty,
|
||||
trace_id = tracing_id,
|
||||
email = field::Empty,
|
||||
)
|
||||
}
|
||||
|
||||
131
backend/windmill-api/src/triggers.rs
Normal file
131
backend/windmill-api/src/triggers.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use axum::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
use windmill_common::error::JsonResult;
|
||||
|
||||
use crate::db::DB;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TriggerPrimarySchedule {
|
||||
schedule: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TriggersCount {
|
||||
primary_schedule: Option<TriggerPrimarySchedule>,
|
||||
schedule_count: i64,
|
||||
http_routes_count: i64,
|
||||
webhook_count: i64,
|
||||
email_count: i64,
|
||||
}
|
||||
pub(crate) async fn get_triggers_count_internal(
|
||||
db: &DB,
|
||||
w_id: &str,
|
||||
path: &str,
|
||||
is_flow: bool,
|
||||
) -> JsonResult<TriggersCount> {
|
||||
let primary_schedule = sqlx::query_scalar!(
|
||||
"SELECT schedule FROM schedule WHERE path = $1 AND script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
path,
|
||||
is_flow,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
|
||||
let schedule_count = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM schedule WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
path,
|
||||
is_flow,
|
||||
w_id
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let http_routes_count = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM http_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
|
||||
path,
|
||||
is_flow,
|
||||
w_id
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let webhook_count = (if is_flow {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
|
||||
} else {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:' || $2]::text[]",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
}).fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let email_count = (if is_flow {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
|
||||
} else {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM token WHERE label LIKE 'email-%' AND workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
|
||||
w_id,
|
||||
path,
|
||||
)
|
||||
}).fetch_one(db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(Json(TriggersCount {
|
||||
primary_schedule: primary_schedule.map(|s| TriggerPrimarySchedule { schedule: s }),
|
||||
schedule_count,
|
||||
http_routes_count,
|
||||
webhook_count,
|
||||
email_count,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize)]
|
||||
pub struct TruncatedTokenWithEmail {
|
||||
pub label: Option<String>,
|
||||
pub token_prefix: Option<String>,
|
||||
pub expiration: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
pub last_used_at: chrono::DateTime<chrono::Utc>,
|
||||
pub scopes: Option<Vec<String>>,
|
||||
pub email: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn list_tokens_internal(
|
||||
db: &DB,
|
||||
w_id: &str,
|
||||
path: &str,
|
||||
is_flow: bool,
|
||||
) -> JsonResult<Vec<TruncatedTokenWithEmail>> {
|
||||
let tokens = if is_flow {
|
||||
sqlx::query_as!(
|
||||
TruncatedTokenWithEmail,
|
||||
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
|
||||
w_id, path).fetch_all(db)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query_as!(
|
||||
TruncatedTokenWithEmail,
|
||||
"SELECT label, concat(substring(token for 10)) as token_prefix, expiration, created_at, last_used_at, scopes, email FROM token WHERE workspace_id = $1 AND scopes @> ARRAY['run:script/' || $2]::text[]",
|
||||
w_id, path)
|
||||
.fetch_all(db)
|
||||
.await?
|
||||
};
|
||||
Ok(Json(tokens))
|
||||
}
|
||||
@@ -128,7 +128,13 @@ pub fn make_unauthed_service() -> Router {
|
||||
|
||||
fn username_override_from_label(label: Option<String>) -> Option<String> {
|
||||
match label {
|
||||
Some(label) if label.starts_with("webhook-") => Some(label),
|
||||
Some(label)
|
||||
if label.starts_with("webhook-")
|
||||
|| label.starts_with("http-")
|
||||
|| label.starts_with("email-") =>
|
||||
{
|
||||
Some(label)
|
||||
}
|
||||
Some(label) if label.starts_with("ephemeral-script-end-user-") => Some(
|
||||
label
|
||||
.trim_start_matches("ephemeral-script-end-user-")
|
||||
@@ -270,9 +276,10 @@ impl AuthCache {
|
||||
_ => {
|
||||
let user_o = sqlx::query_as::<_, (Option<String>, Option<String>, bool, Option<Vec<String>>, Option<String>)>(
|
||||
"UPDATE token SET last_used_at = now() WHERE token = $1 AND (expiration > NOW() \
|
||||
OR expiration IS NULL) RETURNING owner, email, super_admin, scopes, label",
|
||||
OR expiration IS NULL) AND (workspace_id IS NULL OR workspace_id = $2) RETURNING owner, email, super_admin, scopes, label",
|
||||
)
|
||||
.bind(token)
|
||||
.bind(w_id.as_ref())
|
||||
.fetch_optional(&self.db)
|
||||
.await
|
||||
.ok()
|
||||
@@ -837,6 +844,7 @@ pub struct NewToken {
|
||||
pub expiration: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub impersonate_email: Option<String>,
|
||||
pub scopes: Option<Vec<String>>,
|
||||
pub workspace_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -2389,14 +2397,15 @@ async fn create_token(
|
||||
.unwrap_or(false);
|
||||
sqlx::query!(
|
||||
"INSERT INTO token
|
||||
(token, email, label, expiration, super_admin, scopes)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)",
|
||||
(token, email, label, expiration, super_admin, scopes, workspace_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
token,
|
||||
authed.email,
|
||||
new_token.label,
|
||||
new_token.expiration,
|
||||
is_super_admin,
|
||||
new_token.scopes.as_ref().map(|x| x.as_slice())
|
||||
new_token.scopes.as_ref().map(|x| x.as_slice()),
|
||||
new_token.workspace_id,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
@@ -57,6 +57,7 @@ futures-core.workspace = true
|
||||
async-stream.workspace = true
|
||||
const_format.workspace = true
|
||||
crc.workspace = true
|
||||
windmill-macros.workspace = true
|
||||
|
||||
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||
tikv-jemalloc-ctl = { optional = true, workspace = true }
|
||||
|
||||
@@ -81,3 +81,6 @@ pub async fn create_customer_portal_session(
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn worker_groups_alerts(_db: &DB) {}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn jobs_waiting_alerts(_db: &DB) {}
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::{
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use windmill_macros::annotations;
|
||||
|
||||
use crate::{error, global_settings::CUSTOM_TAGS_SETTING, server::Smtp, DB};
|
||||
|
||||
@@ -303,64 +304,25 @@ fn parse_file<T: FromStr>(path: &str) -> Option<T> {
|
||||
.flatten()
|
||||
}
|
||||
|
||||
#[annotations("#")]
|
||||
pub struct PythonAnnotations {
|
||||
pub no_cache: bool,
|
||||
pub no_uv: bool,
|
||||
}
|
||||
|
||||
#[annotations("//")]
|
||||
pub struct TypeScriptAnnotations {
|
||||
pub npm_mode: bool,
|
||||
pub nodejs_mode: bool,
|
||||
pub native_mode: bool,
|
||||
pub npm: bool,
|
||||
pub nodejs: bool,
|
||||
pub native: bool,
|
||||
pub nobundling: bool,
|
||||
}
|
||||
|
||||
pub fn get_annotation_ts(inner_content: &str) -> TypeScriptAnnotations {
|
||||
let annotations = inner_content
|
||||
.lines()
|
||||
.take_while(|x| x.starts_with("//"))
|
||||
.map(|x| x.to_string().replace("//", "").trim().to_string())
|
||||
.collect_vec();
|
||||
let nodejs_mode: bool = annotations.contains(&"nodejs".to_string());
|
||||
let npm_mode: bool = annotations.contains(&"npm".to_string());
|
||||
let native_mode: bool = annotations.contains(&"native".to_string());
|
||||
|
||||
//TODO: remove || npm_mode when bun build is more powerful
|
||||
let nobundling: bool =
|
||||
annotations.contains(&"nobundling".to_string()) || nodejs_mode || *DISABLE_BUNDLING;
|
||||
|
||||
TypeScriptAnnotations { npm_mode, nodejs_mode, native_mode, nobundling }
|
||||
}
|
||||
|
||||
pub struct PythonAnnotations {
|
||||
pub no_uv: bool,
|
||||
pub no_cache: bool,
|
||||
}
|
||||
|
||||
pub fn get_annotation_python(inner_content: &str) -> PythonAnnotations {
|
||||
let annotations = inner_content
|
||||
.lines()
|
||||
.take_while(|x| x.starts_with("#"))
|
||||
.map(|x| x.to_string().replace("#", "").trim().to_string())
|
||||
.collect_vec();
|
||||
|
||||
let no_uv: bool = annotations.contains(&"no_uv".to_string());
|
||||
let no_cache: bool = annotations.contains(&"no_cache".to_string());
|
||||
|
||||
PythonAnnotations { no_uv, no_cache }
|
||||
}
|
||||
|
||||
#[annotations("--")]
|
||||
pub struct SqlAnnotations {
|
||||
pub return_last_result: bool,
|
||||
}
|
||||
|
||||
pub fn get_sql_annotations(inner_content: &str) -> SqlAnnotations {
|
||||
let annotations = inner_content
|
||||
.lines()
|
||||
.take_while(|x| x.starts_with("--"))
|
||||
.map(|x| x.to_string().replace("--", "").trim().to_string())
|
||||
.collect_vec();
|
||||
|
||||
let return_last_result: bool = annotations.contains(&"return_last_result".to_string());
|
||||
|
||||
SqlAnnotations { return_last_result }
|
||||
}
|
||||
|
||||
pub async fn load_cache(bin_path: &str, _remote_path: &str) -> (bool, String) {
|
||||
if tokio::fs::metadata(&bin_path).await.is_ok() {
|
||||
(true, format!("loaded from local cache: {}\n", bin_path))
|
||||
|
||||
@@ -29,3 +29,4 @@ tempfile.workspace = true
|
||||
bytes.workspace = true
|
||||
object_store = { workspace = true, optional = true}
|
||||
tokio-tar.workspace = true
|
||||
lazy_static.workspace = true
|
||||
|
||||
20
backend/windmill-macros/Cargo.toml
Normal file
20
backend/windmill-macros/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "windmill-macros"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2.workspace = true
|
||||
quote.workspace = true
|
||||
syn.workspace = true
|
||||
|
||||
# Dependencies for tests
|
||||
[dev-dependencies]
|
||||
# tests/annotation.rs
|
||||
lazy_static.workspace = true
|
||||
itertools.workspace = true
|
||||
regex.workspace = true
|
||||
95
backend/windmill-macros/src/lib.rs
Normal file
95
backend/windmill-macros/src/lib.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Ident, ItemStruct, Lit};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn annotations(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as ItemStruct);
|
||||
let name = input.ident.clone();
|
||||
let fields = input
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| f.ident.clone().unwrap())
|
||||
.collect::<Vec<Ident>>();
|
||||
|
||||
// Match on the literal to extract the string value
|
||||
let comm_lit = match parse_macro_input!(attr as Lit) {
|
||||
Lit::Str(lit_str) => lit_str.value(), // This will give "#" without quotes
|
||||
_ => panic!("Expected a string literal"),
|
||||
};
|
||||
|
||||
// Generate regex
|
||||
let mut reg = format!("^{}|", &comm_lit);
|
||||
{
|
||||
for field in fields.iter() {
|
||||
reg.push_str(&(field.to_string()));
|
||||
reg.push_str("\\b");
|
||||
}
|
||||
|
||||
reg.push_str(r#"|\w+"#);
|
||||
}
|
||||
// Example of generated regex:
|
||||
// ^#
|
||||
// |ann1\b|ann2\b|ann3\b|ann4\b
|
||||
// |\w+
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#[derive(Default, Debug)]
|
||||
#input
|
||||
|
||||
impl std::ops::BitOrAssign for #name{
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
// Unfold fields
|
||||
// Read more: https://docs.rs/quote/latest/quote/macro.quote.html#interpolation
|
||||
#( self.#fields |= rhs.#fields; )*
|
||||
}
|
||||
}
|
||||
|
||||
impl #name {
|
||||
/// Autogenerated by windmill-macros
|
||||
pub fn parse(inner_content: &str) -> Self{
|
||||
let mut res = Self::default();
|
||||
lazy_static::lazy_static! {
|
||||
static ref RE: regex::Regex = regex::Regex::new(#reg).unwrap();
|
||||
}
|
||||
// Create lines stream
|
||||
let mut lines = inner_content.lines();
|
||||
'outer: while let Some(line) = lines.next() {
|
||||
// If comment sign(s) on the right place
|
||||
let mut comms = false;
|
||||
// New instance
|
||||
// We will apply it if in line only annotations
|
||||
let mut new = Self::default();
|
||||
|
||||
'inner: for (i, mat) in RE.find_iter(line).enumerate() {
|
||||
|
||||
match mat.as_str(){
|
||||
#comm_lit if i == 0 => {
|
||||
comms = true;
|
||||
continue 'inner;
|
||||
},
|
||||
|
||||
// Will expand into something like:
|
||||
// "ann1" => new.ann1 = true,
|
||||
// "ann2" => new.ann2 = true,
|
||||
// "ann3" => new.ann3 = true,
|
||||
#( stringify!(#fields) => new.#fields = true, )*
|
||||
// Non annotations
|
||||
_ => continue 'outer,
|
||||
};
|
||||
}
|
||||
|
||||
if !comms {
|
||||
// We dont want to continue if line does not start with #
|
||||
return res;
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
res |= new;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
169
backend/windmill-macros/tests/annotations.rs
Normal file
169
backend/windmill-macros/tests/annotations.rs
Normal file
@@ -0,0 +1,169 @@
|
||||
#[cfg(test)]
|
||||
mod annotations_tests {
|
||||
|
||||
extern crate windmill_macros;
|
||||
use itertools::Itertools;
|
||||
use windmill_macros::annotations;
|
||||
|
||||
// Previous implementation.
|
||||
// We have to make sure that new one works the same as old one
|
||||
fn old(inner_content: &str) -> Annotations {
|
||||
let annotations = inner_content
|
||||
.lines()
|
||||
.take_while(|x| x.starts_with("#"))
|
||||
.map(|x| x.to_string().replace("#", "").trim().to_string())
|
||||
.collect_vec();
|
||||
|
||||
let ann1: bool = annotations.contains(&"ann1".to_string());
|
||||
let ann2: bool = annotations.contains(&"ann2".to_string());
|
||||
let ann3: bool = annotations.contains(&"ann3".to_string());
|
||||
let ann4: bool = annotations.contains(&"ann4".to_string());
|
||||
let ann5: bool = annotations.contains(&"ann5".to_string());
|
||||
|
||||
Annotations { ann1, ann2, ann3, ann4, ann5 }
|
||||
}
|
||||
|
||||
#[annotations("#")]
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
pub struct Annotations {
|
||||
pub ann1: bool,
|
||||
pub ann2: bool,
|
||||
pub ann3: bool,
|
||||
pub ann4: bool,
|
||||
pub ann5: bool,
|
||||
}
|
||||
|
||||
#[annotations("//")]
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
pub struct SlashedAnnotations {
|
||||
pub ann1: bool,
|
||||
pub ann2: bool,
|
||||
pub ann3: bool,
|
||||
pub ann4: bool,
|
||||
}
|
||||
|
||||
#[annotations("--")]
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
pub struct MinusedAnnotations {
|
||||
pub ann1: bool,
|
||||
pub ann2: bool,
|
||||
}
|
||||
|
||||
// e.g. rust, TS and JS
|
||||
#[test]
|
||||
fn slashed_annotations() {
|
||||
let cont = "// ann1
|
||||
// ann2
|
||||
//ann3";
|
||||
assert_eq!(
|
||||
SlashedAnnotations { ann1: true, ann2: true, ann3: true, ann4: false },
|
||||
SlashedAnnotations::parse(cont)
|
||||
);
|
||||
}
|
||||
|
||||
// e.g. Haskell, SQL
|
||||
#[test]
|
||||
fn minused_annotations() {
|
||||
let cont = "-- ann1
|
||||
-- ann2";
|
||||
assert_eq!(
|
||||
MinusedAnnotations { ann1: true, ann2: true },
|
||||
MinusedAnnotations::parse(cont)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_integration() {
|
||||
let cont = "# ann1";
|
||||
let expected = Annotations { ann1: true, ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_integration() {
|
||||
let cont = "# ann2
|
||||
# ann3
|
||||
# ann4
|
||||
# ann5
|
||||
";
|
||||
let expected = Annotations {
|
||||
ann1: false,
|
||||
ann2: true,
|
||||
ann3: true,
|
||||
ann4: true,
|
||||
ann5: true,
|
||||
//
|
||||
};
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spacing_integration() {
|
||||
// First line is ignored and not used
|
||||
{
|
||||
let cont = "
|
||||
# ann2";
|
||||
let expected = Annotations { ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
// Wrong spacing for ann3
|
||||
{
|
||||
let cont = "# ann2
|
||||
# ann3";
|
||||
|
||||
let expected = Annotations { ann2: true, ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
|
||||
// Drunk but valid spacing
|
||||
{
|
||||
let cont = "#ann1
|
||||
# ann2";
|
||||
let expected = Annotations { ann2: true, ann1: true, ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comments_inbetween_integration() {
|
||||
let cont = "# ann2
|
||||
# Just comment, has nothing to do with annotations
|
||||
# Another comment: ann1 ann2 ann3
|
||||
# ann4 is not valid annotation
|
||||
# Actual annotation next line:
|
||||
# ann5
|
||||
|
||||
# Should be ignored
|
||||
# ann3
|
||||
";
|
||||
let expected = Annotations { ann2: true, ann5: true, ..Default::default() };
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_collision() {
|
||||
// TODO
|
||||
}
|
||||
#[test]
|
||||
fn non_matching_integration() {
|
||||
{
|
||||
let cont = r#" "ann1", ann2 "#;
|
||||
let expected = Annotations::default();
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
// Empty
|
||||
{
|
||||
let cont = "";
|
||||
let expected = Annotations::default();
|
||||
assert_eq!(expected, old(cont));
|
||||
assert_eq!(expected, Annotations::parse(cont));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,17 @@ use deno_net::NetPermissions;
|
||||
#[cfg(feature = "deno_core")]
|
||||
use deno_web::{BlobStore, TimersPermission};
|
||||
#[cfg(feature = "deno_core")]
|
||||
use std::borrow::Cow;
|
||||
#[cfg(feature = "deno_core")]
|
||||
use std::env;
|
||||
#[cfg(feature = "deno_core")]
|
||||
use std::io::Write;
|
||||
#[cfg(feature = "deno_core")]
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
#[cfg(feature = "deno_core")]
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
// #[cfg(feature = "deno_core")]
|
||||
pub struct PermissionsContainer;
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
@@ -24,16 +26,16 @@ impl FetchPermissions for PermissionsContainer {
|
||||
_url: &deno_core::url::Url,
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
unreachable!("snapshotting")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn check_read(
|
||||
fn check_read<'a>(
|
||||
&mut self,
|
||||
_p: &std::path::Path,
|
||||
_p: &'a std::path::Path,
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
) -> Result<Cow<'a, Path>, deno_core::error::AnyError> {
|
||||
unreachable!("snapshotting")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,20 +49,20 @@ impl TimersPermission for PermissionsContainer {
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
impl NetPermissions for PermissionsContainer {
|
||||
fn check_read(
|
||||
fn check_read<'a>(
|
||||
&mut self,
|
||||
_p: &std::path::Path,
|
||||
_p: &'a str,
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
) -> Result<PathBuf, deno_core::error::AnyError> {
|
||||
unreachable!("snapshotting")
|
||||
}
|
||||
|
||||
fn check_write(
|
||||
fn check_write<'a>(
|
||||
&mut self,
|
||||
_p: &std::path::Path,
|
||||
_p: &'a str,
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
) -> Result<PathBuf, deno_core::error::AnyError> {
|
||||
unreachable!("snapshotting")
|
||||
}
|
||||
|
||||
fn check_net<T: AsRef<str>>(
|
||||
@@ -68,7 +70,15 @@ impl NetPermissions for PermissionsContainer {
|
||||
_host: &(T, Option<u16>),
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
unreachable!("snapshotting")
|
||||
}
|
||||
|
||||
fn check_write_path<'a>(
|
||||
&mut self,
|
||||
_: &'a Path,
|
||||
_: &str,
|
||||
) -> Result<Cow<'a, Path>, deno_core::anyhow::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,13 @@ mount {
|
||||
is_bind: true
|
||||
}
|
||||
|
||||
mount {
|
||||
dst: "/dev/shm"
|
||||
fstype: "tmpfs"
|
||||
rw: true
|
||||
is_bind: false
|
||||
}
|
||||
|
||||
mount {
|
||||
src: "/dev/random"
|
||||
dst: "/dev/random"
|
||||
|
||||
@@ -318,15 +318,33 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
|
||||
POWERSHELL_CACHE_DIR
|
||||
);
|
||||
|
||||
// NOTE: powershell error handling / termination is quite tricky compared to bash
|
||||
// here we're trying to catch terminating errors and propagate the exit code
|
||||
// to the caller such that the job will be marked as failed. It's up to the user
|
||||
// to catch specific errors in their script not caught by the below as there is no
|
||||
// generic set -eu as in bash
|
||||
let strict_termination_start = "$ErrorActionPreference = 'Stop'\n\
|
||||
Set-StrictMode -Version Latest\n\
|
||||
try {\n";
|
||||
|
||||
let strict_termination_end = "\n\
|
||||
} catch {\n\
|
||||
Write-Output \"An error occurred:\n\"\
|
||||
Write-Output $_
|
||||
exit 1\n\
|
||||
}\n";
|
||||
|
||||
// make sure param() is first
|
||||
let param_match = windmill_parser_bash::RE_POWERSHELL_PARAM.find(&content);
|
||||
let content: String = if let Some(param_match) = param_match {
|
||||
let param_match = param_match.as_str();
|
||||
format!(
|
||||
"{}\n{}\n{}",
|
||||
"{}\n{}\n{}\n{}\n{}",
|
||||
param_match,
|
||||
profile,
|
||||
content.replace(param_match, "")
|
||||
strict_termination_start,
|
||||
content.replace(param_match, ""),
|
||||
strict_termination_end
|
||||
)
|
||||
} else {
|
||||
format!("{}\n{}", profile, content)
|
||||
@@ -351,7 +369,8 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
|
||||
$pipe = New-TemporaryFile\n\
|
||||
& \"{}\" -File ./main.ps1 @args 2>&1 | Tee-Object -FilePath $pipe\n\
|
||||
Get-Content -Path $pipe | Select-Object -Last 1 | Set-Content -Path './result2.out'\n\
|
||||
Remove-Item $pipe\n",
|
||||
Remove-Item $pipe\n\
|
||||
exit $LASTEXITCODE\n",
|
||||
POWERSHELL_PATH.as_str()
|
||||
),
|
||||
)?;
|
||||
|
||||
@@ -5,7 +5,6 @@ use futures::{FutureExt, TryFutureExt};
|
||||
use serde_json::{json, value::RawValue, Value};
|
||||
use windmill_common::error::to_anyhow;
|
||||
use windmill_common::jobs::QueuedJob;
|
||||
use windmill_common::worker::get_sql_annotations;
|
||||
use windmill_common::{error::Error, worker::to_raw_value};
|
||||
use windmill_parser_sql::{
|
||||
parse_bigquery_sig, parse_db_resource, parse_sql_blocks, parse_sql_statement_named_params,
|
||||
@@ -238,7 +237,7 @@ pub async fn do_bigquery(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let service_account = CustomServiceAccount::from_json(&database)
|
||||
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
|
||||
@@ -47,7 +47,7 @@ use windmill_common::{
|
||||
get_latest_hash_for_path,
|
||||
jobs::{QueuedJob, PREPROCESSOR_FAKE_ENTRYPOINT},
|
||||
scripts::ScriptLang,
|
||||
worker::{exists_in_cache, get_annotation_ts, save_cache, write_file},
|
||||
worker::{exists_in_cache, save_cache, write_file},
|
||||
DB,
|
||||
};
|
||||
|
||||
@@ -663,7 +663,7 @@ pub async fn prebundle_bun_script(
|
||||
if exists_in_cache(&local_path, &remote_path).await {
|
||||
return Ok(());
|
||||
}
|
||||
let annotation = get_annotation_ts(inner_content);
|
||||
let annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content);
|
||||
if annotation.nobundling {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -676,9 +676,9 @@ pub async fn prebundle_bun_script(
|
||||
&token,
|
||||
w_id,
|
||||
script_path,
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
LoaderMode::NodeBundle
|
||||
} else if annotation.native_mode {
|
||||
} else if annotation.native {
|
||||
LoaderMode::BrowserBundle
|
||||
} else {
|
||||
LoaderMode::BunBundle
|
||||
@@ -800,7 +800,7 @@ pub async fn handle_bun_job(
|
||||
new_args: &mut Option<HashMap<String, Box<RawValue>>>,
|
||||
occupancy_metrics: &mut OccupancyMetrics,
|
||||
) -> error::Result<Box<RawValue>> {
|
||||
let mut annotation = windmill_common::worker::get_annotation_ts(inner_content);
|
||||
let mut annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content);
|
||||
|
||||
let (mut has_bundle_cache, cache_logs, local_path, remote_path) =
|
||||
if requirements_o.is_some() && !annotation.nobundling && codebase.is_none() {
|
||||
@@ -822,7 +822,7 @@ pub async fn handle_bun_job(
|
||||
|
||||
if !codebase.is_some() && !has_bundle_cache {
|
||||
let _ = write_file(job_dir, "main.ts", inner_content)?;
|
||||
} else if !annotation.native_mode && codebase.is_none() {
|
||||
} else if !annotation.native && codebase.is_none() {
|
||||
let _ = write_file(job_dir, "package.json", r#"{ "type": "module" }"#)?;
|
||||
};
|
||||
|
||||
@@ -830,7 +830,7 @@ pub async fn handle_bun_job(
|
||||
get_common_bun_proc_envs(Some(&base_internal_url)).await;
|
||||
|
||||
if codebase.is_some() {
|
||||
annotation.nodejs_mode = true
|
||||
annotation.nodejs = true
|
||||
}
|
||||
let (main_override, apply_preprocessor) = match get_main_override(job.args.as_ref()) {
|
||||
Some(main_override) => {
|
||||
@@ -844,7 +844,7 @@ pub async fn handle_bun_job(
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
if annotation.nodejs_mode || annotation.npm_mode {
|
||||
if annotation.nodejs || annotation.npm {
|
||||
return Err(error::Error::ExecutionErr(
|
||||
"Nodejs / npm mode is an EE feature".to_string(),
|
||||
));
|
||||
@@ -875,20 +875,20 @@ pub async fn handle_bun_job(
|
||||
pull_codebase(&job.workspace_id, codebase, job_dir).await?;
|
||||
} else if let Some(reqs) = requirements_o.as_ref() {
|
||||
let splitted = reqs.split(BUN_LOCKB_SPLIT).collect::<Vec<&str>>();
|
||||
if splitted.len() != 2 && !annotation.npm_mode {
|
||||
if splitted.len() != 2 && !annotation.npm {
|
||||
return Err(error::Error::ExecutionErr(
|
||||
format!("Invalid requirements, expected to find //bun.lockb split pattern in reqs. Found: |{reqs}|")
|
||||
));
|
||||
}
|
||||
|
||||
let _ = write_file(job_dir, "package.json", &splitted[0])?;
|
||||
let lockb = if annotation.npm_mode { "" } else { splitted[1] };
|
||||
let lockb = if annotation.npm { "" } else { splitted[1] };
|
||||
if lockb != EMPTY_FILE {
|
||||
let mut skip_install = false;
|
||||
let mut create_buntar = false;
|
||||
let mut buntar_path = "".to_string();
|
||||
|
||||
if !annotation.npm_mode {
|
||||
if !annotation.npm {
|
||||
let _ = write_lockb(&splitted[1], job_dir).await?;
|
||||
|
||||
let mut sha_path = sha2::Sha256::new();
|
||||
@@ -921,7 +921,7 @@ pub async fn handle_bun_job(
|
||||
job_dir,
|
||||
worker_name,
|
||||
common_bun_proc_envs.clone(),
|
||||
annotation.npm_mode,
|
||||
annotation.npm,
|
||||
&mut Some(occupancy_metrics),
|
||||
)
|
||||
.await?;
|
||||
@@ -963,7 +963,7 @@ pub async fn handle_bun_job(
|
||||
worker_name,
|
||||
false,
|
||||
None,
|
||||
annotation.npm_mode,
|
||||
annotation.npm,
|
||||
&mut Some(occupancy_metrics),
|
||||
)
|
||||
.await?;
|
||||
@@ -971,19 +971,19 @@ pub async fn handle_bun_job(
|
||||
// }
|
||||
}
|
||||
|
||||
let mut init_logs = if annotation.native_mode {
|
||||
let mut init_logs = if annotation.native {
|
||||
"\n\n--- NATIVE CODE EXECUTION ---\n".to_string()
|
||||
} else if has_bundle_cache {
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
"\n\n--- NODE BUNDLE SNAPSHOT EXECUTION ---\n".to_string()
|
||||
} else {
|
||||
"\n\n--- BUN BUNDLE SNAPSHOT EXECUTION ---\n".to_string()
|
||||
}
|
||||
} else if codebase.is_some() {
|
||||
"\n\n--- NODE CODEBASE SNAPSHOT EXECUTION ---\n".to_string()
|
||||
} else if annotation.native_mode {
|
||||
} else if annotation.native {
|
||||
"\n\n--- NATIVE CODE EXECUTION ---\n".to_string()
|
||||
} else if annotation.nodejs_mode {
|
||||
} else if annotation.nodejs {
|
||||
write_file(job_dir, "main.ts", &remove_pinned_imports(inner_content)?)?;
|
||||
"\n\n--- NODE CODE EXECUTION ---\n".to_string()
|
||||
} else {
|
||||
@@ -1003,7 +1003,7 @@ pub async fn handle_bun_job(
|
||||
}
|
||||
|
||||
let write_wrapper_f = async {
|
||||
if !has_bundle_cache && annotation.native_mode {
|
||||
if !has_bundle_cache && annotation.native {
|
||||
return Ok(()) as error::Result<()>;
|
||||
}
|
||||
// let mut start = Instant::now();
|
||||
@@ -1105,6 +1105,16 @@ try {{
|
||||
if (step_id) {{
|
||||
err["step_id"] = step_id;
|
||||
}}
|
||||
const extra = {{}};
|
||||
Object.getOwnPropertyNames(e).forEach((key) => {{
|
||||
if (['line', 'name', 'stack', 'column', 'message', 'sourceURL', 'originalLine', 'originalColumn'].includes(key)) {{
|
||||
return;
|
||||
}}
|
||||
extra[key] = e[key];
|
||||
}});
|
||||
if (Object.keys(extra).length > 0) {{
|
||||
err["extra"] = extra;
|
||||
}}
|
||||
await fs.writeFile("result.json", JSON.stringify(err));
|
||||
process.exit(1);
|
||||
}}
|
||||
@@ -1116,7 +1126,7 @@ try {{
|
||||
|
||||
let reserved_variables_args_out_f = async {
|
||||
let args_and_out_f = async {
|
||||
if !annotation.native_mode {
|
||||
if !annotation.native {
|
||||
create_args_and_out_file(&client, job, job_dir, db).await?;
|
||||
}
|
||||
Ok(()) as Result<()>
|
||||
@@ -1133,7 +1143,7 @@ try {{
|
||||
let build_cache = !has_bundle_cache
|
||||
&& !annotation.nobundling
|
||||
&& !codebase.is_some()
|
||||
&& (requirements_o.is_some() || annotation.native_mode);
|
||||
&& (requirements_o.is_some() || annotation.native);
|
||||
|
||||
let write_loader_f = async {
|
||||
if build_cache {
|
||||
@@ -1143,9 +1153,9 @@ try {{
|
||||
&client.get_token().await,
|
||||
&job.workspace_id,
|
||||
&job.script_path(),
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
LoaderMode::NodeBundle
|
||||
} else if annotation.native_mode {
|
||||
} else if annotation.native {
|
||||
LoaderMode::BrowserBundle
|
||||
} else {
|
||||
LoaderMode::BunBundle
|
||||
@@ -1161,7 +1171,7 @@ try {{
|
||||
&client.get_token().await,
|
||||
&job.workspace_id,
|
||||
&job.script_path(),
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
LoaderMode::Node
|
||||
} else {
|
||||
LoaderMode::Bun
|
||||
@@ -1207,7 +1217,7 @@ try {{
|
||||
}
|
||||
}
|
||||
}
|
||||
if !annotation.native_mode {
|
||||
if !annotation.native {
|
||||
let ex_wrapper = read_file_content(&format!("{job_dir}/wrapper.mjs")).await?;
|
||||
write_file(
|
||||
job_dir,
|
||||
@@ -1221,7 +1231,7 @@ try {{
|
||||
}
|
||||
fs::remove_file(format!("{job_dir}/main.ts"))?;
|
||||
has_bundle_cache = true;
|
||||
} else if annotation.nodejs_mode {
|
||||
} else if annotation.nodejs {
|
||||
generate_wrapper_mjs(
|
||||
job_dir,
|
||||
&job.workspace_id,
|
||||
@@ -1237,9 +1247,14 @@ try {{
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
if annotation.native_mode {
|
||||
if annotation.native {
|
||||
#[cfg(not(feature = "deno_core"))]
|
||||
return Ok(to_raw_value("").unwrap());
|
||||
{
|
||||
tracing::error!(
|
||||
r#""deno_core" feature is not activated, but "//native" annotation used. Returning empty value..."#
|
||||
);
|
||||
return Ok(to_raw_value("").unwrap());
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
{
|
||||
@@ -1299,14 +1314,7 @@ try {{
|
||||
job_dir,
|
||||
"run.config.proto",
|
||||
&NSJAIL_CONFIG_RUN_BUN_CONTENT
|
||||
.replace(
|
||||
"{LANG}",
|
||||
if annotation.nodejs_mode {
|
||||
"nodejs"
|
||||
} else {
|
||||
"bun"
|
||||
},
|
||||
)
|
||||
.replace("{LANG}", if annotation.nodejs { "nodejs" } else { "bun" })
|
||||
.replace("{JOB_DIR}", job_dir)
|
||||
.replace("{CACHE_DIR}", BUN_CACHE_DIR)
|
||||
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
|
||||
@@ -1314,7 +1322,7 @@ try {{
|
||||
"{SHARED_MOUNT}",
|
||||
&shared_mount.replace(
|
||||
"/tmp/shared",
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
"/tmp/nodejs/shared"
|
||||
} else {
|
||||
"/tmp/bun/shared"
|
||||
@@ -1324,7 +1332,7 @@ try {{
|
||||
)?;
|
||||
|
||||
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
|
||||
let args = if annotation.nodejs_mode {
|
||||
let args = if annotation.nodejs {
|
||||
vec![
|
||||
"--config",
|
||||
"run.config.proto",
|
||||
@@ -1368,7 +1376,7 @@ try {{
|
||||
.stderr(Stdio::piped());
|
||||
start_child_process(nsjail_cmd, NSJAIL_PATH.as_str()).await?
|
||||
} else {
|
||||
let cmd = if annotation.nodejs_mode {
|
||||
let cmd = if annotation.nodejs {
|
||||
let script_path = format!("{job_dir}/wrapper.mjs");
|
||||
|
||||
let mut bun_cmd = Command::new(&*NODE_BIN_PATH);
|
||||
@@ -1420,7 +1428,7 @@ try {{
|
||||
|
||||
start_child_process(
|
||||
cmd,
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
&*NODE_BIN_PATH
|
||||
} else {
|
||||
&*BUN_PATH
|
||||
@@ -1435,7 +1443,7 @@ try {{
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
child,
|
||||
false,
|
||||
!*DISABLE_NSJAIL,
|
||||
worker_name,
|
||||
&job.workspace_id,
|
||||
"bun run",
|
||||
@@ -1525,10 +1533,10 @@ pub async fn start_worker(
|
||||
let common_bun_proc_envs: HashMap<String, String> =
|
||||
get_common_bun_proc_envs(Some(&base_internal_url)).await;
|
||||
|
||||
let mut annotation = windmill_common::worker::get_annotation_ts(inner_content);
|
||||
let mut annotation = windmill_common::worker::TypeScriptAnnotations::parse(inner_content);
|
||||
|
||||
//TODO: remove this when bun dedicated workers work without issues
|
||||
annotation.nodejs_mode = true;
|
||||
annotation.nodejs = true;
|
||||
|
||||
let context = variables::get_reserved_variables(
|
||||
db,
|
||||
@@ -1582,7 +1590,7 @@ pub async fn start_worker(
|
||||
job_dir,
|
||||
worker_name,
|
||||
common_bun_proc_envs.clone(),
|
||||
annotation.npm_mode,
|
||||
annotation.npm,
|
||||
&mut None,
|
||||
)
|
||||
.await?;
|
||||
@@ -1603,7 +1611,7 @@ pub async fn start_worker(
|
||||
worker_name,
|
||||
false,
|
||||
None,
|
||||
annotation.npm_mode,
|
||||
annotation.npm,
|
||||
&mut None,
|
||||
)
|
||||
.await?;
|
||||
@@ -1681,7 +1689,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) {
|
||||
token,
|
||||
w_id,
|
||||
script_path,
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
LoaderMode::Node
|
||||
} else {
|
||||
LoaderMode::Bun
|
||||
@@ -1690,7 +1698,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) {
|
||||
.await?;
|
||||
}
|
||||
|
||||
if annotation.nodejs_mode && !codebase.is_some() {
|
||||
if annotation.nodejs && !codebase.is_some() {
|
||||
generate_wrapper_mjs(
|
||||
job_dir,
|
||||
w_id,
|
||||
@@ -1706,7 +1714,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) {
|
||||
.await?;
|
||||
}
|
||||
|
||||
if annotation.nodejs_mode {
|
||||
if annotation.nodejs {
|
||||
let script_path = format!("{job_dir}/wrapper.mjs");
|
||||
|
||||
handle_dedicated_process(
|
||||
|
||||
@@ -125,7 +125,8 @@ pub async fn generate_deno_lock(
|
||||
"--unstable-worker-options",
|
||||
"--unstable-http",
|
||||
"--lock=lock.json",
|
||||
"--lock-write",
|
||||
"--frozen=false",
|
||||
"--allow-import",
|
||||
"--import-map",
|
||||
&import_map_path,
|
||||
"main.ts",
|
||||
@@ -156,10 +157,13 @@ pub async fn generate_deno_lock(
|
||||
}
|
||||
|
||||
let path_lock = format!("{job_dir}/lock.json");
|
||||
let mut file = File::open(path_lock).await?;
|
||||
let mut req_content = "".to_string();
|
||||
file.read_to_string(&mut req_content).await?;
|
||||
Ok(req_content)
|
||||
if let Ok(mut file) = File::open(path_lock).await {
|
||||
let mut req_content = "".to_string();
|
||||
file.read_to_string(&mut req_content).await?;
|
||||
Ok(req_content)
|
||||
} else {
|
||||
Ok("".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
@@ -366,10 +370,10 @@ try {{
|
||||
} else if !*DISABLE_NSJAIL {
|
||||
args.push("--allow-net");
|
||||
args.push("--allow-sys");
|
||||
args.push("--allow-hrtime");
|
||||
args.push(allow_read.as_str());
|
||||
args.push("--allow-write=./");
|
||||
args.push("--allow-env");
|
||||
args.push("--allow-import");
|
||||
args.push("--allow-run=git,/usr/bin/chromium");
|
||||
} else {
|
||||
args.push("-A");
|
||||
|
||||
@@ -414,9 +414,30 @@ async fn get_mem_peak(pid: Option<u32>, nsjail: bool) -> i32 {
|
||||
return -1;
|
||||
}
|
||||
let pid = if nsjail {
|
||||
// This is a bit hacky, but the process id of the nsjail process is the pid of nsjail + 1.
|
||||
// Ideally, we would get the number from fork() itself. This works in MOST cases.
|
||||
pid.unwrap() + 1
|
||||
// Read /proc/<nsjail_pid>/task/<nsjail_pid>/children and extract pid
|
||||
let nsjail_pid = pid.unwrap();
|
||||
let children_path = format!("/proc/{}/task/{}/children", nsjail_pid, nsjail_pid);
|
||||
if let Ok(mut file) = File::open(children_path).await {
|
||||
let mut contents = String::new();
|
||||
if tokio::io::AsyncReadExt::read_to_string(&mut file, &mut contents)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
if let Some(child_pid) = contents.split_whitespace().next() {
|
||||
if let Ok(child_pid) = child_pid.parse::<u32>() {
|
||||
child_pid
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
pid.unwrap()
|
||||
};
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cell::RefCell,
|
||||
env,
|
||||
io::{self, BufReader},
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
@@ -111,12 +113,12 @@ impl FetchPermissions for PermissionsContainer {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn check_read(
|
||||
fn check_read<'a>(
|
||||
&mut self,
|
||||
_p: &std::path::Path,
|
||||
p: &'a std::path::Path,
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
) -> Result<Cow<'a, std::path::Path>, anyhow::Error> {
|
||||
Ok(Cow::Borrowed(p))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,20 +132,20 @@ impl TimersPermission for PermissionsContainer {
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
impl NetPermissions for PermissionsContainer {
|
||||
fn check_read(
|
||||
fn check_read<'a>(
|
||||
&mut self,
|
||||
_p: &std::path::Path,
|
||||
p: &'a str,
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
) -> Result<PathBuf, deno_core::error::AnyError> {
|
||||
Ok(PathBuf::from(p))
|
||||
}
|
||||
|
||||
fn check_write(
|
||||
fn check_write<'a>(
|
||||
&mut self,
|
||||
_p: &std::path::Path,
|
||||
p: &'a str,
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
) -> Result<PathBuf, deno_core::error::AnyError> {
|
||||
Ok(PathBuf::from(p))
|
||||
}
|
||||
|
||||
fn check_net<T: AsRef<str>>(
|
||||
@@ -153,6 +155,14 @@ impl NetPermissions for PermissionsContainer {
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_write_path<'a>(
|
||||
&mut self,
|
||||
p: &'a std::path::Path,
|
||||
_api_name: &str,
|
||||
) -> Result<std::borrow::Cow<'a, std::path::Path>, AnyError> {
|
||||
Ok(Cow::Borrowed(p))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
@@ -237,7 +247,7 @@ pub async fn eval_timeout(
|
||||
#[cfg(not(feature = "deno_core"))]
|
||||
{
|
||||
#[allow(unreachable_code)]
|
||||
return todo!();
|
||||
return Err(anyhow::anyhow!("Deno core is not enabled".to_string()).into());
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
|
||||
@@ -9,7 +9,7 @@ use tokio::net::TcpStream;
|
||||
use tokio_util::compat::TokioAsyncWriteCompatExt;
|
||||
use uuid::Uuid;
|
||||
use windmill_common::error::{self, Error};
|
||||
use windmill_common::worker::{get_sql_annotations, to_raw_value};
|
||||
use windmill_common::worker::to_raw_value;
|
||||
use windmill_common::{error::to_anyhow, jobs::QueuedJob};
|
||||
use windmill_parser_sql::{parse_db_resource, parse_mssql_sig};
|
||||
use windmill_queue::{append_logs, CanceledBy};
|
||||
@@ -68,7 +68,7 @@ pub async fn do_mssql(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let mut config = Config::new();
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use tokio::sync::Mutex;
|
||||
use windmill_common::{
|
||||
error::{to_anyhow, Error},
|
||||
jobs::QueuedJob,
|
||||
worker::{get_sql_annotations, to_raw_value},
|
||||
worker::to_raw_value,
|
||||
};
|
||||
use windmill_parser_sql::{
|
||||
parse_db_resource, parse_mysql_sig, parse_sql_blocks, parse_sql_statement_named_params,
|
||||
@@ -148,7 +148,7 @@ pub async fn do_mysql(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let opts = OptsBuilder::default()
|
||||
.db_name(Some(database.database))
|
||||
|
||||
@@ -30,7 +30,7 @@ use tokio_postgres::{
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use windmill_common::error::{self, Error};
|
||||
use windmill_common::worker::{get_sql_annotations, to_raw_value, CLOUD_HOSTED};
|
||||
use windmill_common::worker::{to_raw_value, CLOUD_HOSTED};
|
||||
use windmill_common::{error::to_anyhow, jobs::QueuedJob};
|
||||
use windmill_parser::{Arg, Typ};
|
||||
use windmill_parser_sql::{
|
||||
@@ -191,7 +191,7 @@ pub async fn do_postgresql(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let sslmode = match database.sslmode.as_deref() {
|
||||
Some("allow") => "prefer".to_string(),
|
||||
|
||||
@@ -345,7 +345,7 @@ try {{
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
child,
|
||||
false,
|
||||
!*DISABLE_NSJAIL,
|
||||
worker_name,
|
||||
&job.workspace_id,
|
||||
"php run",
|
||||
|
||||
@@ -498,7 +498,10 @@ except BaseException as e:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
tb = traceback.format_tb(exc_traceback)
|
||||
with open(result_json, 'w') as f:
|
||||
err = {{ "message": str(e), "name": e.__class__.__name__, "stack": '\n'.join(tb[1:]) }}
|
||||
err = {{ "message": str(e), "name": e.__class__.__name__, "stack": '\n'.join(tb[1:]) }}
|
||||
extra = e.__dict__
|
||||
if extra and len(extra) > 0:
|
||||
err['extra'] = extra
|
||||
flow_node_id = os.environ.get('WM_FLOW_STEP_ID')
|
||||
if flow_node_id:
|
||||
err['step_id'] = flow_node_id
|
||||
@@ -895,7 +898,7 @@ async fn handle_python_deps(
|
||||
let requirements = match requirements_o {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
let annotation = windmill_common::worker::get_annotation_python(inner_content);
|
||||
let annotation = windmill_common::worker::PythonAnnotations::parse(inner_content);
|
||||
let mut already_visited = vec![];
|
||||
|
||||
let requirements = windmill_parser_py_imports::parse_python_imports(
|
||||
|
||||
@@ -9,7 +9,6 @@ use serde_json::{json, value::RawValue, Value};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::HashMap;
|
||||
use windmill_common::error::to_anyhow;
|
||||
use windmill_common::worker::get_sql_annotations;
|
||||
|
||||
use windmill_common::jobs::QueuedJob;
|
||||
use windmill_common::{error::Error, worker::to_raw_value};
|
||||
@@ -266,7 +265,7 @@ pub async fn do_snowflake(
|
||||
return Err(Error::BadRequest("Missing database argument".to_string()));
|
||||
};
|
||||
|
||||
let annotations = get_sql_annotations(query);
|
||||
let annotations = windmill_common::worker::SqlAnnotations::parse(query);
|
||||
|
||||
let qualified_username = format!(
|
||||
"{}.{}",
|
||||
|
||||
@@ -12,7 +12,7 @@ use windmill_common::flows::{FlowModule, FlowModuleValue};
|
||||
use windmill_common::get_latest_deployed_hash_for_path;
|
||||
use windmill_common::jobs::JobPayload;
|
||||
use windmill_common::scripts::ScriptHash;
|
||||
use windmill_common::worker::{get_annotation_ts, to_raw_value, to_raw_value_owned, write_file};
|
||||
use windmill_common::worker::{to_raw_value, to_raw_value_owned, write_file};
|
||||
use windmill_common::{
|
||||
error::{self, to_anyhow},
|
||||
flows::FlowValue,
|
||||
@@ -953,10 +953,10 @@ async fn lock_modules<'c>(
|
||||
}
|
||||
|
||||
if language == ScriptLang::Bun || language == ScriptLang::Bunnative {
|
||||
let anns = get_annotation_ts(&content);
|
||||
if anns.native_mode && language == ScriptLang::Bun {
|
||||
let anns = windmill_common::worker::TypeScriptAnnotations::parse(&content);
|
||||
if anns.native && language == ScriptLang::Bun {
|
||||
language = ScriptLang::Bunnative;
|
||||
} else if !anns.native_mode && language == ScriptLang::Bunnative {
|
||||
} else if !anns.native && language == ScriptLang::Bunnative {
|
||||
language = ScriptLang::Bun;
|
||||
};
|
||||
}
|
||||
@@ -1003,10 +1003,10 @@ async fn lock_modules<'c>(
|
||||
|
||||
fn skip_creating_new_lock(language: &ScriptLang, content: &str) -> bool {
|
||||
if language == &ScriptLang::Bun || language == &ScriptLang::Bunnative {
|
||||
let anns = get_annotation_ts(&content);
|
||||
if anns.native_mode && language == &ScriptLang::Bun {
|
||||
let anns = windmill_common::worker::TypeScriptAnnotations::parse(&content);
|
||||
if anns.native && language == &ScriptLang::Bun {
|
||||
return false;
|
||||
} else if !anns.native_mode && language == &ScriptLang::Bunnative {
|
||||
} else if !anns.native && language == &ScriptLang::Bunnative {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@@ -1077,11 +1077,13 @@ async fn lock_modules_app(
|
||||
match new_lock {
|
||||
Ok(new_lock) => {
|
||||
append_logs(&job.id, &job.workspace_id, logs, db).await;
|
||||
let anns = get_annotation_ts(&content);
|
||||
let nlang = if anns.native_mode && language == ScriptLang::Bun {
|
||||
let anns =
|
||||
windmill_common::worker::TypeScriptAnnotations::parse(
|
||||
&content,
|
||||
);
|
||||
let nlang = if anns.native && language == ScriptLang::Bun {
|
||||
Some(ScriptLang::Bunnative)
|
||||
} else if !anns.native_mode && language == ScriptLang::Bunnative
|
||||
{
|
||||
} else if !anns.native && language == ScriptLang::Bunnative {
|
||||
Some(ScriptLang::Bun)
|
||||
} else {
|
||||
None
|
||||
@@ -1437,7 +1439,7 @@ async fn capture_dependency_job(
|
||||
}
|
||||
ScriptLang::Bun | ScriptLang::Bunnative => {
|
||||
let npm_mode = npm_mode.unwrap_or_else(|| {
|
||||
windmill_common::worker::get_annotation_ts(job_raw_code).npm_mode
|
||||
windmill_common::worker::TypeScriptAnnotations::parse(job_raw_code).npm
|
||||
});
|
||||
if !raw_deps {
|
||||
let _ = write_file(job_dir, "main.ts", job_raw_code)?;
|
||||
|
||||
@@ -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.405.5";
|
||||
export const VERSION = "v1.409.3";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { requireLogin, resolveWorkspace, validatePath } from "./context.ts";
|
||||
import { colors, Command, log, SEP, Table, yamlParse } from "./deps.ts";
|
||||
import { colors, Command, log, SEP, Table, yamlParseFile } from "./deps.ts";
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
import { ListableApp, Policy } from "./gen/types.gen.ts";
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function pushApp(
|
||||
return;
|
||||
}
|
||||
alreadySynced.push(localPath);
|
||||
remotePath.replaceAll(SEP, "/");
|
||||
remotePath = remotePath.replaceAll(SEP, "/");
|
||||
let app: any = undefined;
|
||||
// deleting old app if it exists in raw mode
|
||||
try {
|
||||
@@ -40,8 +40,8 @@ export async function pushApp(
|
||||
if (!localPath.endsWith(SEP)) {
|
||||
localPath += SEP;
|
||||
}
|
||||
const localAppRaw = await Deno.readTextFile(localPath + "app.yaml");
|
||||
const localApp = yamlParse(localAppRaw) as AppFile;
|
||||
const path = localPath + "app.yaml";
|
||||
const localApp = (await yamlParseFile(path)) as AppFile;
|
||||
|
||||
function replaceInlineScripts(rec: any) {
|
||||
if (!rec) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { log, yamlParse } from "./deps.ts";
|
||||
import { log, yamlParseFile } from "./deps.ts";
|
||||
|
||||
export interface SyncOptions {
|
||||
stateful?: boolean;
|
||||
@@ -40,9 +40,7 @@ export interface Codebase {
|
||||
|
||||
export async function readConfigFile(): Promise<SyncOptions> {
|
||||
try {
|
||||
const conf = yamlParse(
|
||||
await Deno.readTextFile("wmill.yaml")
|
||||
) as SyncOptions;
|
||||
const conf = (await yamlParseFile("wmill.yaml")) as SyncOptions;
|
||||
if (conf?.defaultTs == undefined) {
|
||||
log.warn(
|
||||
"No defaultTs defined in your wmill.yaml. Using 'bun' as default."
|
||||
|
||||
24
cli/deps.ts
24
cli/deps.ts
@@ -21,7 +21,29 @@ 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, parse as yamlParse } from "jsr:@std/yaml";
|
||||
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
|
||||
|
||||
|
||||
38
cli/flow.ts
38
cli/flow.ts
@@ -1,7 +1,7 @@
|
||||
// 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, yamlParse } from "./deps.ts";
|
||||
import { colors, Command, Table, yamlParseFile } from "./deps.ts";
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
|
||||
import { requireLogin, resolveWorkspace, validatePath } from "./context.ts";
|
||||
@@ -29,21 +29,23 @@ export function replaceInlineScripts(
|
||||
) {
|
||||
modules.forEach((m) => {
|
||||
if (m.value.type == "rawscript") {
|
||||
const path = m.value.content.split(" ")[1];
|
||||
m.value.content = Deno.readTextFileSync(localPath + path);
|
||||
const lock = m.value.lock;
|
||||
if (removeLocks && removeLocks.includes(path)) {
|
||||
m.value.lock = undefined;
|
||||
} else if (
|
||||
lock &&
|
||||
typeof lock == "string" &&
|
||||
lock.trimStart().startsWith("!inline ")
|
||||
) {
|
||||
const path = lock.split(" ")[1];
|
||||
try {
|
||||
m.value.lock = readInlinePathSync(localPath + path);
|
||||
} catch {
|
||||
log.error(`Lock file ${path} not found`);
|
||||
if (m.value.content.startsWith("!inline")) {
|
||||
const path = m.value.content.split(" ")[1];
|
||||
m.value.content = Deno.readTextFileSync(localPath + path);
|
||||
const lock = m.value.lock;
|
||||
if (removeLocks && removeLocks.includes(path)) {
|
||||
m.value.lock = undefined;
|
||||
} else if (
|
||||
lock &&
|
||||
typeof lock == "string" &&
|
||||
lock.trimStart().startsWith("!inline ")
|
||||
) {
|
||||
const path = lock.split(" ")[1];
|
||||
try {
|
||||
m.value.lock = readInlinePathSync(localPath + path);
|
||||
} catch {
|
||||
log.error(`Lock file ${path} not found`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (m.value.type == "forloopflow") {
|
||||
@@ -87,8 +89,7 @@ export async function pushFlow(
|
||||
if (!localPath.endsWith(SEP)) {
|
||||
localPath += SEP;
|
||||
}
|
||||
const localFlowRaw = await Deno.readTextFile(localPath + "flow.yaml");
|
||||
const localFlow = yamlParse(localFlowRaw) as FlowFile;
|
||||
const localFlow = (await yamlParseFile(localPath + "flow.yaml")) as FlowFile;
|
||||
|
||||
replaceInlineScripts(localFlow.value.modules, localPath, undefined);
|
||||
|
||||
@@ -120,6 +121,7 @@ export async function pushFlow(
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
//@ts-ignore
|
||||
`Failed to create flow ${remotePath}: ${e.body ?? e.message}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ export async function pushFolder(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -87,6 +88,7 @@ export async function pushFolder(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
throw Error(`Failed to create folder ${name}: ${e.body ?? e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export const OpenAPI: OpenAPIConfig = {
|
||||
PASSWORD: undefined,
|
||||
TOKEN: getEnv("WM_TOKEN"),
|
||||
USERNAME: undefined,
|
||||
VERSION: '1.401.0',
|
||||
VERSION: '1.407.2',
|
||||
WITH_CREDENTIALS: true,
|
||||
interceptors: {
|
||||
request: new Interceptors(),
|
||||
|
||||
@@ -1015,6 +1015,9 @@ export type FlowModule = {
|
||||
skip_if_stopped?: boolean;
|
||||
expr: string;
|
||||
};
|
||||
skip_if?: {
|
||||
expr: string;
|
||||
};
|
||||
sleep?: InputTransform;
|
||||
cache_ttl?: number;
|
||||
timeout?: number;
|
||||
@@ -1176,6 +1179,7 @@ export type FlowStatusModule = {
|
||||
approver: string;
|
||||
}>;
|
||||
failed_retries?: Array<(string)>;
|
||||
skipped?: boolean;
|
||||
};
|
||||
|
||||
export type type4 = 'WaitingForPriorSteps' | 'WaitingForEvents' | 'WaitingForExecutor' | 'InProgress' | 'Success' | 'Failure';
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
path,
|
||||
Confirm,
|
||||
yamlStringify,
|
||||
yamlParse,
|
||||
yamlParseFile,
|
||||
Command,
|
||||
setClient,
|
||||
Table,
|
||||
@@ -25,8 +25,8 @@ import {
|
||||
import {
|
||||
add as workspaceSetup,
|
||||
addWorkspace,
|
||||
allWorkspaces,
|
||||
removeWorkspace,
|
||||
setActiveWorkspace,
|
||||
} from "./workspace.ts";
|
||||
import {
|
||||
pushInstanceSettings,
|
||||
@@ -35,8 +35,9 @@ import {
|
||||
pushInstanceConfigs,
|
||||
type SimplifiedSettings,
|
||||
} from "./settings.ts";
|
||||
import { sleep, deepEqual } from "./utils.ts";
|
||||
import { deepEqual } from "./utils.ts";
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
import { getActiveWorkspace } from "./workspace.ts";
|
||||
|
||||
export interface Instance {
|
||||
remote: string;
|
||||
@@ -293,22 +294,18 @@ async function instancePull(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
log.info("No instance-level changes to apply");
|
||||
}
|
||||
|
||||
sleep(1000);
|
||||
|
||||
if (opts.includeWorkspaces) {
|
||||
log.info("\nPulling all workspaces");
|
||||
const rootDir = Deno.cwd();
|
||||
const localWorkspaces = await getLocalWorkspaces(rootDir, instance.prefix);
|
||||
|
||||
const previousActiveWorkspace = await getActiveWorkspace(undefined);
|
||||
const remoteWorkspaces = await wmill.listWorkspacesAsSuperAdmin({
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
});
|
||||
let localWorkspaces = await allWorkspaces();
|
||||
localWorkspaces = localWorkspaces.filter((w) =>
|
||||
w.name.startsWith(instance.prefix + "_")
|
||||
);
|
||||
const rootDir = Deno.cwd();
|
||||
for (const remoteWorkspace of remoteWorkspaces) {
|
||||
log.info("\nPulling workspace " + remoteWorkspace.id);
|
||||
sleep(1000);
|
||||
const workspaceName = instance.prefix + "_" + remoteWorkspace.id;
|
||||
await Deno.mkdir(path.join(rootDir, workspaceName), {
|
||||
recursive: true,
|
||||
@@ -341,7 +338,7 @@ async function instancePull(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
}
|
||||
|
||||
const localWorkspacesToDelete = localWorkspaces.filter(
|
||||
(w) => !remoteWorkspaces.find((r) => r.id === w.workspaceId)
|
||||
(w) => !remoteWorkspaces.find((r) => r.id === w.id)
|
||||
);
|
||||
|
||||
if (localWorkspacesToDelete.length > 0) {
|
||||
@@ -350,20 +347,23 @@ async function instancePull(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
(await Confirm.prompt({
|
||||
message:
|
||||
"Do you want to delete the local copy of workspaces that don't exist anymore on the instance?\n" +
|
||||
localWorkspacesToDelete.map((w) => w.workspaceId).join(", "),
|
||||
localWorkspacesToDelete.map((w) => w).join(", "),
|
||||
default: true,
|
||||
}));
|
||||
|
||||
if (confirmDelete) {
|
||||
for (const workspace of localWorkspacesToDelete) {
|
||||
await removeWorkspace(workspace.name, false, {});
|
||||
await Deno.remove(path.join(rootDir, workspace.name), {
|
||||
await removeWorkspace(workspace.id, false, {});
|
||||
await Deno.remove(path.join(rootDir, workspace.dir), {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (previousActiveWorkspace) {
|
||||
await setActiveWorkspace(previousActiveWorkspace?.name);
|
||||
}
|
||||
log.info(colors.green.underline.bold("All workspaces pulled"));
|
||||
}
|
||||
}
|
||||
@@ -421,10 +421,10 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
log.info("No instance-level changes to apply");
|
||||
}
|
||||
|
||||
sleep(1000);
|
||||
|
||||
if (opts.includeWorkspaces) {
|
||||
instances = await allInstances();
|
||||
const rootDir = Deno.cwd();
|
||||
|
||||
const localPrefix = (await Select.prompt({
|
||||
message: "What is the prefix of the local workspaces you want to sync?",
|
||||
options: [
|
||||
@@ -440,18 +440,18 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
});
|
||||
let localWorkspaces = await allWorkspaces();
|
||||
localWorkspaces = localWorkspaces.filter((w) =>
|
||||
w.name.startsWith(localPrefix + "_")
|
||||
);
|
||||
|
||||
log.info("\nPushing all workspaces");
|
||||
const rootDir = Deno.cwd();
|
||||
const previousActiveWorkspace = await getActiveWorkspace(undefined);
|
||||
|
||||
const localWorkspaces = await getLocalWorkspaces(rootDir, localPrefix);
|
||||
|
||||
log.info(
|
||||
`\nPushing all workspaces: ${localWorkspaces.map((x) => x.id).join(", ")}`
|
||||
);
|
||||
for (const localWorkspace of localWorkspaces) {
|
||||
log.info("\nPushing workspace " + localWorkspace.workspaceId);
|
||||
sleep(1000);
|
||||
log.info("\nPushing workspace " + localWorkspace.id);
|
||||
try {
|
||||
await Deno.chdir(path.join(rootDir, localWorkspace.name));
|
||||
await Deno.chdir(path.join(rootDir, localWorkspace.dir));
|
||||
} catch (_) {
|
||||
throw new Error(
|
||||
"Workspace folder not found, are you in the right directory?"
|
||||
@@ -459,9 +459,9 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
}
|
||||
|
||||
try {
|
||||
const workspaceSettings = yamlParse(
|
||||
await Deno.readTextFile("settings.yaml")
|
||||
) as SimplifiedSettings;
|
||||
const workspaceSettings = (await yamlParseFile(
|
||||
"settings.yaml"
|
||||
)) as SimplifiedSettings;
|
||||
await workspaceSetup(
|
||||
{
|
||||
token: instance.token,
|
||||
@@ -471,8 +471,8 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
createWorkspaceName: workspaceSettings.name,
|
||||
createUsername: undefined,
|
||||
},
|
||||
localWorkspace.name,
|
||||
localWorkspace.workspaceId,
|
||||
localWorkspace.dir,
|
||||
localWorkspace.id,
|
||||
instance.remote
|
||||
);
|
||||
} catch (_) {
|
||||
@@ -482,7 +482,7 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
continue;
|
||||
}
|
||||
await push({
|
||||
workspace: localWorkspace.name,
|
||||
workspace: localWorkspace.dir,
|
||||
token: undefined,
|
||||
baseUrl: undefined,
|
||||
includeGroups: true,
|
||||
@@ -495,7 +495,7 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
}
|
||||
|
||||
const workspacesToDelete = remoteWorkspaces.filter(
|
||||
(w) => !localWorkspaces.find((l) => l.workspaceId === w.id)
|
||||
(w) => !localWorkspaces.find((l) => l.id === w.id)
|
||||
);
|
||||
if (workspacesToDelete.length > 0) {
|
||||
const confirmDelete =
|
||||
@@ -514,10 +514,28 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (previousActiveWorkspace) {
|
||||
await setActiveWorkspace(previousActiveWorkspace?.name);
|
||||
}
|
||||
log.info(colors.green.underline.bold("All workspaces pushed"));
|
||||
}
|
||||
}
|
||||
|
||||
async function getLocalWorkspaces(rootDir: string, localPrefix: string) {
|
||||
const localWorkspaces: { dir: string; id: string }[] = [];
|
||||
|
||||
for await (const dir of Deno.readDir(rootDir)) {
|
||||
const dirName = dir.name;
|
||||
if (dirName.startsWith(localPrefix + "_")) {
|
||||
localWorkspaces.push({
|
||||
dir: dirName,
|
||||
id: dirName.substring(localPrefix.length + 1),
|
||||
});
|
||||
}
|
||||
}
|
||||
return localWorkspaces;
|
||||
}
|
||||
|
||||
async function switchI(opts: {}, instanceName: string) {
|
||||
const all = await allInstances();
|
||||
if (all.findIndex((x) => x.name === instanceName) === -1) {
|
||||
@@ -560,6 +578,7 @@ async function whoami(opts: {}) {
|
||||
log.info(JSON.stringify(whoamiInfo, null, 2));
|
||||
} catch (error) {
|
||||
log.error(
|
||||
//@ts-ignore
|
||||
colors.red(`Failed to retrieve whoami information: ${error.message}`)
|
||||
);
|
||||
}
|
||||
@@ -602,7 +621,7 @@ const command = new Command()
|
||||
.description("Remove an instance")
|
||||
.complete("instance", async () => (await allInstances()).map((x) => x.name))
|
||||
.arguments("<instance:string:instance>")
|
||||
.action(async (instance) => {
|
||||
.action(async (instance: any) => {
|
||||
const instances = await allInstances();
|
||||
|
||||
const choice = (await Select.prompt({
|
||||
|
||||
@@ -60,7 +60,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.405.5";
|
||||
export const VERSION = "1.409.3";
|
||||
|
||||
const command = new Command()
|
||||
.name("wmill")
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import { GlobalOptions } from "./types.ts";
|
||||
import { SEP, colors, log, path, yamlParse, yamlStringify } from "./deps.ts";
|
||||
import {
|
||||
SEP,
|
||||
colors,
|
||||
log,
|
||||
path,
|
||||
yamlParseFile,
|
||||
yamlStringify,
|
||||
} from "./deps.ts";
|
||||
import {
|
||||
ScriptMetadata,
|
||||
defaultScriptMetadata,
|
||||
@@ -119,9 +126,9 @@ export async function generateFlowLockInternal(
|
||||
return remote_path;
|
||||
}
|
||||
|
||||
const flowValue = yamlParse(
|
||||
await Deno.readTextFile(folder! + SEP + "flow.yaml")
|
||||
) as FlowFile;
|
||||
const flowValue = (await yamlParseFile(
|
||||
folder! + SEP + "flow.yaml"
|
||||
)) as FlowFile;
|
||||
|
||||
if (!justUpdateMetadataLock) {
|
||||
const changedScripts = [];
|
||||
@@ -798,7 +805,7 @@ export async function parseMetadataFile(
|
||||
try {
|
||||
metadataFilePath = scriptPath + ".script.yaml";
|
||||
await Deno.stat(metadataFilePath);
|
||||
const payload: any = yamlParse(await Deno.readTextFile(metadataFilePath));
|
||||
const payload: any = await yamlParseFile(metadataFilePath);
|
||||
replaceLock(payload);
|
||||
|
||||
return {
|
||||
@@ -840,9 +847,9 @@ export async function parseMetadataFile(
|
||||
codebases,
|
||||
false
|
||||
);
|
||||
scriptInitialMetadata = yamlParse(
|
||||
await Deno.readTextFile(metadataFilePath)
|
||||
) as ScriptMetadata;
|
||||
scriptInitialMetadata = (await yamlParseFile(
|
||||
metadataFilePath
|
||||
)) as ScriptMetadata;
|
||||
replaceLock(scriptInitialMetadata);
|
||||
} catch (e) {
|
||||
log.info(
|
||||
@@ -868,8 +875,7 @@ interface Lock {
|
||||
const WMILL_LOCKFILE = "wmill-lock.yaml";
|
||||
export async function readLockfile(): Promise<Lock> {
|
||||
try {
|
||||
const lockfile = await Deno.readTextFile(WMILL_LOCKFILE);
|
||||
const read = yamlParse(lockfile);
|
||||
const read = await yamlParseFile(WMILL_LOCKFILE);
|
||||
if (typeof read == "object") {
|
||||
return read as Lock;
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { yamlStringify } from "./deps.ts";
|
||||
import { Confirm } from "./deps.ts";
|
||||
import { colors } from "./deps.ts";
|
||||
import { yamlParse } from "./deps.ts";
|
||||
import { yamlParseFile } from "./deps.ts";
|
||||
import { log } from "./deps.ts";
|
||||
import { compareInstanceObjects } from "./instance.ts";
|
||||
import { isSuperset } from "./types.ts";
|
||||
@@ -259,17 +259,24 @@ export async function pushWorkspaceKey(
|
||||
}
|
||||
}
|
||||
|
||||
export async function readInstanceSettings() {
|
||||
let localSettings: GlobalSetting[] = [];
|
||||
|
||||
try {
|
||||
localSettings = (await yamlParseFile(
|
||||
"instance_settings.yaml"
|
||||
)) as GlobalSetting[];
|
||||
} catch {
|
||||
log.warn("No instance_settings.yaml found");
|
||||
}
|
||||
return localSettings;
|
||||
}
|
||||
|
||||
export async function pullInstanceSettings(preview = false) {
|
||||
const remoteSettings = await wmill.listGlobalSettings();
|
||||
|
||||
if (preview) {
|
||||
let localSettings: GlobalSetting[] = [];
|
||||
|
||||
try {
|
||||
localSettings = yamlParse(
|
||||
await Deno.readTextFile("instance_settings.yaml")
|
||||
) as GlobalSetting[];
|
||||
} catch {}
|
||||
const localSettings: GlobalSetting[] = await readInstanceSettings();
|
||||
|
||||
return compareInstanceObjects(
|
||||
remoteSettings,
|
||||
@@ -294,9 +301,7 @@ export async function pushInstanceSettings(
|
||||
baseUrl?: string
|
||||
) {
|
||||
const remoteSettings = await wmill.listGlobalSettings();
|
||||
let localSettings = (await Deno.readTextFile("instance_settings.yaml")
|
||||
.then((raw) => yamlParse(raw))
|
||||
.catch(() => [])) as GlobalSetting[];
|
||||
let localSettings: GlobalSetting[] = await readInstanceSettings();
|
||||
|
||||
if (baseUrl) {
|
||||
localSettings = localSettings.filter((s) => s.name !== "base_url");
|
||||
@@ -354,6 +359,17 @@ export async function pushInstanceSettings(
|
||||
}
|
||||
}
|
||||
|
||||
export async function readLocalConfigs() {
|
||||
let localConfigs: Config[] = [];
|
||||
|
||||
try {
|
||||
localConfigs = (await yamlParseFile("instance_configs.yaml")) as Config[];
|
||||
} catch {
|
||||
log.warn("No instance_configs.yaml found");
|
||||
}
|
||||
return localConfigs;
|
||||
}
|
||||
|
||||
export async function pullInstanceConfigs(preview = false) {
|
||||
const remoteConfigs = (await wmill.listConfigs()).map((x) => {
|
||||
return {
|
||||
@@ -363,12 +379,7 @@ export async function pullInstanceConfigs(preview = false) {
|
||||
});
|
||||
|
||||
if (preview) {
|
||||
let localConfigs: Config[] = [];
|
||||
try {
|
||||
localConfigs = yamlParse(
|
||||
await Deno.readTextFile("instance_configs.yaml")
|
||||
) as Config[];
|
||||
} catch {}
|
||||
const localConfigs: Config[] = await readLocalConfigs();
|
||||
|
||||
return compareInstanceObjects(
|
||||
remoteConfigs,
|
||||
@@ -395,9 +406,7 @@ export async function pushInstanceConfigs(preview: boolean = false) {
|
||||
name: removeWorkerPrefix(x.name),
|
||||
};
|
||||
});
|
||||
const localConfigs = (await Deno.readTextFile("instance_configs.yaml")
|
||||
.then((raw) => yamlParse(raw))
|
||||
.catch(() => [])) as Config[];
|
||||
const localConfigs = await readLocalConfigs();
|
||||
|
||||
if (preview) {
|
||||
return compareInstanceObjects(
|
||||
@@ -415,7 +424,9 @@ export async function pushInstanceConfigs(preview: boolean = false) {
|
||||
}
|
||||
try {
|
||||
await wmill.updateConfig({
|
||||
name: config.name.startsWith('worker__') ? config.name : `worker__${config.name}`,
|
||||
name: config.name.startsWith("worker__")
|
||||
? config.name
|
||||
: `worker__${config.name}`,
|
||||
requestBody: config.config,
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
12
cli/sync.ts
12
cli/sync.ts
@@ -9,7 +9,7 @@ import {
|
||||
path,
|
||||
log,
|
||||
yamlStringify,
|
||||
yamlParse,
|
||||
yamlParseContent,
|
||||
SEP,
|
||||
} from "./deps.ts";
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
@@ -112,7 +112,7 @@ async function addCodebaseDigestIfRelevant(
|
||||
if (isTs) {
|
||||
const c = findCodebase(path, codebases);
|
||||
if (c) {
|
||||
const parsed: any = yamlParse(content);
|
||||
const parsed: any = yamlParseContent(path, content);
|
||||
if (parsed && typeof parsed == "object") {
|
||||
parsed["codebase"] = c.digest;
|
||||
parsed["lock"] = undefined;
|
||||
@@ -660,7 +660,7 @@ export async function elementsToMap(
|
||||
if (json) {
|
||||
o = JSON.parse(content);
|
||||
} else {
|
||||
o = yamlParse(content);
|
||||
o = yamlParseContent(path, content);
|
||||
}
|
||||
if (o["is_secret"]) {
|
||||
continue;
|
||||
@@ -704,7 +704,7 @@ async function compareDynFSElement(
|
||||
|
||||
function parseYaml(k: string, v: string) {
|
||||
if (k.endsWith(".script.yaml")) {
|
||||
const o: any = yamlParse(v);
|
||||
const o: any = yamlParseContent(k, v);
|
||||
if (typeof o == "object") {
|
||||
if (Array.isArray(o?.["lock"])) {
|
||||
o["lock"] = o["lock"].join("\n");
|
||||
@@ -715,7 +715,7 @@ async function compareDynFSElement(
|
||||
}
|
||||
return o;
|
||||
} else if (k.endsWith(".app.yaml")) {
|
||||
const o: any = yamlParse(v);
|
||||
const o: any = yamlParseContent(k, v);
|
||||
const o2 = o["policy"];
|
||||
|
||||
if (typeof o2 == "object") {
|
||||
@@ -728,7 +728,7 @@ async function compareDynFSElement(
|
||||
}
|
||||
return o;
|
||||
} else {
|
||||
return yamlParse(v);
|
||||
return yamlParseContent(k, v);
|
||||
}
|
||||
}
|
||||
for (const [k, v] of Object.entries(m1)) {
|
||||
|
||||
10
cli/types.ts
10
cli/types.ts
@@ -6,7 +6,7 @@ import {
|
||||
colors,
|
||||
log,
|
||||
path,
|
||||
yamlParse,
|
||||
yamlParseContent,
|
||||
yamlStringify,
|
||||
} from "./deps.ts";
|
||||
import { pushApp } from "./apps.ts";
|
||||
@@ -115,7 +115,7 @@ export async function pushObj(
|
||||
newObj: any,
|
||||
plainSecrets: boolean,
|
||||
alreadySynced: string[],
|
||||
message?: string,
|
||||
message?: string
|
||||
) {
|
||||
const typeEnding = getTypeStrFromPath(p);
|
||||
|
||||
@@ -155,7 +155,7 @@ export async function pushObj(
|
||||
|
||||
export function parseFromPath(p: string, content: string): any {
|
||||
return p.endsWith(".yaml")
|
||||
? yamlParse(content)
|
||||
? yamlParseContent(p, content)
|
||||
: p.endsWith(".json")
|
||||
? JSON.parse(content)
|
||||
: content;
|
||||
@@ -164,7 +164,7 @@ export function parseFromFile(p: string): any {
|
||||
if (p.endsWith(".json")) {
|
||||
return JSON.parse(Deno.readTextFileSync(p));
|
||||
} else if (p.endsWith(".yaml") || p.endsWith(".yml")) {
|
||||
return yamlParse(Deno.readTextFileSync(p));
|
||||
return yamlParseContent(p, Deno.readTextFileSync(p));
|
||||
} else {
|
||||
throw new Error("Could not read file " + p);
|
||||
}
|
||||
@@ -227,7 +227,7 @@ export function getTypeStrFromPath(
|
||||
return typeEnding;
|
||||
} else {
|
||||
if (isFileResource(p)) {
|
||||
return "resource"
|
||||
return "resource";
|
||||
}
|
||||
throw new Error("Could not infer type of path " + JSON.stringify(parsed));
|
||||
}
|
||||
|
||||
51
cli/user.ts
51
cli/user.ts
@@ -13,7 +13,7 @@ import {
|
||||
log,
|
||||
Table,
|
||||
yamlStringify,
|
||||
yamlParse,
|
||||
yamlParseFile,
|
||||
} from "./deps.ts";
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
import {
|
||||
@@ -170,6 +170,7 @@ export async function pushWorkspaceUser(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -189,6 +190,7 @@ export async function pushWorkspaceUser(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -251,6 +253,7 @@ export async function pushGroup(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -301,6 +304,7 @@ export async function pushGroup(
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -329,6 +333,7 @@ export async function pushGroup(
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -368,11 +373,13 @@ export async function pushGroup(
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
console.error(e.body);
|
||||
throw e;
|
||||
}
|
||||
@@ -383,11 +390,7 @@ export async function pullInstanceUsers(preview: boolean = false) {
|
||||
const remoteUsers = await wmill.globalUsersExport();
|
||||
|
||||
if (preview) {
|
||||
let localUsers: ExportedUser[] = [];
|
||||
try {
|
||||
const raw = await Deno.readTextFile("instance_users.yaml");
|
||||
localUsers = yamlParse(raw) as ExportedUser[];
|
||||
} catch {}
|
||||
const localUsers: ExportedUser[] = await readInstanceUsers();
|
||||
return compareInstanceObjects(remoteUsers, localUsers, "email", "user");
|
||||
} else {
|
||||
log.info("Pulling users from instance...");
|
||||
@@ -399,11 +402,31 @@ export async function pullInstanceUsers(preview: boolean = false) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function readInstanceUsers() {
|
||||
let localUsers: ExportedUser[] = [];
|
||||
try {
|
||||
localUsers = (await yamlParseFile("instance_users.yaml")) as ExportedUser[];
|
||||
} catch {
|
||||
log.warn("No instance_users.yaml file found");
|
||||
}
|
||||
return localUsers;
|
||||
}
|
||||
|
||||
export async function readInstanceGroups() {
|
||||
let localGroups: InstanceGroup[] = [];
|
||||
try {
|
||||
localGroups = (await yamlParseFile(
|
||||
"instance_groups.yaml"
|
||||
)) as ExportedInstanceGroup[];
|
||||
} catch {
|
||||
log.warn("No instance_groups.yaml file found");
|
||||
}
|
||||
return localGroups;
|
||||
}
|
||||
|
||||
export async function pushInstanceUsers(preview: boolean = false) {
|
||||
const remoteUsers = await wmill.globalUsersExport();
|
||||
const localUsers = (await Deno.readTextFile("instance_users.yaml")
|
||||
.then((raw) => yamlParse(raw))
|
||||
.catch(() => [])) as ExportedUser[];
|
||||
const localUsers: ExportedUser[] = await readInstanceUsers();
|
||||
|
||||
if (preview) {
|
||||
return compareInstanceObjects(localUsers, remoteUsers, "email", "user");
|
||||
@@ -421,11 +444,7 @@ export async function pullInstanceGroups(preview = false) {
|
||||
const remoteGroups = await wmill.exportInstanceGroups();
|
||||
|
||||
if (preview) {
|
||||
let localGroups: InstanceGroup[] = [];
|
||||
try {
|
||||
const raw = await Deno.readTextFile("instance_groups.yaml");
|
||||
localGroups = yamlParse(raw) as InstanceGroup[];
|
||||
} catch {}
|
||||
const localGroups = await readInstanceGroups();
|
||||
return compareInstanceObjects(remoteGroups, localGroups, "name", "group");
|
||||
} else {
|
||||
log.info("Pulling groups from instance...");
|
||||
@@ -441,9 +460,7 @@ export async function pullInstanceGroups(preview = false) {
|
||||
|
||||
export async function pushInstanceGroups(preview: boolean = false) {
|
||||
const remoteGroups = await wmill.exportInstanceGroups();
|
||||
const localGroups = (await Deno.readTextFile("instance_groups.yaml")
|
||||
.then((raw) => yamlParse(raw))
|
||||
.catch(() => [])) as ExportedInstanceGroup[];
|
||||
const localGroups = await readInstanceGroups();
|
||||
|
||||
if (preview) {
|
||||
return compareInstanceObjects(localGroups, remoteGroups, "name", "group");
|
||||
|
||||
@@ -55,7 +55,10 @@ export function deepEqual<T>(a: T, b: T): boolean {
|
||||
if (a.valueOf !== Object.prototype.valueOf) {
|
||||
return a.valueOf() === b.valueOf();
|
||||
}
|
||||
if (a.toString !== Object.prototype.toString) {
|
||||
if (
|
||||
a.toString !== Object.prototype.toString &&
|
||||
typeof a.toString == "function"
|
||||
) {
|
||||
return a.toString() === b.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@ export async function allWorkspaces(): Promise<Workspace[]> {
|
||||
}
|
||||
|
||||
async function getActiveWorkspaceName(
|
||||
opts: GlobalOptions
|
||||
opts: GlobalOptions | undefined
|
||||
): Promise<string | undefined> {
|
||||
if (opts.workspace) {
|
||||
return opts.workspace;
|
||||
if (opts?.workspace) {
|
||||
return opts?.workspace;
|
||||
}
|
||||
try {
|
||||
return await Deno.readTextFile((await getRootStore()) + "/activeWorkspace");
|
||||
@@ -47,7 +47,7 @@ async function getActiveWorkspaceName(
|
||||
}
|
||||
|
||||
export async function getActiveWorkspace(
|
||||
opts: GlobalOptions
|
||||
opts: GlobalOptions | undefined
|
||||
): Promise<Workspace | undefined> {
|
||||
const name = await getActiveWorkspaceName(opts);
|
||||
if (!name) {
|
||||
@@ -115,7 +115,12 @@ async function switchC(opts: GlobalOptions, workspaceName: string) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await Deno.writeTextFile(
|
||||
await setActiveWorkspace(workspaceName);
|
||||
return;
|
||||
}
|
||||
|
||||
export async function setActiveWorkspace(workspaceName: string) {
|
||||
await Deno.writeTextFile(
|
||||
(await getRootStore()) + "/activeWorkspace",
|
||||
workspaceName
|
||||
);
|
||||
@@ -241,10 +246,8 @@ export async function add(
|
||||
},
|
||||
opts
|
||||
);
|
||||
await Deno.writeTextFile(
|
||||
(await getRootStore()) + "/activeWorkspace",
|
||||
workspaceName
|
||||
);
|
||||
await setActiveWorkspace(workspaceName);
|
||||
|
||||
log.info(
|
||||
colors.green.underline(
|
||||
`Added workspace ${workspaceName} for ${workspaceId} on ${remote}!`
|
||||
|
||||
@@ -4,11 +4,13 @@ ARG PYTHON_IMAGE=python:3.11.10-slim-bookworm
|
||||
|
||||
FROM ${RUST_IMAGE} AS rust_base
|
||||
|
||||
RUN yum install -y rust-toolset
|
||||
|
||||
RUN yum update -y && \
|
||||
yum install -y git openssl-devel npm nodejs rustfmt
|
||||
|
||||
# Install rust manually
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true cargo install cargo-chef --version ^0.1
|
||||
|
||||
WORKDIR /windmill
|
||||
@@ -35,7 +37,7 @@ COPY /backend/parsers/windmill-parser-wasm/pkg/ /backend/parsers/windmill-parser
|
||||
COPY /typescript-client/docs/ /frontend/static/tsdocs/
|
||||
|
||||
RUN npm run generate-backend-client
|
||||
ENV NODE_OPTIONS "--max-old-space-size=8192"
|
||||
ENV NODE_OPTIONS "--max-old-space-size=10240"
|
||||
RUN npm run build
|
||||
|
||||
|
||||
|
||||
@@ -3,17 +3,16 @@
|
||||
This directory contains the Dockerfiles for building Windmill binaries for Red Hat Linux 9.
|
||||
|
||||
We build Windmill on the Red Hat Universal Base Image 9. Windmill requires the xmlsec1-devel package which is not available in the default UBI9 repositories. It is however included in the CodeReady Builder for RHEL9 repository which requires a RedHat subscription.
|
||||
Moreover, only rust v1.75 is supported on Red Hat Linux 9. To make Windmill compatible with Rust v1.75, you need to pin the following libraries:
|
||||
```
|
||||
aws-config = "=1.4.0"
|
||||
aws-sdk-sts = "=1.25.0"
|
||||
aws-sdk-ssooidc = "=1.25.0"
|
||||
aws-sdk-sso = "=1.25.0"
|
||||
```
|
||||
|
||||
Make sure to include `aws-sdk-ssooidc` and `aws-sdk-sso` in the Cargo.toml of windmill-common as well to enforce the correct versions of the nested dependencies. Make them optional and include them in the `parquet` feature.
|
||||
It's also possible that you need to add `#[async_recursion]` to the `lock_modules` function in the `backend/windmill-worker/src/worker_lockfiles.rs` file for it to compile.
|
||||
|
||||
Once the image is built, you can simply copy the binary on any Red Hat Linux 9 machine and run it. You will just need to install the xmlsec1 package which can be installed directly using `yum/dnf install xmlsec1`.
|
||||
|
||||
|
||||
## Notes
|
||||
- you will need to register on Red Hat and have an individual developer subscription and pass the username and password to docker build:
|
||||
```
|
||||
docker build \
|
||||
-f docker/RHEL9/Dockerfile \
|
||||
--build-arg features="$features" \
|
||||
--secret id=rh_username,src=/path/to/rh_username \
|
||||
--secret id=rh_password,src=/path/to/rh_password \
|
||||
.
|
||||
```
|
||||
@@ -1,28 +0,0 @@
|
||||
Error parsing /git/windmill/backend/windmill-api/openapi.yaml: duplicated mapping key (8350:3)
|
||||
|
||||
8347 | application/json:
|
||||
8348 | schema: {}
|
||||
8349 |
|
||||
8350 | /w/{workspace}/job_helpers/loa ...
|
||||
----------^
|
||||
8351 | get:
|
||||
8352 | summary: Load a preview of ...
|
||||
ParserError: Error parsing /git/windmill/backend/windmill-api/openapi.yaml: duplicated mapping key (8350:3)
|
||||
|
||||
8347 | application/json:
|
||||
8348 | schema: {}
|
||||
8349 |
|
||||
8350 | /w/{workspace}/job_helpers/loa ...
|
||||
----------^
|
||||
8351 | get:
|
||||
8352 | summary: Load a preview of ...
|
||||
at Object.parse (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parsers/yaml.js:44:23)
|
||||
at getResult (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:116:22)
|
||||
at runNextPlugin (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:64:32)
|
||||
at /git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:55:9
|
||||
at new Promise (<anonymous>)
|
||||
at Object.run (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/util/plugins.js:54:12)
|
||||
at parseFile (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:130:38)
|
||||
at parse (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/parse.js:56:30)
|
||||
at async $RefParser.parse (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:115:28)
|
||||
at async $RefParser.resolve (/git/windmill/frontend/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/index.js:145:13)
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.405.5",
|
||||
"version": "1.409.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.405.5",
|
||||
"version": "1.409.3",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.405.5",
|
||||
"version": "1.409.3",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
@@ -300,6 +300,14 @@
|
||||
"types": "./package/utils.d.ts",
|
||||
"default": "./package/utils.js"
|
||||
},
|
||||
"./components/icons/store": {
|
||||
"types": "./package/components/icons/store.d.ts",
|
||||
"default": "./package/components/icons/store.js"
|
||||
},
|
||||
"./script_helpers": {
|
||||
"types": "./package/script_helpers.d.ts",
|
||||
"default": "./package/script_helpers.js"
|
||||
},
|
||||
"./infer": {
|
||||
"types": "./package/infer.d.ts",
|
||||
"default": "./package/infer.js"
|
||||
@@ -341,6 +349,11 @@
|
||||
"types": "./package/components/DropdownV2.svelte.d.ts",
|
||||
"svelte": "./package/components/DropdownV2.svelte",
|
||||
"default": "./package/components/DropdownV2.svelte"
|
||||
},
|
||||
"./components/flows/FlowHistoryInner.svelte": {
|
||||
"types": "./package/components/flows/FlowHistoryInner.svelte.d.ts",
|
||||
"svelte": "./package/components/flows/FlowHistoryInner.svelte",
|
||||
"default": "./package/components/flows/FlowHistoryInner.svelte"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
@@ -426,6 +439,9 @@
|
||||
"components/EditableSchemaWrapper.svelte": [
|
||||
"./package/components/schema/EditableSchemaWrapper.svelte.d.ts"
|
||||
],
|
||||
"components/flows/FlowHistoryInner.svelte": [
|
||||
"./package/components/flows/FlowHistoryInner.svelte.d.ts"
|
||||
],
|
||||
"utils": [
|
||||
"./package/utils.d.ts"
|
||||
],
|
||||
@@ -461,6 +477,12 @@
|
||||
],
|
||||
"components/DropdownV2.svelte": [
|
||||
"./package/components/DropdownV2.svelte.d.ts"
|
||||
],
|
||||
"script_helpers": [
|
||||
"./package/script_helpers.d.ts"
|
||||
],
|
||||
"components/icons/store": [
|
||||
"./package/components/icons/store.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user