Skip to content
This repository has been archived by the owner on Apr 12, 2023. It is now read-only.

Commit

Permalink
Add bootstrap for auto update (#137)
Browse files Browse the repository at this point in the history
* POC autoupdate

* WIP: Bootstrap update UI

* Fix PR lint

* Refactor

* Fix PR lint

* Fix run build auto-update config

* Update release workflow

* Comment out the version check on startup

* Fix jacoco test report

* Refactoring

* Fix PR lint

* Update path

* Update path

* Fix PR lint

Co-authored-by: Auto Lint <[email protected]>
  • Loading branch information
andrewinci and Auto Lint authored Nov 14, 2020
1 parent 9c4d4b8 commit d2b046d
Show file tree
Hide file tree
Showing 16 changed files with 256 additions and 47 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
if: ${{ !matrix.os.run-integration }}
run: "./gradlew check --no-daemon"
- name: Test build update4j config
if: ${{ !matrix.os.run-integration }}
if: ${{ matrix.os.run-integration }}
run: |
./gradlew getDependencySources
./gradlew :app:mergeLocalLibs
Expand All @@ -84,6 +84,7 @@ jobs:
CC_TEST_REPORTER_ID: ${{secrets.codecovToken}}
JACOCO_SOURCE_PATH: |
${{github.workspace}}/app/src/main/kotlin \
${{github.workspace}}/bootstrap/src/main/kotlin \
${{github.workspace}}/lib/configuration/src/main/kotlin \
${{github.workspace}}/lib/helper/src/main/kotlin \
${{github.workspace}}/lib/jsonhelper/src/main/kotlin \
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ jobs:
- uses: olegtarasov/get-tag@v2
id: tagName
- name: Gradle package
run: "./gradlew packageApp"
run: "./gradlew bootstrap:packageApp"
env:
RELEASE_VERSION: ${{ steps.tagName.outputs.tag }}
- name: Update binary
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.os.artifact }}
path: "./app/${{ matrix.os.path }}"
path: "./bootstrap/${{ matrix.os.path }}"
if-no-files-found: error
- name: Build update4j config and jar
if: ${{ matrix.os.name == 'ubuntu-latest' }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ class ListClusterView @Inject constructor(
it.height = 500.0
it.resizableProperty().value = false
}
checkVersion()
// Enable ONLY when the bootstrap need to be updated
// checkVersion()
super.onDock()
title = "Clusters"
}
Expand Down
15 changes: 15 additions & 0 deletions bootstrap/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
plugins {
id 'insulator.application'
id 'insulator.jpackage'
}

compileKotlin { kotlinOptions.jvmTarget = "1.8" }
compileTestKotlin { kotlinOptions.jvmTarget = "1.8" }

jar { manifest { attributes('Main-Class': 'insulator.BootstrapKt') } }

application { mainClassName ='insulator.BootstrapKt' }

dependencies {
implementation(group: 'org.update4j', name: 'update4j', version: "1.5.6")
}
79 changes: 79 additions & 0 deletions bootstrap/src/main/kotlin/insulator/Bootstrap.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package insulator

import org.update4j.Archive
import org.update4j.Configuration
import org.update4j.UpdateOptions
import org.update4j.service.UpdateHandler
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.InputStream
import java.io.InputStreamReader
import java.net.SocketTimeoutException
import java.net.URL
import java.net.UnknownHostException
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import javax.swing.JFrame
import javax.swing.JOptionPane
import javax.swing.WindowConstants.DISPOSE_ON_CLOSE
import kotlin.system.exitProcess

private val view = BootstrapViewManager(JFrame("Bootstrap").apply { defaultCloseOperation = DISPOSE_ON_CLOSE })

