diff --git a/apps/fabro-web/app/components/automation-form.tsx b/apps/fabro-web/app/components/automation-form.tsx
index 84831c9a6..2a744d394 100644
--- a/apps/fabro-web/app/components/automation-form.tsx
+++ b/apps/fabro-web/app/components/automation-form.tsx
@@ -1,8 +1,10 @@
import { useRef, type ReactNode } from "react";
+import { Link } from "react-router";
import { Switch } from "@headlessui/react";
import type {
Automation,
AutomationTrigger,
+ Environment,
Run,
RunProjection,
WorkflowSettings,
@@ -16,12 +18,14 @@ import {
} from "../lib/automation";
import { Panel, Row } from "./settings-panel";
import { INPUT_CLASS } from "./ui";
+import { isCloneBasedEnvironment, providerLabel } from "../lib/environment-providers";
import { sandboxRuntime } from "../lib/run-sandbox-lifecycle";
export interface AutomationFormValues {
id: string;
name: string;
description: string;
+ environmentId: string;
repository: string;
branch: string;
tag: string;
@@ -36,6 +40,7 @@ export const EMPTY_AUTOMATION_FORM: AutomationFormValues = {
id: "",
name: "",
description: "",
+ environmentId: "",
repository: "",
branch: "main",
tag: "",
@@ -61,6 +66,7 @@ export function automationToFormValues(automation: Automation): AutomationFormVa
id: automation.id,
name: automation.name,
description: automation.description ?? "",
+ environmentId: automation.environment_id ?? "",
repository: target?.repo ?? "",
branch: target?.branch ?? EMPTY_AUTOMATION_FORM.branch,
tag: target?.tag ?? "",
@@ -76,6 +82,7 @@ export function automationFormValuesFromRun(
run: Run,
runState?: RunProjection | null,
settings?: WorkflowSettings | null,
+ environments?: Environment[],
): AutomationFormValues {
const name = firstPresentString(
run.title,
@@ -96,10 +103,18 @@ export function automationFormValuesFromRun(
?? githubRepositoryFromOriginUrl(run.repository?.origin_url)
?? "";
const cloneBranch = sandboxRuntime(run.sandbox)?.clone_branch;
+ const sourceEnvironment = settings?.run?.environment;
+ const environmentId = sourceEnvironment
+ && environments?.some(
+ (environment) => environment.id === sourceEnvironment.id && isCloneBasedEnvironment(environment),
+ )
+ ? sourceEnvironment.id
+ : "";
return {
...EMPTY_AUTOMATION_FORM,
id: kebabify(name),
name,
+ environmentId,
repository,
branch: canonicalTarget?.branch
?? cloneBranch
@@ -130,6 +145,7 @@ export function isFormValid(values: AutomationFormValues): boolean {
return (
values.id.trim() !== "" &&
values.name.trim() !== "" &&
+ values.environmentId.trim() !== "" &&
values.repository.trim() !== "" &&
values.branch.trim() !== "" &&
isOptionalShaValid(values.sha) &&
@@ -226,15 +242,26 @@ interface AutomationFormFieldsProps {
values: AutomationFormValues;
onChange: (values: AutomationFormValues) => void;
lockIdAndTarget?: boolean;
+ environments?: Environment[];
+ environmentsLoading?: boolean;
+ environmentsError?: boolean;
}
export function AutomationFormFields({
values,
onChange,
lockIdAndTarget = false,
+ environments = [],
+ environmentsLoading = false,
+ environmentsError = false,
}: AutomationFormFieldsProps) {
const slugTouchedRef = useRef(values.id.length > 0);
const shaValid = isOptionalShaValid(values.sha);
+ const compatibleEnvironments = environments
+ .filter(isCloneBasedEnvironment)
+ .sort((left, right) => left.id.localeCompare(right.id));
+ const selectedEnvironmentMissing = values.environmentId !== ""
+ && !compatibleEnvironments.some((environment) => environment.id === values.environmentId);
function patch(partial: Partial
+ Couldn't load environments. Refresh the page and try again.
+
+ No Docker or Daytona environments are available.{" "}
+
+ Create an environment
+ {" "}
+ before saving this automation.
+
+ This environment is no longer available. Choose another environment before saving.
+
+ Last scheduled run failed: {automation.last_error} +
+ ) : null}