diff --git a/INSTALL.md b/INSTALL.md index c232741..e989cd3 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -6,13 +6,13 @@ This guide provides step-by-step instructions for installing and running **WoW B ## Download -Download the appropriate installer for your platform from the **[GitHub Releases](https://github.com/rukira/wow-backup/releases)** page: +Download the appropriate installer for your platform from the **[latest release](https://git.asarius.site/rukira/wow-backup/releases)**: -| Platform | Package Format | Installer File Pattern | -|---|---|---| -| **macOS** | DMG Disk Image | `WoW Backup-.dmg` | -| **Windows** | Windows Installer (MSI) | `WoW Backup-.msi` | -| **Linux** | Debian Package (DEB) | `wow-backup__amd64.deb` | +| Platform | Package Format | Installer File Pattern | +|--------------------------|---|---| +| **macOS** | DMG Disk Image | `WoW Backup-.dmg` | +| **Windows** | Windows Installer (MSI) | `WoW Backup-.msi` | +| **Linux** (Experimental) | Debian Package (DEB) | `wow-backup__amd64.deb` | --- @@ -27,16 +27,10 @@ Download the appropriate installer for your platform from the **[GitHub Releases Because WoW Backup is distributed without Apple Developer ID code-signing notarization, macOS Gatekeeper may show a warning on first launch (*"WoW Backup cannot be opened because the developer cannot be verified"* or *"Apple could not verify that WoW Backup is free of malware"*). To launch the app for the first time: - -- **Option A (Finder)**: - 1. Open Finder and navigate to `/Applications`. - 2. Right-click (or Control-click) on **WoW Backup.app** and choose **Open**. - 3. In the security popup, click **Open**. -- **Option B (Terminal)**: - Run the following command in Terminal to clear the quarantine attribute: - ```shell - xattr -d com.apple.quarantine "/Applications/WoW Backup.app" - ``` +1. Open **System Settings** +2. Navigate to **Security & Privacy**. +3. Click **Open Anyway** under **Allow apps downloaded from**. +4. Restart WoW Backup. --- @@ -68,17 +62,3 @@ If Windows Defender SmartScreen flags the installer (*"Windows protected your PC ``` 3. Launch **WoW Backup** from your desktop application launcher or run `wowbackup` in the terminal. ---- - -## First-Time Setup & Overview - -Once started, WoW Backup runs discreetly in the system tray / menu bar: - -1. **Accessing Dashboard**: Click the tray icon in the macOS menu bar or Windows/Linux system tray to open the application window. -2. **Settings Configuration**: - - Navigate to **Settings** (gear icon). - - Set your **World of Warcraft Installation Folder** (e.g. `_retail_` or `_classic_`). - - Select your **Backup Destination Folder**. - - Set your preferred **Daily Backup Time**, **Retention Policy** (number of backups to keep), and **Compression Mode** (`ZIP` or folder replication). - - Optionally enable **Launch at Startup** so backups occur automatically without manual intervention. -3. **Lifecycle**: Closing the main window hides it to the tray while keeping the scheduler running. To quit the application completely, select **Quit** from the tray menu. diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index f0bb771..ca42a48 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -31,6 +31,9 @@ kotlin { implementation(libs.logback.classic) implementation(libs.material.kolor) } + commonTest.dependencies { + implementation(kotlin("test")) + } } } @@ -72,6 +75,8 @@ compose.desktop { packageName = "WoW Backup" packageVersion = appVersion + modules("java.naming", "java.sql", "java.instrument", "jdk.unsupported") + macOS { bundleID = "com.rukira.wowbackup" iconFile.set(project.file("src/jvmMain/resources/icon.icns")) diff --git a/composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/logging/LoggingSetup.kt b/composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/logging/LoggingSetup.kt index e4fc392..03f3e70 100644 --- a/composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/logging/LoggingSetup.kt +++ b/composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/logging/LoggingSetup.kt @@ -12,7 +12,12 @@ import java.io.File object LoggingSetup { + private var initialized = false + fun init() { + if (initialized) return + initialized = true + val logDir = AppDirectories.logsDir val logFile = File(logDir, "wowbackup.log") @@ -26,7 +31,7 @@ object LoggingSetup { val rollingPolicy = SizeAndTimeBasedRollingPolicy().apply { this.context = context - fileNamePattern = "${logDir}/wowbackup.%d{yyyy-MM-dd}.%i.log.gz" + fileNamePattern = "${logDir.absolutePath}/wowbackup.%d{yyyy-MM-dd}.%i.log.gz" setMaxFileSize(FileSize.valueOf("10MB")) maxHistory = 30 setTotalSizeCap(FileSize.valueOf("100MB")) @@ -46,5 +51,10 @@ object LoggingSetup { val rootLogger = context.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME) rootLogger.addAppender(fileAppender) + + Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> + val uncaughtLogger = LoggerFactory.getLogger("UncaughtExceptionHandler") + uncaughtLogger.error("Uncaught exception on thread [${thread.name}]", throwable) + } } } diff --git a/composeApp/src/jvmTest/kotlin/com/rukira/wowbackup/logging/LoggingSetupTest.kt b/composeApp/src/jvmTest/kotlin/com/rukira/wowbackup/logging/LoggingSetupTest.kt new file mode 100644 index 0000000..ab5c074 --- /dev/null +++ b/composeApp/src/jvmTest/kotlin/com/rukira/wowbackup/logging/LoggingSetupTest.kt @@ -0,0 +1,39 @@ +package com.rukira.wowbackup.logging + +import com.rukira.wowbackup.platform.AppDirectories +import io.github.oshai.kotlinlogging.KotlinLogging +import java.io.File +import kotlin.test.Test +import kotlin.test.assertTrue + +class LoggingSetupTest { + + private val testLogger = KotlinLogging.logger {} + + @Test + fun testLoggingSetupInitializesAndWritesToFile() { + LoggingSetup.init() + + val logsDir = AppDirectories.logsDir + assertTrue(logsDir.exists(), "Logs directory should exist") + assertTrue(logsDir.isDirectory, "Logs directory should be a directory") + + val testMessage = "Test log entry for unit verification - ${System.currentTimeMillis()}" + testLogger.info { testMessage } + + val logFile = File(logsDir, "wowbackup.log") + assertTrue(logFile.exists(), "wowbackup.log file should exist") + + val content = logFile.readText() + assertTrue(content.contains(testMessage), "wowbackup.log should contain the logged message") + } + + @Test + fun testAppDirectoriesLogsDir() { + val appDataDir = AppDirectories.appDataDir + val logsDir = AppDirectories.logsDir + + assertTrue(logsDir.absolutePath.startsWith(appDataDir.absolutePath), "Logs dir should be inside app data dir") + assertTrue(logsDir.name == "logs", "Logs directory should be named 'logs'") + } +}