mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 16:02:11 +00:00
fix: show related job when deployment is in progress (#7294)
* feat: show related job when deployment is in progress - Added job_id column to deployment_metadata table to track current deployment jobs - Updated backend to store job_id when creating dependency jobs for scripts and flows - Modified deployment status API endpoints to include job_id in responses - Updated frontend to display clickable job link in "Deployment in progress" badge - Added OpenAPI schema updates for new job_id field Resolves #7293 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> * update --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, job_id)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL\n DO UPDATE SET job_id = EXCLUDED.job_id",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0e621bba5913482b8235d7d8442b8f0e9012c265e150afd4aa41972bf7334ba2"
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT f.lock_error_logs, dm.job_id\n FROM flow f\n LEFT JOIN deployment_metadata dm ON f.versions[array_upper(f.versions, 1)] = dm.flow_version\n AND f.workspace_id = dm.workspace_id AND f.path = dm.path\n WHERE f.path = $1 AND f.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "lock_error_logs",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "job_id",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "1de29cdd474cbd61e15b63d111e1c42aefee683e14cc738a809ecca17370e6ee"
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT s.lock, s.lock_error_logs, dm.job_id\n FROM script s\n LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash AND s.workspace_id = dm.workspace_id\n WHERE s.hash = $1 AND s.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "lock",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "lock_error_logs",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "job_id",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "513ed713afdbafb587026d1536c47a9bbaa6967e36777454746b8817d68219a5"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, job_id)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL\n DO UPDATE SET job_id = EXCLUDED.job_id",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7abde47077c38ccf005ce7180a383f97076b2cbe2f617f7af39550a0db157b2b"
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT lock_error_logs FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "lock_error_logs",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3"
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT lock, lock_error_logs FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "lock",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "lock_error_logs",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e3ac59fcf6193007a21c808a275d3d84fd76e44f9240f4292c5c1208096b8563"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Remove job_id column from deployment_metadata table
|
||||
ALTER TABLE deployment_metadata DROP COLUMN IF EXISTS job_id;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add job_id column to deployment_metadata table to track the current deployment job
|
||||
ALTER TABLE deployment_metadata ADD COLUMN IF NOT EXISTS job_id UUID;
|
||||
@@ -5950,6 +5950,9 @@ paths:
|
||||
type: string
|
||||
lock_error_logs:
|
||||
type: string
|
||||
job_id:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
/w/{workspace}/jobs/list_selected_job_groups:
|
||||
# We use post because sending a huge array as a query param can produce
|
||||
@@ -6836,6 +6839,9 @@ paths:
|
||||
properties:
|
||||
lock_error_logs:
|
||||
type: string
|
||||
job_id:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
/w/{workspace}/flows/get_triggers_count/{path}:
|
||||
get:
|
||||
|
||||
@@ -577,6 +577,20 @@ async fn create_flow(
|
||||
.execute(&mut *new_tx)
|
||||
.await?;
|
||||
|
||||
// Store the job_id in deployment_metadata for this flow deployment
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, flow_version, job_id)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL
|
||||
DO UPDATE SET job_id = EXCLUDED.job_id",
|
||||
w_id,
|
||||
nf.path,
|
||||
version,
|
||||
dependency_job_uuid
|
||||
)
|
||||
.execute(&mut *new_tx)
|
||||
.await?;
|
||||
|
||||
new_tx.commit().await?;
|
||||
webhook.send_message(
|
||||
w_id.clone(),
|
||||
@@ -1115,6 +1129,25 @@ async fn update_flow(
|
||||
))
|
||||
})?;
|
||||
|
||||
// Store the job_id in deployment_metadata for this flow deployment
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, flow_version, job_id)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL
|
||||
DO UPDATE SET job_id = EXCLUDED.job_id",
|
||||
w_id,
|
||||
nf.path,
|
||||
version,
|
||||
dependency_job_uuid
|
||||
)
|
||||
.execute(&mut *new_tx)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error::Error::internal_err(format!(
|
||||
"Error updating deployment_metadata with job_id: {e:#}"
|
||||
))
|
||||
})?;
|
||||
|
||||
if let Some(old_dep_job) = old_dep_job {
|
||||
sqlx::query!(
|
||||
"UPDATE v2_job_queue SET
|
||||
@@ -1154,9 +1187,10 @@ async fn list_tokens(
|
||||
list_tokens_internal(&db, &w_id, &path, true).await
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize)]
|
||||
#[derive(Serialize)]
|
||||
struct DeploymentStatus {
|
||||
lock_error_logs: Option<String>,
|
||||
job_id: Option<sqlx::types::Uuid>,
|
||||
}
|
||||
async fn get_deployment_status(
|
||||
Extension(db): Extension<DB>,
|
||||
@@ -1164,9 +1198,12 @@ async fn get_deployment_status(
|
||||
) -> JsonResult<DeploymentStatus> {
|
||||
let path = path.to_path();
|
||||
let mut tx = db.begin().await?;
|
||||
let status_o: Option<DeploymentStatus> = sqlx::query_as!(
|
||||
DeploymentStatus,
|
||||
"SELECT lock_error_logs FROM flow WHERE path = $1 AND workspace_id = $2",
|
||||
let status_o = sqlx::query!(
|
||||
"SELECT f.lock_error_logs, dm.job_id
|
||||
FROM flow f
|
||||
LEFT JOIN deployment_metadata dm ON f.versions[array_upper(f.versions, 1)] = dm.flow_version
|
||||
AND f.workspace_id = dm.workspace_id AND f.path = dm.path
|
||||
WHERE f.path = $1 AND f.workspace_id = $2",
|
||||
path,
|
||||
w_id,
|
||||
)
|
||||
@@ -1175,8 +1212,13 @@ async fn get_deployment_status(
|
||||
|
||||
let status = not_found_if_none(status_o, "DeploymentStatus", path)?;
|
||||
|
||||
let deployment_status = DeploymentStatus {
|
||||
lock_error_logs: status.lock_error_logs,
|
||||
job_id: status.job_id,
|
||||
};
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(Json(status))
|
||||
Ok(Json(deployment_status))
|
||||
}
|
||||
|
||||
async fn get_flow_by_path(
|
||||
|
||||
@@ -998,14 +998,14 @@ async fn create_script_internal<'c>(
|
||||
}
|
||||
|
||||
let tx = PushIsolationLevel::Transaction(tx);
|
||||
let (_, new_tx) = windmill_queue::push(
|
||||
let (job_id, mut new_tx) = windmill_queue::push(
|
||||
&db,
|
||||
tx,
|
||||
&w_id,
|
||||
JobPayload::Dependencies {
|
||||
hash,
|
||||
language: ns.language,
|
||||
path: ns.path,
|
||||
path: ns.path.clone(),
|
||||
dedicated_worker: ns.dedicated_worker,
|
||||
},
|
||||
windmill_queue::PushArgs::from(&args),
|
||||
@@ -1034,6 +1034,21 @@ async fn create_script_internal<'c>(
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Store the job_id in deployment_metadata for this script deployment
|
||||
sqlx::query!(
|
||||
"INSERT INTO deployment_metadata (workspace_id, path, script_hash, job_id)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL
|
||||
DO UPDATE SET job_id = EXCLUDED.job_id",
|
||||
w_id,
|
||||
ns.path,
|
||||
hash.0,
|
||||
job_id
|
||||
)
|
||||
.execute(&mut *new_tx)
|
||||
.await?;
|
||||
|
||||
Ok((hash, new_tx, None))
|
||||
} else {
|
||||
if codebase.is_none() {
|
||||
@@ -1750,19 +1765,22 @@ async fn raw_script_by_hash(
|
||||
Ok(r.script.content)
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize)]
|
||||
#[derive(Serialize)]
|
||||
struct DeploymentStatus {
|
||||
lock: Option<String>,
|
||||
lock_error_logs: Option<String>,
|
||||
job_id: Option<sqlx::types::Uuid>,
|
||||
}
|
||||
async fn get_deployment_status(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, hash)): Path<(String, ScriptHash)>,
|
||||
) -> JsonResult<DeploymentStatus> {
|
||||
let mut tx = db.begin().await?;
|
||||
let status_o: Option<DeploymentStatus> = sqlx::query_as!(
|
||||
DeploymentStatus,
|
||||
"SELECT lock, lock_error_logs FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
let status_o = sqlx::query!(
|
||||
"SELECT s.lock, s.lock_error_logs, dm.job_id
|
||||
FROM script s
|
||||
LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash AND s.workspace_id = dm.workspace_id
|
||||
WHERE s.hash = $1 AND s.workspace_id = $2",
|
||||
hash.0,
|
||||
w_id,
|
||||
)
|
||||
@@ -1771,8 +1789,14 @@ async fn get_deployment_status(
|
||||
|
||||
let status = not_found_if_none(status_o, "DeploymentStatus", hash.to_string())?;
|
||||
|
||||
let deployment_status = DeploymentStatus {
|
||||
lock: status.lock,
|
||||
lock_error_logs: status.lock_error_logs,
|
||||
job_id: status.job_id,
|
||||
};
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(Json(status))
|
||||
Ok(Json(deployment_status))
|
||||
}
|
||||
|
||||
pub async fn require_is_writer(authed: &ApiAuthed, path: &str, w_id: &str, db: DB) -> Result<()> {
|
||||
|
||||
@@ -3040,7 +3040,7 @@ pub async fn concurrency_key(
|
||||
.await
|
||||
.map(|x| {
|
||||
if x.is_none() {
|
||||
tracing::info!("No concurrency key found for job {id}, defaulting to empty string");
|
||||
tracing::info!("No concurrency key found for job {id}");
|
||||
}
|
||||
return x;
|
||||
})
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
let inputSelected: 'saved' | 'history' | undefined = $state(undefined)
|
||||
let jsonView = $state(false)
|
||||
let deploymentInProgress = $state(false)
|
||||
let deploymentJobId: string | undefined = $state(undefined)
|
||||
|
||||
let intervalId: number | undefined = undefined
|
||||
|
||||
@@ -166,8 +167,11 @@
|
||||
})
|
||||
if (status.lock_error_logs == undefined || status.lock_error_logs != '') {
|
||||
deploymentInProgress = false
|
||||
deploymentJobId = undefined
|
||||
flow.lock_error_logs = status.lock_error_logs
|
||||
clearInterval(intervalId)
|
||||
} else if (status.job_id) {
|
||||
deploymentJobId = status.job_id
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -541,6 +545,13 @@
|
||||
<HeaderBadge color="yellow">
|
||||
<Loader2 size={12} class="inline animate-spin mr-1" />
|
||||
Deployment in progress
|
||||
{#if deploymentJobId}
|
||||
<a
|
||||
href="/run/{deploymentJobId}?workspace={$workspaceStore}"
|
||||
class="underline"
|
||||
target="_blank">view job</a
|
||||
>
|
||||
{/if}
|
||||
</HeaderBadge>
|
||||
{/if}
|
||||
{#if flow.lock_error_logs && flow.lock_error_logs != ''}
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
let topHash: string | undefined = $state()
|
||||
let can_write = $state(false)
|
||||
let deploymentInProgress = $state(false)
|
||||
let deploymentJobId: string | undefined = $state(undefined)
|
||||
let intervalId: number
|
||||
let shareModal: ShareModal | undefined = $state()
|
||||
let runForm: RunForm | undefined = $state()
|
||||
@@ -156,9 +157,12 @@
|
||||
})
|
||||
if (status.lock != undefined || status.lock_error_logs != undefined) {
|
||||
deploymentInProgress = false
|
||||
deploymentJobId = undefined
|
||||
script.lock = status.lock
|
||||
script.lock_error_logs = status.lock_error_logs
|
||||
clearInterval(intervalId)
|
||||
} else if (status.job_id) {
|
||||
deploymentJobId = status.job_id
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -689,6 +693,13 @@
|
||||
<Badge color="yellow">
|
||||
<Loader2 size={12} class="inline animate-spin mr-1" />
|
||||
Deployment in progress
|
||||
{#if deploymentJobId}
|
||||
<a
|
||||
href="/run/{deploymentJobId}?workspace={$workspaceStore}"
|
||||
class="underline"
|
||||
target="_blank">view job</a
|
||||
>
|
||||
{/if}
|
||||
</Badge>
|
||||
{/if}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user