fun main(args: Array<String>) {
tryLoadLocalConfig()
.let { (left, right) -> left?.let { Result.failure(it) } ?: Result.success(right!!) }
.mapCatching { config ->
if (config.requiresUpdate()) {
val result = config.update(UpdateOptions.archive(updatePath).updateHandler(InsulatorUpdateHandler()))
result.exception?.let { throw it } ?: Archive.read(updatePath).install()
}
config
}
.mapCatching { it.launch() }
.fold({ Unit }, { handleErrors(it) })
}

private fun handleErrors(exception: Throwable) {
val errorMessage = when (exception) {
is UnknownHostException -> Triple("Unable to check for updates. Check your internet connection and retry", "Download error", JOptionPane.WARNING_MESSAGE)
is SocketTimeoutException -> Triple("Unable to complete the download. Check your internet connection and retry", "Timeout error", JOptionPane.WARNING_MESSAGE)
is FileNotFoundException -> Triple("Unable to find the remote configuration file. Please, contact the developer.", "Download error", JOptionPane.ERROR_MESSAGE)
else -> Triple("Unexpected error: $exception. Please, contact the developer.", "Unexpected error", JOptionPane.ERROR_MESSAGE)
}
view.showMessageDialog(errorMessage.first, errorMessage.second, errorMessage.third)
exitProcess(-1)
}

fun saveConfig(stream: InputStream): InputStream {
if (!File(localPath).exists()) File(localPath).mkdirs()
Files.copy(
stream,
Paths.get(localConfigFile),
StandardCopyOption.REPLACE_EXISTING
)
return FileInputStream(localConfigFile)
}

fun tryLoadLocalConfig(): Pair<Throwable?, Configuration?> =
URL(configPath).runCatching { openConnection().getInputStream() }
.fold(
{ Result.success(it) },
{ error ->
with(File(localConfigFile)) {
if (exists()) Result.success(inputStream())
else Result.failure(error)
}
}
)
.mapCatching { stream -> saveConfig(stream) }
.mapCatching { stream -> InputStreamReader(stream).use { Configuration.read(it) } }
.fold({ Pair(null, it) }, { Pair(it, null) })

class InsulatorUpdateHandler : UpdateHandler {
override fun startDownloads() = view.showUpdateView()
override fun updateDownloadProgress(frac: Float) = view.updateDownloadProgress(frac)
override fun doneDownloads() = view.closeUpdateView()
}
24 changes: 24 additions & 0 deletions bootstrap/src/main/kotlin/insulator/BootstrapViewManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package insulator

import java.awt.event.WindowEvent
import javax.swing.JFrame
import javax.swing.JOptionPane

class BootstrapViewManager(private val frame: JFrame) {

private var updateView: UpdateView? = null

fun updateDownloadProgress(fraction: Float) {
updateView?.updateDownloadProgress(fraction)
}

fun showMessageDialog(message: String, title: String, iconId: Int) = JOptionPane.showMessageDialog(frame, message, title, iconId)

fun closeUpdateView() {
frame.dispatchEvent(WindowEvent(frame, WindowEvent.WINDOW_CLOSING))
}

fun showUpdateView() {
updateView = UpdateView(frame)
}
}
18 changes: 18 additions & 0 deletions bootstrap/src/main/kotlin/insulator/Constants.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package insulator

import java.nio.file.Path

const val appName = "Insulator"
val localPath: String by lazy {
with(System.getProperty("os.name")) {
when {
contains("nux") -> "${System.getProperty("user.home")!!}/.config/$appName/"
contains("win") -> "${System.getenv("LOCALAPPDATA")}/$appName"
else -> "${System.getProperty("user.home")!!}/Library/Application Support/$appName/"
}
}
}

val localConfigFile = "${localPath}insulator-update.xml"
val updatePath: Path = Path.of(System.getProperty("java.io.tmpdir") ?: "", "update.zip")
const val configPath = "https://github.com/andrea-vinci/Insulator/releases/latest/download/insulator-update.xml"
68 changes: 68 additions & 0 deletions bootstrap/src/main/kotlin/insulator/UpdateView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package insulator

