feat(chv-guest): 轻量镜像 + Gitea 双向同步(停全量版,改 Ubuntu Minimal)
- build.sh 改轻量:Ubuntu Minimal 底座、apt 精简(git/ssh/cloud-init/
python3/pip/curl/ca-certificates/xz-utils/fonts-wqy-microhei)、
Playwright + chromium-headless-shell、去 B-16 +12G 扩盘;
加 git + gt 1.0.0(官方 install.sh + SHA256 校验,GITEA_CLI_VERSION
钉 1.0.0)与 chv-sync 启动钩子(cloud-init runcmd)
- B-19(新发现):dpkg freeze 的 ${Package} 会被客机 shell 展开
成空,284 行全空白;加反斜杠转义,探针实测已出真实版本
- buildinfo.py:轻量 contents、chromium-headless-shell、gt_version
- generate/check_lockfile.py:gt/chv-sync 声明来源、headless-shell 映射
- chv-sync.sh(新):boot/pull/push/status,分叉/脏树失败关闭,
凭证走 seed 盘 guest-env,不进镜像
- README §1/§2/§3/§4 改轻量口径,新增 §7 Gitea 双向同步
- tests:ChvSyncTest 6 项(本地 bare 仓当远端);针对性测试 144 项全过
This commit is contained in:
1 parent
9e60d5dfaa
commit
4d09518338
7 files changed
+452
-73
No files matched your search
Executable
+135
@@ -0,0 +1,135 @@
|
||||
#!/bin/bash
|
||||
# chv-sync:轻量 VM 里的 Gitea 双向同步(用户工作区口径)。
|
||||
#
|
||||
# 主角是用户,不是 agent:用户在 Gitea 上的仓库在 VM 启动时
|
||||
# clone 到 ~/workspace,用完再同步回 Gitea;VM 是用户的工作区,
|
||||
# Gitea 是权威远端。同步口径沿用 Git Workspace Platform 已定
|
||||
# 的规则:分叉或冲突时失败关闭,不强制覆盖、不自动 rebase、
|
||||
# 不静默丢提交。
|
||||
#
|
||||
# 凭证绝不进镜像。启动时由 seed 盘投递 /etc/chv/guest-env
|
||||
# (0600,root 所有),格式(KEY=VALUE,每行一项):
|
||||
# GITEA_URL=https://gitea.tose.top
|
||||
# GITEA_USER=<Gitea 用户名>
|
||||
# GITEA_TOKEN=<专用 API token,仅此次测试/此次 VM 有效>
|
||||
# SYNC_REPO=<owner/repo>
|
||||
# SYNC_BRANCH=<可选,默认分支>
|
||||
# GITEA_EMAIL=<可选,commit 身份邮箱;不给则留镜像占位>
|
||||
# boot(root,cloud-init runcmd 调)读 guest-env,把 token 写进
|
||||
# /home/chv/.git-credentials(0600,chv 所有,git credential
|
||||
# store 标准机制),再以 chv 身份 clone 到 ~/workspace/<仓名>。
|
||||
# pull/push/status 以 chv 身份跑,只用已落盘的 credential,不
|
||||
# 再读 guest-env;token 不会出现在 git remote URL 或镜像里。
|
||||
#
|
||||
# 用法:
|
||||
# chv-sync boot # 启动时 clone(已 clone 则静默退出,不自动 pull)
|
||||
# chv-sync pull [仓目录] # 脏树拒绝;只接受 fast-forward,分叉即失败
|
||||
# chv-sync push [仓目录] # 普通 push;无 upstream 时 push -u origin HEAD;
|
||||
# # 非 fast-forward 即失败,绝不 --force
|
||||
# chv-sync status [仓目录] # 本地状态 + 最近提交
|
||||
# 仓目录省略时取 $WORKSPACE 下唯一的 git 仓库;多个时必须显式给。
|
||||
set -euo pipefail
|
||||
|
||||
GUEST_ENV="${CHV_GUEST_ENV:-/etc/chv/guest-env}"
|
||||
WORKSPACE="${CHV_WORKSPACE:-/home/chv/workspace}"
|
||||
USER_HOME="${CHV_USER_HOME:-/home/chv}"
|
||||
SYNC_USER="${CHV_USER:-chv}"
|
||||
|
||||
die() { echo "chv-sync: $*" >&2; exit 1; }
|
||||
info() { echo "chv-sync: $*"; }
|
||||
|
||||
# 解析仓目录:显式给则用它;否则取 WORKSPACE 下唯一的 git 仓库。
|
||||
resolve_repo() {
|
||||
if [ -n "${1:-}" ]; then
|
||||
[ -d "$1/.git" ] || die "不是 git 仓库:$1"
|
||||
printf '%s' "$1"
|
||||
return 0
|
||||
fi
|
||||
local found=()
|
||||
local d
|
||||
for d in "$WORKSPACE"/*/; do
|
||||
[ -d "${d}.git" ] && found+=("${d%/}")
|
||||
done
|
||||
if [ "${#found[@]}" -eq 1 ]; then
|
||||
printf '%s' "${found[0]}"
|
||||
return 0
|
||||
fi
|
||||
die "WORKSPACE 下 git 仓库不是唯一(${#found[@]} 个),请显式给仓目录"
|
||||
}
|
||||
|
||||
cmd_boot() {
|
||||
# 无 guest-env = 本次启动不同步任何仓,静默成功(VM 照常用)。
|
||||
[ -f "$GUEST_ENV" ] || { info "无 $GUEST_ENV,本次启动不同步"; exit 0; }
|
||||
# shellcheck disable=SC1090
|
||||
set -a; . "$GUEST_ENV"; set +a
|
||||
: "${GITEA_URL:?guest-env 缺 GITEA_URL}"
|
||||
: "${GITEA_USER:?guest-env 缺 GITEA_USER}"
|
||||
: "${GITEA_TOKEN:?guest-env 缺 GITEA_TOKEN}"
|
||||
: "${SYNC_REPO:?guest-env 缺 SYNC_REPO}"
|
||||
[ "$(id -u)" = "0" ] || die "boot 须以 root 运行(cloud-init runcmd)"
|
||||
local host="${GITEA_URL#https://}"
|
||||
host="${host#http://}"
|
||||
host="${host%%/*}"
|
||||
local name="${SYNC_REPO##*/}"
|
||||
local dir="$WORKSPACE/$name"
|
||||
mkdir -p "$WORKSPACE"
|
||||
chown "$SYNC_USER:$SYNC_USER" "$WORKSPACE"
|
||||
# token 只落盘进用户家目录的 credential store(0600),
|
||||
# 不进 git remote URL、不进镜像、不进日志(此处不打印 token)。
|
||||
local creds="$USER_HOME/.git-credentials"
|
||||
printf 'https://%s:%s@%s\n' "$GITEA_USER" "$GITEA_TOKEN" "$host" > "$creds"
|
||||
chmod 0600 "$creds"
|
||||
chown "$SYNC_USER:$SYNC_USER" "$creds"
|
||||
if [ -d "$dir/.git" ]; then
|
||||
info "已 clone:$dir(不自动 pull,改动由用户显式同步)"
|
||||
exit 0
|
||||
fi
|
||||
[ -e "$dir" ] && die "$dir 已存在但不是 git 仓库,请人工处理"
|
||||
local clone_args=(clone)
|
||||
[ -n "${SYNC_BRANCH:-}" ] && clone_args+=(--branch "$SYNC_BRANCH")
|
||||
clone_args+=("$GITEA_URL/$SYNC_REPO.git" "$dir")
|
||||
runuser -u "$SYNC_USER" -- env HOME="$USER_HOME" git "${clone_args[@]}"
|
||||
runuser -u "$SYNC_USER" -- env HOME="$USER_HOME" git -C "$dir" config user.name "$GITEA_USER"
|
||||
if [ -n "${GITEA_EMAIL:-}" ]; then
|
||||
runuser -u "$SYNC_USER" -- env HOME="$USER_HOME" git -C "$dir" config user.email "$GITEA_EMAIL"
|
||||
fi
|
||||
info "已 clone $SYNC_REPO 到 $dir,HEAD=$(runuser -u "$SYNC_USER" -- env HOME="$USER_HOME" git -C "$dir" rev-parse --short HEAD)"
|
||||
}
|
||||
|
||||
cmd_pull() {
|
||||
local dir
|
||||
dir="$(resolve_repo "${1:-}")"
|
||||
[ -z "$(git -C "$dir" status --porcelain)" ] \
|
||||
|| die "工作树有未提交改动,拒绝 pull(先 commit 或 stash)"
|
||||
git -C "$dir" pull --ff-only \
|
||||
|| die "pull 不是 fast-forward(本地与远端已分叉),请人工处理,不自动 rebase/merge"
|
||||
info "已同步:$dir HEAD=$(git -C "$dir" rev-parse --short HEAD)"
|
||||
}
|
||||
|
||||
cmd_push() {
|
||||
local dir
|
||||
dir="$(resolve_repo "${1:-}")"
|
||||
if git -C "$dir" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
|
||||
git -C "$dir" push \
|
||||
|| die "push 被拒绝(远端已有新提交,先 pull 再 push,不 --force)"
|
||||
else
|
||||
git -C "$dir" push -u origin HEAD \
|
||||
|| die "push 被拒绝(远端已有新提交,先 pull 再 push,不 --force)"
|
||||
fi
|
||||
info "已 push:$dir HEAD=$(git -C "$dir" rev-parse --short HEAD)"
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
local dir
|
||||
dir="$(resolve_repo "${1:-}")"
|
||||
git -C "$dir" status -sb
|
||||
git -C "$dir" log --oneline -5
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
boot) shift; cmd_boot "$@" ;;
|
||||
pull) shift; cmd_pull "$@" ;;
|
||||
push) shift; cmd_push "$@" ;;
|
||||
status) shift; cmd_status "$@" ;;
|
||||
*) echo "用法:chv-sync boot|pull|push|status [仓目录]" >&2; exit 2 ;;
|
||||
esac
|
||||
Reference in new issue
Block a user