From 6baf9529046d77763dc5b437ef8ac3548cb6dac4 Mon Sep 17 00:00:00 2001 From: thatsIch Date: Mon, 9 Mar 2015 17:35:19 +0100 Subject: [PATCH] Fixes #976 Now uses GitHub to retrieve most current version Reworked whole Version Checker with an extensible interface to add any other service later on easier. The version checker now has its own config file, to collect the different options and extract them from the main config file. In that you can specify how fine the versions should be checked. --- build.gradle | 1 + src/main/java/appeng/core/AEConfig.java | 34 +- src/main/java/appeng/core/AppEng.java | 36 +- src/main/java/appeng/core/FacadeConfig.java | 4 +- src/main/java/appeng/core/Registration.java | 10 +- .../java/appeng/core/features/AEFeature.java | 2 +- .../sync/packets/PacketCompassRequest.java | 2 +- .../java/appeng/hooks/MeteoriteWorldGen.java | 2 +- .../appeng/recipes/loader/ConfigLoader.java | 17 +- .../java/appeng/services/CompassService.java | 4 +- .../java/appeng/services/VersionChecker.java | 240 ++-- .../CompassException.java | 2 +- .../{helpers => compass}/CompassReader.java | 2 +- .../{helpers => compass}/CompassRegion.java | 2 +- .../ICompassCallback.java | 2 +- .../appeng/services/version/BaseVersion.java | 91 ++ .../java/appeng/services/version/Channel.java | 13 + .../services/version/DefaultVersion.java | 35 + .../services/version/DoNotCheckVersion.java | 25 + .../services/version/MissingVersion.java | 30 + .../services/version/ModVersionFetcher.java | 39 + .../java/appeng/services/version/Version.java | 42 + .../version/VersionCheckerConfig.java | 94 ++ .../services/version/VersionFetcher.java | 10 + .../services/version/VersionParser.java | 132 +++ .../github/DefaultFormattedRelease.java | 32 + .../version/github/FormattedRelease.java | 21 + .../github/MissingFormattedRelease.java | 37 + .../services/version/github/Release.java | 19 + .../version/github/ReleaseFetcher.java | 90 ++ .../services/version/VersionParserTest.java | 80 ++ .../java/appeng/util/UUIDMatcherTest.java | 2 +- .../appeng/services/version/releases.json | 1040 +++++++++++++++++ 33 files changed, 2023 insertions(+), 169 deletions(-) rename src/main/java/appeng/services/{helpers => compass}/CompassException.java (96%) rename src/main/java/appeng/services/{helpers => compass}/CompassReader.java (98%) rename src/main/java/appeng/services/{helpers => compass}/CompassRegion.java (99%) rename src/main/java/appeng/services/{helpers => compass}/ICompassCallback.java (97%) create mode 100644 src/main/java/appeng/services/version/BaseVersion.java create mode 100644 src/main/java/appeng/services/version/Channel.java create mode 100644 src/main/java/appeng/services/version/DefaultVersion.java create mode 100644 src/main/java/appeng/services/version/DoNotCheckVersion.java create mode 100644 src/main/java/appeng/services/version/MissingVersion.java create mode 100644 src/main/java/appeng/services/version/ModVersionFetcher.java create mode 100644 src/main/java/appeng/services/version/Version.java create mode 100644 src/main/java/appeng/services/version/VersionCheckerConfig.java create mode 100644 src/main/java/appeng/services/version/VersionFetcher.java create mode 100644 src/main/java/appeng/services/version/VersionParser.java create mode 100644 src/main/java/appeng/services/version/github/DefaultFormattedRelease.java create mode 100644 src/main/java/appeng/services/version/github/FormattedRelease.java create mode 100644 src/main/java/appeng/services/version/github/MissingFormattedRelease.java create mode 100644 src/main/java/appeng/services/version/github/Release.java create mode 100644 src/main/java/appeng/services/version/github/ReleaseFetcher.java create mode 100644 src/test/java/appeng/services/version/VersionParserTest.java create mode 100644 src/test/resources/appeng/services/version/releases.json diff --git a/build.gradle b/build.gradle index 8360abd1..3a645164 100644 --- a/build.gradle +++ b/build.gradle @@ -79,6 +79,7 @@ jar { minecraft { version = config.minecraft_version + "-" + config.forge_version + replaceIn "AEConfig.java" replace "@version@", project.version replace "@aechannel@", config.aechannel diff --git a/src/main/java/appeng/core/AEConfig.java b/src/main/java/appeng/core/AEConfig.java index 8c7d6633..f25d3ba8 100644 --- a/src/main/java/appeng/core/AEConfig.java +++ b/src/main/java/appeng/core/AEConfig.java @@ -55,9 +55,6 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon public static final double TUNNEL_POWER_LOSS = 0.05; - public String latestVersion = VERSION; - public long latestTimeStamp = 0; - public static final String VERSION = "@version@"; public static final String CHANNEL = "@aechannel@"; @@ -149,7 +146,7 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon public boolean disableColoredCableRecipesInNEI = true; public boolean updatable = false; - final private File myPath; + final private File configFile; public double meteoriteClusterChance = 0.1; public double meteoriteSpawnChance = 0.3; @@ -224,22 +221,20 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon public String getFilePath() { - return this.myPath.toString(); + return this.configFile.toString(); } - public AEConfig(String path) { - super( new File( path + "AppliedEnergistics2.cfg" ) ); - this.myPath = new File( path + "AppliedEnergistics2.cfg" ); + public AEConfig( File configFile ) { + super( configFile ); + this.configFile = configFile; FMLCommonHandler.instance().bus().register( this ); - final double DEFAULT_BC_EXCHANGE = 5.0; final double DEFAULT_IC2_EXCHANGE = 2.0; final double DEFAULT_RTC_EXCHANGE = 1.0 / 11256.0; final double DEFAULT_RF_EXCHANGE = 0.5; final double DEFAULT_MEKANISM_EXCHANGE = 0.2; - PowerUnits.MJ.conversionRatio = this.get( "PowerRatios", "BuildCraft", DEFAULT_BC_EXCHANGE ).getDouble( DEFAULT_BC_EXCHANGE ); PowerUnits.MK.conversionRatio = this.get( "PowerRatios", "Mekanism", DEFAULT_MEKANISM_EXCHANGE ).getDouble( DEFAULT_MEKANISM_EXCHANGE ); PowerUnits.EU.conversionRatio = this.get( "PowerRatios", "IC2", DEFAULT_IC2_EXCHANGE ).getDouble( DEFAULT_IC2_EXCHANGE ); PowerUnits.WA.conversionRatio = this.get( "PowerRatios", "RotaryCraft", DEFAULT_RTC_EXCHANGE ).getDouble( DEFAULT_RTC_EXCHANGE ); @@ -340,19 +335,6 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon this.craftingCalculationTimePerTick ); } - if ( this.isFeatureEnabled( AEFeature.VersionChecker ) ) - { - try - { - this.latestVersion = this.get( "VersionChecker", "LatestVersion", "" ).getString(); - this.latestTimeStamp = Long.parseLong( this.get( "VersionChecker", "LatestTimeStamp", "" ).getString() ); - } - catch (NumberFormatException err) - { - this.latestTimeStamp = 0; - } - } - this.updatable = true; } @@ -411,12 +393,6 @@ public class AEConfig extends Configuration implements IConfigurableObject, ICon @Override public void save() { - if ( this.isFeatureEnabled( AEFeature.VersionChecker ) ) - { - this.get( "VersionChecker", "LatestVersion", this.latestVersion ).set( this.latestVersion ); - this.get( "VersionChecker", "LatestTimeStamp", "" ).set( Long.toString( this.latestTimeStamp ) ); - } - if ( this.isFeatureEnabled( AEFeature.SpatialIO ) ) { this.get( "spatialio", "storageBiomeID", this.storageBiomeID ).set( this.storageBiomeID ); diff --git a/src/main/java/appeng/core/AppEng.java b/src/main/java/appeng/core/AppEng.java index 9992320e..97699dc8 100644 --- a/src/main/java/appeng/core/AppEng.java +++ b/src/main/java/appeng/core/AppEng.java @@ -22,8 +22,6 @@ package appeng.core; import java.io.File; import java.util.concurrent.TimeUnit; -import com.google.common.base.Stopwatch; - import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.Mod; @@ -37,6 +35,8 @@ import cpw.mods.fml.common.event.FMLServerStartingEvent; import cpw.mods.fml.common.event.FMLServerStoppingEvent; import cpw.mods.fml.common.network.NetworkRegistry; +import com.google.common.base.Stopwatch; + import appeng.core.crash.CrashEnhancement; import appeng.core.crash.CrashInfo; import appeng.core.features.AEFeature; @@ -47,6 +47,7 @@ import appeng.integration.IntegrationRegistry; import appeng.integration.IntegrationType; import appeng.server.AECommand; import appeng.services.VersionChecker; +import appeng.services.version.VersionCheckerConfig; import appeng.util.Platform; @@ -70,7 +71,7 @@ public class AppEng private final IMCHandler imcHandler; - private String configPath; + private File configDirectory; public AppEng() { @@ -81,9 +82,9 @@ public class AppEng FMLCommonHandler.instance().registerCrashCallable( new CrashEnhancement( CrashInfo.MOD_VERSION ) ); } - public String getConfigPath() + public final File getConfigDirectory() { - return this.configPath; + return this.configDirectory; } public boolean isIntegrationEnabled( IntegrationType integrationName ) @@ -104,11 +105,16 @@ public class AppEng CommonHelper.proxy.missingCoreMod(); } - Stopwatch star = Stopwatch.createStarted(); - this.configPath = event.getModConfigurationDirectory().getPath() + File.separator + "AppliedEnergistics2" + File.separator; + Stopwatch watch = Stopwatch.createStarted(); + this.configDirectory = new File(event.getModConfigurationDirectory().getPath(), "AppliedEnergistics2"); - AEConfig.instance = new AEConfig( this.configPath ); - FacadeConfig.instance = new FacadeConfig( this.configPath ); + final File configFile = new File( this.configDirectory, "AppliedEnergistics2.cfg"); + final File facadeFile = new File( this.configDirectory, "Facades.cfg" ); + final File versionFile = new File( this.configDirectory, "VersionChecker.cfg" ); + + AEConfig.instance = new AEConfig( configFile ); + FacadeConfig.instance = new FacadeConfig( facadeFile ); + final VersionCheckerConfig versionCheckerConfig = new VersionCheckerConfig( versionFile ); AELog.info( "Pre Initialization ( started )" ); @@ -121,19 +127,23 @@ public class AppEng Registration.INSTANCE.preInitialize( event ); - if ( AEConfig.instance.isFeatureEnabled( AEFeature.VersionChecker ) ) + if ( versionCheckerConfig.isEnabled() ) { - AELog.info( "Starting VersionChecker" ); - this.startService( "AE2 VersionChecker", new Thread( new VersionChecker() ) ); + final VersionChecker versionChecker = new VersionChecker( versionCheckerConfig ); + final Thread versionCheckerThread = new Thread( versionChecker ); + + this.startService( "AE2 VersionChecker", versionCheckerThread ); } - AELog.info( "Pre Initialization ( ended after " + star.elapsed( TimeUnit.MILLISECONDS ) + "ms )" ); + AELog.info( "Pre Initialization ( ended after " + watch.elapsed( TimeUnit.MILLISECONDS ) + "ms )" ); } private void startService( String serviceName, Thread thread ) { thread.setName( serviceName ); thread.setPriority( Thread.MIN_PRIORITY ); + + AELog.info( "Starting " + serviceName ); thread.start(); } diff --git a/src/main/java/appeng/core/FacadeConfig.java b/src/main/java/appeng/core/FacadeConfig.java index 62f35477..a2025f8e 100644 --- a/src/main/java/appeng/core/FacadeConfig.java +++ b/src/main/java/appeng/core/FacadeConfig.java @@ -35,8 +35,8 @@ public class FacadeConfig extends Configuration public static FacadeConfig instance; final Pattern replacementPattern; - public FacadeConfig(String path) { - super( new File( path + "Facades.cfg" ) ); + public FacadeConfig( File facadeFile ) { + super( facadeFile ); this.replacementPattern = Pattern.compile( "[^a-zA-Z0-9]" ); } diff --git a/src/main/java/appeng/core/Registration.java b/src/main/java/appeng/core/Registration.java index c131f7ac..109251f6 100644 --- a/src/main/java/appeng/core/Registration.java +++ b/src/main/java/appeng/core/Registration.java @@ -21,10 +21,6 @@ package appeng.core; import java.lang.reflect.Field; -import com.google.common.base.Optional; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - import net.minecraft.item.crafting.CraftingManager; import net.minecraft.util.WeightedRandomChestContent; import net.minecraft.world.biome.BiomeGenBase; @@ -41,6 +37,10 @@ import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.common.registry.VillagerRegistry; +import com.google.common.base.Optional; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + import appeng.api.AEApi; import appeng.api.IAppEngApi; import appeng.api.config.Upgrades; @@ -546,7 +546,7 @@ public final class Registration ItemMultiMaterial.instance.makeUnique(); if ( AEConfig.instance.isFeatureEnabled( AEFeature.CustomRecipes ) ) - this.recipeHandler.parseRecipes( new ConfigLoader( AppEng.instance.getConfigPath() ), "index.recipe" ); + this.recipeHandler.parseRecipes( new ConfigLoader( AppEng.instance.getConfigDirectory() ), "index.recipe" ); else this.recipeHandler.parseRecipes( new JarLoader( "/assets/appliedenergistics2/recipes/" ), "index.recipe" ); diff --git a/src/main/java/appeng/core/features/AEFeature.java b/src/main/java/appeng/core/features/AEFeature.java index 2600f9cb..2990e85b 100644 --- a/src/main/java/appeng/core/features/AEFeature.java +++ b/src/main/java/appeng/core/features/AEFeature.java @@ -62,7 +62,7 @@ public enum AEFeature MassCannonBlockDamage("BlockFeatures"), TinyTNTBlockDamage("BlockFeatures"), Facades("Facades"), - VersionChecker("Services"), UnsupportedDeveloperTools("Misc", false), Creative("Misc"), + UnsupportedDeveloperTools("Misc", false), Creative("Misc"), GrinderLogging("Misc", false), Logging("Misc"), IntegrationLogging("Misc", false), CustomRecipes("Crafting", false), WebsiteRecipes("Misc", false), diff --git a/src/main/java/appeng/core/sync/packets/PacketCompassRequest.java b/src/main/java/appeng/core/sync/packets/PacketCompassRequest.java index dd790ae7..be765a07 100644 --- a/src/main/java/appeng/core/sync/packets/PacketCompassRequest.java +++ b/src/main/java/appeng/core/sync/packets/PacketCompassRequest.java @@ -29,7 +29,7 @@ import appeng.core.WorldSettings; import appeng.core.sync.AppEngPacket; import appeng.core.sync.network.INetworkInfo; import appeng.core.sync.network.NetworkHandler; -import appeng.services.helpers.ICompassCallback; +import appeng.services.compass.ICompassCallback; public class PacketCompassRequest extends AppEngPacket implements ICompassCallback { diff --git a/src/main/java/appeng/hooks/MeteoriteWorldGen.java b/src/main/java/appeng/hooks/MeteoriteWorldGen.java index f0ccedd9..e8c81fb8 100644 --- a/src/main/java/appeng/hooks/MeteoriteWorldGen.java +++ b/src/main/java/appeng/hooks/MeteoriteWorldGen.java @@ -33,7 +33,7 @@ import appeng.core.AEConfig; import appeng.core.WorldSettings; import appeng.core.features.registries.WorldGenRegistry; import appeng.helpers.MeteoritePlacer; -import appeng.services.helpers.ICompassCallback; +import appeng.services.compass.ICompassCallback; import appeng.util.Platform; final public class MeteoriteWorldGen implements IWorldGenerator diff --git a/src/main/java/appeng/recipes/loader/ConfigLoader.java b/src/main/java/appeng/recipes/loader/ConfigLoader.java index 19ee7a6d..0fb2aa36 100644 --- a/src/main/java/appeng/recipes/loader/ConfigLoader.java +++ b/src/main/java/appeng/recipes/loader/ConfigLoader.java @@ -18,6 +18,7 @@ package appeng.recipes.loader; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -25,19 +26,21 @@ import java.io.InputStreamReader; import appeng.api.recipes.IRecipeLoader; -public class ConfigLoader implements IRecipeLoader + +public final class ConfigLoader implements IRecipeLoader { + private final File rootDirectory; - private final String rootPath; - - public ConfigLoader(String s) { - this.rootPath = s; + public ConfigLoader( File rootDirectory ) + { + this.rootDirectory = rootDirectory; } @Override - public BufferedReader getFile(String s) throws Exception + public BufferedReader getFile( String s ) throws Exception { - File f = new File( this.rootPath + s ); + final File f = new File( this.rootDirectory, s ); + return new BufferedReader( new InputStreamReader( new FileInputStream( f ), "UTF-8" ) ); } } diff --git a/src/main/java/appeng/services/CompassService.java b/src/main/java/appeng/services/CompassService.java index 41a7a69a..24760d07 100644 --- a/src/main/java/appeng/services/CompassService.java +++ b/src/main/java/appeng/services/CompassService.java @@ -32,8 +32,8 @@ import net.minecraft.world.chunk.Chunk; import appeng.api.AEApi; import appeng.api.util.DimensionalCoord; -import appeng.services.helpers.CompassReader; -import appeng.services.helpers.ICompassCallback; +import appeng.services.compass.CompassReader; +import appeng.services.compass.ICompassCallback; public class CompassService implements ThreadFactory { diff --git a/src/main/java/appeng/services/VersionChecker.java b/src/main/java/appeng/services/VersionChecker.java index 3fbfc372..7aa9fc73 100644 --- a/src/main/java/appeng/services/VersionChecker.java +++ b/src/main/java/appeng/services/VersionChecker.java @@ -19,133 +19,167 @@ package appeng.services; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLConnection; import java.util.Date; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - import net.minecraft.nbt.NBTTagCompound; +import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.event.FMLInterModComms; import appeng.core.AEConfig; import appeng.core.AELog; import appeng.core.AppEng; +import appeng.services.version.github.FormattedRelease; +import appeng.services.version.github.ReleaseFetcher; +import appeng.services.version.ModVersionFetcher; +import appeng.services.version.Version; +import appeng.services.version.VersionCheckerConfig; +import appeng.services.version.VersionFetcher; +import appeng.services.version.VersionParser; -public class VersionChecker implements Runnable + +/** + * Tries to connect to GitHub to retrieve the most current build. + * After comparison with the local version, several path can be chosen. + * + * If the local version is invalid, somebody might have build that version themselves + * or it is run in a developer environment, then nothing needs to be done. + * + * If GitHub can not be reached, then either is GitHub down + * or the connection to GitHub disturbed, then nothing needs to be done, + * since no comparison can be reached + * + * If the version was just recently checked, then no need to poll again. + * Nobody wants to bother to update several times a day. + * + * Config enables to fine-tune when a version is considered newer + * + * If the local version is newer or equal to the GitHub version, + * then no update needs to be posted + * + * Only after all that cases, if the external version is higher than the local, + * use Version Checker Mod and post several information needed for it to update the mod. + */ +public final class VersionChecker implements Runnable { - private static final int FOUR_HOURS = 1000 * 3600 * 4; + private static final int SEC_TO_HOUR = 3600; + private static final int MS_TO_SEC = 1000; + private final VersionCheckerConfig config; - private final long delay; - - public VersionChecker() + public VersionChecker( VersionCheckerConfig config ) { - final long now = new Date().getTime(); - final long timeDiff = now - AEConfig.instance.latestTimeStamp; - - this.delay = Math.max( 1, FOUR_HOURS - timeDiff); + this.config = config; } @Override public void run() { - try + Thread.yield(); + + final String rawLastCheck = this.config.lastCheck(); + final long lastCheck = Long.parseLong( rawLastCheck ); + final Date now = new Date(); + final long nowInMs = now.getTime(); + final long intervalInMs = this.config.interval() * SEC_TO_HOUR * MS_TO_SEC; + final long lastAfterInterval = lastCheck + intervalInMs; + + this.processInterval( nowInMs, lastAfterInterval ); + + AELog.info( "Stopping AE2 VersionChecker" ); + } + + /** + * checks if enough time since last check has expired + * + * @param nowInMs now in milli seconds + * @param lastAfterInterval last version check including the interval defined in the config + */ + private void processInterval( long nowInMs, long lastAfterInterval ) + { + if ( nowInMs > lastAfterInterval ) { - this.sleep( this.delay ); + final String rawModVersion = AEConfig.VERSION; + final VersionParser parser = new VersionParser(); + final VersionFetcher modFetcher = new ModVersionFetcher( rawModVersion, parser ); + final ReleaseFetcher githubFetcher = new ReleaseFetcher( this.config, parser ); + + final Version modVersion = modFetcher.get(); + final FormattedRelease githubRelease = githubFetcher.get(); + + this.processVersions( modVersion, githubRelease ); } - catch (InterruptedException e) + else { - // :( - } - - while (true) - { - Thread.yield(); - - try - { - String MCVersion = cpw.mods.fml.common.Loader.instance().getMCVersionString().replace( "Minecraft ", "" ); - URL url = new URL( "http://feeds.ae-mod.info/latest.json?VersionMC=" + MCVersion + "&Channel=" + AEConfig.CHANNEL + "&CurrentVersion=" - + AEConfig.VERSION ); - - URLConnection yc = url.openConnection(); - yc.setRequestProperty( "User-Agent", "AE2/" + AEConfig.VERSION + " (Channel:" + AEConfig.CHANNEL + ',' + MCVersion.replace( " ", ":" ) + ')' ); - BufferedReader in = new BufferedReader( new InputStreamReader( yc.getInputStream() ) ); - - StringBuilder Version = new StringBuilder(); - String inputLine; - - while ((inputLine = in.readLine()) != null) - Version.append( inputLine ); - - in.close(); - - if ( Version.length() > 2 ) - { - JsonElement element = (new JsonParser()).parse( Version.toString() ); - - int version = element.getAsJsonObject().get( "FormatVersion" ).getAsInt(); - if ( version == 1 ) - { - JsonObject Meta = element.getAsJsonObject().get( "Meta" ).getAsJsonObject(); - JsonArray Versions = element.getAsJsonObject().get( "Versions" ).getAsJsonArray(); - if ( Versions.size() > 0 ) - { - JsonObject Latest = Versions.get( 0 ).getAsJsonObject(); - - AEConfig.instance.latestVersion = Latest.get( "Version" ).getAsString(); - AEConfig.instance.latestTimeStamp = (new Date()).getTime(); - AEConfig.instance.save(); - - if ( !AEConfig.VERSION.equals( AEConfig.instance.latestVersion ) ) - { - NBTTagCompound versionInf = new NBTTagCompound(); - versionInf.setString( "modDisplayName", "Applied Energistics 2" ); - versionInf.setString( "oldVersion", AEConfig.VERSION ); - versionInf.setString( "newVersion", AEConfig.instance.latestVersion ); - versionInf.setString( "updateUrl", Latest.get( "UserBuild" ).getAsString() ); - versionInf.setBoolean( "isDirectLink", true ); - - JsonElement changeLog = Latest.get( "ChangeLog" ); - if ( changeLog == null ) - versionInf.setString( "changeLog", "For full change log please see: " + Meta.get( "DownloadLink" ).getAsString() ); - else - versionInf.setString( "changeLog", changeLog.getAsString() ); - - versionInf.setString( "newFileName", "appliedenergistics2-" + AEConfig.instance.latestVersion + ".jar" ); - FMLInterModComms.sendRuntimeMessage( AppEng.instance, "VersionChecker", "addUpdate", versionInf ); - - AELog.info( "Stopping VersionChecker" ); - return; - } - } - } - } - - this.sleep( FOUR_HOURS ); - } - catch (Exception e) - { - try - { - this.sleep( FOUR_HOURS ); - } - catch (InterruptedException e1) - { - AELog.error( e ); - } - } + AELog.info( "Last check was just recently." ); } } - private void sleep(long i) throws InterruptedException + /** + * Checks if the retrieved version is newer as the current mod version. + * Will notify player if config is enabled. + * + * @param modVersion version of mod + * @param githubRelease release retrieved through github + */ + private void processVersions( Version modVersion, FormattedRelease githubRelease ) { - Thread.sleep( i ); + final Version githubVersion = githubRelease.version(); + final String modFormatted = modVersion.formatted(); + final String ghFormatted = githubVersion.formatted(); + + if ( githubVersion.isNewerAs( modVersion ) ) + { + final String changelog = githubRelease.changelog(); + + if ( this.config.shouldNotifyPlayer() ) + { + AELog.info( "Newer version is available: " + ghFormatted + " (found) > " + modFormatted + " (current)" ); + + if ( this.config.shouldPostChangelog() ) + { + AELog.info( "Changelog: " + changelog ); + } + } + + this.interactWithVersionCheckerMod( modFormatted, ghFormatted, changelog ); + } + else + { + AELog.info( "No newer version is available: " + ghFormatted + "(found) < " + modFormatted + " (current)"); + } + } + + /** + * Checks if the version checker mod is installed and handles it depending on that information + * + * @param modFormatted mod version formatted as rv2-beta-8 + * @param ghFormatted retrieved github version formatted as rv2-beta-8 + * @param changelog retrieved github changelog + */ + private void interactWithVersionCheckerMod( String modFormatted, String ghFormatted, String changelog ) + { + if ( Loader.isModLoaded( "VersionChecker" ) ) + { + final NBTTagCompound versionInf = new NBTTagCompound(); + versionInf.setString( "modDisplayName", AppEng.MOD_NAME ); + versionInf.setString( "oldVersion", modFormatted ); + versionInf.setString( "newVersion", ghFormatted ); + versionInf.setString( "updateUrl", "http://ae-mod.info/builds/appliedenergistics2-" + ghFormatted + ".jar" ); + versionInf.setBoolean( "isDirectLink", true ); + + if ( !changelog.isEmpty() ) + { + versionInf.setString( "changeLog", changelog ); + } + + versionInf.setString( "newFileName", "appliedenergistics2-" + ghFormatted + ".jar" ); + FMLInterModComms.sendRuntimeMessage( AppEng.instance, "VersionChecker", "addUpdate", versionInf ); + + AELog.info( "Reported new version to VersionChecker mod." ); + } + else + { + AELog.info( "VersionChecker mod is not installed; Proceeding." ); + } } } diff --git a/src/main/java/appeng/services/helpers/CompassException.java b/src/main/java/appeng/services/compass/CompassException.java similarity index 96% rename from src/main/java/appeng/services/helpers/CompassException.java rename to src/main/java/appeng/services/compass/CompassException.java index 9ddf4991..55c9453a 100644 --- a/src/main/java/appeng/services/helpers/CompassException.java +++ b/src/main/java/appeng/services/compass/CompassException.java @@ -16,7 +16,7 @@ * along with Applied Energistics 2. If not, see . */ -package appeng.services.helpers; +package appeng.services.compass; public class CompassException extends RuntimeException { diff --git a/src/main/java/appeng/services/helpers/CompassReader.java b/src/main/java/appeng/services/compass/CompassReader.java similarity index 98% rename from src/main/java/appeng/services/helpers/CompassReader.java rename to src/main/java/appeng/services/compass/CompassReader.java index 000454cc..3d3b3cbf 100644 --- a/src/main/java/appeng/services/helpers/CompassReader.java +++ b/src/main/java/appeng/services/compass/CompassReader.java @@ -16,7 +16,7 @@ * along with Applied Energistics 2. If not, see . */ -package appeng.services.helpers; +package appeng.services.compass; import java.io.File; import java.util.HashMap; diff --git a/src/main/java/appeng/services/helpers/CompassRegion.java b/src/main/java/appeng/services/compass/CompassRegion.java similarity index 99% rename from src/main/java/appeng/services/helpers/CompassRegion.java rename to src/main/java/appeng/services/compass/CompassRegion.java index 08e30c9e..691eb417 100644 --- a/src/main/java/appeng/services/helpers/CompassRegion.java +++ b/src/main/java/appeng/services/compass/CompassRegion.java @@ -16,7 +16,7 @@ * along with Applied Energistics 2. If not, see . */ -package appeng.services.helpers; +package appeng.services.compass; import java.io.File; diff --git a/src/main/java/appeng/services/helpers/ICompassCallback.java b/src/main/java/appeng/services/compass/ICompassCallback.java similarity index 97% rename from src/main/java/appeng/services/helpers/ICompassCallback.java rename to src/main/java/appeng/services/compass/ICompassCallback.java index 0e2c3266..e1f6cdeb 100644 --- a/src/main/java/appeng/services/helpers/ICompassCallback.java +++ b/src/main/java/appeng/services/compass/ICompassCallback.java @@ -16,7 +16,7 @@ * along with Applied Energistics 2. If not, see . */ -package appeng.services.helpers; +package appeng.services.compass; public interface ICompassCallback { diff --git a/src/main/java/appeng/services/version/BaseVersion.java b/src/main/java/appeng/services/version/BaseVersion.java new file mode 100644 index 00000000..53625e76 --- /dev/null +++ b/src/main/java/appeng/services/version/BaseVersion.java @@ -0,0 +1,91 @@ +package appeng.services.version; + + +/** + * Base version of {@link Version}. + * + * Provides a unified way to test for equality and print a formatted string + */ +public abstract class BaseVersion implements Version +{ + private final int revision; + private final Channel channel; + private final int build; + + /** + * @param revision revision in natural number + * @param channel channel + * @param build build in natural number + * + * @throws AssertionError if assertion are enabled and revision or build are not natural numbers + */ + public BaseVersion( int revision, Channel channel, int build ) + { + assert revision >= 0; + assert build >= 0; + + this.revision = revision; + this.channel = channel; + this.build = build; + } + + @Override + public final int revision() + { + return this.revision; + } + + @Override + public final Channel channel() + { + return this.channel; + } + + @Override + public final int build() + { + return this.build; + } + + @Override + public String formatted() + { + return "rv" + this.revision + '-' + this.channel.name().toLowerCase() + '-' + this.build; + } + + @Override + public final int hashCode() + { + int result = this.revision; + result = 31 * result + ( this.channel != null ? this.channel.hashCode() : 0 ); + result = 31 * result + this.build; + return result; + } + + @Override + public final boolean equals( Object o ) + { + if ( this == o ) + return true; + if ( !( o instanceof Version ) ) + return false; + + Version that = (Version) o; + + if ( this.revision != that.revision() ) + return false; + if ( this.build != that.build() ) + return false; + return this.channel == that.channel(); + } + + @Override + public final String toString() + { + return "Version{" + + "revision=" + this.revision + + ", channel=" + this.channel + + ", build=" + this.build + + '}'; + } +} diff --git a/src/main/java/appeng/services/version/Channel.java b/src/main/java/appeng/services/version/Channel.java new file mode 100644 index 00000000..6b37b5ea --- /dev/null +++ b/src/main/java/appeng/services/version/Channel.java @@ -0,0 +1,13 @@ +package appeng.services.version; + + +/** + * Represents the release channel of Applied Energistics. The mod is either in Alpha, Beta or Release channel. + * Any more might be confusing to the end-user + */ +public enum Channel +{ + Alpha, + Beta, + Release +} diff --git a/src/main/java/appeng/services/version/DefaultVersion.java b/src/main/java/appeng/services/version/DefaultVersion.java new file mode 100644 index 00000000..5dcaa890 --- /dev/null +++ b/src/main/java/appeng/services/version/DefaultVersion.java @@ -0,0 +1,35 @@ +package appeng.services.version; + + +/** + * AE prints version like rv2-beta-8 + * GitHub prints version like rv2.beta.8 + */ +public final class DefaultVersion extends BaseVersion +{ + /** + * @param revision natural number + * @param channel either alpha, beta or release + * @param build natural number + */ + public DefaultVersion( int revision, Channel channel, int build ) + { + super( revision, channel, build ); + } + + @Override + public boolean isNewerAs( Version maybeOlder ) + { + if ( this.revision() > maybeOlder.revision() ) + { + return true; + } + + if ( this.channel().compareTo( maybeOlder.channel() ) > 0 ) + { + return true; + } + + return this.build() > maybeOlder.build(); + } +} diff --git a/src/main/java/appeng/services/version/DoNotCheckVersion.java b/src/main/java/appeng/services/version/DoNotCheckVersion.java new file mode 100644 index 00000000..c92f43d6 --- /dev/null +++ b/src/main/java/appeng/services/version/DoNotCheckVersion.java @@ -0,0 +1,25 @@ +package appeng.services.version; + + +/** + * Exceptional template for {@link Version}, when the mod does not want a check + */ +public final class DoNotCheckVersion extends BaseVersion +{ + public DoNotCheckVersion() + { + super( Integer.MAX_VALUE, Channel.Release, Integer.MAX_VALUE ); + } + + @Override + public boolean isNewerAs( Version maybeOlder ) + { + return true; + } + + @Override + public String formatted() + { + return "dev build"; + } +} diff --git a/src/main/java/appeng/services/version/MissingVersion.java b/src/main/java/appeng/services/version/MissingVersion.java new file mode 100644 index 00000000..bf0a6298 --- /dev/null +++ b/src/main/java/appeng/services/version/MissingVersion.java @@ -0,0 +1,30 @@ +package appeng.services.version; + + +/** + * Exceptional template when the {@link Version} could not be retrieved + */ +public final class MissingVersion extends BaseVersion +{ + public MissingVersion() + { + super( 0, Channel.Alpha, 0 ); + } + + /** + * @param maybeOlder ignored + * + * @return false + */ + @Override + public boolean isNewerAs( Version maybeOlder ) + { + return false; + } + + @Override + public String formatted() + { + return "missing"; + } +} diff --git a/src/main/java/appeng/services/version/ModVersionFetcher.java b/src/main/java/appeng/services/version/ModVersionFetcher.java new file mode 100644 index 00000000..d5eb22a0 --- /dev/null +++ b/src/main/java/appeng/services/version/ModVersionFetcher.java @@ -0,0 +1,39 @@ +package appeng.services.version; + + +/** + * Wrapper for {@link VersionParser} to check if the check is happening in developer environment or in a pull request. + * + * In that case ignore the check. + */ +public final class ModVersionFetcher implements VersionFetcher +{ + private final String rawModVersion; + private final VersionParser parser; + + public ModVersionFetcher( String rawModVersion, VersionParser parser ) + { + this.rawModVersion = rawModVersion; + this.parser = parser; + } + + /** + * Parses only, if not checked in developer environment or in a pull request + * + * @return {@link DoNotCheckVersion} if in developer environment or pull request, else the parsed {@link Version} + */ + @Override + public Version get() + { + if ( this.rawModVersion.equals( "@version@" ) || this.rawModVersion.contains( "pr" ) ) + { + return new DoNotCheckVersion(); + } + else + { + final Version version = this.parser.parse( this.rawModVersion ); + + return version; + } + } +} diff --git a/src/main/java/appeng/services/version/Version.java b/src/main/java/appeng/services/version/Version.java new file mode 100644 index 00000000..94f2ae60 --- /dev/null +++ b/src/main/java/appeng/services/version/Version.java @@ -0,0 +1,42 @@ +package appeng.services.version; + + +/** + * Stores version information, which are easily compared + */ +public interface Version +{ + /** + * @return revision of this version + */ + int revision(); + + /** + * @return channel of this version + */ + Channel channel(); + + /** + * @return build of this version + */ + int build(); + + /** + * A version is never if these criteria are met: + * if the current revision is higher than the compared revision OR + * if revision are equal and the current channel is higher than the compared channel (Release > Beta > Alpha) OR + * if revision, channel are equal and the build is higher than the compared build + * + * @return true if criteria are met + */ + boolean isNewerAs( Version maybeOlder ); + + /** + * Prints the revision, channel and build into a common displayed way + * + * rv2-beta-8 + * + * @return formatted version + */ + String formatted(); +} diff --git a/src/main/java/appeng/services/version/VersionCheckerConfig.java b/src/main/java/appeng/services/version/VersionCheckerConfig.java new file mode 100644 index 00000000..9688972f --- /dev/null +++ b/src/main/java/appeng/services/version/VersionCheckerConfig.java @@ -0,0 +1,94 @@ +package appeng.services.version; + + +import java.io.File; +import java.util.Date; + +import net.minecraftforge.common.config.Configuration; + + +/** + * Seperate config file to handle the version checker + */ +public final class VersionCheckerConfig +{ + private static final int DEFAULT_INTERVAL_HOURS = 24; + private static final int MIN_INTERVAL_HOURS = 0; + private static final int MAX_INTERVAL_HOURS = 7 * 24; + + private final Configuration config; + + private final boolean isEnabled; + + private final String lastCheck; + private final int interval; + + private final String level; + + private final boolean shouldNotifyPlayer; + private final boolean shouldPostChangelog; + + /** + * @param file requires fully qualified file in which the config is saved + */ + public VersionCheckerConfig( File file ) + { + this.config = new Configuration( file ); + + // initializes default values by caching + this.isEnabled = this.config.getBoolean( "enabled", "general", true, "If true, the version checker is enabled. Acts as a master switch." ); + + this.lastCheck = this.config.getString( "lastCheck", "cache", "0", "The number of milliseconds since January 1, 1970, 00:00:00 GMT of the last successful check." ); + this.interval = this.config.getInt( "interval", "cache", DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS, MAX_INTERVAL_HOURS, "Waits as many hours, until it checks again." ); + + this.level = this.config.getString( "level", "channel", "Beta", "Determines the channel level which should be checked for updates. Can be either Release, Beta or Alpha." ); + + this.shouldNotifyPlayer = this.config.getBoolean( "notify", "client", true, "If true, the player is getting a notification, that a new version is available." ); + this.shouldPostChangelog = this.config.getBoolean( "changelog", "client", true, "If true, the player is getting a notification including changelog. Only happens if notification are enabled." ); + } + + public boolean isEnabled() + { + return this.isEnabled; + } + + public String lastCheck() + { + return this.lastCheck; + } + + /** + * Stores the current date in milli seconds into the "lastCheck" field of the config + * and makes it persistent. + */ + public void updateLastCheck() + { + final Date now = new Date(); + final long nowInMs = now.getTime(); + final String nowAsString = Long.toString( nowInMs ); + + this.config.get( "cache", "lastCheck", "0" ).set( nowAsString ); + + this.config.save(); + } + + public int interval() + { + return this.interval; + } + + public String level() + { + return this.level; + } + + public boolean shouldNotifyPlayer() + { + return this.shouldNotifyPlayer; + } + + public boolean shouldPostChangelog() + { + return this.shouldPostChangelog; + } +} diff --git a/src/main/java/appeng/services/version/VersionFetcher.java b/src/main/java/appeng/services/version/VersionFetcher.java new file mode 100644 index 00000000..d70fed83 --- /dev/null +++ b/src/main/java/appeng/services/version/VersionFetcher.java @@ -0,0 +1,10 @@ +package appeng.services.version; + + +/** + * Processes base information to retrieve a {@link Version} + */ +public interface VersionFetcher +{ + Version get(); +} diff --git a/src/main/java/appeng/services/version/VersionParser.java b/src/main/java/appeng/services/version/VersionParser.java new file mode 100644 index 00000000..14cad114 --- /dev/null +++ b/src/main/java/appeng/services/version/VersionParser.java @@ -0,0 +1,132 @@ +package appeng.services.version; + + +import java.security.InvalidParameterException; +import java.util.Scanner; +import java.util.regex.Pattern; + + +/** + * can parse a version in form of rv2-beta-8 or rv2.beta.8 + */ +public final class VersionParser +{ + private static final Pattern PATTERN_DOT = Pattern.compile( "\\." ); + private static final Pattern PATTERN_DASH = Pattern.compile( "-" ); + private static final Pattern PATTERN_REVISION = Pattern.compile( "[^0-9]+" ); + private static final Pattern PATTERN_BUILD = Pattern.compile( "[^0-9]+" ); + private static final Pattern PATTERN_NATURAL = Pattern.compile( "[0-9]+" ); + private static final Pattern PATTERN_VALID_REVISION = Pattern.compile( "^rv\\d+$" ); + + /** + * Parses the {@link Version} out of a String + * + * @param raw String in form of rv2-beta-8 or rv2.beta.8 + * + * @return {@link Version} encoded in the raw String + * + * @throws AssertionError if raw String does not match pattern of a {@link Version} + */ + public Version parse( String raw ) + { + final String transformed = this.transformDelimiter( raw ); + final String[] split = transformed.split( "_" ); + + return this.parseVersion( split ); + } + + /** + * Replaces all "." and "-" into "_" to make them uniform + * + * @param raw raw version string containing "." or "-" + * + * @return transformed raw, where "." and "-" are replaced by "_" + */ + private String transformDelimiter( String raw ) + { + assert raw.contains( "." ) || raw.contains( "-" ); + + final String withoutDot = PATTERN_DOT.matcher( raw ).replaceAll( "_" ); + final String withoutDash = PATTERN_DASH.matcher( withoutDot ).replaceAll( "_" ); + + return withoutDash; + } + + /** + * parses the {@link Version} out of the split. + * The split must have a length of 3, + * representing revision, channel and build. + * + * @param splitRaw raw version split with length of 3 + * + * @return {@link Version} represented by the splitRaw + */ + private Version parseVersion( String[] splitRaw ) + { + assert splitRaw.length == 3; + + final String rawRevision = splitRaw[0]; + final String rawChannel = splitRaw[1]; + final String rawBuild = splitRaw[2]; + + final int revision = this.parseRevision( rawRevision ); + final Channel channel = this.parseChannel( rawChannel ); + final int build = this.parseBuild( rawBuild ); + + return new DefaultVersion( revision, channel, build ); + } + + /** + * A revision starts with the keyword "rv", followed by a natural number + * + * @param rawRevision String containing the revision number + * + * @return revision number + */ + private int parseRevision( String rawRevision ) + { + assert PATTERN_VALID_REVISION.matcher( rawRevision ).matches(); + + final int revision = new Scanner( rawRevision ).useDelimiter( PATTERN_REVISION ).nextInt(); + + return revision; + } + + /** + * A channel is atm either one of {@link Channel#Alpha}, {@link Channel#Beta} or {@link Channel#Release} + * + * @param rawChannel String containing the channel + * + * @return matching {@link Channel} to the String + */ + private Channel parseChannel( String rawChannel ) + { + assert rawChannel.equalsIgnoreCase( Channel.Alpha.name() ) || rawChannel.equalsIgnoreCase( Channel.Beta.name() ) || rawChannel.equalsIgnoreCase( Channel.Release.name() ); + + for ( Channel channel : Channel.values() ) + { + if ( channel.name().equalsIgnoreCase( rawChannel ) ) + { + return channel; + } + } + + throw new InvalidParameterException( "Raw channel did not contain any of the pre-programmed types." ); + } + + /** + * A build is just a natural number + * + * @param rawBuild String containing the build number + * + * @return build number + */ + private int parseBuild( String rawBuild ) + { + assert PATTERN_NATURAL.matcher( rawBuild ).matches(); + + final int build = new Scanner( rawBuild ).useDelimiter( PATTERN_BUILD ).nextInt(); + + return build; + } +} diff --git a/src/main/java/appeng/services/version/github/DefaultFormattedRelease.java b/src/main/java/appeng/services/version/github/DefaultFormattedRelease.java new file mode 100644 index 00000000..1d63a0e4 --- /dev/null +++ b/src/main/java/appeng/services/version/github/DefaultFormattedRelease.java @@ -0,0 +1,32 @@ +package appeng.services.version.github; + + +import appeng.services.version.Version; + + +/** + * Default template when a {@link FormattedRelease} is needed. + */ +public final class DefaultFormattedRelease implements FormattedRelease +{ + private final Version version; + private final String changelog; + + public DefaultFormattedRelease( Version version, String changelog ) + { + this.version = version; + this.changelog = changelog; + } + + @Override + public String changelog() + { + return this.changelog; + } + + @Override + public Version version() + { + return this.version; + } +} diff --git a/src/main/java/appeng/services/version/github/FormattedRelease.java b/src/main/java/appeng/services/version/github/FormattedRelease.java new file mode 100644 index 00000000..3bf1a333 --- /dev/null +++ b/src/main/java/appeng/services/version/github/FormattedRelease.java @@ -0,0 +1,21 @@ +package appeng.services.version.github; + + +import appeng.services.version.Version; + + +/** + * Represents the acquired, processed information through github about a release of Applied Energistics 2 + */ +public interface FormattedRelease +{ + /** + * @return changelog + */ + String changelog(); + + /** + * @return processed version + */ + Version version(); +} diff --git a/src/main/java/appeng/services/version/github/MissingFormattedRelease.java b/src/main/java/appeng/services/version/github/MissingFormattedRelease.java new file mode 100644 index 00000000..941bdac5 --- /dev/null +++ b/src/main/java/appeng/services/version/github/MissingFormattedRelease.java @@ -0,0 +1,37 @@ +package appeng.services.version.github; + + +import appeng.services.version.MissingVersion; +import appeng.services.version.Version; + + +/** + * Exceptional template, when no meaningful {@link FormattedRelease} could be obtained + */ +public final class MissingFormattedRelease implements FormattedRelease +{ + private final Version version; + + public MissingFormattedRelease() + { + this.version = new MissingVersion(); + } + + /** + * @return empty string + */ + @Override + public String changelog() + { + return ""; + } + + /** + * @return {@link MissingVersion} + */ + @Override + public Version version() + { + return this.version; + } +} diff --git a/src/main/java/appeng/services/version/github/Release.java b/src/main/java/appeng/services/version/github/Release.java new file mode 100644 index 00000000..105c5012 --- /dev/null +++ b/src/main/java/appeng/services/version/github/Release.java @@ -0,0 +1,19 @@ +package appeng.services.version.github; + + +/** + * Template class for Gson to write values from the Json Object into an actual class + */ +@SuppressWarnings( "ALL" ) +public class Release +{ + /** + * name of the tag it is saved + */ + public String tag_name; + + /** + * Contains the changelog + */ + public String body; +} diff --git a/src/main/java/appeng/services/version/github/ReleaseFetcher.java b/src/main/java/appeng/services/version/github/ReleaseFetcher.java new file mode 100644 index 00000000..69c70e7a --- /dev/null +++ b/src/main/java/appeng/services/version/github/ReleaseFetcher.java @@ -0,0 +1,90 @@ +package appeng.services.version.github; + + +import java.io.IOException; +import java.lang.reflect.Type; +import java.net.URL; +import java.util.List; + +import org.apache.commons.io.IOUtils; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import appeng.core.AELog; +import appeng.services.version.Channel; +import appeng.services.version.Version; +import appeng.services.version.VersionCheckerConfig; +import appeng.services.version.VersionParser; + + +public final class ReleaseFetcher +{ + private static final String GITHUB_RELEASES_URL = "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases"; + private static final FormattedRelease EXCEPTIONAL_RELEASE = new MissingFormattedRelease(); + + private final VersionCheckerConfig config; + private final VersionParser parser; + + public ReleaseFetcher( VersionCheckerConfig config, VersionParser parser ) + { + this.config = config; + this.parser = parser; + } + + public FormattedRelease get() + { + final Gson gson = new Gson(); + final Type type = new ReleasesTypeToken().getType(); + + try + { + final URL releasesURL = new URL( GITHUB_RELEASES_URL ); + final String rawReleases = this.getRawReleases( releasesURL ); + + this.config.updateLastCheck(); + + final List releases = gson.fromJson( rawReleases, type ); + final FormattedRelease latestFitRelease = this.getLatestFitRelease( releases ); + + return latestFitRelease; + } + catch ( Exception e ) + { + AELog.error( e ); + + return EXCEPTIONAL_RELEASE; + } + } + + private String getRawReleases( URL url ) throws IOException + { + return IOUtils.toString( url ); + } + + private FormattedRelease getLatestFitRelease( Iterable releases ) + { + final String levelInConfig = this.config.level(); + final Channel level = Channel.valueOf( levelInConfig ); + final int levelOrdinal = level.ordinal(); + + for ( Release release : releases ) + { + final String rawVersion = release.tag_name; + final String changelog = release.body; + + final Version version = this.parser.parse( rawVersion ); + + if ( version.channel().ordinal() >= levelOrdinal ) + { + return new DefaultFormattedRelease( version, changelog ); + } + } + + return EXCEPTIONAL_RELEASE; + } + + private static final class ReleasesTypeToken extends TypeToken> + { + } +} diff --git a/src/test/java/appeng/services/version/VersionParserTest.java b/src/test/java/appeng/services/version/VersionParserTest.java new file mode 100644 index 00000000..854298e9 --- /dev/null +++ b/src/test/java/appeng/services/version/VersionParserTest.java @@ -0,0 +1,80 @@ +package appeng.services.version; + + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + + +/** + * Tests for {@link VersionParser} + */ +public final class VersionParserTest +{ + private static final String GITHUB_VERSION = "rv2.beta.8"; + private static final String GITHUB_INVALID_REVISION = "2.beta.8"; + private static final String GITHUB_INVALID_CHANNEL = "rv2.gamma.8"; + private static final String GITHUB_INVALID_BUILD = "rv2.beta.b8"; + private static final String MOD_VERSION = "rv2-beta-8"; + private static final String MOD_INVALID_REVISION = "2-beta-8"; + private static final String MOD_INVALID_CHANNEL = "rv2-gamma-8"; + private static final String MOD_INVALID_BUILD = "rv2-beta-b8"; + + private static final DefaultVersion VERSION = new DefaultVersion( 2, Channel.Beta, 8 ); + + private final VersionParser parser; + + public VersionParserTest() + { + this.parser = new VersionParser(); + } + + @Test + public void testParseGitHub_shouldPass() + { + assertTrue( this.parser.parse( GITHUB_VERSION ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseGH_InvalidRevision_NotPass() + { + assertFalse( this.parser.parse( GITHUB_INVALID_REVISION ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseGH_InvalidChannel_NotPass() + { + assertFalse( this.parser.parse( GITHUB_INVALID_CHANNEL ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseGH_InvalidBuild_NotPass() + { + assertFalse( this.parser.parse( GITHUB_INVALID_BUILD ).equals( VERSION ) ); + } + + @Test + public void testParseMod_shouldPass() + { + assertTrue( this.parser.parse( MOD_VERSION ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseMod_InvalidRevision_NotPass() + { + assertFalse( this.parser.parse( MOD_INVALID_REVISION ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseMod_InvalidChannel_NotPass() + { + assertFalse( this.parser.parse( MOD_INVALID_CHANNEL ).equals( VERSION ) ); + } + + @Test( expected = AssertionError.class ) + public void parseMod_InvalidBuild_NotPass() + { + assertFalse( this.parser.parse( MOD_INVALID_BUILD ).equals( VERSION ) ); + } +} diff --git a/src/test/java/appeng/util/UUIDMatcherTest.java b/src/test/java/appeng/util/UUIDMatcherTest.java index 24a18bc6..54e52c18 100644 --- a/src/test/java/appeng/util/UUIDMatcherTest.java +++ b/src/test/java/appeng/util/UUIDMatcherTest.java @@ -28,7 +28,7 @@ import static org.junit.Assert.assertTrue; /** * Tests for {@link UUIDMatcher} */ -public class UUIDMatcherTest +public final class UUIDMatcherTest { private static final String IS_UUID = "03ba29a1-d6bd-32ba-90b2-375e4d65abc9"; private static final String NO_UUID = "no"; diff --git a/src/test/resources/appeng/services/version/releases.json b/src/test/resources/appeng/services/version/releases.json new file mode 100644 index 00000000..ee473838 --- /dev/null +++ b/src/test/resources/appeng/services/version/releases.json @@ -0,0 +1,1040 @@ +[ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1053651", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1053651/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1053651/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.12", + "id": 1053651, + "tag_name": "rv2.beta.12", + "target_commitish": "4744dfab789b778c5c255a268ef4f1d4a01cca9c", + "name": "Build rv2.beta.12", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-03-15T17:38:03Z", + "published_at": "2015-03-16T06:32:39Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.12", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.12", + "body": "Fixes #1024 Added zinc to the grindstone - thatsIch\r\n\r\nUpdate zh_CN.lang - bakaxyf" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1051116", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1051116/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1051116/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.11", + "id": 1051116, + "tag_name": "rv2.beta.11", + "target_commitish": "ece09a956bdd0fdf3cc0074ffeed5efbdf3a37a4", + "name": "Build rv2.beta.11", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-03-13T22:49:12Z", + "published_at": "2015-03-14T23:53:49Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.11", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.11", + "body": "Fixes #1015 Pattern terminal destroying a single item when not able to satisfy a NEI recipe - yueh\r\n\r\nFixes #1011 skip null values returned by the oredictionary - yueh\r\n\r\nUpdate zh_CN.lang - bakaxyf" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1038746", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1038746/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1038746/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.10", + "id": 1038746, + "tag_name": "rv2.beta.10", + "target_commitish": "8ec9bb8b3b18a9fb4f350e326b267ff35c434217", + "name": "Build rv2.beta.10", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-03-11T15:40:32Z", + "published_at": "2015-03-11T15:53:37Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.10", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.10", + "body": "Fixes #861 Wireless Terminal notifies player, if it is unlinked - thatsIch\r\n\r\nFixes #920 Increased GUI close distance to match Vanilla MC distance - thatsIch\r\n\r\nFixes #743 Crash with BC plugs\r\n\r\nFixes #942 BC builder can build AE networks again\r\n\r\nUpdate #319 Now compiled against BC 6.4.2, with that MJ is gone" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1033060", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1033060/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/1033060/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.9", + "id": 1033060, + "tag_name": "rv2.beta.9", + "target_commitish": "3675f154d1f3af7cf34c52816f7014c74e7031b7", + "name": "Build rv2.beta.9", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-03-09T14:05:21Z", + "published_at": "2015-03-10T09:42:09Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.9", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.9", + "body": "Enhancement #817 Crafting CPU GUI now shows a green background on items being crafted, and a yellow background on scheduled items - Eearslya\r\n\r\nEnhancement Added an option to disable the colored crafting status - yueh\r\n\r\nEnhancement Added more information to the security audit - yueh\r\n\r\nEnhancement Improved performance - yueh\r\n\r\nFixes #972 Level Emitter tool-tip now corresponds to the actual behavior - thatsIch\r\n\r\nFixes #982 Auto-crafting lag reduced - yueh\r\n\r\nFixes #910 Dense cables render correctly with FMP now - yueh\r\n\r\nFixes #943 Reduced lag when breaking multiple blocks at once using Annihilation Plane - yueh\r\n\r\nFixes cable render crash and covered cables render as smart - yueh\r\n\r\nUpdate fr_FR.lang - Mazdallier\r\n\r\nUpdate zh_CN.lang - bakaxyf\r\n\r\nUpdated Texture / Model License to be more specific - AlgorithmX2\r\n\r\nUpdated Forge to recommended 10.13.2.1291 and other dependencies - yueh" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/881276", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/881276/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/881276/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.8", + "id": 881276, + "tag_name": "rv2.beta.8", + "target_commitish": "ef3cb0a4559ababe53cd7f45123e7b28719d06b7", + "name": "Build rv2.beta.8", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-01-22T16:05:23Z", + "published_at": "2015-01-24T16:46:55Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.8", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.8", + "body": "Update pt_BR.lang - al-myr\r\n\r\nImproved the logged warning for FailledConnection Now displays to coordinates of the affected tunnels. - yueh\r\n\r\nFix #701 - Changed harvest tool to axe - TheJulianJES\r\n\r\nFormation plane can now drop blocks as items - yueh\r\n\r\nDisable level emitter on offline networks Turns off level emitters if the goes offline because it runs out of power, channels or similar. - yueh\r\n\r\nFixes #304 WAILA Integration updated and fixed WAILA offers a new interface to sync the server data to the client. - thatsIch\r\n\r\nRemoved buffer from annihilation plane - yueh\r\n\r\nEffects should not collide with anything - yueh\r\n\r\nImprove code quality - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/818857", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/818857/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/818857/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.7", + "id": 818857, + "tag_name": "rv2.beta.7", + "target_commitish": "15ba1970837804dc34de3cdae037fca511103f15", + "name": "Build rv2.beta.7", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2015-01-01T14:35:57Z", + "published_at": "2015-01-01T17:56:50Z", + "assets": [ + + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.7", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.7", + "body": "Fixes #666 vanishing microblocks with Immibis-Microblocks - yueh" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/816514", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/816514/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/816514/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.6", + "id": 816514, + "tag_name": "rv2.beta.6", + "target_commitish": "305fc6c7f2c85afa05e37e127098fc3e0330a9c2", + "name": "Build rv2.beta.6", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-30T22:34:57Z", + "published_at": "2014-12-30T22:40:56Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/361179", + "id": 361179, + "name": "appliedenergistics2-rv2-beta-6-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264460, + "download_count": 2, + "created_at": "2014-12-30T22:42:44Z", + "updated_at": "2014-12-30T22:42:44Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.6/appliedenergistics2-rv2-beta-6-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/361180", + "id": 361180, + "name": "appliedenergistics2-rv2-beta-6-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2388010, + "download_count": 1, + "created_at": "2014-12-30T22:42:47Z", + "updated_at": "2014-12-30T22:42:48Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.6/appliedenergistics2-rv2-beta-6-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/361178", + "id": 361178, + "name": "appliedenergistics2-rv2-beta-6.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2424995, + "download_count": 10, + "created_at": "2014-12-30T22:42:37Z", + "updated_at": "2014-12-30T22:42:39Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.6/appliedenergistics2-rv2-beta-6.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.6", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.6", + "body": "Fixes #663 NPE on disabled Facades - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/815358", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/815358/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/815358/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.5", + "id": 815358, + "tag_name": "rv2.beta.5", + "target_commitish": "97dabc4a5cb2117f872946aea359dc49efa3b79b", + "name": "Build rv2.beta.5", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-30T13:06:08Z", + "published_at": "2014-12-30T13:31:11Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360438", + "id": 360438, + "name": "appliedenergistics2-rv2-beta-5-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264460, + "download_count": 0, + "created_at": "2014-12-30T15:03:30Z", + "updated_at": "2014-12-30T15:03:30Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.5/appliedenergistics2-rv2-beta-5-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360439", + "id": 360439, + "name": "appliedenergistics2-rv2-beta-5-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2387747, + "download_count": 0, + "created_at": "2014-12-30T15:03:32Z", + "updated_at": "2014-12-30T15:03:33Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.5/appliedenergistics2-rv2-beta-5-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360437", + "id": 360437, + "name": "appliedenergistics2-rv2-beta-5.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2424731, + "download_count": 3, + "created_at": "2014-12-30T15:03:25Z", + "updated_at": "2014-12-30T15:03:27Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.5/appliedenergistics2-rv2-beta-5.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.5", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.5", + "body": "Fixes #659 NPE on usage of the FZ sculpting tool - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/814288", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/814288/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/814288/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.4", + "id": 814288, + "tag_name": "rv2.beta.4", + "target_commitish": "02e59faa59ed1715366d0a659197dda9808560df", + "name": "Build rv2.beta.4", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-29T22:02:48Z", + "published_at": "2014-12-29T22:44:26Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360441", + "id": 360441, + "name": "appliedenergistics2-rv2-beta-4-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264460, + "download_count": 0, + "created_at": "2014-12-30T15:03:48Z", + "updated_at": "2014-12-30T15:03:48Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.4/appliedenergistics2-rv2-beta-4-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360442", + "id": 360442, + "name": "appliedenergistics2-rv2-beta-4-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2387673, + "download_count": 0, + "created_at": "2014-12-30T15:03:50Z", + "updated_at": "2014-12-30T15:03:52Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.4/appliedenergistics2-rv2-beta-4-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360440", + "id": 360440, + "name": "appliedenergistics2-rv2-beta-4.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2424666, + "download_count": 0, + "created_at": "2014-12-30T15:03:45Z", + "updated_at": "2014-12-30T15:03:46Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.4/appliedenergistics2-rv2-beta-4.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.4", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.4", + "body": "Fixes #644 Several missing null checks when disabling huge chunks of the mod via config - thatsIch\r\n\r\nUpdate ru_RU.lang - Adaptivity" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/812048", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/812048/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/812048/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.3", + "id": 812048, + "tag_name": "rv2.beta.3", + "target_commitish": "e712a9d8ceca11fd9571625a1902eab2173909b4", + "name": "Build rv2.beta.3", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-28T17:38:56Z", + "published_at": "2014-12-28T17:46:49Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360444", + "id": 360444, + "name": "appliedenergistics2-rv2-beta-3-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264396, + "download_count": 1, + "created_at": "2014-12-30T15:04:28Z", + "updated_at": "2014-12-30T15:04:29Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.3/appliedenergistics2-rv2-beta-3-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360445", + "id": 360445, + "name": "appliedenergistics2-rv2-beta-3-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2386391, + "download_count": 0, + "created_at": "2014-12-30T15:04:30Z", + "updated_at": "2014-12-30T15:04:32Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.3/appliedenergistics2-rv2-beta-3-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360443", + "id": 360443, + "name": "appliedenergistics2-rv2-beta-3.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2423384, + "download_count": 0, + "created_at": "2014-12-30T15:04:25Z", + "updated_at": "2014-12-30T15:04:27Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.3/appliedenergistics2-rv2-beta-3.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.3", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.3", + "body": "Fixes #639 Missing null check for people with disabled channels - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/811619", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/811619/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/811619/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.2", + "id": 811619, + "tag_name": "rv2.beta.2", + "target_commitish": "f7323289a50f75b6b68f2fe06bf2a6bd593d70e3", + "name": "Build rv2.beta.2", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-27T18:05:37Z", + "published_at": "2014-12-28T08:49:55Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360447", + "id": 360447, + "name": "appliedenergistics2-rv2-beta-2-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264396, + "download_count": 0, + "created_at": "2014-12-30T15:04:52Z", + "updated_at": "2014-12-30T15:04:53Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.2/appliedenergistics2-rv2-beta-2-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360448", + "id": 360448, + "name": "appliedenergistics2-rv2-beta-2-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2386310, + "download_count": 0, + "created_at": "2014-12-30T15:04:55Z", + "updated_at": "2014-12-30T15:04:57Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.2/appliedenergistics2-rv2-beta-2-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360446", + "id": 360446, + "name": "appliedenergistics2-rv2-beta-2.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2423307, + "download_count": 2, + "created_at": "2014-12-30T15:04:47Z", + "updated_at": "2014-12-30T15:04:49Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.2/appliedenergistics2-rv2-beta-2.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.2", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.2", + "body": "* #624 Fixes disabled features getting not disabled - thatsIch\r\n* #572 Fixes inventory validation for any kind of Player (enables automation via robots e.g.) - thatsIch\r\n* Sound improvements - TheJulianJES\r\n* Use player inventory to let NEI fill a recipe when it is not provided by the network - riking\r\n* #604 Fixes crash on Achievement - thatsIch\r\n* #593 Updating RF Integration to use version 1.7.10R1.0.2 - thatsIch" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/783731", + "assets_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/783731/assets", + "upload_url": "https://uploads.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/783731/assets{?name}", + "html_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/tag/rv2.beta.1", + "id": 783731, + "tag_name": "rv2.beta.1", + "target_commitish": "8d1a0cdb7df49646d4e7a713eed30c546fff2f4a", + "name": "Build rv2.beta.1", + "draft": false, + "author": { + "login": "AlgorithmX2", + "id": 2846930, + "avatar_url": "https://avatars.githubusercontent.com/u/2846930?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/AlgorithmX2", + "html_url": "https://github.com/AlgorithmX2", + "followers_url": "https://api.github.com/users/AlgorithmX2/followers", + "following_url": "https://api.github.com/users/AlgorithmX2/following{/other_user}", + "gists_url": "https://api.github.com/users/AlgorithmX2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AlgorithmX2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AlgorithmX2/subscriptions", + "organizations_url": "https://api.github.com/users/AlgorithmX2/orgs", + "repos_url": "https://api.github.com/users/AlgorithmX2/repos", + "events_url": "https://api.github.com/users/AlgorithmX2/events{/privacy}", + "received_events_url": "https://api.github.com/users/AlgorithmX2/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": true, + "created_at": "2014-12-15T13:59:55Z", + "published_at": "2014-12-15T14:07:25Z", + "assets": [ + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360452", + "id": 360452, + "name": "appliedenergistics2-rv2-beta-1-api.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 264402, + "download_count": 0, + "created_at": "2014-12-30T15:05:28Z", + "updated_at": "2014-12-30T15:05:28Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.1/appliedenergistics2-rv2-beta-1-api.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360453", + "id": 360453, + "name": "appliedenergistics2-rv2-beta-1-dev.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2383463, + "download_count": 0, + "created_at": "2014-12-30T15:05:30Z", + "updated_at": "2014-12-30T15:05:32Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.1/appliedenergistics2-rv2-beta-1-dev.jar" + }, + { + "url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/releases/assets/360451", + "id": 360451, + "name": "appliedenergistics2-rv2-beta-1.jar", + "label": null, + "uploader": { + "login": "yueh", + "id": 151949, + "avatar_url": "https://avatars.githubusercontent.com/u/151949?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/yueh", + "html_url": "https://github.com/yueh", + "followers_url": "https://api.github.com/users/yueh/followers", + "following_url": "https://api.github.com/users/yueh/following{/other_user}", + "gists_url": "https://api.github.com/users/yueh/gists{/gist_id}", + "starred_url": "https://api.github.com/users/yueh/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/yueh/subscriptions", + "organizations_url": "https://api.github.com/users/yueh/orgs", + "repos_url": "https://api.github.com/users/yueh/repos", + "events_url": "https://api.github.com/users/yueh/events{/privacy}", + "received_events_url": "https://api.github.com/users/yueh/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 2420383, + "download_count": 2, + "created_at": "2014-12-30T15:05:25Z", + "updated_at": "2014-12-30T15:05:27Z", + "browser_download_url": "https://github.com/AppliedEnergistics/Applied-Energistics-2/releases/download/rv2.beta.1/appliedenergistics2-rv2-beta-1.jar" + } + ], + "tarball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/tarball/rv2.beta.1", + "zipball_url": "https://api.github.com/repos/AppliedEnergistics/Applied-Energistics-2/zipball/rv2.beta.1", + "body": "Bump channel to beta" + } +]