release: 1.1.12
TallyNote release / linux-x64 (push) Successful in 6m26s

This commit is contained in:
Qiufeng
2026-09-02 20:23:46 +08:00
parent c518890fc3
commit 344985f514
13 changed files with 597 additions and 58 deletions
+100 -24
View File
@@ -57,6 +57,7 @@ INSTALL_PREVIOUS_TARGET=''
INSTALL_NEW_RELEASE=''
INSTALL_WORK_DIR=''
INSTALL_BACKUP_DIR=''
INSTALL_BACKUP_COMPLETE=0
INSTALL_WAS_ACTIVE=0
INSTALL_PATH_WAS_ACTIVE=0
INSTALL_UPDATE_WAS_ACTIVE=0
@@ -64,6 +65,8 @@ INSTALL_WAS_ENABLED=0
INSTALL_PATH_WAS_ENABLED=0
INSTALL_UPDATE_WAS_ENABLED=0
INSTALL_SYSTEMD_TOUCHED=0
ADMIN_INIT_PATH=/usr/local/sbin/tallynote-admin-init
INSTALL_FIRST_INSTALL=0
DATA_DIR_TEMP_ROOT=0
DATA_DIR_ORIGINAL_OWNER=''
@@ -222,6 +225,55 @@ check_requested_port() {
esac
}
run_initial_admin_wizard() {
if (( ! INSTALL_FIRST_INSTALL )); then
return 0
fi
if (( NON_INTERACTIVE )); then
log '非交互模式:跳过管理员初始化;稍后可执行 sudo tallynote-admin-init'
return 0
fi
[[ -r "$PROMPT_INPUT" && -w "$PROMPT_OUTPUT" ]] || {
log '未检测到交互式终端:跳过管理员初始化;稍后可执行 sudo tallynote-admin-init'
return 0
}
[[ -x "$ADMIN_INIT_PATH" ]] || die '管理员初始化命令未安装'
local status choice
if ! status=$("$ADMIN_INIT_PATH" --check 2>/dev/null); then
log '无法检查管理员初始化状态;基础安装已完成,稍后可执行 sudo tallynote-admin-init'
return 0
fi
[[ "$status" == empty ]] || return 0
exec 9<"$PROMPT_INPUT" || die '无法打开终端输入;请稍后执行 sudo tallynote-admin-init'
{
printf '\n首次安装还差一步:请创建管理员账号。\n'
printf '管理员账号用于登录 TallyNote,首次登录后需要设置正式密码。\n'
} > "$PROMPT_OUTPUT"
while :; do
prompt_value '现在创建管理员?输入 yes 继续,其他内容稍后创建' 'yes'
choice=$PROMPT_REPLY
case "$choice" in
yes|YES|Yes|y|Y) break ;;
no|NO|No|n|N|'')
exec 9<&-
log '已跳过管理员初始化;稍后可执行 sudo tallynote-admin-init'
return 0
;;
*) printf '请输入 yes 或 no。\n' > "$PROMPT_OUTPUT" ;;
esac
done
stage '创建首位管理员(密码不会写入安装日志)'
if ! "$ADMIN_INIT_PATH" <&9 > "$PROMPT_OUTPUT"; then
exec 9<&-
log '管理员初始化未完成;基础安装已完成,稍后可执行 sudo tallynote-admin-init'
return 0
fi
exec 9<&-
stage_done '首位管理员创建完成'
}
wait_for_service_health() {
local host=$1 port=$2 health_host health_url attempt
health_host=$host
@@ -847,13 +899,16 @@ rollback_install_if_needed() {
chmod 700 "$DATA_DIR" 2>/dev/null || true
DATA_DIR_TEMP_ROOT=0
fi
if (( INSTALL_COMMITTED == 0 )) && [[ -n "$INSTALL_BACKUP_DIR" && -d "$INSTALL_BACKUP_DIR" ]]; then
if (( INSTALL_COMMITTED == 0 && INSTALL_BACKUP_COMPLETE == 1 )) && [[ -n "$INSTALL_BACKUP_DIR" && -d "$INSTALL_BACKUP_DIR" ]]; then
local backup_name target
for backup_name in tallynote.service tallynote-update.service tallynote-update.path tallynote-uninstall tallynote.env update-signing-key.pub; do
for backup_name in tallynote.service tallynote-update.service tallynote-update.path tallynote-update tallynote-update-runner tallynote-uninstall tallynote-admin-init tallynote.env update-signing-key.pub; do
case "$backup_name" in
tallynote.env) target="$CONFIG_DIR/tallynote.env" ;;
update-signing-key.pub) target="$CONFIG_DIR/update-signing-key.pub" ;;
tallynote-uninstall) target="/usr/local/sbin/tallynote-uninstall" ;;
tallynote-admin-init) target="$ADMIN_INIT_PATH" ;;
tallynote-update) target="/usr/local/sbin/tallynote-update" ;;
tallynote-update-runner) target="/usr/local/libexec/tallynote-update-runner" ;;
*) target="/etc/systemd/system/$backup_name" ;;
esac
[[ ! -L "$target" ]] || continue
@@ -880,28 +935,41 @@ backup_install_files() {
local directory=$1 target name
mkdir -p "$directory"
chmod 700 "$directory"
for name in tallynote.service tallynote-update.service tallynote-update.path; do
target="/etc/systemd/system/$name"
[[ ! -L "$target" ]] || die "现有 systemd 单元不能是符号链接:$target"
# Validate every target before copying any of them. If validation fails, the
# installer has not changed an existing file and rollback must not infer that
# a partial backup is safe to restore from.
for name in tallynote.service tallynote-update.service tallynote-update.path tallynote-update tallynote-update-runner tallynote-uninstall tallynote-admin-init tallynote.env update-signing-key.pub; do
case "$name" in
tallynote.env) target="$CONFIG_DIR/$name" ;;
update-signing-key.pub) target="$CONFIG_DIR/$name" ;;
tallynote-uninstall) target="/usr/local/sbin/$name" ;;
tallynote-admin-init) target="$ADMIN_INIT_PATH" ;;
tallynote-update) target="/usr/local/sbin/$name" ;;
tallynote-update-runner) target="/usr/local/libexec/$name" ;;
*) target="/etc/systemd/system/$name" ;;
esac
[[ ! -L "$target" ]] || die "现有安装文件不能是符号链接:$target"
if [[ -e "$target" ]]; then
[[ -f "$target" ]] || die "现有 systemd 单元不是普通文件:$target"
cp -a -- "$target" "$directory/$name"
[[ -f "$target" ]] || die "现有安装文件不是普通文件:$target"
fi
done
for name in tallynote.env update-signing-key.pub; do
target="$CONFIG_DIR/$name"
[[ ! -L "$target" ]] || die "现有配置不能是符号链接:$target"
for name in tallynote.service tallynote-update.service tallynote-update.path tallynote-update tallynote-update-runner tallynote-uninstall tallynote-admin-init tallynote.env update-signing-key.pub; do
case "$name" in
tallynote.env) target="$CONFIG_DIR/$name" ;;
update-signing-key.pub) target="$CONFIG_DIR/$name" ;;
tallynote-uninstall) target="/usr/local/sbin/$name" ;;
tallynote-admin-init) target="$ADMIN_INIT_PATH" ;;
tallynote-update) target="/usr/local/sbin/$name" ;;
tallynote-update-runner) target="/usr/local/libexec/$name" ;;
*) target="/etc/systemd/system/$name" ;;
esac
if [[ -e "$target" ]]; then
[[ -f "$target" ]] || die "现有配置不是普通文件:$target"
cp -a -- "$target" "$directory/$name"
cp -a -- "$target" "$directory/$name" || die "无法备份现有安装文件:$target"
[[ -f "$directory/$name" ]] || die "现有安装文件备份不完整:$target"
fi
done
target="/usr/local/sbin/tallynote-uninstall"
[[ ! -L "$target" ]] || die "现有卸载器不能是符号链接:$target"
if [[ -e "$target" ]]; then
[[ -f "$target" ]] || die "现有卸载器不是普通文件:$target"
cp -a -- "$target" "$directory/tallynote-uninstall"
fi
INSTALL_BACKUP_COMPLETE=1
}
read_env_value() {
@@ -1091,9 +1159,9 @@ install_release() {
normalize_release_tree "$tmp/unpacked"
[[ -d "$tmp/unpacked/dist" ]] || die 'release archive must contain dist/ at its root'
[[ -x "$tmp/unpacked/bin/tallynote" ]] || die 'release archive must contain executable bin/tallynote'
[[ -f "$tmp/unpacked/package.json" && -f "$tmp/unpacked/dist/server/index.js" && -f "$tmp/unpacked/dist/web/index.html" ]] || die 'release archive is incomplete'
[[ -f "$tmp/unpacked/package.json" && -f "$tmp/unpacked/dist/server/index.js" && -f "$tmp/unpacked/dist/server/cli/admin-init.js" && -f "$tmp/unpacked/dist/web/index.html" ]] || die 'release archive is incomplete'
[[ -f "$tmp/unpacked/systemd/tallynote.service" && -f "$tmp/unpacked/systemd/tallynote-update.service" && -f "$tmp/unpacked/systemd/tallynote-update.path" ]] || die 'release archive is missing systemd units'
[[ -f "$tmp/unpacked/systemd/tallynote.env.example" && -x "$tmp/unpacked/scripts/tallynote-update.sh" && -x "$tmp/unpacked/scripts/tallynote-update-runner.sh" && -x "$tmp/unpacked/uninstall.sh" ]] || die 'release archive is missing update/uninstall support files'
[[ -f "$tmp/unpacked/systemd/tallynote.env.example" && -x "$tmp/unpacked/scripts/tallynote-update.sh" && -x "$tmp/unpacked/scripts/tallynote-update-runner.sh" && -x "$tmp/unpacked/uninstall.sh" && -x "$tmp/unpacked/bin/tallynote-admin-init" ]] || die 'release archive is missing update/uninstall/admin-init support files'
grep -Eq '"version"[[:space:]]*:[[:space:]]*"'"$version"'"([,}]|[[:space:]])' "$tmp/unpacked/package.json" || die 'release package version does not match requested version'
ensure_root_directory "$PREFIX" 755
ensure_root_directory "$PREFIX/releases" 755
@@ -1288,6 +1356,11 @@ main() {
stage_done '发布包校验通过'
[[ "$PREFIX" = /* && "$DATA_DIR" = /* && "$CONFIG_DIR" = /* ]] || die '安装、数据和配置目录必须是绝对路径'
[[ ! -L "$DATA_DIR" && ! -L "$PREFIX" && ! -L "$CONFIG_DIR" ]] || die 'installation/data/config paths must not be symlinks'
if [[ -L "$PREFIX/current" || -e "$PREFIX/current" ]]; then
INSTALL_FIRST_INSTALL=0
else
INSTALL_FIRST_INSTALL=1
fi
stage '停止旧服务并准备安装、配置和数据目录'
id tallynote >/dev/null 2>&1 || useradd --system --user-group --home-dir "$DATA_DIR" --shell /usr/sbin/nologin tallynote
backup_install_files "$INSTALL_BACKUP_DIR"
@@ -1315,17 +1388,19 @@ main() {
stage_done "版本 ${VERSION#v} 已切换为当前版本"
release_dir="$PREFIX/releases/$VERSION"
[[ -f "$release_dir/systemd/tallynote.service" && -f "$release_dir/systemd/tallynote-update.service" && -f "$release_dir/systemd/tallynote-update.path" ]] || die 'release package is missing systemd unit files'
[[ -f "$release_dir/systemd/tallynote.env.example" && -f "$release_dir/scripts/tallynote-update-runner.sh" && -x "$release_dir/uninstall.sh" ]] || die 'release package is missing update/uninstall support files'
[[ -f "$release_dir/systemd/tallynote.env.example" && -f "$release_dir/scripts/tallynote-update-runner.sh" && -x "$release_dir/uninstall.sh" && -x "$release_dir/bin/tallynote-admin-init" && -f "$release_dir/dist/server/cli/admin-init.js" ]] || die 'release package is missing update/uninstall/admin-init support files'
stage '安装 systemd 单元、更新辅助程序和卸载器'
install -d -m 755 /usr/local/libexec /etc/systemd/system
install -d -m 755 /usr/local/sbin /usr/local/libexec /etc/systemd/system
local unit_tmp
unit_tmp=$(mktemp -d)
sed "s#/opt/tallynote#$PREFIX#g; s#/var/lib/tallynote#$DATA_DIR#g" "$release_dir/systemd/tallynote.service" > "$unit_tmp/tallynote.service"
sed "s#/opt/tallynote#$PREFIX#g; s#/var/lib/tallynote#$DATA_DIR#g; s#/var/lib/tallynote-backups#$(dirname -- "$DATA_DIR")/tallynote-backups#g" "$release_dir/systemd/tallynote-update.service" > "$unit_tmp/tallynote-update.service"
sed "s#/var/lib/tallynote#$DATA_DIR#g" "$release_dir/systemd/tallynote-update.path" > "$unit_tmp/tallynote-update.path"
sed "s#/opt/tallynote#$PREFIX#g; s#/etc/tallynote#$CONFIG_DIR#g" "$release_dir/bin/tallynote-admin-init" > "$unit_tmp/tallynote-admin-init"
install -o root -g root -m 644 "$unit_tmp/tallynote.service" /etc/systemd/system/tallynote.service
install -o root -g root -m 644 "$unit_tmp/tallynote-update.service" /etc/systemd/system/tallynote-update.service
install -o root -g root -m 644 "$unit_tmp/tallynote-update.path" /etc/systemd/system/tallynote-update.path
install -o root -g root -m 755 "$unit_tmp/tallynote-admin-init" "$ADMIN_INIT_PATH"
rm -rf "$unit_tmp"
install -o root -g root -m 755 "$release_dir/scripts/tallynote-update.sh" /usr/local/sbin/tallynote-update
install -o root -g root -m 755 "$release_dir/scripts/tallynote-update-runner.sh" /usr/local/libexec/tallynote-update-runner
@@ -1407,10 +1482,10 @@ main() {
printf 'TALLYNOTE_UPDATE_PUBLIC_KEY_FILE=%s\n' "$CONFIG_DIR/update-signing-key.pub" >> "$CONFIG_DIR/tallynote.env"
fi
fi
stage_done 'systemd 单元、更新辅助程序和卸载器已安装'
stage '重新加载 systemd 并启动 TallyNote'
chown root:root "$CONFIG_DIR/tallynote.env"
chmod 640 "$CONFIG_DIR/tallynote.env"
stage_done 'systemd 单元、更新辅助程序和卸载器已安装'
stage '重新加载 systemd 并启动 TallyNote'
systemctl daemon-reload
INSTALL_SYSTEMD_TOUCHED=1
systemctl enable --now tallynote.service tallynote-update.path
@@ -1443,6 +1518,7 @@ main() {
rm -rf -- "$INSTALL_WORK_DIR" 2>/dev/null || true
INSTALL_WORK_DIR=''
stage_done "安装完成:TallyNote ${VERSION#v}"
run_initial_admin_wizard
local access_url access_host access_port configured_host configured_port
access_url=$(read_env_value "$CONFIG_DIR/tallynote.env" TALLYNOTE_PUBLIC_ORIGIN 2>/dev/null || true)
if [[ -z "$access_url" ]]; then