mirror of
https://github.com/GMWalletApp/epusdt.git
synced 2026-07-07 02:06:16 +00:00
feat(epctl): add upgrade restart controls and rollback-safe deploy
This commit is contained in:
@@ -127,6 +127,24 @@ trf() {
|
||||
zh:service_failed_active) template='服务 %s 未能成功进入 active 状态' ;;
|
||||
en:service_installed) template='installed %s to %s from %s' ;;
|
||||
zh:service_installed) template='已将 %s 安装到 %s,来源版本:%s' ;;
|
||||
en:service_upgraded_restarted) template='updated %s in %s from %s and restarted the service' ;;
|
||||
zh:service_upgraded_restarted) template='已将 %s 在 %s 升级到 %s,并完成服务重启' ;;
|
||||
en:upgrade_existing_config_required) template='upgrade requires an existing config file at %s; run install first' ;;
|
||||
zh:upgrade_existing_config_required) template='升级要求 %s 已存在;请先执行 install' ;;
|
||||
en:upgrade_restart_flag_conflict) template='cannot combine --prompt-restart and --no-restart' ;;
|
||||
zh:upgrade_restart_flag_conflict) template='--prompt-restart 和 --no-restart 不能同时使用' ;;
|
||||
en:upgrade_deploy_failed_rollback_start) template='upgrading %s failed before restart; attempting to roll back the release files' ;;
|
||||
zh:upgrade_deploy_failed_rollback_start) template='升级 %s 在重启前失败,正在尝试回滚 release 文件' ;;
|
||||
en:upgrade_deploy_failed_rolled_back) template='upgrading %s to %s failed before restart; previous release files were restored' ;;
|
||||
zh:upgrade_deploy_failed_rolled_back) template='升级 %s 到 %s 在重启前失败,已恢复到之前的 release 文件' ;;
|
||||
en:upgrade_deploy_failed_rollback_failed) template='upgrading %s to %s failed before restart, and rollback also failed' ;;
|
||||
zh:upgrade_deploy_failed_rollback_failed) template='升级 %s 到 %s 在重启前失败,且回滚也失败了' ;;
|
||||
en:upgrade_restart_failed_rollback_start) template='restart failed after upgrading %s; attempting to roll back the release files' ;;
|
||||
zh:upgrade_restart_failed_rollback_start) template='升级 %s 后重启失败,正在尝试回滚 release 文件' ;;
|
||||
en:upgrade_restart_failed_rolled_back) template='restart failed after upgrading %s to %s; previous release files were restored' ;;
|
||||
zh:upgrade_restart_failed_rolled_back) template='升级 %s 到 %s 后重启失败,已恢复到之前的 release 文件' ;;
|
||||
en:upgrade_restart_failed_rollback_failed) template='restart failed after upgrading %s to %s, and rollback also failed' ;;
|
||||
zh:upgrade_restart_failed_rollback_failed) template='升级 %s 到 %s 后重启失败,且回滚也失败了' ;;
|
||||
en:self_install_current) template='%s already points to the current script' ;;
|
||||
zh:self_install_current) template='%s 已经指向当前脚本,无需重复安装' ;;
|
||||
en:self_install_done) template='installed %s into /usr/local/bin' ;;
|
||||
@@ -215,6 +233,8 @@ trf() {
|
||||
zh:menu_language_input) template='语言选择(Language selection)' ;;
|
||||
en:menu_confirm_prompt) template='Continue? / 是否继续? [y/N] ' ;;
|
||||
zh:menu_confirm_prompt) template='是否继续?/ Continue? [y/N] ' ;;
|
||||
en:upgrade_restart_prompt) template='Release files are updated. Restart %s now? [Y/n] ' ;;
|
||||
zh:upgrade_restart_prompt) template='Release 文件已更新,是否立即重启 %s?[Y/n] ' ;;
|
||||
en:menu_cancelled_back) template='Cancelled and returned to the menu / 已取消,返回菜单' ;;
|
||||
zh:menu_cancelled_back) template='已取消,返回菜单 / Cancelled and returned to the menu' ;;
|
||||
en:language_switched) template='Language switched to %s / 语言已切换为 %s' ;;
|
||||
@@ -239,8 +259,14 @@ trf() {
|
||||
zh:action_install_desc) template='此操作会把 epusdt 安装到 %s,创建或复用 %s,写入 %s,并通过 systemd 启用 %s。若已有 .env,则会保留。' ;;
|
||||
en:action_upgrade_title) template='Upgrade the service' ;;
|
||||
zh:action_upgrade_title) template='升级服务' ;;
|
||||
en:action_upgrade_desc) template='This will download and install a newer release into %s, then restart %s. The existing .env configuration will be preserved.' ;;
|
||||
zh:action_upgrade_desc) template='此操作会下载并安装较新的 release 到 %s,然后重启 %s。现有 .env 配置会被保留。' ;;
|
||||
en:action_upgrade_desc) template='This will download and install a newer release into %s, keep the existing .env configuration, and then ask whether to restart %s. The direct CLI command restarts by default unless you pass --no-restart.' ;;
|
||||
zh:action_upgrade_desc) template='此操作会下载并安装较新的 release 到 %s,保留现有 .env 配置,然后再询问是否重启 %s。直接执行 CLI 时默认会重启,除非显式传入 --no-restart。' ;;
|
||||
en:action_upgrade_restart_notice) template='Restarting %s will briefly interrupt the service and switch the running process to the new binary.' ;;
|
||||
zh:action_upgrade_restart_notice) template='重启 %s 会带来短暂服务中断,并让运行中的进程切换到新的二进制文件。' ;;
|
||||
en:action_upgrade_restart_skipped) template='Release files for %s were updated, but the service was not restarted. Run `systemctl restart %s` manually to apply the new binary.' ;;
|
||||
zh:action_upgrade_restart_skipped) template='已更新 %s 的 release 文件,但没有重启服务。请手动执行 `systemctl restart %s` 以应用新的二进制文件。' ;;
|
||||
en:action_upgrade_restart_no_tty) template='--prompt-restart requires an interactive terminal. Re-run in a TTY, use plain `upgrade` to restart immediately, or pass `--no-restart` to skip the restart.' ;;
|
||||
zh:action_upgrade_restart_no_tty) template='--prompt-restart 需要交互终端。请在 TTY 中重新执行,或直接使用默认 `upgrade` 立即重启,或传入 `--no-restart` 跳过重启。' ;;
|
||||
en:action_self_install_title) template='Install epctl into PATH' ;;
|
||||
zh:action_self_install_title) template='安装 epctl 到 PATH' ;;
|
||||
en:action_self_install_desc) template='This will install %s into /usr/local/bin so it can be called from anywhere. Use %s to force Chinese or %s to force English.' ;;
|
||||
@@ -321,7 +347,7 @@ usage() {
|
||||
epctl [--lang zh|en] menu
|
||||
epctl [--lang zh|en] download [--tag <tag>]
|
||||
epctl [--lang zh|en] install [--tag <tag>] [--app-uri URL] [--listen ADDR:PORT]
|
||||
epctl [--lang zh|en] upgrade [--tag <tag>]
|
||||
epctl [--lang zh|en] upgrade [--tag <tag>] [--prompt-restart|--no-restart]
|
||||
epctl [--lang zh|en] self-install
|
||||
epctl [--lang zh|en] show-config
|
||||
epctl [--lang zh|en] init-password
|
||||
@@ -337,7 +363,7 @@ usage() {
|
||||
menu 进入交互式菜单;直接执行 `epctl` 也会进入
|
||||
download 下载 GitHub Release 包到 /tmp/epusdt/<tag>/
|
||||
install 安装或刷新 /opt/epusdt,并注册 epusdt.service
|
||||
upgrade 升级到新的 release,保留现有 .env
|
||||
upgrade 升级到新的 release,保留现有 .env;默认直接重启,菜单会改为先询问
|
||||
self-install 安装 epctl 到 /usr/local/bin
|
||||
show-config 输出当前生效的 /opt/epusdt/.env
|
||||
init-password 通过本地 HTTP 路由请求初始化管理员密码
|
||||
@@ -351,6 +377,10 @@ usage() {
|
||||
- 需要提权的命令会自动通过 sudo 重新执行
|
||||
- tag 需要是真实的 GitHub release tag,例如 v1.0.6
|
||||
- 未提供 --tag 时,会自动解析 latest,并要求二次确认
|
||||
- upgrade 只接受已有安装;不会创建新的 .env
|
||||
- upgrade 默认会在文件替换后立即重启服务
|
||||
- upgrade --prompt-restart 会在交互终端下询问是否重启
|
||||
- upgrade --no-restart 只替换文件,不重启服务
|
||||
EOF
|
||||
else
|
||||
cat <<'EOF'
|
||||
@@ -361,7 +391,7 @@ Usage:
|
||||
epctl [--lang zh|en] menu
|
||||
epctl [--lang zh|en] download [--tag <tag>]
|
||||
epctl [--lang zh|en] install [--tag <tag>] [--app-uri URL] [--listen ADDR:PORT]
|
||||
epctl [--lang zh|en] upgrade [--tag <tag>]
|
||||
epctl [--lang zh|en] upgrade [--tag <tag>] [--prompt-restart|--no-restart]
|
||||
epctl [--lang zh|en] self-install
|
||||
epctl [--lang zh|en] show-config
|
||||
epctl [--lang zh|en] init-password
|
||||
@@ -377,7 +407,7 @@ Command summary:
|
||||
menu Open the interactive menu; `epctl` with no arguments does this too
|
||||
download Download a GitHub release archive into /tmp/epusdt/<tag>/
|
||||
install Install or refresh /opt/epusdt and register epusdt.service
|
||||
upgrade Upgrade to a newer release while keeping the existing .env
|
||||
upgrade Upgrade to a newer release while keeping the existing .env; direct CLI restarts by default, menu mode asks first
|
||||
self-install Install epctl into /usr/local/bin
|
||||
show-config Print the active /opt/epusdt/.env file
|
||||
init-password Request the initial admin password from the local HTTP route
|
||||
@@ -391,6 +421,10 @@ Notes:
|
||||
- privileged commands automatically re-exec through sudo
|
||||
- tags must match real GitHub release tags such as v1.0.6
|
||||
- if --tag is omitted, epctl resolves the latest release and asks for confirmation
|
||||
- upgrade requires an existing install and does not create a new .env
|
||||
- upgrade restarts the service by default after files are replaced
|
||||
- upgrade --prompt-restart asks whether to restart in an interactive terminal
|
||||
- upgrade --no-restart replaces files only and prints a manual restart warning
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
@@ -701,6 +735,152 @@ render_service_debug_info() {
|
||||
journalctl -u "${SERVICE_NAME}" --no-pager -n 100 || true
|
||||
}
|
||||
|
||||
prepare_release_extract() {
|
||||
local tag="$1"
|
||||
local archive_path
|
||||
|
||||
archive_path="$(download_release "${tag}")"
|
||||
extract_release_archive "${tag}" "${archive_path}"
|
||||
}
|
||||
|
||||
deploy_release_payload() {
|
||||
local extract_dir="$1"
|
||||
|
||||
ensure_system_user
|
||||
install -d -m 0755 "${INSTALL_DIR}"
|
||||
install -m 0755 "${extract_dir}/epusdt" "${BIN_PATH}"
|
||||
install -m 0644 "${extract_dir}/.env.example" "${CONFIG_EXAMPLE_PATH}"
|
||||
}
|
||||
|
||||
finalize_install_dir_ownership() {
|
||||
chown -R "${SYSTEM_USER}:${SYSTEM_USER}" "${INSTALL_DIR}"
|
||||
}
|
||||
|
||||
refresh_service_unit() {
|
||||
write_systemd_unit
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
ensure_existing_upgrade_config() {
|
||||
[[ -f "${CONFIG_PATH}" ]] || die "$(trf upgrade_existing_config_required "${CONFIG_PATH}")"
|
||||
}
|
||||
|
||||
create_upgrade_backup_dir() {
|
||||
local tag="$1"
|
||||
local backup_dir
|
||||
|
||||
backup_dir="$(cache_dir_for_tag "${tag}")/backup-current"
|
||||
rm -rf "${backup_dir}"
|
||||
mkdir -p "${backup_dir}"
|
||||
printf '%s\n' "${backup_dir}"
|
||||
}
|
||||
|
||||
backup_release_files() {
|
||||
local backup_dir="$1"
|
||||
|
||||
[[ -f "${BIN_PATH}" ]] && cp -p "${BIN_PATH}" "${backup_dir}/epusdt"
|
||||
[[ -f "${CONFIG_EXAMPLE_PATH}" ]] && cp -p "${CONFIG_EXAMPLE_PATH}" "${backup_dir}/.env.example"
|
||||
[[ -f "${SYSTEMD_UNIT_PATH}" ]] && cp -p "${SYSTEMD_UNIT_PATH}" "${backup_dir}/${SERVICE_NAME}.service"
|
||||
}
|
||||
|
||||
restore_release_files() {
|
||||
local backup_dir="$1"
|
||||
|
||||
install -d -m 0755 "${INSTALL_DIR}"
|
||||
|
||||
if [[ -f "${backup_dir}/epusdt" ]]; then
|
||||
install -m 0755 "${backup_dir}/epusdt" "${BIN_PATH}"
|
||||
else
|
||||
rm -f "${BIN_PATH}"
|
||||
fi
|
||||
|
||||
if [[ -f "${backup_dir}/.env.example" ]]; then
|
||||
install -m 0644 "${backup_dir}/.env.example" "${CONFIG_EXAMPLE_PATH}"
|
||||
else
|
||||
rm -f "${CONFIG_EXAMPLE_PATH}"
|
||||
fi
|
||||
|
||||
if [[ -f "${backup_dir}/${SERVICE_NAME}.service" ]]; then
|
||||
install -m 0644 "${backup_dir}/${SERVICE_NAME}.service" "${SYSTEMD_UNIT_PATH}"
|
||||
else
|
||||
rm -f "${SYSTEMD_UNIT_PATH}"
|
||||
fi
|
||||
|
||||
finalize_install_dir_ownership
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
apply_upgrade_release() {
|
||||
local extract_dir="$1"
|
||||
|
||||
deploy_release_payload "${extract_dir}"
|
||||
finalize_install_dir_ownership
|
||||
refresh_service_unit
|
||||
}
|
||||
|
||||
restart_service_and_verify() {
|
||||
local tag="$1"
|
||||
local backup_dir="${2:-}"
|
||||
|
||||
if systemctl restart "${SERVICE_NAME}" && systemctl is-active --quiet "${SERVICE_NAME}"; then
|
||||
log "$(trf service_upgraded_restarted "${SERVICE_NAME}" "${INSTALL_DIR}" "${tag}")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
render_service_debug_info
|
||||
|
||||
if [[ -n "${backup_dir}" ]]; then
|
||||
warn "$(trf upgrade_restart_failed_rollback_start "${SERVICE_NAME}")"
|
||||
if ! restore_release_files "${backup_dir}"; then
|
||||
die "$(trf upgrade_restart_failed_rollback_failed "${SERVICE_NAME}" "${tag}")"
|
||||
fi
|
||||
if systemctl restart "${SERVICE_NAME}" && systemctl is-active --quiet "${SERVICE_NAME}"; then
|
||||
die "$(trf upgrade_restart_failed_rolled_back "${SERVICE_NAME}" "${tag}")"
|
||||
fi
|
||||
render_service_debug_info
|
||||
die "$(trf upgrade_restart_failed_rollback_failed "${SERVICE_NAME}" "${tag}")"
|
||||
fi
|
||||
|
||||
die "$(trf service_failed_active "${SERVICE_NAME}")"
|
||||
}
|
||||
|
||||
handle_upgrade_restart() {
|
||||
local tag="$1"
|
||||
local restart_mode="$2"
|
||||
local backup_dir="$3"
|
||||
local reply=""
|
||||
|
||||
case "${restart_mode}" in
|
||||
restart)
|
||||
restart_service_and_verify "${tag}" "${backup_dir}"
|
||||
;;
|
||||
prompt)
|
||||
if ! is_interactive_terminal; then
|
||||
die "$(trf action_upgrade_restart_no_tty)"
|
||||
fi
|
||||
|
||||
warn "$(trf action_upgrade_restart_notice "${SERVICE_NAME}")"
|
||||
printf '%b[%s]%b %s' "${COLOR_BOLD}${COLOR_CYAN}" "${EPCTL_NAME}" "${COLOR_RESET}" "$(trf upgrade_restart_prompt "${SERVICE_NAME}")" >&2
|
||||
if ! read -r reply; then
|
||||
reply="n"
|
||||
fi
|
||||
|
||||
if [[ -z "${reply}" ]] || is_affirmative_reply "${reply}"; then
|
||||
restart_service_and_verify "${tag}" "${backup_dir}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
warn "$(trf action_upgrade_restart_skipped "${SERVICE_NAME}" "${SERVICE_NAME}")"
|
||||
;;
|
||||
skip)
|
||||
warn "$(trf action_upgrade_restart_skipped "${SERVICE_NAME}" "${SERVICE_NAME}")"
|
||||
;;
|
||||
*)
|
||||
die "$(trf upgrade_restart_flag_conflict)"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
command_download() {
|
||||
local tag=""
|
||||
local effective_tag archive_path
|
||||
@@ -736,7 +916,8 @@ command_install() {
|
||||
local app_uri="${DEFAULT_APP_URI}"
|
||||
local listen="${DEFAULT_LISTEN}"
|
||||
local original_args=("$@")
|
||||
local effective_tag archive_path extract_dir
|
||||
local effective_tag
|
||||
local extract_dir
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
@@ -784,6 +965,7 @@ command_install() {
|
||||
require_command tar
|
||||
require_command install
|
||||
require_command systemctl
|
||||
require_command cp
|
||||
require_command grep
|
||||
require_command sed
|
||||
require_command getent
|
||||
@@ -792,18 +974,11 @@ command_install() {
|
||||
require_command chown
|
||||
|
||||
effective_tag="$(resolve_effective_tag "${tag}")"
|
||||
archive_path="$(download_release "${effective_tag}")"
|
||||
extract_dir="$(extract_release_archive "${effective_tag}" "${archive_path}")"
|
||||
|
||||
ensure_system_user
|
||||
install -d -m 0755 "${INSTALL_DIR}"
|
||||
install -m 0755 "${extract_dir}/epusdt" "${BIN_PATH}"
|
||||
install -m 0644 "${extract_dir}/.env.example" "${CONFIG_EXAMPLE_PATH}"
|
||||
extract_dir="$(prepare_release_extract "${effective_tag}")"
|
||||
deploy_release_payload "${extract_dir}"
|
||||
create_default_config_if_missing "${app_uri}" "${listen}"
|
||||
chown -R "${SYSTEM_USER}:${SYSTEM_USER}" "${INSTALL_DIR}"
|
||||
|
||||
write_systemd_unit
|
||||
systemctl daemon-reload
|
||||
finalize_install_dir_ownership
|
||||
refresh_service_unit
|
||||
systemctl enable --now "${SERVICE_NAME}"
|
||||
|
||||
if ! systemctl is-active --quiet "${SERVICE_NAME}"; then
|
||||
@@ -845,15 +1020,31 @@ command_self_install() {
|
||||
}
|
||||
|
||||
command_upgrade() {
|
||||
local tag=""
|
||||
local restart_mode="restart"
|
||||
local original_args=("$@")
|
||||
local effective_tag
|
||||
local extract_dir backup_dir
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--tag|--app-uri|--listen)
|
||||
[[ $# -ge 2 ]] || die "$(trf arg_requires_value "$1")"
|
||||
--tag)
|
||||
[[ $# -ge 2 ]] || die "$(trf arg_requires_value "--tag")"
|
||||
tag="$2"
|
||||
shift 2
|
||||
;;
|
||||
--tag=*|--app-uri=*|--listen=*)
|
||||
--tag=*)
|
||||
tag="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--prompt-restart)
|
||||
[[ "${restart_mode}" == "restart" ]] || die "$(trf upgrade_restart_flag_conflict)"
|
||||
restart_mode="prompt"
|
||||
shift
|
||||
;;
|
||||
--no-restart)
|
||||
[[ "${restart_mode}" == "restart" ]] || die "$(trf upgrade_restart_flag_conflict)"
|
||||
restart_mode="skip"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
@@ -867,7 +1058,33 @@ command_upgrade() {
|
||||
done
|
||||
|
||||
ensure_root_via_sudo upgrade "${original_args[@]}"
|
||||
command_install "${original_args[@]}"
|
||||
|
||||
require_root
|
||||
require_linux
|
||||
require_command tar
|
||||
require_command install
|
||||
require_command systemctl
|
||||
require_command cp
|
||||
require_command grep
|
||||
require_command sed
|
||||
require_command getent
|
||||
require_command groupadd
|
||||
require_command useradd
|
||||
require_command chown
|
||||
|
||||
effective_tag="$(resolve_effective_tag "${tag}")"
|
||||
ensure_existing_upgrade_config
|
||||
extract_dir="$(prepare_release_extract "${effective_tag}")"
|
||||
backup_dir="$(create_upgrade_backup_dir "${effective_tag}")"
|
||||
backup_release_files "${backup_dir}"
|
||||
if ! apply_upgrade_release "${extract_dir}"; then
|
||||
warn "$(trf upgrade_deploy_failed_rollback_start "${SERVICE_NAME}")"
|
||||
if restore_release_files "${backup_dir}"; then
|
||||
die "$(trf upgrade_deploy_failed_rolled_back "${SERVICE_NAME}" "${effective_tag}")"
|
||||
fi
|
||||
die "$(trf upgrade_deploy_failed_rollback_failed "${SERVICE_NAME}" "${effective_tag}")"
|
||||
fi
|
||||
handle_upgrade_restart "${effective_tag}" "${restart_mode}" "${backup_dir}"
|
||||
}
|
||||
|
||||
command_show_config() {
|
||||
@@ -1280,9 +1497,9 @@ interactive_upgrade() {
|
||||
|
||||
tag="$(prompt_input "$(trf input_upgrade_tag)")" || return 0
|
||||
if [[ -n "${tag}" ]]; then
|
||||
run_menu_command upgrade --tag "${tag}"
|
||||
run_menu_command upgrade --tag "${tag}" --prompt-restart
|
||||
else
|
||||
run_menu_command upgrade
|
||||
run_menu_command upgrade --prompt-restart
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
+150
-80
@@ -2,9 +2,8 @@
|
||||
set -euo pipefail
|
||||
|
||||
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
readonly IMAGE_TAG="epctl-ubuntu-systemd-test:$(date +%s)-$$"
|
||||
readonly CONTAINER_NAME="epctl-systemd-test-$$"
|
||||
readonly BASE_IMAGE="ubuntu:24.04"
|
||||
readonly BASE_IMAGE="${EPCTL_DOCKER_IMAGE:-ubuntu:24.04}"
|
||||
|
||||
ACTIVE_LANG=""
|
||||
POSITIONAL_ARGS=()
|
||||
@@ -80,10 +79,8 @@ trf() {
|
||||
zh:upgrade_tag_invalid) template='升级 tag 必须是真实的 GitHub release tag,例如 v1.0.6' ;;
|
||||
en:base_image) template='base image: %s' ;;
|
||||
zh:base_image) template='基础镜像:%s' ;;
|
||||
en:building_image) template='building the Ubuntu systemd test image' ;;
|
||||
zh:building_image) template='正在构建 Ubuntu systemd 测试镜像' ;;
|
||||
en:starting_container) template='starting the test container' ;;
|
||||
zh:starting_container) template='正在启动测试容器' ;;
|
||||
en:starting_container) template='starting the Ubuntu systemd test container directly from %s' ;;
|
||||
zh:starting_container) template='正在直接从 %s 启动 Ubuntu systemd 测试容器' ;;
|
||||
en:systemd_not_ready) template='systemd did not become ready inside the container' ;;
|
||||
zh:systemd_not_ready) template='容器内的 systemd 未能及时就绪' ;;
|
||||
en:self_installing) template='installing epctl into PATH' ;;
|
||||
@@ -96,6 +93,14 @@ trf() {
|
||||
zh:waiting_assets) template='正在等待 epusdt 启动并释放 www 静态文件' ;;
|
||||
en:upgrading_release) template='upgrading epusdt from %s to %s' ;;
|
||||
zh:upgrading_release) template='正在将 epusdt 从 %s 升级到 %s' ;;
|
||||
en:verifying_upgrade_skip_restart) template='verifying that `upgrade --no-restart` skips the restart and keeps the current process running' ;;
|
||||
zh:verifying_upgrade_skip_restart) template='正在验证 `upgrade --no-restart` 会跳过重启,并保持当前进程继续运行' ;;
|
||||
en:verifying_upgrade_default_restart) template='verifying that a non-interactive upgrade restarts the service by default' ;;
|
||||
zh:verifying_upgrade_default_restart) template='正在验证非交互升级默认会重启服务' ;;
|
||||
en:verifying_upgrade_prompt_skip) template='verifying that `upgrade --prompt-restart` can skip the restart when `n` is entered' ;;
|
||||
zh:verifying_upgrade_prompt_skip) template='正在验证 `upgrade --prompt-restart` 在输入 `n` 时会跳过重启' ;;
|
||||
en:verifying_upgrade_prompt_restart) template='verifying that `upgrade --prompt-restart` restarts the service by default when Enter is pressed' ;;
|
||||
zh:verifying_upgrade_prompt_restart) template='正在验证 `upgrade --prompt-restart` 在直接回车时默认会重启服务' ;;
|
||||
en:checking_init_password) template='checking init-password behavior' ;;
|
||||
zh:checking_init_password) template='正在检查 init-password 行为' ;;
|
||||
en:expected_second_failure) template='expected init-password to fail after the password was changed' ;;
|
||||
@@ -181,25 +186,24 @@ map_arch() {
|
||||
}
|
||||
|
||||
docker_bash() {
|
||||
docker exec "${CONTAINER_NAME}" bash -lc "$1"
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c "$1"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
local exit_code="$?"
|
||||
if [[ "${exit_code}" -ne 0 ]]; then
|
||||
log "$(trf test_failed_collecting)"
|
||||
docker exec "${CONTAINER_NAME}" bash -lc 'systemctl status epusdt --no-pager || true' || true
|
||||
docker exec "${CONTAINER_NAME}" bash -lc 'journalctl -u epusdt --no-pager -n 100 || true' || true
|
||||
docker logs "${CONTAINER_NAME}" >/dev/null 2>&1 && docker logs "${CONTAINER_NAME}" >&2 || true
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'systemctl status epusdt --no-pager || true' || true
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'journalctl -u epusdt --no-pager -n 100 || true' || true
|
||||
fi
|
||||
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
|
||||
docker rmi -f "${IMAGE_TAG}" >/dev/null 2>&1 || true
|
||||
rm -rf "${TMPDIR_PATH:-}"
|
||||
}
|
||||
|
||||
wait_for_systemd() {
|
||||
local attempt
|
||||
for attempt in $(seq 1 30); do
|
||||
if docker exec "${CONTAINER_NAME}" bash -lc 'systemctl show --property=Version --value >/dev/null 2>&1'; then
|
||||
for attempt in $(seq 1 120); do
|
||||
if docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'systemctl show --property=Version --value >/dev/null 2>&1'; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
@@ -207,6 +211,24 @@ wait_for_systemd() {
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_service_assets() {
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c '
|
||||
for _ in $(seq 1 30); do
|
||||
if systemctl is-active --quiet epusdt && [[ -f /opt/epusdt/www/index.html ]]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
systemctl status epusdt --no-pager || true
|
||||
journalctl -u epusdt --no-pager -n 100 || true
|
||||
exit 1
|
||||
'
|
||||
}
|
||||
|
||||
service_main_pid() {
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'systemctl show --property=MainPID --value epusdt' | tr -d '\r\n'
|
||||
}
|
||||
|
||||
set_language ""
|
||||
parse_global_options "$@"
|
||||
|
||||
@@ -235,39 +257,10 @@ if [[ -n "${UPGRADE_TAG}" ]]; then
|
||||
curl --fail --silent --show-error --head "https://github.com/GMWalletApp/epusdt/releases/download/${UPGRADE_TAG}/SHA256SUMS" >/dev/null
|
||||
fi
|
||||
|
||||
TMPDIR_PATH="$(mktemp -d)"
|
||||
trap cleanup EXIT
|
||||
|
||||
cat > "${TMPDIR_PATH}/Dockerfile" <<EOF
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
ENV container=docker
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
jq \
|
||||
sudo \
|
||||
systemd \
|
||||
systemd-sysv \
|
||||
tar \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& useradd -m -s /bin/bash tester \
|
||||
&& echo 'tester ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/tester \
|
||||
&& chmod 0440 /etc/sudoers.d/tester
|
||||
|
||||
STOPSIGNAL SIGRTMIN+3
|
||||
|
||||
CMD ["/sbin/init"]
|
||||
EOF
|
||||
|
||||
log "$(trf base_image "${BASE_IMAGE}")"
|
||||
log "$(trf building_image)"
|
||||
docker build -t "${IMAGE_TAG}" "${TMPDIR_PATH}" >/dev/null
|
||||
|
||||
log "$(trf starting_container)"
|
||||
log "$(trf starting_container "${BASE_IMAGE}")"
|
||||
docker run -d \
|
||||
--name "${CONTAINER_NAME}" \
|
||||
--privileged \
|
||||
@@ -278,75 +271,152 @@ docker run -d \
|
||||
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
|
||||
-v "${SCRIPT_DIR}:/work" \
|
||||
-w /work \
|
||||
"${IMAGE_TAG}" \
|
||||
/sbin/init >/dev/null
|
||||
"${BASE_IMAGE}" \
|
||||
bash -euo pipefail -c '
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
expect \
|
||||
jq \
|
||||
sudo \
|
||||
systemd \
|
||||
systemd-sysv \
|
||||
tar
|
||||
apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
if ! id -u tester >/dev/null 2>&1; then
|
||||
useradd -m -s /bin/bash tester
|
||||
fi
|
||||
mkdir -p /etc/sudoers.d
|
||||
echo "tester ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/tester
|
||||
chmod 0440 /etc/sudoers.d/tester
|
||||
exec /sbin/init
|
||||
' >/dev/null
|
||||
|
||||
wait_for_systemd || die "$(trf systemd_not_ready)"
|
||||
|
||||
log "$(trf self_installing)"
|
||||
docker exec "${CONTAINER_NAME}" bash -lc "su - tester -c 'cd /work && ./epctl self-install'"
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c "su - tester -c 'cd /work && ./epctl self-install'"
|
||||
docker_bash '[[ -x /usr/local/bin/epctl ]]'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "EPCTL_NO_COLOR=1 epctl help | grep -q \"^用法:\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang zh help | grep -q \"^用法:\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang en help | grep -q \"^Usage:\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'su - tester -c "EPCTL_NO_COLOR=1 epctl help | grep -q \"^用法:\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang zh help | grep -q \"^用法:\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang en help | grep -q \"^Usage:\""'
|
||||
|
||||
log "$(trf downloading_release "${INSTALL_TAG}")"
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${INSTALL_TAG}" bash -lc 'su - tester -c "epctl download --tag ${TAG}"'
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${INSTALL_TAG}" bash -euo pipefail -c 'su - tester -c "epctl download --tag ${TAG}"'
|
||||
|
||||
log "$(trf installing_release "${INSTALL_TAG}")"
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${INSTALL_TAG}" bash -lc 'su - tester -c "epctl install --tag ${TAG} --app-uri http://127.0.0.1:8000"'
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${INSTALL_TAG}" bash -euo pipefail -c 'su - tester -c "epctl install --tag ${TAG} --app-uri http://127.0.0.1:8000"'
|
||||
|
||||
log "$(trf waiting_assets)"
|
||||
docker exec "${CONTAINER_NAME}" bash -lc '
|
||||
for _ in $(seq 1 30); do
|
||||
if systemctl is-active --quiet epusdt && [[ -f /opt/epusdt/www/index.html ]]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
systemctl status epusdt --no-pager || true
|
||||
journalctl -u epusdt --no-pager -n 100 || true
|
||||
exit 1
|
||||
'
|
||||
wait_for_service_assets
|
||||
|
||||
docker_bash '[[ -x /opt/epusdt/epusdt ]]'
|
||||
docker_bash '[[ -f /opt/epusdt/.env ]]'
|
||||
docker_bash 'systemctl is-active --quiet epusdt'
|
||||
docker_bash '[[ -f /opt/epusdt/www/index.html ]]'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "epctl show-config | grep -q \"^install=false$\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang zh show-config | grep -q \"^install=false$\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc '
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'su - tester -c "epctl show-config | grep -q \"^install=false$\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'su - tester -c "EPCTL_NO_COLOR=1 epctl --lang zh show-config | grep -q \"^install=false$\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c '
|
||||
su - tester -c "epctl status >/tmp/epctl-status.out 2>/tmp/epctl-status.err"
|
||||
! grep -q "systemd-journal" /tmp/epctl-status.err
|
||||
'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc '
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c '
|
||||
su - tester -c "epctl logs --lines 50 >/tmp/epctl-logs.out 2>/tmp/epctl-logs.err"
|
||||
! grep -q "systemd-journal" /tmp/epctl-logs.err
|
||||
'
|
||||
|
||||
if [[ -n "${UPGRADE_TAG}" ]]; then
|
||||
log "$(trf upgrading_release "${INSTALL_TAG}" "${UPGRADE_TAG}")"
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -lc 'su - tester -c "epctl upgrade --tag ${TAG}"'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc '
|
||||
for _ in $(seq 1 30); do
|
||||
if systemctl is-active --quiet epusdt && [[ -f /opt/epusdt/www/index.html ]]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
systemctl status epusdt --no-pager || true
|
||||
journalctl -u epusdt --no-pager -n 100 || true
|
||||
exit 1
|
||||
env_checksum_before="$(docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'sha256sum /opt/epusdt/.env | cut -d" " -f1' | tr -d '\r\n')"
|
||||
pid_before_skip_restart="$(service_main_pid)"
|
||||
|
||||
log "$(trf verifying_upgrade_skip_restart)"
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -euo pipefail -c '
|
||||
output_file="$(mktemp)"
|
||||
su - tester -c "EPCTL_NO_COLOR=1 epctl --lang en upgrade --tag ${TAG} --no-restart" >"${output_file}" 2>&1
|
||||
grep -q "systemctl restart epusdt" "${output_file}"
|
||||
! grep -q "Restart .*\[Y/n\]" "${output_file}"
|
||||
rm -f "${output_file}"
|
||||
'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc 'su - tester -c "epctl show-config | grep -q \"^install=false$\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -lc '
|
||||
pid_after_skip_restart="$(service_main_pid)"
|
||||
[[ "${pid_before_skip_restart}" == "${pid_after_skip_restart}" ]]
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -euo pipefail -c '/opt/epusdt/epusdt version | grep -q "^version: ${TAG}$"'
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -euo pipefail -c 'cmp -s /opt/epusdt/.env.example "/tmp/epusdt/${TAG}/extract/.env.example"'
|
||||
env_checksum_after_skip="$(docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'sha256sum /opt/epusdt/.env | cut -d" " -f1' | tr -d '\r\n')"
|
||||
[[ "${env_checksum_before}" == "${env_checksum_after_skip}" ]]
|
||||
|
||||
log "$(trf verifying_upgrade_default_restart)"
|
||||
pid_before_default_restart="$(service_main_pid)"
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -euo pipefail -c '
|
||||
output_file="$(mktemp)"
|
||||
su - tester -c "EPCTL_NO_COLOR=1 epctl --lang en upgrade --tag ${TAG}" >"${output_file}" 2>&1
|
||||
! grep -q "Restart .*\[Y/n\]" "${output_file}"
|
||||
rm -f "${output_file}"
|
||||
'
|
||||
wait_for_service_assets
|
||||
pid_after_default_restart="$(service_main_pid)"
|
||||
[[ "${pid_before_default_restart}" != "${pid_after_default_restart}" ]]
|
||||
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -euo pipefail -c '
|
||||
output_file="$(mktemp)"
|
||||
if su - tester -c "EPCTL_NO_COLOR=1 epctl --lang en upgrade --tag ${TAG} --prompt-restart" </dev/null >"${output_file}" 2>&1; then
|
||||
cat "${output_file}" >&2
|
||||
rm -f "${output_file}"
|
||||
exit 1
|
||||
fi
|
||||
grep -q "interactive terminal" "${output_file}"
|
||||
rm -f "${output_file}"
|
||||
'
|
||||
|
||||
log "$(trf verifying_upgrade_prompt_skip)"
|
||||
pid_before_prompt_skip="$(service_main_pid)"
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -euo pipefail -c '
|
||||
output_file="$(mktemp)"
|
||||
OUTPUT_FILE="${output_file}" expect <<'"'"'EOF'"'"'
|
||||
set timeout 120
|
||||
log_file -noappend $env(OUTPUT_FILE)
|
||||
spawn su - tester -c "EPCTL_NO_COLOR=1 epctl --lang en upgrade --tag $env(TAG) --prompt-restart"
|
||||
expect {
|
||||
-re {Restart .*\[Y/n\]} { send "n\r" }
|
||||
timeout { exit 1 }
|
||||
}
|
||||
expect eof
|
||||
EOF
|
||||
grep -q "systemctl restart epusdt" "${output_file}"
|
||||
rm -f "${output_file}"
|
||||
'
|
||||
pid_after_prompt_skip="$(service_main_pid)"
|
||||
[[ "${pid_before_prompt_skip}" == "${pid_after_prompt_skip}" ]]
|
||||
|
||||
log "$(trf verifying_upgrade_prompt_restart)"
|
||||
pid_before_prompt_restart="$(service_main_pid)"
|
||||
docker exec "${CONTAINER_NAME}" env TAG="${UPGRADE_TAG}" bash -euo pipefail -c '
|
||||
expect <<'"'"'EOF'"'"'
|
||||
set timeout 120
|
||||
spawn su - tester -c "EPCTL_NO_COLOR=1 epctl --lang en upgrade --tag $env(TAG) --prompt-restart"
|
||||
expect {
|
||||
-re {Restart .*\[Y/n\]} { send "\r" }
|
||||
timeout { exit 1 }
|
||||
}
|
||||
expect eof
|
||||
EOF
|
||||
'
|
||||
wait_for_service_assets
|
||||
pid_after_prompt_restart="$(service_main_pid)"
|
||||
[[ "${pid_before_prompt_restart}" != "${pid_after_prompt_restart}" ]]
|
||||
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c 'su - tester -c "epctl show-config | grep -q \"^install=false$\""'
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c '
|
||||
su - tester -c "epctl status >/tmp/epctl-status-upgrade.out 2>/tmp/epctl-status-upgrade.err"
|
||||
! grep -q "systemd-journal" /tmp/epctl-status-upgrade.err
|
||||
'
|
||||
fi
|
||||
|
||||
log "$(trf checking_init_password)"
|
||||
docker exec "${CONTAINER_NAME}" bash -lc '
|
||||
docker exec "${CONTAINER_NAME}" bash -euo pipefail -c '
|
||||
response="$(su - tester -c "epctl init-password")"
|
||||
password="$(printf "%s\n" "${response}" | jq -r ".data.password")"
|
||||
printf "%s\n" "${response}" | jq -e ".status_code == 200 and (.data.password | type == \"string\" and length > 0)" >/dev/null
|
||||
|
||||
+27
-1
@@ -86,6 +86,10 @@ Upgrade to a newer release:
|
||||
./epctl upgrade --tag v1.0.9
|
||||
```
|
||||
|
||||
Running `./epctl upgrade --tag ...` directly restarts `epusdt` immediately after the files are replaced.
|
||||
If you want to replace files only, pass `--no-restart`.
|
||||
If you want an explicit confirmation step, pass `--prompt-restart`; in an interactive terminal the prompt is `[Y/n]`, so pressing Enter restarts by default.
|
||||
|
||||
Inspect config, status, and logs:
|
||||
|
||||
```bash
|
||||
@@ -136,6 +140,27 @@ When `.env` is auto-created, the script applies only the minimum bootstrap chang
|
||||
- `http_listen=<--listen, default 127.0.0.1:8000>`
|
||||
|
||||
If `/opt/epusdt/.env` already exists, install and upgrade keep it unchanged.
|
||||
`/opt/epusdt/.env.example` is refreshed from the current release on every install / upgrade.
|
||||
|
||||
## What Happens During Upgrade
|
||||
|
||||
`upgrade` performs these steps:
|
||||
|
||||
1. Detect the current CPU architecture and download the target GitHub Release archive
|
||||
2. Extract into `/tmp/epusdt/<tag>/extract/`
|
||||
3. Require the existing `/opt/epusdt/.env`; if it is missing, the command fails and tells you to run `install` first
|
||||
4. Replace `/opt/epusdt/epusdt`
|
||||
5. Replace `/opt/epusdt/.env.example`
|
||||
6. Keep the existing `/opt/epusdt/.env`
|
||||
7. Refresh `epusdt.service` and run `systemctl daemon-reload`
|
||||
8. Restart `epusdt` immediately by default
|
||||
|
||||
Additional behavior:
|
||||
|
||||
- `upgrade` no longer creates `.env` and no longer runs `systemctl enable`
|
||||
- `upgrade --no-restart` replaces files only and prints a manual restart warning
|
||||
- `upgrade --prompt-restart` asks whether to restart when an interactive terminal is available
|
||||
- if the restart fails after an upgrade, the script attempts to roll back the previous binary, `.env.example`, and unit file
|
||||
|
||||
## systemd Service Details
|
||||
|
||||
@@ -191,11 +216,12 @@ Examples:
|
||||
|
||||
It performs these checks on the local machine:
|
||||
|
||||
- builds an `ubuntu:24.04` + systemd test image
|
||||
- starts directly from `ubuntu:24.04` and installs systemd plus the test dependencies during container bootstrap
|
||||
- starts a privileged container
|
||||
- runs `epctl self-install` inside the container
|
||||
- downloads real GitHub Release artifacts
|
||||
- installs `epusdt`
|
||||
- when `upgrade-tag` is provided, verifies `upgrade --no-restart`, the default non-interactive `upgrade`, and both the `n` / Enter branches of `upgrade --prompt-restart`
|
||||
- verifies the systemd service, `www/index.html`, config output, logs, and status
|
||||
- verifies that `init-password` succeeds once and later returns `10040` after the admin password is changed
|
||||
|
||||
|
||||
+27
-1
@@ -86,6 +86,10 @@ epctl
|
||||
./epctl upgrade --tag v1.0.9
|
||||
```
|
||||
|
||||
直接执行 `./epctl upgrade --tag ...` 时,脚本会在文件替换完成后默认立即执行 `systemctl restart epusdt`。
|
||||
如果你只想替换文件而不重启,请显式传入 `--no-restart`。
|
||||
如果你希望人工确认,再传 `--prompt-restart`;交互终端下提示为 `[Y/n]`,直接回车默认重启。
|
||||
|
||||
查看配置、状态、日志:
|
||||
|
||||
```bash
|
||||
@@ -136,6 +140,27 @@ EPCTL_ASSUME_YES=1 ./epctl download
|
||||
- `http_listen=<--listen,默认 127.0.0.1:8000>`
|
||||
|
||||
如果 `/opt/epusdt/.env` 已存在,则安装和升级都会保留它,不会覆盖。
|
||||
`/opt/epusdt/.env.example` 则会在每次 install / upgrade 时按当前 release 重新刷新。
|
||||
|
||||
## 升级时会发生什么
|
||||
|
||||
执行 `upgrade` 时,脚本会:
|
||||
|
||||
1. 按当前机器架构下载目标 GitHub Release 压缩包
|
||||
2. 解压到 `/tmp/epusdt/<tag>/extract/`
|
||||
3. 要求现有 `/opt/epusdt/.env` 已存在;若不存在会直接失败,并提示先执行 `install`
|
||||
4. 覆盖 `/opt/epusdt/epusdt`
|
||||
5. 覆盖 `/opt/epusdt/.env.example`
|
||||
6. 保留现有 `/opt/epusdt/.env`
|
||||
7. 刷新 `epusdt.service` 并执行 `systemctl daemon-reload`
|
||||
8. 默认立即执行 `systemctl restart epusdt`
|
||||
|
||||
补充行为:
|
||||
|
||||
- `upgrade` 不会再补写 `.env`,也不会再执行 `systemctl enable`
|
||||
- `upgrade --no-restart` 只替换文件,不重启服务,并输出手动 restart 提示
|
||||
- `upgrade --prompt-restart` 会在交互终端下询问是否重启
|
||||
- 如果升级后的重启失败,脚本会尝试回滚旧的二进制、`.env.example` 和 unit 文件
|
||||
|
||||
## systemd 服务说明
|
||||
|
||||
@@ -191,11 +216,12 @@ GET /admin/api/v1/auth/init-password
|
||||
|
||||
它会在本机:
|
||||
|
||||
- 构建 `ubuntu:24.04` + systemd 测试镜像
|
||||
- 直接从 `ubuntu:24.04` 启动容器,并在容器启动阶段安装 systemd 与测试依赖
|
||||
- 启动一个特权容器
|
||||
- 在容器内执行 `epctl self-install`
|
||||
- 下载真实 GitHub Release
|
||||
- 安装 `epusdt`
|
||||
- 若传入 `upgrade-tag`,验证 `upgrade --no-restart`、默认 `upgrade`、以及 `upgrade --prompt-restart` 的 `n/回车` 分支
|
||||
- 检查 `systemd` 服务、`www/index.html`、配置文件、日志、状态输出
|
||||
- 验证 `init-password` 首次成功、修改管理员密码后再次请求返回 `10040`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user