self / scripts /release-common.sh
cnxqchen's picture
Upload 5 files
fa7fe4b verified
#!/bin/sh
: "${REPO_API_URL:=https://api.github.com/repos/caidaoli/ccload/releases/latest}"
: "${RUNTIME_DIR:=/tmp/runtime}"
: "${RELEASES_DIR:=$RUNTIME_DIR/releases}"
: "${CURRENT_LINK:=$RUNTIME_DIR/current}"
: "${CURRENT_TAG_FILE:=$RUNTIME_DIR/current.tag}"
: "${DOWNLOADS_DIR:=$RUNTIME_DIR/downloads}"
: "${SUPERVISOR_CONF:=/etc/supervisord.conf}"
: "${SUPERVISOR_PROGRAM_NAME:=app}"
: "${SUPERVISOR_UPDATER_PROGRAM_NAME:=${SUPERVISOR_PROGRAM_NAME}-updater}"
: "${CURRENT_BINARY_NAME:=app}"
: "${APP_BINARY_NAMES:=}"
log() {
timestamp=$(date '+%Y-%m-%d %H:%M:%S %z')
printf '[%s] %s\n' "$timestamp" "$*" >&2
}
tag_to_dir_name() {
printf '%s\n' "$1" | tr '/:' '__'
}
release_dir_for_tag() {
printf '%s/%s\n' "$RELEASES_DIR" "$(tag_to_dir_name "$1")"
}
ensure_runtime_dirs() {
mkdir -p "$RUNTIME_DIR" "$RELEASES_DIR" "$DOWNLOADS_DIR"
}
current_tag() {
if [ -f "$CURRENT_TAG_FILE" ]; then
tr -d '\n' < "$CURRENT_TAG_FILE"
fi
}
current_binary_path() {
printf '%s/%s\n' "$CURRENT_LINK" "$CURRENT_BINARY_NAME"
}
fetch_latest_release_info() {
response=$(curl -fsSL "$REPO_API_URL") || return 1
tag_name=$(
printf '%s\n' "$response" \
| grep -Eo '"tag_name"[[:space:]]*:[[:space:]]*"[^"]+"' \
| head -n 1 \
| cut -d '"' -f 4 \
|| true
)
asset_url=$(
printf '%s\n' "$response" \
| grep -Eo '"browser_download_url"[[:space:]]*:[[:space:]]*"[^"]*"' \
| cut -d '"' -f 4 \
| grep -E '/[^/]*linux[-_]amd64(\.tar\.gz)?$' \
| head -n 1 \
|| true
)
if [ -z "$tag_name" ] || [ -z "$asset_url" ]; then
return 1
fi
printf '%s|%s\n' "$tag_name" "$asset_url"
}
load_latest_release() {
release_info=$(fetch_latest_release_info) || return 1
LATEST_TAG=${release_info%%|*}
LATEST_ASSET_URL=${release_info#*|}
}
find_release_binary() {
release_dir=$1
for name in $APP_BINARY_NAMES; do
if [ -f "$release_dir/$name" ]; then
printf '%s\n' "$release_dir/$name"
return 0
fi
done
for name in $APP_BINARY_NAMES; do
binary_path=$(find "$release_dir" -maxdepth 3 -type f -name "$name" | head -n 1)
if [ -n "$binary_path" ]; then
printf '%s\n' "$binary_path"
return 0
fi
done
file_list=$(find "$release_dir" -maxdepth 3 -type f ! -name '._*' ! -path '*/__MACOSX/*' | sort)
first_file=$(printf '%s\n' "$file_list" | sed -n '1p')
second_file=$(printf '%s\n' "$file_list" | sed -n '2p')
if [ -n "$first_file" ] && [ -z "$second_file" ]; then
printf '%s\n' "$first_file"
return 0
fi
return 1
}
activate_release() {
tag_name=$1
release_dir=$2
rm -f "$CURRENT_LINK"
ln -s "$release_dir" "$CURRENT_LINK"
printf '%s\n' "$tag_name" > "$CURRENT_TAG_FILE"
}
extract_release_binary() {
source_dir=$1
target_path=$2
binary_path=$(find_release_binary "$source_dir" || true)
if [ -z "$binary_path" ]; then
return 1
fi
if [ "$binary_path" != "$target_path" ]; then
cp "$binary_path" "$target_path"
fi
chmod +x "$target_path"
}
cleanup_workspace() {
workspace=$1
if [ -n "$workspace" ] && [ -d "$workspace" ]; then
rm -rf "$workspace"
fi
}
install_release() {
tag_name=$1
asset_url=$2
ensure_runtime_dirs
release_dir=$(release_dir_for_tag "$tag_name")
release_binary="$release_dir/$CURRENT_BINARY_NAME"
if [ -x "$release_binary" ]; then
activate_release "$tag_name" "$release_dir"
return 0
fi
workspace=$(mktemp -d "$RUNTIME_DIR/install.XXXXXX") || return 1
download_name=$(basename "$asset_url")
if [ -z "$download_name" ] || [ "$download_name" = "/" ] || [ "$download_name" = "." ]; then
download_name="release.asset"
fi
download_path="$DOWNLOADS_DIR/$download_name"
stage_dir="$workspace/stage"
rm -f "$download_path" || {
cleanup_workspace "$workspace"
return 1
}
mkdir -p "$stage_dir" || {
cleanup_workspace "$workspace"
return 1
}
curl -fsSL "$asset_url" -o "$download_path" || {
cleanup_workspace "$workspace"
return 1
}
case "$download_path" in
*.tar.gz)
tar -xzf "$download_path" -C "$stage_dir" || {
cleanup_workspace "$workspace"
return 1
}
extract_release_binary "$stage_dir" "$stage_dir/$CURRENT_BINARY_NAME" || {
cleanup_workspace "$workspace"
return 1
}
;;
*)
cp "$download_path" "$stage_dir/$CURRENT_BINARY_NAME" || {
cleanup_workspace "$workspace"
return 1
}
chmod +x "$stage_dir/$CURRENT_BINARY_NAME" || {
cleanup_workspace "$workspace"
return 1
}
;;
esac
rm -rf "$release_dir" || {
cleanup_workspace "$workspace"
return 1
}
mv "$stage_dir" "$release_dir" || {
cleanup_workspace "$workspace"
return 1
}
cleanup_workspace "$workspace"
activate_release "$tag_name" "$release_dir"
}
ensure_initial_release() {
if load_latest_release; then
if install_release "$LATEST_TAG" "$LATEST_ASSET_URL"; then
return 0
fi
log "警告: 安装最新版本失败,尝试使用已有版本继续启动"
fi
[ -x "$(current_binary_path)" ]
}
restart_managed_service() {
supervisorctl -c "$SUPERVISOR_CONF" restart "$SUPERVISOR_PROGRAM_NAME"
}
update_if_needed() {
load_latest_release || {
log "检查最新版本失败"
return 1
}
installed_tag=$(current_tag)
if [ "$LATEST_TAG" = "$installed_tag" ]; then
log "当前已是最新版本: $LATEST_TAG"
return 0
fi
log "发现新版本: ${installed_tag:-<none>} -> $LATEST_TAG"
install_release "$LATEST_TAG" "$LATEST_ASSET_URL" || {
log "安装新版本失败: $LATEST_TAG"
return 1
}
if [ -n "$installed_tag" ]; then
restart_managed_service
fi
}