From efd11657ace5305b96b60dffa4ac348f9302bf5c Mon Sep 17 00:00:00 2001 From: j842 Date: Sun, 17 Aug 2025 21:01:38 +1200 Subject: [PATCH] docs: Add 6, update 4 and remove 2 files --- .gitea/workflows/buildtestpublish.yaml | 43 ++++++++++++ .gitea/workflows/test.yaml | 39 ----------- CLAUDE.md | 96 ++++++++++++++++++++++++++ Dockerfile.dropshell-build | 57 +++++++++++++++ README.md | 4 +- build.sh | 37 ++++++++++ dropshell-install.sh | 8 ++- publish.sh | 46 ++++++++++++ source/build_local.sh | 17 +++++ source/build_native.sh | 53 -------------- source/build_production.sh | 43 ++++-------- source/publish.sh | 59 ++-------------- 12 files changed, 323 insertions(+), 179 deletions(-) create mode 100644 .gitea/workflows/buildtestpublish.yaml delete mode 100644 .gitea/workflows/test.yaml create mode 100644 CLAUDE.md create mode 100644 Dockerfile.dropshell-build create mode 100755 build.sh create mode 100755 publish.sh create mode 100755 source/build_local.sh delete mode 100755 source/build_native.sh diff --git a/.gitea/workflows/buildtestpublish.yaml b/.gitea/workflows/buildtestpublish.yaml new file mode 100644 index 0000000..39b1820 --- /dev/null +++ b/.gitea/workflows/buildtestpublish.yaml @@ -0,0 +1,43 @@ +name: Build-Test-Publish +run-name: Build test and publish dropshell +on: [push] + +defaults: + run: + shell: bash + +jobs: + build: + strategy: + matrix: + platform: + - linux/amd64 + - linux/arm64 + runs-on: ${{ matrix.platform }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Login to Gitea + uses: docker/login-action@v3 + with: + registry: gitea.jde.nz + username: DoesntMatter + password: ${{ secrets.DOCKER_PUSH_TOKEN }} + + - name: Build + run: | + ./build.sh + + - name: Run Tests + run: | + cd source && ./test.sh + + - name: Publish as Latest + run: | + # Only publish on main branch + if [ "$GITHUB_REF" = "refs/heads/main" ]; then + SOS_WRITE_TOKEN=${{ secrets.SOS_WRITE_TOKEN }} \ + ./publish.sh + fi + diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml deleted file mode 100644 index 1e737dd..0000000 --- a/.gitea/workflows/test.yaml +++ /dev/null @@ -1,39 +0,0 @@ -name: Dropshell Test -run-name: Test dropshell -on: [push] - -jobs: - Build_and_Test: - runs-on: ubuntu-latest - steps: - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y openssh-server - - name: Check out repository code - uses: actions/checkout@v4 - - name: Install build dependencies - run: | - cd source - ./install_build_prerequisites.sh - - name: Build Native - run: | - cd source - ./build_native.sh - - name: Test - run: | - cd source - ./test.sh - - name: Build Production - run: | - cd source - ./build_production.sh - - name: Test - run: | - cd source - ./test.sh - - name: Publish - run: | - cd source - ./publish.sh - diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..49c5ada --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,96 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Dropshell is a system management tool for server operations, written in C++. It provides a command-line interface for managing remote servers and services through Docker containers. + +## Build Commands + +The build system uses Docker for consistent, reproducible builds across all environments. + +### Development Build (Debug) +```bash +# From repository root +./build.sh +# Or with environment variables +CMAKE_BUILD_TYPE=Debug INSTALL_LOCAL=true ./build.sh +``` +This builds a debug version using Docker and optionally installs to ~/.local/bin/dropshell + +### Production Build (Release) +```bash +CMAKE_BUILD_TYPE=Release ./build.sh +``` + +### Legacy Build Scripts +The scripts in `source/build_native.sh` and `source/build_production.sh` now call the Docker-based build system. + +### Build Options +- `CMAKE_BUILD_TYPE`: Debug or Release (default: Debug) +- `INSTALL_LOCAL`: true/false - auto-install to ~/.local/bin (default: true) +- `NO_CACHE`: true/false - disable Docker build cache (default: false) + +### Running Tests +```bash +cd source && ./test.sh +``` + +### Publishing Release +```bash +cd source && ./publish.sh +``` + +## Architecture + +### Core Components + +- **Command System**: Commands are registered through CommandRegistry (src/commands/command_registry.hpp). Each command has a handler function and optional autocomplete support. Commands can require config and/or agent installation. + +- **Service Management**: Services are managed through LocalServiceInfo structures (src/services.hpp). Services are created from templates and installed on remote servers via Docker containers. + +- **Server Management**: Servers represent remote hosts that run services. Server configuration includes SSH connection details and installed services. + +- **Template System**: Templates define the structure for services. They are loaded from template sources and used to create new service instances. + +- **Agent Components**: + - Local agent (agent-local/): Installed on the developer's machine + - Remote agent (agent-remote/): Installed on remote servers to manage services + +### Key Design Patterns + +- **Singleton Configuration**: Global configuration accessed through gConfig() +- **Command Registry Pattern**: All commands register themselves with the central registry +- **Static Binary Distribution**: Uses musl-libc for fully static executables that work across Linux distributions + +### Important Files + +- **src/main.cpp**: Entry point, handles command dispatch and validation +- **src/config.cpp/hpp**: Configuration management +- **src/services.cpp/hpp**: Service operations and metadata +- **src/servers.cpp/hpp**: Server management functions +- **src/templates.cpp/hpp**: Template loading and management +- **cmake_prebuild.sh**: Generates autogen files before build + +### Build System + +- Uses CMake with Ninja generator +- Fetches dependencies: libassert, cpptrace, nlohmann/json +- Generates version info from timestamps +- Creates fully static binaries using musl cross-compilation toolchain + +## Common Operations + +### Adding a New Command +1. Create command implementation in src/commands/ +2. Register with CommandRegistry including handler, help text, and argument requirements +3. Add autocomplete handler if needed + +### Modifying Service Behavior +- Service definitions are in src/services.cpp +- Remote agent scripts in source/agent-remote/ handle service operations on servers + +### Working with Templates +- Templates are loaded from configured sources by TemplateManager +- Template metadata and files are cached locally \ No newline at end of file diff --git a/Dockerfile.dropshell-build b/Dockerfile.dropshell-build new file mode 100644 index 0000000..d073459 --- /dev/null +++ b/Dockerfile.dropshell-build @@ -0,0 +1,57 @@ +ARG IMAGE_TAG +FROM gitea.jde.nz/public/dropshell-build-base:latest AS builder + +ARG PROJECT=dropshell +ARG CMAKE_BUILD_TYPE=Debug + +# Set working directory +WORKDIR /app + +# Create cache directories +RUN mkdir -p /ccache + +# Set up ccache +ENV CCACHE_DIR=/ccache +ENV CCACHE_COMPILERCHECK=content +ENV CCACHE_MAXSIZE=2G + +# Copy source files and build scripts +COPY source/src/ src/ +COPY source/agent-local/ agent-local/ +COPY source/agent-remote/ agent-remote/ +COPY source/CMakeLists.txt ./ +COPY source/cmake_prebuild.sh ./ + +# Configure project (this step is cached unless CMakeLists.txt changes) +RUN --mount=type=cache,target=/ccache \ + --mount=type=cache,target=/build \ + mkdir -p /build && \ + cmake -G Ninja -S /app -B /build \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold -static -g" \ + -DCMAKE_CXX_FLAGS="-g -fno-omit-frame-pointer" \ + -DCMAKE_C_FLAGS="-g -fno-omit-frame-pointer" \ + -DPROJECT_NAME="${PROJECT}" \ + -DCMAKE_STRIP=OFF \ + ${CMAKE_TOOLCHAIN_FILE:+-DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE} + +# Build project (ccache will help here when only some files change) +RUN --mount=type=cache,target=/ccache \ + --mount=type=cache,target=/build \ + cmake --build /build + +# Copy the built executables to a regular directory for the final stage +RUN --mount=type=cache,target=/build \ + mkdir -p /output && \ + find /build -type f -executable -name "*${PROJECT}*" -exec cp {} /output/${PROJECT} \; || \ + find /build -type f -executable -exec cp {} /output/${PROJECT} \; + +# Final stage that only contains the binaries +FROM scratch AS project + +ARG PROJECT=dropshell + +# Copy the actual binaries from the regular directory +COPY --from=builder /output/${PROJECT} /${PROJECT} \ No newline at end of file diff --git a/README.md b/README.md index a6e1d67..ee6adad 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A system management tool for server operations, written in C++. ## Installation ``` -curl -fsSL https://gitea.jde.nz/public/dropshell/releases/download/latest/install.sh | bash +curl -fsSL https://getbin.xyz/dropshell-install | bash ``` This installs as dropshell for the local user, with a symbolic link ds. @@ -23,7 +23,7 @@ to configure dropshell and install the local components. Auto setup script which creates a dropshell user, and includes installing docker if not already present: ``` -curl -fsSL https://gitea.jde.nz/public/dropshell/releases/download/latest/server_autosetup.sh | sudo bash +curl -fsSL https://getbin.xyz/dropshell-server-autosetup | sudo bash ``` Manual steps: diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b59e07f --- /dev/null +++ b/build.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +set -euo pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +PROJECT="dropshell" + +rm -rf "${SCRIPT_DIR}/output" +mkdir -p "${SCRIPT_DIR}/output" + +# make sure we have the latest base image. +docker pull gitea.jde.nz/public/dropshell-build-base:latest + +# Build with or without cache based on NO_CACHE environment variable +CACHE_FLAG="" +if [ "${NO_CACHE:-false}" = "true" ]; then + CACHE_FLAG="--no-cache" +fi + +docker build \ + ${CACHE_FLAG} \ + -t "${PROJECT}-build" \ + -f "${SCRIPT_DIR}/Dockerfile.dropshell-build" \ + --build-arg PROJECT="${PROJECT}" \ + --build-arg CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Debug}" \ + --output "${SCRIPT_DIR}/output" \ + "${SCRIPT_DIR}" + +# Auto-install dropshell locally if requested +if [ "${INSTALL_LOCAL:-true}" = "true" ]; then + INSTALL_DIR="${HOME}/.local/bin" + mkdir -p "${INSTALL_DIR}" + cp "${SCRIPT_DIR}/output/dropshell" "${INSTALL_DIR}/dropshell" + echo "Dropshell installed to ${INSTALL_DIR}/dropshell" +fi + +echo "Build process completed!" \ No newline at end of file diff --git a/dropshell-install.sh b/dropshell-install.sh index 8c58b95..b21e6d8 100755 --- a/dropshell-install.sh +++ b/dropshell-install.sh @@ -11,7 +11,13 @@ ARCH=$(uname -m) TARGET_PATH="${HOME}/.local/bin/dropshell" [ ! -f "${TARGET_PATH}" ] || rm -f "${TARGET_PATH}" mkdir -p "$(dirname "${TARGET_PATH}")" -curl -L -s -o "${TARGET_PATH}" "https://getbin.xyz/dropshell.${ARCH}" || die "Failed to download dropshell for ${ARCH}" + +# Download using the latest-${ARCH} tag format +echo "Downloading dropshell for ${ARCH}..." +curl -L -s -o "${TARGET_PATH}" "https://getbin.xyz/dropshell:latest-${ARCH}" || { + echo "Failed to download dropshell for ${ARCH}" >&2 + exit 1 +} chmod +x "${TARGET_PATH}" echo "dropshell installed successfully to $TARGET_PATH" echo " " diff --git a/publish.sh b/publish.sh new file mode 100755 index 0000000..6233342 --- /dev/null +++ b/publish.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +ARCH=$(uname -m) +TEMP_DIR="${SCRIPT_DIR}/temp" +SOS="${TEMP_DIR}/sos" +PROJECT="dropshell" + +echo "Publishing dropshell to getbin.xyz" + +function die() { + echo "error: $1" + exit 1 +} + +[[ -n ${SOS_WRITE_TOKEN:-} ]] || die "SOS_WRITE_TOKEN not specified" + +# clear output dir +rm -rf "${SCRIPT_DIR}/output" +mkdir -p "${SCRIPT_DIR}/output" + +# build release version +export CMAKE_BUILD_TYPE="Release" +export INSTALL_LOCAL="false" +"${SCRIPT_DIR}/build.sh" + +[ -f "${SCRIPT_DIR}/output/dropshell" ] || die "Build failed." + +# download the sos binary +mkdir -p "${TEMP_DIR}" +trap 'rm -rf "${TEMP_DIR}"' EXIT + +curl -L -o "${SOS}" "https://getbin.xyz/sos" +chmod +x "${SOS}" + +# upload arch-specific binary +"${SOS}" upload "getbin.xyz" "${SCRIPT_DIR}/output/${PROJECT}" "${PROJECT}:latest-${ARCH}" + +# upload generic install script +"${SOS}" upload "getbin.xyz" "${SCRIPT_DIR}/dropshell-install.sh" "dropshell-install:latest" + +# upload server auto-setup script +"${SOS}" upload "getbin.xyz" "${SCRIPT_DIR}/dropshell-server-autosetup.sh" "dropshell-server-autosetup:latest" + +echo "Successfully published dropshell:latest-${ARCH} to getbin.xyz" \ No newline at end of file diff --git a/source/build_local.sh b/source/build_local.sh new file mode 100755 index 0000000..d8e3bee --- /dev/null +++ b/source/build_local.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# This script now uses Docker for consistent builds +# For direct builds, use the Docker-based build.sh script in the parent directory + +set -euo pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +PARENT_DIR="$(dirname "${SCRIPT_DIR}")" + +echo "Using Docker-based build system..." +echo "Building debug version for native architecture..." + +# Run the Docker build with debug settings +CMAKE_BUILD_TYPE=Debug INSTALL_LOCAL=true "${PARENT_DIR}/build.sh" + +echo "Build process completed!" diff --git a/source/build_native.sh b/source/build_native.sh deleted file mode 100755 index 3042e97..0000000 --- a/source/build_native.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -OUTPUT_DIR=${SCRIPT_DIR}/output -INSTALL_DIR=${HOME}/.local/bin -mkdir -p "${OUTPUT_DIR}" - -# Exit on error -set -euo pipefail - -ARCH=$(uname -m) -if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ]; then - echo "Unsupported architecture: $ARCH" - exit 1 -fi - - -function build_native() { - local BUILDDIR=${SCRIPT_DIR}/build/native - local PREVDIR=$PWD - local JOBS; - JOBS=$(nproc) # Set JOBS to the number of available CPU cores - mkdir -p "${BUILDDIR}" - cd "${SCRIPT_DIR}" || exit 1 - - CC="${HOME}/.musl-cross/${ARCH}-linux-musl-native/bin/${ARCH}-linux-musl-gcc" - CXX="${HOME}/.musl-cross/${ARCH}-linux-musl-native/bin/${ARCH}-linux-musl-g++" - - - cmake -B "${BUILDDIR}" -G Ninja \ - -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - -DCMAKE_LINKER=mold \ - -DCMAKE_C_COMPILER="${CC}" \ - -DCMAKE_CXX_COMPILER="${CXX}" - - cd "${BUILDDIR}" || exit 1 - ninja -j"$JOBS" - - #upx ${BUILDDIR}/dropshell - cp "${BUILDDIR}/dropshell" "${OUTPUT_DIR}/dropshell.${ARCH}" - - cd "${PREVDIR}" || exit 1 -} - -build_native - - -echo "Auto-installing dropshell locally..." -mkdir -p "${INSTALL_DIR}" -cp "${OUTPUT_DIR}/dropshell.${ARCH}" "${INSTALL_DIR}/dropshell" -echo "Build process completed!" diff --git a/source/build_production.sh b/source/build_production.sh index 2d7948c..b67d7ff 100755 --- a/source/build_production.sh +++ b/source/build_production.sh @@ -1,37 +1,18 @@ #!/bin/bash + +# This script now uses Docker for consistent builds +# For direct builds, use the Docker-based build.sh script in the parent directory + set -euo pipefail + SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +PARENT_DIR="$(dirname "${SCRIPT_DIR}")" -# Create output directory -mkdir -p "${SCRIPT_DIR}/output" -PREV_DIR=$(pwd) -cd "${SCRIPT_DIR}" -trap 'cd "${PREV_DIR}"' EXIT +echo "Using Docker-based build system..." +echo "Building release version for production..." -function build_arch() { - local arch=$1 +# Run the Docker build with release settings +CMAKE_BUILD_TYPE=Release INSTALL_LOCAL=false "${PARENT_DIR}/build.sh" - if [ ! -f "${HOME}/.musl-cross/${arch}-linux-musl-cross/bin/${arch}-linux-musl-c++" ]; then - echo "Musl cross compiler for ${arch} not found. Please run install_build_prerequisites.sh first." - exit 1 - fi - - CMAKE_BUILD_TYPE=Release - CC="${HOME}/.musl-cross/${arch}-linux-musl-cross/bin/${arch}-linux-musl-gcc" - CXX="${HOME}/.musl-cross/${arch}-linux-musl-cross/bin/${arch}-linux-musl-g++" - - BUILDDIR="${SCRIPT_DIR}/build/${arch}" - mkdir -p "${BUILDDIR}" - - cmake -B "${BUILDDIR}" -G Ninja -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" -DCMAKE_C_COMPILER="${CC}" -DCMAKE_CXX_COMPILER="${CXX}" - cmake --build "${BUILDDIR}" - - upx "${BUILDDIR}/dropshell" - cp "${BUILDDIR}/dropshell" "${SCRIPT_DIR}/output/dropshell.${arch}" -} - -build_arch x86_64 -build_arch aarch64 - -echo "Static binaries have been created:" -ls -la output +echo "Static binary has been created in ${PARENT_DIR}/output/" +ls -la "${PARENT_DIR}/output" diff --git a/source/publish.sh b/source/publish.sh index 7d46fb4..53b3da5 100755 --- a/source/publish.sh +++ b/source/publish.sh @@ -1,57 +1,10 @@ #!/bin/bash -set -e -# directory of this script -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -echo "Script directory: $SCRIPT_DIR" +# Legacy publish script - redirects to new publish.sh in repository root +set -euo pipefail -# Check for GITEA_TOKEN_DEPLOY or GITEA_TOKEN -TOKEN="${RELEASE_WRITE_TOKEN:-${GITEA_TOKEN_DEPLOY}}" -[ -z "$TOKEN" ] && { echo "Neither RELEASE_WRITE_TOKEN nor GITEA_TOKEN_DEPLOY environment variable set!" >&2; exit 1; } +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +PARENT_DIR="$(dirname "${SCRIPT_DIR}")" -OLD_PWD="$PWD" -cd "$SCRIPT_DIR" || exit 1 -TEMP_DIR=$(mktemp -d) -trap 'rm -rf "$TEMP_DIR" && cd "$OLD_PWD"' EXIT - -ARCH=$(uname -m) -TAG=$("$SCRIPT_DIR/output/dropshell.${ARCH}" --version) -[ -z "$TAG" ] && echo "Failed to get version from dropshell.${ARCH}" >&2 && exit 1 -echo "Publishing dropshell version $TAG" - - -function die() { - echo "$@" >&2 - exit 1 -} - -# Function to find file in specified locations -find_file() { - local filename="$1" - shift # remove filename from args - local locations=("$@") # grab the rest of the args as locations - - for loc in "${locations[@]}"; do - if [ -f "$loc/$filename" ]; then - echo "$loc/$filename" - return 0 # Found the file, return success - fi - done - echo "" # Return empty string if not found - return 1 -} - -curl -L -s -o "${TEMP_DIR}/sos" "https://getbin.xyz/sos" || die "Failed to download sos" -chmod +x "${TEMP_DIR}/sos" - - -# Upload binaries and install.sh -for FILE in dropshell.x86_64 dropshell.aarch64 dropshell-install.sh dropshell-server-autosetup.sh; do - # Pass the locations directly to the find_file function - filetoupload=$(find_file "$FILE" "output" "../" ".") - [ -z "$filetoupload" ] && die "File $FILE not found in expected locations!" - - "${TEMP_DIR}/sos" upload getbin.xyz "$filetoupload" "$FILE:latest" "$FILE:TAG" -done - -echo "Published dropshell $TAG to getbin.xyz" +echo "Redirecting to new publish.sh in repository root..." +exec "${PARENT_DIR}/publish.sh" "$@"