build.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #!/usr/bin/env bash
  2. #
  3. # packaging/build.sh — Top-level build orchestrator for smartbotic-database
  4. #
  5. # Supports two modes:
  6. # Default: Docker build targeting Debian 13 (production .debs)
  7. # --local: Native build on current system (development/testing)
  8. #
  9. set -euo pipefail
  10. # ---------------------------------------------------------------------------
  11. # Paths
  12. # ---------------------------------------------------------------------------
  13. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  14. PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
  15. BASE_IMAGE_NAME="smartbotic-db-build-base:debian13"
  16. BUILD_JOBS="${BUILD_JOBS:-$(nproc)}"
  17. DEB_REPO_DIR="${DEB_REPO_DIR:-/data/smartbotics-deb-repo}"
  18. # Output directories — separate to prevent local/Docker overwriting each other
  19. # dist/local/ — native build for current system
  20. # dist/debian13/ — Docker build for Debian 13 production
  21. DIST_BASE="${PROJECT_DIR}/dist"
  22. VERSION="$(cat "$PROJECT_DIR/VERSION")"
  23. GIT_COMMIT="$(git -C "$PROJECT_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown")"
  24. # ---------------------------------------------------------------------------
  25. # Defaults
  26. # ---------------------------------------------------------------------------
  27. LOCAL_MODE=0
  28. INSTALL_AFTER=0
  29. SKIP_TESTS=0
  30. DEB_REVISION=1
  31. REBUILD_BASE=0
  32. NO_CACHE=0
  33. DO_REPO=0
  34. SUITE=""
  35. DO_SYNC=0
  36. # ---------------------------------------------------------------------------
  37. # Colored logging
  38. # ---------------------------------------------------------------------------
  39. log_info() { printf '\033[1;34m[INFO]\033[0m %s\n' "$*"; }
  40. log_success() { printf '\033[1;32m[OK]\033[0m %s\n' "$*"; }
  41. log_warn() { printf '\033[1;33m[WARN]\033[0m %s\n' "$*"; }
  42. log_error() { printf '\033[1;31m[ERROR]\033[0m %s\n' "$*"; }
  43. # ---------------------------------------------------------------------------
  44. # Usage
  45. # ---------------------------------------------------------------------------
  46. usage() {
  47. cat <<EOF
  48. Usage: packaging/build.sh [OPTIONS]
  49. Build modes:
  50. (default) Docker build targeting Debian 13 (production)
  51. --local Native build for current system (development)
  52. Options:
  53. --install Install .debs locally after build (--local only)
  54. --skip-tests Skip running tests
  55. --deb-revision N Debian revision suffix (default: 1)
  56. --rebuild-base Force rebuild Docker base image
  57. --no-cache Build without Docker caches
  58. --repo Add packages to local APT repository
  59. --suite NAME Distribution suite (e.g., trixie)
  60. --sync Add to repo and upload to repository.smartbotics.ai
  61. Environment variables:
  62. BUILD_JOBS Parallel build jobs (default: nproc)
  63. DEB_REPO_DIR Path to smartbotics-deb-repo (default: /data/smartbotics-deb-repo)
  64. EOF
  65. exit 0
  66. }
  67. # ---------------------------------------------------------------------------
  68. # Argument parsing
  69. # ---------------------------------------------------------------------------
  70. while [[ $# -gt 0 ]]; do
  71. case "$1" in
  72. --local) LOCAL_MODE=1; shift ;;
  73. --install) INSTALL_AFTER=1; shift ;;
  74. --skip-tests) SKIP_TESTS=1; shift ;;
  75. --deb-revision) DEB_REVISION="$2"; shift 2 ;;
  76. --rebuild-base) REBUILD_BASE=1; shift ;;
  77. --no-cache) NO_CACHE=1; shift ;;
  78. --repo) DO_REPO=1; shift ;;
  79. --suite) SUITE="$2"; shift 2 ;;
  80. --sync) DO_SYNC=1; shift ;;
  81. -h|--help) usage ;;
  82. *)
  83. log_error "Unknown option: $1"
  84. usage
  85. ;;
  86. esac
  87. done
  88. # Validate option combinations
  89. if [[ $INSTALL_AFTER -eq 1 && $LOCAL_MODE -eq 0 ]]; then
  90. log_error "--install is only supported with --local"
  91. exit 1
  92. fi
  93. if [[ $DO_SYNC -eq 1 && $DO_REPO -eq 0 ]]; then
  94. DO_REPO=1
  95. fi
  96. if [[ $DO_REPO -eq 1 && -z "$SUITE" ]]; then
  97. log_error "--repo requires --suite NAME"
  98. exit 1
  99. fi
  100. # ---------------------------------------------------------------------------
  101. # Set output directory based on mode
  102. # ---------------------------------------------------------------------------
  103. if [[ $LOCAL_MODE -eq 1 ]]; then
  104. OUTPUT_DIR="${DIST_BASE}/local"
  105. else
  106. OUTPUT_DIR="${DIST_BASE}/debian13"
  107. fi
  108. mkdir -p "$OUTPUT_DIR"
  109. # ---------------------------------------------------------------------------
  110. # Banner
  111. # ---------------------------------------------------------------------------
  112. log_info "smartbotic-database build v${VERSION} (${GIT_COMMIT})"
  113. log_info "Build jobs: ${BUILD_JOBS}"
  114. if [[ $LOCAL_MODE -eq 1 ]]; then
  115. log_info "Mode: local (native)"
  116. else
  117. log_info "Mode: Docker (Debian 13)"
  118. fi
  119. log_info "Output: ${OUTPUT_DIR}"
  120. # ---------------------------------------------------------------------------
  121. # Local build
  122. # ---------------------------------------------------------------------------
  123. build_local() {
  124. log_info "Configuring CMake (Release)..."
  125. cmake -B "$PROJECT_DIR/build" -G Ninja \
  126. -DCMAKE_BUILD_TYPE=Release \
  127. -DSMARTBOTIC_DB_BUILD_SERVICE=ON \
  128. -DSMARTBOTIC_DB_BUILD_CLIENT=ON \
  129. -DSMARTBOTIC_DB_BUILD_CLI=ON \
  130. -DSMARTBOTIC_DB_BUILD_TESTS=ON \
  131. -DBUILD_SHARED_LIBS=ON \
  132. -S "$PROJECT_DIR"
  133. log_info "Building with ${BUILD_JOBS} jobs..."
  134. cmake --build "$PROJECT_DIR/build" -j"$BUILD_JOBS"
  135. log_success "Build completed"
  136. if [[ $SKIP_TESTS -eq 0 ]]; then
  137. log_info "Running tests..."
  138. LD_LIBRARY_PATH="$PROJECT_DIR/build/client" \
  139. "$PROJECT_DIR/build/tests/test_vector_storage"
  140. log_success "Tests passed"
  141. else
  142. log_warn "Tests skipped (--skip-tests)"
  143. fi
  144. log_info "Creating .deb packages..."
  145. OUTPUT_DIR="$OUTPUT_DIR" BUILD_DEB_REVISION="$DEB_REVISION" "$PROJECT_DIR/packaging/deb/create-debs.sh"
  146. log_success "Packages created"
  147. if [[ $INSTALL_AFTER -eq 1 ]]; then
  148. log_info "Installing .deb packages..."
  149. sudo dpkg -i \
  150. "$OUTPUT_DIR"/libsmartbotic-db-client_*.deb \
  151. "$OUTPUT_DIR"/libsmartbotic-db-client-dev_*.deb \
  152. "$OUTPUT_DIR"/smartbotic-db-cli_*.deb \
  153. "$OUTPUT_DIR"/smartbotic-database_*.deb \
  154. 2>/dev/null || sudo apt-get install -f -y
  155. log_success "Packages installed"
  156. fi
  157. }
  158. # ---------------------------------------------------------------------------
  159. # Docker build
  160. # ---------------------------------------------------------------------------
  161. build_docker() {
  162. if ! command -v docker &>/dev/null; then
  163. log_error "Docker is not installed or not in PATH"
  164. exit 1
  165. fi
  166. # Check / build base image
  167. if [[ $REBUILD_BASE -eq 1 ]] || ! docker image inspect "$BASE_IMAGE_NAME" &>/dev/null; then
  168. if [[ $REBUILD_BASE -eq 1 ]]; then
  169. log_info "Rebuilding base image (--rebuild-base)..."
  170. else
  171. log_info "Base image not found, building ${BASE_IMAGE_NAME}..."
  172. fi
  173. local base_cache_args=()
  174. if [[ $NO_CACHE -eq 1 ]]; then
  175. base_cache_args+=("--no-cache")
  176. fi
  177. docker buildx build \
  178. -f "$PROJECT_DIR/packaging/Dockerfile.base" \
  179. -t "$BASE_IMAGE_NAME" \
  180. "${base_cache_args[@]+"${base_cache_args[@]}"}" \
  181. "$PROJECT_DIR"
  182. log_success "Base image built: ${BASE_IMAGE_NAME}"
  183. else
  184. log_info "Using existing base image: ${BASE_IMAGE_NAME}"
  185. fi
  186. # Build packages
  187. log_info "Building packages in Docker..."
  188. local build_cache_args=()
  189. if [[ $NO_CACHE -eq 1 ]]; then
  190. build_cache_args+=("--no-cache")
  191. fi
  192. docker buildx build \
  193. -f "$PROJECT_DIR/packaging/Dockerfile.build" \
  194. --build-arg "BASE_IMAGE=${BASE_IMAGE_NAME}" \
  195. --build-arg "BUILD_VERSION=${VERSION}" \
  196. --build-arg "BUILD_DEB_REVISION=${DEB_REVISION}" \
  197. --build-arg "BUILD_JOBS=${BUILD_JOBS}" \
  198. --build-arg "BUILD_GIT_COMMIT=${GIT_COMMIT}" \
  199. --target packages \
  200. --output "type=local,dest=$OUTPUT_DIR" \
  201. "${build_cache_args[@]+"${build_cache_args[@]}"}" \
  202. "$PROJECT_DIR"
  203. log_success "Docker build completed"
  204. }
  205. # ---------------------------------------------------------------------------
  206. # Main
  207. # ---------------------------------------------------------------------------
  208. if [[ $LOCAL_MODE -eq 1 ]]; then
  209. build_local
  210. else
  211. build_docker
  212. fi
  213. # ---------------------------------------------------------------------------
  214. # List created packages
  215. # ---------------------------------------------------------------------------
  216. echo ""
  217. log_info "Created packages:"
  218. ls -lh "$OUTPUT_DIR"/*.deb 2>/dev/null || {
  219. log_error "No .deb files found in $OUTPUT_DIR"
  220. exit 1
  221. }
  222. # ---------------------------------------------------------------------------
  223. # Repository operations
  224. # ---------------------------------------------------------------------------
  225. if [[ $DO_REPO -eq 1 ]]; then
  226. echo ""
  227. log_info "Adding packages to repository (suite: ${SUITE})..."
  228. if [[ ! -x "$DEB_REPO_DIR/scripts/add-packages.sh" ]]; then
  229. log_error "Repository tool not found: $DEB_REPO_DIR/scripts/add-packages.sh"
  230. exit 1
  231. fi
  232. "$DEB_REPO_DIR/scripts/add-packages.sh" "$OUTPUT_DIR"
  233. log_success "Packages added to staging"
  234. "$DEB_REPO_DIR/scripts/create-repo.sh" --suite "$SUITE"
  235. log_success "Repository created for suite: ${SUITE}"
  236. if [[ $DO_SYNC -eq 1 ]]; then
  237. log_info "Syncing repository to repository.smartbotics.ai..."
  238. "$DEB_REPO_DIR/scripts/sync-repo.sh"
  239. log_success "Repository synced"
  240. fi
  241. fi
  242. # ---------------------------------------------------------------------------
  243. # Next steps
  244. # ---------------------------------------------------------------------------
  245. echo ""
  246. log_success "Build complete! (v${VERSION}-${DEB_REVISION})"
  247. echo ""
  248. if [[ $LOCAL_MODE -eq 1 && $INSTALL_AFTER -eq 0 ]]; then
  249. log_info "To install locally:"
  250. echo " sudo dpkg -i ${OUTPUT_DIR}/*.deb || sudo apt-get install -f -y"
  251. echo ""
  252. fi
  253. if [[ $DO_REPO -eq 0 ]]; then
  254. log_info "To publish to repository:"
  255. echo " packaging/build.sh --repo --suite trixie"
  256. echo ""
  257. fi
  258. if [[ $DO_REPO -eq 1 && $DO_SYNC -eq 0 ]]; then
  259. log_info "To sync repository to remote:"
  260. echo " packaging/build.sh --repo --suite ${SUITE} --sync"
  261. echo ""
  262. fi