feat: show components versions (#297)
On hover on the Zerobyte version in the sidebar A card will appear to show the versions of the binaries (restic, rclone, shoutrrr)
This commit is contained in:
parent
d6a5c1f10c
commit
d293738bf4
8 changed files with 96 additions and 24 deletions
28
Dockerfile
28
Dockerfile
|
|
@ -2,6 +2,14 @@ ARG BUN_VERSION="1.3.5"
|
||||||
|
|
||||||
FROM oven/bun:${BUN_VERSION}-alpine AS base
|
FROM oven/bun:${BUN_VERSION}-alpine AS base
|
||||||
|
|
||||||
|
ARG RESTIC_VERSION="0.18.1"
|
||||||
|
ARG RCLONE_VERSION="1.72.1"
|
||||||
|
ARG SHOUTRRR_VERSION="0.13.1"
|
||||||
|
|
||||||
|
ENV VITE_RESTIC_VERSION=${RESTIC_VERSION} \
|
||||||
|
VITE_RCLONE_VERSION=${RCLONE_VERSION} \
|
||||||
|
VITE_SHOUTRRR_VERSION=${SHOUTRRR_VERSION}
|
||||||
|
|
||||||
RUN apk upgrade --no-cache && \
|
RUN apk upgrade --no-cache && \
|
||||||
apk add --no-cache davfs2=1.6.1-r2 openssh-client fuse3 sshfs tini
|
apk add --no-cache davfs2=1.6.1-r2 openssh-client fuse3 sshfs tini
|
||||||
|
|
||||||
|
|
@ -16,22 +24,19 @@ FROM base AS deps
|
||||||
WORKDIR /deps
|
WORKDIR /deps
|
||||||
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG RESTIC_VERSION="0.18.1"
|
|
||||||
ARG RCLONE_VERSION="1.72.1"
|
|
||||||
ARG SHOUTRRR_VERSION="0.13.1"
|
|
||||||
ENV TARGETARCH=${TARGETARCH}
|
ENV TARGETARCH=${TARGETARCH}
|
||||||
|
|
||||||
RUN apk add --no-cache curl bzip2 unzip tar
|
RUN apk add --no-cache curl bzip2 unzip tar
|
||||||
|
|
||||||
RUN echo "Building for ${TARGETARCH}"
|
RUN echo "Building for ${TARGETARCH}"
|
||||||
RUN if [ "${TARGETARCH}" = "arm64" ]; then \
|
RUN if [ "${TARGETARCH}" = "arm64" ]; then \
|
||||||
curl -L -o restic.bz2 "https://github.com/restic/restic/releases/download/v$RESTIC_VERSION/restic_$RESTIC_VERSION"_linux_arm64.bz2; \
|
curl -L -o restic.bz2 "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_arm64.bz2"; \
|
||||||
curl -L -o rclone.zip "https://github.com/rclone/rclone/releases/download/v$RCLONE_VERSION/rclone-v$RCLONE_VERSION-linux-arm64.zip"; \
|
curl -L -o rclone.zip "https://github.com/rclone/rclone/releases/download/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-arm64.zip"; \
|
||||||
unzip rclone.zip; \
|
unzip rclone.zip; \
|
||||||
curl -L -o shoutrrr.tar.gz "https://github.com/nicholas-fedor/shoutrrr/releases/download/v$SHOUTRRR_VERSION/shoutrrr_linux_arm64v8_${SHOUTRRR_VERSION}.tar.gz"; \
|
curl -L -o shoutrrr.tar.gz "https://github.com/nicholas-fedor/shoutrrr/releases/download/v${SHOUTRRR_VERSION}/shoutrrr_linux_arm64v8_${SHOUTRRR_VERSION}.tar.gz"; \
|
||||||
elif [ "${TARGETARCH}" = "amd64" ]; then \
|
elif [ "${TARGETARCH}" = "amd64" ]; then \
|
||||||
curl -L -o restic.bz2 "https://github.com/restic/restic/releases/download/v$RESTIC_VERSION/restic_$RESTIC_VERSION"_linux_amd64.bz2; \
|
curl -L -o restic.bz2 "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_amd64.bz2"; \
|
||||||
curl -L -o rclone.zip "https://github.com/rclone/rclone/releases/download/v$RCLONE_VERSION/rclone-v$RCLONE_VERSION-linux-amd64.zip"; \
|
curl -L -o rclone.zip "https://github.com/rclone/rclone/releases/download/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-amd64.zip"; \
|
||||||
unzip rclone.zip; \
|
unzip rclone.zip; \
|
||||||
curl -L -o shoutrrr.tar.gz "https://github.com/nicholas-fedor/shoutrrr/releases/download/v$SHOUTRRR_VERSION/shoutrrr_linux_amd64_${SHOUTRRR_VERSION}.tar.gz"; \
|
curl -L -o shoutrrr.tar.gz "https://github.com/nicholas-fedor/shoutrrr/releases/download/v$SHOUTRRR_VERSION/shoutrrr_linux_amd64_${SHOUTRRR_VERSION}.tar.gz"; \
|
||||||
fi
|
fi
|
||||||
|
|
@ -47,6 +52,7 @@ FROM base AS development
|
||||||
|
|
||||||
ARG APP_VERSION=dev
|
ARG APP_VERSION=dev
|
||||||
ENV APP_VERSION=${APP_VERSION}
|
ENV APP_VERSION=${APP_VERSION}
|
||||||
|
ENV VITE_APP_VERSION=${APP_VERSION}
|
||||||
ENV NODE_ENV="development"
|
ENV NODE_ENV="development"
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
@ -68,9 +74,10 @@ CMD ["bun", "run", "dev"]
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
# PRODUCTION
|
# PRODUCTION
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
FROM oven/bun:${BUN_VERSION}-alpine AS builder
|
FROM base AS builder
|
||||||
|
|
||||||
ARG APP_VERSION=dev
|
ARG APP_VERSION=dev
|
||||||
|
ENV VITE_APP_VERSION=${APP_VERSION}
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
@ -79,9 +86,6 @@ RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN touch .env
|
|
||||||
RUN echo "VITE_APP_VERSION=${APP_VERSION}" >> .env
|
|
||||||
|
|
||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
|
||||||
FROM base AS production
|
FROM base AS production
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@ import {
|
||||||
useSidebar,
|
useSidebar,
|
||||||
} from "~/client/components/ui/sidebar";
|
} from "~/client/components/ui/sidebar";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "~/client/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "~/client/components/ui/tooltip";
|
||||||
|
import { HoverCard, HoverCardContent, HoverCardTrigger } from "~/client/components/ui/hover-card";
|
||||||
import { cn } from "~/client/lib/utils";
|
import { cn } from "~/client/lib/utils";
|
||||||
import { APP_VERSION } from "~/client/lib/version";
|
import { APP_VERSION, RCLONE_VERSION, RESTIC_VERSION, SHOUTRRR_VERSION } from "~/client/lib/version";
|
||||||
import { useUpdates } from "~/client/hooks/use-updates";
|
import { useUpdates } from "~/client/hooks/use-updates";
|
||||||
import { ReleaseNotesDialog } from "./release-notes-dialog";
|
import { ReleaseNotesDialog } from "./release-notes-dialog";
|
||||||
|
|
||||||
|
|
@ -105,16 +106,32 @@ export function AppSidebar() {
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter className="p-4 border-r border-t border-border/50">
|
<SidebarFooter className="p-4 border-r border-t border-border/50">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<a
|
<HoverCard openDelay={200}>
|
||||||
href={releaseUrl}
|
<HoverCardTrigger asChild>
|
||||||
target="_blank"
|
<a
|
||||||
rel="noopener noreferrer"
|
href={releaseUrl}
|
||||||
className={cn("text-xs text-muted-foreground hover:text-foreground", {
|
target="_blank"
|
||||||
"opacity-0 w-0 overflow-hidden": state === "collapsed",
|
rel="noopener noreferrer"
|
||||||
})}
|
className={cn("text-xs text-muted-foreground hover:text-foreground", {
|
||||||
>
|
"opacity-0 w-0 overflow-hidden": state === "collapsed",
|
||||||
{displayVersion}
|
})}
|
||||||
</a>
|
>
|
||||||
|
{displayVersion}
|
||||||
|
</a>
|
||||||
|
</HoverCardTrigger>
|
||||||
|
<HoverCardContent side="top" align="start" className="w-fit p-3">
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-[11px]">
|
||||||
|
<span className="text-muted-foreground">Restic:</span>
|
||||||
|
<span className="font-mono">{RESTIC_VERSION}</span>
|
||||||
|
<span className="text-muted-foreground">Rclone:</span>
|
||||||
|
<span className="font-mono">{RCLONE_VERSION}</span>
|
||||||
|
<span className="text-muted-foreground">Shoutrrr:</span>
|
||||||
|
<span className="font-mono">{SHOUTRRR_VERSION}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</HoverCardContent>
|
||||||
|
</HoverCard>
|
||||||
{hasUpdate && state !== "collapsed" && (
|
{hasUpdate && state !== "collapsed" && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
||||||
42
app/client/components/ui/hover-card.tsx
Normal file
42
app/client/components/ui/hover-card.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
||||||
|
|
||||||
|
import { cn } from "~/client/lib/utils"
|
||||||
|
|
||||||
|
function HoverCard({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
|
||||||
|
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function HoverCardTrigger({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
|
||||||
|
return (
|
||||||
|
<HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function HoverCardContent({
|
||||||
|
className,
|
||||||
|
align = "center",
|
||||||
|
sideOffset = 4,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
|
||||||
|
<HoverCardPrimitive.Content
|
||||||
|
data-slot="hover-card-content"
|
||||||
|
align={align}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</HoverCardPrimitive.Portal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
||||||
|
|
@ -1 +1,4 @@
|
||||||
export const APP_VERSION = import.meta.env.VITE_APP_VERSION || "dev";
|
export const APP_VERSION = import.meta.env.VITE_APP_VERSION || "dev";
|
||||||
|
export const RESTIC_VERSION = import.meta.env.VITE_RESTIC_VERSION || "unknown";
|
||||||
|
export const RCLONE_VERSION = import.meta.env.VITE_RCLONE_VERSION || "unknown";
|
||||||
|
export const SHOUTRRR_VERSION = import.meta.env.VITE_SHOUTRRR_VERSION || "unknown";
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export const CustomForm = ({ form }: Props) => {
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Direct Shoutrrr URL for power users. See
|
Direct Shoutrrr URL for power users. See
|
||||||
<a
|
<a
|
||||||
href="https://shoutrrr.nickfedor.com/v0.12.0/services/overview/"
|
href="https://shoutrrr.nickfedor.com/latest/services/overview/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-strong-accent hover:underline"
|
className="text-strong-accent hover:underline"
|
||||||
|
|
|
||||||
3
bun.lock
3
bun.lock
|
|
@ -15,6 +15,7 @@
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
|
"@radix-ui/react-hover-card": "^1.1.15",
|
||||||
"@radix-ui/react-label": "^2.1.8",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-progress": "^1.1.8",
|
"@radix-ui/react-progress": "^1.1.8",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||||
|
|
@ -347,6 +348,8 @@
|
||||||
|
|
||||||
"@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
|
"@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
|
||||||
|
|
||||||
|
"@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="],
|
||||||
|
|
||||||
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
|
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
|
||||||
|
|
||||||
"@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="],
|
"@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="],
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ services:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
target: production
|
target: production
|
||||||
|
args:
|
||||||
|
- APP_VERSION=0.20.0
|
||||||
container_name: zerobyte
|
container_name: zerobyte
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
devices:
|
devices:
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
|
"@radix-ui/react-hover-card": "^1.1.15",
|
||||||
"@radix-ui/react-label": "^2.1.8",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-progress": "^1.1.8",
|
"@radix-ui/react-progress": "^1.1.8",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue