Compare commits

...

6 Commits

Author SHA1 Message Date
dependabot[bot]
65f076fd34 chore(deps): bump actions/setup-python from 2 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 18:46:25 +00:00
Alexander Petric
4f38cfd17a fix docker image build (#4968)
* main docker file

* fix docker image build test tag

* don't remove tag

* don't update docker file
2024-12-23 18:33:20 +01:00
pyranota
6bd2dc3832 fix(windows&python): Access is denied. (os error 5) (#4969) 2024-12-23 16:25:01 +01:00
HugoCasa
1d20dea663 fix(backend): timeout for bigquery/graphql/snowflake (#4965) 2024-12-22 13:52:23 +09:00
Ruben Fiszel
c1d11ce044 improve app editor behavior when hiding right panel 2024-12-21 00:53:55 +01:00
Alexander Petric
cfdd7d13f9 fix python build (#4963) 2024-12-20 23:12:58 +01:00
14 changed files with 165 additions and 139 deletions

View File

@@ -42,7 +42,7 @@ jobs:
- uses: actions/setup-go@v2
with:
go-version: 1.21.5
- uses: actions/setup-python@v2
- uses: actions/setup-python@v5
with:
python-version: 3.11
- uses: oven-sh/setup-bun@v2

View File

@@ -3,8 +3,7 @@ env:
IMAGE_NAME:
${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' && github.repository || 'windmill-labs/windmill-test' }}
DEV_SHA:
${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' && 'dev' || github.event.inputs.tag }}
${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' && 'dev' || github.event.inputs.tag || github.sha }}
name: Build windmill:main
on:
push:

View File

@@ -2,6 +2,7 @@ use std::collections::HashMap;
use futures::future::BoxFuture;
use futures::{FutureExt, TryFutureExt};
use reqwest::Client;
use serde_json::{json, value::RawValue, Value};
use windmill_common::error::to_anyhow;
use windmill_common::jobs::QueuedJob;
@@ -9,11 +10,11 @@ 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,
};
use windmill_queue::{CanceledBy, HTTP_CLIENT};
use windmill_queue::CanceledBy;
use serde::Deserialize;
use crate::common::OccupancyMetrics;
use crate::common::{build_http_client, OccupancyMetrics};
use crate::handle_child::run_future_with_polling_update_job_poller;
use crate::{
common::{build_args_values, resolve_job_timeout},
@@ -68,9 +69,10 @@ fn do_bigquery_inner<'a>(
all_statement_values: &'a HashMap<String, Value>,
project_id: &'a str,
token: &'a str,
timeout_ms: i32,
timeout_ms: u64,
column_order: Option<&'a mut Option<Vec<String>>>,
skip_collect: bool,
http_client: &'a Client,
) -> windmill_common::error::Result<BoxFuture<'a, windmill_common::error::Result<Box<RawValue>>>> {
let param_names = parse_sql_statement_named_params(query, '@');
@@ -86,7 +88,7 @@ fn do_bigquery_inner<'a>(
.collect::<Vec<&Value>>();
let result_f = async move {
let response = HTTP_CLIENT
let response = http_client
.post(
"https://bigquery.googleapis.com/bigquery/v2/projects/".to_string()
+ project_id
@@ -249,13 +251,10 @@ pub async fn do_bigquery(
.await
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
let timeout_ms = i32::try_from(
resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout)
.await
.0
.as_millis(),
)
.unwrap_or(200000);
let (timeout_duration, _, _) =
resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout).await;
let timeout_ms = timeout_duration.as_millis() as u64;
let http_client = build_http_client(timeout_duration)?;
let project_id = authentication_manager
.project_id()
@@ -325,6 +324,7 @@ pub async fn do_bigquery(
timeout_ms,
None,
annotations.return_last_result && i < queries.len() - 1,
&http_client,
)
})
.collect::<windmill_common::error::Result<Vec<_>>>()?;
@@ -353,6 +353,7 @@ pub async fn do_bigquery(
timeout_ms,
Some(column_order),
false,
&http_client,
)?
};

View File

@@ -3,6 +3,7 @@ use async_recursion::async_recursion;
use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use serde_json::{json, Value};
@@ -32,11 +33,7 @@ use windmill_common::{
use anyhow::{anyhow, Result};
use std::path::Path;
use std::{
collections::HashMap,
sync::Arc,
time::Duration,
};
use std::{collections::HashMap, sync::Arc, time::Duration};
use uuid::Uuid;
use windmill_common::{variables, DB};
@@ -965,3 +962,12 @@ pub fn use_flow_root_path(flow_path: &str) -> String {
return flow_path.to_string();
}
}
pub fn build_http_client(timeout_duration: std::time::Duration) -> error::Result<Client> {
reqwest::ClientBuilder::new()
.user_agent("windmill/beta")
.timeout(timeout_duration)
.connect_timeout(std::time::Duration::from_secs(10))
.build()
.map_err(|e| Error::InternalErr(format!("Error building http client: {e:#}")))
}

View File

@@ -8,11 +8,11 @@ use windmill_common::jobs::QueuedJob;
use windmill_common::worker::to_raw_value;
use windmill_common::{error::Error, worker::CLOUD_HOSTED};
use windmill_parser_graphql::parse_graphql_sig;
use windmill_queue::{CanceledBy, HTTP_CLIENT};
use windmill_queue::CanceledBy;
use serde::Deserialize;
use crate::common::OccupancyMetrics;
use crate::common::{build_http_client, resolve_job_timeout, OccupancyMetrics};
use crate::handle_child::run_future_with_polling_update_job_poller;
use crate::{common::build_args_map, AuthedClientBackgroundTask};
@@ -81,8 +81,12 @@ pub async fn do_graphql(
}
}
}
let (timeout_duration, _, _) =
resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout).await;
let mut request = HTTP_CLIENT.post(api.base_url).json(&json!({
let http_client = build_http_client(timeout_duration)?;
let mut request = http_client.post(api.base_url).json(&json!({
"query": query,
"variables": variables
}));

View File

@@ -336,6 +336,18 @@ pub async fn uv_pip_compile(
.args(&args)
.stdout(Stdio::piped())
.stderr(Stdio::piped());
#[cfg(windows)]
{
child_cmd
.env("SystemRoot", SYSTEM_ROOT.as_str())
.env("USERPROFILE", crate::USERPROFILE_ENV.as_str())
.env(
"TMP",
std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")),
);
}
let child_process = start_child_process(child_cmd, uv_cmd).await?;
append_logs(&job_id, &w_id, logs, db).await;
handle_child(

View File

@@ -4,7 +4,7 @@ use core::fmt::Write;
use futures::future::BoxFuture;
use futures::{FutureExt, TryFutureExt};
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
use reqwest::Response;
use reqwest::{Client, Response};
use serde_json::{json, value::RawValue, Value};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
@@ -17,7 +17,7 @@ use windmill_queue::{CanceledBy, HTTP_CLIENT};
use serde::{Deserialize, Serialize};
use crate::common::{resolve_job_timeout, OccupancyMetrics};
use crate::common::{build_http_client, resolve_job_timeout, OccupancyMetrics};
use crate::handle_child::run_future_with_polling_update_job_poller;
use crate::{common::build_args_values, AuthedClientBackgroundTask};
@@ -122,6 +122,7 @@ fn do_snowflake_inner<'a>(
token_is_keypair: bool,
column_order: Option<&'a mut Option<Vec<String>>>,
skip_collect: bool,
http_client: &'a Client,
) -> windmill_common::error::Result<BoxFuture<'a, windmill_common::error::Result<Box<RawValue>>>> {
body.insert("statement".to_string(), json!(query));
@@ -145,7 +146,7 @@ fn do_snowflake_inner<'a>(
}
let result_f = async move {
let mut request = HTTP_CLIENT
let mut request = http_client
.post(format!(
"https://{}.snowflakecomputing.com/api/v2/statements/",
account_identifier.to_uppercase()
@@ -365,6 +366,11 @@ pub async fn do_snowflake(
let queries = parse_sql_blocks(query);
let (timeout_duration, _, _) =
resolve_job_timeout(&db, &job.workspace_id, job.id, job.timeout).await;
let http_client = build_http_client(timeout_duration)?;
let result_f = if queries.len() > 1 {
let futures = queries
.iter()
@@ -379,6 +385,7 @@ pub async fn do_snowflake(
token_is_keypair,
None,
annotations.return_last_result && i < queries.len() - 1,
&http_client,
)
})
.collect::<windmill_common::error::Result<Vec<_>>>()?;
@@ -407,6 +414,7 @@ pub async fn do_snowflake(
token_is_keypair,
Some(column_order),
false,
&http_client,
)?
};
let r = run_future_with_polling_update_job_poller(

View File

@@ -30,10 +30,10 @@
import ItemPicker from '$lib/components/ItemPicker.svelte'
import VariableEditor from '$lib/components/VariableEditor.svelte'
import { VariableService, type Job, type Policy } from '$lib/gen'
import { VariableService, type Policy } from '$lib/gen'
import { initHistory } from '$lib/history'
import { Component, Minus, Paintbrush, Plus, Smartphone, Scan, Hand, Grab } from 'lucide-svelte'
import { findGridItem, findGridItemParentGrid } from './appUtils'
import { animateTo, findGridItem, findGridItemParentGrid } from './appUtils'
import ComponentNavigation from './component/ComponentNavigation.svelte'
import CssSettings from './componentsPanel/CssSettings.svelte'
import SettingsPanel from './SettingsPanel.svelte'
@@ -49,7 +49,6 @@
import { getTheme } from './componentsPanel/themeUtils'
import StylePanel from './settingsPanel/StylePanel.svelte'
import type DiffDrawer from '$lib/components/DiffDrawer.svelte'
import RunnableJobPanel from './RunnableJobPanel.svelte'
import HideButton from './settingsPanel/HideButton.svelte'
import AppEditorBottomPanel from './AppEditorBottomPanel.svelte'
import panzoom from 'panzoom'
@@ -386,27 +385,6 @@
}
}
function animateTo(start: number, end: number, onUpdate: (newValue: number) => void) {
const duration = 400
const startTime = performance.now()
function animate(time: number) {
const elapsed = time - startTime
const progress = Math.min(elapsed / duration, 1)
const currentValue = start + (end - start) * easeInOut(progress)
onUpdate(currentValue)
if (progress < 1) {
requestAnimationFrame(animate)
}
}
requestAnimationFrame(animate)
}
function easeInOut(t: number) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
}
$: $cssEditorOpen && selectCss()
function selectCss() {
@@ -583,13 +561,11 @@
} else {
leftPanelSize = storedLeftPanelSize
}
storedLeftPanelSize = 0
}
function showRightPanel() {
rightPanelSize = storedRightPanelSize
centerPanelSize = centerPanelSize - storedRightPanelSize
storedRightPanelSize = 0
}
function showBottomPanel(animate: boolean = false) {
@@ -611,7 +587,6 @@
runnablePanelSize = storedBottomPanelSize
gridPanelSize = gridPanelSize - storedBottomPanelSize
}
storedBottomPanelSize = 0
}
function keydown(event: KeyboardEvent) {
@@ -705,9 +680,6 @@
$: $connectingInput.opened, updatePannelInConnecting()
let testJob: Job | undefined = undefined
let jobToWatch: { componentId: string; job: string } | undefined = undefined
$: updateCursorStyle(!!$connectingInput.opened && !$panzoomActive)
function updateCursorStyle(disabled: boolean) {
@@ -1125,14 +1097,7 @@
{rightPanelSize}
{centerPanelWidth}
{runnablePanelSize}
>
<RunnableJobPanel
float={rightPanelSize !== 0}
hidden={runnablePanelSize === 0}
bind:testJob
bind:jobToWatch
/>
</AppEditorBottomPanel>
/>
</Pane>
{/if}
</Splitpanes>

View File

@@ -1,5 +1,4 @@
<script lang="ts">
import { twMerge } from 'tailwind-merge'
import InlineScriptsPanel from './inlineScriptsPanel/InlineScriptsPanel.svelte'
import RunnableJobPanel from './RunnableJobPanel.svelte'
@@ -10,15 +9,13 @@
{#if rightPanelSize !== 0}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class={twMerge('relative h-full w-full overflow-x-visible')} on:mouseenter on:mouseleave>
<div class="relative h-full w-full overflow-x-visible" on:mouseenter on:mouseleave>
<InlineScriptsPanel on:hidePanel />
<RunnableJobPanel hidden={runnablePanelSize === 0} />
<slot />
</div>
{:else}
<div class="flex flex-row relative w-full h-full">
<InlineScriptsPanel width={centerPanelWidth - 400} on:hidePanel />
<slot />
<InlineScriptsPanel width={centerPanelWidth * 0.66} on:hidePanel />
<RunnableJobPanel float={false} hidden={runnablePanelSize === 0} />
</div>
{/if}

View File

@@ -9,6 +9,7 @@
export let hidden: boolean = false
export let testJob: Job | undefined = undefined
export let jobToWatch: { componentId: string; job: string } | undefined = undefined
export let width: number | undefined = undefined
const { runnableJobEditorPanel, selectedComponentInEditor } =
getContext<AppEditorContext>('AppEditorContext')
@@ -16,7 +17,7 @@
let testJobLoader: TestJobLoader
$: $runnableJobEditorPanel.focused &&
$: ($runnableJobEditorPanel.focused || !float) &&
$selectedComponentInEditor &&
$runnableJobEditorPanel.jobs &&
updateSelectedJob()
@@ -59,7 +60,10 @@
<RunnableJobPanelInner {testIsLoading} {frontendJob} {testJob} />
</div>
{:else}
<div class="flex flex-col w-full">
<div
class="flex flex-col min-w-0 grow h-full"
style={width !== undefined ? `width:${width}px;` : ''}
>
{#if $selectedComponentInEditor}
<RunnableJobPanelInner {testIsLoading} {frontendJob} {testJob} />
{:else if !hidden}

View File

@@ -1281,3 +1281,24 @@ export function areShadowsTheSame(
shadow1.h === shadow2.h
)
}
export function animateTo(start: number, end: number, onUpdate: (newValue: number) => void) {
const duration = 400
const startTime = performance.now()
function animate(time: number) {
const elapsed = time - startTime
const progress = Math.min(elapsed / duration, 1)
const currentValue = start + (end - start) * easeInOut(progress)
onUpdate(currentValue)
if (progress < 1) {
requestAnimationFrame(animate)
}
}
requestAnimationFrame(animate)
}
function easeInOut(t: number) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
}

View File

@@ -1,7 +1,6 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppEditorContext, AppViewerContext, HiddenRunnable } from '../../types'
import SplitPanesWrapper from '$lib/components/splitPanes/SplitPanesWrapper.svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import InlineScriptsPanelList from './InlineScriptsPanelList.svelte'
import InlineScriptEditor from './InlineScriptEditor.svelte'
@@ -105,60 +104,58 @@
export let width: number | undefined = undefined
</script>
<SplitPanesWrapper>
<Splitpanes
class={twMerge('!overflow-visible')}
style={width !== undefined ? `width:${width}px;` : ''}
>
<Pane size={25}>
<InlineScriptsPanelList on:hidePanel />
</Pane>
<Pane size={75}>
{#if !$selectedComponentInEditor}
<div class="text-sm text-secondary text-center py-8 px-2">
Select a script on the left panel
</div>
{:else if gridItem}
{#key gridItem?.id}
<InlineScriptsPanelWithTable
<Splitpanes
class={twMerge('!overflow-visible')}
style={width !== undefined ? `width:${width}px;` : 'width: 100%;'}
>
<Pane size={25}>
<InlineScriptsPanelList on:hidePanel />
</Pane>
<Pane size={75}>
{#if !$selectedComponentInEditor}
<div class="text-sm text-secondary text-center py-8 px-2">
Select a script on the left panel
</div>
{:else if gridItem}
{#key gridItem?.id}
<InlineScriptsPanelWithTable
on:createScriptFromInlineScript={(e) => {
createScriptFromInlineScript(gridItem?.id ?? 'unknown', e.detail)
}}
bind:gridItem
/>
{/key}
{:else if unusedInlineScript > -1 && $app.unusedInlineScripts?.[unusedInlineScript]}
{#key unusedInlineScript}
<InlineScriptEditor
on:createScriptFromInlineScript={() =>
sendUserToast('Cannot save to workspace unused scripts', true)}
id={`unused-${unusedInlineScript}`}
bind:name={$app.unusedInlineScripts[unusedInlineScript].name}
bind:inlineScript={$app.unusedInlineScripts[unusedInlineScript].inlineScript}
on:delete={() => {
// remove the script from the array at the index
$app.unusedInlineScripts.splice(unusedInlineScript, 1)
$app.unusedInlineScripts = [...$app.unusedInlineScripts]
}}
/>
{/key}
{:else if hiddenInlineScript > -1}
{#key hiddenInlineScript}
{#if $app.hiddenInlineScripts?.[hiddenInlineScript]}
<InlineScriptHiddenRunnable
on:createScriptFromInlineScript={(e) => {
createScriptFromInlineScript(gridItem?.id ?? 'unknown', e.detail)
createScriptFromInlineScript(BG_PREFIX + hiddenInlineScript, e.detail)
}}
bind:gridItem
/>
{/key}
{:else if unusedInlineScript > -1 && $app.unusedInlineScripts?.[unusedInlineScript]}
{#key unusedInlineScript}
<InlineScriptEditor
on:createScriptFromInlineScript={() =>
sendUserToast('Cannot save to workspace unused scripts', true)}
id={`unused-${unusedInlineScript}`}
bind:name={$app.unusedInlineScripts[unusedInlineScript].name}
bind:inlineScript={$app.unusedInlineScripts[unusedInlineScript].inlineScript}
on:delete={() => {
// remove the script from the array at the index
$app.unusedInlineScripts.splice(unusedInlineScript, 1)
$app.unusedInlineScripts = [...$app.unusedInlineScripts]
}}
/>
{/key}
{:else if hiddenInlineScript > -1}
{#key hiddenInlineScript}
{#if $app.hiddenInlineScripts?.[hiddenInlineScript]}
<InlineScriptHiddenRunnable
on:createScriptFromInlineScript={(e) => {
createScriptFromInlineScript(BG_PREFIX + hiddenInlineScript, e.detail)
}}
transformer={$selectedComponentInEditor?.endsWith('_transformer')}
on:delete={() => deleteBackgroundScript(hiddenInlineScript)}
id={BG_PREFIX + hiddenInlineScript}
bind:runnable={$app.hiddenInlineScripts[hiddenInlineScript]}
/>{/if}{/key}
{:else}
<div class="text-sm text-tertiary text-center py-8 px-2">
No script found at id {$selectedComponentInEditor}
</div>
{/if}
</Pane>
</Splitpanes>
</SplitPanesWrapper>
transformer={$selectedComponentInEditor?.endsWith('_transformer')}
on:delete={() => deleteBackgroundScript(hiddenInlineScript)}
id={BG_PREFIX + hiddenInlineScript}
bind:runnable={$app.hiddenInlineScripts[hiddenInlineScript]}
/>{/if}{/key}
{:else}
<div class="text-sm text-tertiary text-center py-8 px-2">
No script found at id {$selectedComponentInEditor}
</div>
{/if}
</Pane>
</Splitpanes>

View File

@@ -18,13 +18,18 @@
export let defaultUserInput = false
export let hideCreateScript = false
export let onlyFlow = false
let tab: Tab = onlyFlow ? 'workspaceflows' : 'inlinescripts'
let filter: string = ''
let picker: Drawer
export let rawApps = false
const { app, workspace } = getContext<AppViewerContext>('AppViewerContext')
let tab: Tab = onlyFlow
? 'workspaceflows'
: $app?.unusedInlineScripts?.length > 0
? 'inlinescripts'
: 'workspacescripts'
let filter: string = ''
let picker: Drawer
const dispatch = createEventDispatcher<{
pick: {
runnable: Runnable
@@ -138,15 +143,17 @@
<div class="max-w-6xl">
<Tabs bind:selected={tab}>
{#if !onlyFlow}
<Tab size="sm" value="inlinescripts">
<div class="flex gap-2 items-center my-1">
<Building size={18} strokeWidth={1.5} />
Detached Inline Scripts
</div>
</Tab>
{#if !rawApps}
<Tab size="sm" value="inlinescripts">
<div class="flex gap-2 items-center my-1">
<Building size={18} strokeWidth={1.5} />
Detached Inline Scripts
</div>
</Tab>
{/if}
<Tab size="sm" value="workspacescripts">
<div class="flex gap-2 items-center my-1">
<Building size={18} strokeWidth={1.5}/>
<Building size={18} strokeWidth={1.5} />
Workspace Scripts
</div>
</Tab>

View File

@@ -49,7 +49,12 @@ user friendly experience. We use \
echo "" >> windmill-api/README.md.tmp
tail -r windmill-api/README.md | tail -n +14 | tail -r >> windmill-api/README.md.tmp
if [[ "$OSTYPE" == "darwin"* ]]; then
tail -r windmill-api/README.md | tail -n +14 | tail -r >> windmill-api/README.md.tmp
else
head -n -13 windmill-api/README.md >> windmill-api/README.md.tmp
fi
mv windmill-api/README.md.tmp windmill-api/README.md
cd windmill-api && poetry build