First try at making a GitHub-based update checker

This commit is contained in:
Florian CUNY 2020-06-14 11:31:13 +02:00
parent 2cefdcca3f
commit 0b4b5298ab
8 changed files with 226 additions and 7 deletions

View File

@ -74,7 +74,7 @@
<bstats.version>1.7</bstats.version> <bstats.version>1.7</bstats.version>
<vault.version>1.7</vault.version> <vault.version>1.7</vault.version>
<placeholderapi.version>2.10.5</placeholderapi.version> <placeholderapi.version>2.10.5</placeholderapi.version>
<githubapi.version>d5f5e0bbd8</githubapi.version> <githubapi.version>e7597818f5</githubapi.version>
<dynmap.version>3.0-SNAPSHOT</dynmap.version> <dynmap.version>3.0-SNAPSHOT</dynmap.version>
<worldedit.version>7.0.0</worldedit.version> <worldedit.version>7.0.0</worldedit.version>
<!-- Revision variable removes warning about dynamic version --> <!-- Revision variable removes warning about dynamic version -->

View File

@ -273,12 +273,17 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "web.github.connection-interval", since = "1.5.0") @ConfigEntry(path = "web.github.connection-interval", since = "1.5.0")
private int githubConnectionInterval = 120; private int githubConnectionInterval = 120;
@ConfigEntry(path = "web.updater.check-updates.bentobox", since = "1.3.0", hidden = true) @ConfigComment("Checks for BentoBox updates.")
@ConfigEntry(path = "web.updater.check-updates.bentobox", since = "1.14.0")
private boolean checkBentoBoxUpdates = true; private boolean checkBentoBoxUpdates = true;
@ConfigEntry(path = "web.updater.check-updates.addons", since = "1.3.0", hidden = true) @ConfigComment("Checks for addons updates.")
@ConfigEntry(path = "web.updater.check-updates.addons", since = "1.14.0")
private boolean checkAddonsUpdates = true; private boolean checkAddonsUpdates = true;
@ConfigEntry(path = "web.updater.check-updates.include-prereleases", since = "1.14.0")
private boolean checkPreReleasesUpdates = false;
// --------------------------------------------- // ---------------------------------------------
// Getters and setters // Getters and setters
@ -713,4 +718,12 @@ public class Settings implements ConfigObject {
public void setPanelFillerMaterial(Material panelFillerMaterial) { public void setPanelFillerMaterial(Material panelFillerMaterial) {
this.panelFillerMaterial = panelFillerMaterial; this.panelFillerMaterial = panelFillerMaterial;
} }
public boolean isCheckPreReleasesUpdates() {
return checkPreReleasesUpdates;
}
public void setCheckPreReleasesUpdates(boolean checkPreReleasesUpdates) {
this.checkPreReleasesUpdates = checkPreReleasesUpdates;
}
} }

View File

