ensure sync directory exist

This commit is contained in:
mbecker20
2024-06-07 21:02:28 -07:00
parent 5eacb7191b
commit 078ba59002
15 changed files with 135 additions and 14 deletions

View File

@@ -1,3 +1,5 @@
use std::fs;
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use monitor_client::entities::{ use monitor_client::entities::{
sync::ResourceSync, to_monitor_name, toml::ResourcesToml, sync::ResourceSync, to_monitor_name, toml::ResourcesToml,
@@ -32,6 +34,9 @@ pub async fn get_remote_resources(
.transpose()? .transpose()?
.cloned(); .cloned();
fs::create_dir_all(&config.sync_directory)
.context("failed to create sync directory")?;
let mut logs = let mut logs =
git::clone(clone_args, &config.sync_directory, github_token) git::clone(clone_args, &config.sync_directory, github_token)
.await .await

View File

@@ -217,7 +217,7 @@ export const AccountSelector = ({
> >
<SelectTrigger <SelectTrigger
className="w-full lg:w-[200px] max-w-[50%]" className="w-full lg:w-[200px] max-w-[50%]"
disabled={disabled || !id} disabled={disabled}
> >
<SelectValue placeholder={placeholder} /> <SelectValue placeholder={placeholder} />
</SelectTrigger> </SelectTrigger>

View File

@@ -49,7 +49,11 @@ export const ResourceDescription = ({
const inv = useInvalidate(); const inv = useInvalidate();
const key = const key =
type === "ServerTemplate" ? "server_template" : type.toLowerCase(); type === "ServerTemplate"
? "server_template"
: type === "ResourceSync"
? "sync"
: type.toLowerCase();
const resource = useRead(`Get${type}`, { const resource = useRead(`Get${type}`, {
[key]: id, [key]: id,
@@ -244,10 +248,12 @@ export const CopyResource = ({
export const NewResource = ({ export const NewResource = ({
type, type,
readable_type,
server_id, server_id,
build_id, build_id,
}: { }: {
type: UsableResource; type: UsableResource;
readable_type?: string;
server_id?: string; server_id?: string;
build_id?: string; build_id?: string;
}) => { }) => {
@@ -255,7 +261,11 @@ export const NewResource = ({
const { mutateAsync } = useWrite(`Create${type}`); const { mutateAsync } = useWrite(`Create${type}`);
const [name, setName] = useState(""); const [name, setName] = useState("");
const type_display = const type_display =
type === "ServerTemplate" ? "server-template" : type.toLowerCase(); type === "ServerTemplate"
? "server-template"
: type === "ResourceSync"
? "resource-sync"
: type.toLowerCase();
const config = const config =
type === "Deployment" type === "Deployment"
? { ? {
@@ -267,7 +277,7 @@ export const NewResource = ({
: {}; : {};
return ( return (
<NewLayout <NewLayout
entityType={type} entityType={readable_type ?? type}
onSuccess={async () => { onSuccess={async () => {
const id = (await mutateAsync({ name, config }))._id?.$oid!; const id = (await mutateAsync({ name, config }))._id?.$oid!;
nav(`/${usableResourcePath(type)}/${id}`); nav(`/${usableResourcePath(type)}/${id}`);
@@ -276,7 +286,7 @@ export const NewResource = ({
onOpenChange={() => setName("")} onOpenChange={() => setName("")}
> >
<div className="grid md:grid-cols-2 items-center"> <div className="grid md:grid-cols-2 items-center">
{type} Name {readable_type ?? type} Name
<Input <Input
placeholder={`${type_display}-name`} placeholder={`${type_display}-name`}
value={name} value={name}
@@ -296,7 +306,11 @@ export const DeleteResource = ({
}) => { }) => {
const nav = useNavigate(); const nav = useNavigate();
const key = const key =
type === "ServerTemplate" ? "server_template" : type.toLowerCase(); type === "ServerTemplate"
? "server_template"
: type === "ResourceSync"
? "sync"
: type.toLowerCase();
const resource = useRead(`Get${type}`, { const resource = useRead(`Get${type}`, {
[key]: id, [key]: id,
} as any).data; } as any).data;

View File

@@ -7,7 +7,7 @@ import { RepoComponents } from "./repo";
import { ServerComponents } from "./server"; import { ServerComponents } from "./server";
import { ProcedureComponents } from "./procedure/index"; import { ProcedureComponents } from "./procedure/index";
import { ServerTemplateComponents } from "./server-template"; import { ServerTemplateComponents } from "./server-template";
import { ResourceSyncComponents } from "./sync"; import { ResourceSyncComponents } from "./resource-sync";
export const ResourceComponents: { export const ResourceComponents: {
[key in UsableResource]: RequiredResourceComponents; [key in UsableResource]: RequiredResourceComponents;

View File

@@ -0,0 +1,57 @@
import { ConfirmButton } from "@components/util";
import { useExecute, useInvalidate, useRead, useWrite } from "@lib/hooks";
import { sync_no_changes } from "@lib/utils";
import { Loader2, RefreshCcw, SquarePlay } from "lucide-react";
export const RefreshSync = ({ id }: { id: string }) => {
const inv = useInvalidate();
const { mutate, isPending } = useWrite("RefreshResourceSyncPending", {
onSuccess: () => inv(["GetResourceSync", { sync: id }]),
});
const pending = isPending;
return (
<ConfirmButton
title="Refresh"
icon={
pending ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<RefreshCcw className="w-4 h-4" />
)
}
onClick={() => mutate({ sync: id })}
disabled={pending}
loading={pending}
/>
);
};
export const ExecuteSync = ({ id }: { id: string }) => {
const { mutate, isPending } = useExecute("RunSync");
const syncing = useRead(
"GetResourceSyncActionState",
{ sync: id },
{ refetchInterval: 5000 }
).data?.syncing;
const sync = useRead("GetResourceSync", { sync: id }).data;
if (!sync || sync_no_changes(sync)) return null;
const pending = isPending || syncing;
return (
<ConfirmButton
title="Execute Sync"
icon={
pending ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<SquarePlay className="w-4 h-4" />
)
}
onClick={() => mutate({ sync: id })}
disabled={pending}
loading={pending}
/>
);
};

View File

@@ -9,6 +9,7 @@ import { Types } from "@monitor/client";
import { ResourceSyncConfig } from "./config"; import { ResourceSyncConfig } from "./config";
import { useState } from "react"; import { useState } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs";
import { ExecuteSync, RefreshSync } from "./actions";
const useResourceSync = (id?: string) => const useResourceSync = (id?: string) =>
useRead("ListResourceSyncs", {}).data?.find((d) => d.id === id); useRead("ListResourceSyncs", {}).data?.find((d) => d.id === id);
@@ -44,12 +45,12 @@ export const ResourceSyncComponents: RequiredResourceComponents = {
Dashboard: () => { Dashboard: () => {
const syncs_count = useRead("ListResourceSyncs", {}).data?.length; const syncs_count = useRead("ListResourceSyncs", {}).data?.length;
return ( return (
<Link to="/syncs/" className="w-full"> <Link to="/resource-syncs/" className="w-full">
<Card className="hover:bg-accent/50 transition-colors cursor-pointer"> <Card className="hover:bg-accent/50 transition-colors cursor-pointer">
<CardHeader> <CardHeader>
<div className="flex justify-between"> <div className="flex justify-between">
<div> <div>
<CardTitle>Syncs</CardTitle> <CardTitle>Resource Syncs</CardTitle>
<CardDescription>{syncs_count} Total</CardDescription> <CardDescription>{syncs_count} Total</CardDescription>
</div> </div>
<FolderSync className="w-4 h-4" /> <FolderSync className="w-4 h-4" />
@@ -60,7 +61,7 @@ export const ResourceSyncComponents: RequiredResourceComponents = {
); );
}, },
New: () => <NewResource type="ResourceSync" />, New: () => <NewResource type="ResourceSync" readable_type="Resource Sync" />,
Table: ({ resources }) => ( Table: ({ resources }) => (
<ResourceSyncTable syncs={resources as Types.ResourceSyncListItem[]} /> <ResourceSyncTable syncs={resources as Types.ResourceSyncListItem[]} />
@@ -73,7 +74,7 @@ export const ResourceSyncComponents: RequiredResourceComponents = {
Info: {}, Info: {},
Actions: {}, Actions: { RefreshSync, ExecuteSync },
Page: {}, Page: {},

View File

@@ -52,7 +52,12 @@ export const Sidebar = () => {
{RESOURCE_TARGETS.map((type) => { {RESOURCE_TARGETS.map((type) => {
const RTIcon = ResourceComponents[type].Icon; const RTIcon = ResourceComponents[type].Icon;
const name = type === "ServerTemplate" ? "Template" : type; const name =
type === "ServerTemplate"
? "Template"
: type === "ResourceSync"
? "Sync"
: type;
return ( return (
<SidebarLink <SidebarLink
key={type} key={type}

View File

@@ -125,7 +125,11 @@ const PrimaryDropdown = () => {
const [icon, title] = Components const [icon, title] = Components
? [ ? [
<Components.Icon />, <Components.Icon />,
(type === "ServerTemplate" ? "Template" : type) + "s", (type === "ServerTemplate"
? "Template"
: type === "ResourceSync"
? "Sync"
: type) + "s",
] ]
: location.pathname === "/" : location.pathname === "/"
? [<Home className="w-4 h-4" />, "Home"] ? [<Home className="w-4 h-4" />, "Home"]

View File

@@ -192,6 +192,7 @@ export const useResourceParamType = () => {
const type = useParams().type; const type = useParams().type;
if (!type) return undefined; if (!type) return undefined;
if (type === "server-templates") return "ServerTemplate"; if (type === "server-templates") return "ServerTemplate";
if (type === "resource-syncs") return "ResourceSync";
return (type[0].toUpperCase() + type.slice(1, -1)) as UsableResource; return (type[0].toUpperCase() + type.slice(1, -1)) as UsableResource;
}; };

View File

@@ -137,6 +137,15 @@ const on_message = (
); );
} }
if (update.target.type === "ResourceSync") {
invalidate(
["ListResourceSyncs"],
["GetResourceSync"],
["GetResourceSyncActionState"],
["GetResourceSyncsSummary"]
);
}
if ( if (
update.target.type === "System" && update.target.type === "System" &&
update.operation.includes("Variable") update.operation.includes("Variable")

View File

@@ -22,6 +22,7 @@ export const RESOURCE_TARGETS: UsableResource[] = [
"Builder", "Builder",
"Alerter", "Alerter",
"ServerTemplate", "ServerTemplate",
"ResourceSync",
]; ];
export function env_to_text(envVars: Types.EnvironmentVar[] | undefined) { export function env_to_text(envVars: Types.EnvironmentVar[] | undefined) {
@@ -97,6 +98,7 @@ export const convertTsMsToLocalUnixTsInSecs = (ts: number) =>
export const usableResourcePath = (resource: UsableResource) => { export const usableResourcePath = (resource: UsableResource) => {
if (resource === "ServerTemplate") return "server-templates"; if (resource === "ServerTemplate") return "server-templates";
if (resource === "ResourceSync") return "resource-syncs";
return `${resource.toLowerCase()}s`; return `${resource.toLowerCase()}s`;
}; };
@@ -173,3 +175,21 @@ export const filterBySplit = <T>(
: items) ?? [] : items) ?? []
); );
}; };
export const sync_no_changes = (sync: Types.ResourceSync) => {
const pending = sync.info?.pending;
if (!pending) return false;
return (
!pending.server_updates?.length &&
!pending.deployment_updates?.length &&
!pending.build_updates?.length &&
!pending.repo_updates?.length &&
!pending.procedure_updates?.length &&
!pending.alerter_updates?.length &&
!pending.builder_updates?.length &&
!pending.server_template_updates?.length &&
!pending.resource_sync_updates?.length &&
!pending.variable_updates?.length &&
!pending.user_group_updates?.length
);
};

View File

@@ -14,7 +14,12 @@ import { useState } from "react";
export const Resources = () => { export const Resources = () => {
const type = useResourceParamType()!; const type = useResourceParamType()!;
const name = type === "ServerTemplate" ? "Server Template" : type; const name =
type === "ServerTemplate"
? "Server Template"
: type === "ResourceSync"
? "Resource Sync"
: type;
useSetTitle(name + "s"); useSetTitle(name + "s");
const Components = ResourceComponents[type]; const Components = ResourceComponents[type];
const [search, set] = useState(""); const [search, set] = useState("");