create tarball for next release and fix few minor issues, also add download logs

This commit is contained in:
fccview
2026-02-11 18:48:51 +00:00
parent 0ed4942a30
commit f2fb381964
13 changed files with 190 additions and 18 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: fccview

View File

@@ -1,4 +1,4 @@
name: Reusable Docker Build Logic
name: Builder
on:
workflow_call:

View File

@@ -1,4 +1,4 @@
name: Build and Publish Multi-Platform Docker Image
name: Build and Publish
on:
push:

48
.github/workflows/pr-checks.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: PR Checks
on:
pull_request:
branches: ["**"]
jobs:
validate-branch:
name: Validate Target Branch
runs-on: ubuntu-latest
steps:
- name: YOU SHALL NOT PASS
if: github.base_ref == 'main'
run: |
if [ "${{ github.head_ref }}" != "develop" ]; then
echo "ERROR: Pull requests to 'main' are not allowed."
echo "Current source branch: ${{ github.head_ref }}"
echo ""
echo "Please create a PR to 'develop' first, this will become a release candidate when merged into 'main' by a maintainer"
exit 1
fi
echo "Valid PR: develop → main"
- name: PR info
run: |
echo "PR validation passed"
echo "Source: ${{ github.head_ref }}"
echo "Target: ${{ github.base_ref }}"
typing:
name: Type and install checks
runs-on: ubuntu-latest
needs: validate-branch
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup the best engine ever
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "yarn"
- name: Install all dependencies
run: yarn install --frozen-lockfile
- name: This will totally fail if you only use AI
run: yarn tsc --noEmit

60
.github/workflows/prebuild-release.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: Build and Release Prebuilt Tarball
on:
push:
tags: ["*"]
jobs:
build-prebuild:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup the best engine ever
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Bake cronmaster
env:
NODE_ENV: production
NEXT_TELEMETRY_DISABLED: 1
run: yarn build
- name: Get version from tag
id: version
run: |
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: Structure the prebuild stuff
run: |
mkdir -p prebuild-release/cronmaster/.next
cp -r .next/standalone/. prebuild-release/cronmaster/
cp -r .next/static prebuild-release/cronmaster/.next/static
if [ -f .next/BUILD_ID ]; then
cp .next/BUILD_ID prebuild-release/cronmaster/.next/BUILD_ID
fi
cp -r public prebuild-release/cronmaster/public
cp -r howto prebuild-release/cronmaster/howto
- name: Create tarball - tarball is a funny name
run: |
cd prebuild-release
tar -czf cronmaster_${{ steps.version.outputs.version }}_prebuild.tar.gz cronmaster
sha256sum cronmaster_${{ steps.version.outputs.version }}_prebuild.tar.gz > cronmaster_${{ steps.version.outputs.version }}_prebuild.tar.gz.sha256
- name: Attach to Release - pray it works
uses: softprops/action-gh-release@v1
with:
files: |
prebuild-release/cronmaster_*_prebuild.tar.gz
prebuild-release/cronmaster_*_prebuild.tar.gz.sha256
tag_name: ${{ steps.version.outputs.version }}

View File

@@ -92,7 +92,7 @@ export const Sidebar = forwardRef<HTMLDivElement, SidebarProps>(
>
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="absolute -right-3 top-[21.5vh] w-6 h-6 bg-background0 ascii-border items-center justify-center transition-colors z-40 hidden lg:flex"
className="sidebar-shrinker absolute -right-3 top-[21.5vh] w-6 h-6 bg-background0 ascii-border items-center justify-center transition-colors z-40 hidden lg:flex"
>
{isCollapsed ? (
<CaretRightIcon className="h-3 w-3" />

View File

@@ -3,8 +3,9 @@
import { useState, useEffect } from "react";
import { Modal } from "@/app/_components/GlobalComponents/UIElements/Modal";
import { Button } from "@/app/_components/GlobalComponents/UIElements/Button";
import { FileTextIcon, TrashIcon, EyeIcon, XIcon, ArrowsClockwiseIcon, WarningCircleIcon, CheckCircleIcon } from "@phosphor-icons/react";
import { FileTextIcon, TrashIcon, EyeIcon, XIcon, ArrowsClockwiseIcon, WarningCircleIcon, CheckCircleIcon, DownloadIcon } from "@phosphor-icons/react";
import { useTranslations } from "next-intl";
import { zipSync, strToU8 } from "fflate";
import {
getJobLogs,
getLogContent,
@@ -44,6 +45,7 @@ export const LogsModal = ({
const [logContent, setLogContent] = useState<string>("");
const [isLoadingLogs, setIsLoadingLogs] = useState(false);
const [isLoadingContent, setIsLoadingContent] = useState(false);
const [isDownloading, setIsDownloading] = useState(false);
const [stats, setStats] = useState<{
count: number;
totalSize: number;
@@ -133,6 +135,28 @@ export const LogsModal = ({
}
};
const handleDownloadLogs = async () => {
if (logs.length === 0) return;
setIsDownloading(true);
try {
const files: Record<string, Uint8Array> = {};
for (const log of logs) {
const content = await getLogContent(jobId, log.filename);
files[log.filename] = strToU8(content);
}
const zipped = zipSync(files);
const blob = new Blob([zipped as unknown as ArrayBuffer], { type: "application/zip" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${jobComment || jobId}_logs.zip`;
a.click();
URL.revokeObjectURL(url);
} finally {
setIsDownloading(false);
}
};
const formatFileSize = (bytes: number): string => {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
@@ -157,16 +181,29 @@ export const LogsModal = ({
return (
<Modal isOpen={isOpen} onClose={onClose} title={t("cronjobs.viewLogs")} size="xl">
<div className="flex flex-col h-[600px]">
<div className="flex items-center justify-between mb-4 pb-4 border-b border-border">
<div>
<h3 className="font-semibold text-lg">{jobComment || jobId}</h3>
<div className="block sm:flex items-center justify-between mb-4 pb-4 border-b border-border">
<div className="min-w-0 mb-4 sm:mb-0">
<h3 className="font-semibold text-lg truncate">{jobComment || jobId}</h3>
{stats && (
<p className="text-sm text-muted-foreground">
{stats.count} {t("cronjobs.logs")} {stats.totalSizeMB} MB
</p>
)}
</div>
<div className="flex gap-2">
<div className="flex gap-2 flex-shrink-0">
<Button
onClick={handleDownloadLogs}
disabled={logs.length === 0 || isDownloading}
className="btn-primary glow-primary"
size="sm"
>
{isDownloading ? (
<ArrowsClockwiseIcon className="w-4 h-4 sm:mr-2 animate-spin" />
) : (
<DownloadIcon className="w-4 h-4 sm:mr-2" />
)}
<span className="hidden sm:inline">{t("cronjobs.downloadLog")}</span>
</Button>
<Button
onClick={loadLogs}
disabled={isLoadingLogs}
@@ -174,10 +211,10 @@ export const LogsModal = ({
size="sm"
>
<ArrowsClockwiseIcon
className={`w-4 h-4 mr-2 ${isLoadingLogs ? "animate-spin" : ""
className={`w-4 h-4 sm:mr-2 ${isLoadingLogs ? "animate-spin" : ""
}`}
/>
{t("common.refresh")}
<span className="hidden sm:inline">{t("common.refresh")}</span>
</Button>
{logs.length > 0 && (
<Button
@@ -185,15 +222,15 @@ export const LogsModal = ({
variant="destructive"
size="sm"
>
<TrashIcon className="w-4 h-4 mr-2" />
{t("cronjobs.deleteAll")}
<TrashIcon className="w-4 h-4 sm:mr-2" />
<span className="hidden sm:inline">{t("cronjobs.deleteAll")}</span>
</Button>
)}
</div>
</div>
<div className="flex-1 flex gap-4 overflow-hidden">
<div className="w-1/3 flex flex-col border-r border-border pr-4 overflow-hidden">
<div className="flex-1 flex flex-col sm:flex-row gap-4 overflow-hidden">
<div className="sm:w-1/3 flex flex-col sm:border-r border-b sm:border-b-0 border-border sm:pr-4 pb-4 sm:pb-0 overflow-hidden max-h-[40%] sm:max-h-none">
<h4 className="font-semibold mb-2">{t("cronjobs.logFiles")}</h4>
<div className="flex-1 overflow-y-auto space-y-2">
{isLoadingLogs ? (
@@ -288,8 +325,8 @@ export const LogsModal = ({
<div className="mt-4 pt-4 border-t border-border flex justify-end">
<Button onClick={onClose} className="btn-primary glow-primary">
<XIcon className="w-4 h-4 mr-2" />
{t("common.close")}
<XIcon className="w-4 h-4 sm:mr-2" />
<span className="hidden sm:inline">{t("common.close")}</span>
</Button>
</div>
</div>

View File

@@ -47,6 +47,7 @@
"logs": "logs",
"logFiles": "Log Files",
"logContent": "Log Content",
"downloadLog": "Download",
"selectLogToView": "Select a log file to view its content",
"noLogsFound": "No logs found for this job",
"confirmDeleteLog": "Are you sure you want to delete this log file?",

View File

@@ -46,6 +46,7 @@
"logs": "log",
"logFiles": "File",
"logContent": "Contenuto Log",
"downloadLog": "Scarica",
"selectLogToView": "Seleziona un file per visualizzarne il contenuto",
"noLogsFound": "Nessun log trovato per questa operazione",
"confirmDeleteLog": "Sei sicuro di voler eliminare questo file?",

View File

@@ -260,4 +260,20 @@ pre::-webkit-scrollbar {
max-width: 100%;
margin: inherit;
}
}
.sidebar-shrinker {
z-index: 1;
}
.sidebar-shrinker:before {
content: '';
width: 0;
height: 0;
border-top: 6px solid var(--box-border-color, var(--foreground2));
border-right: 12px solid transparent;
position: absolute;
right: -1px;
bottom: -6px;
z-index: -1;
}

View File

@@ -81,7 +81,9 @@ export default async function Home() {
Cr<span className="text-status-error">*</span>nMaster
</h1>
<p className="text-xs terminal-font flex items-center gap-2">
{t("common.version").replace("{version}", version)}
<a href={`https://github.com/fccview/cronmaster/releases/tag/${version}`} target="_blank" rel="noopener noreferrer">
{t("common.version").replace("{version}", version)}
</a>
</p>
</div>
</div>

View File

@@ -1,6 +1,6 @@
{
"name": "cronjob-manager",
"version": "2.0.0",
"version": "2.0.1",
"private": true,
"scripts": {
"dev": "next dev",
@@ -34,6 +34,7 @@
"codemirror": "^6.0.2",
"cron-parser": "^5.3.0",
"cronstrue": "^3.2.0",
"fflate": "^0.8.2",
"jose": "^6.1.1",
"minimatch": "^10.0.3",
"next": "14.2.35",

View File

@@ -2842,6 +2842,11 @@ fdir@^6.5.0:
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
fflate@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"