From 201bfc5b7d990b2dd7250c43c2f966e4897d2116 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 14:33:48 +0200 Subject: [PATCH 01/11] Remove gluon stuff and run as plain desktop app. --- build.gradle | 106 +++++++++++++++--- settings.gradle | 3 - src/main/java/biz/nellemann/mdexpl/App.java | 50 ++++++--- .../biz/nellemann/mdexpl/MainPresenter.java | 55 +++++++++ .../nellemann/mdexpl/NetworkServiceCell.java | 41 +++---- .../biz/nellemann/mdexpl/model/MainModel.java | 20 ---- .../mdexpl/model/NetworkService.java | 2 +- .../mdexpl/service/DiscoveryService.java | 4 - .../nellemann/mdexpl/view/AboutPresenter.java | 64 ----------- .../nellemann/mdexpl/view/AppViewManager.java | 52 --------- .../nellemann/mdexpl/view/MainPresenter.java | 77 ------------- src/main/java/module-info.java | 12 ++ .../{view => }/configuration.properties | 0 .../resources/biz/nellemann/mdexpl/main.fxml | 12 ++ .../biz/nellemann/mdexpl/view/about.fxml | 45 -------- .../biz/nellemann/mdexpl/view/main.fxml | 16 --- 16 files changed, 227 insertions(+), 332 deletions(-) create mode 100644 src/main/java/biz/nellemann/mdexpl/MainPresenter.java delete mode 100644 src/main/java/biz/nellemann/mdexpl/model/MainModel.java delete mode 100644 src/main/java/biz/nellemann/mdexpl/view/AboutPresenter.java delete mode 100644 src/main/java/biz/nellemann/mdexpl/view/AppViewManager.java delete mode 100644 src/main/java/biz/nellemann/mdexpl/view/MainPresenter.java create mode 100644 src/main/java/module-info.java rename src/main/resources/biz/nellemann/mdexpl/{view => }/configuration.properties (100%) create mode 100644 src/main/resources/biz/nellemann/mdexpl/main.fxml delete mode 100644 src/main/resources/biz/nellemann/mdexpl/view/about.fxml delete mode 100644 src/main/resources/biz/nellemann/mdexpl/view/main.fxml diff --git a/build.gradle b/build.gradle index 088bbbb..c5adeb8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,16 @@ -import org.apache.tools.ant.filters.ReplaceTokens - plugins { id 'application' id 'org.openjfx.javafxplugin' version '0.0.14' - id "com.github.johnrengelman.shadow" version "8.1.1" - id 'com.gluonhq.gluonfx-gradle-plugin' version '1.0.19' + id 'com.google.osdetector' version '1.7.3' + id 'org.beryx.jlink' version '2.26.0' + //id "com.github.johnrengelman.shadow" version "8.1.1" + //id 'com.gluonhq.gluonfx-gradle-plugin' version '1.0.19' + } +import org.apache.tools.ant.filters.ReplaceTokens + repositories { mavenCentral() mavenLocal() @@ -27,23 +30,22 @@ tasks.withType(JavaCompile).configureEach { java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - modularity.inferModulePath = false + //modularity.inferModulePath = false } /* This is to be able to build with a JDK not bundled with JavaFX */ javafx { - version = '21+' + version = '17.0.8' modules = [ 'javafx.controls', 'javafx.fxml' ] // platform("linux-aarch64") } dependencies { - implementation 'com.gluonhq:charm-glisten:6.2.3' - implementation 'com.gluonhq:glisten-afterburner:2.1.0' - implementation 'org.slf4j:slf4j-api:2.0.7' // Logging API runtimeOnly 'org.slf4j:slf4j-simple:2.0.7' // Logging API + implementation 'javax.inject:javax.inject:1' + implementation 'javax.annotation:javax.annotation-api:1.3.2' implementation 'org.jmdns:jmdns:3.5.8' testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0' @@ -54,7 +56,7 @@ test { useJUnitPlatform() } - +/* gluonfx { verbose = true target = project.hasProperty("target") ? project.getProperty("target") : 'host' @@ -102,18 +104,96 @@ gluonfx { //skipSigning = true // Will not run or deploy if not signed } -} +}*/ +/* shadowJar { //archiveBaseName.set("vtd-poc-app") //archiveClassifier.set('all') archiveVersion.set("${System.env.BITBUCKET_BRANCH ?: 'dev' }") -} +}*/ -tasks.build.dependsOn tasks.shadowJar +//tasks.build.dependsOn tasks.shadowJar tasks.processResources { filesMatching('**/configuration.properties') { filter(ReplaceTokens, tokens: [copyright: '2023', version: System.env.BITBUCKET_BUILD_NUMBER ?: 'development' ]) } } + +jlink { + + forceMerge 'slf4j' + + options = [ + '--strip-debug', + '--compress', '2', + '--no-header-files', + '--no-man-pages' + ] + + launcher { + name = 'mDNS-Explorer' + noConsole = true + } + + // Only works with Java 14 (and later) + jpackage { + imageName = "mDNS-Explorer" + skipInstaller = true + installerName = "mDNS-Explorer-${osdetector.arch}" + installerOptions += [ + '--vendor', 'Nellemann Data', + '--description', 'mDNS Explorer', + '--copyright', 'Mark Nellemann ', + '--app-version', version + ] + + // Requires: https://wixtoolset.org/ to create installer on Windows + if(osdetector.os == 'windows') { + installerType = 'msi' + skipInstaller = false + installerOptions += [ + '--win-per-user-install', + '--win-dir-chooser', + '--win-menu', + // '--icon', 'src/main/resources/icon.ico' + ] + } + + // Requires: xcode-select --install + if(osdetector.os == 'osx') { + installerType = 'dmg' + skipInstaller = false + installerOptions += [ + //'--icon', 'src/main/resources/icon.icns' + ] + } + + if(osdetector.os == 'linux') { + + installerOptions += [ + '--linux-menu-group', 'Internet', + '--linux-shortcut', + //'--icon', 'src/main/resources/icon_256x256.png' + ] + + if (osdetector.release.isLike('debian')) { + installerType = 'deb' + skipInstaller = false + installerOptions += [ + '--linux-deb-maintainer', 'mark.nellemann@gmail.com' + ] + } else if (osdetector.release.isLike('centos')) { + installerType = 'rpm' + skipInstaller = false + installerOptions += [ + '--linux-rpm-license-type', 'APACHE-20' + ] + } + + } + + } + +} diff --git a/settings.gradle b/settings.gradle index 4aeb3ad..616f1de 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,9 +1,6 @@ pluginManagement { repositories { mavenLocal() - maven { - url "https://nexus.gluonhq.com/nexus/content/repositories/releases" - } gradlePluginPortal() } } diff --git a/src/main/java/biz/nellemann/mdexpl/App.java b/src/main/java/biz/nellemann/mdexpl/App.java index 5742834..83c2d9a 100644 --- a/src/main/java/biz/nellemann/mdexpl/App.java +++ b/src/main/java/biz/nellemann/mdexpl/App.java @@ -1,36 +1,58 @@ package biz.nellemann.mdexpl; -import biz.nellemann.mdexpl.view.AppViewManager; -import com.gluonhq.charm.glisten.application.AppManager; -import com.gluonhq.charm.glisten.visual.Swatch; import javafx.application.Application; +import javafx.application.Platform; +import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; +import javafx.stage.WindowEvent; -import java.util.Objects; +import java.awt.Taskbar; +import java.awt.Toolkit; +import java.awt.Taskbar.Feature; +import java.io.IOException; public class App extends Application { - private final AppManager appManager = AppManager.initialize(this::postInit); - @Override public void init() { - AppViewManager.registerViewsAndDrawer(); + Platform.setImplicitExit(true); } @Override - public void start(Stage primaryStage) { - //System.setProperty(com.gluonhq.attach.util.Constants.ATTACH_DEBUG,"true"); - appManager.start(primaryStage); + public void start(Stage primaryStage) throws IOException { + + // Make all stages close and the app exit when the primary stage is closed + primaryStage.setOnCloseRequest(e -> Platform.exit()); + + // Set icon on the application bar + var appIcon = new Image("/icon.png"); + primaryStage.getIcons().add(appIcon); + + // Set icon on the taskbar/dock + if (Taskbar.isTaskbarSupported()) { + var taskbar = Taskbar.getTaskbar(); + + if (taskbar.isSupported(Feature.ICON_IMAGE)) { + final Toolkit defaultToolkit = Toolkit.getDefaultToolkit(); + var dockIcon = defaultToolkit.getImage(getClass().getResource("/icon.png")); + taskbar.setIconImage(dockIcon); + } + + } + + FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("main.fxml")); + Scene scene = new Scene(fxmlLoader.load()); + primaryStage.setTitle("mDNS Explorer"); + primaryStage.setScene(scene); + primaryStage.show(); } - private void postInit(Scene scene) { - Swatch.GREEN.assignTo(scene); - //scene.getStylesheets().add(App.class.getResource("style.css").toExternalForm()); - ((Stage) scene.getWindow()).getIcons().add(new Image(Objects.requireNonNull(App.class.getResourceAsStream("/icon.png")))); + @Override + public void stop() { } diff --git a/src/main/java/biz/nellemann/mdexpl/MainPresenter.java b/src/main/java/biz/nellemann/mdexpl/MainPresenter.java new file mode 100644 index 0000000..9bc698a --- /dev/null +++ b/src/main/java/biz/nellemann/mdexpl/MainPresenter.java @@ -0,0 +1,55 @@ +package biz.nellemann.mdexpl; + +import biz.nellemann.mdexpl.model.NetworkService; +import biz.nellemann.mdexpl.service.DiscoveryService; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.control.ListView; +import javafx.scene.layout.BorderPane; +import javafx.stage.WindowEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.util.ResourceBundle; + +public class MainPresenter { + + private static final Logger log = LoggerFactory.getLogger(MainPresenter.class); + + + @FXML + private BorderPane view; + + @FXML + private ResourceBundle resources; + + @FXML + private ListView listView; + + @Inject + DiscoveryService discoveryService; + + private final ObservableList devicesList = FXCollections.observableArrayList(); + + + @FXML + public void initialize() { + log.info("initialize()"); + + view.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, event -> { + log.info("Window closing"); + discoveryService.destroy(); + }); + + discoveryService = new DiscoveryService(); + discoveryService.initialize(); + discoveryService.setObservableList(devicesList); + listView.setItems(devicesList); + listView.setCellFactory(p -> new NetworkServiceCell()); + } + + +} diff --git a/src/main/java/biz/nellemann/mdexpl/NetworkServiceCell.java b/src/main/java/biz/nellemann/mdexpl/NetworkServiceCell.java index c01398f..bbd658d 100644 --- a/src/main/java/biz/nellemann/mdexpl/NetworkServiceCell.java +++ b/src/main/java/biz/nellemann/mdexpl/NetworkServiceCell.java @@ -1,40 +1,37 @@ package biz.nellemann.mdexpl; import biz.nellemann.mdexpl.model.NetworkService; -import com.gluonhq.attach.util.impl.ClipboardUtils; -import com.gluonhq.charm.glisten.control.CharmListCell; -import com.gluonhq.charm.glisten.control.ListTile; +import javafx.application.Platform; +import javafx.scene.control.ListCell; import javafx.scene.input.ClipboardContent; +import javafx.scene.layout.HBox; import javafx.scene.shape.Rectangle; import javafx.scene.input.Clipboard; -import java.util.Optional; -public class NetworkServiceCell extends CharmListCell { +public class NetworkServiceCell extends ListCell { - private final ListTile tile; private final Rectangle icon; private final Clipboard clipboard; private final ClipboardContent clipboardContent; + HBox hBox; public NetworkServiceCell() { clipboard = Clipboard.getSystemClipboard(); clipboardContent = new ClipboardContent(); - this.tile = new ListTile(); - //imageView = new ImageView(); - //imageView.setFitHeight(15); - //imageView.setFitWidth(25); - //tile.setPrimaryGraphic(imageView); icon = new Rectangle(); icon.setHeight(25); icon.setWidth(25); - tile.setPrimaryGraphic(icon); - tile.setOnMouseClicked(e -> { - clipboardContent.putString(itemProperty().get().getUrl()); - clipboard.setContent(clipboardContent); + this.setGraphic(icon); + + this.setOnMouseClicked(e -> { + if(itemProperty().get() != null) { + clipboardContent.putString(itemProperty().get().getUrl()); + clipboard.setContent(clipboardContent); + }; }); setText(null); } @@ -43,16 +40,14 @@ public class NetworkServiceCell extends CharmListCell { @Override public void updateItem(NetworkService item, boolean empty) { super.updateItem(item, empty); - if (item != null && !empty) { - tile.textProperty().setAll(item.getName(), - item.getApp() + " (" + item.getSubType() + ") - " + item.getUrl() - ); - icon.setFill(item.getColor()); - setGraphic(tile); - } else { + if (empty) { + setText(null); setGraphic(null); + } else { + setText(item.toString()); + icon.setFill(item.getColor()); + setGraphic(icon); } } - } diff --git a/src/main/java/biz/nellemann/mdexpl/model/MainModel.java b/src/main/java/biz/nellemann/mdexpl/model/MainModel.java deleted file mode 100644 index 4c96aaa..0000000 --- a/src/main/java/biz/nellemann/mdexpl/model/MainModel.java +++ /dev/null @@ -1,20 +0,0 @@ -package biz.nellemann.mdexpl.model; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.PostConstruct; -import javax.inject.Singleton; - -@Singleton -public class MainModel { - - private static final Logger log = LoggerFactory.getLogger(MainModel.class); - - - @PostConstruct - public void initialize() { - log.info("initialize()"); - } - -} diff --git a/src/main/java/biz/nellemann/mdexpl/model/NetworkService.java b/src/main/java/biz/nellemann/mdexpl/model/NetworkService.java index b148c0a..d6c5caa 100644 --- a/src/main/java/biz/nellemann/mdexpl/model/NetworkService.java +++ b/src/main/java/biz/nellemann/mdexpl/model/NetworkService.java @@ -74,7 +74,7 @@ public class NetworkService { @Override public String toString() { - return name + " (" + type + "), app=" + app + ", url=" + url; + return name + " (" + app + ") " + url; } diff --git a/src/main/java/biz/nellemann/mdexpl/service/DiscoveryService.java b/src/main/java/biz/nellemann/mdexpl/service/DiscoveryService.java index d72761d..071a619 100644 --- a/src/main/java/biz/nellemann/mdexpl/service/DiscoveryService.java +++ b/src/main/java/biz/nellemann/mdexpl/service/DiscoveryService.java @@ -7,8 +7,6 @@ import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import javax.inject.Singleton; import javax.jmdns.JmDNS; import java.io.IOException; @@ -71,7 +69,6 @@ public class DiscoveryService { }}; - @PostConstruct public void initialize() { log.info("initialize()"); try { @@ -82,7 +79,6 @@ public class DiscoveryService { } - @PreDestroy public void destroy() { if(jmdns != null) { try { diff --git a/src/main/java/biz/nellemann/mdexpl/view/AboutPresenter.java b/src/main/java/biz/nellemann/mdexpl/view/AboutPresenter.java deleted file mode 100644 index 2062ca9..0000000 --- a/src/main/java/biz/nellemann/mdexpl/view/AboutPresenter.java +++ /dev/null @@ -1,64 +0,0 @@ -package biz.nellemann.mdexpl.view; - -import com.gluonhq.charm.glisten.application.AppManager; -import com.gluonhq.charm.glisten.control.AppBar; -import com.gluonhq.charm.glisten.mvc.View; -import com.gluonhq.charm.glisten.visual.MaterialDesignIcon; -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.ResourceBundle; - -public class AboutPresenter { - - private static final Logger log = LoggerFactory.getLogger(AboutPresenter.class); - - - @FXML - private ResourceBundle resources; - - @FXML - View about; - - @FXML - Label labelInfoWebsite; - - @FXML - Label labelVersion; - - @Inject String appVersion; - @Inject String aboutWebsite; - - - @FXML - public void initialize() { - log.info("initialize()"); - - about.showingProperty().addListener((obs, oldValue, newValue) -> { - if (newValue) { - AppManager appManager = AppManager.getInstance(); - AppBar appBar = appManager.getAppBar(); - - appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> - appManager.getDrawer().open())); - - appBar.setTitleText("About"); - - appBar.getActionItems().add(MaterialDesignIcon.CLOSE.button(e -> { - appManager.goHome(); - })); - - } - }); - - labelVersion.setText(appVersion); - labelInfoWebsite.setText(aboutWebsite); - - } - - - -} diff --git a/src/main/java/biz/nellemann/mdexpl/view/AppViewManager.java b/src/main/java/biz/nellemann/mdexpl/view/AppViewManager.java deleted file mode 100644 index f656d26..0000000 --- a/src/main/java/biz/nellemann/mdexpl/view/AppViewManager.java +++ /dev/null @@ -1,52 +0,0 @@ -package biz.nellemann.mdexpl.view; - -import biz.nellemann.mdexpl.App; -import com.gluonhq.charm.glisten.afterburner.AppView; -import com.gluonhq.charm.glisten.afterburner.AppViewRegistry; -import com.gluonhq.charm.glisten.afterburner.Utils; -import com.gluonhq.charm.glisten.application.AppManager; -import com.gluonhq.charm.glisten.control.Avatar; -import com.gluonhq.charm.glisten.control.NavigationDrawer; -import com.gluonhq.charm.glisten.visual.MaterialDesignIcon; -import javafx.scene.image.Image; - -import java.util.Locale; -import java.util.Objects; - -import static com.gluonhq.charm.glisten.afterburner.AppView.Flag.*; - -public class AppViewManager { - - public static final AppViewRegistry REGISTRY = new AppViewRegistry(); - - // Shown in drawer - public static final AppView PRIMARY_VIEW = view("Explorer", MainPresenter.class, MaterialDesignIcon.HOME, SHOW_IN_DRAWER, HOME_VIEW, SKIP_VIEW_STACK); - public static final AppView ABOUT_VIEW = view("About", AboutPresenter.class, MaterialDesignIcon.HELP, SHOW_IN_DRAWER); - - - - private static AppView view(String title, Class presenterClass, MaterialDesignIcon menuIcon, AppView.Flag... flags ) { - return REGISTRY.createView(name(presenterClass), title, presenterClass, menuIcon, flags); - } - - private static String name(Class presenterClass) { - return presenterClass.getSimpleName().toUpperCase(Locale.ROOT).replace("PRESENTER", ""); - } - - public static void registerViewsAndDrawer() { - for (AppView view : REGISTRY.getViews()) { - view.registerView(); - } - - NavigationDrawer.Header header = new NavigationDrawer.Header("mDNS Explorer", - "Multicast DNS Explorer", - new Avatar(48, new Image(Objects.requireNonNull(App.class.getResourceAsStream("/icon.png")))) - ); - - Utils.buildDrawer(AppManager.getInstance().getDrawer(), header, REGISTRY.getViews()); - - //NavigationDrawer.Footer footer = new NavigationDrawer.Footer("Bla"); - //AppManager.getInstance().getDrawer().setFooter(footer); - } - -} diff --git a/src/main/java/biz/nellemann/mdexpl/view/MainPresenter.java b/src/main/java/biz/nellemann/mdexpl/view/MainPresenter.java deleted file mode 100644 index 7c8d6a8..0000000 --- a/src/main/java/biz/nellemann/mdexpl/view/MainPresenter.java +++ /dev/null @@ -1,77 +0,0 @@ -package biz.nellemann.mdexpl.view; - -import biz.nellemann.mdexpl.NetworkServiceCell; -import biz.nellemann.mdexpl.model.NetworkService; -import biz.nellemann.mdexpl.model.MainModel; -import biz.nellemann.mdexpl.service.DiscoveryService; -import com.gluonhq.charm.glisten.application.AppManager; -import com.gluonhq.charm.glisten.control.AppBar; -import com.gluonhq.charm.glisten.control.CharmListView; -import com.gluonhq.charm.glisten.control.LifecycleEvent; -import com.gluonhq.charm.glisten.mvc.View; -import com.gluonhq.charm.glisten.visual.MaterialDesignIcon; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.input.MouseEvent; -import javafx.scene.input.TouchEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.ResourceBundle; - -public class MainPresenter { - - private static final Logger log = LoggerFactory.getLogger(MainPresenter.class); - - - @FXML - private View main; - - @Inject - private MainModel model; - - @Inject - private DiscoveryService discoveryService; - - @FXML - private ResourceBundle resources; - - @FXML - private CharmListView charmListView; - - private final ObservableList devicesList = FXCollections.observableArrayList(); - - - @FXML - public void initialize() { - - log.info("initialize()"); - - main.showingProperty().addListener((obs, oldValue, newValue) -> { - if (newValue) { - AppManager appManager = AppManager.getInstance(); - AppBar appBar = appManager.getAppBar(); - - appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> - appManager.getDrawer().open())); - - appBar.setTitleText("mDNS Explorer"); - } - }); - - discoveryService.setObservableList(devicesList); - charmListView.setItems(devicesList); - charmListView.setCellFactory(p -> new NetworkServiceCell()); - } - - - public void onEventShowing(LifecycleEvent lifecycleEvent) { - - } - - public void onEventHiding(LifecycleEvent lifecycleEvent) { - } - -} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..387106d --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,12 @@ +module biz.nellemann.mdexpl { + requires javafx.controls; + requires javafx.fxml; + requires javax.jmdns; + requires org.slf4j; + requires javax.inject; + requires java.annotation; + requires java.desktop; + + opens biz.nellemann.mdexpl to javafx.fxml; + exports biz.nellemann.mdexpl; +} diff --git a/src/main/resources/biz/nellemann/mdexpl/view/configuration.properties b/src/main/resources/biz/nellemann/mdexpl/configuration.properties similarity index 100% rename from src/main/resources/biz/nellemann/mdexpl/view/configuration.properties rename to src/main/resources/biz/nellemann/mdexpl/configuration.properties diff --git a/src/main/resources/biz/nellemann/mdexpl/main.fxml b/src/main/resources/biz/nellemann/mdexpl/main.fxml new file mode 100644 index 0000000..bdef55f --- /dev/null +++ b/src/main/resources/biz/nellemann/mdexpl/main.fxml @@ -0,0 +1,12 @@ + + + + + + + +
+ +
+ +
diff --git a/src/main/resources/biz/nellemann/mdexpl/view/about.fxml b/src/main/resources/biz/nellemann/mdexpl/view/about.fxml deleted file mode 100644 index df8b47f..0000000 --- a/src/main/resources/biz/nellemann/mdexpl/view/about.fxml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/biz/nellemann/mdexpl/view/main.fxml b/src/main/resources/biz/nellemann/mdexpl/view/main.fxml deleted file mode 100644 index 31171e8..0000000 --- a/src/main/resources/biz/nellemann/mdexpl/view/main.fxml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - -
- -
- -
From ff68f18ac3f03587d2a654259eab17d1a02863d5 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 17:58:57 +0200 Subject: [PATCH 02/11] Test drone builds. --- .dockerignore | 2 + .drone.yml | 44 ++++++++++++++++++++ README.md | 86 ++++++--------------------------------- build.gradle | 49 +++++++++++----------- docker/Dockerfile.deb-txt | 12 ++++++ docker/Dockerfile.rpm-txt | 14 +++++++ gradle.properties | 1 + 7 files changed, 110 insertions(+), 98 deletions(-) create mode 100644 .dockerignore create mode 100644 .drone.yml create mode 100644 docker/Dockerfile.deb-txt create mode 100644 docker/Dockerfile.rpm-txt create mode 100644 gradle.properties diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..20398ae --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +.gradle diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..ebfa697 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,44 @@ +--- +kind: pipeline +name: default +type: docker + +#platform: +# os: linux +# arch: amd64 + + +steps: + + - name: test + image: docker.io/bellsoft/liberica-openjdk-debian:17 + commands: + - ./gradlew --quiet --no-daemon test + + + - name: build-deb + image: docker.io/debian:stable + environment: + AUTH_TOKEN: # Gitea access token ENV variable + from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above + commands: + - apt-get install -y dpkg-dev unzip zip curl + - ./gradlew --quiet --no-daemon build jpackage + - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done + when: + event: + - tag + + + - name: build-rpm + image: docker.io/almalinux:8 + environment: + AUTH_TOKEN: # Gitea access token ENV variable + from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above + commands: + - dnf -y install unzip zip rpm-build + - ./gradlew --quiet --no-daemon build jpackage + - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done + when: + event: + - tag diff --git a/README.md b/README.md index 9b3ccc9..69fa77a 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,34 @@ # mDNS Explorer -View all multicastDNS devices. +View all multicastDNS devices on your local network. ## Development Java SDK version 17 (or later) is required. -## Native Images - -The native images are built with GraalVM. -Download the Gluon version of GraalVM from https://github.com/gluonhq/graal/releases/tag/gluon-22.1.0.1-Final and extract it locally. - -Point the GRAALVM_HOME environment variable to the folder: - -```export GRAALVM_HOME="/path/to/gluon-22.1.0.1``` - -With all requirements setup and on a supported platform, the general idea is to run: +Information on how to build and package jftpd: ```shell -./gradlew clean build -./gradlew nativeBuild -Ptarget=android # or -Ptarget=ios -./gradlew nativeLink -Ptarget=android -./gradlew nativePackage -Ptarget=android -./gradlew nativeInstall -Ptarget=android -./gradlew nativeRun -Ptarget=android +./gradlew build jpackage ``` -or, in one go for Android: +### Windows -```shell -./gradlew -Ptarget=android clean build nativeBuild nativeLink nativePackage nativeInstall nativeRun -``` +Download and install +- Microsoft .NET Framework 3.5 +- Wix Toolset 3.11.2 (or later) -or, in one go for iOS: -```shell -./gradlew -Ptarget=ios clean build nativeBuild nativeLink nativePackage nativeInstall nativeRun -``` ### Linux -See https://docs.gluonhq.com/#prerequisites_linux +TODO -For nativeBuild on Linux, install the following dependencies. +docker build . -f docker/Dockerfile.rpm-txt -```shell -sudo apt install libgtk-3-dev libavformat-dev libavutil-dev libavcodec-dev libasound2-dev libpango1.0-dev libxtst-dev build-essential -``` +or -#### Building for Android (on Linux) - -See https://docs.gluonhq.com/#prerequisites_android - -Install Android Studio and use sdkmanager to install the following: - -- Android SDK Platform 31 -- NDK (Side by side) - -Point the ANDROID_NDK variable to the installed NDK directory: - -``` -export ANDROID_NDK="/home/mark/Android/Sdk/ndk/25.2.9519653/" -``` - -Add the platform-tools to you PATH to use 'adb' command: - - -``` -export PATH="$PATH:/home/mark/Android/Sdk/platform-tools/" -``` - -Run the 'adb devices' and 'adb usb' to verify your Android networkService is in development mode and allows connections. +docker build . -f docker/Dockerfile.deb-txt ### MacOS -See https://docs.gluonhq.com/#platforms_macos - -Install the xcode command line tools and an iPhone emulator. - -If you have problems with *xcrun* not being able to find *iphoneos*, try: - -```shell -sudo xcode-select --switch /Applications/Xcode.app -``` - -Download and install homebrew, and install the following: - -```shell -brew install maven -brew install --HEAD libusbmuxd -brew install --HEAD libimobiledevice -``` - -#### iOS (on MacOS) - -See https://docs.gluonhq.com/#prerequisites_ios +TODO diff --git a/build.gradle b/build.gradle index c5adeb8..3ad5d0e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,6 @@ plugins { + id 'java' + id 'groovy' id 'application' id 'org.openjfx.javafxplugin' version '0.0.14' id 'com.google.osdetector' version '1.7.3' @@ -14,19 +16,17 @@ import org.apache.tools.ant.filters.ReplaceTokens repositories { mavenCentral() mavenLocal() - maven { - url 'https://nexus.gluonhq.com/nexus/content/repositories/releases' - } } -version = "0.0.1" -mainClassName = "biz.nellemann.mdexpl.App" -application.mainModule = "biz.nellemann.mdexpl" - tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } +application { + mainModule = 'biz.nellemann.mdexpl' + mainClass = 'biz.nellemann.mdexpl.App' +} + java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -106,21 +106,6 @@ gluonfx { }*/ -/* -shadowJar { - //archiveBaseName.set("vtd-poc-app") - //archiveClassifier.set('all') - archiveVersion.set("${System.env.BITBUCKET_BRANCH ?: 'dev' }") -}*/ - -//tasks.build.dependsOn tasks.shadowJar - -tasks.processResources { - filesMatching('**/configuration.properties') { - filter(ReplaceTokens, tokens: [copyright: '2023', version: System.env.BITBUCKET_BUILD_NUMBER ?: 'development' ]) - } -} - jlink { forceMerge 'slf4j' @@ -179,21 +164,37 @@ jlink { ] if (osdetector.release.isLike('debian')) { + // Requires 'dpkg-dev' installed installerType = 'deb' skipInstaller = false installerOptions += [ '--linux-deb-maintainer', 'mark.nellemann@gmail.com' ] - } else if (osdetector.release.isLike('centos')) { + } else if (osdetector.release.isLike('centos') or osdetector.release.isLike('suse')) { + // Requires 'rpm-build' installed installerType = 'rpm' skipInstaller = false installerOptions += [ '--linux-rpm-license-type', 'APACHE-20' ] } - } } } + +/* +shadowJar { + //archiveBaseName.set("vtd-poc-app") + //archiveClassifier.set('all') + archiveVersion.set("${System.env.BITBUCKET_BRANCH ?: 'dev' }") +}*/ + +//tasks.build.dependsOn tasks.shadowJar + +tasks.processResources { + filesMatching('**/configuration.properties') { + filter(ReplaceTokens, tokens: [copyright: '2023', version: System.env.BITBUCKET_BUILD_NUMBER ?: 'development' ]) + } +} diff --git a/docker/Dockerfile.deb-txt b/docker/Dockerfile.deb-txt new file mode 100644 index 0000000..02a4d86 --- /dev/null +++ b/docker/Dockerfile.deb-txt @@ -0,0 +1,12 @@ +FROM docker.io/debian:stable AS build + +RUN apt-get update && apt-get install -y unzip zip curl dpkg-dev +RUN bash -c "curl -s 'https://get.sdkman.io' | bash" +RUN bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8-zulu" + +COPY ./src /tmp/prj/src +COPY ./gradle /tmp/prj/gradle +COPY [ "gradlew", "build.gradle", "gradle.properties", "settings.gradle", "/tmp/prj/" ] + +WORKDIR /tmp/prj +RUN bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" diff --git a/docker/Dockerfile.rpm-txt b/docker/Dockerfile.rpm-txt new file mode 100644 index 0000000..eb9213c --- /dev/null +++ b/docker/Dockerfile.rpm-txt @@ -0,0 +1,14 @@ +FROM docker.io/almalinux:8 AS build + +RUN dnf -y install unzip zip rpm-build +RUN sh -c "curl -s 'https://get.sdkman.io' | bash" +RUN sh -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8-zulu" + +COPY ./src /tmp/prj/src +COPY ./gradle /tmp/prj/gradle +COPY [ "gradlew", "build.gradle", "gradle.properties", "settings.gradle", "/tmp/prj/" ] + +WORKDIR /tmp/prj +RUN sh -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" + +COPY --from=build build/jpackage/* build/ diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..82348b2 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +version = 0.0.1 From 1289f3b8fff9898857e9af59ef7a20067a049e08 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 18:04:49 +0200 Subject: [PATCH 03/11] Test drone builds. --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index ebfa697..1a3a141 100644 --- a/.drone.yml +++ b/.drone.yml @@ -22,7 +22,7 @@ steps: AUTH_TOKEN: # Gitea access token ENV variable from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - - apt-get install -y dpkg-dev unzip zip curl + - apt-get update && apt-get install -y dpkg-dev unzip zip curl - ./gradlew --quiet --no-daemon build jpackage - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: From 6eac4dedec3baed6cc29862d6d307ac4332273b5 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 18:08:19 +0200 Subject: [PATCH 04/11] Test drone builds. --- .drone.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 1a3a141..9c40b9b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -23,7 +23,9 @@ steps: from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - apt-get update && apt-get install -y dpkg-dev unzip zip curl - - ./gradlew --quiet --no-daemon build jpackage + - curl -s 'https://get.sdkman.io' | bash + - source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca + - source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: @@ -36,8 +38,10 @@ steps: AUTH_TOKEN: # Gitea access token ENV variable from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - - dnf -y install unzip zip rpm-build - - ./gradlew --quiet --no-daemon build jpackage + - dnf -y install unzip zip curl rpm-build + - curl -s 'https://get.sdkman.io' | bash + - source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca + - source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: From 5077130eb79f63df6167425747855d26276ef539 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 18:12:07 +0200 Subject: [PATCH 05/11] Test drone builds. --- .drone.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.drone.yml b/.drone.yml index 9c40b9b..4921f47 100644 --- a/.drone.yml +++ b/.drone.yml @@ -23,9 +23,9 @@ steps: from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - apt-get update && apt-get install -y dpkg-dev unzip zip curl - - curl -s 'https://get.sdkman.io' | bash - - source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca - - source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage + - bash -c "curl -s 'https://get.sdkman.io' | bash" + - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8-zulu" + - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: @@ -39,9 +39,9 @@ steps: from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - dnf -y install unzip zip curl rpm-build - - curl -s 'https://get.sdkman.io' | bash - - source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca - - source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage + - bash -c "curl -s 'https://get.sdkman.io' | bash" + - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8-zulu" + - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: From d57541d0eb85493e3c5395ffd4cef0b0f7dfeb26 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 18:15:41 +0200 Subject: [PATCH 06/11] Test drone builds. --- .drone.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone.yml b/.drone.yml index 4921f47..a8e6f52 100644 --- a/.drone.yml +++ b/.drone.yml @@ -24,9 +24,9 @@ steps: commands: - apt-get update && apt-get install -y dpkg-dev unzip zip curl - bash -c "curl -s 'https://get.sdkman.io' | bash" - - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8-zulu" + - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" - - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done + - for file in build/jpackage/*.deb ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: - tag @@ -40,9 +40,9 @@ steps: commands: - dnf -y install unzip zip curl rpm-build - bash -c "curl -s 'https://get.sdkman.io' | bash" - - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8-zulu" + - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" - - for file in build/jpackage/*.{rpm,deb} ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done + - for file in build/jpackage/*.rpm ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: - tag From 1e1bcf1996cb38c60ecc1651afb644f3da97054d Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 18:25:58 +0200 Subject: [PATCH 07/11] Test drone builds. --- .drone.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.drone.yml b/.drone.yml index a8e6f52..2650de1 100644 --- a/.drone.yml +++ b/.drone.yml @@ -10,11 +10,10 @@ type: docker steps: - - name: test - image: docker.io/bellsoft/liberica-openjdk-debian:17 - commands: - - ./gradlew --quiet --no-daemon test - +# - name: test +# image: docker.io/bellsoft/liberica-openjdk-debian:17 +# commands: +# - ./gradlew --quiet --no-daemon test - name: build-deb image: docker.io/debian:stable @@ -22,26 +21,27 @@ steps: AUTH_TOKEN: # Gitea access token ENV variable from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - - apt-get update && apt-get install -y dpkg-dev unzip zip curl - - bash -c "curl -s 'https://get.sdkman.io' | bash" - - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" - - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" + - apt-get update && apt-get install -y dpkg-dev unzip zip curl openjdk-17-jdk + # - bash -c "curl -s 'https://get.sdkman.io' | bash" + # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" + # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" + - ./gradlew --no-daemon clean build jpackage - for file in build/jpackage/*.deb ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: - tag - - name: build-rpm image: docker.io/almalinux:8 environment: AUTH_TOKEN: # Gitea access token ENV variable from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - - dnf -y install unzip zip curl rpm-build - - bash -c "curl -s 'https://get.sdkman.io' | bash" - - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" - - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" + - dnf -y install unzip zip curl rpm-build java-17-openjdk +# - bash -c "curl -s 'https://get.sdkman.io' | bash" +# - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" +# - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" + - ./gradlew --no-daemon clean build jpackage - for file in build/jpackage/*.rpm ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: From e3e80b70037f45d0aeb4387c69a15b8c452ec1a6 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 19:09:05 +0200 Subject: [PATCH 08/11] Test drone builds. --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 2650de1..4446e82 100644 --- a/.drone.yml +++ b/.drone.yml @@ -37,7 +37,7 @@ steps: AUTH_TOKEN: # Gitea access token ENV variable from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - - dnf -y install unzip zip curl rpm-build java-17-openjdk + - dnf -y install unzip zip curl rpm-build java-17-openjdk-gui # - bash -c "curl -s 'https://get.sdkman.io' | bash" # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" From 7678813e457dac8a7093026765cd140321bd86b9 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 19:19:36 +0200 Subject: [PATCH 09/11] Test drone builds. --- .drone.yml | 4 +-- build.gradle | 66 ++---------------------------------------- doc/mDNS-Explorer.png | Bin 0 -> 47849 bytes 3 files changed, 5 insertions(+), 65 deletions(-) create mode 100644 doc/mDNS-Explorer.png diff --git a/.drone.yml b/.drone.yml index 4446e82..cf9ffc4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -32,12 +32,12 @@ steps: - tag - name: build-rpm - image: docker.io/almalinux:8 + image: docker.io/almalinux:9 environment: AUTH_TOKEN: # Gitea access token ENV variable from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - - dnf -y install unzip zip curl rpm-build java-17-openjdk-gui + - dnf -y install unzip zip curl rpm-build java-17-openjdk # - bash -c "curl -s 'https://get.sdkman.io' | bash" # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" diff --git a/build.gradle b/build.gradle index 3ad5d0e..dcaf8a9 100644 --- a/build.gradle +++ b/build.gradle @@ -5,9 +5,6 @@ plugins { id 'org.openjfx.javafxplugin' version '0.0.14' id 'com.google.osdetector' version '1.7.3' id 'org.beryx.jlink' version '2.26.0' - //id "com.github.johnrengelman.shadow" version "8.1.1" - //id 'com.gluonhq.gluonfx-gradle-plugin' version '1.0.19' - } @@ -30,7 +27,9 @@ application { java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - //modularity.inferModulePath = false + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } /* This is to be able to build with a JDK not bundled with JavaFX */ @@ -56,56 +55,6 @@ test { useJUnitPlatform() } -/* -gluonfx { - verbose = true - target = project.hasProperty("target") ? project.getProperty("target") : 'host' - //target = 'ios' // Uncomment to enable iOS - see https://docs.gluonhq.com/#prerequisites_ios - //target = 'android' // Uncomment to enable Android - see https://docs.gluonhq.com/#prerequisites_android - - attachConfig { - version = "4.0.18" - services 'storage', 'display', 'lifecycle', 'statusbar' - } - - reflectionList = [ - "javafx.fxml.FXMLLoader", - "com.gluonhq.charm.glisten.mvc.View", - "com.gluonhq.charm.glisten.control.Icon", - "com.gluonhq.charm.glisten.control.DropdownButton", - "com.gluonhq.charm.glisten.control.BottomNavigation", - "com.gluonhq.charm.glisten.control.BottomNavigationButton", - "biz.nellemann.mdexpl.view.AboutPresenter", - "biz.nellemann.mdexpl.view.MainPresenter", "biz.nellemann.mdexpl.model.MainModel", - "biz.nellemann.mdexpl.service.DiscoveryService", - ] - - compilerArgs = [ - '-Djava.awt.headless=true' - ] - - appIdentifier = 'biz.nellemann.mdexpl' - - release { - // Android - appLabel = "mDNS Explorer" - //versionCode = "1" - //versionName = "1.0" - //providedKeyStorePath = "" - //providedKeyStorePassword = "" - //providedKeyAlias = "" - //providedKeyAliasPassword = "" - // iOS - //bundleName = "mDNS Explorer" - //bundleVersion = "" - //bundleShortVersion = "" - //providedSigningIdentity = "" - //providedProvisioningProfile = "" - //skipSigning = true // Will not run or deploy if not signed - } - -}*/ - jlink { forceMerge 'slf4j' @@ -184,15 +133,6 @@ jlink { } -/* -shadowJar { - //archiveBaseName.set("vtd-poc-app") - //archiveClassifier.set('all') - archiveVersion.set("${System.env.BITBUCKET_BRANCH ?: 'dev' }") -}*/ - -//tasks.build.dependsOn tasks.shadowJar - tasks.processResources { filesMatching('**/configuration.properties') { filter(ReplaceTokens, tokens: [copyright: '2023', version: System.env.BITBUCKET_BUILD_NUMBER ?: 'development' ]) diff --git a/doc/mDNS-Explorer.png b/doc/mDNS-Explorer.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0d9a93bdfc4f06a55fb94bdec2b09a315647af GIT binary patch literal 47849 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 H$; Date: Mon, 14 Aug 2023 19:25:41 +0200 Subject: [PATCH 10/11] Test drone builds. --- .drone.yml | 2 +- README.md | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index cf9ffc4..367c8ba 100644 --- a/.drone.yml +++ b/.drone.yml @@ -37,7 +37,7 @@ steps: AUTH_TOKEN: # Gitea access token ENV variable from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - - dnf -y install unzip zip curl rpm-build java-17-openjdk + - dnf -y install unzip zip rpm-build java-17-openjdk # - bash -c "curl -s 'https://get.sdkman.io' | bash" # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" diff --git a/README.md b/README.md index 69fa77a..56572c0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # mDNS Explorer -View all multicastDNS devices on your local network. +View multicastDNS services on your local network. + +![mDNS-Explorer](doc/mDNS-Explorer.png) + ## Development From 66c79fb2898649b189ff87352cd3ec00e8afadd6 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Mon, 14 Aug 2023 19:35:20 +0200 Subject: [PATCH 11/11] Test drone builds. --- .drone.yml | 25 +++---------------------- build.gradle | 22 ++++------------------ 2 files changed, 7 insertions(+), 40 deletions(-) diff --git a/.drone.yml b/.drone.yml index 367c8ba..ea9005a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -15,34 +15,15 @@ steps: # commands: # - ./gradlew --quiet --no-daemon test - - name: build-deb + - name: jpackage image: docker.io/debian:stable environment: AUTH_TOKEN: # Gitea access token ENV variable from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above commands: - - apt-get update && apt-get install -y dpkg-dev unzip zip curl openjdk-17-jdk - # - bash -c "curl -s 'https://get.sdkman.io' | bash" - # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" - # - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" + - apt-get update && apt-get install -y dpkg-dev rpm unzip zip curl openjdk-17-jdk - ./gradlew --no-daemon clean build jpackage - - for file in build/jpackage/*.deb ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done - when: - event: - - tag - - - name: build-rpm - image: docker.io/almalinux:9 - environment: - AUTH_TOKEN: # Gitea access token ENV variable - from_secret: AUTH_TOKEN # Name of DroneCI secret exposed above - commands: - - dnf -y install unzip zip rpm-build java-17-openjdk -# - bash -c "curl -s 'https://get.sdkman.io' | bash" -# - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install java 17.0.8.fx-librca" -# - bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && ./gradlew --no-daemon clean build jpackage" - - ./gradlew --no-daemon clean build jpackage - - for file in build/jpackage/*.rpm ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done + - for file in build/jpackage/*.* ; do curl -s --user "$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done when: event: - tag diff --git a/build.gradle b/build.gradle index dcaf8a9..f63494d 100644 --- a/build.gradle +++ b/build.gradle @@ -105,28 +105,14 @@ jlink { } if(osdetector.os == 'linux') { - + skipInstaller = false installerOptions += [ '--linux-menu-group', 'Internet', '--linux-shortcut', - //'--icon', 'src/main/resources/icon_256x256.png' + '--linux-deb-maintainer', 'mark.nellemann@gmail.com', + '--linux-rpm-license-type', 'APACHE-20', + '--icon', 'src/main/resources/icon.png', ] - - if (osdetector.release.isLike('debian')) { - // Requires 'dpkg-dev' installed - installerType = 'deb' - skipInstaller = false - installerOptions += [ - '--linux-deb-maintainer', 'mark.nellemann@gmail.com' - ] - } else if (osdetector.release.isLike('centos') or osdetector.release.isLike('suse')) { - // Requires 'rpm-build' installed - installerType = 'rpm' - skipInstaller = false - installerOptions += [ - '--linux-rpm-license-type', 'APACHE-20' - ] - } } }