feat: add configurable preview job tag override in default tags settings (#8649)
* feat: add configurable preview job tag override in default tags settings Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: skip re-tagging for FlowPreview jobs when preview override is active Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,7 +51,7 @@ use windmill_common::{
|
||||
MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NO_DEFAULT_MAVEN_SETTING,
|
||||
NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING,
|
||||
OTEL_TRACING_PROXY_SETTING, PIP_INDEX_URL_SETTING, POWERSHELL_REPO_PAT_SETTING,
|
||||
POWERSHELL_REPO_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
POWERSHELL_REPO_URL_SETTING, PREVIEW_TAGS_OVERRIDE_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RESTART_COORDINATION_SETTING,
|
||||
RETENTION_PERIOD_SECS_SETTING, RUBY_REPOS_SETTING, SAML_METADATA_SETTING,
|
||||
SCIM_TOKEN_SETTING, SMTP_SETTING, TEAMS_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
@@ -99,16 +99,17 @@ use windmill_worker::{
|
||||
};
|
||||
|
||||
use crate::monitor::{
|
||||
initial_load, load_keep_job_dir, load_metrics_debug_enabled, load_require_preexisting_user,
|
||||
load_tag_per_workspace_enabled, load_tag_per_workspace_workspaces, monitor_db,
|
||||
reload_app_workspaced_route_setting, reload_audit_log_retention_days_setting,
|
||||
reload_base_url_setting, reload_bunfig_install_scopes_setting,
|
||||
reload_critical_alert_mute_ui_setting, reload_critical_alerts_on_token_expiry_setting,
|
||||
reload_critical_error_channels_setting, reload_extra_pip_index_url_setting,
|
||||
reload_http_route_workspaced_route_setting, reload_hub_api_secret_setting,
|
||||
reload_hub_base_url_setting, reload_instance_events_webhook_setting,
|
||||
reload_job_default_timeout_setting, reload_job_isolation_setting, reload_jwt_secret_setting,
|
||||
reload_license_key, reload_npm_config_registry_setting, reload_otel_tracing_proxy_setting,
|
||||
initial_load, load_keep_job_dir, load_metrics_debug_enabled, load_preview_tags_override,
|
||||
load_require_preexisting_user, load_tag_per_workspace_enabled,
|
||||
load_tag_per_workspace_workspaces, monitor_db, reload_app_workspaced_route_setting,
|
||||
reload_audit_log_retention_days_setting, reload_base_url_setting,
|
||||
reload_bunfig_install_scopes_setting, reload_critical_alert_mute_ui_setting,
|
||||
reload_critical_alerts_on_token_expiry_setting, reload_critical_error_channels_setting,
|
||||
reload_extra_pip_index_url_setting, reload_http_route_workspaced_route_setting,
|
||||
reload_hub_api_secret_setting, reload_hub_base_url_setting,
|
||||
reload_instance_events_webhook_setting, reload_job_default_timeout_setting,
|
||||
reload_job_isolation_setting, reload_jwt_secret_setting, reload_license_key,
|
||||
reload_npm_config_registry_setting, reload_otel_tracing_proxy_setting,
|
||||
reload_pip_index_url_setting, reload_retention_period_setting, reload_scim_token_setting,
|
||||
reload_smtp_config, reload_uv_index_strategy_setting, reload_worker_config, MonitorIteration,
|
||||
};
|
||||
@@ -1742,6 +1743,11 @@ async fn process_notify_event(
|
||||
);
|
||||
}
|
||||
}
|
||||
PREVIEW_TAGS_OVERRIDE_SETTING => {
|
||||
if let Err(e) = load_preview_tags_override(db).await {
|
||||
tracing::error!("Error loading preview tags override: {e:#}");
|
||||
}
|
||||
}
|
||||
SMTP_SETTING => {
|
||||
reload_smtp_config(db).await;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ use windmill_common::{
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING,
|
||||
NPMRC_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OTEL_SETTING,
|
||||
OTEL_TRACING_PROXY_SETTING, PIP_INDEX_URL_SETTING, POWERSHELL_REPO_PAT_SETTING,
|
||||
POWERSHELL_REPO_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
POWERSHELL_REPO_URL_SETTING, PREVIEW_TAGS_OVERRIDE_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
UV_INDEX_STRATEGY_SETTING,
|
||||
@@ -79,8 +79,8 @@ use windmill_common::{
|
||||
load_periodic_bash_script_interval_from_env, load_whitelist_env_vars_from_env,
|
||||
load_worker_config, reload_custom_tags_setting, store_pull_query,
|
||||
store_suspended_pull_query, Connection, WorkerConfig, DEFAULT_TAGS_PER_WORKSPACE,
|
||||
DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, SCRIPT_TOKEN_EXPIRY, SMTP_CONFIG, WINDMILL_DIR,
|
||||
WORKER_CONFIG, WORKER_GROUP,
|
||||
DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, PREVIEW_TAGS_OVERRIDE, SCRIPT_TOKEN_EXPIRY,
|
||||
SMTP_CONFIG, WINDMILL_DIR, WORKER_CONFIG, WORKER_GROUP,
|
||||
},
|
||||
KillpillSender, AUDIT_LOG_RETENTION_DAYS, BASE_URL, CRITICAL_ALERTS_ON_DB_OVERSIZE,
|
||||
CRITICAL_ALERTS_ON_TOKEN_EXPIRY, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB,
|
||||
@@ -235,6 +235,10 @@ pub async fn initial_load(
|
||||
if let Err(e) = load_tag_per_workspace_workspaces(db).await {
|
||||
tracing::error!("Error loading default tag per workpsace workspaces: {e:#}");
|
||||
}
|
||||
|
||||
if let Err(e) = load_preview_tags_override(db).await {
|
||||
tracing::error!("Error loading preview tags override: {e:#}");
|
||||
}
|
||||
}
|
||||
|
||||
if server_mode {
|
||||
@@ -499,6 +503,16 @@ pub async fn load_tag_per_workspace_workspaces(db: &DB) -> error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn load_preview_tags_override(db: &DB) -> error::Result<()> {
|
||||
let value = load_value_from_global_settings(db, PREVIEW_TAGS_OVERRIDE_SETTING).await;
|
||||
|
||||
match value {
|
||||
Ok(Some(serde_json::Value::Bool(t))) => PREVIEW_TAGS_OVERRIDE.store(t, Ordering::Relaxed),
|
||||
_ => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reload_critical_alert_mute_ui_setting(conn: &Connection) -> error::Result<()> {
|
||||
if let Ok(Some(serde_json::Value::Bool(t))) =
|
||||
load_value_from_global_settings_with_conn(conn, CRITICAL_ALERT_MUTE_UI_SETTING, true).await
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub const CUSTOM_TAGS_SETTING: &str = "custom_tags";
|
||||
pub const DEFAULT_TAGS_PER_WORKSPACE_SETTING: &str = "default_tags_per_workspace";
|
||||
pub const DEFAULT_TAGS_WORKSPACES_SETTING: &str = "default_tags_workspaces";
|
||||
pub const PREVIEW_TAGS_OVERRIDE_SETTING: &str = "preview_tags_override";
|
||||
pub const BASE_URL_SETTING: &str = "base_url";
|
||||
pub const WS_BASE_URL_SETTING: &str = "ws_base_url";
|
||||
pub const OAUTH_SETTING: &str = "oauths";
|
||||
|
||||
@@ -243,6 +243,8 @@ pub struct GlobalSettings {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub default_tags_per_workspace: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub preview_tags_override: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub disable_hub: Option<bool>,
|
||||
|
||||
// String settings
|
||||
|
||||
@@ -205,6 +205,7 @@ lazy_static::lazy_static! {
|
||||
|
||||
pub static ref DEFAULT_TAGS_PER_WORKSPACE: AtomicBool = AtomicBool::new(false);
|
||||
pub static ref DEFAULT_TAGS_WORKSPACES: Arc<RwLock<Option<Vec<String>>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref PREVIEW_TAGS_OVERRIDE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub static ref MAX_TIMEOUT: u64 = std::env::var("TIMEOUT")
|
||||
.ok()
|
||||
|
||||
@@ -77,8 +77,8 @@ use windmill_common::{
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
|
||||
utils::{not_found_if_none, report_critical_error, StripPath, WarnAfterExt},
|
||||
worker::{
|
||||
to_raw_value, CLOUD_HOSTED, DISABLE_FLOW_SCRIPT, NO_LOGS, WORKER_PULL_QUERIES,
|
||||
WORKER_SUSPENDED_PULL_QUERY,
|
||||
to_raw_value, CLOUD_HOSTED, DISABLE_FLOW_SCRIPT, NO_LOGS, PREVIEW_TAGS_OVERRIDE,
|
||||
WORKER_PULL_QUERIES, WORKER_SUSPENDED_PULL_QUERY,
|
||||
},
|
||||
DB, METRICS_ENABLED,
|
||||
};
|
||||
@@ -3302,7 +3302,10 @@ pub async fn pull(
|
||||
};
|
||||
|
||||
if let Some(job) = job.as_ref() {
|
||||
if job.is_flow() || job.is_dependency() {
|
||||
if (job.is_flow() || job.is_dependency())
|
||||
&& !(job.kind.is_preview()
|
||||
&& PREVIEW_TAGS_OVERRIDE.load(std::sync::atomic::Ordering::Relaxed))
|
||||
{
|
||||
let per_workspace = per_workspace_tag(&job.workspace_id).await;
|
||||
let base_tag = if job.is_flow() {
|
||||
"flow".to_string()
|
||||
@@ -5493,25 +5496,35 @@ async fn push_inner<'c, 'd>(
|
||||
};
|
||||
|
||||
interpolated_tag.unwrap_or_else(|| {
|
||||
language
|
||||
.as_ref()
|
||||
.map(|x| {
|
||||
let tag_lang = if x == &ScriptLang::Bunnative {
|
||||
if job_kind == JobKind::Dependencies {
|
||||
ScriptLang::Bun.as_str()
|
||||
if job_kind.is_preview()
|
||||
&& PREVIEW_TAGS_OVERRIDE.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
if per_workspace {
|
||||
format!("preview-{}", workspace_id)
|
||||
} else {
|
||||
"preview".to_string()
|
||||
}
|
||||
} else {
|
||||
language
|
||||
.as_ref()
|
||||
.map(|x| {
|
||||
let tag_lang = if x == &ScriptLang::Bunnative {
|
||||
if job_kind == JobKind::Dependencies {
|
||||
ScriptLang::Bun.as_str()
|
||||
} else {
|
||||
ScriptLang::Nativets.as_str()
|
||||
}
|
||||
} else {
|
||||
ScriptLang::Nativets.as_str()
|
||||
x.as_str()
|
||||
};
|
||||
if per_workspace {
|
||||
format!("{}-{}", tag_lang, workspace_id)
|
||||
} else {
|
||||
tag_lang.to_string()
|
||||
}
|
||||
} else {
|
||||
x.as_str()
|
||||
};
|
||||
if per_workspace {
|
||||
format!("{}-{}", tag_lang, workspace_id)
|
||||
} else {
|
||||
tag_lang.to_string()
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(default)
|
||||
})
|
||||
.unwrap_or_else(default)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -118,6 +118,10 @@ impl JobKind {
|
||||
JobKind::FlowDependencies | JobKind::AppDependencies | JobKind::Dependencies
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_preview(&self) -> bool {
|
||||
matches!(self, JobKind::Preview | JobKind::FlowPreview)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow, Debug, Serialize, Clone)]
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
import { SettingService, WorkerService, WorkspaceService } from '$lib/gen'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { enterpriseLicense, superadmin } from '$lib/stores'
|
||||
import { DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING } from '$lib/consts'
|
||||
import {
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING,
|
||||
DEFAULT_TAGS_WORKSPACES_SETTING,
|
||||
PREVIEW_TAGS_OVERRIDE_SETTING
|
||||
} from '$lib/consts'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import MultiSelect from './select/MultiSelect.svelte'
|
||||
import { safeSelectItems } from './select/utils.svelte'
|
||||
@@ -22,16 +26,19 @@
|
||||
|
||||
let defaultTags = $state<string[] | undefined>(undefined)
|
||||
let limitToWorkspaces = $state(false)
|
||||
let previewTagsOverride = $state(false)
|
||||
|
||||
// Change detection
|
||||
let originalDefaultTagPerWorkspace = $state<boolean | undefined>(defaultTagPerWorkspace)
|
||||
let originalDefaultTagWorkspaces = $state<string[]>(defaultTagWorkspaces)
|
||||
let originalPreviewTagsOverride = $state(false)
|
||||
|
||||
// Detect changes
|
||||
let hasChanges = $derived(
|
||||
originalDefaultTagPerWorkspace !== defaultTagPerWorkspace ||
|
||||
JSON.stringify($state.snapshot(originalDefaultTagWorkspaces)?.sort() || []) !==
|
||||
JSON.stringify($state.snapshot(defaultTagWorkspaces)?.sort() || [])
|
||||
JSON.stringify($state.snapshot(defaultTagWorkspaces)?.sort() || []) ||
|
||||
originalPreviewTagsOverride !== previewTagsOverride
|
||||
)
|
||||
|
||||
let workspaces: string[] = $state([])
|
||||
@@ -47,6 +54,11 @@
|
||||
key: DEFAULT_TAGS_WORKSPACES_SETTING
|
||||
})) as any) ?? []
|
||||
limitToWorkspaces = defaultTagWorkspaces ? defaultTagWorkspaces.length > 0 : false
|
||||
previewTagsOverride =
|
||||
((await SettingService.getGlobal({
|
||||
key: PREVIEW_TAGS_OVERRIDE_SETTING
|
||||
})) as any) ?? false
|
||||
originalPreviewTagsOverride = previewTagsOverride
|
||||
} catch (err) {
|
||||
sendUserToast(`Could not load default tags: ${err}`, true)
|
||||
}
|
||||
@@ -68,10 +80,17 @@
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
await SettingService.setGlobal({
|
||||
key: PREVIEW_TAGS_OVERRIDE_SETTING,
|
||||
requestBody: {
|
||||
value: previewTagsOverride
|
||||
}
|
||||
})
|
||||
|
||||
// Update original state after save
|
||||
originalDefaultTagPerWorkspace = defaultTagPerWorkspace
|
||||
originalDefaultTagWorkspaces = [...(defaultTagWorkspaces || [])]
|
||||
originalPreviewTagsOverride = previewTagsOverride
|
||||
|
||||
loadDefaultTags()
|
||||
sendUserToast('Saved')
|
||||
@@ -146,6 +165,18 @@
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
<div class="flex flex-col gap-1">
|
||||
<Toggle
|
||||
bind:checked={previewTagsOverride}
|
||||
options={{
|
||||
right: 'route preview jobs to dedicated preview tag',
|
||||
rightTooltip:
|
||||
'When enabled, preview jobs (script previews and flow previews) will be routed to the "preview" tag instead of their language-specific tag, allowing you to dedicate specific workers for previews.'
|
||||
}}
|
||||
class="w-fit"
|
||||
disabled={!$enterpriseLicense}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 items-center mb-1">
|
||||
@@ -168,6 +199,17 @@
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{#if previewTagsOverride}
|
||||
<div class="flex gap-2 items-center">
|
||||
<div class="w-36">
|
||||
<Badge color="transparent">preview</Badge>
|
||||
</div>
|
||||
<div class="w-6 flex justify-center text-secondary">→</div>
|
||||
<div class="flex-1">
|
||||
<Badge color="blue">{defaultTagPerWorkspace ? 'preview-$workspace' : 'preview'}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</Section>
|
||||
|
||||
@@ -41,6 +41,7 @@ export const WORKER_S3_BUCKET_SYNC_SETTING = 'worker_s3_bucket_sync'
|
||||
export const CUSTOM_TAGS_SETTING = 'custom_tags'
|
||||
export const DEFAULT_TAGS_PER_WORKSPACE_SETTING = 'default_tags_per_workspace'
|
||||
export const DEFAULT_TAGS_WORKSPACES_SETTING = 'default_tags_workspaces'
|
||||
export const PREVIEW_TAGS_OVERRIDE_SETTING = 'preview_tags_override'
|
||||
|
||||
export const WORKSPACE_SLACK_BOT_TOKEN_PATH = 'f/slack_bot/bot_token'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user