Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
203 lines
7.8 KiB
Bash
Executable File
203 lines
7.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# iCloud Drive for Linux — installer
|
|
# Installs icloud-linux (backend) + icloud-gui (GTK3 frontend) on ElementaryOS / Ubuntu / Debian.
|
|
set -euo pipefail
|
|
|
|
REPO_URL="https://github.com/IsmaeelAkram/icloud-linux"
|
|
INSTALL_DIR="$HOME/.local/lib/icloud-linux"
|
|
GUI_INSTALL="$HOME/.local/lib/icloud-gui"
|
|
BIN_DIR="$HOME/.local/bin"
|
|
DESKTOP_DIR="$HOME/.local/share/applications"
|
|
ICONS_DIR="$HOME/.local/share/icons/hicolor/scalable/apps"
|
|
|
|
BOLD="\033[1m"
|
|
GREEN="\033[0;32m"
|
|
YELLOW="\033[0;33m"
|
|
CYAN="\033[0;36m"
|
|
RED="\033[0;31m"
|
|
RESET="\033[0m"
|
|
|
|
info() { echo -e "${CYAN} →${RESET} $*"; }
|
|
success() { echo -e "${GREEN} ✓${RESET} $*"; }
|
|
warn() { echo -e "${YELLOW} !${RESET} $*"; }
|
|
die() { echo -e "${RED} ✗ ERROR:${RESET} $*" >&2; exit 1; }
|
|
header() { echo -e "\n${BOLD}$*${RESET}"; }
|
|
|
|
# ── Detect distro ─────────────────────────────────────────────────────────────
|
|
if ! command -v apt-get &>/dev/null; then
|
|
die "This installer requires apt (Debian/Ubuntu/ElementaryOS). For other distros, install deps manually."
|
|
fi
|
|
|
|
header "iCloud Drive for Linux — Installer"
|
|
echo " Target install: $INSTALL_DIR"
|
|
echo " GUI install: $GUI_INSTALL"
|
|
echo ""
|
|
|
|
# ── System dependencies ───────────────────────────────────────────────────────
|
|
header "1/6 Installing system dependencies"
|
|
PKGS=(
|
|
python3
|
|
python3-venv
|
|
python3-pip
|
|
python3-gi
|
|
python3-gi-cairo
|
|
gir1.2-gtk-3.0
|
|
python3-yaml
|
|
fuse
|
|
git
|
|
libfuse2
|
|
heif-gdk-pixbuf # HEIC/HEIF support for GNOME Photos
|
|
)
|
|
|
|
# Elementary/Ubuntu 22+ ships libfuse3 but icloud-linux may need libfuse2
|
|
sudo apt-get update -qq
|
|
sudo apt-get install -y "${PKGS[@]}" 2>&1 | grep -E "(install|already)" || true
|
|
success "Dependencies installed"
|
|
|
|
# ── Clone / update icloud-linux backend ───────────────────────────────────────
|
|
header "2/6 Installing icloud-linux backend"
|
|
if [ -d "$INSTALL_DIR/.git" ]; then
|
|
info "Already cloned — pulling latest"
|
|
git -C "$INSTALL_DIR" pull --ff-only
|
|
else
|
|
info "Cloning $REPO_URL"
|
|
git clone "$REPO_URL" "$INSTALL_DIR"
|
|
fi
|
|
|
|
# Set up Python venv and install requirements
|
|
info "Setting up Python environment"
|
|
python3 -m venv "$INSTALL_DIR/venv"
|
|
"$INSTALL_DIR/venv/bin/pip" install -q --upgrade pip
|
|
"$INSTALL_DIR/venv/bin/pip" install -q -r "$INSTALL_DIR/requirements.txt"
|
|
success "Backend ready at $INSTALL_DIR"
|
|
|
|
# ── Install the GUI ───────────────────────────────────────────────────────────
|
|
header "3/6 Installing iCloud Drive GUI"
|
|
mkdir -p "$GUI_INSTALL"
|
|
|
|
# Copy the GUI script alongside this installer script
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
if [ -f "$SCRIPT_DIR/icloud_gui.py" ]; then
|
|
cp "$SCRIPT_DIR/icloud_gui.py" "$GUI_INSTALL/icloud_gui.py"
|
|
success "Copied GUI from $SCRIPT_DIR"
|
|
else
|
|
die "icloud_gui.py not found next to install.sh — make sure you run install.sh from the icloud-gui directory."
|
|
fi
|
|
|
|
# ── Wrapper script in PATH ────────────────────────────────────────────────────
|
|
header "4/6 Creating launcher"
|
|
mkdir -p "$BIN_DIR"
|
|
cat > "$BIN_DIR/icloud-drive" <<EOF
|
|
#!/usr/bin/env bash
|
|
# Auto-generated by icloud-gui installer
|
|
exec python3 "$GUI_INSTALL/icloud_gui.py" "\$@"
|
|
EOF
|
|
chmod +x "$BIN_DIR/icloud-drive"
|
|
success "Launcher: $BIN_DIR/icloud-drive"
|
|
|
|
# Ensure ~/.local/bin is in PATH
|
|
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
|
warn "~/.local/bin is not in your PATH."
|
|
echo " Add this to ~/.bashrc or ~/.profile and re-login:"
|
|
echo ' export PATH="$HOME/.local/bin:$PATH"'
|
|
fi
|
|
|
|
# ── Desktop entry & icon ──────────────────────────────────────────────────────
|
|
mkdir -p "$DESKTOP_DIR" "$ICONS_DIR"
|
|
|
|
# Minimal cloud SVG icon (no external deps)
|
|
cat > "$ICONS_DIR/com.elementary.icloud-linux.svg" <<'SVGEOF'
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
<defs>
|
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
|
<stop offset="0%" stop-color="#64a6e0"/>
|
|
<stop offset="100%" stop-color="#1a6eb0"/>
|
|
</linearGradient>
|
|
</defs>
|
|
<path fill="url(#g)"
|
|
d="M50 30a14 14 0 0 0-26.5-6A10 10 0 1 0 14 44h36a10 10 0 0 0 0-14z"/>
|
|
</svg>
|
|
SVGEOF
|
|
|
|
cat > "$DESKTOP_DIR/com.elementary.icloud-linux.desktop" <<EOF
|
|
[Desktop Entry]
|
|
Name=iCloud Drive
|
|
Comment=Sync iCloud Drive on Linux
|
|
Exec=$BIN_DIR/icloud-drive
|
|
Icon=com.elementary.icloud-linux
|
|
Terminal=false
|
|
Type=Application
|
|
Categories=Network;FileManager;
|
|
Keywords=iCloud;Apple;Sync;Drive;Cloud;
|
|
StartupNotify=true
|
|
StartupWMClass=icloud_gui
|
|
EOF
|
|
|
|
gtk-update-icon-cache -f -t "$HOME/.local/share/icons/hicolor" 2>/dev/null || true
|
|
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
|
|
success "App menu entry installed"
|
|
|
|
# ── Update GUI config to point at correct icloudctl ──────────────────────────
|
|
# Patch the ICLOUDCTL path in the installed GUI script
|
|
ICLOUDCTL_PATH="$INSTALL_DIR/icloudctl"
|
|
sed -i "s|ICLOUDCTL = .*|ICLOUDCTL = \"$ICLOUDCTL_PATH\"|" "$GUI_INSTALL/icloud_gui.py"
|
|
success "GUI configured to use $ICLOUDCTL_PATH"
|
|
|
|
# ── Enable service at boot ─────────────────────────────────────────────────────
|
|
header "6/6 Enabling iCloud service at boot"
|
|
|
|
SERVICE_DIR="$HOME/.config/systemd/user"
|
|
mkdir -p "$SERVICE_DIR"
|
|
|
|
# Only write the service file if icloud-linux provides one; otherwise create it
|
|
if [ -f "$INSTALL_DIR/icloud-linux/icloud.service" ]; then
|
|
cp "$INSTALL_DIR/icloud-linux/icloud.service" "$SERVICE_DIR/icloud.service"
|
|
elif [ ! -f "$SERVICE_DIR/icloud.service" ]; then
|
|
cat > "$SERVICE_DIR/icloud.service" <<SVCEOF
|
|
[Unit]
|
|
Description=iCloud Linux FUSE filesystem (user)
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStartPre=-/usr/bin/fusermount -uz "%h/iCloud"
|
|
ExecStop=-/usr/bin/fusermount -uz "%h/iCloud"
|
|
ExecStart="$INSTALL_DIR/icloud-linux/.venv/bin/python" "$INSTALL_DIR/icloud-linux/driver.py" -f -c "%h/.config/icloud-linux/config.yaml" "%h/iCloud"
|
|
Restart=on-failure
|
|
RestartSec=15
|
|
TimeoutStopSec=10
|
|
KillMode=control-group
|
|
ExecStopPost=-/usr/bin/fusermount -uz "%h/iCloud"
|
|
Environment=ICLOUD_LOG_PATH=%h/.local/state/icloud-linux/icloud.log
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
SVCEOF
|
|
info "Created systemd service file"
|
|
fi
|
|
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable icloud.service
|
|
# Enable linger so the service starts at boot even before graphical login
|
|
loginctl enable-linger "$USER" 2>/dev/null || true
|
|
success "iCloud service enabled at boot (systemctl --user enable icloud.service)"
|
|
|
|
# ── Done ──────────────────────────────────────────────────────────────────────
|
|
header "Installation complete!"
|
|
echo ""
|
|
echo -e " ${BOLD}Next steps:${RESET}"
|
|
echo " 1. Open ${BOLD}iCloud Drive${RESET} from your app menu (Slingshot)"
|
|
echo " or run: icloud-drive"
|
|
echo ""
|
|
echo " 2. Go to ${BOLD}Settings${RESET} and enter your Apple ID + password"
|
|
echo ""
|
|
echo " 3. Go to ${BOLD}Sign In${RESET} and authenticate (handles 2FA)"
|
|
echo ""
|
|
echo " 4. On ${BOLD}Dashboard${RESET}, press ${BOLD}Start${RESET} to begin syncing"
|
|
echo ""
|
|
echo " The iCloud service will start automatically at every login."
|
|
echo ""
|
|
echo " To uninstall, run: $SCRIPT_DIR/uninstall.sh"
|
|
echo ""
|