Merge remote-tracking branch 'origin/main' into glm/select-users

This commit is contained in:
Guilhem
2026-02-16 18:38:22 +00:00
21 changed files with 392 additions and 206 deletions
+13
View File
@@ -1,5 +1,18 @@
# Changelog
## [1.636.0](https://github.com/windmill-labs/windmill/compare/v1.635.1...v1.636.0) (2026-02-16)
### Features
* allow adding workspace scripts and flows as AI chat context ([#7882](https://github.com/windmill-labs/windmill/issues/7882)) ([5b8ec50](https://github.com/windmill-labs/windmill/commit/5b8ec502fef8fb439200e18b8c610d0f5998b6df))
* google native triggers ([#7837](https://github.com/windmill-labs/windmill/issues/7837)) ([6f24f19](https://github.com/windmill-labs/windmill/commit/6f24f1939d75a597acc74c1589794d511e041baa))
### Bug Fixes
* mark base_url as unsaved when using browser fallback ([#7964](https://github.com/windmill-labs/windmill/issues/7964)) ([e7b0b00](https://github.com/windmill-labs/windmill/commit/e7b0b00f5696828dec094155298d0c9dc033b355))
## [1.635.1](https://github.com/windmill-labs/windmill/compare/v1.635.0...v1.635.1) (2026-02-15)
+183 -183
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.635.1"
version = "1.636.0"
authors.workspace = true
edition.workspace = true
@@ -75,7 +75,7 @@ members = [
exclude = ["./windmill-duckdb-ffi-internal"]
[workspace.package]
version = "1.635.1"
version = "1.636.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
+1 -1
View File
@@ -1 +1 @@
8c214ec5039be5353f5fea920e27b0c6af61e1fd
9f6e1e533df7711600ec2b8d5f0c958448db1a20
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.635.1
version: 1.636.0
title: Windmill API
contact:
+2
View File
@@ -3,6 +3,8 @@ pub mod crd_ee;
#[cfg(feature = "private")]
pub mod db_sync_ee;
#[cfg(feature = "private")]
pub use db_sync_ee as db_sync;
#[cfg(feature = "private")]
pub mod reconciler_ee;
#[cfg(feature = "private")]
pub mod resolve_ee;
@@ -8,6 +8,8 @@
#[cfg(feature = "private")]
pub use crate::oauth_refresh_ee::_refresh_token;
#[cfg(feature = "private")]
pub use crate::oauth_refresh_ee::_refresh_workspace_integration_token;
#[cfg(not(feature = "private"))]
use sqlx::{Postgres, Transaction};
@@ -36,3 +38,156 @@ pub async fn _refresh_token<'c>(
)
.await
}
#[cfg(not(feature = "private"))]
pub async fn _refresh_workspace_integration_token<'c>(
mut tx: Transaction<'c, Postgres>,
path: &str,
w_id: &str,
account_id: i32,
db: &DB,
client_name: &str,
refresh_token: &str,
) -> error::Result<String> {
use windmill_common::global_settings::{
get_instance_oauth_credentials, workspace_integration_auth_endpoint,
workspace_integration_oauth_key, workspace_integration_token_endpoint,
};
use windmill_common::utils::now_from_db;
use windmill_common::variables::{build_crypt, encrypt};
use windmill_oauth::{OClient, RefreshToken, Url, OAUTH_HTTP_CLIENT};
tracing::info!(
client = %client_name,
workspace_id = %w_id,
account_id = %account_id,
"Refreshing workspace integration OAuth token"
);
let oauth_data: serde_json::Value = sqlx::query_scalar(
"SELECT oauth_data FROM workspace_integrations \
WHERE workspace_id = $1 AND service_name::text = $2",
)
.bind(w_id)
.bind(client_name)
.fetch_optional(&mut *tx)
.await?
.ok_or_else(|| {
error::Error::NotFound(format!(
"Workspace integration for {} not found or not configured",
client_name
))
})?;
let is_instance_shared = oauth_data
.get("instance_shared")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let (client_id, client_secret, base_url);
if is_instance_shared {
let oauth_key = workspace_integration_oauth_key(client_name);
let (id, secret) = get_instance_oauth_credentials(db, oauth_key).await?;
client_id = id;
client_secret = secret;
base_url = String::new();
} else {
client_id = oauth_data["client_id"]
.as_str()
.ok_or_else(|| {
error::Error::InternalErr("Missing client_id in workspace integration".into())
})?
.to_string();
client_secret = oauth_data["client_secret"]
.as_str()
.ok_or_else(|| {
error::Error::InternalErr(
"Missing client_secret in workspace integration".into(),
)
})?
.to_string();
base_url = oauth_data["base_url"].as_str().unwrap_or("").to_string();
}
let token_endpoint = workspace_integration_token_endpoint(client_name, &base_url);
let auth_endpoint = workspace_integration_auth_endpoint(client_name, &base_url);
let auth_url = Url::parse(&auth_endpoint)
.map_err(|e| error::Error::InternalErr(format!("Invalid auth URL: {}", e)))?;
let token_url = Url::parse(&token_endpoint)
.map_err(|e| error::Error::InternalErr(format!("Invalid token URL: {}", e)))?;
let mut client = OClient::new(client_id, auth_url, token_url);
client.set_client_secret(client_secret);
let token = client
.exchange_refresh_token(&RefreshToken::from(refresh_token))
.with_client(&*OAUTH_HTTP_CLIENT)
.execute::<serde_json::Value>()
.await
.map_err(|e| {
error::Error::InternalErr(format!(
"Failed to refresh workspace integration token: {:?}",
e
))
})?;
#[derive(serde::Deserialize)]
struct WsTokenResponse {
access_token: String,
refresh_token: Option<String>,
expires_in: Option<i64>,
}
let token_result: WsTokenResponse = serde_json::from_value(token)
.map_err(|e| error::Error::InternalErr(format!("Failed to parse token response: {}", e)))?;
let expires_at = now_from_db(&mut *tx).await?
+ chrono::Duration::try_seconds(
token_result
.expires_in
.ok_or_else(|| {
error::Error::InternalErr("expires_in expected and not found".into())
})?
.try_into()
.unwrap(),
)
.unwrap_or_default();
sqlx::query(
"UPDATE account SET refresh_token = $1, expires_at = $2, refresh_error = NULL \
WHERE workspace_id = $3 AND id = $4",
)
.bind(
token_result
.refresh_token
.as_deref()
.unwrap_or(refresh_token),
)
.bind(expires_at)
.bind(w_id)
.bind(account_id)
.execute(&mut *tx)
.await?;
tx.commit().await?;
let token_str = &token_result.access_token;
let mc = build_crypt(db, w_id).await?;
let encrypted_token = encrypt(&mc, token_str);
sqlx::query("UPDATE variable SET value = $1 WHERE workspace_id = $2 AND path = $3")
.bind(encrypted_token)
.bind(w_id)
.bind(path)
.execute(db)
.await?;
tracing::info!(
client = %client_name,
workspace_id = %w_id,
account_id = %account_id,
"Workspace integration OAuth token refreshed successfully"
);
Ok(token_result.access_token)
}
+1 -1
View File
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.635.1";
export const VERSION = "v1.636.0";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({
+1 -1
View File
@@ -79,7 +79,7 @@ export {
// }
// });
export const VERSION = "1.635.1";
export const VERSION = "1.636.0";
// Re-exported from constants.ts to maintain backwards compatibility
export { WM_FORK_PREFIX } from "./core/constants.ts";
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.635.1",
"version": "1.636.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.635.1",
"version": "1.636.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.635.1",
"version": "1.636.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@@ -35,9 +35,10 @@
loading?: boolean
openSmtpSettings?: () => void
oauths?: Record<string, any>
warning?: string
}
let { setting, version, values, loading = true, openSmtpSettings, oauths }: Props = $props()
let { setting, version, values, loading = true, openSmtpSettings, oauths, warning }: Props = $props()
const dispatch = createEventDispatcher()
let latestKeyRenewalAttempt: {
@@ -282,6 +283,11 @@
bind:value={$values[setting.key]}
class="max-w-lg"
/>
{#if warning}
<span class="text-yellow-600 dark:text-yellow-500 text-2xs">
{warning}
</span>
{/if}
{#if setting.advancedToggle}
<div class="mt-1">
<Toggle
@@ -52,6 +52,7 @@
let requirePreexistingUserForOauth: boolean = $state(false)
let initialValues: Record<string, any> = $state({})
let baseUrlIsFallback = $state(false)
let snowflakeAccountIdentifier = $state('')
let version: string = $state('')
let loading = $state(true)
@@ -96,16 +97,20 @@
let nvalues: Record<string, any> = { ...gs }
if (!nvalues['base_url']) {
nvalues['base_url'] = window.location.origin
}
baseUrlIsFallback = !nvalues['base_url']
if (nvalues['retention_period_secs'] == undefined) {
nvalues['retention_period_secs'] = 60 * 60 * 24 * 30
}
applyFormDefaults(nvalues)
// Snapshot initialValues before applying the base_url fallback so that
// the dirty-check detects the unsaved default and enables the Save button.
initialValues = JSON.parse(JSON.stringify(nvalues))
if (baseUrlIsFallback) {
nvalues['base_url'] = window.location.origin
}
$values = nvalues
initialValues = JSON.parse(JSON.stringify($values))
loading = false
// populate snowflake account identifier from db
@@ -186,6 +191,7 @@
initialValues = JSON.parse(JSON.stringify($values))
initialOauths = JSON.parse(JSON.stringify(oauths))
initialRequirePreexistingUserForOauth = requirePreexistingUserForOauth
baseUrlIsFallback = false
if (licenseKeySet) {
setLicense()
@@ -502,6 +508,9 @@
const v = $values[s.key]
initialValues[s.key] = v !== undefined ? JSON.parse(JSON.stringify(v)) : undefined
}
if (categorySettings.some((s) => s.key === 'base_url')) {
baseUrlIsFallback = false
}
// Handle Auth/OAuth/SAML-specific saves
if (category === 'Auth/OAuth/SAML') {
@@ -998,6 +1007,7 @@
{values}
{version}
{oauths}
warning={setting.key === 'base_url' && baseUrlIsFallback ? 'Auto-detected from browser — not yet saved' : undefined}
/>
{/if}
{/each}
+2 -2
View File
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.635.1"
wmill_pg = ">=1.635.1"
wmill = ">=1.636.0"
wmill_pg = ">=1.636.0"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: '3.0.3'
info:
version: 1.635.1
version: 1.636.0
title: OpenFlow Spec
contact:
name: Ruben Fiszel
@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.635.1'
ModuleVersion = '1.636.0'
# Supported PSEditions
# CompatiblePSEditions = @()
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.635.1"
version = "1.636.0"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.635.1"
version = "1.636.0"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.635.1",
"version": "1.636.0",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./sqlUtils.ts", "./client.ts"]
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.635.1",
"version": "1.636.0",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"sideEffects": false,
+1 -1
View File
@@ -1 +1 @@
1.635.1
1.636.0