workflow.md 4.8 KB

Workflow

flowchart TD
    %% =========================
    %% Entry points
    %% =========================
    A1[systemd timer 触发<br/>OnBootSec=2min / OnUnitActiveSec=interval] --> A2[vmess-domain-rotator.service]
    A2 --> A3[run_update_and_commit.sh config.json]

    A4[手动执行<br/>bash scripts/run_update_and_commit.sh config.json] --> A3
    A5[仅手动更新不走服务提交<br/>python3 scripts/domain_updater.py --config config.json] --> B1

    %% =========================
    %% domain_updater.py pipeline
    %% =========================
    subgraph U["domain_updater.py(域名选择主流程)"]
      direction TB
      B1[读取 config.json<br/>解析 output.runtime_dir] --> B2[读取 runtime/state.json<br/>last_good_domain]
      B2 --> B3[请求 API<br/>api.url/method/headers/params/body/timeout]
      B3 --> B4[解析候选域名<br/>parser.field_paths/json_paths/regex fallback]
      B4 --> B5[域名过滤<br/>domain_filter.include_suffixes/exclude_regex]
      B5 --> B6[记录级过滤<br/>record_filter.exclude_if_any<br/>contains/equals/regex]
      B6 --> B7[解析评分记录<br/>scoring.records_path/ip_field/created_time_field/score_fields]
      B7 --> B8[排序<br/>within_hours + prefer_lower/use_api_order]
      B8 --> B9{healthcheck.enabled?}

      B9 -- 是 --> B10[TLS 探测候选<br/>attempts/timeout_ms/port/tls_verify]
      B9 -- 否 --> B11[跳过 healthcheck]
      B10 --> B12[choose_domain]
      B11 --> B12

      B12 --> B13{是否选出域名?}
      B13 -- 是 --> B16[status=ok]
      B13 -- 否 --> B14{last_good_domain 存在?}
      B14 -- 是 --> B15[使用 last_good_domain<br/>status=fallback_last_good]
      B14 -- 否 --> BE1[报错并退出]

      B15 --> B17[写 runtime/current_domain.txt]
      B16 --> B17
      B17 --> B18[写 runtime/current_domain.json]
      B18 --> B19[写 runtime/substore_vars.json]
      B19 --> B20[可选渲染 v2ray 模板<br/>v2ray.template_file/output_file/replace_token]
      B20 --> B21[写 runtime/state.json]
      B21 --> B22[可选 notify.command]
      B22 --> B23[stdout 输出本次 JSON 结果]

      BE1 --> BE2[写 state.json status=error]
      BE2 --> BE3{last_good_domain 存在?}
      BE3 -- 是 --> BE4[写 current_domain*.json/txt<br/>status=error_use_last_good]
      BE4 --> BE5[notify.command + 输出 error_use_last_good]
      BE3 -- 否 --> BE6[stderr 输出 error 并 exit 1]
    end

    %% updater 结果回到 wrapper
    B23 --> C1
    BE5 --> C1

    %% =========================
    %% run_update_and_commit.sh pipeline
    %% =========================
    subgraph W["run_update_and_commit.sh(runtime-state 自动提交/推送)"]
      direction TB
      C1[检查 runtime/current_domain.txt 存在且非空] --> C2{满足 git 环境?<br/>git存在+在仓库+HEAD有效}
      C2 -- 否 --> C0[仅完成本地 runtime 更新并退出]
      C2 -- 是 --> C3[确定 runtime_branch / push_remote / auth 选项]
      C3 --> C4{当前分支是 runtime-state?}
      C4 -- 是 --> C5[直接在当前仓库操作]
      C4 -- 否 --> C6[创建临时 worktree]
      C6 --> C7{runtime-state 分支存在?}
      C7 -- 本地存在 --> C8[checkout 本地 runtime-state]
      C7 -- 仅远程存在 --> C9[fetch 后 checkout]
      C7 -- 都不存在 --> C10[创建 orphan runtime-state]
      C8 --> C11
      C9 --> C11
      C10 --> C11
      C5 --> C11[读取 runtime-state HEAD 的 runtime/current_domain.txt]

      C11 --> C12{force_commit!=1 且 新旧域名相同?}
      C12 -- 是 --> C13[skip git commit/push]
      C12 -- 否 --> C14[同步 4 个 runtime 文件到目标 worktree]
      C14 --> C15[git add runtime/*.txt/json]
      C15 --> C16{有 staged 变化?}
      C16 -- 否 且 force=0 --> C17[skip commit]
      C16 -- 否 且 force=1 --> C18[allow-empty commit]
      C16 -- 是 --> C19[正常 commit]

      C18 --> C20[提交信息 manual: ...]
      C19 --> C21[提交信息 chore: rotate preferred domain ...]
      C20 --> C22{GIT_PUSH_ENABLED=1?}
      C21 --> C22
      C22 -- 否 --> C23[结束(仅本地提交)]
      C22 -- 是 --> C24{有可用 remote?}
      C24 -- 否且required=1 --> C25[exit 1]
      C24 -- 否且required=0 --> C26[跳过 push]
      C24 -- 是 --> C27[按认证方式 push<br/>credential helper 或 HTTP header/token]
      C27 --> C28{push 成功?}
      C28 -- 是 --> C29[结束]
      C28 -- 否且required=1 --> C30[exit 1]
      C28 -- 否且required=0 --> C31[记录失败并结束]
    end

    %% =========================
    %% Consumers
    %% =========================
    B18 --> D1[runtime/current_domain.json 对外可读]
    D1 --> D2[substore/operator_template.js 拉取 JSON]
    D2 --> D3[scriptResourceCache 缓存 domain(默认 5 分钟)]
    D3 --> D4[重写匹配节点 vmess server]

    C29 --> E1[runtime-state 分支更新]
    E1 --> E2[下游通过 raw/runtime-state/runtime/*.json 消费]