Compare commits

..
Author SHA1 Message Date
Andy Lee 12a51b2451 refactor: fine tune serial console
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-04 00:35:25 +08:00
Andy Lee 9f9069fb63 refactor: clear searach result after close
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-03 23:21:37 +08:00
Andy Lee 57ee92051e feat(serial-console): add terminal search toolbar and highlight tuning
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-03 19:35:49 +08:00
Andy Lee ad3f91f298 build: artifact
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-03 18:32:32 +08:00
Andy Lee bc4f70e889 feat: improve serial console
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-03 18:31:43 +08:00
Andy Lee 423a721cb8 fix: move filter labels/schedule buttons position
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-03 16:42:07 +08:00
Andy Lee a4c28e6dff deps: update rancher/shell to 3.0.12-rc.7
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-03 16:42:06 +08:00
8 changed files with 20 additions and 123 deletions
+3 -7
View File
@@ -8,16 +8,12 @@
- [ ] Yes, the backend owner is @
- [ ] No, frontend-only.
### Related Issue
### Related Issue #
<!-- Link the issue as harvester/harvester#<issue_number> -->
harvester/harvester#<issue_number>
### Test Steps
<!-- Provide the test steps for reviewer to verify the issue -->
1. Go to ... page
### Test screenshot or video
<!-- Attach screenshot or video of the changes and eventual comparison -->
### Test screenshot or video (Required)
<!-- Attach screenshot or video of the changes and eventual comparison if you find it necessary -->
@@ -1,12 +1,12 @@
name: Build Standalone on PR Merge
on:
workflow_dispatch: # manually trigger on Github Actions only support PR branch pushed to harvester/harvester-ui-extension
push:
branches:
- main
- 'release-harvester-v*'
- '*-dev'
- bump_shell_rc7_demo
pull_request:
branches:
- main
@@ -30,35 +30,3 @@ jobs:
id-token: write
with:
CI_BRANCH: ${{github.ref_name}}
comment-on-pr:
name: Comment build result on PR
# Only for manual runs so the existing push / pull_request flows are untouched.
if: github.event_name == 'workflow_dispatch'
needs:
- build
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Add build artifact comment to the PR opened from this branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: ${{ github.ref_name }}
run: |
set -euo pipefail
# Match scripts/version: sanitize the branch and map main -> latest.
if [ "$BRANCH" = "main" ]; then
DIR="latest"
else
DIR=$(printf '%s' "$BRANCH" | sed -E 's/[^a-zA-Z0-9.-]+/-/g')
fi
URL="https://releases.rancher.com/harvester-ui/dashboard/${DIR}/index.html"
PR=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --base main --head "$BRANCH" --json number --jq '.[0].number // empty')
if [ -z "$PR" ]; then
echo "No open PR found for branch $BRANCH, skipping comment."
exit 0
fi
BODY=$(printf '🚀 **UI Artifact Build Status:** `SUCCESS` ✅\n> ui-index: %s\n> ui-source: external' "$URL")
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "$BODY"
@@ -213,15 +213,9 @@ export default {
},
clearSearchHighlights() {
this.terminal?.clearSelection();
this.searchAddon?.clearActiveDecoration();
this.searchAddon?.clearDecorations();
this.terminal?.clearSelection();
// Disposing the search decorations doesn't repaint the webgl/canvas
// layer, so force a refresh to remove the lingering highlight.
if (this.terminal) {
this.terminal.refresh(0, this.terminal.rows - 1);
}
},
findNext() {
@@ -1,5 +1,4 @@
<script>
import jsyaml from 'js-yaml';
import { mapGetters } from 'vuex';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
@@ -178,17 +177,8 @@ export default {
},
async saveVMT(buttonCb) {
this.errors = [];
this.parseVM();
this.validateCloudInit();
if (this.errors.length) {
buttonCb(false);
return;
}
const templates = await this.$store.dispatch('harvester/findAll', { type: HCI.VM_TEMPLATE });
const template = templates.find( (O) => O.metadata.name === this.templateValue.metadata.name);
@@ -247,24 +237,6 @@ export default {
this.$refs.yamlEditor?.refresh();
}
},
validateCloudInit() {
if (this.userScript) {
try {
jsyaml.load(this.userScript);
} catch (e) {
this.errors.push(this.t('harvester.virtualMachine.cloudConfig.user.invalidYaml'));
}
}
if (this.networkScript) {
try {
jsyaml.load(this.networkScript);
} catch (e) {
this.errors.push(this.t('harvester.virtualMachine.cloudConfig.network.invalidYaml'));
}
}
},
},
};
</script>
+3 -14
View File
@@ -9,7 +9,7 @@ import Loading from '@shell/components/Loading';
import CreateEditView from '@shell/mixins/create-edit-view';
import { RadioGroup } from '@components/Form/Radio';
import { NETWORK_PROTOCOL, NETWORK_TYPE } from '@pkg/harvester/config/types';
import { set, remove } from '@shell/utils/object';
import { set } from '@shell/utils/object';
import ArrayList from '@shell/components/form/ArrayList';
import { allHash } from '@shell/utils/promise';
import { HCI } from '../../types';
@@ -142,12 +142,10 @@ export default {
const inStore = this.$store.getters['currentProduct'].inStore;
const vlans = this.$store.getters[`${ inStore }/all`](HCI.VLAN) || [];
const options = vlans.map((vlan) => ({
return vlans.map((vlan) => ({
label: vlan.id,
value: vlan.id,
}));
return [{ label: this.t('generic.none'), value: '' }, ...options];
}
},
@@ -162,14 +160,6 @@ export default {
}
},
methods: {
onVlanChange(value) {
if (value === '') {
remove(this.value.spec, 'vlan');
} else {
set(this.value, 'spec.vlan', value);
}
},
async saveSubnet(buttonCb) {
const errors = [];
const name = this.value?.metadata?.name;
@@ -293,13 +283,12 @@ export default {
</div>
<div class="col span-6">
<LabeledSelect
:value="value.spec.vlan ?? ''"
v-model:value="value.spec.vlan"
class="mb-20"
:options="vlanOptions"
:placeholder="t('harvester.subnet.vlan.placeholder')"
:label="t('harvester.subnet.vlan.label')"
:mode="mode"
@update:value="onVlanChange"
/>
</div>
</div>
@@ -1,5 +1,4 @@
<script>
import jsyaml from 'js-yaml';
import { isEqual } from 'lodash';
import { mapGetters } from 'vuex';
import Tabbed from '@shell/components/Tabbed';
@@ -380,7 +379,6 @@ export default {
this.validateCPUMemory();
this.validateWindowsSysprep();
this.validateCloudInit();
// block create VM flow if has validation errors
if (this.errors.length) {
@@ -426,24 +424,6 @@ export default {
}
},
validateCloudInit() {
if (this.userScript) {
try {
jsyaml.load(this.userScript);
} catch (e) {
this.errors.push(this.t('harvester.virtualMachine.cloudConfig.user.invalidYaml'));
}
}
if (this.networkScript) {
try {
jsyaml.load(this.networkScript);
} catch (e) {
this.errors.push(this.t('harvester.virtualMachine.cloudConfig.network.invalidYaml'));
}
}
},
async saveSingle(buttonCb) {
this.parseVM();
this.value.spec.template.spec.hostname = this.hostname ? this.hostname : this.value.metadata.name;
@@ -63,7 +63,6 @@ export default {
RANCHER_TYPES,
fvFormRuleSets: [{ path: 'spec.displayName', rules: ['required'] }],
canEditPSPBindings: true,
isQuotasValid: true,
};
},
computed: {
@@ -191,13 +190,15 @@ export default {
this['membershipUpdate'] = update;
},
validateQuotas(isValid) {
this.isQuotasValid = isValid;
},
onQuotasInput({ projectLimit, nsLimit } = {}) {
this.value.spec.resourceQuota = { ...this.value.spec.resourceQuota, limit: projectLimit };
this.value.spec.namespaceDefaultResourceQuota = { ...this.value.spec.namespaceDefaultResourceQuota, limit: nsLimit };
removeQuota(key) {
['resourceQuota', 'namespaceDefaultResourceQuota'].forEach((specProp) => {
if (this.value?.spec[specProp]?.limit && this.value?.spec[specProp]?.limit[key]) {
delete this.value?.spec[specProp]?.limit[key];
}
if (this.value?.spec[specProp]?.usedLimit && this.value?.spec[specProp]?.usedLimit[key]) {
delete this.value?.spec[specProp]?.usedLimit[key];
}
});
}
},
};
@@ -211,7 +212,7 @@ export default {
:resource="value"
:subtypes="[]"
:can-yaml="false"
:validation-passed="fvFormIsValid && isQuotasValid"
:validation-passed="fvFormIsValid"
@error="e=>errors=e"
@finish="save"
@cancel="done"
@@ -266,11 +267,10 @@ export default {
:weight="9"
>
<ResourceQuota
:value="value"
v-model:value="value"
:mode="canEditTabElements"
:types="isStandaloneHarvester ? HARVESTER_TYPES : RANCHER_TYPES"
@input="onQuotasInput"
@validationChanged="validateQuotas"
@remove="removeQuota"
/>
</Tab>
<Tab
-2
View File
@@ -830,12 +830,10 @@ harvester:
label: User Data Template
title: "User Data:"
tip: "You can specify user data to configure an instance or run a configuration script during launch. If you launch more than one instance at a time, the user data is available to all the instances in that reservation. <a href='https://cloudinit.readthedocs.io/en/latest/topics/examples.html' target='_blank'>Learn more</a>"
invalidYaml: 'Invalid User Data YAML syntax'
network:
label: Network Data Template
title: "Network Data:"
tip: "The network-data configuration allows you to customize the instance's networking interfaces by assigning subnet configuration, virtual device creation (bonds, bridges, VLANs) routes and DNS configuration. <a href='https://cloudinit.readthedocs.io/en/latest/reference/network-config-format-v1.html' target='_blank'>Learn more</a>"
invalidYaml: 'Invalid Network Data YAML syntax'
sysprep:
title: Windows Sysprep Configuration
description: "Configure Windows automated installation using autounattend.xml. The configuration will be stored in a Kubernetes Secret and mounted as a CD-ROM during installation. <a href='https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/automate-windows-setup' target='_blank'>Learn more</a>"