[MVN] Overview of managing a server with Maven + Git

Discussion in 'Bukkit Tools' started by Mitsugaru, May 9, 2013.

?

What do you think about this system?

  1. It's awesome

    4 vote(s)
    100.0%
  2. It's OK

    0 vote(s)
    0.0%
  3. It's WAY too complicated

    0 vote(s)
    0.0%
  4. tl;dr

    0 vote(s)
    0.0%
Thread Status:
Not open for further replies.
  1. Offline

    Mitsugaru

    Managing servers isn't hard... but its nice to have tools to make things easier. One tool that some developers use is Maven, which helps with project and dependency management. Normally, this is used for doing builds and analysis of source code... but Maven is flexible for more than just that. Here, I'll be showing you how to use Maven to:
    • Manage the server's plugins
    • Update CraftBukkit and plugins
    With a little section on why to use Git.
    Why read this?
    • You want an automated way to add / upgrade / downgrade the server JAR as well as plugins
    • You want to centralize and manage your server plugins from one file
    • You want the ability to edit and deploy your server anywhere
    • You have multiple people editing server files and want them to be responsible for what they changed
    • You want your server configuration files to be backed up through awesome version control
    Assumptions
    This post assumes the following:
    • You know what Maven and Git are and how to use them
    Using Maven
    So, first off, you need a pom.xml that will sit in the root directory of the Minecraft server instance. Here's the skeleton that I'll be referencing:

    Code:
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
     
        <!-- Project Information -->
        <groupId>GROUP.ID.HERE/groupId>
        <artifactId>SERVERNAME</artifactId>
        <version>1.5.2</version>
        <description>Maven project for server files.</description>
        <packaging>pom</packaging>
     
        <!-- Project Properties -->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <dir.plugins>${basedir}/plugins</dir.plugins>
        </properties>
     
        <!-- Maven Repositories -->
        <repositories>
            <repository>
                <id>Bukkit</id>
                <url>http://repo.bukkit.org/content/groups/public</url>
            </repository>
            <repository>
                <id>sk89q-repo</id>
                <url>http://maven.sk89q.com/repo/</url>
            </repository>
            <repository>
                <id>kitteh-repo</id>
                <url>http://repo.kitteh.org/content/groups/public</url>
            </repository>
            <repository>
                <id>comphenix</id>
                <url>http://repo.comphenix.net/content/repositories/public/</url>
            </repository>
        </repositories>
     
      <!-- Server build section -->
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>2.1</version>
                    <executions>
                        <execution>
                            <id>copy</id>
                            <phase>validate</phase>
                            <goals>
                                <goal>copy</goal>
                            </goals>
                            <configuration>
                                <artifactItems>
                                    <artifactItem>
                                        <groupId>com.sk89q</groupId>
                                        <artifactId>commandbook</artifactId>
                                        <version>2.2-SNAPSHOT</version>
                                        <type>jar</type>
                                        <destFileName>CommandBook.jar</destFileName>
                                        <outputDirectory>${dir.plugins}</outputDirectory>
                                        <overWrite>true</overWrite>
                                    </artifactItem>
                                    <artifactItem>
                                        <groupId>org.bukkit</groupId>
                                        <artifactId>craftbukkit</artifactId>
                                        <version>1.5.2-R0.2-SNAPSHOT</version>
                                        <type>jar</type>
                                        <destFileName>craftbukkit.jar</destFileName>
                                        <outputDirectory>${basedir}</outputDirectory>
                                        <overWrite>true</overWrite>
                                    </artifactItem>
                                    <artifactItem>
                                        <groupId>com.comphenix.protocol</groupId>
                                        <artifactId>ProtocolLib</artifactId>
                                        <version>2.3.0</version>
                                        <type>jar</type>
                                        <destFileName>ProtocolLib.jar</destFileName>
                                        <outputDirectory>${dir.plugins}</outputDirectory>
                                        <overWrite>true</overWrite>
                                    </artifactItem>
                                    <artifactItem>
                                        <groupId>org.kitteh</groupId>
                                        <artifactId>tagapi</artifactId>
                                        <version>2.2</version>
                                        <type>jar</type>
                                        <destFileName>TagAPI.jar</destFileName>
                                        <outputDirectory>${dir.plugins}</outputDirectory>
                                        <overWrite>true</overWrite>
                                    </artifactItem>
                                    <artifactItem>
                                        <groupId>com.sk89q</groupId>
                                        <artifactId>worldedit</artifactId>
                                        <version>5.5.5</version>
                                        <type>jar</type>
                                        <destFileName>WorldEdit.jar</destFileName>
                                        <outputDirectory>${dir.plugins}</outputDirectory>
                                        <overWrite>true</overWrite>
                                    </artifactItem>
                                    <artifactItem>
                                        <groupId>com.sk89q</groupId>
                                        <artifactId>worldguard</artifactId>
                                        <version>5.7.3</version>
                                        <type>jar</type>
                                        <destFileName>WorldGuard.jar</destFileName>
                                        <outputDirectory>${dir.plugins}</outputDirectory>
                                        <overWrite>true</overWrite>
                                    </artifactItem>
                                </artifactItems>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    You'll want to fill in the group and artifact id fields appropriately. And normally the version is set to match the version of Minecraft its running.

    As you can see, we have specified the plugins folder in our properties and have specified that as the output directory for all artifacts (excluding craftbukkit). Also, we have specified a file name for all artifacts so as to overwrite existing plugins whenever we use maven to validate them. This is important to make sure we have only one JAR file per artifact and that the version that we have specified is the one in use.

    Telling Maven to refresh the JARs is as simple as running the command:
    Code:
    mvn -U validate
    So, as you can see, its easy to tell exactly what plugins are officially in use as well as the version that they are currently at. Adding new plugins is as simple as adding its section to the maven-dependency-plugin configuration section.

    Adding Plugins to Maven Repo
    The major problem with this setup is availability: Not all plugin developers have maven repositories.

    Over a year ago, desht made an awesome suggestion: having a Maven repository of released plugins from DevBukkit. Something like that would really benefit a server that utilizes Maven for managing the JAR files. Unfortunately it never happened.

    The old alternative was for me to download a plugin's jar file and manually upload it to Archiva (the maven repository software that I use). That's not so bad when you only have a few plugins... but it gets very tiresome when you have many of them (think 50-ish).

    So, considering that, I wondered if there was a simple way to deploy plugins pulled from the site and deploy them to my own repository so that my server had a place to automagically grab artifacts that I cared about.

    At first I figured I had to do some html scraping... Thankfully not. Enter BukGet.org, which provided all the information that I cared about in JSON format. With that, I wrote a derpy little script: devbukkit2mvn. This way, the process of deploying the latest files for a plugin is automated with one Python script that I can run when I get notified that plugin that I'm interested in is updated. Once its been deployed, all I have to do is update the pom.xml of the server for the plugin's new version number and have maven revalidate the jar files.

    But as I said, the script is derpy and has issues and limitations and needs work to make it more flexible and configurable... Preferably by people who actually have time to work on it (AKA, not me).

    Using Git

    Git is awesome. Even if you're not going to host your files onto a remote repository, it can still be useful for a local installation. This allows for some crazy servers that are completely open source.

    Commits
    When multiple people are involved with server file maintenance and editing, Git helps by putting a name to the changes. For instance, on one server we have a save script that requires a quick description of the changes made and then who made them. Something around the lines of:
    Code:
    ./save.sh "Added the People group to permissions" "User"
    And it essentially runs the following:
    Code:
    git commit -a -m "$1" --author=$2
    Thus generating a new commit of all the changes, with the message, and sets the author, User.
    This means you can experiment with radical changes in a branch and save changes without harming the master branch. You can undo commits made by a user. Go back to a previous state that you have tagged. And many other things that you normally do with source code that is under version control.

    .gitignore

    There are some files that you don't want to use Git with:
    • World files
    • Binary jar files
    • Session files
    As well as some plugin specific ones, such as Dynmaps web tiles (since those are generated anyways).
    Here's an example .gitignore:
    Code:
    # World files
    /logs
    /world
    /world_nether
    /world_the_end
     
    # Binary files
    *.jar
     
    # Other
    *.lck
     
    ##########################
    # Plugin Specific
    ##########################
     
    # CommandBook
    /plugins/CommandBook/sessions
     
    # dynmap
    /plugins/dynmap/web/tiles
    /plugins/dynmap/*.pending
    /plugins/dynmap/markers
    And if you're running an open source server, you obviously also want to include configuration files that contain server credentials, such as LogBlock's config.yml

    Code:
    # Logblock
    /plugins/LogBlock/config.yml
    Deployment
    If you do happen to use a remote git repository to store your configuration files, this gives you the benefit of deploying your server anywhere using Git and Maven. For instance:
    In the event of a complete server failure, you have a copy of the server's entire configuration saved in your remote git repository. You can then clone the remote to a new server using Git and then run Maven to automatically grab the JAR files that you need. Now you're back up and running without having to completely redo everything.

    GitHub
    I'm a huge fan of GitHub, so it makes sense to mention it since its one the most popular remote repository storage service. Using GitHub allows you to edit the files from anywhere just using the browser. And with multiple server admins, you have a centralized place to be social and discuss / propose changes and edits. Not to mention it gives a very visual and interactive way to explore your server files.

    End
    By using Maven and Git, you can have many benefits to your server:
    • Responsibility - Know who edited what
    • History - Entire log of all changes over time
    • Redundancy (remote) - Backup files to a remote repository
    • Versioning - Easily move to new versions for plugins
    Mind you, this doesn't do EVERYTHING for you and does require you to still be the vigilant server admin that you are... but it definitely makes things way easier and less time consuming than without.
     
    Darq, Comphenix, Goblom and 1 other person like this.
  2. Offline

    silthus

    Mitsugaru nice tutorial, thanks!

    Is there anyway to get maven to copy the jar files I have defined in a parent pom too?

    // nvm: I just needed to change the <id> tag to something different from the master pom.
     
Thread Status:
Not open for further replies.

Share This Page