fix: make v02 git mirror sync manual and atomic

This commit is contained in:
Codex
2026-05-30 02:55:53 +08:00
parent a509d74f8f
commit e46f2c28ea
3 changed files with 96 additions and 54 deletions
+85 -51
View File
@@ -2809,24 +2809,99 @@ repo_url="ssh://git@ssh.github.com:443/pikasTech/HWLAB.git"
repo_path="/cache/pikasTech/HWLAB.git"
lock_dir="/cache/.hwlab-sync.lock"
mkdir -p /cache/pikasTech /root/.ssh
if ! mkdir "\${lock_dir}" 2>/dev/null; then
if ! mkdir "$lock_dir" 2>/dev/null; then
echo "git mirror sync already running"
exit 0
fi
trap 'rmdir "\${lock_dir}" 2>/dev/null || true' EXIT INT TERM
trap 'rmdir "$lock_dir" 2>/dev/null || true' EXIT INT TERM
cp /git-ssh/ssh-privatekey /root/.ssh/id_rsa
chmod 0400 /root/.ssh/id_rsa
export GIT_SSH_COMMAND="ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.ssh/known_hosts -o ConnectTimeout=10 -o ServerAliveInterval=5 -o ServerAliveCountMax=1"
if [ -d "\${repo_path}/objects" ] && [ -f "\${repo_path}/HEAD" ]; then
git -C "\${repo_path}" remote set-url origin "\${repo_url}"
timeout 180 git -C "\${repo_path}" remote update --prune
if [ -d "$repo_path/objects" ] && [ -f "$repo_path/HEAD" ]; then
git -C "$repo_path" remote set-url origin "$repo_url" || git -C "$repo_path" remote add origin "$repo_url"
else
rm -rf "\${repo_path}"
timeout 240 git clone --mirror "\${repo_url}" "\${repo_path}"
rm -rf "$repo_path"
git init --bare "$repo_path"
git -C "$repo_path" remote add origin "$repo_url"
fi
git -C "\${repo_path}" update-server-info
git -C "\${repo_path}" rev-parse refs/heads/v0.2 | tee /cache/HWLAB-v0.2.head
date -u +%Y-%m-%dT%H:%M:%SZ > /cache/HWLAB.last-sync
git -C "$repo_path" config uploadpack.hideRefs refs/mirror-stage
git -C "$repo_path" config --unset-all remote.origin.fetch 2>/dev/null || true
git -C "$repo_path" config --add remote.origin.fetch '+refs/heads/*:refs/mirror-stage/heads/*'
git -C "$repo_path" config --add remote.origin.fetch '+refs/tags/*:refs/mirror-stage/tags/*'
git -C "$repo_path" for-each-ref --format='delete %(refname)' refs/mirror-stage | git -C "$repo_path" update-ref --stdin || true
timeout 180 git -C "$repo_path" fetch --prune origin
git -C "$repo_path" for-each-ref --format='%(objectname) %(refname)' refs/mirror-stage/heads > /tmp/hwlab-stage-heads
git -C "$repo_path" for-each-ref --format='%(refname)' refs/mirror-stage/heads > /tmp/hwlab-stage-head-refs
git -C "$repo_path" for-each-ref --format='%(objectname) %(refname)' refs/mirror-stage/tags > /tmp/hwlab-stage-tags
git -C "$repo_path" for-each-ref --format='%(refname)' refs/mirror-stage/tags > /tmp/hwlab-stage-tag-refs
if [ ! -s /tmp/hwlab-stage-heads ]; then
echo "git mirror sync fetched no branch refs" >&2
exit 41
fi
while read -r sha ref; do
[ -n "$sha" ] || continue
git -C "$repo_path" cat-file -e "$sha^{commit}"
git -C "$repo_path" cat-file -e "$sha^{tree}"
if git -C "$repo_path" rev-list --objects --missing=print "$sha" | grep -q '^?'; then
echo "git mirror sync found missing objects for $ref $sha" >&2
exit 42
fi
done < /tmp/hwlab-stage-heads
while read -r sha ref; do
[ -n "$sha" ] || continue
name=$(printf '%s\n' "$ref" | sed 's#^refs/mirror-stage/heads/##')
git -C "$repo_path" update-ref "refs/heads/$name" "$sha"
done < /tmp/hwlab-stage-heads
while read -r sha ref; do
[ -n "$sha" ] || continue
name=$(printf '%s\n' "$ref" | sed 's#^refs/mirror-stage/tags/##')
git -C "$repo_path" update-ref "refs/tags/$name" "$sha"
done < /tmp/hwlab-stage-tags
git -C "$repo_path" for-each-ref --format='%(refname)' refs/heads > /tmp/hwlab-public-heads
while read -r ref; do
[ -n "$ref" ] || continue
name=$(printf '%s\n' "$ref" | sed 's#^refs/heads/##')
if ! grep -Fxq "refs/mirror-stage/heads/$name" /tmp/hwlab-stage-head-refs; then
git -C "$repo_path" update-ref -d "$ref"
fi
done < /tmp/hwlab-public-heads
git -C "$repo_path" for-each-ref --format='%(refname)' refs/tags > /tmp/hwlab-public-tags
while read -r ref; do
[ -n "$ref" ] || continue
name=$(printf '%s\n' "$ref" | sed 's#^refs/tags/##')
if ! grep -Fxq "refs/mirror-stage/tags/$name" /tmp/hwlab-stage-tag-refs; then
git -C "$repo_path" update-ref -d "$ref"
fi
done < /tmp/hwlab-public-tags
git -C "$repo_path" for-each-ref --format='delete %(refname)' refs/mirror-stage | git -C "$repo_path" update-ref --stdin || true
if git -C "$repo_path" show-ref --verify --quiet refs/heads/v0.2; then
git -C "$repo_path" symbolic-ref HEAD refs/heads/v0.2
elif git -C "$repo_path" show-ref --verify --quiet refs/heads/G14; then
git -C "$repo_path" symbolic-ref HEAD refs/heads/G14
fi
git -C "$repo_path" fsck --connectivity-only --no-dangling >/tmp/hwlab-git-fsck.out
git -C "$repo_path" update-server-info
v02_head=$(git -C "$repo_path" rev-parse refs/heads/v0.2 2>/dev/null || true)
g14_head=$(git -C "$repo_path" rev-parse refs/heads/G14 2>/dev/null || true)
v02_gitops_head=$(git -C "$repo_path" rev-parse refs/heads/v0.2-gitops 2>/dev/null || true)
published_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
printf '%s\n' "$v02_head" | tee /cache/HWLAB-v0.2.head
printf '%s\n' "$published_at" > /cache/HWLAB.last-sync
export v02_head g14_head v02_gitops_head published_at
node - <<'NODE' | tee /cache/HWLAB.last-sync.json
const payload = {
event: "git-mirror-sync",
status: "published",
repository: "pikasTech/HWLAB",
publishedAt: process.env.published_at,
refs: {
"refs/heads/v0.2": process.env.v02_head || null,
"refs/heads/G14": process.env.g14_head || null,
"refs/heads/v0.2-gitops": process.env.v02_gitops_head || null
}
};
console.log(JSON.stringify(payload));
NODE
`,
"http.mjs": `import { createServer } from "node:http";
import { spawn } from "node:child_process";
@@ -3007,47 +3082,6 @@ function contentType(filePath) {
kind: "Service",
metadata: { name: "git-mirror-http", namespace: "devops-infra", labels: { ...labels, "app.kubernetes.io/component": "read-only-http" } },
spec: { selector: { "app.kubernetes.io/name": "git-mirror", "app.kubernetes.io/component": "read-only-http" }, ports: [{ name: "http", port: 80, targetPort: "http" }] }
},
{
apiVersion: "batch/v1",
kind: "CronJob",
metadata: { name: "git-mirror-hwlab-sync", namespace: "devops-infra", labels: { ...labels, "app.kubernetes.io/component": "sync-controller" } },
spec: {
schedule: "*/2 * * * *",
concurrencyPolicy: "Forbid",
successfulJobsHistoryLimit: 3,
failedJobsHistoryLimit: 3,
jobTemplate: {
spec: {
backoffLimit: 1,
activeDeadlineSeconds: 300,
template: {
metadata: { labels: { ...labels, "app.kubernetes.io/component": "sync-controller" } },
spec: {
restartPolicy: "Never",
hostNetwork: true,
dnsPolicy: "ClusterFirstWithHostNet",
volumes: [
{ name: "cache", persistentVolumeClaim: { claimName: "git-mirror-cache" } },
{ name: "git-ssh", secret: { secretName: "git-mirror-github-ssh", defaultMode: 0o400 } },
{ name: "script", configMap: { name: "git-mirror-sync-script", defaultMode: 0o755 } }
],
containers: [{
name: "sync",
image: ciToolsRunnerImage,
imagePullPolicy: "IfNotPresent",
command: ["/script/sync.sh"],
volumeMounts: [
{ name: "cache", mountPath: "/cache" },
{ name: "git-ssh", mountPath: "/git-ssh", readOnly: true },
{ name: "script", mountPath: "/script", readOnly: true }
]
}]
}
}
}
}
}
}
]
};