| #!/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 |
| } |
|
|