Dew-OF-Aurora 2 недель назад
Родитель
Сommit
2d5e9194b7
3 измененных файлов с 58 добавлено и 8 удалено
  1. 10 3
      CLAUDE.md
  2. 12 4
      README.md
  3. 36 1
      scripts/run_update_and_commit.sh

+ 10 - 3
CLAUDE.md

@@ -27,6 +27,13 @@ python3 scripts/domain_updater.py --config config.json
 bash scripts/run_update_and_commit.sh config.json
 bash scripts/run_update_and_commit.sh config.json
 ```
 ```
 
 
+### Force commit to runtime-state once (manual)
+```bash
+bash scripts/run_update_and_commit.sh --force-commit config.json
+# or
+GIT_FORCE_COMMIT=1 bash scripts/run_update_and_commit.sh config.json
+```
+
 ### Update VMess links from selected domain
 ### Update VMess links from selected domain
 ```bash
 ```bash
 python3 scripts/update_vmess_links.py \
 python3 scripts/update_vmess_links.py \
@@ -89,10 +96,10 @@ sudo bash scripts/uninstall_debian.sh --keep-auth-files
 `scripts/run_update_and_commit.sh` wraps the updater and git workflow:
 `scripts/run_update_and_commit.sh` wraps the updater and git workflow:
 - Resolves git top-level robustly and skips git actions if not inside a git repo.
 - Resolves git top-level robustly and skips git actions if not inside a git repo.
 - Requires non-empty `runtime/current_domain.txt` after updater run.
 - Requires non-empty `runtime/current_domain.txt` after updater run.
+- Compares selected domain with `runtime-state` HEAD (`runtime/current_domain.txt`) and skips commit/push when unchanged (default behavior).
 - Copies runtime outputs into `runtime-state` (same branch or temporary worktree).
 - Copies runtime outputs into `runtime-state` (same branch or temporary worktree).
-- Commits only runtime outputs (`runtime/current_domain.txt`, `runtime/current_domain.json`, `runtime/state.json`, `runtime/substore_vars.json`).
-- Commits when `runtime-state` staged diff is non-empty (comparison is branch-targeted, not main-working-tree-only).
-- Targets `runtime-state` branch (configurable via `GIT_RUNTIME_BRANCH`) and includes branch safety check before staging.
+- Commits only runtime outputs (`runtime/current_domain.txt`, `runtime/current_domain.json`, `runtime/state.json`, `runtime/substore_vars.json`) when domain changes.
+- Supports manual force commit via `--force-commit` or `GIT_FORCE_COMMIT=1`; when no content changes it creates an empty commit with a `manual:` commit message that includes selected domain and UTC update time.
 - Supports non-interactive push auth in two modes:
 - Supports non-interactive push auth in two modes:
   - credential helper mode (`GIT_CREDENTIAL_HELPER`, e.g. `store`)
   - credential helper mode (`GIT_CREDENTIAL_HELPER`, e.g. `store`)
   - header mode (`GIT_HTTP_USERNAME` + `GIT_HTTP_TOKEN`/`GIT_HTTP_TOKEN_FILE`)
   - header mode (`GIT_HTTP_USERNAME` + `GIT_HTTP_TOKEN`/`GIT_HTTP_TOKEN_FILE`)

+ 12 - 4
README.md

@@ -190,12 +190,20 @@ sudo bash scripts/install_debian.sh [options]
 `scripts/run_update_and_commit.sh` 的行为:
 `scripts/run_update_and_commit.sh` 的行为:
 
 
 1. 运行 `domain_updater.py` 更新根目录 `runtime/`
 1. 运行 `domain_updater.py` 更新根目录 `runtime/`
-2. 切到(或创建)`runtime-state` worktree
-3. 将 `runtime/` 下四个文件同步到 `runtime-state` worktree
-4. 仅当 **runtime-state 分支内容有差异** 时才 commit
+2. 读取本次选出的域名,并与 `runtime-state` 分支上次记录的域名比较
+3. 域名相同则直接跳过 commit/push(会输出 skip 日志)
+4. 域名变化时,将 `runtime/` 下四个文件同步到 `runtime-state` worktree,再 commit
 5. `--git-push 1` 时要求 push 成功
 5. `--git-push 1` 时要求 push 成功
 
 
-即:不再只看 main 分支当前文件是否变化,而是看 `runtime-state` 目标内容是否有变化。
+手动强制提交(忽略“域名相同”与“无变更”跳过逻辑):
+
+```bash
+bash scripts/run_update_and_commit.sh --force-commit config.json
+# 或
+GIT_FORCE_COMMIT=1 bash scripts/run_update_and_commit.sh config.json
+```
+
+说明:强制模式会使用 `manual:` 前缀的提交信息(包含域名和更新时间);若内容无变化,会创建 empty commit 后继续按配置 push。
 
 
 ---
 ---
 
 

