Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a mirror-clone.sh wrapper script to run mirror-clone in a tunasync working directory, driven by environment variables.
Changes:
- Introduces a bash entrypoint that validates
TUNASYNC_MIRRORCLONE_SOURCEand runsmirror-cloneviaexec - Uses
TUNASYNC_WORKING_DIRas the working directory and configures file buffer/base paths under it
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
如果要增加外部工具,最好也提供一份 dockerfile 安装此工具(如 |
59eedd5 to
c9fca11
Compare
|
Done — added One caveat: wiring the new image into
Happy to add that as a separate commit if a maintainer confirms, or feel free to apply it directly. |
|
Copilot review addressed:
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved critical wrapper defects and moderate Docker integration issues block approval.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (3)
Resolved since last review (4)
TUNASYNC_WORKING_DIRis referenced without being initialized/validated, andset -uwill cause… The binary path is hard-coded to/home/mirror-clone, which reduces portability and can fail… Unquoted$TUNASYNC_MIRRORCLONE_OPTIONS/$TUNASYNC_MIRRORCLONE_ARGSwill undergo word-splitting… Considerset -euo pipefail(orset -euplusset -o pipefail) to avoid pipelines masking…
| apt-get install -y --no-install-recommends ca-certificates git && \ | ||
| rm -rf /var/lib/apt/lists/* | ||
|
|
||
| COPY --from=builder /build/bin/mirror-clone /usr/local/bin/mirror-clone |
There was a problem hiding this comment.
Noted, and called out in the PR description: the Dockerfile is deliberately not wired into docker-images.yml yet — the workflow entry can follow once the image contents are approved, so a merge does not immediately start publishing tunathu/mirror-clone.
happyaron
left a comment
There was a problem hiding this comment.
Thanks for putting this together — a shared wrapper is a nice cleanup, and the recent fixes (TUNASYNC_WORKING_DIR check, pipefail) look good. I checked it against current mirror-clone (89fcc52) and tunasync's docker provider, and found a couple of things that I think will stop it from working in practice:
1. Buffer dir on a different filesystem → rename fails
mirror-clone's file backend moves finished downloads into place with tokio::fs::rename (src/file_backend.rs:97), which can't cross filesystems. tunasync's docker provider only bind-mounts the working dir plus configured volumes (worker/docker.go:62), so <workdir>.mirror-clone-buffer ends up in the container's overlay fs and I'd expect every file-backed transfer to fail with EXDEV (Invalid cross-device link). The same can happen on bare metal when each mirror is its own ZFS dataset / btrfs subvolume. As a side effect, it also leaves a foo.mirror-clone-buffer dir next to the mirrors in the served root.
Maybe make the buffer path configurable (e.g. TUNASYNC_MIRRORCLONE_BUFFER) and document that it must be on the same filesystem as the working dir and mounted into the container?
2. Wrapper can't find the binary in the new image
The Dockerfile installs /usr/local/bin/mirror-clone, but the wrapper only looks at $MIRRORCLONE_BIN, /home/mirror-clone, and ${_here}/mirror-clone — never PATH. So with the image from this PR it won't find the binary unless overridden. Following tsumugu.sh, something like mirror_clone=${MIRRORCLONE_BIN:-mirror-clone} would work, and the /home/mirror-clone bind-mount case could probably go (the PR description would then need a small update too).
Smaller things (non-blocking)
GIT_REF=mastermakes image builds non-reproducible compared to the pinnedCRATE_VERSIONimages; also--branch "$GIT_REF"won't accept a tag or commit SHA —--revhandles all of them.- The runtime image installs
git, which mirror-clone doesn't use, but notrsync, which thersyncsource shells out to (src/rsync.rs:79). - Totally fine to keep
$TUNASYNC_MIRRORCLONE_OPTIONS/$TUNASYNC_MIRRORCLONE_ARGSunquoted for word-splitting (same astsumugu.sh), but they'll also be glob-expanded against the mirror dir; aset -fbefore theexecwould avoid surprises with*in args. - As you noted, the image still needs wiring into
docker-images.ymlbefore it gets built.
Thanks again!
|
Round-2 review findings addressed in 7ac3aeb (also deployed to NJU production):
diff --git a/.github/workflows/docker-images.yml b/.github/workflows/docker-images.yml
index bc442d0..cb09679 100644
--- a/.github/workflows/docker-images.yml
+++ b/.github/workflows/docker-images.yml
@@ -47,7 +47,7 @@ jobs:
run: |
set -euo pipefail
- all_images='["ftpsync","nix-channels","rubygems-mirror","rustup-mirror","tsumugu","tunasync-scripts"]'
+ all_images='["ftpsync","mirror-clone","nix-channels","rubygems-mirror","rustup-mirror","tsumugu","tunasync-scripts"]'
if [ "$EVENT_NAME" = schedule ]; then
images="$all_images"
elif [ "$EVENT_NAME" = workflow_dispatch ]; then
@@ -59,7 +59,7 @@ jobs:
IFS=','
for image in $requested; do
case "$image" in
- ftpsync|nix-channels|rubygems-mirror|rustup-mirror|tsumugu|tunasync-scripts)
+ ftpsync|mirror-clone|nix-channels|rubygems-mirror|rustup-mirror|tsumugu|tunasync-scripts)
selected="$selected $image"
;;
*)
@@ -90,6 +90,7 @@ jobs:
selected="$selected tunasync-scripts"
;;
dockerfiles/ftpsync/*) selected="$selected ftpsync" ;;
+ dockerfiles/mirror-clone/*) selected="$selected mirror-clone" ;;
dockerfiles/nix-channels/*) selected="$selected nix-channels" ;;
dockerfiles/rubygems-mirror/*) selected="$selected rubygems-mirror" ;;
dockerfiles/rustup-mirror/*) selected="$selected rustup-mirror" ;; |
| buffer_dir="${TUNASYNC_WORKING_DIR%/}.mirror-clone-buffer" | ||
| mkdir -p "$buffer_dir" |
There was a problem hiding this comment.
Fixed in 65496d2: TUNASYNC_WORKING_DIR is resolved to an absolute path ($(cd ... && pwd)) before the cd, and the buffer default is derived from that absolute path, so a relative working dir can no longer relocate the buffer inside the base.
mirror-clone supports many source plugins (ghcup, crates.io, GitHub releases, ...), but every tunasync job using it has to hand-roll the same boilerplate (resolve TUNASYNC_WORKING_DIR, set --target-type file, choose the source). This wrapper centralises that: a tunasync .conf only sets TUNASYNC_MIRRORCLONE_OPTIONS (flags before the source name), TUNASYNC_MIRRORCLONE_SOURCE (required), and TUNASYNC_MIRRORCLONE_ARGS (plugin args after it). Key design decisions (shaped by review): - Buffer location: mirror-clone's file backend moves finished downloads into place with rename(2) (src/file_backend.rs), which cannot cross filesystems, and tunasync's docker provider only bind-mounts the working dir -- so TUNASYNC_MIRRORCLONE_BUFFER defaults to a hidden dir INSIDE the working dir (.mirror-clone-buffer), same filesystem by construction. Upstream recommends keeping the buffer outside the base path (the backend scans every file under the base as mirror content), so deployments that mount an extra volume can point TUNASYNC_MIRRORCLONE_BUFFER at a path outside the working dir (on the same filesystem) to keep it out of the served tree. - Binary lookup follows tsumugu.sh: mirror-clone is taken from PATH unless MIRRORCLONE_BIN overrides it; the image built from dockerfiles/mirror-clone installs it to /usr/local/bin. - TUNASYNC_WORKING_DIR is validated with an actionable error and resolved to an absolute path before cd, so a relative value cannot shift derived paths. - The pass-through variables are intentionally word-split (same interface as tsumugu.sh); set -f before exec keeps them from also being glob-expanded against the mirror dir. Also adds dockerfiles/mirror-clone/Dockerfile: builds the binary with cargo install --rev from a pinned commit (GIT_REF=02134d5, overridable with any sha/tag/branch for reproducible builds), and the runtime image installs rsync (the rsync source shells out to it) and not git. Wiring the image into docker-images.yml is deliberately left out pending approval. Verified: image builds with docker build; wrapper running in production at mirror.nju.edu.cn.
65496d2 to
0ba82f9
Compare
|
Branch cleanup note: this branch has been squashed to a single commit, Review items → how they were addressed (all included in the current diff)Maintainer review (@happyaron, from the review body — no inline anchors, so answering here by name):
Smaller items from the same review:
Copilot rounds:
Two stale intermediate replies in the round-1 threads were already corrected in place (the Some earlier in-thread replies described intermediate states of the branch; please rely on the current diff and this summary. |



Summary
Add a generic shell wrapper for the sjtug/mirror-clone binary so multiple tunasync jobs can share one script, plus
dockerfiles/mirror-clone/Dockerfileto build the image.Why
mirror-clonesupports many source plugins (ghcup, crates.io, GitHub releases, …). Today each tunasync job using mirror-clone has to write its own short shell file, which is mostly boilerplate (resolveTUNASYNC_WORKING_DIR, set--target-type file, choose source). This wrapper centralises that boilerplate so each tunasync.confonly sets a few env vars.Interface
Environment variables:
TUNASYNC_MIRRORCLONE_SOURCEghcup,crates-io)TUNASYNC_MIRRORCLONE_OPTIONSTUNASYNC_MIRRORCLONE_ARGSTUNASYNC_MIRRORCLONE_BUFFER$TUNASYNC_WORKING_DIR/.mirror-clone-buffer)MIRRORCLONE_BINmirror-clone, looked up inPATH, liketsumugu.sh)TUNASYNC_WORKING_DIRis validated with an actionable error and resolved to an absolute path before use. The pass-through option variables are intentionally word-split (same interface astsumugu.sh);set -fkeeps them from also being glob-expanded against the mirror dir.Buffer directory
mirror-clone's file backend moves finished downloads into place with
rename(2)(src/file_backend.rs), which cannot cross filesystems, and tunasync's docker provider only bind-mounts the working dir — so the buffer defaults to a hidden dir inside the working dir (same filesystem by construction). Upstream recommends keeping the buffer outside the base path (the backend scans every file under the base as mirror content), so deployments that mount an extra volume can setTUNASYNC_MIRRORCLONE_BUFFERto a path outside the working dir (on the same filesystem) to keep it out of the served tree.Image
dockerfiles/mirror-clone/Dockerfilebuilds the binary from a pinned commit (GIT_REF=02134d5…, overridable via--build-arg GIT_REF=<sha/tag/branch>;cargo install --revaccepts all three) and the runtime image installsrsync(mirror-clone's rsync source shells out to it,src/rsync.rs) —gitis not needed at runtime and not installed. The image is deliberately not wired intodocker-images.ymlyet; that can follow once the approach is approved.Testing
The image builds with
docker buildfrom the pinned commit; the wrapper is running in production at mirror.nju.edu.cn.