@ -6,6 +6,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import javafx.scene.text.Text;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.addons.GameModeAddon;
@ -14,6 +15,7 @@ import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.versions.ServerCompatibility; import world.bentobox.bentobox.versions.ServerCompatibility;
import world.bentobox.bentobox.versions.ServerCompatibility.ServerSoftware; import world.bentobox.bentobox.versions.ServerCompatibility.ServerSoftware;
import world.bentobox.bentobox.versions.UpdateChecker;
/** /**
* Displays information about Gamemodes, Addons and versioning. * Displays information about Gamemodes, Addons and versioning.
@ -85,6 +87,22 @@ public class BentoBoxVersionCommand extends CompositeCommand {
.forEach(a -> user.sendMessage("commands.bentobox.version.addon-syntax", TextVariables.NAME, a.getDescription().getName(), .forEach(a -> user.sendMessage("commands.bentobox.version.addon-syntax", TextVariables.NAME, a.getDescription().getName(),
TextVariables.VERSION, a.getDescription().getVersion(), "[state]", a.getState().toString())); TextVariables.VERSION, a.getDescription().getVersion(), "[state]", a.getState().toString()));
long availableUpdates = getPlugin().getWebManager().getUpdateCheckers().stream()
.filter(updateChecker -> updateChecker.getResult() != null)
.count();
if (availableUpdates > 0) {
user.sendMessage("commands.bentobox.version.available-updates", TextVariables.NUMBER, String.valueOf(availableUpdates));
for (UpdateChecker updateChecker : getPlugin().getWebManager().getUpdateCheckers()) {
UpdateChecker.Result result = updateChecker.getResult();
if (result != null) {
user.sendMessage("commands.bentobox.version.update", "[repo]", updateChecker.getRepository(),
TextVariables.VERSION, result.getVersion(),
"[link]", "https://github.com/" + updateChecker.getRepository() + "/releases/tag/" + result.getVersion());
}
}
}
return true; return true;
} }
} }

View File

@ -89,8 +89,22 @@ public class JoinLeaveListener implements Listener {
// Clear inventory if required // Clear inventory if required
clearPlayersInventory(Util.getWorld(event.getPlayer().getWorld()), user); clearPlayersInventory(Util.getWorld(event.getPlayer().getWorld()), user);
// Notify player about updates
notifyUpdates(user);
} }
private void notifyUpdates(User user) {
if (user.hasPermission("bentobox.notify-updates")) {
long availableUpdates = plugin.getWebManager().getUpdateCheckers().stream()
.filter(updateChecker -> updateChecker.getResult() != null)
.count();
if (availableUpdates > 0) {
user.sendMessage("updates-available");
}
}
}
private void firstTime(User user) { private void firstTime(User user) {
// Make sure the player is loaded into the cache or create the player if they don't exist // Make sure the player is loaded into the cache or create the player if they don't exist

View File

@ -10,6 +10,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import io.github.TheBusyBiscuit.GitHubWebAPI4Java.objects.users.GitHubOrganization;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -23,6 +24,7 @@ import io.github.TheBusyBiscuit.GitHubWebAPI4Java.objects.repositories.GitHubCon
import io.github.TheBusyBiscuit.GitHubWebAPI4Java.objects.repositories.GitHubRepository; import io.github.TheBusyBiscuit.GitHubWebAPI4Java.objects.repositories.GitHubRepository;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.Settings;
import world.bentobox.bentobox.versions.UpdateChecker;
import world.bentobox.bentobox.web.catalog.CatalogEntry; import world.bentobox.bentobox.web.catalog.CatalogEntry;
import world.bentobox.bentobox.web.credits.Contributor; import world.bentobox.bentobox.web.credits.Contributor;
@ -34,17 +36,21 @@ import world.bentobox.bentobox.web.credits.Contributor;
*/ */
public class WebManager { public class WebManager {
private @NonNull BentoBox plugin; private @NonNull final BentoBox plugin;
private @Nullable GitHubWebAPI gitHub; private @Nullable GitHubWebAPI gitHub;
private @NonNull List<CatalogEntry> addonsCatalog; private @NonNull final List<CatalogEntry> addonsCatalog;
private @NonNull List<CatalogEntry> gamemodesCatalog; private @NonNull final List<CatalogEntry> gamemodesCatalog;
private @NonNull Map<String, List<Contributor>> contributors; private @NonNull final Map<String, List<Contributor>> contributors;
@NonNull
private final List<UpdateChecker> updateCheckers;
public WebManager(@NonNull BentoBox plugin) { public WebManager(@NonNull BentoBox plugin) {
this.plugin = plugin; this.plugin = plugin;
this.addonsCatalog = new ArrayList<>(); this.addonsCatalog = new ArrayList<>();
this.gamemodesCatalog = new ArrayList<>(); this.gamemodesCatalog = new ArrayList<>();
this.contributors = new HashMap<>(); this.contributors = new HashMap<>();
this.updateCheckers = new ArrayList<>();
// Setup the GitHub connection // Setup the GitHub connection
if (plugin.getSettings().isGithubDownloadData()) { if (plugin.getSettings().isGithubDownloadData()) {
@ -100,12 +106,14 @@ public class WebManager {
.stream().map(addon -> addon.getDescription().getRepository()) .stream().map(addon -> addon.getDescription().getRepository())
.filter(repo -> !repo.isEmpty()) .filter(repo -> !repo.isEmpty())
.collect(Collectors.toList())); .collect(Collectors.toList()));
/*
repositories.addAll(addonsCatalog.stream().map(CatalogEntry::getRepository) repositories.addAll(addonsCatalog.stream().map(CatalogEntry::getRepository)
.filter(repo -> !repositories.contains(repo)) .filter(repo -> !repositories.contains(repo))
.collect(Collectors.toList())); .collect(Collectors.toList()));
repositories.addAll(gamemodesCatalog.stream().map(CatalogEntry::getRepository) repositories.addAll(gamemodesCatalog.stream().map(CatalogEntry::getRepository)
.filter(repo -> !repositories.contains(repo)) .filter(repo -> !repositories.contains(repo))
.collect(Collectors.toList())); .collect(Collectors.toList()));
*/
/* Download the contributors */ /* Download the contributors */
if (plugin.getSettings().isLogGithubDownloadData()) { if (plugin.getSettings().isLogGithubDownloadData()) {
@ -128,6 +136,13 @@ public class WebManager {
} }
} }
/* Check for updates */
if (plugin.getSettings().isLogGithubDownloadData()) {
plugin.log("Checking for updates...");
}
checkUpdates(gh);
// People were concerned that the download took ages, so we need to tell them it's over now. // People were concerned that the download took ages, so we need to tell them it's over now.
if (plugin.getSettings().isLogGithubDownloadData()) { if (plugin.getSettings().isLogGithubDownloadData()) {
plugin.log("Successfully downloaded data from GitHub."); plugin.log("Successfully downloaded data from GitHub.");
@ -223,6 +238,36 @@ public class WebManager {
} }
} }
private void checkUpdates(@NonNull GitHubWebAPI gh) {
if (updateCheckers.isEmpty()) {
// Grab the repositories we will have to go through
Map<String, String> repositories = new HashMap<>();
if (plugin.getSettings().isCheckBentoBoxUpdates()) {
repositories.put("BentoBoxWorld/BentoBox", plugin.getDescription().getVersion());
}
if (plugin.getSettings().isCheckAddonsUpdates()) {
repositories.putAll(plugin.getAddonsManager().getEnabledAddons()
.stream()
.filter(addon -> !addon.getDescription().getRepository().isEmpty())
.collect(Collectors.toMap(addon -> addon.getDescription().getRepository(), addon -> addon.getDescription().getVersion())));
}
for (Map.Entry<String, String> repo : repositories.entrySet()) {
UpdateChecker updateChecker = new UpdateChecker(gh, repo.getKey(), repo.getValue());
updateCheckers.add(updateChecker);
}
}
for (UpdateChecker updateChecker : updateCheckers) {
try {
updateChecker.checkUpdates();
} catch (IllegalAccessException e) {
// Fail silently
}
}
}
/** /**
* Returns the contents of the addons catalog (may be an empty list). * Returns the contents of the addons catalog (may be an empty list).
* @return the contents of the addons catalog. * @return the contents of the addons catalog.
@ -263,4 +308,13 @@ public class WebManager {
public Optional<GitHubWebAPI> getGitHub() { public Optional<GitHubWebAPI> getGitHub() {
return Optional.ofNullable(gitHub); return Optional.ofNullable(gitHub);
} }
/**
* Returns the list of update checkers.
* @return the list of update checkers.
* @since 1.14.0
*/
public List<UpdateChecker> getUpdateCheckers() {
return updateCheckers;
}
} }

View File

@ -0,0 +1,111 @@
package world.bentobox.bentobox.versions;
import io.github.TheBusyBiscuit.GitHubWebAPI4Java.GitHubWebAPI;
import io.github.TheBusyBiscuit.GitHubWebAPI4Java.objects.repositories.GitHubRelease;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.util.Util;
import java.util.List;
/**
* Checks for updates through the GitHub API.
* @author Poslovitch
* @since 1.14.0
*/
public class UpdateChecker {
private final BentoBox plugin;
private final GitHubWebAPI gitHub;
private final String repository;
private final String currentVersion;
@Nullable
private Result result;
public UpdateChecker(GitHubWebAPI gitHub, String repository, String currentVersion) {
this.plugin = BentoBox.getInstance();
this.gitHub = gitHub;
this.repository = repository;
this.currentVersion = currentVersion;
}
public void checkUpdates() throws IllegalAccessException {
String[] repo = repository.split("/");
List<GitHubRelease> releases = gitHub.getRepository(repo[0], repo[1]).getReleases();
if (!releases.isEmpty()) {
for (GitHubRelease release : releases) {
if (release.isDraft()) {
// Drafts should be ignored (they're not published yet)
continue;
}
if (release.isPreRelease() && !plugin.getSettings().isCheckPreReleasesUpdates()) {
// We don't care about pre-releases
continue;
}
String newVersion = release.getTagName();
if (isMoreRecent(newVersion)) {
// We found the new version, and it should be the latest.
this.result = new Result(newVersion, release.isPreRelease());
break;
}
}
} else {
this.result = null;
}
}
private boolean isMoreRecent(String newVersion) {
String[] currentVer = currentVersion.split("\\D");
String[] newVer = newVersion.split("\\D");
for (int i = 0; i < currentVer.length; i++) {
int newVersionNumber = 0;
if (i < newVer.length && Util.isInteger(newVer[i], false)) {
newVersionNumber = Integer.parseInt(newVer[i]);
}
int currentVersionNumber = Util.isInteger(currentVer[i], false) ? Integer.parseInt(currentVer[i]) : -1;
if (newVersionNumber > currentVersionNumber) {
return false; // The current version is greater than the "new" version -> up to date.
}
if (newVersionNumber < currentVersionNumber) {
return true; // The current version is outdated
}
// If it is equal, go to the next number
}
return false; // Everything is equal, so return true
}
@Nullable
public Result getResult() {
return result;
}
public String getRepository() {
return repository;
}
public static class Result {
private final String version;
private final boolean preRelease;
public Result(String version, boolean preRelease) {
this.version = version;
this.preRelease = preRelease;
}
public String getVersion() {
return version;
}
public boolean isPreRelease() {
return preRelease;
}
}
}

View File

@ -413,6 +413,8 @@ commands:
game-world: "&2 [name] &7 (&3 [addon]&7 ): &3 [worlds]" game-world: "&2 [name] &7 (&3 [addon]&7 ): &3 [worlds]"
server: "&2 Running &3 [name] [version]&2 ." server: "&2 Running &3 [name] [version]&2 ."
database: "&2 Database: &3 [database]" database: "&2 Database: &3 [database]"
available-updates: "Available updates ([number]):"
update: "&2 [repo] &3 [version]&7 : &e [link]"
manage: manage:
description: "displays the Management Panel" description: "displays the Management Panel"
catalog: catalog:
@ -1405,6 +1407,10 @@ panel:
&a Allow BentoBox to connect to GitHub in &a Allow BentoBox to connect to GitHub in
&a the configuration or try again later. &a the configuration or try again later.
updates-available: |
[prefix_bentobox]&6 &l Update Checker
[prefix_bentobox]&a Updates are available. Check &b /bentobox version &a for details.
successfully-loaded: | successfully-loaded: |
&6 ____ _ ____ &6 ____ _ ____

View File

@ -39,3 +39,6 @@ permissions:
bentobox.version: bentobox.version:
description: Allows to use /bentobox version description: Allows to use /bentobox version
default: op default: op
bentobox.notify-updates:
description: Sends players a message about available updates when they join the server
default: op