From 50f2a7029a16a8a921fc76534455081be9c33804 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 27 Feb 2026 11:13:46 +1300 Subject: [PATCH] docs: Update 2 files --- TEMPLATES.md | 39 ++++++++++++++++++++++-- source/src/commands/install.cpp | 53 +++++++++++++++++++++++---------- 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/TEMPLATES.md b/TEMPLATES.md index 632a277..ea726d1 100644 --- a/TEMPLATES.md +++ b/TEMPLATES.md @@ -11,6 +11,7 @@ template-name/ ├── template_info.env # REQUIRED: Template metadata (at root level) ├── config/ │ └── service.env # REQUIRED: Default service configuration +├── install-pre.sh # OPTIONAL: Pre-install script (runs BEFORE uninstall, e.g. docker pull) ├── install.sh # REQUIRED: Installation script ├── uninstall.sh # REQUIRED: Uninstallation script ├── status.sh # REQUIRED: Check service status (needed for 'dropshell list' command) @@ -152,7 +153,41 @@ This means: - Variables in `service.env` override those in `template_info.env` if they have the same name - Users customize deployments by editing `service.env`, never `template_info.env` -### 3. install.sh +### 3. install-pre.sh (OPTIONAL) + +Pre-install script that runs **before the old service is uninstalled**. This is useful for reducing downtime by performing slow operations (like pulling Docker images) while the old service is still running. + +**When does it run?** +- Only when updating an existing service (not on first install) +- Checked in the local template cache first (fast, no remote calls) +- Only synced and run on the remote server if the script exists in the template + +**If install-pre.sh fails**, installation continues with a warning. It is not considered a fatal error. + +```bash +#!/bin/bash +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" + +# Pull the new Docker image while the old service is still running +docker pull -q "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || echo "Warning: pre-pull failed, install.sh will retry" + +echo "Pre-install complete" +``` + +**Docker Compose example:** +```bash +#!/bin/bash +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" + +# Pull updated images while the old service is still running +docker compose -p "${CONTAINER_NAME}" pull || echo "Warning: pre-pull failed, install.sh will retry" + +echo "Pre-install complete" +``` + +### 4. install.sh Installation script that sets up the service: @@ -182,7 +217,7 @@ bash ./start.sh || _die "Failed to start container" echo "Installation of ${CONTAINER_NAME} complete" ``` -### 4. uninstall.sh +### 5. uninstall.sh Uninstallation script to remove the service (must be non-interactive and MUST preserve data): diff --git a/source/src/commands/install.cpp b/source/src/commands/install.cpp index af2047d..aa1bdff 100644 --- a/source/src/commands/install.cpp +++ b/source/src/commands/install.cpp @@ -77,23 +77,17 @@ namespace dropshell error << "Failed to install - service information not valid." << std::endl; return false; } - + if (!server_env.is_valid()) return false; // should never hit this. - + std::string user = service_info.user; std::string remote_service_path = remotepath(server,user).service(service); ASSERT(!remote_service_path.empty(), "Install_Service: Remote service path is empty for " + service + " on " + server); ASSERT(!user.empty(), "Install_Service: User is empty for " + service + " on " + server); - if (server_env.check_remote_dir_exists(remote_service_path, user)) - { // uninstall the old service before we update the config or template! - info << "Service " << service << " is already installed on " << server << std::endl; - shared_commands::uninstall_service(server_env, service); - } - maketitle("Installing " + service + " (" + service_info.template_name + ") on " + server); // Check if template exists @@ -114,13 +108,6 @@ namespace dropshell return false; } - // Sync template and config files to remote - if (!shared_commands::rsync_service_config(server_env, service, false)) - { - error << "Failed to sync service configuration to remote" << std::endl; - return false; - } - // Validate service.env matches template service.env { std::filesystem::path template_service_env = tinfo.local_template_service_env_path(); //tinfo.local_template_path() / "config" / "service.env"; @@ -157,6 +144,40 @@ namespace dropshell } } + // Run install-pre.sh if it exists in the local template cache (reduces downtime by e.g. pulling images before uninstall) + bool has_install_pre = std::filesystem::exists(tinfo.local_template_path() / "install-pre.sh"); + if (has_install_pre && server_env.check_remote_dir_exists(remote_service_path, user)) + { + info << "Running pre-install script to prepare for update..." << std::endl; + + // Sync template and config so install-pre.sh is available on remote + if (!shared_commands::rsync_service_config(server_env, service, false)) + { + error << "Failed to sync service configuration to remote for pre-install" << std::endl; + return false; + } + + shared_commands::cRemoteTempFolder remote_temp_folder(server_env, user); + if (!server_env.run_remote_template_command(service, "install-pre", {}, false, {{"TEMP_DIR", remote_temp_folder.path()}}, NULL)) + { + warning << "Pre-install script failed, continuing with install..." << std::endl; + } + } + + // Uninstall the old service (this removes the remote service directory) + if (server_env.check_remote_dir_exists(remote_service_path, user)) + { + info << "Service " << service << " is already installed on " << server << std::endl; + shared_commands::uninstall_service(server_env, service); + } + + // Sync template and config files to remote + if (!shared_commands::rsync_service_config(server_env, service, false)) + { + error << "Failed to sync service configuration to remote" << std::endl; + return false; + } + // Run install script { info << "Running " << service_info.template_name << " install script on " << server << "..." << std::endl; @@ -184,7 +205,7 @@ namespace dropshell // print health tick info << "Health: " << shared_commands::healthtick(server, service) << std::endl; - + return true; }