Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/buildx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ on:
pull_request:

env:
DOCKERHUB_USER: wiserain
IMAGE_NAME: wiserain/rclone
DOCKERHUB_USER: totza2010
IMAGE_NAME: totza2010/rclone

jobs:
buildx:
Expand All @@ -21,13 +21,15 @@ jobs:
if: github.event_name != 'pull_request'
strategy:
matrix:
rclone-type: [latest, mod]
rclone-type: [latest, tgdrive, mod]
steps:
-
name: Check if buildx required
run: |
if [ ${{ matrix.rclone-type }} = "latest" ]; then
rclone_install_script_url="https://rclone.org/install.sh"
elif [ ${{ matrix.rclone-type }} = "tgdrive" ]; then
rclone_install_script_url="instl.vercel.app/rclone";
elif [ ${{ matrix.rclone-type }} = "mod" ]; then
rclone_install_script_url="https://raw.githubusercontent.com/wiserain/rclone/mod/install.sh"
fi
Expand Down Expand Up @@ -139,6 +141,8 @@ jobs:
run: |
if [ ${{ matrix.rclone-type }} = "latest" ]; then
rclone_install_script_url="https://rclone.org/install.sh"
elif [ ${{ matrix.rclone-type }} = "tgdrive" ]; then
rclone_install_script_url="instl.vercel.app/rclone";
elif [ ${{ matrix.rclone-type }} = "mod" ]; then
rclone_install_script_url="https://raw.githubusercontent.com/wiserain/rclone/mod/install.sh"
fi
Expand Down
11 changes: 6 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ RUN \
if [ "${RCLONE_TYPE}" = "latest" ]; then \
rclone_install_script_url="https://rclone.org/install.sh"; \
elif [ "${RCLONE_TYPE}" = "mod" ]; then \
rclone_install_script_url="https://raw.githubusercontent.com/wiserain/rclone/mod/install.sh"; fi && \
rclone_install_script_url="https://raw.githubusercontent.com/wiserain/rclone/mod/install.sh"; \
elif [ "${RCLONE_TYPE}" = "tgdrive" ]; then \
rclone_install_script_url="instl.vercel.app/rclone"; fi && \
curl -fsSL $rclone_install_script_url | bash

#
Expand Down Expand Up @@ -50,7 +52,6 @@ RUN \
mkdir -p \
/bar/cache \
/bar/log \
/bar/cloud \
/bar/data \
/bar/local \
&& \
Expand Down Expand Up @@ -123,14 +124,14 @@ ENV \
S6_SERVICES_GRACETIME=5000 \
S6_KILL_GRACETIME=5000 \
RCLONE_CONFIG=/config/rclone.conf \
UFS_BRANCHES="/local=RW:/cloud=RO" \
MFS_BRANCHES="/local=RW:/cloud=NC" \
UFS_BRANCHES="/local=RW:" \
MFS_BRANCHES="/local=RW:" \
UFS_USER_OPTS="cow,direct_io,nonempty,auto_cache,sync_read" \
MFS_USER_OPTS="rw,use_ino,func.getattr=newest,category.action=all,category.create=ff,cache.files=auto-full,dropcacheonclose=true" \
KEEP_EMPTY_DIRS=0 \
DATE_FORMAT="+%4Y/%m/%d %H:%M:%S"

VOLUME /config /cache /log /cloud /data /local
VOLUME /config /cache /log /data /local

HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \
CMD /usr/local/bin/healthcheck
Expand Down
9 changes: 8 additions & 1 deletion root/etc/cont-finish.d/10-unmount
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
#!/usr/bin/with-contenv bash
. /usr/local/bin/variables

unmount() { if findmnt "$1" | grep -q fuse; then fusermount -uz "$1" ; fi }

# unmount fuse
unmount /data
[[ -n "${POOLING_FS:-}" ]] && unmount /cloud
echo ">>> unmounting rclone mountpoint: /data"
for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
mountpoint="${value#*|}"; mountpoint="${mountpoint%%|*}"
echo ">>> unmounting rclone mountpoint: ${mountpoint}"
unmount "${mountpoint}"
done

exit 0
187 changes: 156 additions & 31 deletions root/etc/cont-init.d/50-rclone
Original file line number Diff line number Diff line change
Expand Up @@ -15,60 +15,185 @@ if [[ ! "${RCLONE_REMOTE_PATH}" == :* ]]; then
done
fi

