Skip to content

Commit

Permalink
init eph envs (#179)
Browse files Browse the repository at this point in the history
Co-authored-by: Aditya Choudhari <[email protected]>
  • Loading branch information
jsbroks and adityachoudhari26 authored Oct 28, 2024
1 parent 70c090d commit 23a2272
Show file tree
Hide file tree
Showing 14 changed files with 4,471 additions and 196 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type * as SCHEMA from "@ctrlplane/db/schema";
import React from "react";
import _ from "lodash";
import { z } from "zod";

import { Button } from "@ctrlplane/ui/button";
Expand All @@ -11,24 +10,16 @@ import {
FormField,
FormItem,
FormLabel,
FormMessage,
useForm,
} from "@ctrlplane/ui/form";
import { Input } from "@ctrlplane/ui/input";
import { RadioGroup, RadioGroupItem } from "@ctrlplane/ui/radio-group";
import {
defaultCondition,
isEmptyCondition,
releaseCondition,
} from "@ctrlplane/validators/releases";

import { api } from "~/trpc/react";
import { ReleaseConditionRender } from "../release-condition/ReleaseConditionRender";

const schema = z.object({
concurrencyType: z.enum(["all", "some"]),
concurrencyLimit: z.number().min(1, "Must be a positive number"),
releaseFilter: releaseCondition.nullable(),
});

export const DeploymentControl: React.FC<{
Expand All @@ -41,12 +32,8 @@ export const DeploymentControl: React.FC<{

const { id, systemId } = environmentPolicy;
const onSubmit = form.handleSubmit((data) => {
const releaseFilter =
data.releaseFilter != null && isEmptyCondition(data.releaseFilter)
? null
: data.releaseFilter;
updatePolicy
.mutateAsync({ id, data: { ...data, releaseFilter } })
.mutateAsync({ id, data })
.then(() => form.reset(data))
.then(() => utils.environment.policy.byId.invalidate(id))
.then(() => utils.environment.policy.bySystemId.invalidate(systemId));
Expand Down Expand Up @@ -118,28 +105,6 @@ export const DeploymentControl: React.FC<{
)}
/>

<FormField
control={form.control}
name="releaseFilter"
render={({ field: { onChange, value } }) => (
<FormItem>
<FormLabel>Filter</FormLabel>
<FormControl>
<ReleaseConditionRender
condition={value ?? defaultCondition}
onChange={onChange}
/>
</FormControl>
<FormMessage />
{form.formState.isDirty && (
<span className="text-xs text-muted-foreground">
Save to apply
</span>
)}
</FormItem>
)}
/>

<Button
type="submit"
disabled={form.formState.isSubmitting || !form.formState.isDirty}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const schema = z.object({
endTime: z.date(),
}),
),
duration: z.string().refine(isValidDuration, {
rolloutDuration: z.string().refine(isValidDuration, {
message: "Invalid duration pattern",
}),
});
Expand All @@ -78,10 +78,10 @@ export const RolloutAndTiming: React.FC<{
releaseWindows: SCHEMA.EnvironmentPolicyReleaseWindow[];
};
}> = ({ environmentPolicy }) => {
const duration = prettyMilliseconds(environmentPolicy.duration);
const rolloutDuration = prettyMilliseconds(environmentPolicy.rolloutDuration);
const form = useForm({
schema,
defaultValues: { ...environmentPolicy, duration },
defaultValues: { ...environmentPolicy, rolloutDuration },
});

const { fields, append, remove } = useFieldArray({
Expand All @@ -95,10 +95,10 @@ export const RolloutAndTiming: React.FC<{

const { id: policyId, systemId } = environmentPolicy;
const onSubmit = form.handleSubmit(async (data) => {
const { releaseWindows, duration: durationString } = data;
const duration = ms(durationString);
const { releaseWindows, rolloutDuration: durationString } = data;
const rolloutDuration = ms(durationString);
await setPolicyWindows.mutateAsync({ policyId, releaseWindows });
await updatePolicy.mutateAsync({ id: policyId, data: { duration } });
await updatePolicy.mutateAsync({ id: policyId, data: { rolloutDuration } });

form.reset(data);
await utils.environment.policy.byId.invalidate(policyId);
Expand Down Expand Up @@ -235,7 +235,7 @@ export const RolloutAndTiming: React.FC<{

<FormField
control={form.control}
name="duration"
name="rolloutDuration"
render={({ field }) => (
<FormItem className="space-y-4">
<div className="flex flex-col gap-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import type {
EnvironmentPolicyDeployment,
Release,
} from "@ctrlplane/db/schema";
import type { ReleaseCondition } from "@ctrlplane/validators/releases";
import type { NodeProps } from "reactflow";
import { useState } from "react";
import { useParams, useRouter } from "next/navigation";
import { useRouter } from "next/navigation";
import { IconCheck, IconLoader2, IconMinus, IconX } from "@tabler/icons-react";
import { addMilliseconds, isBefore } from "date-fns";
import prettyMilliseconds from "pretty-ms";
Expand All @@ -26,12 +25,7 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from "@ctrlplane/ui/alert-dialog";
import { ColumnOperator } from "@ctrlplane/validators/conditions";
import { JobStatus } from "@ctrlplane/validators/jobs";
import {
ReleaseFilterType,
ReleaseOperator,
} from "@ctrlplane/validators/releases";

import { api } from "~/trpc/react";

Expand Down Expand Up @@ -109,57 +103,6 @@ type PolicyNodeProps = NodeProps<
}
>;

const useEvaluateReleaseFilterCheck = (
check: ReleaseCondition | null,
release: Release,
) => {
const { workspaceSlug, systemSlug, deploymentSlug } = useParams<{
workspaceSlug: string;
systemSlug: string;
deploymentSlug: string;
}>();
const deploymentQ = api.deployment.bySlug.useQuery({
workspaceSlug,
systemSlug,
deploymentSlug,
});
const deployment = deploymentQ.data;
const filter: ReleaseCondition = {
type: ReleaseFilterType.Comparison,
operator: ReleaseOperator.And,
conditions:
check != null
? [
{
type: ReleaseFilterType.Version,
operator: ColumnOperator.Equals,
value: release.version,
},
check,
]
: [],
};

const targetsQ = api.release.list.useQuery(
{ deploymentId: deployment?.id ?? "", filter },
{ enabled: deployment?.id != null && check != null },
);

return {
loading: targetsQ.isLoading,
passing: (targetsQ.data?.total ?? 0) > 0,
};
};

const EvaluateFilterCheck: React.FC<{
passing: boolean;
}> = ({ passing }) => (
<div className="flex items-center gap-2">
{passing ? <Passing /> : <Blocked />}
Release version satisfies filter
</div>
);

const MinSuccessCheck: React.FC<PolicyNodeProps["data"]> = ({
successMinimum,
successType,
Expand Down Expand Up @@ -207,7 +150,10 @@ const MinSuccessCheck: React.FC<PolicyNodeProps["data"]> = ({
};

const GradualRolloutCheck: React.FC<PolicyNodeProps["data"]> = (data) => {
const completeDate = addMilliseconds(data.release.createdAt, data.duration);
const completeDate = addMilliseconds(
data.release.createdAt,
data.rolloutDuration,
);
const [now, setNow] = useState(new Date());
useTimeoutFn(() => setNow(new Date()), 1000);
const completesInMs = completeDate.getTime() - now.getTime();
Expand Down Expand Up @@ -273,38 +219,22 @@ const ApprovalCheck: React.FC<PolicyNodeProps["data"]> = ({ id, release }) => {
};

export const PolicyNode: React.FC<PolicyNodeProps> = ({ data }) => {
const hasFilterCheck = data.releaseFilter != null;
const { loading: isFilterCheckLoading, passing: isFilterCheckPassing } =
useEvaluateReleaseFilterCheck(data.releaseFilter, data.release);

const noMinSuccess = data.successType === "optional";
const noRollout = data.duration === 0;
const noRollout = data.rolloutDuration === 0;
const noApproval = data.approvalRequirement === "automatic";

if (hasFilterCheck && isFilterCheckLoading)
return (
<div
className={cn(
"relative w-[250px] space-y-1 rounded-md border px-2 py-1.5 text-sm",
)}
/>
);

return (
<>
<div
className={cn(
"relative w-[250px] space-y-1 rounded-md border px-2 py-1.5 text-sm",
)}
>
{hasFilterCheck && (
<EvaluateFilterCheck passing={isFilterCheckPassing} />
)}
{!noMinSuccess && <MinSuccessCheck {...data} />}
{!noRollout && <GradualRolloutCheck {...data} />}
{!noApproval && <ApprovalCheck {...data} />}

{!hasFilterCheck && noMinSuccess && noRollout && noApproval && (
{noMinSuccess && noRollout && noApproval && (
<div className="text-muted-foreground">No policy checks.</div>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/router/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const processReleaseJobTriggerWithAdditionalDataRows = (
v[0]!.release.id,
v[0]!.environment.id,
v[0]!.release.createdAt,
v[0]!.environment_policy.duration,
v[0]!.environment_policy.rolloutDuration,
v
.map((r) => r.environment_policy_release_window)
.filter(isPresent),
Expand Down
Loading

0 comments on commit 23a2272

Please sign in to comment.