Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

317 lignes
7.8 KiB

  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. # Build selected app images and package them as an incremental update.
  4. # Usage:
  5. # ./build-update.sh admin monitor
  6. # SERVICES="admin monitor" ./build-update.sh
  7. # EMP_ROOT=/path/to/emp IMAGE_TAG=20260602153000 ./build-update.sh emp-admin emp-monitor
  8. #
  9. # Output:
  10. # dist/emp-test-update-${IMAGE_TAG}-${services}.tar.gz
  11. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  12. DIST_DIR="${DIST_DIR:-$SCRIPT_DIR/dist}"
  13. BUILD_CONTEXT_DIR="$SCRIPT_DIR/.build-context"
  14. IMAGE_NAMESPACE="${IMAGE_NAMESPACE:-emp-test}"
  15. IMAGE_TAG="${IMAGE_TAG:-$(date '+%Y%m%d%H%M%S')}"
  16. NPM_REGISTRY="${NPM_REGISTRY:-https://registry.npmjs.org}"
  17. SKIP_BUILD="${SKIP_BUILD:-0}"
  18. log() {
  19. echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
  20. }
  21. die() {
  22. echo "ERROR: $*" >&2
  23. exit 1
  24. }
  25. need_cmd() {
  26. command -v "$1" >/dev/null 2>&1 || die "Missing command: $1"
  27. }
  28. has_emp_root() {
  29. [[ -d "$1/emp_server" && -d "$1/emp_admin" && -d "$1/emp_ws" ]]
  30. }
  31. resolve_root_dir() {
  32. if [[ -n "${EMP_ROOT:-}" ]]; then
  33. local root
  34. root="$(cd "$EMP_ROOT" && pwd)"
  35. has_emp_root "$root" || die "EMP_ROOT is invalid: $EMP_ROOT"
  36. printf '%s\n' "$root"
  37. return
  38. fi
  39. local candidates=(
  40. "$SCRIPT_DIR/../.."
  41. "$SCRIPT_DIR/../../emp"
  42. "$SCRIPT_DIR/.."
  43. "$PWD"
  44. )
  45. local candidate root
  46. for candidate in "${candidates[@]}"; do
  47. if [[ -d "$candidate" ]]; then
  48. root="$(cd "$candidate" && pwd)"
  49. if has_emp_root "$root"; then
  50. printf '%s\n' "$root"
  51. return
  52. fi
  53. fi
  54. done
  55. die "Cannot find EMP root. Set EMP_ROOT=/path/to/emp where emp_server, emp_admin and emp_ws exist."
  56. }
  57. normalize_service() {
  58. case "$1" in
  59. gateway|emp-gateway) echo "gateway" ;;
  60. auth|emp-auth) echo "auth" ;;
  61. monitor|emp-monitor) echo "monitor" ;;
  62. data|emp-data) echo "data" ;;
  63. pdf|emp-pdf) echo "pdf" ;;
  64. ws|emp-ws) echo "ws" ;;
  65. admin|emp-admin) echo "admin" ;;
  66. *) die "Unknown service: $1. Allowed: gateway auth monitor data pdf ws admin" ;;
  67. esac
  68. }
  69. compose_service_name() {
  70. echo "emp-$1"
  71. }
  72. image_name() {
  73. echo "$IMAGE_NAMESPACE/emp-$1"
  74. }
  75. java_module_name() {
  76. case "$1" in
  77. gateway) echo "emp_gateway" ;;
  78. auth) echo "emp_auth" ;;
  79. monitor) echo "emp_monitor" ;;
  80. data) echo "emp_data" ;;
  81. *) return 1 ;;
  82. esac
  83. }
  84. is_java_service() {
  85. case "$1" in
  86. gateway|auth|monitor|data) return 0 ;;
  87. *) return 1 ;;
  88. esac
  89. }
  90. read_requested_services() {
  91. local raw_services=()
  92. if [[ "$#" -gt 0 ]]; then
  93. raw_services=("$@")
  94. elif [[ -n "${SERVICES:-}" ]]; then
  95. # shellcheck disable=SC2206
  96. raw_services=($SERVICES)
  97. else
  98. die "No services specified. Example: ./build-update.sh admin monitor"
  99. fi
  100. local -A seen=()
  101. local service normalized
  102. for service in "${raw_services[@]}"; do
  103. normalized="$(normalize_service "$service")"
  104. if [[ -z "${seen[$normalized]:-}" ]]; then
  105. seen[$normalized]=1
  106. echo "$normalized"
  107. fi
  108. done
  109. }
  110. join_by_comma() {
  111. local IFS=,
  112. echo "$*"
  113. }
  114. run_step() {
  115. local workdir="$1"
  116. shift
  117. (cd "$workdir" && "$@")
  118. }
  119. build_java_images() {
  120. local services=("$@")
  121. [[ "${#services[@]}" -gt 0 ]] || return 0
  122. local server_dir="$ROOT_DIR/emp_server"
  123. local modules=()
  124. local service module jar target_dir
  125. for service in "${services[@]}"; do
  126. modules+=("$(java_module_name "$service")")
  127. done
  128. log "Build backend jars: ${modules[*]}"
  129. run_step "$server_dir" mvn package -DskipTests -B -pl "$(join_by_comma "${modules[@]}")" -am
  130. for service in "${services[@]}"; do
  131. module="$(java_module_name "$service")"
  132. target_dir="$server_dir/$module/target"
  133. jar="$(find "$target_dir" -maxdepth 1 -name '*.jar' ! -name '*original*' ! -name 'app.jar' | head -1)"
  134. [[ -n "$jar" ]] || die "Jar not found for $module"
  135. cp "$jar" "$target_dir/app.jar"
  136. log "Build image: $(image_name "$service"):$IMAGE_TAG"
  137. run_step "$server_dir" docker build \
  138. -f Dockerfile.service \
  139. --build-arg "MODULE=$module" \
  140. -t "$(image_name "$service"):$IMAGE_TAG" \
  141. -t "$(image_name "$service"):latest" \
  142. .
  143. done
  144. }
  145. build_pdf_image() {
  146. local server_dir="$ROOT_DIR/emp_server"
  147. log "Build image: $(image_name pdf):$IMAGE_TAG"
  148. run_step "$server_dir" docker build \
  149. -f emp_pdf/Dockerfile \
  150. -t "$(image_name pdf):$IMAGE_TAG" \
  151. -t "$(image_name pdf):latest" \
  152. emp_pdf
  153. }
  154. build_ws_image() {
  155. log "Build image: $(image_name ws):$IMAGE_TAG"
  156. run_step "$ROOT_DIR" docker build \
  157. -f "$SCRIPT_DIR/dockerfiles/emp-ws.Dockerfile" \
  158. --build-arg "NPM_REGISTRY=$NPM_REGISTRY" \
  159. -t "$(image_name ws):$IMAGE_TAG" \
  160. -t "$(image_name ws):latest" \
  161. .
  162. }
  163. build_admin_image() {
  164. local admin_dir="$ROOT_DIR/emp_admin"
  165. local context_dir="$BUILD_CONTEXT_DIR/emp-admin"
  166. log "Build frontend dist"
  167. run_step "$admin_dir" pnpm install --frozen-lockfile
  168. run_step "$admin_dir" pnpm run build:shunfeng
  169. log "Prepare frontend image context"
  170. rm -rf "$context_dir"
  171. mkdir -p "$context_dir"
  172. cp -R "$admin_dir/dist" "$context_dir/dist"
  173. cp "$SCRIPT_DIR/nginx/admin.conf" "$context_dir/admin.conf"
  174. log "Build image: $(image_name admin):$IMAGE_TAG"
  175. run_step "$context_dir" docker build \
  176. -f "$SCRIPT_DIR/dockerfiles/emp-admin.Dockerfile" \
  177. -t "$(image_name admin):$IMAGE_TAG" \
  178. -t "$(image_name admin):latest" \
  179. .
  180. }
  181. prepare_package() {
  182. local services_slug="$1"
  183. PACKAGE_NAME="${PACKAGE_NAME:-emp-test-update-${IMAGE_TAG}-${services_slug}}"
  184. PACKAGE_DIR="$DIST_DIR/$PACKAGE_NAME"
  185. PACKAGE_ARCHIVE="$DIST_DIR/${PACKAGE_NAME}.tar.gz"
  186. rm -rf "$PACKAGE_DIR"
  187. mkdir -p "$PACKAGE_DIR"
  188. cp "$SCRIPT_DIR/apply-update.sh" "$PACKAGE_DIR/apply-update.sh"
  189. chmod +x "$PACKAGE_DIR/apply-update.sh"
  190. local service
  191. for service in "${REQUESTED_SERVICES[@]}"; do
  192. compose_service_name "$service"
  193. done > "$PACKAGE_DIR/services.txt"
  194. {
  195. echo "image_namespace=$IMAGE_NAMESPACE"
  196. echo "image_tag=$IMAGE_TAG"
  197. echo "services=${REQUESTED_SERVICES[*]}"
  198. echo "compose_services=$(tr '\n' ' ' < "$PACKAGE_DIR/services.txt")"
  199. echo "created_at=$(date '+%Y-%m-%d %H:%M:%S')"
  200. } > "$PACKAGE_DIR/manifest.txt"
  201. }
  202. save_images() {
  203. local images=()
  204. local service image
  205. for service in "${REQUESTED_SERVICES[@]}"; do
  206. image="$(image_name "$service")"
  207. images+=("$image:$IMAGE_TAG" "$image:latest")
  208. done
  209. log "Save images: ${images[*]}"
  210. docker save -o "$PACKAGE_DIR/images.tar" "${images[@]}"
  211. }
  212. archive_package() {
  213. mkdir -p "$DIST_DIR"
  214. rm -f "$PACKAGE_ARCHIVE"
  215. tar -czf "$PACKAGE_ARCHIVE" -C "$DIST_DIR" "$PACKAGE_NAME"
  216. log "Update package created: $PACKAGE_ARCHIVE"
  217. }
  218. need_cmd docker
  219. need_cmd tar
  220. mapfile -t REQUESTED_SERVICES < <(read_requested_services "$@")
  221. [[ "${#REQUESTED_SERVICES[@]}" -gt 0 ]] || die "No services to update."
  222. ROOT_DIR="$(resolve_root_dir)"
  223. SERVICES_SLUG="$(IFS=-; echo "${REQUESTED_SERVICES[*]}")"
  224. log "EMP root: $ROOT_DIR"
  225. log "Deploy root: $SCRIPT_DIR"
  226. log "Update services: ${REQUESTED_SERVICES[*]}"
  227. log "Image tag: $IMAGE_TAG"
  228. if [[ "$SKIP_BUILD" != "1" ]]; then
  229. JAVA_SERVICES=()
  230. NEED_PNPM=0
  231. for service in "${REQUESTED_SERVICES[@]}"; do
  232. if is_java_service "$service"; then
  233. JAVA_SERVICES+=("$service")
  234. fi
  235. if [[ "$service" == "admin" ]]; then
  236. NEED_PNPM=1
  237. fi
  238. done
  239. if [[ "${#JAVA_SERVICES[@]}" -gt 0 ]]; then
  240. need_cmd mvn
  241. fi
  242. if [[ "$NEED_PNPM" == "1" ]]; then
  243. need_cmd pnpm
  244. fi
  245. build_java_images "${JAVA_SERVICES[@]}"
  246. for service in "${REQUESTED_SERVICES[@]}"; do
  247. case "$service" in
  248. pdf) build_pdf_image ;;
  249. ws) build_ws_image ;;
  250. admin) build_admin_image ;;
  251. esac
  252. done
  253. else
  254. log "Skip build, package existing local images only"
  255. fi
  256. prepare_package "$SERVICES_SLUG"
  257. save_images
  258. archive_package
  259. cat <<EOF
  260. Next steps on target server:
  261. mkdir -p /tmp/emp-update
  262. tar -xzf $(basename "$PACKAGE_ARCHIVE") -C /tmp/emp-update --strip-components=1
  263. cd /tmp/emp-update
  264. bash apply-update.sh
  265. EOF