fix(gui): update check fixed to match current artifact naming

This commit is contained in:
Skylot
2024-10-17 21:38:49 +01:00
parent 249801880c
commit a43b3282ef
8 changed files with 74 additions and 209 deletions
+1
View File
@@ -27,6 +27,7 @@ dependencies {
implementation("org.jcommander:jcommander:2.0")
implementation("ch.qos.logback:logback-classic:1.5.11")
implementation("io.github.oshai:kotlin-logging-jvm:7.0.0")
implementation("com.fifesoft:rsyntaxtextarea:3.4.1")
implementation(files("libs/jfontchooser-1.0.5.jar"))
@@ -666,10 +666,10 @@ public class JadxSettingsWindow extends JDialog {
group.addRow(NLS.str("preferences.lineNumbersMode"), lineNumbersMode);
group.addRow(NLS.str("preferences.jumpOnDoubleClick"), jumpOnDoubleClick);
group.addRow(NLS.str("preferences.useAlternativeFileDialog"), useAltFileDialog);
group.addRow(NLS.str("preferences.check_for_updates"), update);
group.addRow(NLS.str("preferences.cfg"), cfg);
group.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
group.addRow(NLS.str("preferences.xposed_codegen_language"), xposedCodegenLanguage);
group.addRow(NLS.str("preferences.check_for_updates"), update);
group.addRow(NLS.str("preferences.update_channel"), updateChannel);
return group;
}
@@ -1,31 +1,42 @@
package jadx.gui.update
import com.google.gson.Gson
import com.google.gson.JsonParser
import com.google.gson.annotations.SerializedName
import io.github.oshai.kotlinlogging.KotlinLogging
import jadx.api.JadxDecompiler
import jadx.core.Jadx
import jadx.gui.settings.JadxUpdateChannel
import jadx.gui.update.data.Artifact
import jadx.gui.update.data.Release
import org.jetbrains.kotlin.konan.file.use
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.InputStream
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.Date
import java.net.URI
import kotlin.reflect.KClass
data class Release(val name: String)
data class ArtifactList(val artifacts: List<Artifact>)
data class Artifact(
val name: String,
@SerializedName("workflow_run") val workflowRun: WorkflowRun,
)
data class WorkflowRun(
@SerializedName("head_branch") val branch: String,
)
interface IUpdateCallback {
fun onUpdate(r: Release)
}
object JadxUpdate {
private val LOG: Logger = LoggerFactory.getLogger(JadxUpdate::class.java)
private val log = KotlinLogging.logger {}
const val JADX_ARTIFACTS_URL = "https://nightly.link/skylot/jadx/workflows/build-artifacts/master"
const val JADX_RELEASES_URL = "https://github.com/skylot/jadx/releases"
private const val GITHUB_API_URL = "https://api.github.com/repos/skylot/jadx"
private const val GITHUB_ARTIFACTS_URL = "$GITHUB_API_URL/actions/artifacts"
private const val GITHUB_LATEST_ARTIFACTS_URL = "$GITHUB_API_URL/actions/artifacts?per_page=5&page=1"
private const val GITHUB_LATEST_RELEASE_URL = "$GITHUB_API_URL/releases/latest"
@JvmStatic
@@ -37,7 +48,7 @@ object JadxUpdate {
callback.onUpdate(release)
}
} catch (e: Exception) {
LOG.debug("Jadx update error", e)
log.warn(e) { "Jadx update error" }
}
}.apply {
name = "Jadx update thread"
@@ -48,12 +59,12 @@ object JadxUpdate {
private fun checkForNewRelease(updateChannel: JadxUpdateChannel): Release? {
if (Jadx.isDevVersion()) {
LOG.debug("Ignore check for update: development version")
log.debug { "Ignore check for update: development version" }
return null
}
LOG.info("Checking for updates... Update channel: {}, current version: {}", updateChannel, JadxDecompiler.getVersion())
log.info {
"Checking for updates... Update channel: $updateChannel, current version: ${JadxDecompiler.getVersion()}"
}
return when (updateChannel) {
JadxUpdateChannel.STABLE -> checkForNewStableRelease()
JadxUpdateChannel.UNSTABLE -> checkForNewUnstableRelease()
@@ -61,76 +72,40 @@ object JadxUpdate {
}
private fun checkForNewStableRelease(): Release? {
val latestRelease = get(GITHUB_LATEST_RELEASE_URL)?.let { inputStream ->
InputStreamReader(inputStream).use {
Gson().fromJson(it, Release::class.java)
}
} ?: return null
val currentVersion = JadxDecompiler.getVersion()
if (currentVersion.equals(latestRelease.name, ignoreCase = true)) return null
if (currentVersion.startsWith("r")) {
// current version is 'unstable', but update channel set to 'stable'
log.info { "Skip update check: can't compare unstable and stable versions" }
return null
}
val latestRelease = getAndParse(GITHUB_LATEST_RELEASE_URL, Release::class) ?: return null
if (VersionComparator.checkAndCompare(currentVersion, latestRelease.name) >= 0) return null
LOG.info("Found new jadx version: {}", latestRelease)
log.info { "Found new jadx version: ${latestRelease.name}" }
return latestRelease
}
private fun checkForNewUnstableRelease(): Release? {
val artifacts = getArtifacts() ?: return null
val artifacts = getAndParse(GITHUB_LATEST_ARTIFACTS_URL, ArtifactList::class)
?.artifacts
?.filter { it.workflowRun.branch == "master" }
?: return null
if (artifacts.isEmpty()) return null
val currentVersion = JadxDecompiler.getVersion()
val currentArtifactName = "jadx-$currentVersion"
var newestArtifact: Artifact? = null
var currentArtifact: Artifact? = null
for (artifact in artifacts) {
if (newestArtifact == null && artifact.name.startsWith("jadx-") && !artifact.name.startsWith("jadx-gui-")) {
newestArtifact = artifact
}
if (currentArtifact == null && artifact.name == currentArtifactName) {
currentArtifact = artifact
}
if (newestArtifact != null && currentArtifact != null) break
}
LOG.debug("Current artifact: {}, newest artifact: {}", currentArtifact, newestArtifact)
return if (currentArtifact != null && newestArtifact != null && newestArtifact.createdAt > currentArtifact.createdAt) {
newestArtifact.let { Release().apply { name = it.name } }
} else {
null
}
val latestVersion = artifacts[0].name.removePrefix("jadx-gui-").removePrefix("jadx-").substringBefore('-')
if (VersionComparator.checkAndCompare(JadxDecompiler.getVersion(), latestVersion) >= 0) return null
log.info { "Found new unstable version: $latestVersion" }
return Release(latestVersion)
}
private fun getArtifacts(): List<Artifact>? {
return get(GITHUB_ARTIFACTS_URL)?.let { inputStream ->
InputStreamReader(inputStream).use { reader ->
val response = JsonParser.parseReader(reader).asJsonObject
val count = response.get("total_count").asInt
LOG.debug("Fetched $count artifacts...")
response.getAsJsonArray("artifacts").map {
val obj = it.asJsonObject
val name = obj.get("name").asString
val sizeInBytes = obj.get("size_in_bytes").asLong
val createdAt = obj.get("created_at").asString
val parsedCreatedAt = ZonedDateTime.parse(createdAt, DateTimeFormatter.ISO_ZONED_DATE_TIME)
Artifact(name, sizeInBytes, Date.from(parsedCreatedAt.toInstant()))
}
private fun <T : Any> getAndParse(url: String, klass: KClass<T>): T? {
val con = URI(url).toURL().openConnection() as? HttpURLConnection
if (con == null || con.responseCode != 200) {
return null
}
return con.inputStream.use { stream ->
InputStreamReader(stream).use { reader ->
Gson().fromJson(reader, klass.java)
}
}
}
private fun get(url: String): InputStream? {
val con = URL(url).openConnection() as HttpURLConnection
return if (con.responseCode == 200) con.inputStream else null
}
interface IUpdateCallback {
fun onUpdate(r: Release)
}
}
@@ -14,10 +14,23 @@ public class VersionComparator {
return "";
}
String result = str.trim().toLowerCase();
if (result.startsWith("jadx-gui-")) {
result = result.substring(9);
}
if (result.startsWith("jadx-")) {
result = result.substring(5);
}
if (result.charAt(0) == 'v') {
result = result.substring(1);
}
// treat package version as part of version
if (result.charAt(0) == 'r') {
result = result.substring(1);
int dot = result.indexOf('.');
if (dot != -1) {
result = result.substring(0, dot);
}
}
// treat a package version as part of version
result = result.replace('-', '.');
return result;
}
@@ -50,8 +63,7 @@ public class VersionComparator {
private static boolean isZeroTail(String[] arr, int pos) {
for (int i = pos; i < arr.length; i++) {
String s = arr[i];
if (Integer.valueOf(s) != 0) {
if (Integer.parseInt(arr[i]) != 0) {
return false;
}
}
@@ -1,9 +0,0 @@
package jadx.gui.update.data
import java.util.Date
data class Artifact(
val name: String,
val sizeInBytes: Long,
val createdAt: Date,
)
@@ -1,75 +0,0 @@
package jadx.gui.update.data;
import com.google.gson.annotations.SerializedName;
public class Asset {
private int id;
private String name;
private long size;
@SerializedName("download_count")
private int downloadCount;
@SerializedName("browser_download_url")
private String downloadUrl;
@SerializedName("created_at")
private String createdAt;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public int getDownloadCount() {
return downloadCount;
}
public void setDownloadCount(int downloadCount) {
this.downloadCount = downloadCount;
}
public String getDownloadUrl() {
return downloadUrl;
}
public void setDownloadUrl(String downloadUrl) {
this.downloadUrl = downloadUrl;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
@Override
public String toString() {
return name
+ ", size: " + String.format("%.2fMB", size / 1024. / 1024.)
+ ", downloads count: " + downloadCount
+ ", url: " + downloadUrl
+ ", date: " + createdAt;
}
}
@@ -1,44 +0,0 @@
package jadx.gui.update.data;
import java.util.List;
public class Release {
private int id;
private String name;
private List<Asset> assets;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<Asset> getAssets() {
return assets;
}
public void setAssets(List<Asset> assets) {
this.assets = assets;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
for (Asset asset : getAssets()) {
sb.append("\n ");
sb.append(asset);
}
return sb.toString();
}
}
@@ -28,6 +28,11 @@ class VersionComparatorTest {
checkCompare("1.3.3.1-1", "1.3.3", 1);
}
@Test
public void testCompareUnstable() {
checkCompare("r2190.ce527ed", "jadx-r2299.742d30d", -1);
}
private static void checkCompare(String a, String b, int result) {
assertThat(VersionComparator.checkAndCompare(a, b))
.as("Compare %s and %s expect %d", a, b, result)