+ 36 - 1
scripts/run_update_and_commit.sh

@@ -4,6 +4,15 @@ set -euo pipefail
 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 DEFAULT_APP_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
 DEFAULT_APP_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
 APP_DIR="$(git -C "$DEFAULT_APP_DIR" rev-parse --show-toplevel 2>/dev/null || printf '%s' "$DEFAULT_APP_DIR")"
 APP_DIR="$(git -C "$DEFAULT_APP_DIR" rev-parse --show-toplevel 2>/dev/null || printf '%s' "$DEFAULT_APP_DIR")"
+force_commit="${GIT_FORCE_COMMIT:-0}"
+if [[ "${1:-}" == "--force-commit" ]]; then
+  force_commit="1"
+  shift
+fi
+if [[ ! "$force_commit" =~ ^[01]$ ]]; then
+  echo "[vmess-domain-rotator] invalid GIT_FORCE_COMMIT=${force_commit}, expected 0 or 1"
+  exit 1
+fi
 CONFIG_PATH="${1:-${APP_DIR}/config.json}"
 CONFIG_PATH="${1:-${APP_DIR}/config.json}"
 DOMAIN_FILE="${APP_DIR}/runtime/current_domain.txt"
 DOMAIN_FILE="${APP_DIR}/runtime/current_domain.txt"
 
 
@@ -116,6 +125,16 @@ if [[ "$work_branch" != "$runtime_branch" ]]; then
   exit 1
   exit 1
 fi
 fi
 
 
+before=""
+if before_raw="$(git -C "$work_dir" show "HEAD:runtime/current_domain.txt" 2>/dev/null)"; then
+  before="$(printf '%s' "$before_raw" | tr -d '\r\n')"
+fi
+
+if [[ "$force_commit" != "1" ]] && [[ -n "$before" ]] && [[ "$after" == "$before" ]]; then
+  echo "[vmess-domain-rotator] selected domain unchanged (${after}), skip git commit and push"
+  exit 0
+fi
+
 mkdir -p "$work_dir/runtime"
 mkdir -p "$work_dir/runtime"
 for file in current_domain.txt current_domain.json state.json substore_vars.json; do
 for file in current_domain.txt current_domain.json state.json substore_vars.json; do
   src="$APP_DIR/runtime/$file"
   src="$APP_DIR/runtime/$file"
@@ -129,15 +148,31 @@ done
 
 
 git -C "$work_dir" add -A runtime/current_domain.txt runtime/current_domain.json runtime/state.json runtime/substore_vars.json || true
 git -C "$work_dir" add -A runtime/current_domain.txt runtime/current_domain.json runtime/state.json runtime/substore_vars.json || true
 
 
+staged_changed="1"
 if git -C "$work_dir" diff --cached --quiet; then
 if git -C "$work_dir" diff --cached --quiet; then
+  staged_changed="0"
+fi
+
+if [[ "$staged_changed" == "0" ]] && [[ "$force_commit" != "1" ]]; then
   echo "[vmess-domain-rotator] no staged changes for ${runtime_branch}, skip git commit"
   echo "[vmess-domain-rotator] no staged changes for ${runtime_branch}, skip git commit"
   exit 0
   exit 0
 fi
 fi
 
 
+commit_extra_args=()
+if [[ "$staged_changed" == "0" ]] && [[ "$force_commit" == "1" ]]; then
+  commit_extra_args+=(--allow-empty)
+  echo "[vmess-domain-rotator] force commit enabled with unchanged content, creating empty commit"
+fi
+
+commit_message="chore: rotate preferred domain to ${after} (${ts})"
+if [[ "$force_commit" == "1" ]]; then
+  commit_message="manual: domain ${after}, updated at ${ts}"
+fi
+
 git -C "$work_dir" \
 git -C "$work_dir" \
   -c user.name="$commit_name" \
   -c user.name="$commit_name" \
   -c user.email="$commit_email" \
   -c user.email="$commit_email" \
-  commit -m "chore: rotate preferred domain to ${after} (${ts})"
+  commit "${commit_extra_args[@]}" -m "$commit_message"
 
 
 if [[ "$push_enabled" != "1" ]]; then
 if [[ "$push_enabled" != "1" ]]; then
   echo "[vmess-domain-rotator] git push disabled by GIT_PUSH_ENABLED=${push_enabled}"
   echo "[vmess-domain-rotator] git push disabled by GIT_PUSH_ENABLED=${push_enabled}"