Skip to content
This repository has been archived by the owner on Mar 10, 2019. It is now read-only.

Commit

Permalink
Update script (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
devgianlu committed Mar 21, 2018
1 parent 9fea157 commit 372accb
Show file tree
Hide file tree
Showing 27 changed files with 336 additions and 64 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
target
target/
.update/
.idea/
.backup/
WebContent/node_modules/
WebContent/.backup/
.old/
Expand Down
2 changes: 1 addition & 1 deletion dist/start.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/bash
#!/usr/bin/env bash
sudo java -jar ./PYX-Reloaded.jar
9 changes: 9 additions & 0 deletions dist/update.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@echo off
pushd %~dp0

ren PYX-Reloaded.jar PYX-Reloaded.old.jar
java -jar ./PYX-Reloaded.old.jar --update
del PYX-Reloaded.old.jar
pause

popd
4 changes: 4 additions & 0 deletions dist/update.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
sudo mv ./PYX-Reloaded.jar ./PYX-Reloaded.old.jar
sudo java -jar ./PYX-Reloaded.old.jar --update
sudo rm ./PYX-Reloaded.old.jar
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@
</build>

<dependencies>
<!-- Updater -->
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>github-api</artifactId>
<version>1.92</version>
</dependency>

<!-- Send emails -->
<dependency>
<groupId>org.simplejavamail</groupId>
Expand Down
16 changes: 14 additions & 2 deletions src/main/java/com/gianlu/pyxreloaded/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import io.undertow.server.handlers.encoding.ContentEncodingRepository;
import io.undertow.server.handlers.encoding.EncodingHandler;
import io.undertow.server.handlers.encoding.GzipEncodingProvider;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import javax.net.ssl.*;
Expand All @@ -34,7 +37,6 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

public class Server {
private static final Logger logger = Logger.getLogger(Server.class.getSimpleName());
Expand All @@ -44,8 +46,18 @@ public class Server {
private static final long BROADCAST_UPDATE_DELAY = TimeUnit.SECONDS.toMillis(60);

public static void main(String[] args) throws IOException, SQLException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
Preferences preferences = Preferences.load(args);
BasicConfigurator.configure();
Logger.getRootLogger().setLevel(Level.INFO);

for (String arg : args) {
if (arg.equals("--update")) {
Updater.update();
return;
}
}


Preferences preferences = Preferences.load(args);
ServerDatabase serverDatabase = new ServerDatabase(preferences);

Providers.add(Annotations.Preferences.class, (Provider<Preferences>) () -> preferences);
Expand Down
229 changes: 229 additions & 0 deletions src/main/java/com/gianlu/pyxreloaded/Updater.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package com.gianlu.pyxreloaded;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.kohsuke.github.GHAsset;
import org.kohsuke.github.GHRelease;
import org.kohsuke.github.GitHub;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class Updater {
private final static Logger logger = Logger.getLogger(Updater.class.getSimpleName());
private static final String[] BACKUP_EXCLUDE = {".git", ".backup", ".idea", ".update"};
private final GHRelease latestRelease;
private final File currentFiles;
private final JsonParser parser;

private Updater(GitHub api) throws IOException {
this.latestRelease = api.getRepository("devgianlu/PYX-Reloaded").getLatestRelease();
this.currentFiles = new File(".");
this.parser = new JsonParser();
}

public static void update() throws IOException, SQLException {
logger.info("Checking for a newer version...");
Updater updater = new Updater(GitHub.connectAnonymously());
if (updater.checkVersion()) {
File backupDir = updater.doBackup();
logger.info("Files have been backed up into " + backupDir.getAbsolutePath());

File updateDir = new File(updater.currentFiles, ".update");
if (!updateDir.exists() && !updateDir.mkdir())
throw new IllegalStateException("Cannot create update directory: " + updateDir.getAbsolutePath());

FileUtils.cleanDirectory(updateDir);

File updateFiles = updater.downloadLatest(updateDir);
updater.copyPreferences(updateFiles);
updater.copyDatabase(updateFiles);

updater.moveUpdatedFiles(updateFiles);
FileUtils.deleteDirectory(updateDir);
logger.info("The server has been updated successfully!");
System.exit(0);
}
}

private static void copyValues(JsonObject from, JsonObject to) {
for (Map.Entry<String, JsonElement> entry : from.entrySet()) {
if (entry.getValue().isJsonObject()) {
JsonElement child = to.get(entry.getKey());
if (child == null || !child.isJsonObject()) {
child = new JsonObject();
to.add(entry.getKey(), child);
}

copyValues(entry.getValue().getAsJsonObject(), child.getAsJsonObject());
} else {
to.add(entry.getKey(), entry.getValue());
}
}
}

private void moveUpdatedFiles(File updateFiles) throws IOException {
File[] files = updateFiles.listFiles();
if (files == null) throw new IllegalStateException("Cannot read update files.");

for (File file : files) {
File destFile = new File(currentFiles, file.getName());
if (destFile.exists()) FileUtils.forceDelete(destFile);

if (file.isDirectory()) FileUtils.moveDirectoryToDirectory(file, currentFiles, true);
else FileUtils.moveFileToDirectory(file, currentFiles, true);
}
}

@NotNull
private File doBackup() throws IOException {
File backupDir = new File(currentFiles, ".backup");
if (!backupDir.exists() && !backupDir.mkdir())
throw new IllegalStateException("Cannot create backup directory: " + backupDir.getAbsolutePath());

FileUtils.cleanDirectory(backupDir);

FileUtils.copyDirectory(currentFiles, backupDir, pathname -> !Utils.contains(BACKUP_EXCLUDE, pathname.getName()));
return backupDir;
}

private void copyDatabase(File updateFiles) throws SQLException {
File oldDb = new File(currentFiles, "server.sqlite");
File newDb = new File(updateFiles, "server.sqlite");

try (Connection newConn = DriverManager.getConnection("jdbc:sqlite:" + newDb.getAbsolutePath())) {
List<String> tables = DatabaseHelper.listTables(newConn);
DatabaseHelper.copyTables(oldDb, newConn, tables);
}

logger.info("Server database has been copied successfully.");
}

private void copyPreferences(File updateFiles) throws IOException {
File newerPrefs = new File(updateFiles, "preferences.json");
JsonObject newer;

try (FileReader olderReader = new FileReader(new File(currentFiles, "preferences.json"));
FileReader newerReader = new FileReader(newerPrefs)) {
JsonObject older = parser.parse(olderReader).getAsJsonObject();
newer = parser.parse(newerReader).getAsJsonObject();

copyValues(older, newer);
}

FileUtils.writeStringToFile(newerPrefs, newer.toString());
logger.info("Preferences have been copied successfully. NOTE: The newer file may contain new keys (which have been set to their default)");
}

@NotNull
private File downloadLatest(@NotNull File into) throws IOException {
List<GHAsset> assets = latestRelease.getAssets();

GHAsset zipAsset = null;
for (GHAsset asset : assets)
if (asset.getContentType().equals("application/x-zip-compressed"))
zipAsset = asset;

if (zipAsset == null) throw new IllegalStateException("Cannot find ZIP asset. " + assets);

logger.info("Downloading the latest release... (" + zipAsset.getBrowserDownloadUrl() + ")");

try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpGet get = new HttpGet(zipAsset.getBrowserDownloadUrl());
HttpResponse resp = client.execute(get);

StatusLine sl = resp.getStatusLine();
if (sl.getStatusCode() != 200)
throw new IllegalStateException("Failed downloading the asset. Response code was " + sl.getStatusCode());

HttpEntity entity = resp.getEntity();
if (entity == null) throw new IllegalStateException("Response has no entity.");

String dir = null;
try (ZipInputStream zis = new ZipInputStream(entity.getContent())) {
byte[] buffer = new byte[4096];
ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null) {
File newFile = new File(into, zipEntry.getName());
if (zipEntry.isDirectory()) {
if (dir == null) dir = zipEntry.getName();
//noinspection ResultOfMethodCallIgnored
newFile.mkdirs();
continue;
}

// TODO: Log progress

try (FileOutputStream fos = new FileOutputStream(newFile)) {
int len;
while ((len = zis.read(buffer)) > 0) fos.write(buffer, 0, len);
}
}
zis.closeEntry();
}

if (dir == null) throw new IllegalStateException("What happened?");

logger.info("Latest release downloaded successfully!");
return new File(into, dir);
}
}

private boolean checkVersion() {
String currentVersion = Utils.getServerVersion(Package.getPackage("com.gianlu.pyxreloaded"));
if (currentVersion.equals("debug")) throw new IllegalStateException("Cannot update debug version!");

String latestVersion = latestRelease.getTagName().substring(1);

if (Utils.isVersionNewer(currentVersion, latestVersion)) {
logger.info("There is a newer version available! (" + currentVersion + " => " + latestVersion + ")");
return true;
} else {
logger.info("Your version is already the latest! (" + currentVersion + ")");
return false;
}
}

private static final class DatabaseHelper {

@SuppressWarnings("ALL")
private static List<String> listTables(Connection conn) throws SQLException {
try (Statement statement = conn.createStatement();
ResultSet set = statement.executeQuery("SELECT name FROM sqlite_master WHERE type='table'")) {
List<String> list = new ArrayList<>();
while (set.next()) list.add(set.getString(1));
return list;
}
}

@SuppressWarnings("ALL")
private static void copyTables(File from, Connection to, List<String> tables) throws SQLException {
try (Statement statement = to.createStatement()) {
statement.execute("ATTACH DATABASE '" + from.getAbsolutePath() + "' AS old");

for (String table : tables)
statement.executeUpdate("INSERT INTO main." + table + " SELECT * FROM old." + table);

statement.execute("DETACH old");
}
}
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/gianlu/pyxreloaded/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,34 @@
public class Utils {
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

public static boolean isVersionNewer(String older, String newer) {
String[] olderSplit = older.split("\\.");
String[] newerSplit = newer.split("\\.");

for (int i = 0; i < olderSplit.length && i < newerSplit.length; i++) {
int olderNum = Integer.parseInt(olderSplit[i]);
int newerNum = Integer.parseInt(newerSplit[i]);
if (newerNum > olderNum) return true;
}

return false;
}

public static JsonArray toIntsJsonArray(Collection<Integer> items) {
JsonArray jsonArray = new JsonArray(items.size());
for (int item : items) jsonArray.add(item);
return jsonArray;
}

@NotNull
public static String getServerVersion(Package pkg) {
String version = pkg.getImplementationVersion();
if (version == null) version = pkg.getSpecificationVersion();
if (version == null) version = System.getenv("version");
if (version == null) version = "debug";
return version;
}

public static JsonArray toStringsJsonArray(Collection<String> items) {
JsonArray jsonArray = new JsonArray(items.size());
for (String item : items) jsonArray.add(item);
Expand Down
Loading

0 comments on commit 372accb

Please sign in to comment.