feat: History commit message can be updated from the UI (#2777)

* feat: History comit message can be updated from the UI

* Script version history

* Display commit message in left column

* Fix FE compile break

* sqlx prepare and fix openapi file
This commit is contained in:
Guillaume Bouvignies
2023-12-05 15:45:36 +01:00
committed by GitHub
parent 20b0500a28
commit 874b7a50f7
18 changed files with 972 additions and 244 deletions
@@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "SELECT s.hash as hash, dm.deployment_msg as deployment_msg \n FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash\n WHERE s.workspace_id = $1 AND s.path = $2\n ORDER by created_at DESC",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "hash",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "deployment_msg",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
true
]
},
"hash": "362419eb262c83d6a98a0200b116e831ada60399fe5f55a56d930cc69aff2675"
}
@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Int8",
"Text"
]
},
"nullable": []
},
"hash": "652835b2b7f801532a591988ac76d385188991c6654d529f6d65f6f03794844a"
}
@@ -0,0 +1,22 @@
{
"db_name": "PostgreSQL",
"query": "SELECT path FROM app WHERE id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "path",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
},
"hash": "abb56dec024600d0d7a63212f63c3296196898003707f2cb3a890690173a51a5"
}
@@ -0,0 +1,35 @@
{
"db_name": "PostgreSQL",
"query": "SELECT a.id as app_id, av.id as version_id, dm.deployment_msg as deployment_msg\n FROM app a LEFT JOIN app_version av ON a.id = av.app_id LEFT JOIN deployment_metadata dm ON av.id = dm.app_version\n WHERE a.workspace_id = $1 AND a.path = $2\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "app_id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "version_id",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "deployment_msg",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false,
false,
true
]
},
"hash": "d02c1cfefc7d5f87ca4555a092f5fbc777a91271b80d0e488a8cae79afd56d8f"
}
@@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET deployment_msg = $4",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Int8",
"Text"
]
},
"nullable": []
},
"hash": "db558b5ecdc4c3b1af0def511f1bcd91a548f00376f644c8ba38f73812b462d0"
}
File diff suppressed because it is too large Load Diff
+120
View File
@@ -3225,6 +3225,53 @@ paths:
schema:
$ref: "#/components/schemas/NewScriptWithDraft"
/w/{workspace}/scripts/history/p/{path}:
get:
summary: get history of a script by path
operationId: getScriptHistoryByPath
tags:
- script
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: script history
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ScriptHistory"
/w/{workspace}/scripts/history_update/h/{hash}/p/{path}:
post:
summary: update history of a script
operationId: updateScriptHistory
tags:
- script
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptHash"
- $ref: "#/components/parameters/ScriptPath"
requestBody:
description: Script deployment message
required: true
content:
application/json:
schema:
type: object
properties:
deployment_msg:
type: string
responses:
"200":
description: success
content:
text/plain:
schema:
type: string
/w/{workspace}/scripts/raw/p/{path}:
get:
summary: raw script by path
@@ -4065,6 +4112,53 @@ paths:
schema:
$ref: "#/components/schemas/AppWithLastVersionWDraft"
/w/{workspace}/apps/history/p/{path}:
get:
summary: get app history by path
operationId: getAppHistoryByPath
tags:
- app
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
responses:
"200":
description: app history
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/AppHistory"
/w/{workspace}/apps/history_update/a/{id}/v/{version}:
post:
summary: update app history
operationId: updateAppHistory
tags:
- app
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/PathId"
- $ref: "#/components/parameters/PathVersion"
requestBody:
description: App deployment message
required: true
content:
application/json:
schema:
type: object
properties:
deployment_msg:
type: string
responses:
"200":
description: success
content:
text/plain:
schema:
type: string
/w/{workspace}/apps_u/public_app/{path}:
get:
summary: get public app by secret
@@ -6695,6 +6789,12 @@ components:
required: true
schema:
type: integer
PathVersion:
name: version
in: path
required: true
schema:
type: integer
Name:
name: name
in: path
@@ -7133,6 +7233,16 @@ components:
required:
- hash
ScriptHistory:
type: object
properties:
script_hash:
type: string
deployment_msg:
type: string
required:
- script_hash
ScriptArgs:
type: object
additionalProperties: {}
@@ -8572,6 +8682,16 @@ components:
type: boolean
draft: {}
AppHistory:
type: object
properties:
version:
type: integer
deployment_msg:
type: string
required:
- version
SlackToken:
type: object
properties:
+73
View File
@@ -55,6 +55,8 @@ pub fn workspaced_service() -> Router {
.route("/update/*path", post(update_app))
.route("/delete/*path", delete(delete_app))
.route("/create", post(create_app))
.route("/history/p/*path", get(get_app_history))
.route("/history_update/a/:id/v/:version", post(update_app_history))
}
pub fn unauthed_service() -> Router {
@@ -125,6 +127,19 @@ pub struct AppWithLastVersionAndDraft {
pub draft_only: Option<bool>,
}
#[derive(Serialize)]
pub struct AppHistory {
pub app_id: i64,
pub version: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub deployment_msg: Option<String>,
}
#[derive(Deserialize)]
pub struct AppHistoryUpdate {
pub deployment_msg: Option<String>,
}
pub type StaticFields = HashMap<String, Box<RawValue>>;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
@@ -313,6 +328,64 @@ async fn get_app_w_draft(
Ok(Json(app))
}
async fn get_app_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Vec<AppHistory>> {
let mut tx = user_db.begin(&authed).await?;
let query_result = sqlx::query!(
"SELECT a.id as app_id, av.id as version_id, dm.deployment_msg as deployment_msg
FROM app a LEFT JOIN app_version av ON a.id = av.app_id LEFT JOIN deployment_metadata dm ON av.id = dm.app_version
WHERE a.workspace_id = $1 AND a.path = $2
ORDER BY created_at DESC",
w_id,
path.to_path(),
).fetch_all(&mut *tx).await?;
tx.commit().await?;
let result: Vec<AppHistory> = query_result
.into_iter()
.map(|row| AppHistory {
app_id: row.app_id,
version: row.version_id,
deployment_msg: row.deployment_msg,
})
.collect();
return Ok(Json(result));
}
async fn update_app_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, app_id, app_version)): Path<(String, i64, i64)>,
Json(app_history_update): Json<AppHistoryUpdate>,
) -> Result<()> {
let mut tx = user_db.begin(&authed).await?;
let app_path = sqlx::query_scalar!("SELECT path FROM app WHERE id = $1", app_id)
.fetch_optional(&mut *tx)
.await?;
if app_path.is_none() {
tx.commit().await?;
return Err(Error::NotFound(
format!("App with ID {app_id} not found").to_string(),
));
}
sqlx::query!(
"INSERT INTO deployment_metadata (workspace_id, path, app_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
w_id,
app_path.unwrap(),
app_version,
app_history_update.deployment_msg,
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
return Ok(());
}
async fn get_app_by_id(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
+54 -1
View File
@@ -40,7 +40,7 @@ use windmill_common::{
schedule::Schedule,
scripts::{
to_i64, HubScript, ListScriptQuery, ListableScript, NewScript, Schema, Script, ScriptHash,
ScriptKind, ScriptLang,
ScriptHistory, ScriptHistoryUpdate, ScriptKind, ScriptLang,
},
users::username_to_permissioned_as,
utils::{
@@ -121,6 +121,11 @@ pub fn workspaced_service() -> Router {
"/toggle_workspace_error_handler/p/*path",
post(toggle_workspace_error_handler),
)
.route("/history/p/*path", get(get_script_history))
.route(
"/history_update/h/:hash/p/*path",
post(update_script_history),
)
}
#[derive(Serialize, FromRow)]
@@ -724,6 +729,54 @@ async fn get_script_by_path_w_draft(
Ok(Json(script))
}
async fn get_script_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, path)): Path<(String, StripPath)>,
) -> JsonResult<Vec<ScriptHistory>> {
let mut tx = user_db.begin(&authed).await?;
let query_result = sqlx::query!(
"SELECT s.hash as hash, dm.deployment_msg as deployment_msg
FROM script s LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash
WHERE s.workspace_id = $1 AND s.path = $2
ORDER by created_at DESC",
w_id,
path.to_path(),
)
.fetch_all(&mut *tx)
.await?;
tx.commit().await?;
let result: Vec<ScriptHistory> = query_result
.into_iter()
.map(|row| ScriptHistory {
script_hash: ScriptHash(row.hash),
deployment_msg: row.deployment_msg,
})
.collect();
return Ok(Json(result));
}
async fn update_script_history(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Path((w_id, script_hash, script_path)): Path<(String, ScriptHash, StripPath)>,
Json(script_history_update): Json<ScriptHistoryUpdate>,
) -> Result<()> {
let mut tx = user_db.begin(&authed).await?;
sqlx::query!(
"INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET deployment_msg = $4",
w_id,
script_path.to_path(),
script_hash.0,
script_history_update.deployment_msg,
)
.fetch_optional(&mut *tx)
.await?;
tx.commit().await?;
return Ok(());
}
async fn list_paths(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
+12
View File
@@ -204,6 +204,18 @@ pub struct ListableScript {
pub ws_error_handler_muted: Option<bool>,
}
#[derive(Serialize)]
pub struct ScriptHistory {
pub script_hash: ScriptHash,
#[serde(skip_serializing_if = "Option::is_none")]
pub deployment_msg: Option<String>,
}
#[derive(Deserialize)]
pub struct ScriptHistoryUpdate {
pub deployment_msg: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(feature = "sqlx", sqlx)]
@@ -1,32 +1,57 @@
<script lang="ts">
import { Pane, Splitpanes } from 'svelte-splitpanes'
import PanelSection from './apps/editor/settingsPanel/common/PanelSection.svelte'
import { classNames } from '$lib/utils'
import { ScriptService } from '$lib/gen'
import { classNames, emptyString } from '$lib/utils'
import { ScriptService, type ScriptHistory } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { Skeleton } from '$lib/components/common'
import FlowModuleScript from './flows/content/FlowModuleScript.svelte'
import { createEventDispatcher } from 'svelte'
import Button from './common/button/Button.svelte'
import { ExternalLink } from 'lucide-svelte'
import { ExternalLink, Pencil, ArrowRight, X } from 'lucide-svelte'
const dispatch = createEventDispatcher()
export let openDetails: boolean = false
export let scriptPath: string
let selectedVersion: string | undefined = undefined
let versions: string[] | undefined = undefined
let deploymentMsgUpdateMode = false
let deploymentMsgUpdate: string | undefined = undefined
let selectedVersion: ScriptHistory | undefined = undefined
let versions: ScriptHistory[] | undefined = undefined
let loading: boolean = false
async function loadVersions() {
loading = true
versions = (
await ScriptService.getScriptByPath({ workspace: $workspaceStore!, path: scriptPath })
).parent_hashes
versions = await ScriptService.getScriptHistoryByPath({
workspace: $workspaceStore!,
path: scriptPath
})
loading = false
}
async function updateDeploymentMsg(scriptHash: string | undefined) {
if (
selectedVersion === undefined ||
scriptHash === undefined ||
emptyString(deploymentMsgUpdate)
) {
return
}
await ScriptService.updateScriptHistory({
workspace: $workspaceStore!,
path: scriptPath,
hash: scriptHash,
requestBody: {
deployment_msg: deploymentMsgUpdate!
}
})
selectedVersion.deployment_msg = deploymentMsgUpdate
deploymentMsgUpdateMode = false
loadVersions()
}
loadVersions()
</script>
@@ -42,15 +67,23 @@
<div
class={classNames(
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
selectedVersion == version ? 'bg-blue-100 text-blue-600' : ''
selectedVersion?.script_hash == version.script_hash
? 'bg-blue-100 text-blue-600'
: ''
)}
on:click={() => (selectedVersion = version)}
on:click={() => {
selectedVersion = version
deploymentMsgUpdate = undefined
deploymentMsgUpdateMode = false
}}
>
<span class="text-xs truncate">{version}</span>
<span class="text-xs truncate">
{#if emptyString(version.deployment_msg)}Version {version.script_hash}{:else}{version.deployment_msg}{/if}
</span>
{#if openDetails}
<Button
on:click={() => {
dispatch('openDetails', { version })
dispatch('openDetails', { version: version.script_hash })
}}
class="ml-2 inline-flex gap-1 text-xs items-center"
size="xs"
@@ -76,7 +109,67 @@
<div class="h-full w-full overflow-auto">
{#if selectedVersion}
{#key selectedVersion}
<FlowModuleScript path={scriptPath} hash={selectedVersion} />
<div class="flex flex-col">
<span class="flex flex-row text-sm p-2 text-tertiary">
{#if deploymentMsgUpdateMode}
<div class="flex w-full">
<input
type="text"
bind:value={deploymentMsgUpdate}
class="!w-auto grow"
on:click|stopPropagation={() => {}}
on:keydown|stopPropagation
on:keypress|stopPropagation={({ key }) => {
if (key === 'Enter') updateDeploymentMsg(selectedVersion?.script_hash)
}}
/>
<Button
size="xs"
color="blue"
buttonType="button"
btnClasses="!p-1 !w-[34px] !ml-1"
aria-label="Save Deployment Message"
on:click={() => {
updateDeploymentMsg(selectedVersion?.script_hash)
}}
>
<ArrowRight size={14} />
</Button>
<Button
size="xs"
color="light"
buttonType="button"
btnClasses="!p-1 !w-[34px] !ml-1"
aria-label="Abort"
on:click={() => {
deploymentMsgUpdateMode = false
deploymentMsgUpdate = undefined
}}
>
<X size={14} />
</Button>
</div>
{:else}
{#if selectedVersion.deployment_msg}
{selectedVersion.deployment_msg}
{:else}
Version {selectedVersion.script_hash}
{/if}
<button
on:click={() => {
deploymentMsgUpdate = selectedVersion?.deployment_msg
deploymentMsgUpdateMode = true
}}
title="Update commit message"
class="flex items-center px-1 rounded-sm hover:text-primary text-secondary h-5"
aria-label="Update commit message"
>
<Pencil size={14} />
</button>
{/if}
</span>
<FlowModuleScript path={scriptPath} hash={selectedVersion.script_hash} />
</div>
{/key}
{:else}
<div class="text-sm p-2 text-tertiary">Select a deployment version to see its details</div>
@@ -5,11 +5,11 @@
import { sendUserToast } from '$lib/toast'
import DeploymentHistory from './DeploymentHistory.svelte'
let versions: number[] = []
let appPath: string | undefined = undefined
let historyBrowserDrawerOpen = false
export function open(newVersions: number[]) {
versions = newVersions
export function open(appPath: string) {
appPath = appPath
historyBrowserDrawerOpen = true
}
@@ -33,7 +33,7 @@
sendUserToast('App restored from previous deployment')
updateApp(e.detail)
}}
{versions}
{appPath}
on:close={() => {
historyBrowserDrawerOpen = false
}}
@@ -61,7 +61,6 @@
export let policy: Policy
export let summary: string
export let fromHub: boolean = false
export let versions: number[]
export let diffDrawer: DiffDrawer | undefined = undefined
export let savedApp:
| {
@@ -493,7 +492,6 @@
{#if $appStore}
<AppEditorHeader
on:restore
{versions}
{policy}
{fromHub}
bind:this={appEditorHeader}
@@ -103,7 +103,6 @@
export let policy: Policy
export let fromHub: boolean = false
export let versions: number[]
export let diffDrawer: DiffDrawer | undefined = undefined
export let savedApp:
| {
@@ -836,7 +835,7 @@
<Drawer bind:open={historyBrowserDrawerOpen} size="1200px">
<DrawerContent title="Deployment History" on:close={() => (historyBrowserDrawerOpen = false)}>
<DeploymentHistory on:restore {versions} />
<DeploymentHistory on:restore {appPath} />
</DrawerContent>
</Drawer>
@@ -1,20 +1,42 @@
<script lang="ts">
import { Pane, Splitpanes } from 'svelte-splitpanes'
import PanelSection from './settingsPanel/common/PanelSection.svelte'
import { classNames, displayDate } from '$lib/utils'
import { AppService, AppWithLastVersion } from '$lib/gen'
import { classNames, displayDate, emptyString } from '$lib/utils'
import { AppService, AppWithLastVersion, type AppHistory } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import AppPreview from './AppPreview.svelte'
import { Skeleton } from '$lib/components/common'
import Button from '$lib/components/common/button/Button.svelte'
import { createEventDispatcher } from 'svelte'
import { Pencil, ArrowRight, X } from 'lucide-svelte'
export let versions: number[]
export let appPath: string | undefined
let loading: boolean = false
let selectedVersion: number | undefined = undefined
let versions: AppHistory[] = []
let selectedVersion: AppHistory | undefined = undefined
let selected: AppWithLastVersion | undefined = undefined
$: selectedVersion !== undefined && loadValue(selectedVersion)
let deploymentMsgUpdateMode = false
let deploymentMsgUpdate: string | undefined = undefined
$: selectedVersion !== undefined && loadValue(selectedVersion.version)
async function loadVersions() {
console.log('loading versions')
if (appPath === undefined) {
return
}
console.log('loading versions')
loading = true
versions = await AppService.getAppHistoryByPath({
workspace: $workspaceStore!,
path: appPath
})
loading = false
}
async function loadValue(version: number) {
let app = await AppService.getAppByVersion({ workspace: $workspaceStore!, id: version })
@@ -22,32 +44,63 @@
selected = app
}
$: reversedVersions = versions.slice().reverse()
async function updateDeploymentMsg(appId: number | undefined, appVersion: number | undefined) {
if (
selectedVersion === undefined ||
appId === undefined ||
appVersion === undefined ||
emptyString(deploymentMsgUpdate)
) {
return
}
await AppService.updateAppHistory({
workspace: $workspaceStore!,
id: appId,
version: appVersion,
requestBody: {
deployment_msg: deploymentMsgUpdate!
}
})
selectedVersion.deployment_msg = deploymentMsgUpdate
deploymentMsgUpdateMode = false
loadVersions()
}
const dispatch = createEventDispatcher()
loadVersions()
</script>
<Splitpanes class="!overflow-visible">
<Pane size={20}>
<PanelSection title="Past Deployments">
<div class="flex flex-col gap-2 w-full">
{#if versions.length > 0}
<div class="flex gap-2 flex-col">
{#each reversedVersions ?? [] as version}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class={classNames(
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
selectedVersion == version ? 'bg-blue-100 text-blue-600' : ''
)}
on:click={() => (selectedVersion = version)}
>
<span class="text-xs truncate">{version}</span>
</div>
{/each}
</div>
{#if !loading}
{#if versions.length > 0}
<div class="flex gap-2 flex-col">
{#each versions ?? [] as version}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class={classNames(
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
selectedVersion?.version == version.version ? 'bg-blue-100 text-blue-600' : ''
)}
on:click={() => {
selectedVersion = version
deploymentMsgUpdateMode = false
deploymentMsgUpdate = undefined
}}
>
<span class="text-xs truncate">
{#if emptyString(version.deployment_msg)}Version {version.version}{:else}{version.deployment_msg}{/if}
</span>
</div>
{/each}
</div>
{:else}
<div class="text-sm text-tertiary">No items</div>
{/if}
{:else}
<div class="text-sm text-tertiary">No items</div>
<Skeleton layout={[[40], [40], [40], [40], [40]]} />
{/if}
</div>
</PanelSection>
@@ -56,14 +109,74 @@
<div class="h-full w-full overflow-auto">
{#if selectedVersion}
{#if selected}
<div class="flex justify-between">
<h3 class="p-1">Deployed {displayDate(selected.created_at)} by {selected.created_by}</h3
>
<div class="flex gap-2">
<Button on:click={() => window.open(`/apps/add?template_id=${selectedVersion}`)}>
<div class="flex flex-col justify-between">
<span class="flex flex-row text-sm p-1 text-tertiary">
{#if deploymentMsgUpdateMode}
<div class="flex w-full">
<input
type="text"
bind:value={deploymentMsgUpdate}
class="!w-auto grow"
on:click|stopPropagation={() => {}}
on:keydown|stopPropagation
on:keypress|stopPropagation={({ key }) => {
if (key === 'Enter')
updateDeploymentMsg(selected?.id, selectedVersion?.version)
}}
/>
<Button
size="xs"
color="blue"
buttonType="button"
btnClasses="!p-1 !w-[34px] !ml-1"
aria-label="Save Deployment Message"
on:click={() => {
updateDeploymentMsg(selected?.id, selectedVersion?.version)
}}
>
<ArrowRight size={14} />
</Button>
<Button
size="xs"
color="light"
buttonType="button"
btnClasses="!p-1 !w-[34px] !ml-1"
aria-label="Abort"
on:click={() => {
deploymentMsgUpdateMode = false
deploymentMsgUpdate = undefined
}}
>
<X size={14} />
</Button>
</div>
{:else}
{#if selectedVersion.deployment_msg}
{selectedVersion.deployment_msg}
{:else}
Deployed {displayDate(selected.created_at)} by {selected.created_by}
{/if}
<button
on:click={() => {
deploymentMsgUpdate = selectedVersion?.deployment_msg
deploymentMsgUpdateMode = true
}}
title="Update commit message"
class="flex items-center px-1 rounded-sm hover:text-primary text-secondary h-5"
aria-label="Update commit message"
>
<Pencil size={14} />
</button>
{/if}
</span>
<div class="flex p-1 gap-2">
<Button
size="xs"
on:click={() => window.open(`/apps/add?template_id=${selectedVersion}`)}
>
Restore as fork
</Button>
<Button on:click={() => dispatch('restore', selected)}
<Button size="xs" on:click={() => dispatch('restore', selected)}
>Redeploy with that version
</Button>
</div>
@@ -66,7 +66,7 @@
path
})) as unknown as AppWithLastVersion
appDeploymentHistory.open(app.versions)
appDeploymentHistory.open(app.path)
}
</script>
@@ -109,7 +109,7 @@
{#if value}
<div class="h-screen">
{#key value}
<AppEditor versions={[]} {summary} app={value} path={''} {policy} fromHub={hubId != null} />
<AppEditor {summary} app={value} path={''} {policy} fromHub={hubId != null} />
{/key}
</div>
{/if}
@@ -188,7 +188,6 @@
app = e.detail
redraw++
}}
versions={app.versions}
summary={app.summary}
app={app.value}
path={app.path}