echo "*** checking rclone remote path: ${RCLONE_REMOTE_PATH}"
if [[ ! "${RCLONE_REMOTE_PATH}" == *:* ]]; then
echo "ERROR: Missing colon (:) in RCLONE_REMOTE_PATH (${RCLONE_REMOTE_PATH})"
exit 1
fi
if [[ ! "${RCLONE_REMOTE_PATH}" == :* ]]; then
rclone_remote="$(echo "$RCLONE_REMOTE_PATH" | cut -d: -f1):"
if ! rclone listremotes | grep -q "${rclone_remote}"; then
echo "ERROR: Invalid rclone remote name (${rclone_remote})"
echo "ERROR: Available rclone remotes ($(rclone listremotes | xargs))"
echo "*** DYNAMIC MOUNTPOINT MANAGEMENT (Cleanup and Creation) ***"
declare -a EXPECTED_MOUNTPOINTS
for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
mountpoint="${value#*|}"; mountpoint="${mountpoint%%|*}"

if [[ -n "$mountpoint" ]]; then
EXPECTED_MOUNTPOINTS+=("$mountpoint")
fi
done

for current_dir in /cloud*; do
if [[ -d "$current_dir" ]] && [[ "$current_dir" != "/cloud" ]]; then

IS_EXPECTED=0
for expected_mp in "${EXPECTED_MOUNTPOINTS[@]}"; do
if [[ "$current_dir" == "$expected_mp" ]]; then
IS_EXPECTED=1
break
fi
done

if [[ $IS_EXPECTED -eq 0 ]]; then
echo " - Cleaning up unused mountpoint: $current_dir"
rmdir "$current_dir" || echo " (Note: Could not remove $current_dir, likely not empty)"
fi
fi
done

declare -a ALL_DIRS_FOR_CHOWN
ALL_DIRS_FOR_CHOWN=("/config" "/log" "/data")

for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
mountpoint="${value#*|}"; mountpoint="${mountpoint%%|*}"
opts="${value##*|}"

if [[ -n "$mountpoint" ]] && [[ ! -d "$mountpoint" ]]; then
echo " - Creating required mountpoint: $mountpoint"
mkdir -p "$mountpoint"
fi
if [[ -n "$mountpoint" ]]; then
ALL_DIRS_FOR_CHOWN+=("$mountpoint")
fi

cache_dir=$(echo "$opts" | grep -oP '\-\-cache\-dir=\K[^ ]+' || true)
if [[ -n "$cache_dir" ]] && [[ ! -d "$cache_dir" ]]; then
echo " - Creating required cache directory: $cache_dir"
mkdir -p "$cache_dir"
fi
if [[ -n "$cache_dir" ]]; then
ALL_DIRS_FOR_CHOWN+=("$cache_dir")
fi
done

echo "*** checking rclone ${#RCLONE_PATHS[@]} remote(s):"
for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
remote="${value%%|*}"
mountpoint="${value#*|}"; mountpoint="${mountpoint%%|*}"
opts="${value##*|}"
echo " - ${key} → ${remote} to ${mountpoint} with options: $(echo $opts)"
done
for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
remote="${value%%|*}"
echo ">>> checking rclone remote path: ${remote}"
if [[ ! "${remote}" == *:* ]]; then
echo "ERROR: Missing colon (:) in RCLONE_REMOTE_PATH (${remote})"
exit 1
fi
fi

echo "*** checking rclone mountpoint: ${rclone_mountpoint}"
while findmnt "${rclone_mountpoint}" | grep -q fuse.rclone; do
echo "ERROR: rclone mountpoint (${rclone_mountpoint}) already mounted"
fusermount -uz "${rclone_mountpoint}"
echo "Retrying in 30s ..."
sleep 30s
if [[ ! "${remote}" == :* ]]; then
rclone_remote="$(echo "$remote" | cut -d: -f1):"
if ! rclone listremotes | grep -q "${rclone_remote}"; then
echo "ERROR: Invalid rclone remote name (${rclone_remote})"
echo "ERROR: Available rclone remotes ($(rclone listremotes | xargs))"
exit 1
fi
fi
done
for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
mountpoint="${value#*|}"; mountpoint="${mountpoint%%|*}"
echo ">>> checking rclone mountpoint: ${mountpoint}"
while findmnt "$mountpoint" | grep -q fuse.rclone; do
echo "ERROR: rclone mountpoint (${mountpoint}) already mounted"
fusermount -uz "$mountpoint"
echo "Retrying in 30s ..."
sleep 30s
done
done

