Files
test-quarkus/scripts/deploy-services.sh
T
darroyo 782a130bb9 build: deploy several Quarkus services from one project list
Add deploy-services.sh so a single command runs build-and-push.sh for every path in a project file, keeps going after a failure, and reports which services failed.
2026-09-24 22:54:45 -03:00

261 lines
6.8 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Run scripts/build-and-push.sh once per service listed in a project file.
# Continues after a failure and prints a summary when the batch finishes.
#
# The list path is required (--services). Relative service paths are resolved
# against the directory that contains that file, not the caller's cwd.
set -euo pipefail
IFS=$'\n\t'
readonly SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
readonly BUILD_SCRIPT="${SCRIPT_DIR}/build-and-push.sh"
SERVICES_FILE=""
REMOTE=""
forward_args=()
# ---------- logging ----------
if [[ -t 1 ]]; then
readonly C_INFO=$'\033[0;36m'
readonly C_OK=$'\033[0;32m'
readonly C_WARN=$'\033[0;33m'
readonly C_ERR=$'\033[0;31m'
readonly C_RESET=$'\033[0m'
else
readonly C_INFO="" C_OK="" C_WARN="" C_ERR="" C_RESET=""
fi
log() { printf '%s[INFO]%s %s\n' "$C_INFO" "$C_RESET" "$*"; }
ok() { printf '%s[OK]%s %s\n' "$C_OK" "$C_RESET" "$*"; }
warn() { printf '%s[WARN]%s %s\n' "$C_WARN" "$C_RESET" "$*" >&2; }
err() { printf '%s[ERROR]%s %s\n' "$C_ERR" "$C_RESET" "$*" >&2; }
die() { err "$*"; exit 1; }
on_error() {
local exit_code=$?
err "Script aborted on line ${BASH_LINENO[0]} (exit ${exit_code})."
exit "$exit_code"
}
trap on_error ERR
# ---------- helpers ----------
usage() {
cat <<EOF
Usage: $(basename "$0") --remote TARGET --services FILE [options]
Run build-and-push.sh once for every service listed in FILE. One list file
per project: pass a different --services path to deploy another project.
A failure does not stop the batch. The exit code is 1 if any service failed.
Required:
--remote TARGET SSH destination forwarded to build-and-push.sh
('user@host[:port]' or a ~/.ssh/config alias)
--services FILE Service list for one project. One path per line.
Blank lines and lines starting with '#' are
ignored. Relative paths resolve against the
directory that contains FILE.
Forwarded to each service (build-and-push.sh):
--ssh-key PATH Private key file
--ssh-config PATH SSH config file
--skip-build Push an existing local image
--no-verify Skip the remote image check
--cleanup Prune the remote without building or pushing
Not accepted (each service reads its own pom.xml; --project-dir is set
from the list):
--name, --tag, --project-dir
Other:
-h, --help Show this help
Examples:
$(basename "$0") --remote prod --services /srv/proyecto-a/services.txt
$(basename "$0") --remote prod --services /srv/proyecto-b/services.txt --skip-build
EOF
}
trim() {
local value="$1"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
}
require_value() {
[[ $# -ge 2 && -n "${2:-}" && "$2" != -* ]] \
|| die "Missing value for $1"
}
# ---------- arg parsing ----------
while [[ $# -gt 0 ]]; do
case "$1" in
--services)
require_value "$@"
SERVICES_FILE="$2"
shift 2
;;
--remote)
require_value "$@"
REMOTE="$2"
shift 2
;;
--ssh-key|--ssh-config)
require_value "$@"
forward_args+=("$1" "$2")
shift 2
;;
--skip-build|--no-verify|--cleanup)
forward_args+=("$1")
shift
;;
--name|--tag|--project-dir)
die "$1 is not accepted. Each service uses its own directory and pom.xml."
;;
-h|--help)
usage
exit 0
;;
*)
die "Unknown argument: $1 (use --help)"
;;
esac
done
[[ -n "$REMOTE" ]] || die "Missing --remote TARGET (use --help)."
[[ -n "$SERVICES_FILE" ]] || die "Missing --services FILE (use --help)."
[[ -f "$BUILD_SCRIPT" ]] || die "build-and-push.sh not found next to this script: ${BUILD_SCRIPT}"
[[ -f "$SERVICES_FILE" ]] || die "Services file not found: ${SERVICES_FILE}"
SERVICES_FILE="$(readlink -f "$SERVICES_FILE")"
SERVICES_DIR="$(cd "$(dirname "$SERVICES_FILE")" && pwd)"
services=()
while IFS= read -r raw_line || [[ -n "$raw_line" ]]; do
line="$(trim "$raw_line")"
[[ -z "$line" || "$line" == \#* ]] && continue
line="${line/#\~/$HOME}"
services+=("$line")
done < "$SERVICES_FILE"
[[ ${#services[@]} -gt 0 ]] || die "Services file is empty: ${SERVICES_FILE}"
run_build() {
local project_dir="$1"
if [[ ${#forward_args[@]} -eq 0 ]]; then
"$BUILD_SCRIPT" --project-dir "$project_dir" --remote "$REMOTE"
else
"$BUILD_SCRIPT" --project-dir "$project_dir" --remote "$REMOTE" "${forward_args[@]}"
fi
}
resolve_service() {
local entry="$1"
if [[ "$entry" == /* ]]; then
printf '%s' "$entry"
else
printf '%s' "${SERVICES_DIR}/${entry}"
fi
}
# Collapse "." and ".." without requiring the path to exist.
normalize_path() {
local path="$1"
local part joined=""
local -a stack=()
local old_ifs="$IFS"
set -f
IFS=/
# shellcheck disable=SC2206
local -a parts=($path)
IFS="$old_ifs"
set +f
for part in "${parts[@]}"; do
case "$part" in
""|.) ;;
..)
if [[ ${#stack[@]} -gt 0 ]]; then
unset 'stack[${#stack[@]}-1]'
fi
;;
*) stack+=("$part") ;;
esac
done
if [[ ${#stack[@]} -eq 0 ]]; then
printf '/'
return
fi
for part in "${stack[@]}"; do
joined+="/${part}"
done
printf '%s' "$joined"
}
total="${#services[@]}"
index=0
ok_count=0
fail_count=0
result_paths=()
result_notes=()
log "Services file: ${SERVICES_FILE}"
log "Remote: ${REMOTE}"
log "Services: ${total}"
for entry in "${services[@]}"; do
index=$((index + 1))
resolved="$(normalize_path "$(resolve_service "$entry")")"
if [[ -d "$resolved" ]]; then
resolved="$(cd "$resolved" && pwd)"
fi
printf '\n%s========== [%s/%s] %s ==========%s\n' \
"$C_INFO" "$index" "$total" "$resolved" "$C_RESET"
if [[ ! -d "$resolved" ]]; then
err "Service directory not found: ${resolved}"
result_paths+=("$resolved")
result_notes+=("not found")
fail_count=$((fail_count + 1))
continue
fi
exit_code=0
if run_build "$resolved"; then
ok "Finished ${resolved}"
result_paths+=("$resolved")
result_notes+=("0")
ok_count=$((ok_count + 1))
else
exit_code=$?
err "Failed ${resolved} (exit ${exit_code})"
result_paths+=("$resolved")
result_notes+=("exit ${exit_code}")
fail_count=$((fail_count + 1))
fi
done
printf '\n%s========== Summary ==========%s\n' "$C_INFO" "$C_RESET"
summary_index=0
while [[ "$summary_index" -lt "$total" ]]; do
note="${result_notes[$summary_index]}"
path="${result_paths[$summary_index]}"
if [[ "$note" == "0" ]]; then
ok "${path}"
else
err "${path} (${note})"
fi
summary_index=$((summary_index + 1))
done
if [[ "$fail_count" -gt 0 ]]; then
err "${ok_count} succeeded, ${fail_count} failed."
exit 1
fi
ok "${ok_count} succeeded, 0 failed."
exit 0