fix(ci): restore custom widget checks

This commit is contained in:
Thomas Camlong committed 2026-07-18 17:14:10 +02:00
1 parent eae48f50fd
commit a31a8fd4dd
5 files changed
+65 -111

No files matched your search

+12
View File
@@ -312,6 +312,18 @@ jobs:
"${PREVIEW_IMAGE}@${AMD64_DIGEST}" \
"${PREVIEW_IMAGE}@${ARM64_DIGEST}"
- name: Verify preview manifest platforms
if: "!env.ACT"
run: |
image="${PREVIEW_IMAGE}:${PREVIEW_TAG}"
manifest="$(docker buildx imagetools inspect "$image" --raw)"
for architecture in amd64 arm64; do
jq --exit-status --arg architecture "$architecture" \
'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \
<<<"$manifest" >/dev/null
done
docker buildx imagetools inspect "$image"
cleanup:
name: Clean up preview image
if: github.event_name == 'pull_request_target' || (github.event_name == 'workflow_dispatch' && inputs.operation == 'cleanup')
@@ -105,7 +105,7 @@ External redis is currently not supported with Proxmox Community Scripts
The advanced deployments environment variables should only be used if you know what you are doing.
:::
| Environment Variable | Description |
| ------------------------ | -------------------------------------------------- |
| `DB_MIGRATIONS_DISABLED` | Disable db migrations. For example for helm charts |
| Environment Variable | Description |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `DB_MIGRATIONS_DISABLED` | Disable db migrations. For example for helm charts |
| `NEXT_PUBLIC_WORKSHOP_API_URL` | Build-time public origin for a separately hosted Workshop PocketBase service. Defaults to `https://homarr.dev`. |
+1 -2
View File
@@ -6,13 +6,12 @@ ENV COREPACK_HOME=/corepack
RUN mkdir -p /corepack && corepack enable pnpm && corepack install --global pnpm@11.11.0
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json turbo.json ./
COPY patches ./patches
RUN --mount=type=cache,id=workshop-pnpm-store,target=/pnpm/store pnpm config set store-dir /pnpm/store && pnpm fetch --ignore-scripts
COPY apps/docs ./apps/docs
COPY packages/custom-widgets ./packages/custom-widgets
COPY packages/definitions ./packages/definitions
COPY packages/workshop ./packages/workshop
COPY tooling/typescript ./tooling/typescript
RUN --mount=type=cache,id=workshop-pnpm-store,target=/pnpm/store pnpm install --recursive --offline --frozen-lockfile
RUN --mount=type=cache,id=workshop-pnpm-store,target=/pnpm/store pnpm config set store-dir /pnpm/store && pnpm install --filter @homarr/docs... --frozen-lockfile
FROM dependencies AS website
RUN CI=true SKIP_ENV_VALIDATION=true pnpm turbo build --filter=@homarr/docs
@@ -6862,16 +6862,8 @@
"width",
"wrapperProps"
],
"Chip.Group": [
"defaultValue",
"multiple",
"value"
],
"ChipGroup": [
"defaultValue",
"multiple",
"value"
],
"Chip.Group": ["defaultValue", "multiple", "value"],
"ChipGroup": ["defaultValue", "multiple", "value"],
"CloseButton": [
"attributes",
"bd",
@@ -8171,10 +8163,7 @@
"vocab",
"w"
],
"CopyButton": [
"timeout",
"value"
],
"CopyButton": ["timeout", "value"],
"DataList": [
"about",
"accessKey",
@@ -12187,10 +12176,7 @@
"withOverlay",
"withRoles"
],
"HoverCard.Target": [
"eventPropsWrapperName",
"popupType"
],
"HoverCard.Target": ["eventPropsWrapperName", "popupType"],
"HoverCard.Dropdown": [
"about",
"accessKey",
@@ -12346,10 +12332,7 @@
"vocab",
"w"
],
"HoverCard.Group": [
"closeDelay",
"openDelay"
],
"HoverCard.Group": ["closeDelay", "openDelay"],
"HoverCardDropdown": [
"about",
"accessKey",
@@ -12505,14 +12488,8 @@
"vocab",
"w"
],
"HoverCardGroup": [
"closeDelay",
"openDelay"
],
"HoverCardTarget": [
"eventPropsWrapperName",
"popupType"
],
"HoverCardGroup": ["closeDelay", "openDelay"],
"HoverCardTarget": ["eventPropsWrapperName", "popupType"],
"HueSlider": [
"about",
"accessKey",
@@ -15762,10 +15739,7 @@
"vocab",
"w"
],
"Menu.CheckboxGroup": [
"defaultValue",
"value"
],
"Menu.CheckboxGroup": ["defaultValue", "value"],
"Menu.RadioItem": [
"about",
"accessKey",
@@ -15930,18 +15904,9 @@
"vocab",
"w"
],
"Menu.RadioGroup": [
"defaultValue",
"value"
],
"Menu.ContextMenu": [
"disabled",
"longPressDelay"
],
"MenuCheckboxGroup": [
"defaultValue",
"value"
],
"Menu.RadioGroup": ["defaultValue", "value"],
"Menu.ContextMenu": ["disabled", "longPressDelay"],
"MenuCheckboxGroup": ["defaultValue", "value"],
"MenuCheckboxItem": [
"about",
"accessKey",
@@ -16106,10 +16071,7 @@
"vocab",
"w"
],
"MenuContextMenu": [
"disabled",
"longPressDelay"
],
"MenuContextMenu": ["disabled", "longPressDelay"],
"MenuDivider": [
"about",
"accessKey",
@@ -16638,10 +16600,7 @@
"vocab",
"w"
],
"MenuRadioGroup": [
"defaultValue",
"value"
],
"MenuRadioGroup": ["defaultValue", "value"],
"MenuRadioItem": [
"about",
"accessKey",
@@ -21530,10 +21489,7 @@
"withOverlay",
"withRoles"
],
"Popover.Target": [
"key",
"popupType"
],
"Popover.Target": ["key", "popupType"],
"Popover.Dropdown": [
"about",
"accessKey",
@@ -21689,14 +21645,8 @@
"vocab",
"w"
],
"Popover.ContextMenu": [
"disabled",
"longPressDelay"
],
"PopoverContextMenu": [
"disabled",
"longPressDelay"
],
"Popover.ContextMenu": ["disabled", "longPressDelay"],
"PopoverContextMenu": ["disabled", "longPressDelay"],
"PopoverDropdown": [
"about",
"accessKey",
@@ -21852,10 +21802,7 @@
"vocab",
"w"
],
"PopoverTarget": [
"key",
"popupType"
],
"PopoverTarget": ["key", "popupType"],
"Progress": [
"about",
"accessKey",
@@ -30320,9 +30267,7 @@
"vocab",
"w"
],
"Table.DataRenderer": [
"data"
],
"Table.DataRenderer": ["data"],
"TableCaption": [
"about",
"accessKey",
@@ -34705,10 +34650,7 @@
"vocab",
"w"
],
"Tooltip.Group": [
"closeDelay",
"openDelay"
],
"Tooltip.Group": ["closeDelay", "openDelay"],
"TooltipFloating": [
"about",
"accessKey",
@@ -34872,10 +34814,7 @@
"vocab",
"w"
],
"TooltipGroup": [
"closeDelay",
"openDelay"
],
"TooltipGroup": ["closeDelay", "openDelay"],
"Tree": [
"about",
"accessKey",
@@ -39832,12 +39771,7 @@
"withSuccessStyles",
"wrapperProps"
],
"TimeValue": [
"amPmLabels",
"format",
"value",
"withSeconds"
],
"TimeValue": ["amPmLabels", "format", "value", "withSeconds"],
"WeekdaysRow": [
"about",
"accessKey",
+28 -19
View File
@@ -6,37 +6,46 @@ import { createLanguageMapping } from "@homarr/translation";
import { widgetImports } from "..";
const doesOptionRenderTranslatedCopy = (option: unknown) => {
if (typeof option !== "object" || option === null || !("type" in option)) return false;
return option.type !== "internal" && option.type !== "customWidgetConfiguration";
};
describe("Widget properties with description should have matching translations", async () => {
const enTranslation = await createLanguageMapping().en();
objectEntries(widgetImports).forEach(([key, value]) => {
Object.entries(value.definition.createOptions({} as SettingsContextProps)).forEach(([optionKey, optionValue_]) => {
const optionValue = optionValue_ as { withDescription: boolean };
it(`should have matching translations for ${optionKey} option description of ${key} widget`, () => {
const option = enTranslation.default.widget[key].option;
if (!(optionKey in option)) {
throw new Error(`Option ${optionKey} not found in translation`);
}
const value = option[optionKey as keyof typeof option];
Object.entries(value.definition.createOptions({} as SettingsContextProps))
.filter(([, option]) => doesOptionRenderTranslatedCopy(option))
.forEach(([optionKey, optionValue_]) => {
const optionValue = optionValue_ as { withDescription: boolean };
it(`should have matching translations for ${optionKey} option description of ${key} widget`, () => {
const option = enTranslation.default.widget[key].option;
if (!(optionKey in option)) {
throw new Error(`Option ${optionKey} not found in translation`);
}
const value = option[optionKey as keyof typeof option];
expect("description" in value).toBe(optionValue.withDescription);
expect("description" in value).toBe(optionValue.withDescription);
});
});
});
});
});
describe("Widget properties should have matching name translations", async () => {
const enTranslation = await createLanguageMapping().en();
objectEntries(widgetImports).forEach(([key, value]) => {
Object.keys(value.definition.createOptions({} as SettingsContextProps)).forEach((optionKey) => {
it(`should have matching translations for ${optionKey} option name of ${key} widget`, () => {
const option = enTranslation.default.widget[key].option;
if (!(optionKey in option)) {
throw new Error(`Option ${optionKey} not found in translation`);
}
const value = option[optionKey as keyof typeof option];
Object.entries(value.definition.createOptions({} as SettingsContextProps))
.filter(([, option]) => doesOptionRenderTranslatedCopy(option))
.forEach(([optionKey]) => {
it(`should have matching translations for ${optionKey} option name of ${key} widget`, () => {
const option = enTranslation.default.widget[key].option;
if (!(optionKey in option)) {
throw new Error(`Option ${optionKey} not found in translation`);
}
const value = option[optionKey as keyof typeof option];
expect("label" in value).toBe(true);
expect("label" in value).toBe(true);
});
});
});
});
});