Files
test-quarkus/scripts/deploy-services.sh
T
darroyo b65730c45f El nombre de la imagen sale de la carpeta del servicio y el Dockerfile depende de si es java o web.
Así se puede desplegar sin pom.xml y ubicar el Dockerfile en la raíz cuando el servicio es web.
2026-09-25 12:55:43 -03:00

335 lines
9.3 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.
# That summary is also written to deploy-YYYYMMDD-HHMMSS.log in the directory
# from which the script was launched.
#
# The list path is required (--services). Relative service paths are resolved
# against the directory that contains that file, not the caller's cwd.
# An optional second field, java or web, selects the Dockerfile location.
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.
When the batch finishes, that summary is written to deploy-YYYYMMDD-HHMMSS.log
in the current directory (local start time). If that name already exists, the
process id is appended.
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,
with an optional second field: java or web
(default java). 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 has its own directory; --type comes from the
list, and --project-dir is set from that path):
--name, --tag, --project-dir, --type
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|--type)
die "$1 is not accepted. Each service uses its own directory; --type comes from the services file."
;;
-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)"
service_paths=()
service_types=()
while IFS= read -r raw_line || [[ -n "$raw_line" ]]; do
line="$(trim "$raw_line")"
[[ -z "$line" || "$line" == \#* ]] && continue
svc_path=""
svc_type=""
svc_extra=""
IFS=$' \t' read -r svc_path svc_type svc_extra <<< "$line"
[[ -n "$svc_extra" ]] && die "Service line has an extra field: ${line}"
[[ -n "$svc_path" ]] || die "Service line is missing a path: ${line}"
svc_path="${svc_path/#\~/$HOME}"
[[ -z "$svc_type" ]] && svc_type="java"
case "$svc_type" in
java|web) ;;
*) die "Unknown service type '${svc_type}' in ${SERVICES_FILE} (use java or web)." ;;
esac
service_paths+=("$svc_path")
service_types+=("$svc_type")
done < "$SERVICES_FILE"
[[ ${#service_paths[@]} -gt 0 ]] || die "Services file is empty: ${SERVICES_FILE}"
run_build() {
local project_dir="$1"
local app_type="$2"
if [[ ${#forward_args[@]} -eq 0 ]]; then
"$BUILD_SCRIPT" --project-dir "$project_dir" --remote "$REMOTE" --type "$app_type"
else
"$BUILD_SCRIPT" --project-dir "$project_dir" --remote "$REMOTE" --type "$app_type" "${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"
}
# Plain-text summary in the directory from which the batch was launched.
# A write failure is reported and does not change the batch exit code.
write_summary_log() {
local finished="" summary_index=0 note="" path="" status=""
local -a lines=()
finished="$(date '+%Y-%m-%d %H:%M:%S %z')" || {
warn "Could not write log file: ${log_path}"
return 0
}
lines+=("started: ${run_started}")
lines+=("finished: ${finished}")
lines+=("remote: ${REMOTE}")
lines+=("services_file: ${SERVICES_FILE}")
lines+=("")
while [[ "$summary_index" -lt "$total" ]]; do
note="${result_notes[$summary_index]}"
path="${result_paths[$summary_index]}"
if [[ "$note" == "0" ]]; then
status="ok"
else
status="$note"
fi
lines+=("${path} ${status}")
summary_index=$((summary_index + 1))
done
lines+=("")
lines+=("succeeded: ${ok_count}")
lines+=("failed: ${fail_count}")
if ! printf '%s\n' "${lines[@]}" >"$log_path"; then
warn "Could not write log file: ${log_path}"
return 0
fi
log "Log file: ${log_path}"
}
total="${#service_paths[@]}"
index=0
ok_count=0
fail_count=0
result_paths=()
result_notes=()
launch_dir="$(pwd)"
run_started="$(date '+%Y-%m-%d %H:%M:%S %z')"
log_stamp="$(date '+%Y%m%d-%H%M%S')"
log_path="${launch_dir}/deploy-${log_stamp}.log"
if [[ -e "$log_path" ]]; then
log_path="${launch_dir}/deploy-${log_stamp}-$$.log"
fi
log "Services file: ${SERVICES_FILE}"
log "Remote: ${REMOTE}"
log "Services: ${total}"
while [[ "$index" -lt "$total" ]]; do
entry="${service_paths[$index]}"
app_type="${service_types[$index]}"
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" "$app_type"; 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
batch_exit=0
if [[ "$fail_count" -gt 0 ]]; then
err "${ok_count} succeeded, ${fail_count} failed."
batch_exit=1
else
ok "${ok_count} succeeded, 0 failed."
fi
write_summary_log
exit "$batch_exit"