Commit Graph
2 Commits
Author SHA1 Message Date
FinDataHub f112e9d277 feat(paths): 服务资源定位支持 bundle 与源码树两种布局(关闭 L2)
## 问题

backfill_stock_daily.py 把部署路径写死在 sys.path.insert 里:

  sys.path.insert(0, "/Applications/FinDataHub.app/Contents/Resources/akshare_fetcher/src")

macOS 现网能跑,换平台或换安装位置即 ImportError。这是待办清单里长期挂着的 L2。

## 为什么不在脚本里写 try/except 双路径

那样只解决 bundle 形态,源码树运行时仍走 hardcode。正确做法是让「资源在哪」
由 paths.py 统一回答——它已经是路径的唯一事实来源。

## 新增三个函数

- resources_root():bundle → <App>/Contents/Resources;源码树 → 仓库根
- service_dir(name):<root>/<name> 或 <root>/Services/<name>
- service_src_dir(name):service_dir(name)/"src",可直接塞进 sys.path

**判据是内容而非层级**:仓库根下有 Services/,bundle 的资源目录下没有
(build-app.sh 把各服务平铺拷进 Resources/)。首版按「两级往上」硬算,
源码树里解析成 <repo>/Scripts——被 test_real_repo_layout_resolves 当场抓出。

## 验证(两形态实跑)

  源码树:<repo>/Services/akshare_fetcher/src                      存在 ✓
  bundle:/Applications/…/Resources/akshare_fetcher/src            存在 ✓
          import akshare_fetcher 成功 ✓

bundle 侧解析结果与旧硬编码**逐字一致**,故行为零漂移。

## 回归

TestServiceLayout 6 条:bundle / 源码树 / 两者共存时优先 bundle /
都不存在时返回 bundle 形态(错误信息才能指向期望位置)/ src 后缀 / 真实仓库解析。
scripts 257 → 263。
2026-10-27 23:23:06 +08:00
FinDataHub fb76b10033 feat(cross-platform): 收敛路径与进程语义到 paths / process_utils
Windows 迁移 M0。纯新增两个模块 + 4 个脚本接线,**macOS 行为零漂移**。

背景:仓库此前把 /tmp/findatahub-*.pid、~/Library/Logs、
~/Library/Preferences/...plist 直接写死在十来个文件里;采集管线散落着
start_new_session=True、os.kill(pid,0)、signal.SIGHUP 等 POSIX 专属写法。
这些在 Windows 上分别是:路径不存在、ValueError、探测即杀进程、AttributeError。

- paths.py:temp_dir / pid_file / heartbeat_file / log_root / log_dir /
  app_support_dir / backups_dir / config_dir / user_defaults_plist / env_file
- process_utils.py:detach_kwargs / is_running / terminate_pid /
  available_signals / register_signal_handler / restore_default_and_die /
  utf8_subprocess_env

两个必须记住的坑(已写成测试断言):
1. tempfile.gettempdir() 在 macOS 返回 $TMPDIR(/var/folders/.../T),
   与现网 PID 文件所在的 /tmp **不是同一目录**。paths.temp_dir() 返回
   /tmp 是刻意的,混用会导致新旧进程互相看不见。
2. Windows 上 os.kill(pid, 0) 等价于 TerminateProcess,不是「探测」,
   必须用 OpenProcess。

接线:newsflash_collect(PID/心跳/日志/plist)、daily_summary(PID)、
migrate_date_format(备份目录)、install_collector_launchd(plist + 平台守卫)。

同时修 newsflash_collect.write_heartbeat 的静默失败:Swift 端以心跳
300s 未更新判定卡死并重启,而此处原为 except OSError: pass——重启理由
在日志里毫无痕迹。改为状态翻转才记录(心跳 60s 一次,无脑打日志会刷屏)。

验证:现网采集器 status 正常,5 个路径与旧硬编码逐字一致。
回归:test_paths_process_utils.py 32 条 + 心跳可观测性 5 条。
2026-10-26 20:55:31 +08:00