import java.awt.Component
import java.awt.Dimension
import java.awt.Image
import java.awt.RenderingHints
import java.awt.Toolkit
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import javax.swing.BorderFactory
import javax.swing.BoxLayout
import javax.swing.ImageIcon
import javax.swing.JFrame
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.JProgressBar
import kotlin.math.floor

class UpdateView(private val frame: JFrame) {
private val progressBar = JProgressBar().apply { isStringPainted = true }

init {
with(frame) {
add(
JPanel().apply {
add(insulatorIcon())
add(progressBar)

layout = BoxLayout(this, BoxLayout.PAGE_AXIS)
border = BorderFactory.createEmptyBorder(10, 20, 20, 20)
}
)
fixSize(300, 130)
center()
isVisible = true
}
}

fun updateDownloadProgress(frac: Float) =
with(progressBar) {
value = floor(frac.toDouble() * 100).toInt()
if (value >= 98) frame.isVisible = false
}

private fun insulatorIcon() =
ImageIO.read(this.javaClass.getResource("/icon.png"))
.let { JLabel(ImageIcon(getScaledImage(it, 60, 60))).apply { text = "Insulator auto-update" } }
.also { it.alignmentX = Component.CENTER_ALIGNMENT }

private fun getScaledImage(srcImg: Image, width: Int, height: Int) =
BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB).apply {
with(createGraphics()) {
setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
drawImage(srcImg, 0, 0, width, height, null)
dispose()
}
}

private fun JFrame.center() = apply {
val screen = Toolkit.getDefaultToolkit().screenSize
setLocation(screen.width / 2 - size.width / 2, screen.height / 2 - size.height / 2)
}

private fun JFrame.fixSize(width: Int, height: Int) = this.apply {
setSize(width, height)
maximumSize = Dimension(width, height)
}
}
Binary file added bootstrap/src/main/resources/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions buildSrc/src/main/groovy/insulator.application.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ plugins {
id 'application'
id 'distribution'
id 'insulator.update4j'
id 'insulator.base'
id 'com.github.johnrengelman.shadow'
}
23 changes: 23 additions & 0 deletions buildSrc/src/main/groovy/insulator.base.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
plugins{
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.kotlin.kapt'
}

repositories {
jcenter()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://dl.bintray.com/arrow-kt/arrow-kt/" }
maven { url "https://packages.confluent.io/maven/" }
maven { url "https://kotlin.bintray.com/kotlinx/" }
maven { url "https://repository.mulesoft.org/nexus/content/repositories/public/" }
}

compileKotlin { kotlinOptions.jvmTarget = "1.8" }
compileTestKotlin { kotlinOptions.jvmTarget = "1.8" }