# checking redefined args
redefined=""
for opt in $rclone_mount_basic_opts; do
opt="${opt%=*}"
case " $RCLONE_MOUNT_USER_OPTS " in *" ${opt}="* | *" ${opt} "* ) redefined="$redefined ${opt}";; esac
for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
opts="${value##*|}"
for opt in $opts; do
opt="${opt%=*}"
case " $RCLONE_MOUNT_USER_OPTS " in *" ${opt}="* | *" ${opt} "* ) redefined="$redefined ${opt}";; esac
done
done
if [ -n "${redefined}" ]; then
echo "*** ERROR: Some of the arguments you have specified are already built-in. Please remove them from the RCLONE_MOUNT_USER_OPTS environment variable:${redefined}"
exit 1
fi

# checking duplicates in RCLONE_REFRESH_CRON@
crons=()
for cron in "${!RCLONE_REFRESH_CRON@}"; do
crons+=("${!cron}")
done
# ----------------------------------------------------------------------
# DYNAMIC CRON DUPLICATE CHECK (Adapted to original format)
# ----------------------------------------------------------------------
declare -a crons # Array เพื่อเก็บ "Cron Value|Remote Index"
declare -a all_var_names # Array เพื่อเก็บชื่อตัวแปรที่เกี่ยวข้อง

# 1. วนลูปผ่าน Environment Variables เพื่อสร้าง "คีย์ผสม"
while IFS='=' read -r name value; do
# ตรวจสอบเฉพาะตัวแปร RCLONE_REFRESH_CRON*
[[ "$name" =~ ^RCLONE_REFRESH_CRON(_[0-9]+)?(_[A-Za-z0-9]+)?$ ]] || continue

if [[ -n "$value" ]]; then
# ดึง Remote Index
remote_num="${BASH_REMATCH[1]}"
index_num="${remote_num#_}"
index_num="${index_num:-1}" # ถ้าว่างเปล่า ให้ใช้ 1

# สร้างคีย์ผสม (Key): CronValue|RemoteIndex
check_key="${value}|${index_num}"

# เก็บทั้งคีย์และชื่อตัวแปร
crons+=("$check_key")
all_var_names+=("$name")
fi
done < <(printenv)


# 2. ใช้ AWK ตรวจสอบความซ้ำซ้อนในคีย์ผสม
# ndup คือจำนวนคีย์ที่ไม่ซ้ำกัน
ndup=$(printf '%s\n' "${crons[@]}"|awk '!($0 in seen){seen[$0];c++} END {print c}')

