mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
fix: create git branch right before creating the workspace fork to catch errors and have a coherent fork point (#7073)
* Workspace forks: add endpoint to create a branch before creating a fork * Update hubPaths + create branch before creating fork on frontend * Update tmp ee-repo-ref * Remove debug hubPath * Prepare sqlx * Fix ee imports * Update ee-ref * Update ee-repo-ref final * Prepare sqlx
This commit is contained in:
+5
-5
@@ -46,11 +46,11 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -14,5 +14,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e8e33f599eae064011232f9f715e676d0c8ae31982865cb2c8103ed735c42c69"
|
||||
"hash": "6f49a47b0b77720b0e8a2ddbd46e45e414dd646c912387ee63609d9f5611690f"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5) \n ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5) \n ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -14,5 +14,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2e131a019051bdac7c9c65f7c504cfba31cfdd64a0f68001e123694ed5cde5ed"
|
||||
"hash": "8e750d4b3af9b5844c11b1b92741f2ee9d2d3412d4a2c96c6ccc87ec1c382384"
|
||||
}
|
||||
+2
-2
@@ -18,8 +18,8 @@
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n (elem->>'installation_id')::bigint as installation_id\n FROM workspace_settings,\n LATERAL jsonb_array_elements(git_app_installations) AS elem\n WHERE workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "installation_id",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "be00ac55e8668a0ed3befda7d8595c7cda0cba0b119d4fdb8a0dea1b28a1d560"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
|
||||
"query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -14,5 +14,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e572fa64eec9188368d7c271ac7ecd6b45dc161423abab22cddb2b13f6fb9833"
|
||||
"hash": "f844026597d2e1c59a80dc8e1ddd069c0ad82b27b41171825928a938cfa8316f"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
3674871005d1dc3b92fd5acfd76009a95a43288a
|
||||
ab6116235f7869cef306996059f8663f7e831b8a
|
||||
|
||||
@@ -728,6 +728,32 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/workspaces/create_workspace_fork_branch:
|
||||
post:
|
||||
summary: create forked workspace branch with git sync
|
||||
operationId: createWorkspaceForkGitBranch
|
||||
tags:
|
||||
- workspace
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: new forked workspace
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateWorkspaceFork"
|
||||
responses:
|
||||
"201":
|
||||
description: forked workspace branch created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
/w/{workspace}/workspaces/create_fork:
|
||||
post:
|
||||
summary: create forked workspace
|
||||
|
||||
@@ -50,7 +50,7 @@ use windmill_common::{
|
||||
oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH,
|
||||
utils::{paginate, rd_string, require_admin, Pagination},
|
||||
};
|
||||
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
|
||||
use windmill_git_sync::{handle_fork_branch_creation, handle_deployment_metadata, DeployedObject};
|
||||
use windmill_worker::scoped_dependency_map::{DependencyMap, ScopedDependencyMap};
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
@@ -163,6 +163,7 @@ pub fn workspaced_service() -> Router {
|
||||
post(acknowledge_all_critical_alerts),
|
||||
)
|
||||
.route("/critical_alerts/mute", post(mute_critical_alerts))
|
||||
.route("/create_workspace_fork_branch", post(create_workspace_fork_branch))
|
||||
.route("/operator_settings", post(update_operator_settings));
|
||||
|
||||
#[cfg(all(feature = "stripe", feature = "enterprise"))]
|
||||
@@ -3026,19 +3027,33 @@ async fn deprecated_create_workspace_fork(_authed: ApiAuthed) -> Result<String>
|
||||
return Err(Error::BadRequest("This API endpoint has been relocated. Your Windmill CLI version is outdated and needs to be updated.".to_string()));
|
||||
}
|
||||
|
||||
/// Return the uuids of the git sync jobs to create the branch before creating the fork
|
||||
async fn create_workspace_fork_branch(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(nw): Json<CreateWorkspaceFork>,
|
||||
) -> JsonResult<Vec<Uuid>> {
|
||||
if *CLOUD_HOSTED {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"Forking workspaces is not available on app.windmill.dev"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(Json(
|
||||
handle_fork_branch_creation(&authed.email, &authed.username, &db, &w_id, &nw.id).await?,
|
||||
))
|
||||
}
|
||||
|
||||
async fn create_workspace_fork(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(parent_workspace_id): Path<String>,
|
||||
Json(nw): Json<CreateWorkspaceFork>,
|
||||
) -> Result<String> {
|
||||
// if *CREATE_WORKSPACE_REQUIRE_SUPERADMIN {
|
||||
// require_super_admin(&db, &authed.email).await?;
|
||||
// }
|
||||
|
||||
if *CLOUD_HOSTED {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"Forking workspaces is not available on Cloud"
|
||||
"Forking workspaces is not available on app.windmill.dev"
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,3 +21,14 @@ pub async fn handle_deployment_metadata<'c>(
|
||||
// Git sync is an enterprise feature and not part of the open-source version
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
pub async fn handle_fork_branch_creation<'c>(
|
||||
_email: &str,
|
||||
_created_by: &str,
|
||||
_db: &DB,
|
||||
_w_id: &str,
|
||||
_fork_workspace_id: &str,
|
||||
) -> Result<Vec<uuid::Uuid>> {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use windmill_common::{scripts::ScriptHash, DB};
|
||||
pub mod git_sync_ee;
|
||||
pub mod git_sync_oss;
|
||||
|
||||
pub use git_sync_oss::handle_deployment_metadata;
|
||||
pub use git_sync_oss::{handle_deployment_metadata, handle_fork_branch_creation};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DeployedObject {
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
import { AI_PROVIDERS } from '$lib/components/copilot/lib'
|
||||
import { GitFork } from 'lucide-svelte'
|
||||
import { GitFork, LoaderCircle } from 'lucide-svelte'
|
||||
import PrefixedInput from '../PrefixedInput.svelte'
|
||||
import TextInput from '../text_input/TextInput.svelte'
|
||||
import { jobManager } from '$lib/services/JobManager'
|
||||
import Alert from '../common/alert/Alert.svelte'
|
||||
|
||||
interface Props {
|
||||
isFork?: boolean
|
||||
@@ -76,19 +78,78 @@
|
||||
|
||||
const WM_FORK_PREFIX = 'wm-fork-'
|
||||
|
||||
let forkCreationLoading = $state(false)
|
||||
let forkCreationError = $state("")
|
||||
let errorMsgs: string[] = $state([])
|
||||
|
||||
async function createOrForkWorkspace() {
|
||||
const prefixed_id = `${WM_FORK_PREFIX}${id}`
|
||||
if (isFork) {
|
||||
if ($workspaceStore) {
|
||||
await WorkspaceService.createWorkspaceFork({
|
||||
forkCreationLoading = true
|
||||
errorMsgs = []
|
||||
forkCreationError = ""
|
||||
|
||||
let gitSyncJobIds = await WorkspaceService.createWorkspaceForkGitBranch({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
id: prefixed_id,
|
||||
name,
|
||||
color: colorEnabled && workspaceColor ? workspaceColor : undefined,
|
||||
color: colorEnabled && workspaceColor ? workspaceColor : undefined
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
await Promise.all(
|
||||
gitSyncJobIds.map((jobId) =>
|
||||
jobManager.runWithProgress(() => Promise.resolve(jobId), {
|
||||
workspace: $workspaceStore,
|
||||
timeout: 60000,
|
||||
timeoutMessage: `Deploy fork job timed out after 60s`,
|
||||
onProgress: (status) => {
|
||||
if (status.status === 'failure') {
|
||||
errorMsgs.push(status.error ?? 'Deploy fork job failed')
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
} catch (error) {
|
||||
forkCreationLoading = false
|
||||
sendUserToast(
|
||||
`Could not fork workspace ${$workspaceStore} because branch creation failed: ${errorMsgs} - ${error}`,
|
||||
true
|
||||
)
|
||||
return
|
||||
}
|
||||
if (errorMsgs.length != 0) {
|
||||
forkCreationError = "Failed to create a branch for this fork on the git sync repo(s)"
|
||||
forkCreationLoading = false
|
||||
sendUserToast(
|
||||
`Could not fork workspace ${$workspaceStore} because branch creation failed: ${errorMsgs}`,
|
||||
true
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await WorkspaceService.createWorkspaceFork({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
id: prefixed_id,
|
||||
name,
|
||||
color: colorEnabled && workspaceColor ? workspaceColor : undefined
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
forkCreationError = `Failed to create fork '${prefixed_id}'`
|
||||
errorMsgs.push(e?.body ?? e ?? "Unknown error")
|
||||
forkCreationLoading = false
|
||||
sendUserToast(`Could not create fork '${prefixed_id}' ${e}`, true)
|
||||
return
|
||||
}
|
||||
|
||||
forkCreationLoading = false
|
||||
sendUserToast(`Successfully forked workspace ${$workspaceStore} as: wm-fork-${id}`)
|
||||
} else {
|
||||
sendUserToast('No workspace selected, cannot fork non-existent workspace', true)
|
||||
@@ -256,6 +317,15 @@
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#if errorMsgs.length != 0}
|
||||
<Alert class="p-2" title={forkCreationError} type="error">
|
||||
<ul class="pl-2 pr-4 break-words">
|
||||
{#each errorMsgs as errorMsg}
|
||||
<li><pre class="whitespace-pre-wrap">- {errorMsg}</pre></li>
|
||||
{/each}
|
||||
</ul>
|
||||
</Alert>
|
||||
{/if}
|
||||
<label class="flex flex-col gap-1">
|
||||
{#if isFork}
|
||||
<span class="text-xs font-semibold text-emphasis">Fork name</span>
|
||||
@@ -429,24 +499,33 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-wrap flex-row justify-between pt-12 gap-4">
|
||||
<Button variant="default" size="sm" href="{base}/user/workspaces"
|
||||
>← Back to workspaces</Button
|
||||
>
|
||||
<Button
|
||||
variant="accent"
|
||||
disabled={checking ||
|
||||
errorId != '' ||
|
||||
!name ||
|
||||
(!automateUsernameCreation && (errorUser != '' || !username)) ||
|
||||
!id}
|
||||
on:click={createOrForkWorkspace}
|
||||
disabled={forkCreationLoading}
|
||||
variant="default"
|
||||
size="sm"
|
||||
href="{base}/user/workspaces">← Back to workspaces</Button
|
||||
>
|
||||
{#if isFork}
|
||||
Fork workspace
|
||||
{:else}
|
||||
Create workspace
|
||||
{/if}
|
||||
</Button>
|
||||
{#if !forkCreationLoading}
|
||||
<Button
|
||||
variant="accent"
|
||||
disabled={checking ||
|
||||
errorId != '' ||
|
||||
!name ||
|
||||
(!automateUsernameCreation && (errorUser != '' || !username)) ||
|
||||
!id}
|
||||
on:click={createOrForkWorkspace}
|
||||
>
|
||||
{#if isFork}
|
||||
Fork workspace
|
||||
{:else}
|
||||
Create workspace
|
||||
{/if}
|
||||
</Button>
|
||||
{:else}
|
||||
<Button variant="accent" disabled={true}>
|
||||
<LoaderCircle class="animate-spin" /> Creating branch
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</CenteredModal>
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"gitSync_13": "hub/19801/sync-script-to-git-repo-windmill",
|
||||
"gitSync_14": "hub/19803/sync-script-to-git-repo-windmill",
|
||||
"gitSync_15": "hub/19816/sync-script-to-git-repo-windmill",
|
||||
"gitSync": "hub/19818/sync-script-to-git-repo-windmill",
|
||||
"gitSync_16": "hub/19818/sync-script-to-git-repo-windmill",
|
||||
"gitSync": "hub/28073/sync-script-to-git-repo-windmill",
|
||||
"gitSyncTest_0": "hub/9073/git-repo-test-read-write-windmill",
|
||||
"gitSyncTest_1": "hub/11499/git-repo-test-read-write-windmill",
|
||||
"gitSyncTest_2": "hub/11667/git-repo-test-read-write-windmill",
|
||||
|
||||
Reference in New Issue
Block a user