dependencies {
// Kotlin
implementation platform('org.jetbrains.kotlin:kotlin-bom')
implementation(group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8')
}
19 changes: 1 addition & 18 deletions buildSrc/src/main/groovy/insulator.common.gradle
Original file line number Diff line number Diff line change
@@ -1,34 +1,17 @@
plugins {
id 'idea'
id 'jacoco'
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.kotlin.kapt'
id 'insulator.base'
id 'org.jlleitschuh.gradle.ktlint'
id 'com.adarshr.test-logger'
}

repositories {
jcenter()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://dl.bintray.com/arrow-kt/arrow-kt/" }
maven { url "https://packages.confluent.io/maven/" }
maven { url "https://kotlin.bintray.com/kotlinx/" }
maven { url "https://repository.mulesoft.org/nexus/content/repositories/public/" }
}

compileKotlin { kotlinOptions.jvmTarget = "1.8" }
compileTestKotlin { kotlinOptions.jvmTarget = "1.8" }

tasks.withType(Test) { useJUnitPlatform() }

def arrow_version = "0.11.0"
def kotest_version = "4.3.0"

dependencies {
// Kotlin
implementation platform('org.jetbrains.kotlin:kotlin-bom')
implementation(group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8')

// Arrow
implementation(group: 'io.arrow-kt', name: 'arrow-annotations', version: "$arrow_version")
Expand Down
4 changes: 2 additions & 2 deletions buildSrc/src/main/groovy/insulator.jpackage.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ task dist(type: Copy) {
group = 'distribution'
dependsOn 'distZip'

def zipFile = file("${buildDir}/distributions/app.zip")
def zipFile = file("${buildDir}/distributions/${project.name}.zip")
def outputDir = file("${buildDir}/distributions")

from zipTree(zipFile)
Expand All @@ -17,7 +17,7 @@ task packageApp(type: Exec) {

// build command
def appVersion = "${System.getenv().get("RELEASE_VERSION") ?: "0.0.0"}"
def command = ['jpackage', '--input', './build/distributions/app/lib/', '--main-jar', 'app.jar', '-d', '.',
def command = ['jpackage', '--input', "./build/distributions/${project.name}/lib/", '--main-jar', "${project.name}.jar", '-d', '.',
'--name', 'Insulator', '--java-options', "'--enable-preview'", '--app-version', appVersion]

// customization for each OS
Expand Down
5 changes: 0 additions & 5 deletions buildSrc/src/main/groovy/insulator.update4j.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
plugins {
id 'insulator.common'
}


task mergeLocalLibs(type: Jar) {
group = 'distribution'
if (!project.tasks.names.contains("dist")) {
Expand Down
36 changes: 18 additions & 18 deletions scripts/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<base uri="" path="${app.lib}"/>
<properties>
<property key="app.name" value="Insulator"/>
<property key="app.dir" value="${user.dir}/${app.name}"/>
<property key="app.lib" value="${LOCALAPPDATA}/${app.name}/lib" os="win"/>
<property key="app.lib" value="${app.dir}/lib"/>
<property key="app.lib" value="${LOCALAPPDATA}/${app.name}/" os="win"/>
<property key="app.lib" value="${user.dir}/Library/Application Support/${app.name}/" os="mac"/>
<property key="app.lib" value="${user.dir}/.config/${app.name}/" os="linux"/>
<property key="default.launcher.main.class" value="insulator.AppKt"/>
<property key="default.launcher.main.classpath" value="insulator.jar"/>
</properties>
Expand All @@ -16,21 +16,21 @@
manual_dependencies = {
"kotlinx-serialization-runtime-jvm-1.0-M1-1.4.0-rc-218.jar": '<file uri="https://jcenter.bintray.com/org/jetbrains/kotlinx/kotlinx-serialization-runtime-jvm/1.0-M1-1.4.0-rc-218/kotlinx-serialization-runtime-jvm-1.0-M1-1.4.0-rc-218.jar" size="531216" classpath="true" checksum="222f5a14"/>',
# javafx for mac
"javafx-swing-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-swing/15.0.1/javafx-swing-15.0.1-mac.jar" size="88725" classpath="true" checksum="f761e2bf"/>',
"javafx-graphics-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-graphics/15.0.1/javafx-graphics-15.0.1-mac.jar" size="4962652" classpath="true" checksum="6511c8ad"/>',
"javafx-base-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-base/15.0.1/javafx-base-15.0.1-mac.jar" size="745527" classpath="true" checksum="cb1b6659"/>',
"javafx-controls-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-controls/15.0.1/javafx-controls-15.0.1-mac.jar" size="2531050" classpath="true" checksum="98ed0adf"/>',
"javafx-fxml-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-fxml/15.0.1/javafx-fxml-15.0.1-mac.jar" size="128790" classpath="true" checksum="b15b8785"/>',
"javafx-swing-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-swing/15.0.1/javafx-swing-15.0.1-mac.jar" size="88725" classpath="true" checksum="f761e2bf"/>',
"javafx-graphics-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-graphics/15.0.1/javafx-graphics-15.0.1-mac.jar" size="4962652" classpath="true" checksum="6511c8ad"/>',
"javafx-base-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-base/15.0.1/javafx-base-15.0.1-mac.jar" size="745527" classpath="true" checksum="cb1b6659"/>',
"javafx-controls-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-controls/15.0.1/javafx-controls-15.0.1-mac.jar" size="2531050" classpath="true" checksum="98ed0adf"/>',
"javafx-fxml-15.0.1-mac.jar": '<file os="mac" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-fxml/15.0.1/javafx-fxml-15.0.1-mac.jar" size="128790" classpath="true" checksum="b15b8785"/>',
# javafx for linux
"javafx-swing-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-swing/15.0.1/javafx-swing-15.0.1-linux.jar" size="88725" classpath="true" checksum="62b59890"/>',
"javafx-graphics-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-graphics/15.0.1/javafx-graphics-15.0.1-linux.jar" size="5063475" classpath="true" checksum="46774dc6"/>',
"javafx-base-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-base/15.0.1/javafx-base-15.0.1-linux.jar" size="745527" classpath="true" checksum="d6bedd4d"/>',
"javafx-controls-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-controls/15.0.1/javafx-controls-15.0.1-linux.jar" size="2531099" classpath="true" checksum="8e5467f3"/>',
"javafx-fxml-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-fxml/15.0.1/javafx-fxml-15.0.1-linux.jar" size="128790" classpath="true" checksum="ecb5193e"/>',
"javafx-swing-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-swing/15.0.1/javafx-swing-15.0.1-linux.jar" size="88725" classpath="true" checksum="62b59890"/>',
"javafx-graphics-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-graphics/15.0.1/javafx-graphics-15.0.1-linux.jar" size="5063475" classpath="true" checksum="46774dc6"/>',
"javafx-base-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-base/15.0.1/javafx-base-15.0.1-linux.jar" size="745527" classpath="true" checksum="d6bedd4d"/>',
"javafx-controls-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-controls/15.0.1/javafx-controls-15.0.1-linux.jar" size="2531099" classpath="true" checksum="8e5467f3"/>',
"javafx-fxml-15.0.1-linux.jar": '<file os="linux" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-fxml/15.0.1/javafx-fxml-15.0.1-linux.jar" size="128790" classpath="true" checksum="ecb5193e"/>',
# javafx for windows
"javafx-swing-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-swing/15.0.1/javafx-swing-15.0.1-win.jar" size="88725" classpath="true" checksum="34bf6673"/>',
"javafx-graphics-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-graphics/15.0.1/javafx-graphics-15.0.1-win.jar" size="5898578" classpath="true" checksum="4eef746f"/>',
"javafx-base-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-base/15.0.1/javafx-base-15.0.1-win.jar" size="745526" classpath="true" checksum="95ac37b3"/>',
"javafx-controls-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-controls/15.0.1/javafx-controls-15.0.1-win.jar" size="2531028" classpath="true" checksum="94c5eb71"/>',
"javafx-fxml-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-fxml/15.0.1/javafx-fxml-15.0.1-win.jar" size="128790" classpath="true" checksum="6a818590"/>',
"javafx-swing-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-swing/15.0.1/javafx-swing-15.0.1-win.jar" size="88725" classpath="true" checksum="34bf6673"/>',
"javafx-graphics-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-graphics/15.0.1/javafx-graphics-15.0.1-win.jar" size="5898578" classpath="true" checksum="4eef746f"/>',
"javafx-base-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-base/15.0.1/javafx-base-15.0.1-win.jar" size="745526" classpath="true" checksum="95ac37b3"/>',
"javafx-controls-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-controls/15.0.1/javafx-controls-15.0.1-win.jar" size="2531028" classpath="true" checksum="94c5eb71"/>',
"javafx-fxml-15.0.1-win.jar": '<file os="win" uri="https://repo1.maven.org/maven2/org/openjfx/javafx-fxml/15.0.1/javafx-fxml-15.0.1-win.jar" size="128790" classpath="true" checksum="6a818590"/>',
}
Loading

0 comments on commit d2b046d

Please sign in to comment.