From e055f92f7388901d8edf4ab7c5ccf577daca63d4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 15 Jun 2025 15:00:21 +1200 Subject: [PATCH] 'Generic Commit' --- .gitea/workflows/buildtestpublish.yaml | 55 ++++++++++++++--- build.sh | 3 +- publish.sh | 82 ++++++++----------------- src/assert.hpp | 85 ++++++++++++++++++++++---- test.sh | 4 +- testing/compose.yaml | 2 +- 6 files changed, 151 insertions(+), 80 deletions(-) diff --git a/.gitea/workflows/buildtestpublish.yaml b/.gitea/workflows/buildtestpublish.yaml index 3c5a882..51b62ca 100644 --- a/.gitea/workflows/buildtestpublish.yaml +++ b/.gitea/workflows/buildtestpublish.yaml @@ -9,12 +9,13 @@ defaults: jobs: build: - runs-on: ubuntu-latest + strategy: + matrix: + platform: + - linux/amd64 + - linux/arm64 + runs-on: ${{ matrix.platform }} steps: - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - name: Checkout uses: actions/checkout@v4 - name: Login to Gitea @@ -23,12 +24,48 @@ jobs: registry: gitea.jde.nz username: DoesntMatter password: ${{ secrets.DOCKER_PUSH_TOKEN }} - - name: Build + - name: Build run: | ./build.sh - - name: Test + - name: Run Tests run: | ./test.sh - - name: Publish + - name: Publish as Latest run: | - SOS_WRITE_TOKEN=${{ secrets.SOS_WRITE_TOKEN }} ./publish.sh + # Only publish on main branch + if [ "$GITHUB_REF" = "refs/heads/main" ]; then + SOS_WRITE_TOKEN=${{ secrets.SOS_WRITE_TOKEN }} \ + DOCKER_PUSH_TOKEN=${{ secrets.DOCKER_PUSH_TOKEN }} \ + ./publish.sh + fi + + create-manifest: + needs: [build] + runs-on: ubuntu-latest + steps: + - name: Login to Gitea + uses: docker/login-action@v3 + with: + registry: gitea.jde.nz + username: DoesntMatter + password: ${{ secrets.DOCKER_PUSH_TOKEN }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y jq + + - name: Create and push manifest list + run: | + # Only create manifest on main branch + if [ "$GITHUB_REF" = "refs/heads/main" ]; then + # Create the manifest list using the architecture-specific digests + docker manifest create gitea.jde.nz/public/simple-object-server:latest \ + --amend gitea.jde.nz/public/simple-object-server:latest-x86_64 \ + --amend gitea.jde.nz/public/simple-object-server:latest-aarch64 + + # Push the manifest list + docker manifest push gitea.jde.nz/public/simple-object-server:latest + + echo "Manifest list created and pushed successfully" + fi diff --git a/build.sh b/build.sh index 54a9606..1e90fa1 100755 --- a/build.sh +++ b/build.sh @@ -16,11 +16,10 @@ PROJECT="simple-object-server" export DOCKER_BUILDKIT=1 docker build \ - -t "gitea.jde.nz/public/${PROJECT}-build:latest" \ + -t "${PROJECT}-build" \ -f "${SCRIPT_DIR}/Dockerfile.dropshell-build" \ --build-arg PROJECT="${PROJECT}" \ --build-arg CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ --output "${SCRIPT_DIR}/output" \ "${SCRIPT_DIR}" -# --platform linux/amd64 \ diff --git a/publish.sh b/publish.sh index ad2c391..37032a4 100755 --- a/publish.sh +++ b/publish.sh @@ -6,69 +6,29 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" echo "Publishing simple-object-server to gitea.jde.nz/public/simple-object-server:latest" -PROJECT="simple-object-server" -[[ -n $SOS_WRITE_TOKEN ]] || die "SOS_WRITE_TOKEN not specified" - -# FUNCTIONS -function title() { - echo "----------------------------------------" - # Center the text - local text="$1" - local line_length=40 - local text_length=${#text} - local padding=$(( (line_length - text_length) / 2 )) - printf "%*s%s%*s\n" $padding "" "$text" $padding "" - echo "----------------------------------------" -} - - function die() { title "error: $1" exit 1 } -# Create buildx builder if it doesn't exist -if ! docker buildx ls | grep -q "${PROJECT}-multiarch"; then - docker buildx create --name ${PROJECT}-multiarch --use \ - --driver-opt env.BUILDKIT_MAX_PARALLELISM=4 -else - docker buildx use ${PROJECT}-multiarch -fi +[[ -n $SOS_WRITE_TOKEN ]] || die "SOS_WRITE_TOKEN not specified" -function build() { - local PLATFORM="$1" - # convert linux/amd64 to linux-amd64, windows/amd64 to windows-amd64, etc. - local OSARCH="${PLATFORM//\//-}" - - title "Building ${PROJECT} for ${PLATFORM}" - - docker buildx build \ - -t "gitea.jde.nz/public/${PROJECT}-build:latest" \ - -f "${SCRIPT_DIR}/Dockerfile.dropshell-build" \ - --build-arg PROJECT="${PROJECT}" \ - --build-arg CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ - --output "${SCRIPT_DIR}/output/${OSARCH}" \ - --platform "${PLATFORM}" \ - --cache-from type=local,src=/tmp/.buildx-cache \ - --cache-to type=local,dest=/tmp/.buildx-cache,mode=max \ - "${SCRIPT_DIR}" - - mv "${SCRIPT_DIR}/output/${OSARCH}/${PROJECT}" "${SCRIPT_DIR}/output/${PROJECT}-${OSARCH}" - rm -rf "${SCRIPT_DIR}/output/${OSARCH}" -} - -CMAKE_BUILD_TYPE="Release" - -rm -rf "${SCRIPT_DIR}/output" +rm -rf "${SCRIPT_DIR}/output" mkdir -p "${SCRIPT_DIR}/output" -mkdir -p /tmp/.buildx-cache -build "linux/amd64" -build "linux/arm64" +export CMAKE_BUILD_TYPE="Release" +export PROJECT="simple-object-server" -[ -f "${SCRIPT_DIR}/output/${PROJECT}-linux-amd64" ] || die "${PROJECT}-linux-amd64 not found" -[ -f "${SCRIPT_DIR}/output/${PROJECT}-linux-arm64" ] || die "${PROJECT}-linux-arm64 not found" +docker build \ + -t "${PROJECT}-build" \ + -f "${SCRIPT_DIR}/Dockerfile.dropshell-build" \ + --build-arg PROJECT="${PROJECT}" \ + --build-arg CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + --output "${SCRIPT_DIR}/output" \ + "${SCRIPT_DIR}" + +[ -f ${SCRIPT_DIR}/output/simple-object-server ] || die "Build failed." # uplaod to getbin.xyz @@ -80,7 +40,19 @@ trap 'rm -rf "${SCRIPT_DIR}/temp"' EXIT curl -L -o "${SOS}" "https://getbin.xyz/sos" chmod +x "${SOS}" -"${SOS}" upload "getbin.xyz" "${SCRIPT_DIR}/output/${PROJECT}-linux-amd64" "${PROJECT}:amd64" -"${SOS}" upload "getbin.xyz" "${SCRIPT_DIR}/output/${PROJECT}-linux-arm64" "${PROJECT}:arm64" +ARCH=$(uname -m) +# upload arch-specific binary +"${SOS}" upload "getbin.xyz" "${SCRIPT_DIR}/output/${PROJECT}" "${PROJECT}:latest-${ARCH}" + +# upload generic install script (ok if multiple times as we iterate through arch's) "${SOS}" upload "getbin.xyz" "${SCRIPT_DIR}/install.sh" "simple-object-server-install:latest" + +# create and upload the arch-specific docker image +IMAGE_NAME="gitea.jde.nz/public/simple-object-server:latest-${ARCH}" +docker build \ + -f "${SCRIPT_DIR}/Dockerfile.sos" \ + -t "${IMAGE_NAME}" \ + "${SCRIPT_DIR}" + +docker push "${IMAGE_NAME}" diff --git a/src/assert.hpp b/src/assert.hpp index 016dba6..eb421dd 100644 --- a/src/assert.hpp +++ b/src/assert.hpp @@ -109,8 +109,24 @@ std::vector get_source_info_batch(const char* executable, const std: return results; } +// Helper function to get the program counter (return address) in a portable way +inline void* get_pc() { + void* pc; + #if defined(__aarch64__) || defined(__arm__) + // For ARM/AArch64, we can use the link register (x30 on AArch64, r14 on ARM) + #if defined(__aarch64__) + asm volatile ("mov %0, x30" : "=r" (pc)); + #else + asm volatile ("mov %0, lr" : "=r" (pc)); + #endif + #else + // For x86/x86_64, use __builtin_return_address + pc = __builtin_return_address(0); + #endif + return pc; +} + void print_stacktrace() { - // Simple stacktrace implementation that doesn't segfault std::cerr << colors::yellow << "Stack trace:" << colors::reset << "\n"; char exe_path[1024] = {0}; @@ -121,19 +137,29 @@ void print_stacktrace() { exe_path[count] = '\0'; } - // Get just a couple of stack frames safely std::vector addresses; - void* addr1 = __builtin_return_address(1); // assert_failed - void* addr2 = __builtin_return_address(2); // caller of ASSERT + // Get current frame pointer and return address + void* frame_ptr = __builtin_frame_address(0); + void* return_addr = get_pc(); - // Adjust addresses to point to call site instead of return address - // Subtract a small offset to get the call instruction instead of the return address - if (addr1) { - addresses.push_back(static_cast(addr1) - 1); + // Add current return address + if (return_addr) { + addresses.push_back(return_addr); } - if (addr2) { - addresses.push_back(static_cast(addr2) - 1); + + // Try to walk up the stack if we have a valid frame pointer + if (frame_ptr) { + void** frame = static_cast(frame_ptr); + // Limit the number of frames to prevent potential issues + for (int i = 0; i < 5; ++i) { + // The return address is typically at frame[1] + if (frame[1] == nullptr) break; + addresses.push_back(frame[1]); + // Move to the next frame + if (frame[0] == nullptr) break; + frame = static_cast(frame[0]); + } } if (addresses.empty()) { @@ -144,18 +170,55 @@ void print_stacktrace() { // Get source info std::vector results = get_source_info_batch(exe_path, addresses); + // Filter out frames from assert.hpp + results.erase(std::remove_if(results.begin(), results.end(), + [](const SourceInfo& frame) { + if (frame.file.empty()) return false; + std::string_view file(frame.file); + return file.find("assert.hpp") != std::string_view::npos; + }), + results.end() + ); + + if (results.empty()) { + std::cerr << " " << colors::red << "[no user frames available]" << colors::reset << "\n"; + return; + } + + // First pass: find the maximum function name length for alignment + size_t max_func_length = 0; + for (const auto& frame : results) { + size_t length = frame.function.empty() + ? strlen("[unknown function]") + : frame.function.length(); + if (length > max_func_length) { + max_func_length = length; + } + } + + // Second pass: print with aligned output for (size_t i = 0; i < results.size(); ++i) { const auto& frame = results[i]; std::cerr << " "; + // Print function name with alignment + size_t current_length; if (!frame.function.empty()) { std::cerr << colors::green << frame.function << colors::reset; + current_length = frame.function.length(); } else { std::cerr << colors::red << "[unknown function]" << colors::reset; + current_length = strlen("[unknown function]"); } + // Add padding to align the file/line text if (!frame.file.empty() && frame.line > 0) { - std::cerr << " at " << colors::blue << frame.file << colors::reset + // Calculate padding (minimum 1 space) + size_t padding = (max_func_length > current_length) + ? (max_func_length - current_length + 1) + : 1; + std::cerr << std::string(padding, ' ') + << " " << colors::blue << frame.file << colors::reset << ":" << colors::magenta << frame.line << colors::reset; } diff --git a/test.sh b/test.sh index c741688..4954604 100755 --- a/test.sh +++ b/test.sh @@ -5,9 +5,9 @@ set -euo pipefail SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -docker buildx build --load \ +docker build \ -f "${SCRIPT_DIR}/Dockerfile.sos" \ - -t gitea.jde.nz/public/simple-object-server:test \ + -t simple-object-server:test \ "${SCRIPT_DIR}" diff --git a/testing/compose.yaml b/testing/compose.yaml index fa88e6b..f2563ee 100644 --- a/testing/compose.yaml +++ b/testing/compose.yaml @@ -1,6 +1,6 @@ services: sos: - image: gitea.jde.nz/public/simple-object-server:test + image: simple-object-server:test container_name: sos-test network_mode: "host" ports: