diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a927b539..7cd51d9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: java: [21] - os: ['ubuntu-latest', 'windows-latest'] + os: ['ubuntu-latest', 'windows-latest'] # 'macos-latest' not supported at the moment env: DEFAULT_JAVA: 21 DEFAULT_OS: 'ubuntu-latest' @@ -59,8 +59,8 @@ jobs: name: executable-jar retention-days: 5 path: | - jfxui/build/libs/white-rabbit-fx-*.jar - jfxui/build/libs-checksums/* + product/build/libs/white-rabbit-fx-*.jar + product/build/libs-checksums/* if-no-files-found: error - name: Archive test reports for ${{ matrix.os }} using Java ${{ matrix.java }} @@ -92,8 +92,8 @@ jobs: name: packages-${{ runner.os }} retention-days: 5 path: | - jfxui/build/jpackage-dist/* - jfxui/build/jpackage-checksums/* + product/build/jpackage-dist/* + product/build/jpackage-checksums/* if-no-files-found: error build: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bbb4c0cf..8c45e486 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,8 +52,8 @@ jobs: retention-days: 5 if-no-files-found: error path: | - jfxui/build/libs/white-rabbit-fx-*.jar - jfxui/build/libs-checksums/* + product/build/libs/white-rabbit-fx-*.jar + product/build/libs-checksums/* - name: Archive native package for ${{ runner.os }} uses: actions/upload-artifact@v4 @@ -62,8 +62,8 @@ jobs: retention-days: 5 if-no-files-found: error path: | - jfxui/build/jpackage-dist/* - jfxui/build/jpackage-checksums/* + product/build/jpackage-dist/* + product/build/jpackage-checksums/* release: runs-on: ubuntu-latest diff --git a/api/src/main/java/org/itsallcode/whiterabbit/api/features/MonthDataStorage.java b/api/src/main/java/org/itsallcode/whiterabbit/api/features/MonthDataStorage.java index 27790282..ece037b7 100644 --- a/api/src/main/java/org/itsallcode/whiterabbit/api/features/MonthDataStorage.java +++ b/api/src/main/java/org/itsallcode/whiterabbit/api/features/MonthDataStorage.java @@ -59,7 +59,7 @@ public interface MonthDataStorage extends PluginFeature /** * A {@link ModelFactory} allows creating new instances of the data model. */ - public interface ModelFactory + interface ModelFactory { /** * Create a new {@link MonthData} instance. @@ -82,4 +82,4 @@ public interface ModelFactory */ ActivityData createActivityData(); } -} \ No newline at end of file +} diff --git a/api/src/main/java/org/itsallcode/whiterabbit/api/model/ActivityData.java b/api/src/main/java/org/itsallcode/whiterabbit/api/model/ActivityData.java index 120869c3..1d4abe08 100644 --- a/api/src/main/java/org/itsallcode/whiterabbit/api/model/ActivityData.java +++ b/api/src/main/java/org/itsallcode/whiterabbit/api/model/ActivityData.java @@ -61,4 +61,4 @@ public interface ActivityData * the new comment. */ void setComment(String comment); -} \ No newline at end of file +} diff --git a/api/src/main/java/org/itsallcode/whiterabbit/api/model/DayData.java b/api/src/main/java/org/itsallcode/whiterabbit/api/model/DayData.java index 87969568..bd544be8 100644 --- a/api/src/main/java/org/itsallcode/whiterabbit/api/model/DayData.java +++ b/api/src/main/java/org/itsallcode/whiterabbit/api/model/DayData.java @@ -130,4 +130,4 @@ public interface DayData * new new activities. */ void setActivities(List activities); -} \ No newline at end of file +} diff --git a/api/src/main/java/org/itsallcode/whiterabbit/api/model/DayType.java b/api/src/main/java/org/itsallcode/whiterabbit/api/model/DayType.java index b44af6d4..0880e5f8 100644 --- a/api/src/main/java/org/itsallcode/whiterabbit/api/model/DayType.java +++ b/api/src/main/java/org/itsallcode/whiterabbit/api/model/DayType.java @@ -20,7 +20,7 @@ public enum DayType private final boolean workDay; - private DayType(final boolean workDay) + DayType(final boolean workDay) { this.workDay = workDay; } diff --git a/api/src/main/java/org/itsallcode/whiterabbit/api/model/MonthData.java b/api/src/main/java/org/itsallcode/whiterabbit/api/model/MonthData.java index c75f083d..70b82980 100644 --- a/api/src/main/java/org/itsallcode/whiterabbit/api/model/MonthData.java +++ b/api/src/main/java/org/itsallcode/whiterabbit/api/model/MonthData.java @@ -68,4 +68,4 @@ public interface MonthData * the new {@link DayData}s. */ void setDays(List days); -} \ No newline at end of file +} diff --git a/api/src/main/java/org/itsallcode/whiterabbit/api/model/Project.java b/api/src/main/java/org/itsallcode/whiterabbit/api/model/Project.java index 555c8422..e4524097 100644 --- a/api/src/main/java/org/itsallcode/whiterabbit/api/model/Project.java +++ b/api/src/main/java/org/itsallcode/whiterabbit/api/model/Project.java @@ -26,4 +26,4 @@ public interface Project * @return the cost carrier of this project. */ String getCostCarrier(); -} \ No newline at end of file +} diff --git a/api/src/main/java/org/itsallcode/whiterabbit/api/model/ProjectReport.java b/api/src/main/java/org/itsallcode/whiterabbit/api/model/ProjectReport.java index d8a1a9d8..1b440e04 100644 --- a/api/src/main/java/org/itsallcode/whiterabbit/api/model/ProjectReport.java +++ b/api/src/main/java/org/itsallcode/whiterabbit/api/model/ProjectReport.java @@ -28,4 +28,4 @@ public interface ProjectReport * @return the activities in this project report. */ List getProjects(); -} \ No newline at end of file +} diff --git a/docs/developer_guide.md b/docs/developer_guide.md index 825f808b..d7a565d5 100644 --- a/docs/developer_guide.md +++ b/docs/developer_guide.md @@ -20,7 +20,7 @@ echo "data = $HOME/time-recording-data/" > $HOME/.whiterabbit.properties # To skip unit and ui-tests, run ./gradlew build installPlugins -x test -x uiTest # Run -java -jar jfxui/build/libs/white-rabbit-fx-[-SNAPSHOT].jar +java -jar product/build/libs/white-rabbit-fx-[-SNAPSHOT].jar # Build and run, loading plugins from $HOME/.whiterabbit/plugins/ ./gradlew run @@ -28,6 +28,9 @@ java -jar jfxui/build/libs/white-rabbit-fx-[-SNAPSHOT].jar # Build and run including plugins. Useful when developing plugins. # Make sure to remove unwanted plugins from $HOME/.whiterabbit/plugins/ ./gradlew runWithPlugins + +# Build and run the shadowJar of the final product. +./gradlew runProduct ``` ### Running Tests @@ -66,6 +69,7 @@ Precondition for Windows: Install the [WiX Toolset](https://wixtoolset.org) and ```sh ./gradlew jpackage --info ``` + ### Creating a Release #### Preparations diff --git a/docs/user_guide.md b/docs/user_guide.md index 02a80a9e..ca34f6e8 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -137,47 +137,7 @@ WhiteRabbit logs to stdout and to `$data/logs/white-rabbit.log` where `$data` is ## Using Plugins -The default plugins pmsmart, holidays-calculator and csv are already included in the download packages. To install third-party plugins, copy them to `$HOME/.whiterabbit/plugins/`. - -### Using the PM-Smart Exporter Plugin - -#### Requirements for Using the PM-Smart Exporter Plugin - -* Microsoft Edge browser with password-less access to pm-smart - -#### Setup and usage - -1. Create a project configuration as described [above](#project_config). Make sure to use the same IDs for `costCarrier` as in pm-smart. -1. Add the base URL of your pm-smart server to the configuration file: - - ```properties - pmsmart.baseurl = http://my-pmsmart.example.com - ``` - -1. In pm-smart open the week view ("Wochenansicht") and add favorites for all projects you use. The export will only work for projects added as favorite. -1. Start the export in WhiteRabbit: - - 1. Select the month that you want to export - 1. Select menu Reports > Project report - 1. Click button "Export to pmsmart" - -#### Optional configuration settings - -Optionally you can configure pmsmart plugin to skip transfer of a comment for each activity. - -For each activity in WhiteRabbit you can enter a comment. By default pmsmart plugin transfers these comment to pm-smart. As the web ui is quite slow, transfer of comments can take a while. If you want to speed-up pm-smart export by skipping transfer of comments you can add an optional property to WhiteRabbit configuration file: - -```properties -pmsmart.transfer.comments = false -``` - -Optionally you can configure pmsmart plugin to clear durations for all other projects, not matching any activity recorded in WhiteRabbit. - -For each day pm-smart plugin by default transfers the durations of all activities entered into WhiteRabbit. If pm-smart contains durations for other projects then pm-smart plugin does not overwrite these. This will especially happen if users export their time recordings for a particular month multiple times and change the selection of activities in between. In order to ensure consistent data in pm-smart you can add an optional property to WhiteRabbit configuration file: - -```properties -pmsmart.clear_other_projects = true -``` +The default plugins `holidays-calculator` and `csv` are already included in the download packages. To install third-party plugins, copy them to `$HOME/.whiterabbit/plugins/`. ### Using the Holidays Calculator Plugin diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9355b415..df97d72b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/jfxui/build.gradle b/jfxui/build.gradle index 7ba7c3f7..34d7f473 100644 --- a/jfxui/build.gradle +++ b/jfxui/build.gradle @@ -1,9 +1,3 @@ -plugins { - id 'com.github.johnrengelman.shadow' - id 'org.panteleyev.jpackageplugin' - id 'org.gradle.crypto.checksum' -} - dependencies { implementation project(':logic') def javaFxVersion = libs.versions.javafx.get() @@ -28,87 +22,6 @@ dependencies { testRuntimeOnly libs.monocle } -shadowJar { - archiveBaseName.set('white-rabbit-fx') - archiveClassifier.set(null) - archiveVersion.set(project.version.toString()) -} - -task createSha512ChecksumExecutableJar(type: org.gradle.crypto.checksum.Checksum, dependsOn: [tasks.shadowJar]) { - files = files(tasks.shadowJar.outputs.files) - outputDir = new File(project.buildDir, "libs-checksums") - algorithm = org.gradle.crypto.checksum.Checksum.Algorithm.SHA512 - appendFileNameToChecksum = true -} - -tasks.shadowJar.configure { - finalizedBy createSha512ChecksumExecutableJar -} - -ext { - mainClass = 'org.itsallcode.whiterabbit.jfxui.App' -} - -jar { - manifest { - attributes 'Main-Class': project.mainClass - } -} - -build.dependsOn shadowJar - -def jPackageDir = "$buildDir/jpackage-jars" - -project(':plugins').afterEvaluate { - List pluginJarTasks = project(':plugins').ext.pluginBuildTasks - - def userHome = System.properties['user.home'] - if(project.hasProperty('user.home')) { - userHome = project.property('user.home') - } - - task runWithPlugins(type: JavaExec, dependsOn: [shadowJar] + pluginJarTasks, group: 'run') { - group = "run" - def pluginJars = files(pluginJarTasks.collect {it.outputs.files }) - classpath = shadowJar.outputs.files + pluginJars - mainClass = project.mainClass - workingDir = rootProject.projectDir - systemProperties = ['user.home': userHome] - } - - task runJfxuiWithPlugins(dependsOn: runWithPlugins, group: 'run') { - // deprecated - } - - def getUnwantedPlatforms = { - OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem - if(os.isLinux()) { - return ["mac", "win"] - } - if(os.isWindows()) { - return ["mac", "linux"] - } - if(os.isMacOsX()) { - return ["linux", "win"] - } - } - - task copyJPackageDependencies(type: Copy, dependsOn: ["jar"]) { - def unwantedPlatforms = getUnwantedPlatforms() - from(configurations.runtimeClasspath.filter { file -> - unwantedPlatforms.every { platform -> !file.name.endsWith("-${platform}.jar") } - }).into jPackageDir - from(tasks.jar.outputs.files).into jPackageDir - from(pluginJarTasks).into jPackageDir - } -} - -task run(type: JavaExec, dependsOn: shadowJar, group: 'run') { - classpath = shadowJar.outputs.files - mainClass = project.mainClass - workingDir = rootProject.projectDir -} - sourceSets { uiTest { compileClasspath += sourceSets.main.output @@ -153,51 +66,12 @@ check.dependsOn uiTest tasks["jacocoTestReport"].executionData fileTree(project.buildDir).include("jacoco/*.exec") -tasks.jpackage { - dependsOn("copyJPackageDependencies") - - input = jPackageDir - destination = "$buildDir/jpackage-dist" - - appName = "WhiteRabbit" - vendor = '"It\'s all code"' - copyright = '"Copyright (C) 2024 Christoph Pirkl "' - licenseFile = "${rootProject.rootDir}/LICENSE" - appDescription = '"A time recording tool"' - icon = "${projectDir}/src/main/resources/icon.png" - - mainJar = tasks.jar.archiveFileName.get() - mainClass = project.mainClass - - verbose = false - arguments = [] - javaOptions = ["-Dfile.encoding=UTF-8"] - additionalParameters = ["--about-url", "https://github.com/itsallcode/white-rabbit/blob/main/README.md"] - windows { - winConsole = false - winPerUserInstall = true - winMenu = true - winMenuGroup = "Time Recording" - winShortcut = true - icon = "${projectDir}/src/main/resources/icon.ico" - } - - mac { - macPackageIdentifier = "org.itsallcode.white-rabbit" - macPackageName = "WhiteRabbit" - icon = "${projectDir}/src/main/resources/icon.icns" - } -} - -task createSha512ChecksumsNativePackage(type: org.gradle.crypto.checksum.Checksum, dependsOn: [tasks.shadowJar]) { - files = files(fileTree(tasks.jpackage.destination) { - include '*' - }) - outputDir = new File(project.buildDir, "jpackage-checksums") - algorithm = org.gradle.crypto.checksum.Checksum.Algorithm.SHA512 - appendFileNameToChecksum = true +ext { + mainClass = 'org.itsallcode.whiterabbit.jfxui.App' } -tasks.jpackage.configure { - finalizedBy createSha512ChecksumsNativePackage +task run(type: JavaExec, group: 'run') { + mainClass = project.mainClass + workingDir = rootProject.projectDir + classpath = sourceSets.main.runtimeClasspath } diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/JavaFxUtil.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/JavaFxUtil.java index 4fb5ed2d..26270749 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/JavaFxUtil.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/JavaFxUtil.java @@ -7,7 +7,7 @@ import javafx.application.Platform; -public class JavaFxUtil +public final class JavaFxUtil { private JavaFxUtil() { diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/OsCheck.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/OsCheck.java index 4b8488f8..ac57229d 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/OsCheck.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/OsCheck.java @@ -49,7 +49,10 @@ else if (os.indexOf("linux") >= 0) { return OSType.LINUX; } - return OSType.OTHER; + else + { + return OSType.OTHER; + } } public boolean isDesktopSupported() diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/UiActions.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/UiActions.java index 992b9a91..e623fa78 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/UiActions.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/UiActions.java @@ -33,7 +33,7 @@ import javafx.stage.Modality; import javafx.stage.Stage; -public class UiActions +public final class UiActions { private static final Logger LOG = LogManager.getLogger(UiActions.class); @@ -95,7 +95,7 @@ private void createAndOpenDirectory(final Path directory) openFileWithDefaultProgram(directory); } - private void createDir(final Path directory) + private static void createDir(final Path directory) { try { @@ -195,14 +195,14 @@ private String formatAboutHeaderText() return "White Rabbit version " + properties.getVersion() + " (" + properties.getBuildDate() + ")"; } - private String formatSystemPropertyValues(final Map properties) + private static String formatSystemPropertyValues(final Map properties) { return properties.entrySet().stream() // .map(entry -> entry.getKey() + ": " + System.getProperty(entry.getValue())) // .collect(joining("\n")); } - private Map getProperties() + private static Map getProperties() { final Map properties = new LinkedHashMap<>(); properties.put("Java Vendor", "java.vendor"); diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/property/ClockPropertyFactory.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/property/ClockPropertyFactory.java index d3ba0d83..7990b476 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/property/ClockPropertyFactory.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/property/ClockPropertyFactory.java @@ -14,7 +14,7 @@ import javafx.beans.property.SimpleObjectProperty; -public class ClockPropertyFactory +public final class ClockPropertyFactory { private static final Logger LOG = LogManager.getLogger(ClockPropertyFactory.class); diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/service/DesktopService.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/service/DesktopService.java index aa25d189..40903e5f 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/service/DesktopService.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/service/DesktopService.java @@ -8,6 +8,7 @@ public interface DesktopService { + static DesktopService create() { final Logger log = LogManager.getLogger(DesktopService.class); diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/service/RealDesktopService.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/service/RealDesktopService.java index e1de1e13..57a39040 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/service/RealDesktopService.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/service/RealDesktopService.java @@ -14,13 +14,13 @@ class RealDesktopService implements DesktopService private static final Logger LOG = LogManager.getLogger(RealDesktopService.class); private final Desktop desktop; - RealDesktopService(Desktop desktop) + RealDesktopService(final Desktop desktop) { this.desktop = desktop; } @Override - public void open(Path file) + public void open(final Path file) { SwingUtil.invokeInAwtEventQueue(() -> { LOG.info("Opening file {} with default application", file); @@ -34,4 +34,4 @@ public void open(Path file) } }); } -} \ No newline at end of file +} diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/splashscreen/SplashScreenLoader.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/splashscreen/SplashScreenLoader.java index 588a4605..58911691 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/splashscreen/SplashScreenLoader.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/splashscreen/SplashScreenLoader.java @@ -24,10 +24,10 @@ public class SplashScreenLoader extends Preloader { private static final Logger LOG = LogManager.getLogger(SplashScreenLoader.class); - private Stage splashScreen; + private Stage splashScreen = null; @Override - public void start(Stage stage) + public void start(final Stage stage) { splashScreen = stage; splashScreen.initStyle(StageStyle.UNDECORATED); @@ -49,7 +49,7 @@ public Scene createScene() return new Scene(root, 300, 300); } - private Image loadImage(String resourceName) + private Image loadImage(final String resourceName) { try (InputStream iconStream = getClass().getResourceAsStream(resourceName)) { @@ -62,11 +62,10 @@ private Image loadImage(String resourceName) } @Override - public void handleApplicationNotification(PreloaderNotification notification) + public void handleApplicationNotification(final PreloaderNotification notification) { - if (notification instanceof ProgressPreloaderNotification) + if (notification instanceof final ProgressPreloaderNotification progressNotification) { - final ProgressPreloaderNotification progressNotification = (ProgressPreloaderNotification) notification; LOG.debug("Preloader application notification: {}", progressNotification.getNotificationType()); if (progressNotification.getNotificationType() == Type.STARTUP_FINISHED) { @@ -81,7 +80,7 @@ public void handleApplicationNotification(PreloaderNotification notification) } @Override - public boolean handleErrorNotification(ErrorNotification info) + public boolean handleErrorNotification(final ErrorNotification info) { splashScreen.hide(); final Alert alert = createAlert(info); @@ -89,7 +88,7 @@ public boolean handleErrorNotification(ErrorNotification info) return false; } - private Alert createAlert(ErrorNotification info) + private static Alert createAlert(final ErrorNotification info) { final Throwable exception = info.getCause(); if (exception instanceof OtherInstanceAlreadyRunningException) @@ -97,7 +96,7 @@ private Alert createAlert(ErrorNotification info) final String message = "Another instance of WhiteRabbit is already running.\n\n" + exception.getMessage(); return new Alert(AlertType.WARNING, message, ButtonType.OK); } - final String location = info.getLocation() != null ? info.getLocation() + "\n" : ""; + final String location = info.getLocation() != null ? (info.getLocation() + "\n") : ""; final String message = "Error during initialization: " + location + info.getDetails() + "\n" + exception; LOG.error(message, exception); return new Alert(AlertType.ERROR, message, ButtonType.OK); diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/systemmenu/DesktopIntegration.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/systemmenu/DesktopIntegration.java index 3b3d66d7..f288891e 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/systemmenu/DesktopIntegration.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/systemmenu/DesktopIntegration.java @@ -4,7 +4,7 @@ public interface DesktopIntegration { - public static DesktopIntegration getInstance() + static DesktopIntegration getInstance() { return StaticInstanceHolder.getInstance(); } @@ -12,4 +12,4 @@ public static DesktopIntegration getInstance() void register(); void setUiActions(UiActions actions); -} \ No newline at end of file +} diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/systemmenu/StaticInstanceHolder.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/systemmenu/StaticInstanceHolder.java index b8f7d3de..10a07fa0 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/systemmenu/StaticInstanceHolder.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/systemmenu/StaticInstanceHolder.java @@ -4,7 +4,7 @@ import org.itsallcode.whiterabbit.jfxui.OsCheck; -class StaticInstanceHolder +final class StaticInstanceHolder { private static DesktopIntegration instance; @@ -26,7 +26,7 @@ static class InstanceFactory { private final OsCheck osCheck; - InstanceFactory(OsCheck osCheck) + InstanceFactory(final OsCheck osCheck) { this.osCheck = osCheck; } diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/DelegatingChangeListener.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/DelegatingChangeListener.java index 0f087c2a..3538cdd1 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/DelegatingChangeListener.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/DelegatingChangeListener.java @@ -9,18 +9,18 @@ public class DelegatingChangeListener implements ChangeListener private final ChangeListener delegate; private final BooleanProperty currentlyUpdating; - public DelegatingChangeListener(ChangeListener delegate, BooleanProperty currentlyUpdating) + public DelegatingChangeListener(final ChangeListener delegate, final BooleanProperty currentlyUpdating) { this.delegate = delegate; this.currentlyUpdating = currentlyUpdating; } @Override - public void changed(ObservableValue observable, T oldValue, T newValue) + public void changed(final ObservableValue observable, final T oldValue, final T newValue) { if (!currentlyUpdating.get()) { delegate.changed(observable, oldValue, newValue); } } -} \ No newline at end of file +} diff --git a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/converter/DayTypeStringConverter.java b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/converter/DayTypeStringConverter.java index 5fdcf72f..f860cb66 100644 --- a/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/converter/DayTypeStringConverter.java +++ b/jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/table/converter/DayTypeStringConverter.java @@ -7,14 +7,14 @@ public class DayTypeStringConverter extends StringConverter { @Override - public String toString(DayType object) + public String toString(final DayType object) { return object != null ? object.name() : null; } @Override - public DayType fromString(String string) + public DayType fromString(final String string) { return DayType.valueOf(string); } -} \ No newline at end of file +} diff --git a/jfxui/src/uiTest/java/org/itsallcode/whiterabbit/jfxui/DailyProjectReportTest.java b/jfxui/src/uiTest/java/org/itsallcode/whiterabbit/jfxui/DailyProjectReportTest.java index 16beb3ff..f5cdb58a 100644 --- a/jfxui/src/uiTest/java/org/itsallcode/whiterabbit/jfxui/DailyProjectReportTest.java +++ b/jfxui/src/uiTest/java/org/itsallcode/whiterabbit/jfxui/DailyProjectReportTest.java @@ -50,7 +50,7 @@ void exportButtonsFromPluginsAvailable() { time().tickSeparateMinutes(2); final DailyProjectReportWindow report = app().openDailyProjectReport(); - report.assertExportButtons("Export to demo", "Export to pmsmart"); + report.assertExportButtons("Export to demo", "Export to csv"); report.closeViaEscKey(); } @@ -72,7 +72,7 @@ void filledProjectReport() @Override @Start - void start(Stage stage) + void start(final Stage stage) { setLocale(Locale.GERMANY); setInitialTime(Instant.parse("2007-12-03T10:15:30.20Z")); diff --git a/logic/src/main/java/org/itsallcode/whiterabbit/logic/service/plugin/origin/PluginOrigin.java b/logic/src/main/java/org/itsallcode/whiterabbit/logic/service/plugin/origin/PluginOrigin.java index 65ddbdbc..dcce1524 100644 --- a/logic/src/main/java/org/itsallcode/whiterabbit/logic/service/plugin/origin/PluginOrigin.java +++ b/logic/src/main/java/org/itsallcode/whiterabbit/logic/service/plugin/origin/PluginOrigin.java @@ -28,7 +28,7 @@ public static PluginOrigin forJar(final Path jar) private static ClassLoader getBaseClassLoader() { - return PluginOrigin.class.getClassLoader(); + return Thread.currentThread().getContextClassLoader(); } private static ClassLoader createClassLoader(final Path jar) diff --git a/product/build.gradle b/product/build.gradle new file mode 100644 index 00000000..eae31679 --- /dev/null +++ b/product/build.gradle @@ -0,0 +1,170 @@ +plugins { + id 'com.github.johnrengelman.shadow' + id 'org.panteleyev.jpackageplugin' + id 'org.gradle.crypto.checksum' +} + +dependencies { + runtimeOnly project(':jfxui') + runtimeOnly project(':plugins').subprojects +} + +shadowJar { + mergeServiceFiles() + archiveBaseName.set('white-rabbit-fx') + archiveClassifier.set(null) + archiveVersion.set(project.version.toString()) +} + +task createSha512ChecksumExecutableJar(type: org.gradle.crypto.checksum.Checksum, dependsOn: [tasks.shadowJar]) { + files = files(tasks.shadowJar.outputs.files) + outputDir = new File(project.buildDir, "libs-checksums") + algorithm = org.gradle.crypto.checksum.Checksum.Algorithm.SHA512 + appendFileNameToChecksum = true +} + +tasks.shadowJar.configure { + finalizedBy createSha512ChecksumExecutableJar +} + +ext { + mainClass = project(':jfxui').ext.mainClass +} + +jar { + manifest { + attributes 'Main-Class': project.mainClass + } +} + +build.dependsOn shadowJar + +def jPackageDir = "$buildDir/jpackage-jars" + +task runProduct(type: JavaExec, dependsOn: [shadowJar], group: 'run') { + group = "run" + classpath = shadowJar.outputs.files + mainClass = project.mainClass + workingDir = rootProject.projectDir +} + +task runWithPlugins(type: JavaExec, group: 'run') { + group = "run" + mainClass = project.mainClass + workingDir = rootProject.projectDir + classpath = sourceSets.main.runtimeClasspath +} + +def getUnwantedPlatforms = { + OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem + if(os.isLinux()) { + return ["mac", "win"] + } + if(os.isWindows()) { + return ["mac", "linux"] + } + if(os.isMacOsX()) { + return ["linux", "win"] + } +} + +task copyJPackageDependencies(type: Copy, dependsOn: ["jar"]) { + def unwantedPlatforms = getUnwantedPlatforms() + from(configurations.runtimeClasspath.filter { file -> + unwantedPlatforms.every { platform -> !file.name.endsWith("-${platform}.jar") } + }).into jPackageDir + from(tasks.jar.outputs.files).into jPackageDir +} + +sourceSets { + uiTest { + compileClasspath += sourceSets.main.output + runtimeClasspath += sourceSets.main.output + } +} + +configurations { + uiTestImplementation.extendsFrom testImplementation + uiTestRuntimeOnly.extendsFrom testRuntimeOnly +} + +task uiTest(type: Test, group: 'verification') { + description = 'Runs integration tests.' + + useJUnitPlatform { + if(project.hasProperty('skipFlakyTests') && project.property('skipFlakyTests') == 'true') { + excludeTags 'FLAKY' + } + } + + forkEvery 5 + jvmArgs '-XX:+HeapDumpOnOutOfMemoryError', '-XX:+EnableDynamicAgentLoading' + + if(!project.hasProperty("uiTestsHeadless") || project.property("uiTestsHeadless") != "false") { + systemProperty 'java.awt.headless', 'true' + systemProperty 'testfx.headless', 'true' + systemProperty 'testfx.robot', 'glass' + systemProperty 'glass.platform', 'Monocle' + systemProperty 'monocle.platform', 'Headless' + } + + systemProperty 'testfx.launch.timeout', '60000' + systemProperty 'testfx.setup.timeout', '30000' + + testClassesDirs = sourceSets.uiTest.output.classesDirs + classpath = sourceSets.uiTest.runtimeClasspath + shouldRunAfter test +} + +check.dependsOn uiTest + +tasks["jacocoTestReport"].executionData fileTree(project.buildDir).include("jacoco/*.exec") + +tasks.jpackage { + dependsOn("copyJPackageDependencies") + + input = jPackageDir + destination = "$buildDir/jpackage-dist" + + appName = "WhiteRabbit" + vendor = '"It\'s all code"' + copyright = '"Copyright (C) 2024 Christoph Pirkl "' + licenseFile = "${rootProject.rootDir}/LICENSE" + appDescription = '"A time recording tool"' + icon = "${project(':jfxui').projectDir}/src/main/resources/icon.png" + + mainJar = tasks.jar.archiveFileName.get() + mainClass = project.mainClass + + verbose = false + arguments = [] + javaOptions = ["-Dfile.encoding=UTF-8"] + additionalParameters = ["--about-url", "https://github.com/itsallcode/white-rabbit/blob/main/README.md"] + windows { + winConsole = false + winPerUserInstall = true + winMenu = true + winMenuGroup = "Time Recording" + winShortcut = true + icon = "${projectDir}/src/main/resources/icon.ico" + } + + mac { + macPackageIdentifier = "org.itsallcode.white-rabbit" + macPackageName = "WhiteRabbit" + icon = "${projectDir}/src/main/resources/icon.icns" + } +} + +task createSha512ChecksumsNativePackage(type: org.gradle.crypto.checksum.Checksum, dependsOn: [tasks.shadowJar]) { + files = files(fileTree(tasks.jpackage.destination) { + include '*' + }) + outputDir = new File(project.buildDir, "jpackage-checksums") + algorithm = org.gradle.crypto.checksum.Checksum.Algorithm.SHA512 + appendFileNameToChecksum = true +} + +tasks.jpackage.configure { + finalizedBy createSha512ChecksumsNativePackage +} diff --git a/jfxui/create-icons.sh b/product/create-icons.sh similarity index 95% rename from jfxui/create-icons.sh rename to product/create-icons.sh index d60a573b..2cb6df97 100755 --- a/jfxui/create-icons.sh +++ b/product/create-icons.sh @@ -7,7 +7,7 @@ set -o pipefail # See https://www.codingforentrepreneurs.com/blog/create-icns-icons-for-macos-apps/ base_dir="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -icon="$base_dir/src/main/resources/icon.png" +icon="$base_dir/../jfxui/src/main/resources/icon.png" output_icons="$base_dir/src/main/resources/icon.icns" iconset_dir="$base_dir/build/icon.iconset" diff --git a/jfxui/src/main/resources/icon.icns b/product/src/main/resources/icon.icns similarity index 100% rename from jfxui/src/main/resources/icon.icns rename to product/src/main/resources/icon.icns diff --git a/jfxui/src/main/resources/icon.ico b/product/src/main/resources/icon.ico similarity index 100% rename from jfxui/src/main/resources/icon.ico rename to product/src/main/resources/icon.ico diff --git a/settings.gradle b/settings.gradle index a3f943a9..1001281c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,6 +8,7 @@ include 'logic' include 'jfxui' include 'api' +include 'product' include 'plugins' include 'plugins:demo' include 'plugins:holiday-calculator'