Updater 2.3- Easy, Safe, and Policy-Compliant auto-updating for your plugins [NEW!]

Discussion in 'Resources' started by Gravity, Aug 28, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    Gravity

    Updater - Version 2.3
    Updater is an easy-to-use but robust and fully policy-compliant plugin updating system. It provides plugin developers with the ability to both check for and download new updates straight from BukkitDev, and requires no web server setup to function.

    Download and Source:
    Updater is a single class file that you need to add to your plugin. Simply create a new class somewhere in your plugin named "Updater", and populate it with Updater's source code, which you can find by clicking the "Get Updater" link below. Then, go to the "How to use it" section to learn how adding one line of code to your plugin will implement Updater.

    Get Updater

    Features:

    • No more hassle! Never worry about configuring your Dropbox text files to the latest build's url, or forgetting to update external files again. Upload once to BukkitDev, and as soon as your file is approved clients will start downloading it, even if the approval comes at 4am and you're fast asleep.
    • Setup is as easy as copying a class and giving it your BukkitDev project slug. Updater will do the rest.
    • Ability to tag certain builds as non-update builds. For instance, on my Jenkins system every build is tagged with -DEV, so that people who are using it do not get switched to the official latest build, and can stay testing the pre-release. Simply edit the "noUpdateTag" array in the class to define what kind of builds should be left alone.
    • Don't hassle your users anymore. Server admins have enough on their hands, don't concern them with updates, because they just will /not/ update. From personal experience, I know that the only time I cared about a plugin update was when something broke. It's far too difficult to worry about a new file every day, but if you let Updater automatically install updates, your users will rejoice!
    • Be safe. EVERY file that Updater downloads has been approved by BukkitDev staff. Real humans go line-by-line through the code of each plugin that is approved on dev.bukkit.org, to verify it is free of any malicious code. Your user's shouldn't have to blindly accept your trust, you can instead show and prove to them that by using updater, you are keeping them secure.
    • Works with both .jar file and .zip file updates.
    -- Get Updater --


    How it works:

    - First, Updater connects to BukkitDev API and requests information about your project.

    - It then searches the information for the latest file, and obtains information about it like its name and version number.

    - Optionally, Updater will run a version check, comparing the newest file with the plugin's current version. NOTE: For this to work, your file titles must be named in this format: 'PluginName vVersionNumber', such as 'AntiCheat v1.0' (or simply 'v1.0', the name is not needed, but suggested). Here's a screenshot of how this should look, if done properly:
    File titles with proper version numbers (open)

    [​IMG]

    - Assuming that an update is needed, Updater will download the file from dev.bukkit.org and store it in the update folder. This is a folder defined in the bukkit.yml file where any stored jars will be switched with its currently-in-use counterpart when the system is reloaded or restarted. This means that the user does not need to worry about replacing the downloaded file with the current file; it's all done in the background.

    How to use it:

    If you are using Maven to manage your project you can use my Maven repository to get the dependency. To do this, edit your pom.xml to add the following repository:
    Code:
        <repositories>
            <repository>
                <id>gravity-repo</id>
                <url>http://repo.gravitydevelopment.net</url>
            </repository>
        </repositories>
    
    Then, add the following dependency:
    Code:
        <dependencies>
            <dependency>
                <groupId>net.gravitydevelopment.updater</groupId>
                <artifactId>updater</artifactId>
                <version>2.1</version>
            </dependency>
        </dependencies>
    
    Otherwise, you can use the traditional way and download the source code for Updater. Simply place this somewhere within your plugin's packages, and then switch over to your main class to get to work.

    As with most of my projects, I boast the fact that you only need one line of code added to your main class (the one that extends JavaPlugin) to make this work (along with my Updater class, of course), so here's what it is:

    Code:
    Updater updater = new Updater(this, id, this.getFile(), Updater.UpdateType.DEFAULT, false);
    That's it! This single line of code will literally keep the user updated for the rest of their life. Here's a breakdown of what all these values are:

    1) "this" - The plugin instance. I suggest using this in your onEnable() method, so that you can properly issue the 'this' keyword. Other methods that are called before onEnable() will not work (but anything after it, or that is called BY onEnable() does work).

    2) "id" - This is how Updater finds your project on BukkitDev. If you don't know what this is, follow the instructions on this wiki article.

    3) "this.getFile()" - The plugin's file, this is so that Updater can properly replace your plugin with the update when it is downloaded. Note that this is a protected value, and so it can only be accessed within your plugin's main class

    4) "Updater.UpdateType.DEFAULT" - This allows you to choose which type of update you would like to take place. Currently there are 3 options:
    - DEFAULT - Typically what you would want. Do an update check, and then if it's out of date download and install the latest update.
    - NO_VERSION_CHECK - In case you know you need (or want) to update, skip version checking and just download the latest file, regardless of any it's details.
    - NO_DOWNLOAD - In case you just want to do a version check. No files will be downloaded, but you still get information about the newest build on DBO, like it's version number and size.

    5) "false" - This is a value declaring whether you want Updater to announce the progress of the download, as it takes place. This is similar to what this output (to the console) will look like:
    Output (open)

    2012-08-29 16:30:56 [INFO] [AntiCheat] Enabling AntiCheat v1.3.6-DEV
    2012-08-29 16:30:57 [INFO] About to download a new update: AntiCheat v1.3.5
    2012-08-29 16:30:57 [INFO] Downloading update: 10% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 20% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 30% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 50% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 70% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 80% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 90% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 100% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Finished updating.

    If this option is true, and there is no update, there will be no output to the console.

    You can also see these values documented in JavaDocs here: http://gravitydevelopment.net/docs/updater/

    ------------------------------------------------------------------------------------------------------------------------------------
    NOTICE:
    As of Updater 2.0, a configuration file is created to allow server administrators to globally toggle updating for any plugin using this class. While this option does provide a convenient method for server admins to disable all Updater instances, Bukkit project submission guidelines still require that you make your plugin's Updater instance specifically toggleable with its own configuration option. This gives server administrators the opportunity to only disable the updating capabilities of one plugin in particular, should they choose to do so. You may read more about compliance with this policy here.
    ------------------------------------------------------------------------------------------------------------------------------------


    Expanding updater:

    Note: The following contains more advanced user information on controlling Updater. While Updater is very simple to use, it also gives a great deal of feedback and control to the developer if they want to use it. If you are just starting to develop plugins, it is recommended that you stop here and just use Updater as you have learned to use it so far. If you are an advanced user, you may continue on, but know that all of the following info is optional, and only necessary if you want to customize your experience.

    Now, of course you may want to know what the outcome of the process was, so you can inform the user or update some values in your plugin to reflect that it is now updated. This result can easily be obtained by using the "getResult()" call. This returns an UpdateResult that reflects what happened.​

    Code:
            Updater.UpdateResult result = updater.getResult();
            switch(result)
            {
                case SUCCESS:
                    // Success: The updater found an update, and has readied it to be loaded the next time the server restarts/reloads
                    break;
                case NO_UPDATE:
                    // No Update: The updater did not find an update, and nothing was downloaded.
                    break;
                case DISABLED:
                    // Won't Update: The updater was disabled in its configuration file.
                    break;
                case FAIL_DOWNLOAD:
                    // Download Failed: The updater found an update, but was unable to download it.
                    break;
                case FAIL_DBO:
                    // dev.bukkit.org Failed: For some reason, the updater was unable to contact DBO to download the file.
                    break;
                case FAIL_NOVERSION:
                    // No version found: When running the version check, the file on DBO did not contain the a version in the format 'vVersion' such as 'v1.0'.
                    break;
                case FAIL_BADID:
                    // Bad id: The id provided by the plugin running the updater was invalid and doesn't exist on DBO.
                    break;
                case FAIL_APIKEY:
                    // Bad API key: The user provided an invalid API key for the updater to use.
                    break;
                case UPDATE_AVAILABLE:
                  // There was an update found, but because you had the UpdateType set to NO_DOWNLOAD, it was not downloaded.
            }
    All these values, of course, are documented in easy-to-read HTML here: http://gravitydevelopment.net/docs/updater/

    You also may want to know information about the newest update. Some people prefer to have Updater run a version check ONLY (using UpdateType.NO_DOWNLOAD), then, if there is an update available, start notifying admins as they log in that there is a new version ready, with information like file size and version. An admin would then issue a command, and the developer would run Updater again but this time with UpdateType set to NO_VERSION_CHECK, thus downloading the newest build at the admin's request.

    We have a few methods available for you to use for this information. We already know that we can determine the outcome of the version check by calling getResult(), but here are some more methods you can call to get information about the newest file:

    - getLatestName() - Returns the name of the latest file you have uploaded to BukkitDev (Ex: "AntiCheat v1.5.9")
    - getLatestType() - Returns the type of the latest file you have uploaded to BukkitDev (Alpha, Beta, Release)
    - getLatestGameVersion() - Returns the compatible Game Version of the latest file you have uploaded to BukkitDev (Ex: "CB 1.6.2-R1.0")
    - getLatestFileLink() - Returns the link to the latest file you have uploaded.

    The scenario mentioned about would look something like this (pseudocode):

    Code:
    // In main class
    
    public static boolean update = false;
    public static String name = "";
    public static ReleaseType type = null;
    public static String version = "";
    public static String link = "";
    // You would want to make getter methods in your class, this is just for simplicity.
    
    public void onEnable()
    {
      Updater updater = new Updater(this, YOUR_ID_HERE, this.getFile(), Updater.UpdateType.NO_DOWNLOAD, false); // Start Updater but just do a version check
      update = updater.getResult() == Updater.UpdateResult.UPDATE_AVAILABLE; // Determine if there is an update ready for us
      name = updater.getLatestName(); // Get the latest name
      version = updater.getLatestGameVersion(); // Get the latest game version
      type = updater.getLatestType(); // Get the latest file's type
      link = updater.getLatestFileLink(); // Get the latest link
    }
    
    // In a listener class:
    
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event)
    {
      Player player = event.getPlayer();
      if(player.hasPermission("foo.bar") && Main.update)
      {
        player.sendMessage("An update is available: " + Main.name + ", a " + Main.type + " for " + Main.version + " available at " + Main.link);
        // Will look like - An update is available: AntiCheat v1.5.9, a release for CB 1.6.2-R0.1 available at http://media.curseforge.com/XYZ
        player.sendMessage("Type /update if you would like to automatically update.");
      }
    }
    
    // And then later in a CommandExecutor class, when they type /update:
    
    Updater updater = new Updater(this, YOUR_ID_HERE, this.getFile(), Updater.UpdateType.NO_VERSION_CHECK, true); // Go straight to downloading, and announce progress to console.
    

     
    Last edited: May 6, 2016
    FisheyLP, Nathat23, Eathuis and 36 others like this.
  2. Offline

    Conarnar

    Quick question.
    When a plugin is downloaded using this, will it add to the plugin download count.
    Gravity
     
  3. Offline

    chrisman0091

    Thank you very much for this, I will be using this.

    One question:
    Should I be getting an unused error warning on 'updater' with the line of code you provided?
    Code:java
    1. Updater updater = new Updater(this, 68778, this.getFile(), Updater.UpdateType.DEFAULT, false);


    Just to be clear I'm talking about on the second word.
     
  4. Offline

    Gravity

    You're not ever accessing (checking the result) of the updater object you created, so your IDE is asking why you made it in the first place. Which might be a good question to ask :p
     
  5. Offline

    chrisman0091

    Yeah I realized that a few minutes after posting. I've decided to change the way I do it and make it a command, but I am getting an error on using this.getFile in my CommandExecuter class. Any idea what I'm doing wrong? It doesn't give me an error when used in my onEnable method, but of course I don't want to update upon starting the plugin.
     
  6. Offline

    AnorZaken

    Thats because "this" is something different in the CommandExecuter class. What you want is a variable that points to your plugin. So it's "plugin.getFile()" ...or whatever name you give that variable.

    Also if you check the result in a command you could get freeze lag unless you wrap it up in a separate thread (the main reason I decided to fork Updater 2.0). Just a heads up.
     
  7. Offline

    the_merciless

    Doesn't that leave this open for abuse. Whats to stop me uploading 'fake' updates everyday to increase my downloads?
     
  8. Offline

    AnorZaken

    Lots of things in this world are open for abuse. Like kickstarter for instance. But unless the gains are worth the effort it wont get abused. Most ppl are lazy u know. What would you gain? And every update still has to be approved so they cant be completely fake either. (Clearly if you want abuse kickstarter is a better bet than dbo.)
     
  9. Offline

    SimSonic

  10. Offline

    jamesst20


    Then write

    Code:java
    1. new Updater(this, 68778, this.getFile(), Updater.UpdateType.DEFAULT, false);


    instead ;)

    Gravity
    I have seen a few errors in your source code, if I make a pull request with errors fixed, will you take a look or merge it or it would be totally useless? Wondering because you already have a few pull request on GitHub :) I like to contribute to project I think is worth for everyone or for project I use

    Edit : https://github.com/gravitylow/Updater/pull/15
     
  11. Offline

    AnorZaken

    jamesst20
    I had a look at your pull requests (they are of interest to me because of my non-blocking fork of Updater2.0), some of the changes I will probably incorporate (when I have time to work on this again) but regarding the "huge mistake" I'd say it's not a mistake. Can't imagine him not being aware of the limitations of the version checker, it is probably more of a design issue: not wanting to force developers to adhere to a specific versioning scheme.

    At some point I will probably make it possible to supply your own version-checking code as an argument to the updater (my fork that is) as well as provide a few standard ones to choose from (yours would be an excellent "numbers-only version-checker" if you approve of me using it).

    (There is also no reason to not let the plugin developer supply their own set of no-update tags.)

    Just sharing my thoughts.
     
    jamesst20 likes this.
  12. Offline

    Gravity

    jamesst20 thanks for your PR. I looked through your changes and left some feedback on them. What AnorZaken said about the version was absolutely right; if you look through the recent history you'll notice a method similar to the one you wrote was removed explicitly because of problems in parsing versions into comparable values. I'm not going to be merging that change, but if you look at the other things I pointed out I'll be happy to take a look at the other diffs.
     
    jamesst20 likes this.
  13. Offline

    Ibas


    Hey, maybe stupid question I will ask, but I and my players doesn't like, that Updater folder is being created. Would be much more comfortable for my users if I would made option in my own plugin's folder in config.yml, maybe I missed something and this feature already exists?
     
  14. Offline

    AnorZaken

    You MUST make a config option for it in your own plugin folder (otherwise your plugin will be/should be rejected on submission)! Read through the instructions again: This is something the updater cant do for you (conflict preventing design choice) - you have to do yourself.

    (The file inside the Updater-folder is for global settings = affects ALL plugins using the updater. This is not something a plugindeveloper needs to or should alter, what you need is a separate setting for just your plugin - how or where you implement this is up to you, but inside config.yml is recommended.)
     
  15. Offline

    Ibas


    I have done this..

    Well I don't like, that Updater folder is being created and my plugin users asking such kind of questions, gonna make my own updater then.
     
  16. Offline

    Gravity

    You can fork it and strip the code out if you wish. It's open source.
     
  17. Offline

    Mishrathium

    This is a really neat idea but lets be clear. Safe? No.

    Just a few months ago bukkit.org approved malicious code on several large projects by accident. /essentials as an example.

    If a lazy admin never checked - they would never know - would be infected - forever.


    Good idea and fantastic implementation and I applaud you, but this just makes bad admins worse. If an admin has no time to check important pages and updates - they should not be admins.
     
  18. Offline

    Gravity

    Mishrathium the dev.bukkit.org code review is done by human beings. When malicious code was missed in the review the files were immediately removed, the authors banned, and the community notified in every way possible. As EvilSeph said in this announcement:
    Being the head of the staff who do the reviews, I'm absolutely invested in the safety of the users as well as the completeness of the review. However, the idea that the Updater makes it less safe is untrue. All updates that are processed by it go through staff review, and if they are ever found to be malicious, can be removed from our site. This makes it inherently safer than any other sort of updating, and it's why the Project Submission Guidelines require this to be the process used by any updater - not just this one.

    Updater is designed to give developers updating tools that are both easy to use and follow these guidelines exactly. In addition to this, it can give users interested in making their own tools a good reference point for a guideline-complaint system; any updater following the rules I linked earlier is no less safe than this. In the review process, we deny any updater which does otherwise.

    I do believe that a characteristic of a good server owner that they stay on top of news about plugin updates. However, similar to all the apps you might have on your smartphone, servers have a lot of plugins, and you can't always expect them to go to each page individually and download new versions. I have run servers in the past and know this is a difficult option.

    If you are concerned about any of this as a server administrator, you are free to disable the updating properties of any plugin we approve and manually update. This is, again, why we enforce the submission guidelines in our review.
     
  19. Offline

    Ibas

    Could you do that :/ ? I'm not that good at reading other people codes.
     
  20. Offline

    Gravity

    The code is put there to help server admins. I implemented it for a reason, and if you want the system to operate another way, you should make it do so. You said you were going to create your own system, I noted that you could just edit mine to your liking -- that should be much easier if you were going to do the former.

    I'm not going to do it for you because Updater works how I believe it should. If that's not your belief, you can change it.
     
  21. Offline

    PatoTheBest

    What do you mean with:
    version = updater.getLatestGameVersion(); // Get the latest game version <-
    type = updater.getLatestType(); // Get the latest game version <-
     
  22. Offline

    Gravity

    Forgot to change that :) I've updated it in the OP. Above that code you can see documentation that explains it, and links to javadocs.
     
  23. Offline

    PatoTheBest

    Will FAIL_DBO Change to FAIL_Curse?
     
  24. Offline

    Gravity

    It's a BukkitDev API. I wouldn't break things if I didn't need to, especially for a discrepancy that doesn't really matter.
     
  25. Offline

    Gravity

    Howdy!

    There's now two ways to get Updater working in your project.

    1) You can use the traditional way and download the source code, incorporating it into your project somewhere.

    2) Or, if you are using Maven to manage your project, starting with Updater 2.1 (and I've also uploaded a copy of 2.0) you can use my repository to get the dependency.

    To do this, edit your pom.xml to add the following repository:
    Code:
        <repositories>
            <repository>
                <id>gravity-repo</id>
                <url>http://repo.gravitydevelopment.net</url>
            </repository>
        </repositories>
    
    Then, add the following dependency:
    Code:
        <dependencies>
            <dependency>
                <groupId>net.gravitydevelopment.updater</groupId>
                <artifactId>updater</artifactId>
                <version>2.1</version>
            </dependency>
        </dependencies>
    
    Then, you'll need to shade the file into your project so it's included when you build with Maven
    NOTE: you need to modify the below code to change "your.package.updater" to the actual package you're using in your code. This plugin will include Updater in your build at that location, so you can import and use it in your classes.
    Code:
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.3</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <artifactSet>
                                    <includes>
                                        <include>net.gravitydevelopment.updater:updater</include>
                                    </includes>
                                </artifactSet>
                                <relocations>
                                    <relocation>
                                        <pattern>net.gravitydevelopment.updater</pattern>
                                        <shadedPattern>your.package.updater</shadedPattern>
                                    </relocation>
                                </relocations>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
    And that's all! Once you build your project or have maven download the required dependencies, you're set to use them in your Plugin!
     
    Minecrell likes this.
  26. Best update ever!
     
  27. Offline

    timtower Administrator Administrator Moderator

    Gravity Currently coding a plugin that will check if updates are available for listed plugins in the config.
    Is it possible to leave the file blank? Since I don't need to update it, or do I need to get the file anyways by reflection?
    EDIT: Nevermind, just commented the file stuff out. Don't need updates
     
  28. Offline

    PatoTheBest

    What's new? Changelog?
     
  29. Offline

    Gravity

    Mostly just internal cleanups.

    The only difference in terms of usage is that getLatestType now returns a ReleaseType enum which represents the three different types a file can have: Alpha, Beta, or Release. Previously it just returned a string with this value.

    Aside from that, now "vVersion", "Name_vVersion" and "Name-vVersion" are recognized version schemes that can be parsed correctly (previously it was just "Name vVersion").

    And as a sidenote, Updater is now MIT licensed. If you want to see the full extent of the changes, there's a commit log here: https://github.com/gravitylow/Updater/commits/master
     
  30. Offline

    MrAwellstein

    Everything works great, except for when your plugin is a higher date than listed (I don't know why, or even think, that there would be a real instance of this, other than when debugging your own plugin).
    Great Job ^_^
     
  31. Offline

    Plumeex

    Hello Gravity, I think I have a problem with Updater. When a plugin (this one) cannot search for an update, the server crashes: here are the logs.

    I first thought it wasn't a plugin issue, so I asked on IRC and someone told me that "The plugin was blocking the main thread waiting for the updater to complete". So I made a ticket, but as you can see in the developer's comment it seems the problem comes from Updater. And that's why I came here to ask!

    Thank you. c:
     
Thread Status:
Not open for further replies.

Share This Page