From 416ebedb4dd9c1d85271e4ff985db7850cb0f08a Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Tue, 10 Sep 2024 17:19:25 -0700 Subject: [PATCH 1/9] fix: Init redeploy --- .../[systemSlug]/deployments/DeployButton.tsx | 39 ++++ .../deployments/DeploymentBarChart.tsx | 40 ++++ .../deployments/ReleaseDropdownMenu.tsx | 93 +++++++++ .../[systemSlug]/deployments/TableCells.tsx | 194 ++++++++---------- .../deployments/TableDeployments.tsx | 39 ++-- .../[systemSlug]/deployments/TableRelease.tsx | 73 +++---- .../[deploymentSlug]/DistroBarChart.tsx | 35 ++++ .../[deploymentSlug]/JobAgentMissingAlert.tsx | 14 +- .../deployments/[deploymentSlug]/page.tsx | 134 +++++------- .../systems/[systemSlug]/deployments/page.tsx | 1 + .../[systemSlug]/deployments/status-color.tsx | 21 ++ packages/api/src/router/release.ts | 11 +- 12 files changed, 435 insertions(+), 259 deletions(-) create mode 100644 apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx create mode 100644 apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeploymentBarChart.tsx create mode 100644 apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx create mode 100644 apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx create mode 100644 apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/status-color.tsx diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx new file mode 100644 index 00000000..19d8f59c --- /dev/null +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx @@ -0,0 +1,39 @@ +"use client"; + +import { useRouter } from "next/navigation"; + +import { cn } from "@ctrlplane/ui"; +import { Button } from "@ctrlplane/ui/button"; + +import { api } from "../../../../../trpc/react"; + +export const DeployButton: React.FC<{ + releaseId: string; + environmentId: string; +}> = ({ releaseId, environmentId }) => { + const deploy = api.release.deploy.toEnvironment.useMutation(); + const router = useRouter(); + + return ( + + ); +}; diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeploymentBarChart.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeploymentBarChart.tsx new file mode 100644 index 00000000..b946c18d --- /dev/null +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeploymentBarChart.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { capitalCase } from "change-case"; +import { + Bar, + BarChart, + Cell, + ResponsiveContainer, + XAxis, + YAxis, +} from "recharts"; + +import { getStatusColor } from "./status-color"; + +export const DeploymentBarChart: React.FC<{ + data: { name: string; count: number }[]; +}> = ({ data }) => { + return ( + + + capitalCase(value as string)} + /> + + + {data.map(({ name }) => ( + + ))} + + + + ); +}; diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx new file mode 100644 index 00000000..6de20390 --- /dev/null +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { TbDotsVertical, TbReload } from "react-icons/tb"; + +import { Badge } from "@ctrlplane/ui/badge"; +import { Button } from "@ctrlplane/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@ctrlplane/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@ctrlplane/ui/dropdown-menu"; + +import { api } from "~/trpc/react"; + +export const ReleaseDropdownMenu: React.FC<{ + release: { + id: string; + name: string; + }; + environment: { + id: string; + name: string; + }; + isReleaseCompleted: boolean; +}> = ({ release, environment, isReleaseCompleted }) => { + const router = useRouter(); + const redeploy = api.release.deploy.toEnvironment.useMutation(); + + return ( + + + + + + + + e.preventDefault()} + className="space-x-2" + > + + Redeploy + + + + + + Redeploy{" "} + + {release.name} + {" "} + to {environment.name}? + + + This will redeploy the release to all targets in the + environment. + + + + + + + + + + + ); +}; diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableCells.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableCells.tsx index c16b4868..fc2f0a87 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableCells.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableCells.tsx @@ -1,14 +1,10 @@ -"use client"; - import type { Deployment, JobConfig, JobExecution, - JobExecutionStatus, Target, } from "@ctrlplane/db/schema"; import Link from "next/link"; -import { useParams } from "next/navigation"; import { capitalCase } from "change-case"; import { format } from "date-fns"; import _ from "lodash"; @@ -23,15 +19,6 @@ import { TbLoader2, TbSettingsX, } from "react-icons/tb"; -import { - Bar, - BarChart, - Cell, - ResponsiveContainer, - XAxis, - YAxis, -} from "recharts"; -import colors from "tailwindcss/colors"; import { isPresent } from "ts-is-present"; import { Badge } from "@ctrlplane/ui/badge"; @@ -42,6 +29,10 @@ import { HoverCardTrigger, } from "@ctrlplane/ui/hover-card"; +import { DeploymentBarChart } from "./DeploymentBarChart"; +import { ReleaseDropdownMenu } from "./ReleaseDropdownMenu"; +import { getStatusColor, statusColor } from "./status-color"; + const ReleaseIcon: React.FC<{ jobConfigs: Array; }> = ({ jobConfigs }) => { @@ -117,26 +108,15 @@ const ReleaseIcon: React.FC<{ ); }; -const statusColor: Record = { - completed: colors.green[400], - cancelled: colors.neutral[400], - skipped: colors.gray[400], - in_progress: colors.blue[400], - action_required: colors.yellow[400], - pending: colors.cyan[400], - failure: colors.red[400], - invalid_job_agent: colors.red[400], - configured: colors.gray[400], - invalid_integration: colors.red[400], - external_run_not_found: colors.red[400], -}; - -const getStatusColor = (status: string) => - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - statusColor[status as JobExecutionStatus | "configured"] ?? colors.gray[400]; +const completedStatus = ["completed", "cancelled", "skipped", "failure"]; export const Release: React.FC<{ name: string; + releaseId: string; + environment: { + id: string; + name: string; + }; activeDeploymentCount?: number; deployedAt: Date; jobConfigs: Array< @@ -146,8 +126,21 @@ export const Release: React.FC<{ deployment?: Deployment | null; } >; + workspaceSlug: string; + systemSlug: string; + deploymentSlug: string; }> = (props) => { - const { name, deployedAt, jobConfigs, activeDeploymentCount } = props; + const { + name, + deployedAt, + jobConfigs, + activeDeploymentCount, + releaseId, + environment, + workspaceSlug, + systemSlug, + deploymentSlug, + } = props; const data = _.chain(jobConfigs) .groupBy((r) => r.jobExecution?.status ?? "configured") .entries() @@ -161,86 +154,73 @@ export const Release: React.FC<{ [d.jobExecution, d.target, d.jobExecution?.message].every(isPresent), ); - const params = useParams<{ - workspaceSlug: string; - systemSlug: string; - deploymentSlug: string; - }>(); - const firstJobConfig = jobConfigs.at(0); + const isReleaseCompleted = jobConfigs.every((d) => + completedStatus.includes(d.jobExecution?.status ?? "configured"), + ); + return ( - - - - -
-
- {name} - {activeDeploymentCount != null && activeDeploymentCount > 0 && ( - - {activeDeploymentCount} - - )} -
-
- {format(deployedAt, "MMM d, hh:mm aa")} +
+ + + + +
+
+ {name} + {activeDeploymentCount != null && activeDeploymentCount > 0 && ( + + {activeDeploymentCount} + + )} +
+
+ {format(deployedAt, "MMM d, hh:mm aa")} +
-
- - - -
- - - capitalCase(value as string)} - /> - - - {data.map(({ name }) => ( - - ))} - - - - {configuredWithMessages.length > 0 && ( - - {configuredWithMessages.map((d) => ( -
-
- - {d.target.name} + + + +
+ + {configuredWithMessages.length > 0 && ( + + {configuredWithMessages.map((d) => ( +
+
+ + {d.target.name} +
+ {d.jobExecution?.message != null && + d.jobExecution.message !== "" && ( +
+ {capitalCase(d.jobExecution.status)} —{" "} + {d.jobExecution.message} +
+ )}
- {d.jobExecution?.message != null && - d.jobExecution.message !== "" && ( -
- {capitalCase(d.jobExecution.status)} —{" "} - {d.jobExecution.message} -
- )} -
- ))} - - )} -
- - + ))} + + )} +
+ + + + +
); }; diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableDeployments.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableDeployments.tsx index 916156aa..0130fd68 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableDeployments.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableDeployments.tsx @@ -1,8 +1,5 @@ -"use client"; - import type { Deployment, Environment, Target } from "@ctrlplane/db/schema"; import Link from "next/link"; -import { useParams } from "next/navigation"; import { TbCircleFilled, TbRocket, TbTerminal2 } from "react-icons/tb"; import { isPresent } from "ts-is-present"; @@ -11,7 +8,7 @@ import { Badge } from "@ctrlplane/ui/badge"; import { Button } from "@ctrlplane/ui/button"; import { DeploymentOptionsDropdown } from "~/app/[workspaceSlug]/_components/DeploymentOptionsDropdown"; -import { api } from "~/trpc/react"; +import { api } from "~/trpc/server"; import { Release } from "./TableCells"; const Tb: React.FC<{ children?: React.ReactNode; className?: string }> = ({ @@ -32,8 +29,9 @@ const EnvTb: React.FC<{ isFirst?: boolean; isLast?: boolean; environment: Environment & { targets: Target[] }; -}> = ({ environment: env, isFirst, isLast }) => { - const params = useParams<{ workspaceSlug: string; systemSlug: string }>(); + workspaceSlug: string; + systemSlug: string; +}> = ({ environment: env, isFirst, isLast, workspaceSlug, systemSlug }) => { return (
{env.name} @@ -62,17 +60,25 @@ const EnvTb: React.FC<{ }; const ReleaseCell: React.FC<{ + workspaceSlug: string; + systemSlug: string; environment: Environment & { targets: Target[] }; release: { id: string; version: string; createdAt: Date } | null; deployment: Deployment; -}> = ({ release, environment: env, deployment }) => { - const jobConfigs = api.job.config.byDeploymentAndEnvironment.useQuery({ +}> = async ({ + release, + environment: env, + deployment, + workspaceSlug, + systemSlug, +}) => { + const jobConfigs = await api.job.config.byDeploymentAndEnvironment({ environmentId: env.id, deploymentId: deployment.id, }); const hasTargets = env.targets.length > 0; const hasRelease = release != null; - const jc = (jobConfigs.data ?? []) + const jc = jobConfigs .filter( (jobConfig) => isPresent(jobConfig.environmentId) && @@ -90,9 +96,14 @@ const ReleaseCell: React.FC<{ <> {hasRelease && hasTargets && ( )} @@ -115,8 +126,8 @@ const DeploymentTable: React.FC<{ latestRelease: { id: string; version: string; createdAt: Date } | null; } >; -}> = ({ systemSlug, deployments, environments }) => { - const { workspaceSlug } = useParams<{ workspaceSlug: string }>(); + workspaceSlug: string; +}> = ({ systemSlug, deployments, environments, workspaceSlug }) => { return (
@@ -129,6 +140,8 @@ const DeploymentTable: React.FC<{ environment={env} isFirst={idx === 0} isLast={idx === environments.length - 1} + workspaceSlug={workspaceSlug} + systemSlug={systemSlug} /> ))} @@ -181,6 +194,8 @@ const DeploymentTable: React.FC<{ release={r.latestRelease} environment={env} deployment={r} + workspaceSlug={workspaceSlug} + systemSlug={systemSlug} /> ); diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableRelease.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableRelease.tsx index b939f920..8699b6d2 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableRelease.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableRelease.tsx @@ -1,5 +1,3 @@ -"use client"; - import type { Deployment, JobConfig, @@ -8,7 +6,6 @@ import type { } from "@ctrlplane/db/schema"; import type { ReleaseType } from "semver"; import Link from "next/link"; -import { useParams, useRouter } from "next/navigation"; import _ from "lodash"; import { parse, valid } from "semver"; @@ -17,7 +14,8 @@ import { Badge } from "@ctrlplane/ui/badge"; import { Button } from "@ctrlplane/ui/button"; import { CreateReleaseDialog } from "~/app/[workspaceSlug]/_components/CreateRelease"; -import { api } from "~/trpc/react"; +import { api } from "~/trpc/server"; +import { DeployButton } from "./DeployButton"; import { Release } from "./TableCells"; const Tb: React.FC<{ children?: React.ReactNode; className?: string }> = ({ @@ -92,20 +90,20 @@ export const ReleaseTable: React.FC<{ name: string; targets: { id: string }[]; }[]; -}> = ({ deployment, jobConfigs, releases, environments }) => { + workspaceSlug: string; + systemSlug: string; +}> = async ({ + deployment, + jobConfigs, + releases, + environments, + workspaceSlug, + systemSlug, +}) => { const firstRelease = releases.at(0); - const deploy = api.release.deploy.toEnvironment.useMutation(); - const router = useRouter(); - const distrubtion = api.deployment.distrubtionById.useQuery(deployment.id, { - refetchInterval: 2_000, - }); + const distrubtion = await api.deployment.distrubtionById(deployment.id); const releaseIds = releases.map((r) => r.id); - const blockedEnvByRelease = api.release.blockedEnvironments.useQuery( - releaseIds, - { enabled: releaseIds.length > 0 }, - ); - - const params = useParams<{ workspaceSlug: string; systemSlug: string }>(); + const blockedEnvByRelease = await api.release.blockedEnvironments(releaseIds); return (
@@ -131,7 +129,7 @@ export const ReleaseTable: React.FC<{ )} >
{env.name} @@ -150,7 +148,7 @@ export const ReleaseTable: React.FC<{
{releases.map((r, releaseIdx) => { - const blockedEnvs = blockedEnvByRelease.data?.[r.id] ?? []; + const blockedEnvs = blockedEnvByRelease[r.id] ?? []; return ( - + @@ -159,8 +123,12 @@ export default function DeploymentPage({ - {deployment.data?.jobAgentId == null ? ( - + {deployment.jobAgentId == null ? ( + ) : (
t.releaseId === r.id && t.environmentId === env.id, ); - const hasActiveDeployment = - distrubtion.data?.filter( - (d) => - d.release.id === r.id && - d.jobConfig.environmentId === env.id, - ).length ?? 0; + const hasActiveDeployment = distrubtion.filter( + (d) => + d.release.id === r.id && + d.jobConfig.environmentId === env.id, + ).length; const hasTargets = env.targets.length > 0; const hasRelease = environmentReleaseJobConfigs.length > 0; const hasJobAgent = deployment.jobAgentId != null; @@ -192,7 +189,7 @@ export const ReleaseTable: React.FC<{ {showRelease && ( { - e.preventDefault(); - - await deploy.mutateAsync({ - environmentId: env.id, - releaseId: r.id, - }); - - router.refresh(); - }} - disabled={deploy.isPending} - > - Deploy - + )} {!canDeploy && !hasRelease && ( diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx new file mode 100644 index 00000000..8d43c0e2 --- /dev/null +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx @@ -0,0 +1,35 @@ +"use client"; + +import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from "recharts"; + +export const DistroBarChart: React.FC<{ + distro: Array<{ + version: string; + count: number; + }>; + distroPadding: Array<{ + version: string; + count: number; + }>; +}> = ({ distro, distroPadding }) => { + return ( + + + + + + + + ); +}; diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/JobAgentMissingAlert.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/JobAgentMissingAlert.tsx index 564be1b1..9be27ee9 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/JobAgentMissingAlert.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/JobAgentMissingAlert.tsx @@ -1,15 +1,13 @@ import Link from "next/link"; -import { useParams } from "next/navigation"; import { Alert, AlertDescription, AlertTitle } from "@ctrlplane/ui/alert"; import { Button } from "@ctrlplane/ui/button"; -export const JobAgentMissingAlert: React.FC = () => { - const params = useParams<{ - workspaceSlug: string; - systemSlug: string; - deploymentSlug: string; - }>(); +export const JobAgentMissingAlert: React.FC<{ + workspaceSlug: string; + systemSlug: string; + deploymentSlug: string; +}> = ({ workspaceSlug, systemSlug, deploymentSlug }) => { return ( @@ -21,7 +19,7 @@ export const JobAgentMissingAlert: React.FC = () => { Slug {deployment.data?.slug}{deployment.slug}
System - {system.data?.name ?? system.data?.slug} + {system.name}
@@ -169,14 +137,14 @@ export default function DeploymentPage({ Type - +
- {capitalCase(deployment.data.agent?.type ?? "")} + {capitalCase(deployment.agent?.type ?? "")}
Name {deployment.data.agent?.name}{deployment.agent?.name}
diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/page.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/page.tsx index e7fc4cee..9f029350 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/page.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/page.tsx @@ -30,6 +30,7 @@ export default async function SystemDeploymentsPage({ deployments={deployments} environments={environments} systemSlug={params.systemSlug} + workspaceSlug={params.workspaceSlug} />
)} diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/status-color.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/status-color.tsx new file mode 100644 index 00000000..7388d6e7 --- /dev/null +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/status-color.tsx @@ -0,0 +1,21 @@ +import colors from "tailwindcss/colors"; + +import type { JobExecutionStatus } from "@ctrlplane/db/schema"; + +export const statusColor: Record = { + completed: colors.green[400], + cancelled: colors.neutral[400], + skipped: colors.gray[400], + in_progress: colors.blue[400], + action_required: colors.yellow[400], + pending: colors.cyan[400], + failure: colors.red[400], + invalid_job_agent: colors.red[400], + configured: colors.gray[400], + invalid_integration: colors.red[400], + external_run_not_found: colors.red[400], +}; + +export const getStatusColor = (status: string) => + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + statusColor[status as JobExecutionStatus | "configured"] ?? colors.gray[400]; diff --git a/packages/api/src/router/release.ts b/packages/api/src/router/release.ts index 19b7c0e2..04230c2b 100644 --- a/packages/api/src/router/release.ts +++ b/packages/api/src/router/release.ts @@ -173,10 +173,13 @@ export const releaseRouter = createTRPCRouter({ blockedEnvironments: protectedProcedure .meta({ authorizationCheck: ({ canUser, input }) => - canUser.perform(Permission.ReleaseGet).on( - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - input.map((releaseId: any) => ({ type: "release", id: releaseId })), - ), + Promise.all( + (input as string[]).map((releaseId) => + canUser + .perform(Permission.ReleaseGet) + .on({ type: "release", id: releaseId }), + ), + ).then((results) => results.every(Boolean)), }) .input(z.array(z.string().uuid())) .query(async ({ input }) => { From 911924cc10220574b3ac5c2846acb7405b7d0324 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Tue, 10 Sep 2024 17:27:29 -0700 Subject: [PATCH 2/9] clean --- .../[systemSlug]/deployments/DeployButton.tsx | 14 +++--- .../deployments/DeploymentBarChart.tsx | 46 +++++++++---------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx index 19d8f59c..15f21976 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx @@ -21,15 +21,15 @@ export const DeployButton: React.FC<{ )} variant="outline" size="sm" - onClick={async (e) => { + onClick={(e) => { e.preventDefault(); - await deploy.mutateAsync({ - environmentId, - releaseId, - }); - - router.refresh(); + deploy + .mutateAsync({ + environmentId, + releaseId, + }) + .then(() => router.refresh()); }} disabled={deploy.isPending} > diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeploymentBarChart.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeploymentBarChart.tsx index b946c18d..559eda24 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeploymentBarChart.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeploymentBarChart.tsx @@ -14,27 +14,25 @@ import { getStatusColor } from "./status-color"; export const DeploymentBarChart: React.FC<{ data: { name: string; count: number }[]; -}> = ({ data }) => { - return ( - - - capitalCase(value as string)} - /> - - - {data.map(({ name }) => ( - - ))} - - - - ); -}; +}> = ({ data }) => ( + + + capitalCase(value as string)} + /> + + + {data.map(({ name }) => ( + + ))} + + + +); From d3a8b55c70f74aa4c01c1a303bdd5a8bb2d3c09f Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Tue, 10 Sep 2024 17:28:11 -0700 Subject: [PATCH 3/9] clean --- .../[deploymentSlug]/DistroBarChart.tsx | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx index 8d43c0e2..d71e35a4 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx @@ -11,25 +11,23 @@ export const DistroBarChart: React.FC<{ version: string; count: number; }>; -}> = ({ distro, distroPadding }) => { - return ( - - - - - - - - ); -}; +}> = ({ distro, distroPadding }) => ( + + + + + + + +); From 472791f25d17cd9b727950e546e931921900f2cb Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Tue, 10 Sep 2024 22:45:56 -0700 Subject: [PATCH 4/9] live queries --- .../[systemSlug]/deployments/TableRelease.tsx | 87 +++++++++++-------- .../[deploymentSlug]/DistroBarChart.tsx | 76 ++++++++++------ .../deployments/[deploymentSlug]/page.tsx | 41 ++------- 3 files changed, 104 insertions(+), 100 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableRelease.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableRelease.tsx index 8699b6d2..75f8ae29 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableRelease.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableRelease.tsx @@ -1,20 +1,19 @@ -import type { - Deployment, - JobConfig, - JobExecution, - Target, -} from "@ctrlplane/db/schema"; +"use client"; + +import type { Deployment } from "@ctrlplane/db/schema"; import type { ReleaseType } from "semver"; import Link from "next/link"; +import { useParams } from "next/navigation"; import _ from "lodash"; import { parse, valid } from "semver"; +import { isPresent } from "ts-is-present"; import { cn } from "@ctrlplane/ui"; import { Badge } from "@ctrlplane/ui/badge"; import { Button } from "@ctrlplane/ui/button"; import { CreateReleaseDialog } from "~/app/[workspaceSlug]/_components/CreateRelease"; -import { api } from "~/trpc/server"; +import { api } from "~/trpc/react"; import { DeployButton } from "./DeployButton"; import { Release } from "./TableCells"; @@ -78,32 +77,47 @@ const SemverHelperButtons: React.FC<{ export const ReleaseTable: React.FC<{ deployment: Deployment; - jobConfigs: Array< - JobConfig & { - jobExecution: JobExecution | null; - target: Target; - } - >; - releases: { id: string; version: string; createdAt: Date }[]; environments: { id: string; name: string; targets: { id: string }[]; }[]; - workspaceSlug: string; - systemSlug: string; -}> = async ({ - deployment, - jobConfigs, - releases, - environments, - workspaceSlug, - systemSlug, -}) => { - const firstRelease = releases.at(0); - const distrubtion = await api.deployment.distrubtionById(deployment.id); - const releaseIds = releases.map((r) => r.id); - const blockedEnvByRelease = await api.release.blockedEnvironments(releaseIds); +}> = ({ deployment, environments }) => { + const { workspaceSlug, systemSlug } = useParams<{ + workspaceSlug: string; + systemSlug: string; + }>(); + const jobConfigsQuery = api.job.config.byDeploymentId.useQuery( + deployment.id, + { refetchInterval: 2_000 }, + ); + + const releases = api.release.list.useQuery( + { deploymentId: deployment.id }, + { refetchInterval: 10_000 }, + ); + + const jobConfigs = (jobConfigsQuery.data ?? []) + .filter( + (jobConfig) => + isPresent(jobConfig.environmentId) && + isPresent(jobConfig.releaseId) && + isPresent(jobConfig.targetId), + ) + .map((jobConfig) => ({ + ...jobConfig, + environmentId: jobConfig.environmentId!, + target: jobConfig.target!, + releaseId: jobConfig.releaseId!, + })); + + const firstRelease = releases.data?.at(0); + const distrubtion = api.deployment.distrubtionById.useQuery(deployment.id, { + refetchInterval: 2_000, + }); + const releaseIds = releases.data?.map((r) => r.id) ?? []; + const blockedEnvByRelease = + api.release.blockedEnvironments.useQuery(releaseIds); return (
@@ -147,8 +161,8 @@ export const ReleaseTable: React.FC<{ - {releases.map((r, releaseIdx) => { - const blockedEnvs = blockedEnvByRelease[r.id] ?? []; + {releases.data?.map((r, releaseIdx) => { + const blockedEnvs = blockedEnvByRelease.data?.[r.id] ?? []; return ( {r.version} @@ -166,11 +180,12 @@ export const ReleaseTable: React.FC<{ (t) => t.releaseId === r.id && t.environmentId === env.id, ); - const hasActiveDeployment = distrubtion.filter( - (d) => - d.release.id === r.id && - d.jobConfig.environmentId === env.id, - ).length; + const hasActiveDeployment = + distrubtion.data?.filter( + (d) => + d.release.id === r.id && + d.jobConfig.environmentId === env.id, + ).length ?? 0; const hasTargets = env.targets.length > 0; const hasRelease = environmentReleaseJobConfigs.length > 0; const hasJobAgent = deployment.jobAgentId != null; diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx index d71e35a4..2f7b4ca6 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx @@ -1,33 +1,53 @@ "use client"; +import _ from "lodash"; import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from "recharts"; +import { api } from "~/trpc/react"; + export const DistroBarChart: React.FC<{ - distro: Array<{ - version: string; - count: number; - }>; - distroPadding: Array<{ - version: string; - count: number; - }>; -}> = ({ distro, distroPadding }) => ( - - - - - - - -); + deploymentId: string; + showPreviousReleaseDistro: number; +}> = ({ deploymentId, showPreviousReleaseDistro }) => { + const releases = api.release.list.useQuery( + { deploymentId }, + { refetchInterval: 10_000 }, + ); + const distribution = api.deployment.distrubtionById.useQuery(deploymentId, { + refetchInterval: 2_000, + }); + + const distro = _.chain(releases.data ?? []) + .map((r) => ({ + version: r.version, + count: (distribution.data ?? []).filter((d) => d.release.id === r.id) + .length, + })) + .take(showPreviousReleaseDistro) + .value(); + const distroPadding = _.range( + 0, + showPreviousReleaseDistro - distro.length, + ).map(() => ({ version: "", count: 0 })); + + return ( + + + + + + + + ); +}; diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/page.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/page.tsx index 9bafa22d..f470fd27 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/page.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/page.tsx @@ -3,7 +3,6 @@ import { notFound } from "next/navigation"; import { capitalCase } from "change-case"; import _ from "lodash"; import { TbInfoCircle, TbLink } from "react-icons/tb"; -import { isPresent } from "ts-is-present"; import { Card } from "@ctrlplane/ui/card"; import { @@ -26,23 +25,9 @@ export default async function DeploymentPage({ const environments = await api.environment.bySystemId(system.id); const deployment = await api.deployment.bySlug(params); if (deployment == null) return notFound(); - const releases = await api.release.list({ deploymentId: deployment.id }); - const jobConfigs = await api.job.config.byDeploymentId(deployment.id); - const distrubtion = await api.deployment.distrubtionById(deployment.id); const showPreviousReleaseDistro = 30; - const distro = _.chain(releases) - .map((r) => ({ - version: r.version, - count: distrubtion.filter((d) => d.release.id === r.id).length, - })) - .take(showPreviousReleaseDistro) - .value(); - const distroPadding = _.range( - 0, - showPreviousReleaseDistro - distro.length, - ).map(() => ({ version: "", count: 0 })); return ( @@ -52,31 +37,15 @@ export default async function DeploymentPage({ Distrubtion of the last {showPreviousReleaseDistro} releases across all targets
- +
- - isPresent(jobConfig.environmentId) && - isPresent(jobConfig.releaseId) && - isPresent(jobConfig.targetId), - ) - .map((jobConfig) => ({ - ...jobConfig, - environmentId: jobConfig.environmentId!, - target: jobConfig.target!, - releaseId: jobConfig.releaseId!, - }))} - releases={releases} - environments={environments} - workspaceSlug={params.workspaceSlug} - systemSlug={system.slug} - /> +
From 7527ada395e49655fb611773f80046f0fe47d8c8 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Tue, 10 Sep 2024 22:54:54 -0700 Subject: [PATCH 5/9] clean --- .../systems/[systemSlug]/deployments/DeployButton.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx index 15f21976..b109d61b 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/DeployButton.tsx @@ -5,7 +5,7 @@ import { useRouter } from "next/navigation"; import { cn } from "@ctrlplane/ui"; import { Button } from "@ctrlplane/ui/button"; -import { api } from "../../../../../trpc/react"; +import { api } from "~/trpc/react"; export const DeployButton: React.FC<{ releaseId: string; @@ -21,16 +21,14 @@ export const DeployButton: React.FC<{ )} variant="outline" size="sm" - onClick={(e) => { - e.preventDefault(); - + onClick={() => deploy .mutateAsync({ environmentId, releaseId, }) - .then(() => router.refresh()); - }} + .then(() => router.refresh()) + } disabled={deploy.isPending} > Deploy From 5fe0aa7ba4a22b4a6477b2de74b73d7ec2c47615 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Tue, 10 Sep 2024 23:49:27 -0700 Subject: [PATCH 6/9] force release --- .../deployments/ReleaseDropdownMenu.tsx | 192 +++++++++++++----- .../deployments/TableDeployments.tsx | 2 +- packages/api/src/router/release.ts | 54 ++++- packages/job-dispatch/src/job-config.ts | 2 +- packages/job-dispatch/src/job-dispatch.ts | 2 +- 5 files changed, 191 insertions(+), 61 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx index 6de20390..deb3c695 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx @@ -1,10 +1,21 @@ "use client"; import { useRouter } from "next/navigation"; -import { TbDotsVertical, TbReload } from "react-icons/tb"; +import { TbAlertTriangle, TbDotsVertical, TbReload } from "react-icons/tb"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@ctrlplane/ui/alert-dialog"; import { Badge } from "@ctrlplane/ui/badge"; -import { Button } from "@ctrlplane/ui/button"; +import { Button, buttonVariants } from "@ctrlplane/ui/button"; import { Dialog, DialogContent, @@ -23,7 +34,7 @@ import { import { api } from "~/trpc/react"; -export const ReleaseDropdownMenu: React.FC<{ +const RedeployReleaseDialog: React.FC<{ release: { id: string; name: string; @@ -32,62 +43,133 @@ export const ReleaseDropdownMenu: React.FC<{ id: string; name: string; }; - isReleaseCompleted: boolean; -}> = ({ release, environment, isReleaseCompleted }) => { + children: React.ReactNode; +}> = ({ release, environment, children }) => { const router = useRouter(); const redeploy = api.release.deploy.toEnvironment.useMutation(); return ( - - - - - - - - e.preventDefault()} - className="space-x-2" - > - - Redeploy - - - - - - Redeploy{" "} - - {release.name} - {" "} - to {environment.name}? - - - This will redeploy the release to all targets in the - environment. - - + + {children} + + + + Redeploy{" "} + + {release.name} + {" "} + to {environment.name}? + + + This will redeploy the release to all targets in the environment. + + - - - - - - - + + + + + ); }; + +const ForceReleaseDialog: React.FC<{ + release: { + id: string; + name: string; + }; + environment: { + id: string; + name: string; + }; + children: React.ReactNode; +}> = ({ release, environment, children }) => { + const forceDeploy = api.release.deploy.toEnvironment.useMutation(); + const router = useRouter(); + return ( + + {children} + + + + Force release {release.name} to {environment.name}? + + + This will force the release to be deployed to all targets in the + environment regardless of any policies set on the environment. + + + + Cancel +
+ + forceDeploy + .mutateAsync({ + environmentId: environment.id, + releaseId: release.id, + isForcedRelease: true, + }) + .then(() => router.refresh()) + } + > + Force deploy + + + + + ); +}; + +export const ReleaseDropdownMenu: React.FC<{ + release: { + id: string; + name: string; + }; + environment: { + id: string; + name: string; + }; + isReleaseCompleted: boolean; +}> = ({ release, environment, isReleaseCompleted }) => ( + + + + + + + e.preventDefault()} + className="space-x-2" + > + + Redeploy + + + + e.preventDefault()} + className="space-x-2" + > + + Force deploy + + + + +); diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableDeployments.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableDeployments.tsx index 0130fd68..5722d022 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableDeployments.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableDeployments.tsx @@ -184,7 +184,7 @@ const DeploymentTable: React.FC<{ { + const cancelPreviousJobExecutions = async ( + tx: Tx, + jobConfigs: JobConfig[], + ) => + tx + .select() + .from(jobConfig) + .where( + and( + eq(jobConfig.releaseId, input.releaseId), + eq(jobConfig.environmentId, input.environmentId), + notInArray( + jobConfig.id, + jobConfigs.map((j) => j.id), + ), + ), + ) + .then((existingJobConfigs) => + createJobExecutions(tx, existingJobConfigs, "cancelled").then( + () => {}, + ), + ); + const jobConfigs = await createJobConfigs(ctx.db, "redeploy") .causedById(ctx.session.user.id) .environments([input.environmentId]) .releases([input.releaseId]) .filter(isPassingReleaseSequencingCancelPolicy) + .then( + input.isForcedRelease + ? cancelPreviousJobExecutions + : createJobExecutionApprovals, + ) .insert(); await dispatchJobConfigs(ctx.db) .jobConfigs(jobConfigs) - .filter(isPassingAllPolicies) + .filter( + input.isForcedRelease ? () => jobConfigs : isPassingAllPolicies, + ) .then(cancelOldJobConfigsOnJobDispatch) .dispatch(); diff --git a/packages/job-dispatch/src/job-config.ts b/packages/job-dispatch/src/job-config.ts index 51a08e64..21c5c774 100644 --- a/packages/job-dispatch/src/job-config.ts +++ b/packages/job-dispatch/src/job-config.ts @@ -20,7 +20,7 @@ type FilterFunc = ( insertJobConfigs: JobConfigInsert[], ) => Promise; -type ThenFunc = (tx: Tx, jobConfigs: JobConfig[]) => Promise; +type ThenFunc = (tx: Tx, jobConfigs: JobConfig[]) => Promise | void; export const createJobConfigs = (tx: Tx, type: JobConfigType) => new JobConfigBuilder(tx, type); diff --git a/packages/job-dispatch/src/job-dispatch.ts b/packages/job-dispatch/src/job-dispatch.ts index ce317e70..e3bd02af 100644 --- a/packages/job-dispatch/src/job-dispatch.ts +++ b/packages/job-dispatch/src/job-dispatch.ts @@ -9,7 +9,7 @@ import { dispatchJobExecutionsQueue } from "./queue.js"; export type DispatchFilterFunc = ( db: Tx, jobConfigs: JobConfig[], -) => Promise; +) => Promise | JobConfig[]; type ThenFunc = (tx: Tx, jobConfigs: JobConfig[]) => Promise; From 3fd1f01c5207930effa56587422d0efdd42a162d Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Wed, 11 Sep 2024 16:25:22 -0700 Subject: [PATCH 7/9] format --- .../systems/[systemSlug]/deployments/status-color.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/status-color.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/status-color.tsx index 7388d6e7..105ec86d 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/status-color.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/status-color.tsx @@ -1,6 +1,5 @@ -import colors from "tailwindcss/colors"; - import type { JobExecutionStatus } from "@ctrlplane/db/schema"; +import colors from "tailwindcss/colors"; export const statusColor: Record = { completed: colors.green[400], From 068c6255951435ee89dbc88779587db3f24db824 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Wed, 11 Sep 2024 16:32:23 -0700 Subject: [PATCH 8/9] clean --- packages/api/src/router/release.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/api/src/router/release.ts b/packages/api/src/router/release.ts index 77b7e4d9..3b8b59c4 100644 --- a/packages/api/src/router/release.ts +++ b/packages/api/src/router/release.ts @@ -221,13 +221,12 @@ export const releaseRouter = createTRPCRouter({ blockedEnvironments: protectedProcedure .meta({ authorizationCheck: ({ canUser, input }) => - Promise.all( - (input as string[]).map((releaseId) => - canUser - .perform(Permission.ReleaseGet) - .on({ type: "release", id: releaseId }), - ), - ).then((results) => results.every(Boolean)), + canUser.perform(Permission.ReleaseGet).on( + ...(input as string[]).map((t) => ({ + type: "release" as const, + id: t, + })), + ), }) .input(z.array(z.string().uuid())) .query(async ({ input }) => { From 44f0afb7c3b0cb7cc088f0465992ed11b70f8ada Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Wed, 11 Sep 2024 18:59:26 -0700 Subject: [PATCH 9/9] clean --- .../deployments/ReleaseDropdownMenu.tsx | 30 ++++--------------- .../[systemSlug]/deployments/TableCells.tsx | 5 +--- .../[deploymentSlug]/DistroBarChart.tsx | 7 ++--- packages/job-dispatch/src/job-config.ts | 2 +- 4 files changed, 11 insertions(+), 33 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx index deb3c695..6a294dde 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/ReleaseDropdownMenu.tsx @@ -35,14 +35,8 @@ import { import { api } from "~/trpc/react"; const RedeployReleaseDialog: React.FC<{ - release: { - id: string; - name: string; - }; - environment: { - id: string; - name: string; - }; + release: { id: string; name: string }; + environment: { id: string; name: string }; children: React.ReactNode; }> = ({ release, environment, children }) => { const router = useRouter(); @@ -85,14 +79,8 @@ const RedeployReleaseDialog: React.FC<{ }; const ForceReleaseDialog: React.FC<{ - release: { - id: string; - name: string; - }; - environment: { - id: string; - name: string; - }; + release: { id: string; name: string }; + environment: { id: string; name: string }; children: React.ReactNode; }> = ({ release, environment, children }) => { const forceDeploy = api.release.deploy.toEnvironment.useMutation(); @@ -134,14 +122,8 @@ const ForceReleaseDialog: React.FC<{ }; export const ReleaseDropdownMenu: React.FC<{ - release: { - id: string; - name: string; - }; - environment: { - id: string; - name: string; - }; + release: { id: string; name: string }; + environment: { id: string; name: string }; isReleaseCompleted: boolean; }> = ({ release, environment, isReleaseCompleted }) => ( diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableCells.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableCells.tsx index fc2f0a87..c00c2a0e 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableCells.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/TableCells.tsx @@ -113,10 +113,7 @@ const completedStatus = ["completed", "cancelled", "skipped", "failure"]; export const Release: React.FC<{ name: string; releaseId: string; - environment: { - id: string; - name: string; - }; + environment: { id: string; name: string }; activeDeploymentCount?: number; deployedAt: Date; jobConfigs: Array< diff --git a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx index 2f7b4ca6..7563de36 100644 --- a/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/systems/[systemSlug]/deployments/[deploymentSlug]/DistroBarChart.tsx @@ -25,10 +25,9 @@ export const DistroBarChart: React.FC<{ })) .take(showPreviousReleaseDistro) .value(); - const distroPadding = _.range( - 0, - showPreviousReleaseDistro - distro.length, - ).map(() => ({ version: "", count: 0 })); + const distroPadding = _.range(showPreviousReleaseDistro - distro.length).map( + () => ({ version: "", count: 0 }), + ); return ( diff --git a/packages/job-dispatch/src/job-config.ts b/packages/job-dispatch/src/job-config.ts index 21c5c774..51a08e64 100644 --- a/packages/job-dispatch/src/job-config.ts +++ b/packages/job-dispatch/src/job-config.ts @@ -20,7 +20,7 @@ type FilterFunc = ( insertJobConfigs: JobConfigInsert[], ) => Promise; -type ThenFunc = (tx: Tx, jobConfigs: JobConfig[]) => Promise | void; +type ThenFunc = (tx: Tx, jobConfigs: JobConfig[]) => Promise; export const createJobConfigs = (tx: Tx, type: JobConfigType) => new JobConfigBuilder(tx, type);