mirror of
https://github.com/hanzo-io/cloud.git
synced 2026-10-30 18:57:42 +00:00
334 lines
7.1 KiB
Vue
334 lines
7.1 KiB
Vue
<template>
|
|
<Dialog v-if="show" :options="{ title, position: 'top' }" v-model="show">
|
|
<template v-slot:body-content>
|
|
<div class="flex flex-col gap-4">
|
|
<p class="text-base text-gray-600">
|
|
You can select the app patch by either entering the patch URL, or by
|
|
selecting a patch file.
|
|
</p>
|
|
<div class="flex flex-col gap-2">
|
|
<FormControl
|
|
v-if="!app"
|
|
class="w-full"
|
|
v-model="applyToApp"
|
|
label="Select app"
|
|
placeholder="Select app to patch"
|
|
type="select"
|
|
variant="outline"
|
|
:options="$resources.apps.data"
|
|
/>
|
|
|
|
<!-- Patch Selector (URL or File) -->
|
|
<div class="flex w-full items-end gap-1">
|
|
<FormControl
|
|
v-if="!patch"
|
|
class="w-full"
|
|
label="Patch URL"
|
|
type="data"
|
|
v-model="patchURL"
|
|
variant="outline"
|
|
placeholder="Enter patch URL"
|
|
/>
|
|
<FormControl
|
|
v-else
|
|
class="w-full"
|
|
label="Patch File Name"
|
|
type="data"
|
|
variant="outline"
|
|
v-model="patchFileName"
|
|
placeholder="Set patch file name"
|
|
/>
|
|
|
|
<!-- File Selector -->
|
|
<input
|
|
ref="fileSelector"
|
|
type="file"
|
|
:accept="['text/x-patch', 'text/x-diff', 'application/x-patch']"
|
|
class="hidden"
|
|
@change="onPatchFileSelect"
|
|
/>
|
|
<Button
|
|
@click="$refs.fileSelector.click()"
|
|
title="Select patch file"
|
|
>
|
|
<FeatherIcon name="file-text" class="h-5 w-5 text-gray-600" />
|
|
</Button>
|
|
|
|
<!-- Clear Patch File -->
|
|
<Button @click="clear" v-if="patch" title="Clear patch file">
|
|
<FeatherIcon name="x" class="h-5 w-5 text-gray-600" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<ErrorMessage class="-mt-2 w-full" :message="error" />
|
|
<h3 class="mt-4 text-base font-semibold">Patch Config</h3>
|
|
<FormControl
|
|
v-if="!applyToAllBenches && !applyToLatestDeploy"
|
|
v-model="applyToBench"
|
|
label="Select bench"
|
|
type="select"
|
|
variant="outline"
|
|
:options="$resources.benches.data"
|
|
/>
|
|
<FormControl
|
|
v-if="!applyToLatestDeploy"
|
|
label="Apply patch to all active benches"
|
|
type="checkbox"
|
|
v-model="applyToAllBenches"
|
|
/>
|
|
<FormControl
|
|
v-if="!applyToAllBenches"
|
|
label="Apply patch to all active benches from the latest deploy"
|
|
type="checkbox"
|
|
v-model="applyToLatestDeploy"
|
|
/>
|
|
<FormControl
|
|
label="Build assets after applying patch"
|
|
type="checkbox"
|
|
v-model="buildAssets"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<template v-slot:actions>
|
|
<Button
|
|
variant="solid"
|
|
class="w-full"
|
|
@click="applyPatch"
|
|
:loading="$resources.applyPatch.loading"
|
|
>
|
|
Apply Patch
|
|
</Button>
|
|
</template>
|
|
</Dialog>
|
|
</template>
|
|
<script>
|
|
import {
|
|
Button,
|
|
Dialog,
|
|
ErrorMessage,
|
|
FeatherIcon,
|
|
FileUploader,
|
|
FormControl,
|
|
} from 'frappe-ui';
|
|
|
|
export default {
|
|
name: 'PatchAppDialog',
|
|
props: {
|
|
app: [null, String],
|
|
group: String,
|
|
},
|
|
components: {
|
|
Dialog,
|
|
FormControl,
|
|
ErrorMessage,
|
|
FileUploader,
|
|
Button,
|
|
FeatherIcon,
|
|
},
|
|
watch: {
|
|
app(value) {
|
|
this.show = !!value;
|
|
this.applyToApp = value || '';
|
|
},
|
|
show(value) {
|
|
this.error = '';
|
|
if (value) {
|
|
return;
|
|
}
|
|
|
|
setTimeout(this.clearApp, 150);
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
show: true,
|
|
error: '',
|
|
patch: '',
|
|
patchURL: '',
|
|
patchFileName: '',
|
|
buildAssets: false,
|
|
applyToApp: '',
|
|
applyToBench: '',
|
|
applyToAllBenches: false,
|
|
applyToLatestDeploy: false,
|
|
};
|
|
},
|
|
computed: {
|
|
title() {
|
|
const app = this.app || this.applyToApp;
|
|
if (app) {
|
|
return `Apply a patch to ${app}`;
|
|
}
|
|
|
|
return 'Apply a patch';
|
|
},
|
|
},
|
|
methods: {
|
|
clearApp() {
|
|
this.$emit('clear-app-to-patch');
|
|
},
|
|
validate() {
|
|
if (!this.$resources.benches.data.length) {
|
|
this.error =
|
|
'This bench group has no benches, patch cannot be applied.';
|
|
return false;
|
|
}
|
|
|
|
if (this.patch && !this.patchFileName) {
|
|
this.error = 'Please enter a patch file Name.';
|
|
return false;
|
|
}
|
|
|
|
if (!this.patch && !this.patchURL) {
|
|
this.error = 'Please enter the patch URL or select a patch file.';
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
!this.applyToAllBenches &&
|
|
!this.applyToBench &&
|
|
!this.applyToLatestDeploy
|
|
) {
|
|
this.error =
|
|
'Please select a bench or check Apply patch to all active benches';
|
|
return false;
|
|
}
|
|
|
|
if (!this.app && !this.applyToApp) {
|
|
this.error = 'Please select an app to patch.';
|
|
return false;
|
|
}
|
|
|
|
if (this.patchURL && !this.patchURL.split('?')[0].endsWith('.patch')) {
|
|
this.error =
|
|
'Patch URL does not have a `.patch` extension. Please enter a valid URL,';
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
applyPatch() {
|
|
if (!this.validate()) {
|
|
return;
|
|
}
|
|
|
|
if (!this.patchFileName && this.patchURL) {
|
|
const patchURL = this.patchURL.split('?')[0];
|
|
this.patchFileName = patchURL.split('/').at(-1);
|
|
}
|
|
|
|
if (!this.patchFileName.endsWith('.patch')) {
|
|
this.patchFileName += '.patch';
|
|
}
|
|
|
|
const app = this.app || this.applyToApp;
|
|
const args = {
|
|
release_group: this.group,
|
|
app,
|
|
patch_config: {
|
|
patch: this.patch,
|
|
filename: this.patchFileName,
|
|
patch_url: this.patchURL,
|
|
build_assets: this.buildAssets,
|
|
patch_bench: this.applyToBench,
|
|
patch_all_benches: this.applyToAllBenches,
|
|
patch_latest_deploy: this.applyToLatestDeploy,
|
|
},
|
|
};
|
|
|
|
this.$resources.applyPatch.submit(args);
|
|
},
|
|
async onPatchFileSelect(e) {
|
|
this.error = '';
|
|
const file = e.target.files?.[0];
|
|
if (!file) {
|
|
return;
|
|
}
|
|
|
|
this.patch = await file.text();
|
|
this.patchFileName = file.name;
|
|
},
|
|
clear() {
|
|
this.error = '';
|
|
this.patch = '';
|
|
this.patchFileName = '';
|
|
},
|
|
close() {
|
|
this.show = false;
|
|
this.clear();
|
|
},
|
|
},
|
|
resources: {
|
|
apps() {
|
|
return {
|
|
type: 'list',
|
|
doctype: 'Release Group App',
|
|
parent: 'Release Group',
|
|
auto: true,
|
|
filters: {
|
|
parenttype: 'Release Group',
|
|
parent: this.group,
|
|
},
|
|
onSuccess(data) {
|
|
if (data.length === 1) {
|
|
this.applyToApp = data[0].value;
|
|
}
|
|
},
|
|
onError(data) {
|
|
this.error = data;
|
|
},
|
|
transform(data) {
|
|
return data.map(({ name }) => ({ value: name, label: name }));
|
|
},
|
|
};
|
|
},
|
|
benches() {
|
|
return {
|
|
type: 'list',
|
|
doctype: 'Bench',
|
|
fields: ['name'],
|
|
filters: {
|
|
group: this.group,
|
|
status: 'Active',
|
|
},
|
|
auto: true,
|
|
onSuccess(data) {
|
|
if (data.length > 0) {
|
|
this.applyToBench = data.at(-1).value;
|
|
return;
|
|
}
|
|
|
|
this.error =
|
|
'This bench group has no benches, patch cannot be applied.';
|
|
},
|
|
onError(data) {
|
|
this.error = data;
|
|
},
|
|
transform(data) {
|
|
return data.map(({ name }) => ({ value: name, label: name }));
|
|
},
|
|
};
|
|
},
|
|
applyPatch() {
|
|
return {
|
|
url: 'press.api.bench.apply_patch',
|
|
onSuccess() {
|
|
this.close();
|
|
this.$router.push({
|
|
name: 'Release Group Detail Jobs',
|
|
params: { name: this.group },
|
|
});
|
|
},
|
|
onError(error) {
|
|
if (error.messages.length) {
|
|
this.error = error.messages.join('\n');
|
|
} else {
|
|
this.error = error.message;
|
|
}
|
|
},
|
|
};
|
|
},
|
|
},
|
|
};
|
|
</script>
|