mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-03-21 18:58:03 +00:00
feat: install and upgrade charts from URLs (#663)
* feat: add support for installing and upgrading charts from URLs Adds "Install from URL" button to the repositories page, allowing users to install charts directly from OCI registries and other URLs without adding them as repositories first. Also adds URL mode to the upgrade modal (via pencil/X toggle) for charts not found in any configured repo. Closes #660 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Alter --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,8 @@ import {
|
||||
} from "react";
|
||||
import { useParams } from "react-router";
|
||||
|
||||
import { BsPencil, BsX } from "react-icons/bs";
|
||||
|
||||
import apiService from "../../../API/apiService";
|
||||
import type { LatestChartVersion } from "../../../API/interfaces";
|
||||
import {
|
||||
@@ -88,7 +90,10 @@ export const InstallReleaseChartModal = ({
|
||||
const selectedVersion = selectedVersionData?.version || "";
|
||||
const selectedRepo = selectedVersionData?.repository || "";
|
||||
|
||||
const chartAddress = useMemo(() => {
|
||||
const [chartURL, setChartURL] = useState("");
|
||||
const [useURLMode, setUseURLMode] = useState(false);
|
||||
|
||||
const repoChartAddress = useMemo(() => {
|
||||
if (!selectedVersionData || !selectedVersionData.repository) return "";
|
||||
|
||||
return selectedVersionData.urls?.[0]?.startsWith("file://")
|
||||
@@ -96,6 +101,8 @@ export const InstallReleaseChartModal = ({
|
||||
: `${selectedVersionData.repository}/${chartName}`;
|
||||
}, [selectedVersionData, chartName]);
|
||||
|
||||
const chartAddress = useURLMode ? chartURL : repoChartAddress || chartURL;
|
||||
|
||||
// the original chart values
|
||||
const { data: chartValues = "" } = useChartRepoValues({
|
||||
version: selectedVersion,
|
||||
@@ -216,13 +223,48 @@ export const InstallReleaseChartModal = ({
|
||||
},
|
||||
]}
|
||||
>
|
||||
{versions && isNoneEmptyArray(versions) && (
|
||||
<VersionToInstall
|
||||
versions={versions}
|
||||
initialVersion={selectedVersionData}
|
||||
onSelectVersion={setSelectedVersionData}
|
||||
showCurrentVersion
|
||||
/>
|
||||
{!useURLMode && versions && isNoneEmptyArray(versions) ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<VersionToInstall
|
||||
versions={versions}
|
||||
initialVersion={selectedVersionData}
|
||||
onSelectVersion={setSelectedVersionData}
|
||||
showCurrentVersion
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer p-1 text-gray-400 hover:text-gray-600"
|
||||
title="Switch to URL"
|
||||
onClick={() => setUseURLMode(true)}
|
||||
>
|
||||
<BsPencil className="text-lg" />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-end gap-2">
|
||||
<div className="flex-1">
|
||||
<h4 className="text-lg">Chart URL:</h4>
|
||||
<input
|
||||
className="w-full rounded-sm border border-1 border-gray-300 bg-white px-2 py-1 text-lg"
|
||||
value={chartURL}
|
||||
onChange={(e) => setChartURL(e.target.value)}
|
||||
placeholder="oci://registry-1.docker.io/example/chart"
|
||||
/>
|
||||
</div>
|
||||
{versions && isNoneEmptyArray(versions) && (
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer p-1 text-gray-400 hover:text-gray-600"
|
||||
title="Switch to repository"
|
||||
onClick={() => {
|
||||
setUseURLMode(false);
|
||||
setChartURL("");
|
||||
}}
|
||||
>
|
||||
<BsX className="text-2xl" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<GeneralDetails
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
} from "react";
|
||||
import { useParams } from "react-router";
|
||||
|
||||
import { BsPencil, BsX } from "react-icons/bs";
|
||||
|
||||
import apiService from "../../../API/apiService";
|
||||
import type { LatestChartVersion } from "../../../API/interfaces";
|
||||
import { useGetVersions, useVersionData } from "../../../API/releases";
|
||||
@@ -32,7 +34,8 @@ export const InstallRepoChartModal = ({
|
||||
onClose,
|
||||
chartName,
|
||||
currentlyInstalledChartVersion,
|
||||
}: InstallChartModalProps) => {
|
||||
urlMode: initialURLMode = false,
|
||||
}: InstallChartModalProps & { urlMode?: boolean }) => {
|
||||
const navigate = useNavigateWithSearchParams();
|
||||
const [userValues, setUserValues] = useState("");
|
||||
const [installError, setInstallError] = useState("");
|
||||
@@ -83,7 +86,10 @@ export const InstallRepoChartModal = ({
|
||||
|
||||
const selectedRepo = selectedVersionData?.repository;
|
||||
|
||||
const chartAddress = useMemo(() => {
|
||||
const [chartURL, setChartURL] = useState("");
|
||||
const [useURLMode, setUseURLMode] = useState(initialURLMode);
|
||||
|
||||
const repoChartAddress = useMemo(() => {
|
||||
if (!selectedVersionData || !selectedVersionData?.repository) {
|
||||
return "";
|
||||
}
|
||||
@@ -92,6 +98,8 @@ export const InstallRepoChartModal = ({
|
||||
: `${selectedVersionData?.repository}/${chartName}`;
|
||||
}, [selectedVersionData, chartName]);
|
||||
|
||||
const chartAddress = useURLMode ? chartURL : repoChartAddress || chartURL;
|
||||
|
||||
const { data: chartValues = "", isLoading: loadingChartValues } =
|
||||
useChartRepoValues({
|
||||
version: selectedVersion || "",
|
||||
@@ -175,11 +183,15 @@ export const InstallRepoChartModal = ({
|
||||
onClose();
|
||||
}}
|
||||
title={
|
||||
<InstallUpgradeTitle
|
||||
isUpgrade={false}
|
||||
releaseValues={false}
|
||||
chartName={chartName}
|
||||
/>
|
||||
initialURLMode ? (
|
||||
<div className="font-bold">Install from URL</div>
|
||||
) : (
|
||||
<InstallUpgradeTitle
|
||||
isUpgrade={false}
|
||||
releaseValues={false}
|
||||
chartName={chartName}
|
||||
/>
|
||||
)
|
||||
}
|
||||
containerClassNames="w-full text-2xl h-2/3"
|
||||
actions={[
|
||||
@@ -195,13 +207,48 @@ export const InstallRepoChartModal = ({
|
||||
},
|
||||
]}
|
||||
>
|
||||
{versions && isNoneEmptyArray(versions) && (
|
||||
<VersionToInstall
|
||||
versions={versions}
|
||||
initialVersion={selectedVersionData}
|
||||
onSelectVersion={setSelectedVersionData}
|
||||
showCurrentVersion={false}
|
||||
/>
|
||||
{!useURLMode && versions && isNoneEmptyArray(versions) ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<VersionToInstall
|
||||
versions={versions}
|
||||
initialVersion={selectedVersionData}
|
||||
onSelectVersion={setSelectedVersionData}
|
||||
showCurrentVersion={false}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer p-1 text-gray-400 hover:text-gray-600"
|
||||
title="Switch to URL"
|
||||
onClick={() => setUseURLMode(true)}
|
||||
>
|
||||
<BsPencil className="text-lg" />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-end gap-2">
|
||||
<div className="flex-1">
|
||||
<h4 className="text-lg">Chart URL:</h4>
|
||||
<input
|
||||
className="w-full rounded-sm border border-1 border-gray-300 bg-white px-2 py-1 text-lg"
|
||||
value={chartURL}
|
||||
onChange={(e) => setChartURL(e.target.value)}
|
||||
placeholder="oci://registry-1.docker.io/example/chart:1.0.0"
|
||||
/>
|
||||
</div>
|
||||
{versions && isNoneEmptyArray(versions) && (
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer p-1 text-gray-400 hover:text-gray-600"
|
||||
title="Switch to repository"
|
||||
onClick={() => {
|
||||
setUseURLMode(false);
|
||||
setChartURL("");
|
||||
}}
|
||||
>
|
||||
<BsX className="text-2xl" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<GeneralDetails
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Repository } from "../../data/types";
|
||||
import useCustomSearchParams from "../../hooks/useCustomSearchParams";
|
||||
import AddRepositoryModal from "../modal/AddRepositoryModal";
|
||||
import { InstallRepoChartModal } from "../modal/InstallChartModal/InstallRepoChartModal";
|
||||
|
||||
type RepositoriesListProps = {
|
||||
selectedRepository: Repository | undefined;
|
||||
@@ -23,6 +24,14 @@ function RepositoriesList({
|
||||
removeSearchParam("add_repo");
|
||||
}
|
||||
};
|
||||
const showInstallURLModal = searchParamsObject["install_url"] === "true";
|
||||
const setShowInstallURLModal = (value: boolean) => {
|
||||
if (value) {
|
||||
upsertSearchParams("install_url", "true");
|
||||
} else {
|
||||
removeSearchParam("install_url");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -61,6 +70,14 @@ function RepositoriesList({
|
||||
>
|
||||
+ Add Repository
|
||||
</button>
|
||||
<button
|
||||
data-cy="install-url-button"
|
||||
type="button"
|
||||
className="flex h-8 w-fit cursor-pointer items-center gap-2 rounded-sm border border-gray-300 px-3 py-1 text-sm font-semibold text-muted"
|
||||
onClick={() => setShowInstallURLModal(true)}
|
||||
>
|
||||
Install from URL
|
||||
</button>
|
||||
<p className="text-xs">
|
||||
Charts developers: you can also add local directories as chart source.
|
||||
Use{" "}
|
||||
@@ -72,6 +89,12 @@ function RepositoriesList({
|
||||
isOpen={showAddRepositoryModal}
|
||||
onClose={() => setShowAddRepositoryModal(false)}
|
||||
/>
|
||||
<InstallRepoChartModal
|
||||
isOpen={showInstallURLModal}
|
||||
onClose={() => setShowInstallURLModal(false)}
|
||||
chartName=""
|
||||
urlMode
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user