refactor: 迁移至 bashly CLI 与功能域模块结构

- CLI 定义迁移至 src/bashly.yml(bashly 1.4 生成参数解析),入口收敛为 src/root_command.sh
- 源码按功能域重组:config/storage/integrate/media/pipeline,消除扁平散落
- 新增零依赖测试框架 tests/(123 用例)与 CI 流水线(lint + test + build + 产物一致性)
- build.sh 支持版本单一来源注入与 --check 产物校验;新增 lint.sh(bash -n + shellcheck 零容忍)
- 删除旧单文件 media_organizer.sh 与旧扁平模块
This commit is contained in:
Shuery committed 2026-08-14 22:41:28 +08:00
1 parent d978a13a83
commit 0df07ad0ae
48 files changed
+5813 -6812

No files matched your search

+56 -76
View File
@@ -1,106 +1,86 @@
#!/usr/bin/env bash
# ============================================================================
# 构建脚本:将 src/ 下的模块拼接为单个自包含分发脚本 media_organizer.sh
# 构建脚本:bashly generate → 单文件分发脚本 dist/media_organizer
#
# 用法:
# ./build.sh 构建 media_organizer.sh(覆盖根目录文件)
# ./build.sh --check 仅校验根目录 media_organizer.sh 是否与最新源码一致
# ./build.sh 构建(bashly generate + 语法/重复函数检查)
# ./build.sh --check 仅校验当前 dist/media_organizer 是否与 src/ 最新源码一致
#
# 模块清单(顺序 = 依赖顺序,不可随意调整):
# src/main.sh 文件头注释 + 常量 + 全局变量
# src/lib/log.sh 日志与色彩(最先加载,保证全脚本日志输出一致)
# src/lib/config.sh 配置加载与认证
# src/lib/maps.sh 特典映射与季偏移
# src/lib/strings.sh 字符串工具
# src/lib/cache.sh 缓存管理(cache_* 系列)
# src/lib/tmdb.sh TMDB API(tmdb_api 统一缓存封装)
# src/lib/filename.sh 媒体文件名解析
# src/lib/registry.sh 识别登记与并发池(register_* / emit / pool_run)
# src/lib/identify.sh 媒体识别(电影/电视剧)
# src/lib/ai.sh AI 批处理(请求构造/调用/特典学习)
# src/lib/link.sh 硬链接基础工具(inode 判断/链接/错误处理)
# src/lib/args.sh 帮助与参数解析
# src/lib/process.sh 目录校验/扫描/识别流水线
# src/lib/ai_resolve.sh AI 结果消费(重搜/匹配/回退命名)
# src/lib/link_media.sh 创建硬链接及伴随文件
# src/lib/report.sh 对照表导出与运行汇总
# src/lib/entry.sh main() 唯一入口(文件末尾调用)
# 版本单一来源:src/lib/main.sh 的 SCRIPT_VERSION 是唯一权威版本号,
# 构建时注入 bashly.yml 的 version 字段(产物 --version 与脚本内版本一致)。
#
# 依赖:bashly(gem install bashly;开发期工具,产物运行无需 Ruby)
# ============================================================================
set -euo pipefail
cd "$(dirname "$0")"
OUT="media_organizer.sh"
OUT="dist/media_organizer"
BASE="$(pwd)"
MODULES=(
src/main.sh
src/lib/log.sh
src/lib/config.sh
src/lib/maps.sh
src/lib/strings.sh
src/lib/cache.sh
src/lib/tmdb.sh
src/lib/filename.sh
src/lib/registry.sh
src/lib/identify.sh
src/lib/ai.sh
src/lib/link.sh
src/lib/args.sh
src/lib/process.sh
src/lib/ai_resolve.sh
src/lib/link_media.sh
src/lib/report.sh
src/lib/entry.sh
)
# 预检:所有模块存在
for m in "${MODULES[@]}"; do
if [[ ! -f "$m" ]]; then
echo "错误:缺失模块 $m(请先拆分源码或检查模块清单)" >&2
exit 1
# 定位 bashly:环境变量 BASHLY 覆盖 > PATH > 本仓库 .rv-rubies(rv 管理的 Ruby)
find_bashly() {
if [[ -n "${BASHLY:-}" ]] && [[ -x "$BASHLY" ]]; then
echo "$BASHLY"
return 0
fi
done
tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
# 生成:shebang + 生成说明 + 按序拼接全部模块
{
printf '#!/bin/bash\n'
printf '%s\n' \
'################################################################################' \
'# 本文件由 build.sh 从 src/ 自动生成,请勿直接编辑。' \
'# 修改源码请编辑 src/ 下的模块文件(模块清单见 build.sh),然后运行 ./build.sh。' \
'################################################################################'
printf '\n'
for m in "${MODULES[@]}"; do
cat "$m"
if command -v bashly >/dev/null 2>&1; then
command -v bashly
return 0
fi
local d
for d in "$BASE"/.rv-rubies/*/bin/bashly; do
[[ -x "$d" ]] && {
echo "$d"
return 0
}
done
} >"$tmp"
return 1
}
if ! BASHLY_CMD="$(find_bashly)"; then
echo "错误:未找到 bashly(gem install bashly,或设 BASHLY=/path/to/bashly)" >&2
exit 1
fi
# 版本单一来源:从 main.sh 提取 SCRIPT_VERSION
VERSION="$(sed -n 's/^readonly SCRIPT_VERSION="\([^"]*\)"/\1/p' src/lib/main.sh | head -1)"
if [[ -z "$VERSION" ]]; then
echo "错误:无法从 src/lib/main.sh 提取 SCRIPT_VERSION" >&2
exit 1
fi
# 在临时目录生成(注入版本号,不触碰工作区源码)
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
cp -r src "$work/src"
sed -i "s/^version: .*/version: $VERSION/" "$work/src/bashly.yml"
(cd "$work" && "$BASHLY_CMD" generate --quiet >/dev/null)
generated="$work/media_organizer"
# 语法检查
if ! bash -n "$tmp"; then
if ! bash -n "$generated"; then
echo "错误:构建产物语法检查未通过" >&2
exit 1
fi
# 重复函数定义检查(防止模块合并后命名冲突)
dups="$(grep -oE '^[A-Za-z_][A-Za-z0-9_]*\(\) \{' "$tmp" | sort | uniq -d || true)"
# 重复函数定义检查
dups="$(grep -oE '^[A-Za-z_][A-Za-z0-9_]*\(\) \{' "$generated" | sort | uniq -d || true)"
if [[ -n "$dups" ]]; then
echo "错误:存在重复函数定义:$dups" >&2
exit 1
fi
if [[ "${1:-}" == "--check" ]]; then
if cmp -s "$tmp" "$OUT"; then
echo "OK:$OUT 与最新源码一致"
if [[ -f "$OUT" ]] && cmp -s "$generated" "$OUT"; then
echo "OK:$OUT 与最新源码一致(v$VERSION)"
else
echo "DIFF:$OUT 已过期,请运行 ./build.sh 重新生成" >&2
diff "$OUT" "$tmp" | head -50 >&2 || true
echo "DIFF:$OUT 已过期或不存在,请运行 ./build.sh 重新生成" >&2
exit 1
fi
else
chmod +x "$tmp"
mv "$tmp" "$OUT"
mkdir -p "$(dirname "$OUT")"
chmod +x "$generated"
mv "$generated" "$OUT"
trap - EXIT
echo "构建完成:$OUT($(wc -l <"$OUT") 行,$(wc -c <"$OUT") 字节)"
echo "构建完成:$OUT v$VERSION($(wc -l <"$OUT") 行,$(wc -c <"$OUT") 字节)"
fi