Basic AppImage Updater
This commit is contained in:
parent
b055980103
commit
00fee9c410
@ -76,6 +76,9 @@ if(BUILD_ARM_COMPONENTS)
|
||||
endif()
|
||||
cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}")
|
||||
|
||||
# Build Dependencies
|
||||
add_subdirectory(dependencies)
|
||||
|
||||
# Fast Math
|
||||
add_compile_options(-ffast-math)
|
||||
|
||||
@ -91,9 +94,6 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Build Dependencies
|
||||
add_subdirectory(dependencies)
|
||||
|
||||
# Build libreborn
|
||||
add_subdirectory(libreborn)
|
||||
|
||||
|
@ -291,10 +291,12 @@ void ConfigurationUI::draw_about() {
|
||||
Updater *updater = Updater::instance;
|
||||
if (updater) {
|
||||
ImGui::Separator();
|
||||
ImGui::BeginDisabled(!updater->can_start());
|
||||
draw_right_aligned_buttons({updater->get_status().c_str()}, [&updater](__attribute__((unused)) int id, const bool was_clicked) {
|
||||
if (was_clicked) {
|
||||
updater->start();
|
||||
}
|
||||
}, true);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "logger/logger.h"
|
||||
#include "util/util.h"
|
||||
#include "client/configuration.h"
|
||||
#include "updater/updater.h"
|
||||
|
||||
// Bind Options To Environmental Variable
|
||||
static void bind_to_env(const char *env, const bool value) {
|
||||
@ -39,12 +40,34 @@ static void setup_environment(const options_t &options) {
|
||||
|
||||
// Non-Launch Commands
|
||||
static void handle_non_launch_commands(const options_t &options) {
|
||||
// SDK
|
||||
if (options.copy_sdk) {
|
||||
const std::string binary_directory = get_binary_directory();
|
||||
copy_sdk(binary_directory, true);
|
||||
fflush(stdout);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
// Updater
|
||||
if (options.run_update) {
|
||||
Updater *updater = Updater::instance;
|
||||
if (updater) {
|
||||
updater->update();
|
||||
if (updater->status == ERROR) {
|
||||
ERR("Unable To Update");
|
||||
} else if (updater->status == UP_TO_DATE) {
|
||||
INFO("Already Up-To-Date");
|
||||
} else {
|
||||
if (updater->status != RESTART_NEEDED) {
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
INFO("Update Completed");
|
||||
}
|
||||
} else {
|
||||
ERR("Built-In Updater Unavailable, Use System Package Manager");
|
||||
}
|
||||
fflush(stdout);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
// Start The Game
|
||||
|
@ -10,4 +10,5 @@ OPTION(only_generate, "only-generate", -8, "Generate World And Exit (Server-Mode
|
||||
OPTION(force_headless, "force-headless", -9, "Force Disable Game Rendering")
|
||||
OPTION(force_non_headless, "force-non-headless", -10, "Force Enable Game Rendering")
|
||||
OPTION(server_mode, "server", -11, "Run In Server-Mode")
|
||||
OPTION(skip_pagesize_check, "skip-pagesize-check", -12, "Skip Page-Size Check (Not Recommended)")
|
||||
OPTION(skip_pagesize_check, "skip-pagesize-check", -12, "Skip Page-Size Check (Not Recommended)")
|
||||
OPTION(run_update, "update", -13, "Run Updater (If Available)")
|
@ -0,0 +1,122 @@
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <libreborn/log.h>
|
||||
#include <libreborn/util/exec.h>
|
||||
#include <libreborn/config.h>
|
||||
|
||||
#include "updater.h"
|
||||
|
||||
// Implement
|
||||
struct AppImageUpdater final : Updater {
|
||||
void update() override;
|
||||
void restart() override;
|
||||
static AppImageUpdater instance;
|
||||
};
|
||||
AppImageUpdater AppImageUpdater::instance;
|
||||
|
||||
// Update
|
||||
template <typename... Args>
|
||||
static std::optional<std::string> run_wget(Args... args) {
|
||||
int status = 0;
|
||||
const char *const command[] = {"wget", "-O", std::forward<Args>(args)..., nullptr};
|
||||
const std::vector<unsigned char> *output = run_command(command, &status);
|
||||
std::string output_str = (const char *) output->data();
|
||||
delete output;
|
||||
if (!is_exit_status_success(status)) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return output_str;
|
||||
}
|
||||
}
|
||||
static std::string extract_from_json(const std::string &json_str, const std::string &key) {
|
||||
std::string::size_type pos = json_str.find(key);
|
||||
std::array<std::string::size_type, 3> indices = {};
|
||||
unsigned int i = 0;
|
||||
while (true) {
|
||||
if (pos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
if (i >= indices.size()) {
|
||||
break;
|
||||
}
|
||||
pos = json_str.find('"', pos) + 1;
|
||||
indices[i++] = pos;
|
||||
}
|
||||
const std::string::size_type start = indices[1];
|
||||
const std::string::size_type end = indices[2];
|
||||
return json_str.substr(start, end - start - 1);
|
||||
}
|
||||
static const char *get_appimage_path() {
|
||||
const char *path = getenv("APPIMAGE");
|
||||
if (path == nullptr) {
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
void AppImageUpdater::update() {
|
||||
// Check
|
||||
if (status != CHECKING) {
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
const std::optional<std::string> json = run_wget("-", MCPI_APPIMAGE_JSON_URL);
|
||||
if (!json.has_value()) {
|
||||
status = ERROR;
|
||||
return;
|
||||
}
|
||||
const std::string tag_name = extract_from_json(json.value(), "tag_name");
|
||||
|
||||
// Check Version
|
||||
if (tag_name == MCPI_VERSION) {
|
||||
status = UP_TO_DATE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get URL
|
||||
std::string url = MCPI_APPIMAGE_DOWNLOAD_URL;
|
||||
while (true) {
|
||||
const std::string placeholder = MCPI_APPIMAGE_VERSION_PLACEHOLDER;
|
||||
const std::string::size_type pos = url.find(placeholder);
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
url.replace(pos, placeholder.size(), tag_name);
|
||||
}
|
||||
|
||||
// Get Path
|
||||
const char *appimage_path = get_appimage_path();
|
||||
const std::string new_appimage_path_base = std::string(appimage_path) + ".new";
|
||||
std::string new_appimage_path = new_appimage_path_base;
|
||||
int num = 1;
|
||||
while (access(new_appimage_path.c_str(), F_OK) != -1) {
|
||||
new_appimage_path = new_appimage_path_base + '.' + std::to_string(num++);
|
||||
}
|
||||
|
||||
// Download
|
||||
status = DOWNLOADING;
|
||||
const std::optional<std::string> out = run_wget(new_appimage_path.c_str(), url.c_str());
|
||||
bool ret = out.has_value();
|
||||
if (ret) {
|
||||
ret = chmod(new_appimage_path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0;
|
||||
}
|
||||
if (ret) {
|
||||
ret = rename(new_appimage_path.c_str(), appimage_path) == 0;
|
||||
}
|
||||
if (!ret) {
|
||||
unlink(new_appimage_path.c_str());
|
||||
status = ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Done
|
||||
status = RESTART_NEEDED;
|
||||
}
|
||||
|
||||
// Restart
|
||||
void AppImageUpdater::restart() {
|
||||
const char *const command[] = {get_appimage_path(), nullptr};
|
||||
safe_execvpe(command, environ);
|
||||
}
|
@ -20,6 +20,7 @@ std::string Updater::get_status() const {
|
||||
case CHECKING: return "Checking...";
|
||||
case UP_TO_DATE: return "Up-To-Date";
|
||||
case DOWNLOADING: return "Downloading...";
|
||||
case ERROR: return "Error";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
@ -33,6 +34,7 @@ static void *update_thread(void *data) {
|
||||
void Updater::start() {
|
||||
switch (status) {
|
||||
case NOT_STARTED: {
|
||||
status = CHECKING;
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, nullptr, update_thread, this);
|
||||
break;
|
||||
|
@ -8,7 +8,8 @@ enum UpdateStatus {
|
||||
CHECKING,
|
||||
UP_TO_DATE,
|
||||
DOWNLOADING,
|
||||
RESTART_NEEDED
|
||||
RESTART_NEEDED,
|
||||
ERROR
|
||||
};
|
||||
|
||||
// Updater
|
||||
|
@ -142,7 +142,7 @@ std::vector<unsigned char> *run_command(const char *const command[], int *exit_s
|
||||
// Child Process
|
||||
reborn_debug_tag = CHILD_PROCESS_TAG;
|
||||
// Run
|
||||
safe_execvpe(command, (const char *const *) environ);
|
||||
safe_execvpe(command, environ);
|
||||
} else {
|
||||
// Read stdout
|
||||
std::vector<unsigned char> *output = new std::vector<unsigned char>;
|
||||
|
@ -49,7 +49,15 @@ export function getDebianVersion() {
|
||||
|
||||
// Make File Executable
|
||||
export function makeExecutable(path) {
|
||||
fs.chmodSync(path, 0o755);
|
||||
fs.chmodSync(path,
|
||||
fs.constants.S_IRUSR |
|
||||
fs.constants.S_IWUSR |
|
||||
fs.constants.S_IXUSR |
|
||||
fs.constants.S_IRGRP |
|
||||
fs.constants.S_IXGRP |
|
||||
fs.constants.S_IROTH |
|
||||
fs.constants.S_IXOTH
|
||||
);
|
||||
}
|
||||
|
||||
// Get Scripts Directory
|
||||
|
Loading…
x
Reference in New Issue
Block a user