diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index eb0649a..b04a676 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -134,6 +134,20 @@ jobs: chmod +x scripts/docker-prepare-tools.sh scripts/docker-prepare-tools.sh "${{ matrix.arch }}" docker-context/tools + # 验证关键工具目录存在且非空 + echo "--- 验证下载结果 ---" + for tool_dir in docker-context/tools/*/; do + if [ -d "$tool_dir" ]; then + echo "✓ $tool_dir ($(du -sh "$tool_dir" | cut -f1))" + fi + done + TOOL_COUNT=$(find docker-context/tools -mindepth 1 -maxdepth 1 -type d | wc -l) + if [ "$TOOL_COUNT" -eq 0 ]; then + echo "::error::预下载工具失败:docker-context/tools 目录为空" + exit 1 + fi + echo "共下载 $TOOL_COUNT 个工具目录" + # 仅 arm/v7 需要 QEMU(用于 apt-get 安装 ffmpeg 等) - name: Set up QEMU if: matrix.arch == 'arm' @@ -211,7 +225,9 @@ jobs: - name: Create and push multi-arch manifest working-directory: /tmp/digests run: | - # 构建 digest 参数列表(仅匹配 SHA256 格式的文件名:64个十六进制字符) + # 构建 digest 参数列表 + # 验证文件名是否为 SHA256 格式(64个十六进制字符),跳过非 digest 文件 + # 原因:actions/download-artifact 合并多个 artifact 时可能引入元数据文件 DIGEST_ARGS="" for dgst in *; do if echo "$dgst" | grep -qE '^[0-9a-f]{64}$'; then diff --git a/Dockerfile b/Dockerfile index bd0aa10..bfe0bdb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -77,7 +77,9 @@ RUN set -eux; \ exit 1; \ fi; \ else \ - echo "错误: 必须提供预编译二进制 (docker-context/bin/) 或 --build-arg tag=vX.Y.Z"; \ + echo "错误: 未找到可用的 bililive-go 二进制"; \ + echo " CI 模式: 请确保 docker-context/bin/bililive-go 存在"; \ + echo " 本地模式: docker build --build-arg tag=v0.8.0-rc.3 -t bililive-go ."; \ exit 1; \ fi; \ chmod +x /usr/bin/bililive-go; \ diff --git a/scripts/docker-prepare-tools.sh b/scripts/docker-prepare-tools.sh index dae4aca..897b83e 100644 --- a/scripts/docker-prepare-tools.sh +++ b/scripts/docker-prepare-tools.sh @@ -41,7 +41,8 @@ download_and_extract() { local url="$1" local dest="$2" # 最终目标目录(例如 tools/ffmpeg/n8.0-latest) local filename - filename=$(basename "$url") + # 清理文件名:移除路径遍历字符和特殊字符,仅保留安全字符 + filename=$(basename "$url" | tr -cd 'a-zA-Z0-9._-') # 确保目标目录的父目录存在 mkdir -p "$(dirname "$dest")" @@ -53,7 +54,7 @@ download_and_extract() { # 使用 mktemp 创建唯一临时文件,避免并发构建时文件冲突 local tmp_file - tmp_file=$(mktemp "/tmp/docker-prepare-tools-XXXXXX-$filename") + tmp_file=$(mktemp "/tmp/docker-prepare-tools-XXXXXX") _CLEANUP_FILES+=("$tmp_file") _CLEANUP_DIRS+=("$tmp_dir") @@ -72,12 +73,11 @@ download_and_extract() { unzip -qo "$tmp_file" -d "$tmp_dir" ;; *) - echo " 错误: 不支持的格式: $filename" - rm -rf "$tmp_dir" + echo " 错误: 不支持的格式: $filename" >&2 + # trap 会自动清理临时文件和目录 exit 1 ;; esac - rm -f "$tmp_file" # 模拟 remotetools 的 "单目录提升" 逻辑: # 如果解压后顶层只有一个子目录,将其内容提升为目标目录 @@ -91,13 +91,18 @@ download_and_extract() { echo " 检测到单一顶层目录 '$first_entry',自动提升" rm -rf "$dest" mv "$tmp_dir/$first_entry" "$dest" - rm -rf "$tmp_dir" + rm -rf "$tmp_dir" # 顶层目录已空,清理残留 else echo " 直接使用解压内容" rm -rf "$dest" - mv "$tmp_dir" "$dest" + mv "$tmp_dir" "$dest" # tmp_dir 被 rename,不再需要清理 fi + # 下载和解压成功,清理临时文件并从 trap 追踪中移除 + rm -f "$tmp_file" 2>/dev/null || true + _CLEANUP_FILES=("${_CLEANUP_FILES[@]/$tmp_file}") + _CLEANUP_DIRS=("${_CLEANUP_DIRS[@]/$tmp_dir}") + echo " 完成: $dest" } diff --git a/src/cmd/bililive/bililive.go b/src/cmd/bililive/bililive.go index bb9f848..4995913 100644 --- a/src/cmd/bililive/bililive.go +++ b/src/cmd/bililive/bililive.go @@ -77,9 +77,11 @@ func getConfigBesidesExecutable() (*configs.Config, error) { // 必须优先使用用户原始目录的配置文件。 if launcherExe := os.Getenv("BILILIVE_LAUNCHER_EXE"); launcherExe != "" { launcherConfigPath := filepath.Join(filepath.Dir(launcherExe), "config.yml") - if config, err := configs.NewConfigWithFile(launcherConfigPath); err == nil { + if config, loadErr := configs.NewConfigWithFile(launcherConfigPath); loadErr == nil { fmt.Fprintf(os.Stderr, "[Config] 使用 Launcher 目录的配置文件: %s\n", launcherConfigPath) return config, nil + } else { + fmt.Fprintf(os.Stderr, "[Config] Launcher 配置文件加载失败 (%s): %v,回退到 exe 目录\n", launcherConfigPath, loadErr) } }