190 lines
5.6 KiB
Bash
Executable File
190 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
remote_target="${1:-}"
|
|
app_name="${APP_NAME:-cpone-middleware}"
|
|
build_binary="${BUILD_BINARY:-dist/${app_name}-linux-amd64}"
|
|
requested_remote_dir="${REMOTE_DIR:-auto}"
|
|
remote_binary_override="${REMOTE_BINARY:-}"
|
|
service="${SERVICE:-${app_name}.service}"
|
|
service_scope="${SERVICE_SCOPE:-auto}"
|
|
restart_mode="${RESTART_MODE:-systemd-kill}"
|
|
ssh_timeout="${SSH_TIMEOUT:-45s}"
|
|
transfer_timeout="${TRANSFER_TIMEOUT:-120s}"
|
|
|
|
if [[ -z "$remote_target" ]]; then
|
|
printf 'Error: target server belum diisi.\n' >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ ! "$remote_target" =~ ^[A-Za-z0-9._-]+@[A-Za-z0-9._:-]+$ ]]; then
|
|
printf 'Error: target harus berbentuk user@host (gunakan SSH_OPTIONS untuk port).\n' >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ ! -f "$build_binary" ]]; then
|
|
printf 'Error: binary tidak ditemukan: %s\n' "$build_binary" >&2
|
|
printf 'Jalankan `make build` terlebih dahulu.\n' >&2
|
|
exit 2
|
|
fi
|
|
|
|
case "$restart_mode" in
|
|
systemd-kill|systemctl) ;;
|
|
*)
|
|
printf 'Error: RESTART_MODE harus systemd-kill atau systemctl.\n' >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
case "$service_scope" in
|
|
auto|user|system) ;;
|
|
*)
|
|
printf 'Error: SERVICE_SCOPE harus auto, user, atau system.\n' >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
if ! command -v ssh >/dev/null 2>&1 || ! command -v scp >/dev/null 2>&1 || ! command -v timeout >/dev/null 2>&1; then
|
|
printf 'Error: ssh, scp, dan timeout wajib tersedia di komputer lokal.\n' >&2
|
|
exit 2
|
|
fi
|
|
|
|
read -r -a ssh_options <<< "${SSH_OPTIONS:-}"
|
|
|
|
remote_metadata="$(timeout --foreground "$ssh_timeout" ssh "${ssh_options[@]}" "$remote_target" bash -s -- "$service" "$service_scope" <<'REMOTE_METADATA'
|
|
set -Eeuo pipefail
|
|
|
|
service="$1"
|
|
requested_scope="$2"
|
|
|
|
if [[ "$requested_scope" == "user" ]]; then
|
|
systemctl_cmd=(systemctl --user)
|
|
resolved_scope=user
|
|
elif [[ "$requested_scope" == "system" ]]; then
|
|
systemctl_cmd=(systemctl)
|
|
resolved_scope=system
|
|
elif systemctl --user show "$service" >/dev/null 2>&1; then
|
|
systemctl_cmd=(systemctl --user)
|
|
resolved_scope=user
|
|
elif systemctl show "$service" >/dev/null 2>&1; then
|
|
systemctl_cmd=(systemctl)
|
|
resolved_scope=system
|
|
else
|
|
echo "Service tidak ditemukan: $service" >&2
|
|
exit 1
|
|
fi
|
|
|
|
working_directory="$("${systemctl_cmd[@]}" show "$service" -p WorkingDirectory --value 2>/dev/null || true)"
|
|
printf '%s\t%s\n' "$resolved_scope" "$working_directory"
|
|
REMOTE_METADATA
|
|
)"
|
|
IFS=$'\t' read -r resolved_service_scope service_working_directory <<< "$remote_metadata"
|
|
|
|
if [[ "$resolved_service_scope" != user && "$resolved_service_scope" != system ]]; then
|
|
printf 'Error: scope service remote tidak valid: %s\n' "$resolved_service_scope" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$requested_remote_dir" == "auto" ]]; then
|
|
if [[ -n "$service_working_directory" && "$service_working_directory" != "-" ]]; then
|
|
remote_dir="${service_working_directory%/}"
|
|
else
|
|
remote_home="$(timeout --foreground "$ssh_timeout" ssh "${ssh_options[@]}" "$remote_target" 'printf "%s" "$HOME"')"
|
|
if [[ -z "$remote_home" ]]; then
|
|
printf 'Error: tidak dapat menentukan HOME pada server remote.\n' >&2
|
|
exit 1
|
|
fi
|
|
remote_dir="${remote_home%/}/apps"
|
|
fi
|
|
else
|
|
remote_dir="$requested_remote_dir"
|
|
fi
|
|
|
|
remote_binary="${remote_binary_override:-${remote_dir}/${app_name}}"
|
|
remote_tmp="${remote_dir}/.${app_name}.new.$$.$RANDOM"
|
|
cleanup_needed=0
|
|
|
|
cleanup_remote_tmp() {
|
|
if (( cleanup_needed )); then
|
|
timeout --foreground "$ssh_timeout" ssh "${ssh_options[@]}" "$remote_target" bash -s -- "$remote_tmp" <<'REMOTE_CLEANUP' >/dev/null 2>&1 || true
|
|
set -eu
|
|
rm -f -- "$1"
|
|
REMOTE_CLEANUP
|
|
fi
|
|
}
|
|
trap cleanup_remote_tmp EXIT
|
|
|
|
printf 'Preparing remote directory %s:%s\n' "$remote_target" "$remote_dir"
|
|
timeout --foreground "$ssh_timeout" ssh "${ssh_options[@]}" "$remote_target" bash -s -- "$remote_dir" <<'REMOTE_PREPARE'
|
|
set -eu
|
|
install -d -- "$1"
|
|
REMOTE_PREPARE
|
|
|
|
printf 'Uploading %s -> %s:%s\n' "$build_binary" "$remote_target" "$remote_tmp"
|
|
timeout --foreground "$transfer_timeout" scp "${ssh_options[@]}" "$build_binary" "$remote_target:$remote_tmp"
|
|
cleanup_needed=1
|
|
|
|
printf 'Installing binary and restarting %s\n' "$service"
|
|
timeout --foreground "$ssh_timeout" ssh "${ssh_options[@]}" "$remote_target" bash -s -- \
|
|
"$remote_binary" "$service" "$remote_tmp" "$restart_mode" "$resolved_service_scope" <<'REMOTE_INSTALL'
|
|
set -Eeuo pipefail
|
|
|
|
binary="$1"
|
|
service="$2"
|
|
tmp="$3"
|
|
restart_mode="$4"
|
|
service_scope="$5"
|
|
main_pid=""
|
|
|
|
if [[ "$service_scope" == "user" ]]; then
|
|
systemctl_cmd=(systemctl --user)
|
|
else
|
|
systemctl_cmd=(systemctl)
|
|
fi
|
|
|
|
if [[ ! -s "$tmp" ]]; then
|
|
echo "Uploaded binary kosong atau tidak ditemukan: $tmp" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$restart_mode" == "systemd-kill" ]]; then
|
|
main_pid="$("${systemctl_cmd[@]}" show "$service" -p MainPID --value 2>/dev/null || true)"
|
|
if [[ ! "$main_pid" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo "Tidak dapat menemukan MainPID aktif untuk $service; binary belum diganti." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
chmod 0755 "$tmp"
|
|
if [[ -e "$binary" ]]; then
|
|
backup="${binary}.before-$(date +%Y%m%d%H%M%S)-$$"
|
|
cp -p -- "$binary" "$backup"
|
|
echo "Backup: $backup"
|
|
fi
|
|
mv -f -- "$tmp" "$binary"
|
|
|
|
if [[ "$restart_mode" == "systemctl" ]]; then
|
|
"${systemctl_cmd[@]}" restart "$service"
|
|
else
|
|
kill -TERM "$main_pid"
|
|
fi
|
|
|
|
for attempt in {1..20}; do
|
|
if "${systemctl_cmd[@]}" is-active --quiet "$service"; then
|
|
new_pid="$("${systemctl_cmd[@]}" show "$service" -p MainPID --value 2>/dev/null || true)"
|
|
if [[ "$restart_mode" == "systemctl" || "$new_pid" != "$main_pid" ]]; then
|
|
echo "Service aktif: $service (MainPID=${new_pid:-unknown})"
|
|
exit 0
|
|
fi
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
echo "Service tidak kembali aktif dalam 20 detik: $service" >&2
|
|
"${systemctl_cmd[@]}" status "$service" --no-pager >&2 || true
|
|
exit 1
|
|
REMOTE_INSTALL
|
|
cleanup_needed=0
|
|
|
|
printf 'Deploy selesai ke %s\n' "$remote_target"
|