diff --git a/assets/design-system/src/components/Input.tsx b/assets/design-system/src/components/Input.tsx index c8902f549b..ed902e4a94 100644 --- a/assets/design-system/src/components/Input.tsx +++ b/assets/design-system/src/components/Input.tsx @@ -112,15 +112,15 @@ function Input({ paddingLeft: prefix ? 'xsmall' : titleContent - ? startIcon - ? 'xsmall' - : 'small' - : 'medium', + ? startIcon + ? 'xsmall' + : 'small' + : 'medium', paddingRight: suffix ? 'xsmall' : showClearButton || endIcon - ? 'xsmall' - : 'medium', + ? 'xsmall' + : 'medium', }, ], StartIcon: [ @@ -146,8 +146,8 @@ function Input({ const size = (props as any).large ? 'large' : (props as any).small - ? 'small' - : 'medium' + ? 'small' + : 'medium' inputProps = mergeProps(useFormField()?.fieldProps ?? {}, inputProps) const hasEndIcon = (showClearButton && props.value) || endIcon || suffix diff --git a/assets/design-system/src/components/Input2.tsx b/assets/design-system/src/components/Input2.tsx index fe6c4aba12..4155c89147 100644 --- a/assets/design-system/src/components/Input2.tsx +++ b/assets/design-system/src/components/Input2.tsx @@ -194,8 +194,8 @@ const EndIcon = styled(BaseIcon)<{ paddingRight: $hasEndContent ? theme.spacing.small : $hasDropdownButton - ? 0 - : theme.spacing.medium, + ? 0 + : theme.spacing.medium, paddingLeft: $hasEndContent ? theme.spacing.xsmall : theme.spacing.medium, })) diff --git a/assets/design-system/src/components/ListBox.tsx b/assets/design-system/src/components/ListBox.tsx index 2e2103e028..8893160679 100644 --- a/assets/design-system/src/components/ListBox.tsx +++ b/assets/design-system/src/components/ListBox.tsx @@ -102,10 +102,10 @@ function propsToTextValue(props: Record | null | undefined) { return typeof textValue === 'string' && textValue ? textValue : typeof label === 'string' && label - ? label - : typeof children === 'string' && children - ? children - : '' + ? label + : typeof children === 'string' && children + ? children + : '' } function useItemWrappedChildren( diff --git a/assets/design-system/src/components/ListBoxItem.tsx b/assets/design-system/src/components/ListBoxItem.tsx index 8bad47fb8d..e3f4936cfe 100644 --- a/assets/design-system/src/components/ListBoxItem.tsx +++ b/assets/design-system/src/components/ListBoxItem.tsx @@ -76,8 +76,8 @@ const ListBoxItemInner = styled.div>( color: disabled ? theme.colors['text-primary-disabled'] : destructive - ? theme.colors['text-danger-light'] - : theme.colors.text, + ? theme.colors['text-danger-light'] + : theme.colors.text, }, '.description': { ...theme.partials.text.caption, diff --git a/assets/design-system/src/components/Radio.tsx b/assets/design-system/src/components/Radio.tsx index 09527239e7..6822fd98bb 100644 --- a/assets/design-system/src/components/Radio.tsx +++ b/assets/design-system/src/components/Radio.tsx @@ -68,8 +68,8 @@ const HonorableLabelStyled = styled(Label)<{ backgroundColor: $disabled ? theme.colors['action-primary-disabled'] : $isFocusVisible - ? theme.colors['action-input-hover'] - : theme.colors['fill-zero'], + ? theme.colors['action-input-hover'] + : theme.colors['fill-zero'], '.icon': { display: 'flex', alignItems: 'center', @@ -77,8 +77,8 @@ const HonorableLabelStyled = styled(Label)<{ color: $disabled ? theme.colors['action-primary-disabled'] : $isFocusVisible - ? theme.colors['action-primary-hover'] - : theme.colors['action-primary'], + ? theme.colors['action-primary-hover'] + : theme.colors['action-primary'], }, }, ...(!$disabled diff --git a/assets/design-system/src/components/Select.tsx b/assets/design-system/src/components/Select.tsx index 39757564bf..166db6212c 100644 --- a/assets/design-system/src/components/Select.tsx +++ b/assets/design-system/src/components/Select.tsx @@ -147,8 +147,8 @@ const SelectButtonInner = styled.div<{ backgroundColor: transparent ? 'transparent' : theme.mode === 'light' - ? theme.colors['fill-zero'] - : theme.colors[parentFillLevelToBackground[parentFillLevel]], + ? theme.colors['fill-zero'] + : theme.colors[parentFillLevelToBackground[parentFillLevel]], display: 'flex', flexDirection: 'row', flexShrink: 1, diff --git a/assets/src/components/cd/cluster/upgrade-plan/ClusterUpgradePlan.tsx b/assets/src/components/cd/cluster/upgrade-plan/ClusterUpgradePlan.tsx index 517bd8b7b3..26001b31c1 100644 --- a/assets/src/components/cd/cluster/upgrade-plan/ClusterUpgradePlan.tsx +++ b/assets/src/components/cd/cluster/upgrade-plan/ClusterUpgradePlan.tsx @@ -124,7 +124,9 @@ export function ClusterUpgradePlan() { const kubeVersion = getClusterKubeVersion(clusterBasic) const parsedKubeVersion = semver.coerce(kubeVersion) ?? semver.coerce('1.21.0') - const nextKubeVersion = `${parsedKubeVersion.major}.${parsedKubeVersion.minor + 1}` + const nextKubeVersion = `${parsedKubeVersion.major}.${ + parsedKubeVersion.minor + 1 + }` const { data, loading, error } = useClusterOverviewDetailsQuery({ variables: { @@ -198,7 +200,9 @@ export function ClusterUpgradePlan() { onClusterChange={(cluster) => { if (cluster?.id) navigate( - `${getClusterDetailsPath({ clusterId: cluster.id })}/${CLUSTER_UPGRADES_REL_PATH}` + `${getClusterDetailsPath({ + clusterId: cluster.id, + })}/${CLUSTER_UPGRADES_REL_PATH}` ) }} placeholder="Select a cluster" diff --git a/assets/src/components/kubernetes/common/NamespaceMultiSelect.tsx b/assets/src/components/kubernetes/common/NamespaceMultiSelect.tsx index 1a252b83e5..4a387e3eee 100644 --- a/assets/src/components/kubernetes/common/NamespaceMultiSelect.tsx +++ b/assets/src/components/kubernetes/common/NamespaceMultiSelect.tsx @@ -28,8 +28,8 @@ export function NamespaceMultiSelect({ {sortedSelectedNamespaces.length === 0 ? 'Select namespaces' : allSelected - ? 'All namespaces' - : sortedSelectedNamespaces.join(', ')} + ? 'All namespaces' + : sortedSelectedNamespaces.join(', ')} } selectionMode="multiple" diff --git a/assets/src/components/profile/MyProfile.tsx b/assets/src/components/profile/MyProfile.tsx index bb06125a68..cf4c2e877b 100644 --- a/assets/src/components/profile/MyProfile.tsx +++ b/assets/src/components/profile/MyProfile.tsx @@ -30,8 +30,8 @@ export default function MyProfile() { if (!me) return null - const currentTab = directory.find((tab) => - pathname?.startsWith(`${pathPrefix}/${tab.path}`) + const currentTab = directory.find( + (tab) => pathname?.startsWith(`${pathPrefix}/${tab.path}`) ) return ( diff --git a/static/compatibilities.yaml b/static/compatibilities.yaml index 3a685d6bd6..b8ffaaec6d 100644 --- a/static/compatibilities.yaml +++ b/static/compatibilities.yaml @@ -15936,3 +15936,42 @@ addons: chart_version: 0.6.0 images: [] name: dynatrace-operator +- icon: https://raw.githubusercontent.com/karmada-io/karmada/master/docs/images/Karmada-logo-horizontal-color.png + git_url: https://github.com/karmada-io/karmada + release_url: https://github.com/karmada-io/karmada/releases/tag/v{vsn} + helm_repository_url: https://raw.githubusercontent.com/karmada-io/karmada/master/charts + versions: + - version: 1.16.0 + kube: ['1.34', '1.33', '1.32', '1.31', '1.30', '1.29', '1.28', '1.27', '1.26', + '1.25'] + requirements: [] + incompatibilities: [] + summary: null + chart_version: 1.16.0 + images: ['docker.io/bitnamisecure/kubectl:latest', 'docker.io/cfssl/cfssl:latest', + 'docker.io/karmada/karmada-aggregated-apiserver:v1.16.0', 'docker.io/karmada/karmada-controller-manager:v1.16.0', + 'docker.io/karmada/karmada-scheduler:v1.16.0', 'docker.io/karmada/karmada-webhook:v1.16.0', + 'registry.k8s.io/etcd:3.6.0-0', 'registry.k8s.io/kube-apiserver:v1.34.1', 'registry.k8s.io/kube-controller-manager:v1.34.1'] + - version: 1.15.0 + kube: ['1.33', '1.32', '1.31', '1.30', '1.29', '1.28', '1.27', '1.26', '1.25', + '1.24'] + requirements: [] + incompatibilities: [] + summary: null + chart_version: 1.15.0 + images: ['docker.io/bitnami/kubectl:latest', 'docker.io/cfssl/cfssl:latest', 'docker.io/karmada/karmada-aggregated-apiserver:v1.15.0', + 'docker.io/karmada/karmada-controller-manager:v1.15.0', 'docker.io/karmada/karmada-scheduler:v1.15.0', + 'docker.io/karmada/karmada-webhook:v1.15.0', 'registry.k8s.io/etcd:3.5.16-0', + 'registry.k8s.io/kube-apiserver:v1.31.3', 'registry.k8s.io/kube-controller-manager:v1.31.3'] + - version: 1.14.0 + kube: ['1.32', '1.31', '1.30', '1.29', '1.28', '1.27', '1.26', '1.25', '1.24', + '1.23'] + requirements: [] + incompatibilities: [] + summary: null + chart_version: 1.14.0 + images: ['docker.io/bitnami/kubectl:latest', 'docker.io/cfssl/cfssl:latest', 'docker.io/karmada/karmada-aggregated-apiserver:v1.14.0', + 'docker.io/karmada/karmada-controller-manager:v1.14.0', 'docker.io/karmada/karmada-scheduler:v1.14.0', + 'docker.io/karmada/karmada-webhook:v1.14.0', 'registry.k8s.io/etcd:3.5.16-0', + 'registry.k8s.io/kube-apiserver:v1.31.3', 'registry.k8s.io/kube-controller-manager:v1.31.3'] + name: karmada diff --git a/static/compatibilities/karmada.yaml b/static/compatibilities/karmada.yaml new file mode 100644 index 0000000000..36d2c2ae3c --- /dev/null +++ b/static/compatibilities/karmada.yaml @@ -0,0 +1,35 @@ +icon: https://raw.githubusercontent.com/karmada-io/karmada/master/docs/images/Karmada-logo-horizontal-color.png +git_url: https://github.com/karmada-io/karmada +release_url: https://github.com/karmada-io/karmada/releases/tag/v{vsn} +helm_repository_url: https://raw.githubusercontent.com/karmada-io/karmada/master/charts +versions: +- version: 1.16.0 + kube: ['1.34', '1.33', '1.32', '1.31', '1.30', '1.29', '1.28', '1.27', '1.26', '1.25'] + requirements: [] + incompatibilities: [] + summary: null + chart_version: 1.16.0 + images: ['docker.io/bitnamisecure/kubectl:latest', 'docker.io/cfssl/cfssl:latest', + 'docker.io/karmada/karmada-aggregated-apiserver:v1.16.0', 'docker.io/karmada/karmada-controller-manager:v1.16.0', + 'docker.io/karmada/karmada-scheduler:v1.16.0', 'docker.io/karmada/karmada-webhook:v1.16.0', + 'registry.k8s.io/etcd:3.6.0-0', 'registry.k8s.io/kube-apiserver:v1.34.1', 'registry.k8s.io/kube-controller-manager:v1.34.1'] +- version: 1.15.0 + kube: ['1.33', '1.32', '1.31', '1.30', '1.29', '1.28', '1.27', '1.26', '1.25', '1.24'] + requirements: [] + incompatibilities: [] + summary: null + chart_version: 1.15.0 + images: ['docker.io/bitnami/kubectl:latest', 'docker.io/cfssl/cfssl:latest', 'docker.io/karmada/karmada-aggregated-apiserver:v1.15.0', + 'docker.io/karmada/karmada-controller-manager:v1.15.0', 'docker.io/karmada/karmada-scheduler:v1.15.0', + 'docker.io/karmada/karmada-webhook:v1.15.0', 'registry.k8s.io/etcd:3.5.16-0', + 'registry.k8s.io/kube-apiserver:v1.31.3', 'registry.k8s.io/kube-controller-manager:v1.31.3'] +- version: 1.14.0 + kube: ['1.32', '1.31', '1.30', '1.29', '1.28', '1.27', '1.26', '1.25', '1.24', '1.23'] + requirements: [] + incompatibilities: [] + summary: null + chart_version: 1.14.0 + images: ['docker.io/bitnami/kubectl:latest', 'docker.io/cfssl/cfssl:latest', 'docker.io/karmada/karmada-aggregated-apiserver:v1.14.0', + 'docker.io/karmada/karmada-controller-manager:v1.14.0', 'docker.io/karmada/karmada-scheduler:v1.14.0', + 'docker.io/karmada/karmada-webhook:v1.14.0', 'registry.k8s.io/etcd:3.5.16-0', + 'registry.k8s.io/kube-apiserver:v1.31.3', 'registry.k8s.io/kube-controller-manager:v1.31.3'] diff --git a/static/compatibilities/manifest.yaml b/static/compatibilities/manifest.yaml index 73e1f5f7d2..0493b28245 100644 --- a/static/compatibilities/manifest.yaml +++ b/static/compatibilities/manifest.yaml @@ -40,3 +40,4 @@ names: - kserve - kubescape-operator - dynatrace-operator +- karmada diff --git a/utils/compatibility/scrapers/karmada.py b/utils/compatibility/scrapers/karmada.py new file mode 100644 index 0000000000..12035f14af --- /dev/null +++ b/utils/compatibility/scrapers/karmada.py @@ -0,0 +1,127 @@ +import re +from collections import OrderedDict + +from utils import ( + fetch_page, + get_chart_versions, + print_error, + update_compatibility_info, + validate_semver, +) + +APP_NAME = "karmada" +README_URL = "https://raw.githubusercontent.com/karmada-io/karmada/master/README.md" +HELM_REPO_URL = "https://raw.githubusercontent.com/karmada-io/karmada/master/charts" +TARGET_FILE = f"../../static/compatibilities/{APP_NAME}.yaml" +SUPPORTED_MARKERS = {"✓", "+", "✔"} + + +def extract_table_lines(markdown: str) -> list[str]: + if "## Kubernetes compatibility" not in markdown: + return [] + + section = markdown.split("## Kubernetes compatibility", 1)[1] + lines: list[str] = [] + for line in section.splitlines(): + stripped = line.strip() + if stripped.startswith("|"): + lines.append(stripped) + elif lines: + break + return lines + + +def parse_header(line: str) -> list[str]: + cells = [cell.strip() for cell in line.strip("|").split("|")] + kube_headers: list[str] = [] + for cell in cells[1:]: + match = re.search(r"(\d+\.\d+)", cell) + if match: + kube_headers.append(match.group(1)) + return kube_headers + + +def normalize_version(label: str) -> str | None: + match = re.search(r"(\d+\.\d+)", label) + if not match: + return None + + version = f"{match.group(1)}.0" + semver = validate_semver(version) + if not semver: + return None + return str(semver) + + +def parse_versions(markdown: str) -> list[OrderedDict]: + lines = extract_table_lines(markdown) + if len(lines) < 3: + return [] + + header = parse_header(lines[0]) + if not header: + return [] + + versions: list[OrderedDict] = [] + for row in lines[2:]: + stripped = row.replace("|", "").strip() + if not stripped or all(ch in "-:" for ch in stripped): + continue + + cells = [cell.strip() for cell in row.strip("|").split("|")] + if not cells: + continue + + version = normalize_version(cells[0]) + if not version: + continue + + kube_list: list[str] = [] + for kube, cell in zip(header, cells[1:]): + marker = cell.strip() + if any(symbol in marker for symbol in SUPPORTED_MARKERS): + kube_list.append(kube) + + if not kube_list: + continue + + kube_unique = sorted( + set(kube_list), + key=lambda v: tuple(map(int, v.split("."))), + reverse=True, + ) + + versions.append( + OrderedDict( + [ + ("version", version), + ("kube", kube_unique), + ("requirements", []), + ("incompatibilities", []), + ] + ) + ) + + return versions + + +def scrape(): + content = fetch_page(README_URL) + if not content: + print_error("Failed to download Karmada README.") + return + + markdown = content.decode("utf-8", errors="replace") + versions = parse_versions(markdown) + if not versions: + print_error("No Karmada compatibility data extracted.") + return + + chart_versions = get_chart_versions(APP_NAME) + if chart_versions: + for entry in versions: + chart_version = chart_versions.get(entry["version"]) + if chart_version: + entry["chart_version"] = chart_version + + update_compatibility_info(TARGET_FILE, versions)