@@ -150,11 +152,45 @@ export default function EnvironmentCard({
);
}
+function GlobalEnvironmentSessionImageBadge({
+ launcher,
+}: {
+ launcher: SessionLauncher;
+}) {
+ const environment = launcher.environment;
+ const { data, isLoading } = useGetSessionsImagesQuery(
+ environment && environment.container_image
+ ? { imageUrl: environment.container_image }
+ : skipToken
+ );
+ const { data: resourcePools, isLoading: isLoadingResourcePools } =
+ computeResourcesApi.endpoints.getResourcePools.useQueryState({});
+ const resourcePool = useMemo(() => {
+ if (launcher?.resource_class_id == null || resourcePools == null) {
+ return undefined;
+ }
+ return resourcePools.find(({ classes }) =>
+ classes.some(({ id }) => id === launcher.resource_class_id)
+ );
+ }, [launcher?.resource_class_id, resourcePools]);
+
+ return (
+
-
+ {showImageBadge && (
+
+ )}
{!isLoading && data?.accessible === false && (
{!data.connection && !data.provider ? (
@@ -336,6 +391,23 @@ function CustomBuildEnvironmentValues({
}
);
+ const { data: imageCheck, isLoading: isLoadingContainerImage } =
+ useGetSessionsImagesQuery(
+ environment.container_image != null
+ ? { imageUrl: environment.container_image }
+ : skipToken
+ );
+ const { data: resourcePools, isLoading: isLoadingResourcePools } =
+ computeResourcesApi.endpoints.getResourcePools.useQueryState({});
+ const resourcePool = useMemo(() => {
+ if (launcher?.resource_class_id == null || resourcePools == null) {
+ return undefined;
+ }
+ return resourcePools.find(({ classes }) =>
+ classes.some(({ id }) => id === launcher.resource_class_id)
+ );
+ }, [launcher?.resource_class_id, resourcePools]);
+
// Invalidate launchers if the container image is not the same as the
// image from the last successful build
const dispatch = useAppDispatch();
@@ -368,7 +440,12 @@ function CustomBuildEnvironmentValues({
) : (
<>
-
+
{lastSuccessfulBuild && (
-
+
)}
@@ -505,25 +582,6 @@ function EnvironmentJSONArrayRowWithLabel({
);
}
-function ReadyStatusBadge() {
- return (
-
-
- Ready
-
- );
-}
-
function NotReadyStatusBadge() {
return (
{
+ if (imageCheck == null || resourcePool == null) {
+ return "unknown";
+ }
+ return isImageCompatibleWith(imageCheck, resourcePool.platform);
+ }, [imageCheck, resourcePool]);
+
const badgeIcon =
- status === "in_progress" ? (
+ buildStatus === "in_progress" ? (
) : (
);
const badgeText =
- status === "in_progress"
+ isCompatible === false
+ ? "Image incompatible"
+ : buildStatus === "in_progress"
? "Build in progress"
- : status === "cancelled"
+ : buildStatus === "cancelled"
? "Build cancelled"
- : status === "succeeded"
+ : buildStatus === "succeeded"
? "Build succeeded"
: "Build failed";
const badgeColorClasses =
- status === "in_progress"
+ isCompatible === false
+ ? ["border-danger", "bg-danger-subtle", "text-danger-emphasis"]
+ : buildStatus === "in_progress"
? ["border-warning", "bg-warning-subtle", "text-warning-emphasis"]
- : status === "succeeded"
+ : buildStatus === "succeeded"
? ["border-success", "bg-success-subtle", "text-success-emphasis"]
: ["border-danger", "bg-danger-subtle", "text-danger-emphasis"];
diff --git a/client/src/features/sessionsV2/components/SessionForm/BuilderAdvancedSettings.tsx b/client/src/features/sessionsV2/components/SessionForm/BuilderAdvancedSettings.tsx
new file mode 100644
index 0000000000..e0299328e3
--- /dev/null
+++ b/client/src/features/sessionsV2/components/SessionForm/BuilderAdvancedSettings.tsx
@@ -0,0 +1,138 @@
+/*!
+ * Copyright 2025 - Swiss Data Science Center (SDSC)
+ * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
+ * Eidgenössische Technische Hochschule Zürich (ETHZ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import cx from "classnames";
+import { useCallback, useMemo, useState } from "react";
+import {
+ Controller,
+ useController,
+ type Control,
+ type FieldValues,
+ type UseControllerProps,
+} from "react-hook-form";
+import { Collapse, Label } from "reactstrap";
+
+import ChevronFlippedIcon from "~/components/icons/ChevronFlippedIcon";
+import { BUILDER_PLATFORMS } from "../../session.constants";
+import type { SessionLauncherForm } from "../../sessionsV2.types";
+import BuilderSelectorCommon from "./BuilderSelectorCommon";
+
+interface BuilderAdvancedSettingsProps {
+ control: Control;
+}
+
+export default function BuilderAdvancedSettings({
+ control,
+}: BuilderAdvancedSettingsProps) {
+ const {
+ formState: { defaultValues },
+ } = useController({ control, name: "platform" });
+ defaultValues?.platform;
+ const isDefaultPlatform =
+ defaultValues?.platform == null ||
+ defaultValues.platform === BUILDER_PLATFORMS[0].value;
+ const [isOpen, setIsOpen] = useState(!isDefaultPlatform);
+ const toggleIsOpen = useCallback(
+ () => setIsOpen((isAdvancedSettingOpen) => !isAdvancedSettingOpen),
+ []
+ );
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+interface BuilderPlatformSelectorProps
+ extends UseControllerProps {}
+
+function BuilderPlatformSelector({
+ ...controllerProps
+}: BuilderPlatformSelectorProps) {
+ const defaultValue = useMemo(
+ () =>
+ controllerProps.defaultValue
+ ? controllerProps.defaultValue
+ : BUILDER_PLATFORMS[0],
+ [controllerProps.defaultValue]
+ );
+
+ return (
+
+
+
(
+ <>
+
+
+
+
+ {error?.message ? (
+ <>{error.message}>
+ ) : (
+ <>Please select a valid platform.>
+ )}
+
+ >
+ )}
+ rules={
+ controllerProps.rules ?? {
+ required: "Please select a platform.",
+ }
+ }
+ />
+
+ );
+}
diff --git a/client/src/features/sessionsV2/components/SessionForm/BuilderEnvironmentFields.tsx b/client/src/features/sessionsV2/components/SessionForm/BuilderEnvironmentFields.tsx
index b7dc4d95fa..0dd6ea7f1c 100644
--- a/client/src/features/sessionsV2/components/SessionForm/BuilderEnvironmentFields.tsx
+++ b/client/src/features/sessionsV2/components/SessionForm/BuilderEnvironmentFields.tsx
@@ -29,6 +29,7 @@ import { DEFAULT_APP_PARAMS } from "../../../../utils/context/appParams.constant
import { useProject } from "../../../ProjectPageV2/ProjectPageContainer/ProjectPageContainer";
import { useGetRepositoriesProbesQuery } from "../../../repositories/repositories.api";
import type { SessionLauncherForm } from "../../sessionsV2.types";
+import BuilderAdvancedSettings from "./BuilderAdvancedSettings";
import BuilderFrontendSelector from "./BuilderFrontendSelector";
import BuilderTypeSelector from "./BuilderTypeSelector";
import CodeRepositoryAdvancedSettings from "./CodeRepositoryAdvancedSettings";
@@ -104,6 +105,7 @@ export default function BuilderEnvironmentFields({