# 3. ตรวจสอบว่ามีจำนวนซ้ำกันหรือไม่
if [ ${#crons[@]} -ne 0 ] && [ ${#crons[@]} -ne "$ndup" ]; then
echo "*** ERROR: found duplicates in your RCLONE_REFRESH_CRON*"
while IFS= read -r dup; do
printf " - \"%s\" -> " "$dup"
for cron in "${!RCLONE_REFRESH_CRON@}"; do
[ "$dup" = "${!cron}" ] && printf '%s ' "${cron}"
echo "*** ERROR: found duplicates in your RCLONE_REFRESH_CRON settings."

# 4. ใช้ AWK เดิมเพื่อหาค่าที่ซ้ำซ้อน
# AWK นี้จะแสดงเฉพาะค่า (คีย์ผสม) ที่ซ้ำออกมา
while IFS= read -r dup_key; do
# แยก Cron Value และ Remote Index
IFS='|' read -r dup_cron_value dup_remote_index <<< "$dup_key"

printf " - Remote Index %s: \"%s\" is used by -> " "$dup_remote_index" "$dup_cron_value"

# 5. วนลูปผ่านชื่อตัวแปรทั้งหมดเพื่อหาตัวแปรที่ตรงกับคีย์ซ้ำนี้
i=0
for name in "${all_var_names[@]}"; do
# สร้างคีย์ผสมจากชื่อตัวแปรปัจจุบันเพื่อเปรียบเทียบ
current_cron_value="${!name}" # ดึงค่า Cron Expression

[[ "$name" =~ ^RCLONE_REFRESH_CRON(_[0-9]+)?(_[A-Za-z0-9]+)?$ ]]
current_remote_num="${BASH_REMATCH[1]}"
current_index_num="${current_remote_num#_}"
current_index_num="${current_index_num:-1}"

current_check_key="${current_cron_value}|${current_index_num}"

# เปรียบเทียบกับคีย์ที่ซ้ำ
if [ "$dup_key" = "$current_check_key" ]; then
printf '%s ' "$name"
fi

i=$((i+1))
done
printf "\n"
done <<< "$(printf '%s\n' "${crons[@]}"|awk '!($0 in seen){seen[$0];next} 1')"
exit 1
fi

# permissions
chown -R abc:abc \
/config /log /cloud /data
chown -R abc:abc "${ALL_DIRS_FOR_CHOWN[@]}"
chown abc:abc /cache /local

echo "*** rclone v$(rclone version | head -n1 | cut -f2 -dv) ready!"
31 changes: 25 additions & 6 deletions root/etc/s6-overlay/s6-rc.d/cron/run
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/with-contenv bash
. /usr/local/bin/variables

s6-svwait -U /var/run/service/mount

Expand All @@ -15,13 +16,31 @@ if [[ -n "${MOVE_LOCAL_CRON:-}" ]]; then
fi

# any ENVs starting with RCLONE_REFRESH_CRON
for cron in "${!RCLONE_REFRESH_CRON@}"; do
dirs="RCLONE_REFRESH_DIRS${cron//RCLONE_REFRESH_CRON/}"
if [[ -n "${!cron}" ]] && [[ -n "${!dirs}" ]]; then
echo "*** scheduling job: vfs/refresh for '${!dirs}'"
go-cron "${!cron}" /usr/local/bin/rclone_refresh "${!dirs}" &
while IFS='=' read -r name value; do
[[ "$name" =~ ^RCLONE_REFRESH_CRON(_[0-9]+)?(_[A-Za-z0-9]+)?$ ]] || continue
remote_num="${BASH_REMATCH[1]}"
job_name="${BASH_REMATCH[2]}"

dirs_var="RCLONE_REFRESH_DIRS${remote_num}${job_name}"
remote_key="RCLONE_REMOTE_PATH${remote_num}"

if [[ -n "$value" ]] && [[ -n "${!dirs_var:-}" ]] && [[ -n "${RCLONE_PATHS[$remote_key]:-}" ]]; then

rc_path_value="${RCLONE_PATHS[$remote_key]}"
opts="${rc_path_value##*|}"

rc_port=$(echo "$opts" | grep -oP '\-\-rc\-addr=:\K[0-9]+' || true)

if [[ -z "$rc_port" ]]; then
echo "!!! ERROR: Skipping cron for remote $remote_key. Missing --rc-addr=:PORT in options."
continue
fi

echo "*** scheduling job: vfs/refresh [${job_name}] for '${!dirs_var}' on RC port $rc_port"

go-cron "$value" /usr/local/bin/rclone_refresh "${!dirs_var}" "$rc_port" &
sleep 2
fi
done
done < <(printenv)

exec wait
10 changes: 7 additions & 3 deletions root/etc/s6-overlay/s6-rc.d/mount/data/check
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
. /usr/local/bin/variables
set -eo pipefail

if ! findmnt "$rclone_mountpoint" | grep -q fuse.rclone; then
exit 1
fi
for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
mountpoint="${value#*|}"; mountpoint="${mountpoint%%|*}"
if ! findmnt "$mountpoint" | grep -q fuse.rclone; then
exit 1
fi
done

trap run_exit_commands EXIT
trap 'run_commands "${POST_COMMANDS_FAILURE:-}"; exit 1' ERR
Expand Down
10 changes: 9 additions & 1 deletion root/etc/s6-overlay/s6-rc.d/mount/finish
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
#!/usr/bin/with-contenv bash
. /usr/local/bin/variables

unmount() { if findmnt "$1" | grep -q fuse; then fusermount -uz "$1" ; fi }

# unmount fuse
unmount /data
[[ -n "${POOLING_FS:-}" ]] && unmount /cloud
echo ">>> unmounting rclone mountpoint: /data"
for key in $(printf '%s\n' "${!RCLONE_PATHS[@]}" | sort -t_ -k4n); do
value="${RCLONE_PATHS[$key]}"
mountpoint="${value#*|}"; mountpoint="${mountpoint%%|*}"
echo ">>> unmounting rclone mountpoint: ${mountpoint}"

unmount "${mountpoint}"
done
Loading