feat: git sync improvements (#6182)

* init checkpoint

* ui second pass...

* round 1 backend + saving settings + detecting changes...

* checkpoint

* fix openapi

* saving + correct wmill.yaml diff

* cli refactor

* cli and tests refactor done

* cli multi workspace support

* cli support skip core types to align with ui

* new test framework

* sqlx

* openapi spec

* frontend

* sync + settings changes

* some fixes

* some fixes

* security: Remove hardcoded EE license key, use environment variable only

- Remove hardcoded license key from containerized test backend
- Environment variable EE_LICENSE_KEY now required for EE features
- License key no longer stored in database during tests

* sqlx

* tests

* fixing tests

* fix tests

* checkpoint

* checkpoint

* cli build

* frontend - cli exchange

* settings match

* ee repo ref

* npm check

* openapi

* tests

* checkpoint

* cli + tests

* reset to preview on changes

* merge issue ee

* cleanup

* hubscript

* simplifications

* ee repo ref

* cli fixes

* fix sync and add tests

* extra test

* git sync settings / key change aware

* ee-repo ref

* ee-repo ref

* ee repo ref

* ee ref

* review 1

* ee ref

* Update frontend/src/lib/components/PullGitRepoPopover.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Update frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* ee ref

* remove extra includes from ui

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
This commit is contained in:
Alexander Petric
2025-07-15 16:25:31 +00:00
committed by GitHub
co-authored by ellipsis-dev[bot]
parent 574f14b349
commit 27bf4e34d8
41 changed files with 7892 additions and 924 deletions
@@ -18,8 +18,8 @@
"Left": []
},
"nullable": [
false,
true
true,
false
]
},
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"
@@ -41,11 +41,11 @@
]
},
"nullable": [
false,
false,
false,
false,
false,
true,
true,
true,
true,
true,
true
]
},
+1 -1
View File
@@ -1 +1 @@
22aa4778f97d034e5faea8b8d8c971c89ce74137
0e1db1beeac098e8f2ddd2a0abfe9f83751b2092
+41 -26
View File
@@ -581,7 +581,7 @@ paths:
summary: get connected repositories
operationId: getGlobalConnectedRepositories
tags:
- git_sync
- Git Sync
responses:
"200":
description: connected repositories
@@ -12304,8 +12304,7 @@ paths:
"200":
description: Parquet Preview
content:
application/json:
schema: {}
application/json: {}
/w/{workspace}/job_helpers/load_table_count/{path}:
get:
@@ -12384,8 +12383,7 @@ paths:
"200":
description: Csv Preview
content:
application/json:
schema: {}
application/json: {}
/w/{workspace}/job_helpers/delete_s3_file:
delete:
@@ -17006,27 +17004,6 @@ components:
WorkspaceGitSyncSettings:
type: object
properties:
include_path:
type: array
items:
type: string
include_type:
type: array
items:
type: string
enum:
- script
- flow
- app
- folder
- resource
- variable
- secret
- resourcetype
- schedule
- user
- group
- trigger
repositories:
type: array
items:
@@ -17078,6 +17055,42 @@ components:
type: boolean
group_by_folder:
type: boolean
collapsed:
type: boolean
settings:
type: object
properties:
include_path:
type: array
items:
type: string
include_type:
type: array
items:
type: string
enum:
- script
- flow
- app
- folder
- resource
- variable
- secret
- resourcetype
- schedule
- user
- group
- trigger
- settings
- key
exclude_path:
type: array
items:
type: string
extra_include_path:
type: array
items:
type: string
exclude_types_override:
type: array
items:
@@ -17095,6 +17108,8 @@ components:
- user
- group
- trigger
- settings
- key
required:
- script_path
- git_repo_resource_path
+161 -3
View File
@@ -47,7 +47,7 @@ use windmill_common::{
oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH,
utils::{paginate, rd_string, require_admin, Pagination},
};
use windmill_git_sync::handle_deployment_metadata;
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
#[cfg(feature = "enterprise")]
use windmill_common::utils::require_admin_or_devops;
@@ -636,6 +636,17 @@ async fn edit_deploy_to(
.await?;
tx.commit().await?;
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
DeployedObject::Settings { setting_type: "deploy_to".to_string() },
None,
false,
)
.await?;
Ok(format!("Edit deploy to for {}", &w_id))
}
@@ -701,6 +712,17 @@ async fn edit_webhook(
.await?;
tx.commit().await?;
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
DeployedObject::Settings { setting_type: "webhook".to_string() },
None,
false,
)
.await?;
Ok(format!("Edit webhook for workspace {}", &w_id))
}
@@ -741,6 +763,18 @@ async fn edit_copilot_config(
.await?;
tx.commit().await?;
// Trigger git sync for AI config changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Settings { setting_type: "ai_config".to_string() },
Some("AI configuration updated".to_string()),
false,
)
.await?;
Ok(format!("Edit copilot config for workspace {}", &w_id))
}
@@ -818,6 +852,20 @@ async fn edit_large_file_storage_config(
}
tx.commit().await?;
// Trigger git sync for large file storage changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Settings {
setting_type: "large_file_storage".to_string(),
},
Some("Large file storage configuration updated".to_string()),
false,
)
.await?;
Ok(format!(
"Edit large file storage config for workspace {}",
&w_id
@@ -887,6 +935,18 @@ async fn edit_git_sync_config(
}
tx.commit().await?;
// Trigger git sync for git sync settings changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Settings { setting_type: "git_sync".to_string() },
Some("Git sync configuration updated".to_string()),
false,
)
.await?;
Ok(format!("Edit git sync config for workspace {}", &w_id))
}
@@ -1014,6 +1074,18 @@ async fn edit_default_scripts(
}
tx.commit().await?;
// Trigger git sync for default scripts changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Settings { setting_type: "default_scripts".to_string() },
Some("Default scripts configuration updated".to_string()),
false,
)
.await?;
Ok(format!("Edit default scripts for workspace {}", &w_id))
}
@@ -1084,6 +1156,18 @@ async fn edit_default_app(
}
tx.commit().await?;
// Trigger git sync for default app changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Settings { setting_type: "default_app".to_string() },
Some("Default app configuration updated".to_string()),
false,
)
.await?;
Ok(format!("Edit default app for workspace {}", &w_id))
}
@@ -1160,6 +1244,18 @@ async fn edit_error_handler(
.await?;
tx.commit().await?;
// Trigger git sync for error handler changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Settings { setting_type: "error_handler".to_string() },
Some("Error handler configuration updated".to_string()),
false,
)
.await?;
Ok(format!("Edit error_handler for workspace {}", &w_id))
}
@@ -1201,6 +1297,7 @@ async fn set_environment_variable(
)
.await?;
tx.commit().await?;
Ok(format!("Set environment variable {}", name))
}
None => {
@@ -1223,6 +1320,7 @@ async fn set_environment_variable(
)
.await?;
tx.commit().await?;
Ok(format!("Deleted environment variable {}", name))
}
}
@@ -1316,6 +1414,18 @@ async fn set_encryption_key(
}
}
// Trigger git sync for encryption key changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Key { key_type: "encryption_key".to_string() },
Some("Encryption key updated".to_string()),
false,
)
.await?;
return Ok(());
}
@@ -2053,6 +2163,18 @@ async fn change_workspace_name(
tx.commit().await?;
// Trigger git sync for workspace name changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Settings { setting_type: "workspace_name".to_string() },
Some(format!("Workspace name updated to {}", &rw.new_name)),
false,
)
.await?;
Ok(format!("updated workspace name to {}", &rw.new_name))
}
@@ -2076,6 +2198,17 @@ async fn change_workspace_color(
tx.commit().await?;
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
DeployedObject::Settings { setting_type: "workspace_color".to_string() },
None,
false,
)
.await?;
Ok(format!(
"updated workspace color to {}",
rw.color.as_deref().unwrap_or("no color")
@@ -2154,10 +2287,10 @@ pub struct MuteCriticalAlertRequest {
async fn mute_critical_alerts(
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
ApiAuthed { is_admin, username, .. }: ApiAuthed,
authed: ApiAuthed,
Json(m_r): Json<MuteCriticalAlertRequest>,
) -> Result<String> {
require_admin(is_admin, &username)?;
require_admin(authed.is_admin, &authed.username)?;
let mute_alerts = m_r.mute_critical_alerts.unwrap_or(false);
@@ -2178,6 +2311,17 @@ async fn mute_critical_alerts(
.execute(&db)
.await?;
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
DeployedObject::Settings { setting_type: "critical_alerts".to_string() },
None,
false,
)
.await?;
Ok(format!(
"Updated mute criticital alert ui settings for workspace: {}",
&w_id
@@ -2225,5 +2369,19 @@ async fn update_operator_settings(
tx.commit().await?;
// Trigger git sync for operator settings changes
handle_deployment_metadata(
&authed.email,
&authed.username,
&db,
&w_id,
windmill_git_sync::DeployedObject::Settings {
setting_type: "operator_settings".to_string(),
},
Some("Operator settings updated".to_string()),
false,
)
.await?;
Ok("Operator settings updated successfully".to_string())
}
+36 -4
View File
@@ -3,9 +3,15 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct WorkspaceGitSyncSettings {
pub include_path: Vec<String>,
pub include_type: Vec<ObjectType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_path: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_type: Option<Vec<ObjectType>>,
pub repositories: Vec<GitRepositorySettings>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exclude_path: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra_include_path: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
@@ -14,7 +20,7 @@ pub struct WorkspaceDeploymentUISettings {
pub include_type: Vec<ObjectType>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))]
pub enum ObjectType {
Script,
@@ -29,15 +35,41 @@ pub enum ObjectType {
User,
Group,
Trigger,
Settings,
Key,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GitRepositorySettings {
#[serde(skip_serializing_if = "Option::is_none")]
pub exclude_types_override: Option<Vec<ObjectType>>,
pub script_path: String,
pub git_repo_resource_path: String,
pub use_individual_branch: Option<bool>,
pub group_by_folder: Option<bool>,
pub exclude_types_override: Option<Vec<ObjectType>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub settings: Option<GitSyncSettings>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GitSyncSettings {
pub include_path: Vec<String>,
pub include_type: Vec<ObjectType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exclude_path: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra_include_path: Option<Vec<String>>,
}
impl Default for GitSyncSettings {
fn default() -> Self {
Self {
include_path: Vec::new(),
include_type: Vec::new(),
exclude_path: None,
extra_include_path: None,
}
}
}
lazy_static::lazy_static! {
+7 -1
View File
@@ -37,6 +37,8 @@ pub enum DeployedObject {
MqttTrigger { path: String },
SqsTrigger { path: String },
GcpTrigger { path: String },
Settings { setting_type: String },
Key { key_type: String },
}
impl DeployedObject {
@@ -60,12 +62,14 @@ impl DeployedObject {
DeployedObject::MqttTrigger { path } => path.to_owned(),
DeployedObject::SqsTrigger { path } => path.to_owned(),
DeployedObject::GcpTrigger { path } => path.to_owned(),
DeployedObject::Settings { .. } => "settings.yaml".to_string(),
DeployedObject::Key { .. } => "encryption_key.yaml".to_string(),
}
}
pub fn get_ignore_regex_filter(&self) -> bool {
match self {
Self::User { .. } | Self::Group { .. } | Self::ResourceType { .. } => true,
Self::User { .. } | Self::Group { .. } | Self::ResourceType { .. } | Self::Settings { .. } | Self::Key { .. } => true,
_ => false,
}
}
@@ -90,6 +94,8 @@ impl DeployedObject {
DeployedObject::MqttTrigger { .. } => None,
DeployedObject::SqsTrigger { .. } => None,
DeployedObject::GcpTrigger { .. } => None,
DeployedObject::Settings { .. } => None,
DeployedObject::Key { .. } => None,
}
}
}