From ac29436f35067b01a1b3e6ff7dedbf8b1761165f Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 17 May 2025 09:43:06 +1200 Subject: [PATCH] Add update routine --- include/version.hpp | 1 + install.sh | 61 +++++++++++++++++++++++++++++ publish.sh | 95 +++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 52 +++++++++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 include/version.hpp create mode 100755 install.sh create mode 100755 publish.sh diff --git a/include/version.hpp b/include/version.hpp new file mode 100644 index 0000000..4ea83f5 --- /dev/null +++ b/include/version.hpp @@ -0,0 +1 @@ +static const char *VERSION = "1"; diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..a61cd80 --- /dev/null +++ b/install.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -e + +PROJECT="dehydrate" + +# 0. see if we were passed a folder to install to +# ----------------------------------------------------------------------------- +INSTALL_DIR="$1" +if [[ -z "$INSTALL_DIR" ]]; then + INSTALL_DIR="/usr/local/bin" +else + echo "Installing $PROJECT to $INSTALL_DIR" + if [[ ! -d "$INSTALL_DIR" ]]; then + mkdir -p "$INSTALL_DIR" + fi +fi + +# 0. see if we were passed a user to chown to +# ----------------------------------------------------------------------------- +CHOWN_USER="$2" +if [[ -z "$CHOWN_USER" ]]; then + CHOWN_USER=$USER +fi + +# 1. Determine architecture +# ----------------------------------------------------------------------------- + +ARCH=$(uname -m) +if [[ "$ARCH" == "x86_64" ]]; then + BIN=$PROJECT.amd64 +elif [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then + BIN=$PROJECT.arm64 +else + echo "Unsupported architecture: $ARCH" >&2 + exit 1 +fi + +# 3. Download the appropriate binary to a temp directory +# ----------------------------------------------------------------------------- +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT +URL="https://gitea.jde.nz/public/$PROJECT/releases/download/latest/$BIN" +echo "Downloading $BIN from $URL..." + +curl -fsSL -o "$TMPDIR/$PROJECT" "$URL" + +# 4. Make it executable +# ----------------------------------------------------------------------------- +chmod +x "$TMPDIR/$PROJECT" + +# 5. Move to /usr/local/bin +# ----------------------------------------------------------------------------- +docker run --rm -v "$TMPDIR:/tmp" -v "$INSTALL_DIR:/target" alpine sh -c "cp /tmp/$PROJECT /target/$PROJECT; chown $CHOWN_USER /target/$PROJECT" +rm "$TMPDIR/$PROJECT" + +# 6. Print success message +# ----------------------------------------------------------------------------- +echo "$PROJECT installed successfully to $INSTALL_DIR/$PROJECT (arch $ARCH)" +echo " " +echo "Update $PROJECT with:" +echo " $PROJECT -u" diff --git a/publish.sh b/publish.sh new file mode 100755 index 0000000..9e19644 --- /dev/null +++ b/publish.sh @@ -0,0 +1,95 @@ +#!/bin/bash +set -e + +# Increment version +VERFILE="include/version.hpp" +PROJECT="dehydrate" + +if [ ! -f $VERFILE ]; then + echo "$VERFILE not found!" >&2 + exit 1 +else + v=$(cat $VERFILE | grep -o 'static const char \*VERSION = "[0-9.]*";' | cut -d'"' -f2) + oldv=$v + v=$((v+1)) + echo "Incrementing version from $oldv to $v" >&2 + echo "static const char *VERSION = \"$v\";" > $VERFILE +fi +TAG="v$v" + +# Build binaries +./build.sh + +# make sure we've commited. +git add . && git commit -m "$PROJECT release $TAG" && git push + + +# Find repo info from .git/config +REPO_URL=$(git config --get remote.origin.url) +if [[ ! $REPO_URL =~ gitea ]]; then + echo "Remote origin is not a Gitea repository: $REPO_URL" >&2 + exit 1 +fi +# Extract base URL, owner, and repo +# Example: https://gitea.example.com/username/reponame.git +BASE_URL=$(echo "$REPO_URL" | sed -E 's#(https?://[^/]+)/.*#\1#') +OWNER=$(echo "$REPO_URL" | sed -E 's#.*/([^/]+)/[^/]+(\.git)?$#\1#') +REPO=$(echo "$REPO_URL" | sed -E 's#.*/([^/]+)(\.git)?$#\1#') + +API_URL="$BASE_URL/api/v1/repos/$OWNER/$REPO" + +# Check for GITEA_TOKEN_DEPLOY or GITEA_TOKEN +if [ -n "$GITEA_TOKEN_DEPLOY" ]; then + TOKEN="$GITEA_TOKEN_DEPLOY" +elif [ -n "$GITEA_TOKEN" ]; then + TOKEN="$GITEA_TOKEN" +else + echo "GITEA_TOKEN_DEPLOY or GITEA_TOKEN environment variable not set!" >&2 + exit 1 +fi + +# Create release +RELEASE_DATA=$(cat <&2 + exit 1 +fi + +# Upload binaries and install.sh +for FILE in $PROJECT.amd64 $PROJECT.arm64 install.sh; do + if [ -f "output/$FILE" ]; then + filetoupload="output/$FILE" + elif [ -f "$FILE" ]; then + filetoupload="$FILE" + else + continue + fi + + # Auto-detect content type + ctype=$(file --mime-type -b "$filetoupload") + + curl -s -X POST "$API_URL/releases/$RELEASE_ID/assets?name=$FILE" \ + -H "Content-Type: $ctype" \ + -H "Authorization: token $TOKEN" \ + --data-binary @"$filetoupload" + echo "Uploaded $FILE to release $TAG as $ctype." +done + +echo "Published $PROJECT version $v to $REPO_URL (tag $TAG) with binaries." + + diff --git a/src/main.cpp b/src/main.cpp index e0bbcab..1fd330e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,60 @@ #include #include +#include + #include "../include/argparse.hpp" #include "../include/generator.hpp" + + +std::string get_arch() +{ + // determine the architecture of the system + std::string arch; +#ifdef __aarch64__ + arch = "arm64"; +#elif __x86_64__ + arch = "amd64"; +#endif + return arch; +} + +int update() +{ + // determine path to this executable + std::filesystem::path exepath = std::filesystem::canonical("/proc/self/exe"); + std::filesystem::path parent_path = exepath.parent_path(); + std::string project_name = exepath.filename().string(); + + // determine the architecture of the system + std::string arch = get_arch(); + + std::string url = "https://gitea.jde.nz/public/"+project_name+"/releases/download/latest/"+project_name+"." + arch; + + // download new version, preserve permissions and ownership + std::string bash_script; + bash_script += "docker run --rm -v "+parent_path.string()+":/target"; + bash_script += " gitea.jde.nz/public/debian-curl:latest"; + bash_script += " sh -c \""; + bash_script += " curl -fsSL " + url + " -o /target/"+project_name+"_temp &&"; + bash_script += " chmod --reference=/target/"+project_name+" /target/"+project_name+"_temp &&"; + bash_script += " chown --reference=/target/"+project_name+" /target/"+project_name+"_temp &&"; + bash_script += " mv /target/"+project_name+"_temp /target/"+project_name; + bash_script += "\""; + + std::cout << "Updating " << exepath << " to the latest " << arch << " version." << std::endl; + + // std::cout << "bash_script: " << std::endl + // << bash_script << std::endl; + + // run the bash script + execlp("bash", "bash", "-c", bash_script.c_str(), (char *)nullptr); + std::cerr << "Failed to execute command." << std::endl; + return -1; +} + + + int main(int argc, char* argv[]) { try { Args args = parse_args(argc, argv);