mirror of
https://github.com/skylot/jadx.git
synced 2024-11-23 04:39:46 +00:00
feat(plugins): allow to set minimum required jadx version in plugin info (#2314)
Some checks are pending
Build Artifacts / build (push) Waiting to run
Build Artifacts / build-win-bundle (push) Waiting to run
Build Test / tests (ubuntu-latest) (push) Waiting to run
Build Test / tests (windows-latest) (push) Waiting to run
CodeQL / Analyze (java) (push) Waiting to run
Some checks are pending
Build Artifacts / build (push) Waiting to run
Build Artifacts / build-win-bundle (push) Waiting to run
Build Test / tests (ubuntu-latest) (push) Waiting to run
Build Test / tests (windows-latest) (push) Waiting to run
CodeQL / Analyze (java) (push) Waiting to run
This commit is contained in:
parent
5d064d3e50
commit
be6cb573b1
@ -4,7 +4,7 @@ import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@ -13,6 +13,7 @@ import java.util.function.Supplier;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.ParameterDescription;
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import com.beust.jcommander.Parameterized;
|
||||
@ -133,14 +134,16 @@ public class JCommanderWrapper<T> {
|
||||
out.println("options:");
|
||||
|
||||
List<ParameterDescription> params = jc.getParameters();
|
||||
Map<String, ParameterDescription> paramsMap = new LinkedHashMap<>(params.size());
|
||||
Map<String, ParameterDescription> paramsMap = new HashMap<>(params.size());
|
||||
int maxNamesLen = 0;
|
||||
for (ParameterDescription p : params) {
|
||||
paramsMap.put(p.getParameterized().getName(), p);
|
||||
int len = p.getNames().length();
|
||||
if (len > maxNamesLen) {
|
||||
maxNamesLen = len;
|
||||
String valueDesc = getValueDesc(p);
|
||||
if (valueDesc != null) {
|
||||
len += 1 + valueDesc.length();
|
||||
}
|
||||
maxNamesLen = Math.max(maxNamesLen, len);
|
||||
}
|
||||
maxNamesLen += 3;
|
||||
|
||||
@ -153,8 +156,12 @@ public class JCommanderWrapper<T> {
|
||||
}
|
||||
StringBuilder opt = new StringBuilder();
|
||||
opt.append(" ").append(p.getNames());
|
||||
String description = p.getDescription();
|
||||
String valueDesc = getValueDesc(p);
|
||||
if (valueDesc != null) {
|
||||
opt.append(' ').append(valueDesc);
|
||||
}
|
||||
addSpaces(opt, maxNamesLen - opt.length());
|
||||
String description = p.getDescription();
|
||||
if (description.contains("\n")) {
|
||||
String[] lines = description.split("\n");
|
||||
opt.append("- ").append(lines[0]);
|
||||
@ -177,6 +184,11 @@ public class JCommanderWrapper<T> {
|
||||
return maxNamesLen;
|
||||
}
|
||||
|
||||
private static @Nullable String getValueDesc(ParameterDescription p) {
|
||||
Parameter parameterAnnotation = p.getParameterAnnotation();
|
||||
return parameterAnnotation == null ? null : parameterAnnotation.defaultValueDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all declared fields of the specified class and all super classes
|
||||
*/
|
||||
|
@ -12,6 +12,7 @@ import com.beust.jcommander.Parameters;
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
import jadx.cli.JCommanderWrapper;
|
||||
import jadx.cli.LogHelper;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.plugins.tools.JadxPluginsList;
|
||||
import jadx.plugins.tools.JadxPluginsTools;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
@ -20,33 +21,40 @@ import jadx.plugins.tools.data.JadxPluginUpdate;
|
||||
@Parameters(commandDescription = "manage jadx plugins")
|
||||
public class CommandPlugins implements ICommand {
|
||||
|
||||
@Parameter(names = { "-i", "--install" }, description = "install plugin with locationId")
|
||||
@Parameter(names = { "-i", "--install" }, description = "install plugin with locationId", defaultValueDescription = "<locationId>")
|
||||
protected String install;
|
||||
|
||||
@Parameter(names = { "-j", "--install-jar" }, description = "install plugin from jar file")
|
||||
@Parameter(names = { "-j", "--install-jar" }, description = "install plugin from jar file", defaultValueDescription = "<path-to.jar>")
|
||||
protected String installJar;
|
||||
|
||||
@Parameter(names = { "-l", "--list" }, description = "list installed plugins")
|
||||
protected boolean list;
|
||||
|
||||
@Parameter(names = { "--list-all" }, description = "list all plugins including bundled and dropins")
|
||||
protected boolean listAll;
|
||||
|
||||
@Parameter(names = { "-a", "--available" }, description = "list available plugins")
|
||||
@Parameter(names = { "-a", "--available" }, description = "list available plugins from jadx-plugins-list (aka marketplace)")
|
||||
protected boolean available;
|
||||
|
||||
@Parameter(names = { "-u", "--update" }, description = "update installed plugins")
|
||||
protected boolean update;
|
||||
|
||||
@Parameter(names = { "--uninstall" }, description = "uninstall plugin with pluginId")
|
||||
@Parameter(names = { "--uninstall" }, description = "uninstall plugin with pluginId", defaultValueDescription = "<pluginId>")
|
||||
protected String uninstall;
|
||||
|
||||
@Parameter(names = { "--disable" }, description = "disable plugin with pluginId")
|
||||
@Parameter(names = { "--disable" }, description = "disable plugin with pluginId", defaultValueDescription = "<pluginId>")
|
||||
protected String disable;
|
||||
|
||||
@Parameter(names = { "--enable" }, description = "enable plugin with pluginId")
|
||||
@Parameter(names = { "--enable" }, description = "enable plugin with pluginId", defaultValueDescription = "<pluginId>")
|
||||
protected String enable;
|
||||
|
||||
@Parameter(names = { "--list-all" }, description = "list all plugins including bundled and dropins")
|
||||
protected boolean listAll;
|
||||
|
||||
@Parameter(
|
||||
names = { "--list-versions" },
|
||||
description = "fetch latest versions of plugin from locationId (will download all artefacts, limited to 10)",
|
||||
defaultValueDescription = "<locationId>"
|
||||
)
|
||||
protected String listVersions;
|
||||
|
||||
@Parameter(names = { "-h", "--help" }, description = "print this help", help = true)
|
||||
protected boolean printHelp = false;
|
||||
|
||||
@ -55,6 +63,7 @@ public class CommandPlugins implements ICommand {
|
||||
return "plugins";
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnnecessaryReturnStatement")
|
||||
@Override
|
||||
public void process(JCommanderWrapper<?> jcw, JCommander subCommander) {
|
||||
if (printHelp) {
|
||||
@ -71,13 +80,16 @@ public class CommandPlugins implements ICommand {
|
||||
|
||||
if (install != null) {
|
||||
installPlugin(install);
|
||||
return;
|
||||
}
|
||||
if (installJar != null) {
|
||||
installPlugin("file:" + installJar);
|
||||
return;
|
||||
}
|
||||
if (uninstall != null) {
|
||||
boolean uninstalled = JadxPluginsTools.getInstance().uninstall(uninstall);
|
||||
System.out.println(uninstalled ? "Uninstalled" : "Plugin not found");
|
||||
return;
|
||||
}
|
||||
if (update) {
|
||||
List<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();
|
||||
@ -89,14 +101,20 @@ public class CommandPlugins implements ICommand {
|
||||
System.out.println(" " + update.getPluginId() + ": " + update.getOldVersion() + " -> " + update.getNewVersion());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (list) {
|
||||
printPlugins(JadxPluginsTools.getInstance().getInstalled());
|
||||
return;
|
||||
}
|
||||
if (listAll) {
|
||||
printAllPlugins();
|
||||
return;
|
||||
}
|
||||
if (listVersions != null) {
|
||||
printVersions(listVersions, 10);
|
||||
return;
|
||||
}
|
||||
|
||||
if (available) {
|
||||
List<JadxPluginMetadata> availableList = JadxPluginsList.getInstance().get();
|
||||
System.out.println("Available plugins: " + availableList.size());
|
||||
@ -104,6 +122,7 @@ public class CommandPlugins implements ICommand {
|
||||
System.out.println(" - " + plugin.getName() + ": " + plugin.getDescription()
|
||||
+ " (" + plugin.getLocationId() + ")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (disable != null) {
|
||||
@ -112,6 +131,7 @@ public class CommandPlugins implements ICommand {
|
||||
} else {
|
||||
System.out.println("Plugin '" + disable + "' already disabled.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (enable != null) {
|
||||
if (JadxPluginsTools.getInstance().changeDisabledStatus(enable, false)) {
|
||||
@ -119,6 +139,7 @@ public class CommandPlugins implements ICommand {
|
||||
} else {
|
||||
System.out.println("Plugin '" + enable + "' already enabled.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,6 +161,26 @@ public class CommandPlugins implements ICommand {
|
||||
}
|
||||
}
|
||||
|
||||
private void printVersions(String locationId, int limit) {
|
||||
System.out.println("Loading ...");
|
||||
List<JadxPluginMetadata> versions = JadxPluginsTools.getInstance().getVersionsByLocation(locationId, 1, limit);
|
||||
if (versions.isEmpty()) {
|
||||
System.out.println("No versions found");
|
||||
return;
|
||||
}
|
||||
JadxPluginMetadata plugin = versions.get(0);
|
||||
System.out.println("Versions for plugin id: " + plugin.getPluginId());
|
||||
for (JadxPluginMetadata version : versions) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(" - ").append(version.getVersion());
|
||||
String reqVer = version.getRequiredJadxVersion();
|
||||
if (StringUtils.notBlank(reqVer)) {
|
||||
sb.append(", require jadx: ").append(reqVer);
|
||||
}
|
||||
System.out.println(sb);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printAllPlugins() {
|
||||
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
|
||||
printPlugins(installed);
|
||||
|
@ -1,15 +1,29 @@
|
||||
package jadx.api.plugins;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JadxPluginInfo {
|
||||
private final String pluginId;
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final String homepage;
|
||||
private String homepage;
|
||||
|
||||
/**
|
||||
* Conflicting plugins should have the same 'provides' property; only one will be loaded
|
||||
*/
|
||||
private final String provides;
|
||||
private String provides;
|
||||
|
||||
/**
|
||||
* Minimum required jadx version to run this plugin.
|
||||
* <br>
|
||||
* Format: "<stable version>, r<revision number of unstable version>".
|
||||
* Example: "1.5.1, r2305"
|
||||
*
|
||||
* @see <a href="https://github.com/skylot/jadx/wiki/Jadx-plugins-guide#required-jadx-version">wiki
|
||||
* page</a>
|
||||
* for details.
|
||||
*/
|
||||
private @Nullable String requiredJadxVersion;
|
||||
|
||||
public JadxPluginInfo(String id, String name, String description) {
|
||||
this(id, name, description, "", id);
|
||||
@ -43,10 +57,26 @@ public class JadxPluginInfo {
|
||||
return homepage;
|
||||
}
|
||||
|
||||
public void setHomepage(String homepage) {
|
||||
this.homepage = homepage;
|
||||
}
|
||||
|
||||
public String getProvides() {
|
||||
return provides;
|
||||
}
|
||||
|
||||
public void setProvides(String provides) {
|
||||
this.provides = provides;
|
||||
}
|
||||
|
||||
public @Nullable String getRequiredJadxVersion() {
|
||||
return requiredJadxVersion;
|
||||
}
|
||||
|
||||
public void setRequiredJadxVersion(@Nullable String requiredJadxVersion) {
|
||||
this.requiredJadxVersion = requiredJadxVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return pluginId + ": " + name + " - '" + description + '\'';
|
||||
|
@ -4,11 +4,14 @@ import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.plugins.versions.VerifyRequiredVersion;
|
||||
|
||||
public class JadxPluginInfoBuilder {
|
||||
private String pluginId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String homepage = "";
|
||||
private @Nullable String requiredJadxVersion;
|
||||
private @Nullable String provides;
|
||||
|
||||
/**
|
||||
@ -43,6 +46,11 @@ public class JadxPluginInfoBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JadxPluginInfoBuilder requiredJadxVersion(String versions) {
|
||||
this.requiredJadxVersion = versions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JadxPluginInfo build() {
|
||||
Objects.requireNonNull(pluginId, "PluginId is required");
|
||||
Objects.requireNonNull(name, "Name is required");
|
||||
@ -50,6 +58,11 @@ public class JadxPluginInfoBuilder {
|
||||
if (provides == null) {
|
||||
provides = pluginId;
|
||||
}
|
||||
return new JadxPluginInfo(pluginId, name, description, homepage, provides);
|
||||
if (requiredJadxVersion != null) {
|
||||
VerifyRequiredVersion.verify(requiredJadxVersion);
|
||||
}
|
||||
JadxPluginInfo pluginInfo = new JadxPluginInfo(pluginId, name, description, homepage, provides);
|
||||
pluginInfo.setRequiredJadxVersion(requiredJadxVersion);
|
||||
return pluginInfo;
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import jadx.api.plugins.input.JadxCodeInput;
|
||||
import jadx.api.plugins.loader.JadxPluginLoader;
|
||||
import jadx.api.plugins.options.JadxPluginOptions;
|
||||
import jadx.api.plugins.options.OptionDescription;
|
||||
import jadx.core.plugins.versions.VerifyRequiredVersion;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class JadxPluginManager {
|
||||
@ -50,15 +51,16 @@ public class JadxPluginManager {
|
||||
|
||||
public void load(JadxPluginLoader pluginLoader) {
|
||||
allPlugins.clear();
|
||||
VerifyRequiredVersion verifyRequiredVersion = new VerifyRequiredVersion();
|
||||
for (JadxPlugin plugin : pluginLoader.load()) {
|
||||
addPlugin(plugin);
|
||||
addPlugin(plugin, verifyRequiredVersion);
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
|
||||
public void register(JadxPlugin plugin) {
|
||||
Objects.requireNonNull(plugin);
|
||||
PluginContext addedPlugin = addPlugin(plugin);
|
||||
PluginContext addedPlugin = addPlugin(plugin, new VerifyRequiredVersion());
|
||||
if (addedPlugin == null) {
|
||||
LOG.debug("Can't register plugin, it was disabled: {}", plugin.getPluginInfo().getPluginId());
|
||||
return;
|
||||
@ -67,11 +69,17 @@ public class JadxPluginManager {
|
||||
resolve();
|
||||
}
|
||||
|
||||
private @Nullable PluginContext addPlugin(JadxPlugin plugin) {
|
||||
private @Nullable PluginContext addPlugin(JadxPlugin plugin, VerifyRequiredVersion verifyRequiredVersion) {
|
||||
PluginContext pluginContext = new PluginContext(decompiler, pluginsData, plugin);
|
||||
if (disabledPlugins.contains(pluginContext.getPluginId())) {
|
||||
return null;
|
||||
}
|
||||
String requiredJadxVersion = pluginContext.getPluginInfo().getRequiredJadxVersion();
|
||||
if (!verifyRequiredVersion.isCompatible(requiredJadxVersion)) {
|
||||
LOG.warn("Plugin '{}' not loaded: requires '{}' jadx version which it is not compatible with current: {}",
|
||||
pluginContext, requiredJadxVersion, verifyRequiredVersion.getJadxVersion());
|
||||
return null;
|
||||
}
|
||||
LOG.debug("Loading plugin: {}", pluginContext);
|
||||
if (!allPlugins.add(pluginContext)) {
|
||||
throw new IllegalArgumentException("Duplicate plugin id: " + pluginContext + ", class " + plugin.getClass());
|
||||
|
@ -0,0 +1,85 @@
|
||||
package jadx.core.plugins.versions;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.Jadx;
|
||||
|
||||
public class VerifyRequiredVersion {
|
||||
|
||||
public static boolean isJadxCompatible(@Nullable String reqVersionStr) {
|
||||
return new VerifyRequiredVersion().isCompatible(reqVersionStr);
|
||||
}
|
||||
|
||||
public static void verify(String requiredJadxVersion) {
|
||||
try {
|
||||
parse(requiredJadxVersion);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Malformed 'requiredJadxVersion': " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private final String jadxVersion;
|
||||
private final boolean unstable;
|
||||
|
||||
private final boolean dev;
|
||||
|
||||
public VerifyRequiredVersion() {
|
||||
this(Jadx.getVersion());
|
||||
}
|
||||
|
||||
public VerifyRequiredVersion(String jadxVersion) {
|
||||
this.jadxVersion = jadxVersion;
|
||||
this.unstable = jadxVersion.startsWith("r");
|
||||
this.dev = jadxVersion.equals(Jadx.VERSION_DEV);
|
||||
}
|
||||
|
||||
public boolean isCompatible(@Nullable String reqVersionStr) {
|
||||
if (reqVersionStr == null || reqVersionStr.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
RequiredVersionData reqVer = parse(reqVersionStr);
|
||||
if (dev) {
|
||||
// keep version str parsing for verification
|
||||
return true;
|
||||
}
|
||||
if (unstable) {
|
||||
return VersionComparator.checkAndCompare(jadxVersion, reqVer.getUnstableRev()) >= 0;
|
||||
}
|
||||
return VersionComparator.checkAndCompare(jadxVersion, reqVer.getReleaseVer()) >= 0;
|
||||
}
|
||||
|
||||
public String getJadxVersion() {
|
||||
return jadxVersion;
|
||||
}
|
||||
|
||||
private static final Pattern REQ_VER_FORMAT = Pattern.compile("(\\d+\\.\\d+\\.\\d+),\\s+(r\\d+)");
|
||||
|
||||
private static RequiredVersionData parse(String reqVersionStr) {
|
||||
Matcher matcher = REQ_VER_FORMAT.matcher(reqVersionStr);
|
||||
if (!matcher.matches()) {
|
||||
throw new RuntimeException("Expect format: " + REQ_VER_FORMAT + ", got: " + reqVersionStr);
|
||||
}
|
||||
return new RequiredVersionData(matcher.group(1), matcher.group(2));
|
||||
}
|
||||
|
||||
private static final class RequiredVersionData {
|
||||
private final String releaseVer;
|
||||
private final String unstableRev;
|
||||
|
||||
private RequiredVersionData(String releaseVer, String unstableRev) {
|
||||
this.releaseVer = releaseVer;
|
||||
this.unstableRev = unstableRev;
|
||||
}
|
||||
|
||||
public String getReleaseVer() {
|
||||
return releaseVer;
|
||||
}
|
||||
|
||||
public String getUnstableRev() {
|
||||
return unstableRev;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package jadx.gui.update;
|
||||
package jadx.core.plugins.versions;
|
||||
|
||||
public class VersionComparator {
|
||||
|
@ -0,0 +1,27 @@
|
||||
package jadx.core.plugins.versions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class VerifyRequiredVersionTest {
|
||||
@Test
|
||||
public void test() {
|
||||
isCompatible("1.5.0, r2000", "1.5.1", true);
|
||||
isCompatible("1.5.1, r3000", "1.5.1", true);
|
||||
isCompatible("1.5.1, r3000", "1.6.0", true);
|
||||
isCompatible("1.5.1, r3000", "1.5.0", false);
|
||||
|
||||
isCompatible("1.5.1, r3000", "r3001.417bb7a", true);
|
||||
isCompatible("1.5.1, r3000", "r4000", true);
|
||||
isCompatible("1.5.1, r3000", "r3000", true);
|
||||
isCompatible("1.5.1, r3000", "r2000", false);
|
||||
}
|
||||
|
||||
private static void isCompatible(String requiredVersion, String jadxVersion, boolean result) {
|
||||
assertThat(new VerifyRequiredVersion(jadxVersion).isCompatible(requiredVersion))
|
||||
.as("Expect plugin with required version %s is%s compatible with jadx %s",
|
||||
requiredVersion, result ? "" : " not", jadxVersion)
|
||||
.isEqualTo(result);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package jadx.gui.update;
|
||||
package jadx.core.plugins.versions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -5,6 +5,7 @@ import com.google.gson.annotations.SerializedName
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import jadx.api.JadxDecompiler
|
||||
import jadx.core.Jadx
|
||||
import jadx.core.plugins.versions.VersionComparator
|
||||
import jadx.gui.settings.JadxUpdateChannel
|
||||
import org.jetbrains.kotlin.konan.file.use
|
||||
import java.io.InputStreamReader
|
||||
|
@ -1,24 +1,23 @@
|
||||
package jadx.plugins.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.plugins.JadxPlugin;
|
||||
import jadx.api.plugins.loader.JadxPluginLoader;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class JadxExternalPluginsLoader implements JadxPluginLoader {
|
||||
@ -31,9 +30,8 @@ public class JadxExternalPluginsLoader implements JadxPluginLoader {
|
||||
close();
|
||||
long start = System.currentTimeMillis();
|
||||
Map<Class<? extends JadxPlugin>, JadxPlugin> map = new HashMap<>();
|
||||
ClassLoader classLoader = JadxPluginsTools.class.getClassLoader();
|
||||
loadFromClsLoader(map, classLoader);
|
||||
loadInstalledPlugins(map, classLoader);
|
||||
loadFromClsLoader(map, thisClassLoader());
|
||||
loadInstalledPlugins(map);
|
||||
|
||||
List<JadxPlugin> list = new ArrayList<>(map.size());
|
||||
list.addAll(map.values());
|
||||
@ -44,52 +42,53 @@ public class JadxExternalPluginsLoader implements JadxPluginLoader {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: find a better way to load only plugin from single jar without plugins from parent
|
||||
* classloader
|
||||
*/
|
||||
public JadxPlugin loadFromJar(Path jar) {
|
||||
Map<Class<? extends JadxPlugin>, JadxPlugin> map = new HashMap<>();
|
||||
ClassLoader classLoader = JadxPluginsTools.class.getClassLoader();
|
||||
loadFromClsLoader(map, classLoader);
|
||||
Set<Class<? extends JadxPlugin>> clspPlugins = new HashSet<>(map.keySet());
|
||||
try (URLClassLoader pluginClassLoader = loadFromJar(map, classLoader, jar)) {
|
||||
return map.entrySet().stream()
|
||||
.filter(entry -> !clspPlugins.contains(entry.getKey()))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getValue)
|
||||
.orElseThrow(() -> new RuntimeException("No plugin found in jar: " + jar));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load plugin jar: " + jar, e);
|
||||
loadFromJar(map, jar);
|
||||
int loaded = map.size();
|
||||
if (loaded == 0) {
|
||||
throw new JadxRuntimeException("No plugin found in jar: " + jar);
|
||||
}
|
||||
if (loaded > 1) {
|
||||
String plugins = map.values().stream().map(p -> p.getPluginInfo().getPluginId()).collect(Collectors.joining(", "));
|
||||
throw new JadxRuntimeException("Expect only one plugin per jar: " + jar + ", but found: " + loaded + " - " + plugins);
|
||||
}
|
||||
return Utils.first(map.values());
|
||||
|
||||
}
|
||||
|
||||
private void loadFromClsLoader(Map<Class<? extends JadxPlugin>, JadxPlugin> map, ClassLoader classLoader) {
|
||||
ServiceLoader.load(JadxPlugin.class, classLoader)
|
||||
.stream()
|
||||
.filter(p -> p.type().getClassLoader() == classLoader)
|
||||
.filter(p -> !map.containsKey(p.type()))
|
||||
.forEach(p -> map.put(p.type(), p.get()));
|
||||
}
|
||||
|
||||
private void loadInstalledPlugins(Map<Class<? extends JadxPlugin>, JadxPlugin> map, ClassLoader classLoader) {
|
||||
private void loadInstalledPlugins(Map<Class<? extends JadxPlugin>, JadxPlugin> map) {
|
||||
List<Path> jars = JadxPluginsTools.getInstance().getEnabledPluginJars();
|
||||
for (Path jar : jars) {
|
||||
classLoaders.add(loadFromJar(map, classLoader, jar));
|
||||
loadFromJar(map, jar);
|
||||
}
|
||||
}
|
||||
|
||||
private URLClassLoader loadFromJar(Map<Class<? extends JadxPlugin>, JadxPlugin> map, ClassLoader classLoader, Path jar) {
|
||||
private void loadFromJar(Map<Class<? extends JadxPlugin>, JadxPlugin> map, Path jar) {
|
||||
try {
|
||||
File jarFile = jar.toFile();
|
||||
String clsLoaderName = "jadx-plugin:" + jarFile.getName();
|
||||
URL[] urls = new URL[] { jarFile.toURI().toURL() };
|
||||
URLClassLoader pluginClsLoader = new URLClassLoader("jadx-plugin:" + jarFile.getName(), urls, classLoader);
|
||||
URLClassLoader pluginClsLoader = new URLClassLoader(clsLoaderName, urls, thisClassLoader());
|
||||
classLoaders.add(pluginClsLoader);
|
||||
loadFromClsLoader(map, pluginClsLoader);
|
||||
return pluginClsLoader;
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to load plugins, jar: " + jar, e);
|
||||
throw new JadxRuntimeException("Failed to load plugins from jar: " + jar, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassLoader thisClassLoader() {
|
||||
return JadxExternalPluginsLoader.class.getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
|
@ -16,12 +16,18 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import jadx.api.plugins.JadxPlugin;
|
||||
import jadx.api.plugins.JadxPluginInfo;
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.plugins.versions.VerifyRequiredVersion;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.plugins.tools.data.JadxInstalledPlugins;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
@ -35,6 +41,8 @@ import static jadx.plugins.tools.utils.PluginFiles.INSTALLED_DIR;
|
||||
import static jadx.plugins.tools.utils.PluginFiles.PLUGINS_JSON;
|
||||
|
||||
public class JadxPluginsTools {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxPluginsTools.class);
|
||||
|
||||
private static final JadxPluginsTools INSTANCE = new JadxPluginsTools();
|
||||
|
||||
public static JadxPluginsTools getInstance() {
|
||||
@ -45,25 +53,62 @@ public class JadxPluginsTools {
|
||||
}
|
||||
|
||||
public JadxPluginMetadata install(String locationId) {
|
||||
JadxPluginMetadata pluginMetadata = resolveMetadata(locationId);
|
||||
install(pluginMetadata);
|
||||
return pluginMetadata;
|
||||
IJadxPluginResolver resolver = ResolversRegistry.getResolver(locationId);
|
||||
boolean hasVersion = resolver.hasVersion(locationId);
|
||||
if (hasVersion) {
|
||||
JadxPluginMetadata pluginMetadata = resolver.resolve(locationId)
|
||||
.orElseThrow(() -> new JadxRuntimeException("Failed to resolve plugin location: " + locationId));
|
||||
fillMetadata(pluginMetadata);
|
||||
install(pluginMetadata);
|
||||
return pluginMetadata;
|
||||
}
|
||||
// try other versions in case latest is not compatible with current jadx
|
||||
VerifyRequiredVersion verifyRequiredVersion = new VerifyRequiredVersion();
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
try {
|
||||
for (JadxPluginMetadata pluginMetadata : resolver.resolveVersions(locationId, i, 1)) {
|
||||
fillMetadata(pluginMetadata);
|
||||
if (verifyRequiredVersion.isCompatible(pluginMetadata.getRequiredJadxVersion())) {
|
||||
install(pluginMetadata);
|
||||
return pluginMetadata;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to fetch plugin ({} version before latest)", i, e);
|
||||
}
|
||||
}
|
||||
throw new JadxRuntimeException("Can't find compatible version to install");
|
||||
}
|
||||
|
||||
public JadxPluginMetadata resolveMetadata(String locationId) {
|
||||
JadxPluginMetadata pluginMetadata = ResolversRegistry.resolve(locationId)
|
||||
IJadxPluginResolver resolver = ResolversRegistry.getResolver(locationId);
|
||||
JadxPluginMetadata pluginMetadata = resolver.resolve(locationId)
|
||||
.orElseThrow(() -> new RuntimeException("Failed to resolve locationId: " + locationId));
|
||||
fillMetadata(pluginMetadata);
|
||||
return pluginMetadata;
|
||||
}
|
||||
|
||||
public List<JadxPluginMetadata> getVersionsByLocation(String locationId, int page, int perPage) {
|
||||
IJadxPluginResolver resolver = ResolversRegistry.getResolver(locationId);
|
||||
List<JadxPluginMetadata> list = resolver.resolveVersions(locationId, page, perPage);
|
||||
for (JadxPluginMetadata pluginMetadata : list) {
|
||||
fillMetadata(pluginMetadata);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<JadxPluginUpdate> updateAll() {
|
||||
JadxInstalledPlugins plugins = loadPluginsJson();
|
||||
int size = plugins.getInstalled().size();
|
||||
List<JadxPluginUpdate> updates = new ArrayList<>(size);
|
||||
List<JadxPluginMetadata> newList = new ArrayList<>(size);
|
||||
for (JadxPluginMetadata plugin : plugins.getInstalled()) {
|
||||
JadxPluginMetadata newVersion = update(plugin);
|
||||
JadxPluginMetadata newVersion = null;
|
||||
try {
|
||||
newVersion = update(plugin);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to update plugin: {}", plugin.getPluginId(), e);
|
||||
}
|
||||
if (newVersion != null) {
|
||||
updates.add(new JadxPluginUpdate(plugin, newVersion));
|
||||
newList.add(newVersion);
|
||||
@ -135,7 +180,7 @@ public class JadxPluginsTools {
|
||||
for (JadxPluginMetadata pluginMetadata : loadPluginsJson().getInstalled()) {
|
||||
list.add(INSTALLED_DIR.resolve(pluginMetadata.getJar()));
|
||||
}
|
||||
collectFromDir(list, DROPINS_DIR);
|
||||
collectJarsFromDir(list, DROPINS_DIR);
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -147,7 +192,7 @@ public class JadxPluginsTools {
|
||||
}
|
||||
list.add(INSTALLED_DIR.resolve(pluginMetadata.getJar()));
|
||||
}
|
||||
collectFromDir(list, DROPINS_DIR);
|
||||
collectJarsFromDir(list, DROPINS_DIR);
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -172,7 +217,7 @@ public class JadxPluginsTools {
|
||||
}
|
||||
|
||||
private @Nullable JadxPluginMetadata update(JadxPluginMetadata plugin) {
|
||||
IJadxPluginResolver resolver = ResolversRegistry.getById(plugin.getResolverId());
|
||||
IJadxPluginResolver resolver = ResolversRegistry.getResolver(plugin.getLocationId());
|
||||
if (!resolver.isUpdateSupported()) {
|
||||
return null;
|
||||
}
|
||||
@ -189,19 +234,28 @@ public class JadxPluginsTools {
|
||||
return update;
|
||||
}
|
||||
|
||||
public void install(JadxPluginMetadata metadata) {
|
||||
private void install(JadxPluginMetadata metadata) {
|
||||
String reqVersionStr = metadata.getRequiredJadxVersion();
|
||||
if (!VerifyRequiredVersion.isJadxCompatible(reqVersionStr)) {
|
||||
throw new JadxRuntimeException("Can't install plugin, required version: \"" + reqVersionStr + '\"'
|
||||
+ " is not compatible with current jadx version: " + Jadx.getVersion());
|
||||
}
|
||||
|
||||
String version = metadata.getVersion();
|
||||
String fileName = metadata.getPluginId() + (version != null ? '-' + version : "") + ".jar";
|
||||
String fileName = metadata.getPluginId() + (StringUtils.notBlank(version) ? '-' + version : "") + ".jar";
|
||||
Path pluginJar = INSTALLED_DIR.resolve(fileName);
|
||||
copyJar(Paths.get(metadata.getJar()), pluginJar);
|
||||
metadata.setJar(INSTALLED_DIR.relativize(pluginJar).toString());
|
||||
|
||||
JadxInstalledPlugins plugins = loadPluginsJson();
|
||||
// remove previous version jar
|
||||
plugins.getInstalled().stream()
|
||||
.filter(p -> p.getPluginId().equals(metadata.getPluginId()))
|
||||
.forEach(this::deletePluginJar);
|
||||
plugins.getInstalled().remove(metadata);
|
||||
plugins.getInstalled().removeIf(p -> {
|
||||
if (p.getPluginId().equals(metadata.getPluginId())) {
|
||||
deletePluginJar(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
plugins.getInstalled().add(metadata);
|
||||
plugins.setUpdated(System.currentTimeMillis());
|
||||
savePluginsJson(plugins);
|
||||
@ -227,6 +281,9 @@ public class JadxPluginsTools {
|
||||
metadata.setName(pluginInfo.getName());
|
||||
metadata.setDescription(pluginInfo.getDescription());
|
||||
metadata.setHomepage(pluginInfo.getHomepage());
|
||||
metadata.setRequiredJadxVersion(pluginInfo.getRequiredJadxVersion());
|
||||
} catch (NoSuchMethodError e) {
|
||||
throw new RuntimeException("Looks like plugin uses unknown API, try to update jadx version", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,10 +315,14 @@ public class JadxPluginsTools {
|
||||
|
||||
private JadxInstalledPlugins loadPluginsJson() {
|
||||
if (!Files.isRegularFile(PLUGINS_JSON)) {
|
||||
return new JadxInstalledPlugins();
|
||||
JadxInstalledPlugins plugins = new JadxInstalledPlugins();
|
||||
plugins.setVersion(1);
|
||||
return plugins;
|
||||
}
|
||||
try (Reader reader = Files.newBufferedReader(PLUGINS_JSON, StandardCharsets.UTF_8)) {
|
||||
return buildGson().fromJson(reader, JadxInstalledPlugins.class);
|
||||
JadxInstalledPlugins data = buildGson().fromJson(reader, JadxInstalledPlugins.class);
|
||||
upgradePluginsData(data);
|
||||
return data;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to read file: " + PLUGINS_JSON);
|
||||
}
|
||||
@ -284,7 +345,13 @@ public class JadxPluginsTools {
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectFromDir(List<Path> list, Path dir) {
|
||||
private void upgradePluginsData(JadxInstalledPlugins data) {
|
||||
if (data.getVersion() == 0) {
|
||||
data.setVersion(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectJarsFromDir(List<Path> list, Path dir) {
|
||||
try (Stream<Path> files = Files.list(dir)) {
|
||||
files.filter(p -> p.getFileName().toString().endsWith(".jar")).forEach(list::add);
|
||||
} catch (IOException e) {
|
||||
|
@ -4,11 +4,18 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JadxInstalledPlugins {
|
||||
|
||||
private int version;
|
||||
private long updated;
|
||||
|
||||
private List<JadxPluginMetadata> installed = new ArrayList<>();
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public long getUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
@ -8,9 +8,10 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
private String name;
|
||||
private String description;
|
||||
private String homepage;
|
||||
private @Nullable String requiredJadxVersion;
|
||||
|
||||
private @Nullable String version;
|
||||
private String locationId;
|
||||
private String resolverId;
|
||||
private String jar;
|
||||
private boolean disabled;
|
||||
|
||||
@ -34,7 +35,7 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
public void setVersion(@Nullable String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@ -54,6 +55,14 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
this.homepage = homepage;
|
||||
}
|
||||
|
||||
public @Nullable String getRequiredJadxVersion() {
|
||||
return requiredJadxVersion;
|
||||
}
|
||||
|
||||
public void setRequiredJadxVersion(@Nullable String requiredJadxVersion) {
|
||||
this.requiredJadxVersion = requiredJadxVersion;
|
||||
}
|
||||
|
||||
public String getLocationId() {
|
||||
return locationId;
|
||||
}
|
||||
@ -62,14 +71,6 @@ public class JadxPluginMetadata implements Comparable<JadxPluginMetadata> {
|
||||
this.locationId = locationId;
|
||||
}
|
||||
|
||||
public String getResolverId() {
|
||||
return resolverId;
|
||||
}
|
||||
|
||||
public void setResolverId(String resolverId) {
|
||||
this.resolverId = resolverId;
|
||||
}
|
||||
|
||||
public String getJar() {
|
||||
return jar;
|
||||
}
|
||||
|
@ -1,14 +1,37 @@
|
||||
package jadx.plugins.tools.resolvers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
|
||||
public interface IJadxPluginResolver {
|
||||
|
||||
/**
|
||||
* Unique resolver identifier, should be same as locationId prefix
|
||||
*/
|
||||
String id();
|
||||
|
||||
/**
|
||||
* This resolver support updates and can fetch the latest version.
|
||||
*/
|
||||
boolean isUpdateSupported();
|
||||
|
||||
/**
|
||||
* Fetch the latest version plugin metadata by location
|
||||
*/
|
||||
Optional<JadxPluginMetadata> resolve(String locationId);
|
||||
|
||||
/**
|
||||
* Fetch several latest versions (pageable) of plugin by locationId.
|
||||
*
|
||||
* @param page page number, starts with 1
|
||||
* @param perPage result's count limit
|
||||
*/
|
||||
List<JadxPluginMetadata> resolveVersions(String locationId, int page, int perPage);
|
||||
|
||||
/**
|
||||
* Check if locationId has a specified version number
|
||||
*/
|
||||
boolean hasVersion(String locationId);
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
package jadx.plugins.tools.resolvers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Objects;
|
||||
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
import jadx.plugins.tools.resolvers.file.LocalFileResolver;
|
||||
import jadx.plugins.tools.resolvers.github.GithubReleaseResolver;
|
||||
|
||||
public class ResolversRegistry {
|
||||
|
||||
private static final Map<String, IJadxPluginResolver> RESOLVERS_MAP = new TreeMap<>();
|
||||
private static final Map<String, IJadxPluginResolver> RESOLVERS_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
register(new LocalFileResolver());
|
||||
@ -21,14 +20,13 @@ public class ResolversRegistry {
|
||||
RESOLVERS_MAP.put(resolver.id(), resolver);
|
||||
}
|
||||
|
||||
public static Optional<JadxPluginMetadata> resolve(String locationId) {
|
||||
for (IJadxPluginResolver resolver : RESOLVERS_MAP.values()) {
|
||||
Optional<JadxPluginMetadata> result = resolver.resolve(locationId);
|
||||
if (result.isPresent()) {
|
||||
return result;
|
||||
}
|
||||
public static IJadxPluginResolver getResolver(String locationId) {
|
||||
Objects.requireNonNull(locationId);
|
||||
int sep = locationId.indexOf(':');
|
||||
if (sep <= 0) {
|
||||
throw new IllegalArgumentException("Malformed locationId: " + locationId);
|
||||
}
|
||||
return Optional.empty();
|
||||
return getById(locationId.substring(0, sep));
|
||||
}
|
||||
|
||||
public static IJadxPluginResolver getById(String resolverId) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package jadx.plugins.tools.resolvers.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
@ -30,8 +31,23 @@ public class LocalFileResolver implements IJadxPluginResolver {
|
||||
}
|
||||
JadxPluginMetadata metadata = new JadxPluginMetadata();
|
||||
metadata.setLocationId(locationId);
|
||||
metadata.setResolverId(id());
|
||||
metadata.setJar(jarFile.getAbsolutePath());
|
||||
return Optional.of(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JadxPluginMetadata> resolveVersions(String locationId, int page, int perPage) {
|
||||
if (page > 1) {
|
||||
// no other versions
|
||||
return List.of();
|
||||
}
|
||||
// return only the current file
|
||||
return resolve(locationId).map(List::of).orElseGet(List::of);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVersion(String locationId) {
|
||||
// no supported
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package jadx.plugins.tools.resolvers.github;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||
@ -22,16 +23,38 @@ public class GithubReleaseResolver implements IJadxPluginResolver {
|
||||
return Optional.empty();
|
||||
}
|
||||
Release release = GithubTools.fetchRelease(info);
|
||||
JadxPluginMetadata metadata = buildMetadata(release, info);
|
||||
return Optional.of(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JadxPluginMetadata> resolveVersions(String locationId, int page, int perPage) {
|
||||
LocationInfo info = parseLocation(locationId);
|
||||
if (info == null) {
|
||||
return List.of();
|
||||
}
|
||||
return GithubTools.fetchReleases(info, page, perPage)
|
||||
.stream()
|
||||
.map(r -> buildMetadata(r, info))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVersion(String locationId) {
|
||||
LocationInfo locationInfo = parseLocation(locationId);
|
||||
return locationInfo != null && locationInfo.getVersion() != null;
|
||||
}
|
||||
|
||||
private JadxPluginMetadata buildMetadata(Release release, LocationInfo info) {
|
||||
List<Asset> assets = release.getAssets();
|
||||
String releaseVersion = removePrefix(release.getName(), "v");
|
||||
Asset asset = searchPluginAsset(assets, info.getArtifactPrefix(), releaseVersion);
|
||||
|
||||
JadxPluginMetadata metadata = new JadxPluginMetadata();
|
||||
metadata.setResolverId(id());
|
||||
metadata.setVersion(releaseVersion);
|
||||
metadata.setLocationId(buildLocationIdWithoutVersion(info)); // exclude version for later updates
|
||||
metadata.setJar(asset.getDownloadUrl());
|
||||
return Optional.of(metadata);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static LocationInfo parseLocation(String locationId) {
|
||||
@ -87,7 +110,7 @@ public class GithubReleaseResolver implements IJadxPluginResolver {
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return "github-release";
|
||||
return "github";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,8 +30,8 @@ public class GithubTools {
|
||||
// get latest version
|
||||
return get(projectUrl + "/releases/latest", RELEASE_TYPE);
|
||||
}
|
||||
// search version among all releases (by name)
|
||||
List<Release> releases = get(projectUrl + "/releases", RELEASE_LIST_TYPE);
|
||||
// search version in other releases (by name)
|
||||
List<Release> releases = fetchReleases(info, 1, 50);
|
||||
return releases.stream()
|
||||
.filter(r -> r.getName().equals(version))
|
||||
.findFirst()
|
||||
@ -39,6 +39,12 @@ public class GithubTools {
|
||||
+ " Available versions: " + releases.stream().map(Release::getName).collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
public static List<Release> fetchReleases(LocationInfo info, int page, int perPage) {
|
||||
String projectUrl = GITHUB_API_URL + "repos/" + info.getOwner() + "/" + info.getProject();
|
||||
String requestUrl = projectUrl + "/releases?page=" + page + "&per_page=" + perPage;
|
||||
return get(requestUrl, RELEASE_LIST_TYPE);
|
||||
}
|
||||
|
||||
private static <T> T get(String url, Type type) {
|
||||
HttpURLConnection con;
|
||||
try {
|
||||
|
Loading…
Reference in New Issue
Block a user