build.sh 9.5 KB

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