self / scripts /release-common.sh
cnxqchen's picture
Upload 6 files
32a85ff verified
Raw History Blame Contribute Delete
6.42 kB
#!/bin/sh
: "${REPO_RELEASES_URL:=https://github.com/caidaoli/ccLoad/releases}"
: "${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"
}
repo_base_url() {
# Strip a trailing "/releases" (and any trailing slash) to get the repo root.
printf '%s\n' "$REPO_RELEASES_URL" | sed -E 's#/+$##; s#/releases$##'
}
resolve_latest_tag() {
# The Atom feed is ordered newest-first and includes prereleases.
releases_feed=$(curl -fsSL "$(repo_base_url)/releases.atom") || return 1
tag_name=$(
printf '%s\n' "$releases_feed" \
| sed -n 's#.*href="[^"]*/releases/tag/\([^"]*\)".*#\1#p' \
| head -n 1
)
[ -n "$tag_name" ] || return 1
printf '%s\n' "$tag_name"
}
fetch_latest_release_info() {
tag_name=$(resolve_latest_tag) || return 1
base_url=$(repo_base_url)
# The expanded_assets fragment lists every asset as an <a href="..."> link.
assets_html=$(curl -fsSL "$base_url/releases/expanded_assets/$tag_name") || return 1
asset_path=$(
printf '%s\n' "$assets_html" \
| grep -Eo 'href="[^"]*/releases/download/[^"]+"' \
| cut -d '"' -f 2 \
| grep -E '/[^/]*linux[-_]amd64(\.tar\.gz)?$' \
| head -n 1 \
|| true
)
[ -n "$asset_path" ] || return 1
case "$asset_path" in
http://*|https://*) asset_url=$asset_path ;;
/*) asset_url="https://github.com$asset_path" ;;
*) asset_url="$base_url/$asset_path" ;;
esac
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
}