initial: homelab scripts
This commit is contained in:
commit
dbb97e9493
5 files changed
+956
No files matched your search
@@ -0,0 +1,48 @@
|
||||
# edemilov/scripts
|
||||
|
||||
Personal homelab scripts hosted on Gitea at `git.edemilov.ru`.
|
||||
|
||||
## Quick usage
|
||||
|
||||
```bash
|
||||
# Fresh machine setup — install apps first
|
||||
curl -s https://git.edemilov.ru/edemilov/scripts/raw/branch/main/apps.sh | sudo bash
|
||||
|
||||
# Then mount samba drives + setup 2nd drive symlinks
|
||||
bash <(curl -s https://git.edemilov.ru/edemilov/scripts/raw/branch/main/netdrivelink.sh)
|
||||
|
||||
# Windows (PowerShell as Admin)
|
||||
irm https://git.edemilov.ru/edemilov/scripts/raw/branch/main/apps.ps1 | iex
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Description | Distros |
|
||||
|--------|-------------|---------|
|
||||
| `apps.sh` | Install common dev/security/daily tools | Arch, Parrot, Kali, Debian, Ubuntu |
|
||||
| `netdrivelink.sh` | Mount vanga Samba shares + 2nd drive symlinks | Same |
|
||||
| `apps.ps1` | Windows equivalent via winget + map network drives | Windows 10/11 |
|
||||
| `gitea-ct-setup.sh` | Create CT116 with Gitea on Proxmox host | Proxmox 8+ |
|
||||
|
||||
## Samba shares (vanga — 10.0.99.100)
|
||||
|
||||
| Share | Path on vanga | Mounted at |
|
||||
|-------|---------------|------------|
|
||||
| `Edemilov` | `/srv/users/edemilov` | `/mnt/users/edemilov` |
|
||||
| `Depo` | `/srv/depo` | `/mnt/depo` |
|
||||
| `Family-Documents` | `/srv/shared/documents` | `/mnt/shared/documents` |
|
||||
| `Family-Downloads` | `/srv/shared/downloads` | `/mnt/shared/downloads` |
|
||||
| `Movies/TVShows/Music/...` | `/srv/media/*` | `/mnt/media/*` |
|
||||
|
||||
## 2nd drive (nvme-data)
|
||||
|
||||
Mounted at `/nvme-data` — symlinked to home:
|
||||
|
||||
```
|
||||
~/Documents → /nvme-data/Documents
|
||||
~/Downloads → /nvme-data/Downloads
|
||||
~/Pictures → /nvme-data/Pictures
|
||||
~/Videos → /nvme-data/Videos
|
||||
~/Projects → /nvme-data/Projects
|
||||
~/Scripts → /nvme-data/Scripts
|
||||
```
|
||||
@@ -0,0 +1,128 @@
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# apps.ps1
|
||||
# Windows equivalent of apps.sh — uses winget
|
||||
# Run in PowerShell as Administrator:
|
||||
# irm https://git.edemilov.ru/edemilov/scripts/raw/branch/main/apps.ps1 | iex
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
#Requires -RunAsAdministrator
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
|
||||
function Write-Header { param($t) Write-Host "`n── $t ──────────────────────────────" -ForegroundColor Cyan }
|
||||
function Write-Info { param($t) Write-Host "[•] $t" -ForegroundColor Cyan }
|
||||
function Write-Success { param($t) Write-Host "[✓] $t" -ForegroundColor Green }
|
||||
function Write-Skip { param($t) Write-Host "[~] $t (already installed)" -ForegroundColor Magenta }
|
||||
function Write-Warn { param($t) Write-Host "[!] $t" -ForegroundColor Yellow }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " ╔══════════════════════════════════════════════╗" -ForegroundColor Cyan
|
||||
Write-Host " ║ apps.ps1 — Windows installer ║" -ForegroundColor Cyan
|
||||
Write-Host " ║ edemilov homelab scripts ║" -ForegroundColor Cyan
|
||||
Write-Host " ╚══════════════════════════════════════════════╝" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# ── Check winget ────────────────────────────────────────────────────
|
||||
Write-Header "Checking winget"
|
||||
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
|
||||
Write-Warn "winget not found — install App Installer from Microsoft Store first"
|
||||
Write-Warn "https://www.microsoft.com/store/productId/9NBLGGH4NNS1"
|
||||
exit 1
|
||||
}
|
||||
Write-Success "winget available"
|
||||
|
||||
# ── Smart install function ───────────────────────────────────────────
|
||||
function Install-App {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$WingetId,
|
||||
[string]$CheckCmd = ""
|
||||
)
|
||||
|
||||
# Check if already installed via command or winget list
|
||||
$alreadyInstalled = $false
|
||||
if ($CheckCmd -and (Get-Command $CheckCmd -ErrorAction SilentlyContinue)) {
|
||||
$alreadyInstalled = $true
|
||||
} elseif (winget list --id $WingetId 2>$null | Select-String $WingetId) {
|
||||
$alreadyInstalled = $true
|
||||
}
|
||||
|
||||
if ($alreadyInstalled) {
|
||||
Write-Skip $Name
|
||||
} else {
|
||||
Write-Info "Installing $Name..."
|
||||
winget install --id $WingetId --silent --accept-package-agreements --accept-source-agreements 2>$null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Success "$Name installed"
|
||||
} else {
|
||||
Write-Warn "$Name failed — skipping"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ── Core tools ──────────────────────────────────────────────────────
|
||||
Write-Header "Core tools"
|
||||
Install-App "Git" "Git.Git" "git"
|
||||
Install-App "curl" "cURL.cURL" "curl"
|
||||
Install-App "wget" "GnuWin32.Wget"
|
||||
Install-App "7-Zip" "7zip.7zip" "7z"
|
||||
Install-App "jq" "jqlang.jq" "jq"
|
||||
Install-App "Neofetch" "nepnep.neofetch"
|
||||
|
||||
# ── Shell ────────────────────────────────────────────────────────────
|
||||
Write-Header "Shell & terminal"
|
||||
Install-App "Windows Terminal" "Microsoft.WindowsTerminal"
|
||||
Install-App "PowerShell 7" "Microsoft.PowerShell" "pwsh"
|
||||
Install-App "Oh My Posh" "JanDeDobbeleer.OhMyPosh" "oh-my-posh"
|
||||
|
||||
# ── Development ─────────────────────────────────────────────────────
|
||||
Write-Header "Development tools"
|
||||
Install-App "VS Code" "Microsoft.VisualStudioCode" "code"
|
||||
Install-App "Python 3" "Python.Python.3.12" "python"
|
||||
Install-App "Node.js" "OpenJS.NodeJS.LTS" "node"
|
||||
Install-App "Docker Desktop" "Docker.DockerDesktop" "docker"
|
||||
|
||||
# ── Network & security ───────────────────────────────────────────────
|
||||
Write-Header "Network tools"
|
||||
Install-App "nmap" "Nmap.Nmap" "nmap"
|
||||
Install-App "Wireshark" "WiresharkFoundation.Wireshark" "Wireshark"
|
||||
Install-App "WinSCP" "WinSCP.WinSCP"
|
||||
Install-App "PuTTY" "PuTTY.PuTTY" "putty"
|
||||
|
||||
# ── Samba / network drives ───────────────────────────────────────────
|
||||
Write-Header "Samba network drives"
|
||||
Write-Info "Configuring vanga homelab network drives..."
|
||||
|
||||
$SAMBA = "10.0.99.100"
|
||||
$CRED = Get-Credential -Message "Enter Samba credentials for $SAMBA"
|
||||
|
||||
$drives = @(
|
||||
@{ Letter="E"; Share="Edemilov"; Label="edemilov-user" },
|
||||
@{ Letter="D"; Share="Depo"; Label="depo" },
|
||||
@{ Letter="M"; Share="Family-Documents"; Label="documents" },
|
||||
@{ Letter="P"; Share="Family-Photos"; Label="photos" }
|
||||
)
|
||||
|
||||
foreach ($d in $drives) {
|
||||
$unc = "\\$SAMBA\$($d.Share)"
|
||||
if (Get-PSDrive -Name $d.Letter -ErrorAction SilentlyContinue) {
|
||||
Write-Skip "$($d.Letter): ($unc)"
|
||||
} else {
|
||||
try {
|
||||
New-PSDrive -Name $d.Letter -PSProvider FileSystem -Root $unc `
|
||||
-Credential $CRED -Persist -ErrorAction Stop | Out-Null
|
||||
Write-Success "Mapped $($d.Letter): → $unc"
|
||||
} catch {
|
||||
Write-Warn "Could not map $($d.Letter): — $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ── Done ─────────────────────────────────────────────────────────────
|
||||
Write-Host ""
|
||||
Write-Host "═══════════════════════════════════════════════════" -ForegroundColor Green
|
||||
Write-Host " ✓ apps.ps1 complete!" -ForegroundColor Green
|
||||
Write-Host "═══════════════════════════════════════════════════" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host " Restart PowerShell to pick up PATH changes" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
@@ -0,0 +1,290 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# apps.sh
|
||||
# Universal app installer — Arch / Parrot / Kali / Debian / Ubuntu
|
||||
# Detects distro, installs the right packages, skips existing ones
|
||||
#
|
||||
# curl -s https://git.edemilov.ru/edemilov/scripts/raw/branch/main/apps.sh | bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
# ── Colors ──────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'; MAGENTA='\033[0;35m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
info() { echo -e "${CYAN}[•]${NC} $1"; }
|
||||
success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
skip() { echo -e "${MAGENTA}[~]${NC} $1 (already installed)"; }
|
||||
die() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
|
||||
header() { echo -e "\n${BOLD}${CYAN}── $1 ──────────────────────────────${NC}"; }
|
||||
|
||||
echo -e "${BOLD}"
|
||||
echo " ╔══════════════════════════════════════════════╗"
|
||||
echo " ║ apps.sh — universal installer ║"
|
||||
echo " ║ edemilov homelab scripts ║"
|
||||
echo " ╚══════════════════════════════════════════════╝"
|
||||
echo -e "${NC}"
|
||||
|
||||
# ── Detect distro ───────────────────────────────────────────────────
|
||||
header "Detecting system"
|
||||
[[ -f /etc/os-release ]] && . /etc/os-release || die "Cannot detect distro"
|
||||
DISTRO_ID="${ID,,}"
|
||||
DISTRO_LIKE="${ID_LIKE,,}"
|
||||
info "Distro: ${PRETTY_NAME}"
|
||||
|
||||
# Determine family
|
||||
if command -v pacman &>/dev/null; then
|
||||
FAMILY="arch"
|
||||
elif [[ "$DISTRO_ID" =~ ^(kali|parrot|debian|ubuntu|linuxmint|pop)$ ]] || \
|
||||
[[ "$DISTRO_LIKE" =~ debian ]] || [[ "$DISTRO_LIKE" =~ ubuntu ]]; then
|
||||
FAMILY="debian"
|
||||
elif command -v dnf &>/dev/null; then
|
||||
FAMILY="fedora"
|
||||
else
|
||||
die "Unsupported distro: $DISTRO_ID"
|
||||
fi
|
||||
info "Family: $FAMILY"
|
||||
|
||||
# ── Package manager setup ────────────────────────────────────────────
|
||||
case $FAMILY in
|
||||
arch)
|
||||
PKG_MGR="pacman"
|
||||
UPDATE_CMD="pacman -Syu --noconfirm"
|
||||
INSTALL="pacman -S --noconfirm --needed"
|
||||
IS_INSTALLED() { pacman -Qi "$1" &>/dev/null; }
|
||||
# Check for yay/paru for AUR
|
||||
if command -v yay &>/dev/null; then
|
||||
AUR_INSTALL="yay -S --noconfirm --needed"
|
||||
elif command -v paru &>/dev/null; then
|
||||
AUR_INSTALL="paru -S --noconfirm --needed"
|
||||
else
|
||||
AUR_INSTALL=""
|
||||
fi
|
||||
;;
|
||||
debian)
|
||||
PKG_MGR="apt"
|
||||
UPDATE_CMD="apt-get update -qq"
|
||||
INSTALL="apt-get install -y -qq"
|
||||
IS_INSTALLED() { dpkg -l "$1" 2>/dev/null | grep -q "^ii"; }
|
||||
;;
|
||||
fedora)
|
||||
PKG_MGR="dnf"
|
||||
UPDATE_CMD="dnf check-update -q || true"
|
||||
INSTALL="dnf install -y -q"
|
||||
IS_INSTALLED() { rpm -q "$1" &>/dev/null; }
|
||||
;;
|
||||
esac
|
||||
|
||||
# ── Smart install function ───────────────────────────────────────────
|
||||
# Usage: pkg_install "display_name" "package_name" ["arch_pkg"] ["deb_pkg"]
|
||||
pkg_install() {
|
||||
local NAME="$1"
|
||||
local PKG_ARCH="${3:-$2}"
|
||||
local PKG_DEB="${4:-$2}"
|
||||
local PKG_FED="${5:-$2}"
|
||||
local PKG
|
||||
|
||||
case $FAMILY in
|
||||
arch) PKG="$PKG_ARCH" ;;
|
||||
debian) PKG="$PKG_DEB" ;;
|
||||
fedora) PKG="$PKG_FED" ;;
|
||||
esac
|
||||
|
||||
if IS_INSTALLED "$PKG" 2>/dev/null || command -v "$2" &>/dev/null; then
|
||||
skip "$NAME"
|
||||
else
|
||||
info "Installing $NAME..."
|
||||
$INSTALL $PKG && success "$NAME installed" || warn "$NAME failed — skipping"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Update package database ──────────────────────────────────────────
|
||||
header "Updating package database"
|
||||
$UPDATE_CMD
|
||||
success "Package database updated"
|
||||
|
||||
# ── Core system tools ────────────────────────────────────────────────
|
||||
header "Core system tools"
|
||||
pkg_install "git" "git"
|
||||
pkg_install "curl" "curl"
|
||||
pkg_install "wget" "wget"
|
||||
pkg_install "htop" "htop"
|
||||
pkg_install "btop" "btop"
|
||||
pkg_install "tmux" "tmux"
|
||||
pkg_install "neofetch" "neofetch"
|
||||
pkg_install "tree" "tree"
|
||||
pkg_install "jq" "jq"
|
||||
pkg_install "unzip" "unzip"
|
||||
pkg_install "rsync" "rsync"
|
||||
pkg_install "openssh" "ssh" "openssh" "openssh-client"
|
||||
pkg_install "net-tools" "net-tools" "net-tools" "net-tools"
|
||||
pkg_install "nmap" "nmap"
|
||||
|
||||
# ── Shell ────────────────────────────────────────────────────────────
|
||||
header "Shell setup"
|
||||
pkg_install "zsh" "zsh"
|
||||
pkg_install "bash-completion" "bash-completion"
|
||||
|
||||
# Install oh-my-zsh if not present
|
||||
if [[ ! -d "$HOME/.oh-my-zsh" ]] && [[ -n "${SUDO_USER:-}" ]]; then
|
||||
info "Installing oh-my-zsh for $SUDO_USER..."
|
||||
su - "$SUDO_USER" -c \
|
||||
'sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended' \
|
||||
&& success "oh-my-zsh installed" || warn "oh-my-zsh failed — skipping"
|
||||
elif [[ ! -d "$HOME/.oh-my-zsh" ]]; then
|
||||
info "Installing oh-my-zsh..."
|
||||
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended \
|
||||
&& success "oh-my-zsh installed" || warn "oh-my-zsh failed — skipping"
|
||||
else
|
||||
skip "oh-my-zsh"
|
||||
fi
|
||||
|
||||
# ── Development tools ────────────────────────────────────────────────
|
||||
header "Development tools"
|
||||
pkg_install "python3" "python3"
|
||||
pkg_install "pip" "pip" "python-pip" "python3-pip"
|
||||
pkg_install "nodejs" "node" "nodejs" "nodejs"
|
||||
pkg_install "npm" "npm" "npm" "npm"
|
||||
|
||||
# ── Docker ──────────────────────────────────────────────────────────
|
||||
header "Docker"
|
||||
if command -v docker &>/dev/null; then
|
||||
skip "Docker"
|
||||
else
|
||||
info "Installing Docker..."
|
||||
case $FAMILY in
|
||||
arch)
|
||||
$INSTALL docker docker-compose
|
||||
systemctl enable --now docker
|
||||
;;
|
||||
debian)
|
||||
# Official Docker install script
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
;;
|
||||
fedora)
|
||||
dnf config-manager --add-repo \
|
||||
https://download.docker.com/linux/fedora/docker-ce.repo
|
||||
$INSTALL docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
systemctl enable --now docker
|
||||
;;
|
||||
esac
|
||||
# Add current/sudo user to docker group
|
||||
DOCKER_USER="${SUDO_USER:-$USER}"
|
||||
usermod -aG docker "$DOCKER_USER" 2>/dev/null && \
|
||||
success "Docker installed — ${DOCKER_USER} added to docker group" || \
|
||||
success "Docker installed"
|
||||
fi
|
||||
|
||||
# ── VS Code ──────────────────────────────────────────────────────────
|
||||
header "VS Code"
|
||||
if command -v code &>/dev/null; then
|
||||
skip "VS Code"
|
||||
else
|
||||
info "Installing VS Code..."
|
||||
case $FAMILY in
|
||||
arch)
|
||||
if [[ -n "$AUR_INSTALL" ]]; then
|
||||
su - "${SUDO_USER:-$USER}" -c "$AUR_INSTALL visual-studio-code-bin" \
|
||||
&& success "VS Code installed" || warn "VS Code AUR install failed"
|
||||
else
|
||||
warn "AUR helper not found — install yay first, then: yay -S visual-studio-code-bin"
|
||||
fi
|
||||
;;
|
||||
debian)
|
||||
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | \
|
||||
gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] \
|
||||
https://packages.microsoft.com/repos/vscode stable main" \
|
||||
> /etc/apt/sources.list.d/vscode.list
|
||||
apt-get update -qq
|
||||
$INSTALL code
|
||||
success "VS Code installed"
|
||||
;;
|
||||
fedora)
|
||||
rpm --import https://packages.microsoft.com/keys/microsoft.asc
|
||||
cat > /etc/yum.repos.d/vscode.repo << 'EOF'
|
||||
[code]
|
||||
name=Visual Studio Code
|
||||
baseurl=https://packages.microsoft.com/yumrepos/vscode
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://packages.microsoft.com/keys/microsoft.asc
|
||||
EOF
|
||||
$INSTALL code
|
||||
success "VS Code installed"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ── Terminal & GUI apps ──────────────────────────────────────────────
|
||||
header "Terminal emulator"
|
||||
# Install a good terminal if none of the preferred ones exist
|
||||
if command -v kitty &>/dev/null || command -v alacritty &>/dev/null || \
|
||||
command -v tilix &>/dev/null; then
|
||||
skip "terminal emulator (already have one)"
|
||||
else
|
||||
case $FAMILY in
|
||||
arch) pkg_install "kitty" "kitty" ;;
|
||||
debian) pkg_install "tilix" "tilix" ;;
|
||||
fedora) pkg_install "tilix" "tilix" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ── Network & security tools ─────────────────────────────────────────
|
||||
header "Network & security tools"
|
||||
pkg_install "nmap" "nmap"
|
||||
pkg_install "wireshark" "wireshark" "wireshark-qt" "wireshark"
|
||||
pkg_install "net-tools" "net-tools"
|
||||
pkg_install "traceroute" "traceroute"
|
||||
pkg_install "whois" "whois"
|
||||
pkg_install "dnsutils" "dig" "bind-tools" "dnsutils"
|
||||
|
||||
# ── Kali/Parrot specific extras ──────────────────────────────────────
|
||||
if [[ "$DISTRO_ID" =~ ^(kali|parrot)$ ]]; then
|
||||
header "Pentesting distro extras"
|
||||
pkg_install "seclists" "seclists" "" "seclists"
|
||||
pkg_install "gobuster" "gobuster" "" "gobuster"
|
||||
pkg_install "ffuf" "ffuf" "" "ffuf"
|
||||
pkg_install "burpsuite" "burpsuite" "" "burpsuite"
|
||||
fi
|
||||
|
||||
# ── Samba client (for netdrivelink.sh) ───────────────────────────────
|
||||
header "Samba client"
|
||||
pkg_install "cifs-utils" "mount.cifs" "cifs-utils" "cifs-utils"
|
||||
pkg_install "samba-client" "smbclient" "samba" "smbclient"
|
||||
|
||||
# ── Flatpak (for apps not in repos) ──────────────────────────────────
|
||||
header "Flatpak"
|
||||
if [[ "$FAMILY" != "arch" ]]; then
|
||||
pkg_install "flatpak" "flatpak"
|
||||
if ! flatpak remotes | grep -q flathub; then
|
||||
flatpak remote-add --if-not-exists flathub \
|
||||
https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
success "Flathub added"
|
||||
else
|
||||
skip "Flathub remote"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Cleanup ──────────────────────────────────────────────────────────
|
||||
header "Cleanup"
|
||||
case $FAMILY in
|
||||
arch) pacman -Sc --noconfirm &>/dev/null ;;
|
||||
debian) apt-get autoremove -y -qq && apt-get clean -qq ;;
|
||||
fedora) dnf clean all -q ;;
|
||||
esac
|
||||
success "Cache cleaned"
|
||||
|
||||
# ── Done ─────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${BOLD}${GREEN}═══════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BOLD} ✓ apps.sh complete!${NC}"
|
||||
echo -e "${GREEN}═══════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}Installed on:${NC} ${PRETTY_NAME}"
|
||||
echo -e " ${CYAN}Run next:${NC}"
|
||||
echo -e " ${YELLOW}bash <(curl -s https://git.edemilov.ru/edemilov/scripts/raw/branch/main/netdrivelink.sh)${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}Note:${NC} Log out and back in for Docker group to take effect"
|
||||
echo ""
|
||||
@@ -0,0 +1,205 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# gitea-ct-setup.sh
|
||||
# Run this ON THE PROXMOX HOST (vanga) as root
|
||||
# Creates CT116, installs Gitea, bind-mounts /srv/depo/scripts
|
||||
# After: point git.edemilov.ru → 10.0.99.116:3000 in NPM
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
# ── Config ──────────────────────────────────────────────────────────
|
||||
CTID=116
|
||||
CTNAME="gitea"
|
||||
CT_IP="10.0.99.116/24"
|
||||
CT_GW="10.0.99.1"
|
||||
CT_RAM=512
|
||||
CT_DISK=8 # GB on local-lvm
|
||||
CT_CORES=2
|
||||
CT_PASSWORD="changeme123" # ← change this before running!
|
||||
GITEA_VERSION="1.22.6"
|
||||
STORAGE="local-lvm"
|
||||
TEMPLATE_STORAGE="local"
|
||||
|
||||
# ── Colors ──────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
info() { echo -e "${CYAN}[•]${NC} $1"; }
|
||||
success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
die() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
|
||||
|
||||
echo -e "${BOLD}"
|
||||
echo " ╔══════════════════════════════════════╗"
|
||||
echo " ║ Gitea CT116 Setup — vanga ║"
|
||||
echo " ╚══════════════════════════════════════╝"
|
||||
echo -e "${NC}"
|
||||
|
||||
# ── Preflight ───────────────────────────────────────────────────────
|
||||
[[ $EUID -ne 0 ]] && die "Run as root on Proxmox host"
|
||||
pct status $CTID &>/dev/null && die "CT$CTID already exists. Abort."
|
||||
|
||||
# ── Download Debian template if needed ──────────────────────────────
|
||||
info "Checking for Debian 12 template..."
|
||||
TEMPLATE=$(pveam list $TEMPLATE_STORAGE 2>/dev/null | grep "debian-12" | tail -1 | awk '{print $1}')
|
||||
if [[ -z "$TEMPLATE" ]]; then
|
||||
info "Downloading Debian 12 template..."
|
||||
pveam update
|
||||
TEMPLATE_NAME=$(pveam available --section system | grep "debian-12" | tail -1 | awk '{print $2}')
|
||||
pveam download $TEMPLATE_STORAGE $TEMPLATE_NAME
|
||||
TEMPLATE="$TEMPLATE_STORAGE:vztmpl/$TEMPLATE_NAME"
|
||||
fi
|
||||
success "Template ready: $TEMPLATE"
|
||||
|
||||
# ── Create CT ───────────────────────────────────────────────────────
|
||||
info "Creating CT$CTID ($CTNAME)..."
|
||||
pct create $CTID $TEMPLATE \
|
||||
--hostname $CTNAME \
|
||||
--password $CT_PASSWORD \
|
||||
--cores $CT_CORES \
|
||||
--memory $CT_RAM \
|
||||
--swap 512 \
|
||||
--rootfs $STORAGE:$CT_DISK \
|
||||
--net0 name=eth0,bridge=vmbr0,ip=$CT_IP,gw=$CT_GW \
|
||||
--nameserver 10.0.99.101 \
|
||||
--searchdomain local \
|
||||
--unprivileged 1 \
|
||||
--features nesting=1 \
|
||||
--onboot 1
|
||||
success "CT$CTID created"
|
||||
|
||||
# ── Bind mount /srv/depo/scripts into CT ────────────────────────────
|
||||
info "Adding bind mount /srv/depo/scripts → /opt/gitea-repos/scripts..."
|
||||
# Create the ZFS dataset for gitea data if not exists
|
||||
zfs list zdata4tbnvme1/apps/gitea &>/dev/null || \
|
||||
zfs create -o mountpoint=/srv/apps/gitea zdata4tbnvme1/apps/gitea
|
||||
mkdir -p /srv/apps/gitea/data
|
||||
mkdir -p /srv/depo/scripts
|
||||
|
||||
# Bind mounts (need privileged=0 workaround — use mp)
|
||||
pct set $CTID \
|
||||
--mp0 /srv/depo/scripts,mp=/opt/scripts,ro=0 \
|
||||
--mp1 /srv/apps/gitea/data,mp=/opt/gitea-data,ro=0
|
||||
success "Bind mounts configured"
|
||||
|
||||
# ── Start CT ────────────────────────────────────────────────────────
|
||||
info "Starting CT$CTID..."
|
||||
pct start $CTID
|
||||
sleep 5
|
||||
success "CT$CTID running"
|
||||
|
||||
# ── Install Gitea inside CT ─────────────────────────────────────────
|
||||
info "Installing Gitea $GITEA_VERSION inside CT..."
|
||||
pct exec $CTID -- bash -c "
|
||||
set -e
|
||||
|
||||
# Update & deps
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq git curl wget sqlite3 ca-certificates
|
||||
|
||||
# Create git user
|
||||
adduser --system --shell /bin/bash --gecos 'Gitea' --group \
|
||||
--disabled-password --home /home/git git
|
||||
|
||||
# Download Gitea binary
|
||||
cd /tmp
|
||||
wget -q https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64 \
|
||||
-O /usr/local/bin/gitea
|
||||
chmod +x /usr/local/bin/gitea
|
||||
|
||||
# Gitea directory structure
|
||||
mkdir -p /opt/gitea-data/{repositories,custom,log,data}
|
||||
mkdir -p /etc/gitea
|
||||
chown -R git:git /opt/gitea-data
|
||||
chown root:git /etc/gitea
|
||||
chmod 770 /etc/gitea
|
||||
|
||||
# app.ini config
|
||||
cat > /etc/gitea/app.ini << 'APPINI'
|
||||
APP_NAME = edemilov scripts & notes
|
||||
RUN_USER = git
|
||||
RUN_MODE = prod
|
||||
|
||||
[server]
|
||||
HTTP_PORT = 3000
|
||||
ROOT_URL = https://git.edemilov.ru/
|
||||
DOMAIN = git.edemilov.ru
|
||||
DISABLE_SSH = false
|
||||
SSH_PORT = 2222
|
||||
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
PATH = /opt/gitea-data/data/gitea.db
|
||||
|
||||
[repository]
|
||||
ROOT = /opt/gitea-data/repositories
|
||||
|
||||
[log]
|
||||
ROOT_PATH = /opt/gitea-data/log
|
||||
MODE = file
|
||||
LEVEL = warn
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = false
|
||||
SECRET_KEY = $(openssl rand -hex 32)
|
||||
INTERNAL_TOKEN = $(openssl rand -hex 32)
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = true
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
|
||||
[ui]
|
||||
DEFAULT_THEME = arc-green
|
||||
APPINI
|
||||
|
||||
chown git:git /etc/gitea/app.ini
|
||||
|
||||
# systemd service
|
||||
cat > /etc/systemd/system/gitea.service << 'SERVICE'
|
||||
[Unit]
|
||||
Description=Gitea
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=git
|
||||
Group=git
|
||||
WorkingDirectory=/opt/gitea-data
|
||||
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/opt/gitea-data
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
SERVICE
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable gitea
|
||||
systemctl start gitea
|
||||
echo 'Gitea service started'
|
||||
"
|
||||
|
||||
success "Gitea installed and running inside CT$CTID"
|
||||
|
||||
# ── Final summary ───────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${BOLD}${GREEN}═══════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BOLD} ✓ Gitea CT116 ready!${NC}"
|
||||
echo -e "${GREEN}═══════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}Internal URL:${NC} http://10.0.99.116:3000"
|
||||
echo -e " ${CYAN}Public URL:${NC} https://git.edemilov.ru (after NPM setup)"
|
||||
echo ""
|
||||
echo -e "${YELLOW} NPM Steps:${NC}"
|
||||
echo " 1. New Proxy Host"
|
||||
echo " 2. Domain: git.edemilov.ru"
|
||||
echo " 3. Forward: 10.0.99.116 port 3000"
|
||||
echo " 4. Enable SSL (Let's Encrypt)"
|
||||
echo ""
|
||||
echo -e "${YELLOW} First run — finish setup at:${NC}"
|
||||
echo " http://10.0.99.116:3000"
|
||||
echo " (creates admin account, everything is pre-configured)"
|
||||
echo ""
|
||||
echo -e "${YELLOW} Script curl usage (after pushing scripts to Gitea):${NC}"
|
||||
echo " curl -s https://git.edemilov.ru/edemilov/scripts/raw/branch/main/apps.sh | bash"
|
||||
echo ""
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# netdrivelink.sh
|
||||
# vanga homelab — Samba mounts + 2nd drive symlinks
|
||||
# Works on: Arch, Debian, Ubuntu, Parrot, Kali, Mint
|
||||
#
|
||||
# curl -s https://git.edemilov.ru/edemilov/scripts/raw/branch/main/netdrivelink.sh | bash
|
||||
# OR for interactive (recommended first time):
|
||||
# bash <(curl -s https://git.edemilov.ru/edemilov/scripts/raw/branch/main/netdrivelink.sh)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
# ── Colors ──────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
info() { echo -e "${CYAN}[•]${NC} $1"; }
|
||||
success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
die() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
|
||||
header() { echo -e "\n${BOLD}${CYAN}── $1 ──────────────────────────────${NC}"; }
|
||||
|
||||
# ── Root check ──────────────────────────────────────────────────────
|
||||
[[ $EUID -ne 0 ]] && die "Run as root: sudo bash netdrivelink.sh"
|
||||
|
||||
echo -e "${BOLD}"
|
||||
echo " ╔══════════════════════════════════════════════╗"
|
||||
echo " ║ vanga homelab — netdrivelink.sh ║"
|
||||
echo " ║ Samba mounts + 2nd drive setup ║"
|
||||
echo " ╚══════════════════════════════════════════════╝"
|
||||
echo -e "${NC}"
|
||||
|
||||
# ── Detect distro & package manager ─────────────────────────────────
|
||||
header "Detecting system"
|
||||
detect_distro() {
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
DISTRO_ID="${ID,,}"
|
||||
DISTRO_LIKE="${ID_LIKE,,}"
|
||||
else
|
||||
die "Cannot detect distro — /etc/os-release missing"
|
||||
fi
|
||||
|
||||
if command -v pacman &>/dev/null; then
|
||||
PKG_MGR="pacman"
|
||||
INSTALL_CMD="pacman -S --noconfirm --needed"
|
||||
CIFS_PKG="cifs-utils"
|
||||
elif command -v apt-get &>/dev/null; then
|
||||
PKG_MGR="apt"
|
||||
INSTALL_CMD="apt-get install -y -qq"
|
||||
CIFS_PKG="cifs-utils"
|
||||
apt-get update -qq
|
||||
elif command -v dnf &>/dev/null; then
|
||||
PKG_MGR="dnf"
|
||||
INSTALL_CMD="dnf install -y -q"
|
||||
CIFS_PKG="cifs-utils"
|
||||
else
|
||||
die "Unsupported package manager"
|
||||
fi
|
||||
info "Distro: ${DISTRO_ID} | Package manager: ${PKG_MGR}"
|
||||
}
|
||||
detect_distro
|
||||
|
||||
# ── Install cifs-utils ───────────────────────────────────────────────
|
||||
if ! command -v mount.cifs &>/dev/null; then
|
||||
info "Installing cifs-utils..."
|
||||
$INSTALL_CMD $CIFS_PKG
|
||||
success "cifs-utils installed"
|
||||
else
|
||||
success "cifs-utils already installed"
|
||||
fi
|
||||
|
||||
# ── Get current user (who will own the mounts) ──────────────────────
|
||||
# Works even when script is run with sudo
|
||||
REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || echo $USER)}"
|
||||
REAL_UID=$(id -u "$REAL_USER" 2>/dev/null || echo 1000)
|
||||
REAL_GID=$(id -g "$REAL_USER" 2>/dev/null || echo 1000)
|
||||
info "Mount owner: ${REAL_USER} (uid=${REAL_UID} gid=${REAL_GID})"
|
||||
|
||||
# ── Samba credentials ────────────────────────────────────────────────
|
||||
header "Samba credentials"
|
||||
CRED_FILE="/etc/samba/credentials.emilovi"
|
||||
|
||||
if [[ -f "$CRED_FILE" ]]; then
|
||||
warn "Credentials file already exists: $CRED_FILE"
|
||||
read -rp " Overwrite? [y/N]: " OVERWRITE
|
||||
[[ "${OVERWRITE,,}" != "y" ]] && info "Keeping existing credentials" || WRITE_CRED=true
|
||||
else
|
||||
WRITE_CRED=true
|
||||
fi
|
||||
|
||||
if [[ "$WRITE_CRED" == "true" ]]; then
|
||||
read -rp " Samba username: " SAMBA_USER
|
||||
read -rsp " Samba password: " SAMBA_PASS
|
||||
echo ""
|
||||
mkdir -p /etc/samba
|
||||
cat > "$CRED_FILE" << EOF
|
||||
username=${SAMBA_USER}
|
||||
password=${SAMBA_PASS}
|
||||
domain=WORKGROUP
|
||||
EOF
|
||||
chmod 600 "$CRED_FILE"
|
||||
success "Credentials saved to $CRED_FILE (permissions: 600)"
|
||||
fi
|
||||
|
||||
# ── Common mount options (exact copy of desktop pattern) ────────────
|
||||
SAMBA_SERVER="10.0.99.100"
|
||||
CIFS_OPTS="credentials=${CRED_FILE},uid=${REAL_UID},gid=${REAL_GID},vers=3.0,iocharset=utf8,cache=none,nofail,noauto,x-systemd.automount,x-systemd.idle-timeout=600"
|
||||
|
||||
# ── Define mounts ────────────────────────────────────────────────────
|
||||
# Format: "share_name|mount_point"
|
||||
MOUNTS=(
|
||||
# Media — 12TB HDD
|
||||
"Movies|/mnt/media/movies"
|
||||
"TVShows|/mnt/media/tvshows"
|
||||
"Music|/mnt/media/music"
|
||||
"Audiobooks|/mnt/media/audiobooks"
|
||||
"Ebooks|/mnt/media/ebooks"
|
||||
# Shared — 4TB NVMe
|
||||
"Family-Documents|/mnt/shared/documents"
|
||||
"Family-Photos|/mnt/shared/photos"
|
||||
"Family-Downloads|/mnt/shared/downloads"
|
||||
# Users — 4TB NVMe
|
||||
"Edemilov|/mnt/users/edemilov"
|
||||
"Aemilova|/mnt/users/aemilova"
|
||||
"Memilova|/mnt/users/memilova"
|
||||
"Nemilova|/mnt/users/nemilova"
|
||||
"Ediemilov|/mnt/users/ediemilov"
|
||||
# Depo — HDD
|
||||
"Depo|/mnt/depo"
|
||||
# Isos
|
||||
"Isos|/mnt/depo/isos"
|
||||
# PCBackups
|
||||
"PCBackups|/mnt/pcbackups"
|
||||
)
|
||||
|
||||
# ── Create mount dirs ────────────────────────────────────────────────
|
||||
header "Creating mount directories"
|
||||
for MOUNT in "${MOUNTS[@]}"; do
|
||||
MPOINT="${MOUNT##*|}"
|
||||
mkdir -p "$MPOINT"
|
||||
info " Created: $MPOINT"
|
||||
done
|
||||
success "All mount directories ready"
|
||||
|
||||
# ── Write fstab ──────────────────────────────────────────────────────
|
||||
header "Configuring /etc/fstab"
|
||||
FSTAB_MARKER="# vanga homelab Samba — ${SAMBA_SERVER}"
|
||||
|
||||
# Remove existing vanga block if present
|
||||
if grep -q "$FSTAB_MARKER" /etc/fstab; then
|
||||
warn "Existing vanga fstab entries found — replacing..."
|
||||
# Use python3 to safely remove the block
|
||||
python3 -c "
|
||||
import re
|
||||
with open('/etc/fstab', 'r') as f:
|
||||
content = f.read()
|
||||
# Remove from marker to next blank section
|
||||
content = re.sub(r'# vanga homelab.*?(?=\n\n|\Z)', '', content, flags=re.DOTALL)
|
||||
with open('/etc/fstab', 'w') as f:
|
||||
f.write(content)
|
||||
"
|
||||
fi
|
||||
|
||||
# Append new entries
|
||||
{
|
||||
echo ""
|
||||
echo "# ═══════════════════════════════════════════════════════"
|
||||
echo "# vanga homelab Samba — ${SAMBA_SERVER}"
|
||||
echo "# Generated by netdrivelink.sh on $(date '+%Y-%m-%d %H:%M')"
|
||||
echo "# ═══════════════════════════════════════════════════════"
|
||||
echo "# Media — 12TB HDD"
|
||||
echo "//${SAMBA_SERVER}/Movies /mnt/media/movies cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/TVShows /mnt/media/tvshows cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Music /mnt/media/music cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Audiobooks /mnt/media/audiobooks cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Ebooks /mnt/media/ebooks cifs ${CIFS_OPTS} 0 0"
|
||||
echo "# Shared — 4TB NVMe"
|
||||
echo "//${SAMBA_SERVER}/Family-Documents /mnt/shared/documents cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Family-Photos /mnt/shared/photos cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Family-Downloads /mnt/shared/downloads cifs ${CIFS_OPTS} 0 0"
|
||||
echo "# Users — 4TB NVMe"
|
||||
echo "//${SAMBA_SERVER}/Edemilov /mnt/users/edemilov cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Aemilova /mnt/users/aemilova cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Memilova /mnt/users/memilova cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Nemilova /mnt/users/nemilova cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Ediemilov /mnt/users/ediemilov cifs ${CIFS_OPTS} 0 0"
|
||||
echo "# Depo — HDD"
|
||||
echo "//${SAMBA_SERVER}/Depo /mnt/depo cifs ${CIFS_OPTS} 0 0"
|
||||
echo "//${SAMBA_SERVER}/Isos /mnt/depo/isos cifs ${CIFS_OPTS} 0 0"
|
||||
echo "# PCBackups"
|
||||
echo "//${SAMBA_SERVER}/PCBackups /mnt/pcbackups cifs ${CIFS_OPTS} 0 0"
|
||||
} >> /etc/fstab
|
||||
success "fstab updated"
|
||||
|
||||
# ── Reload systemd to pick up new automount units ────────────────────
|
||||
systemctl daemon-reload
|
||||
success "systemd daemon reloaded"
|
||||
|
||||
# ── Test mount (Edemilov share only) ─────────────────────────────────
|
||||
header "Testing connection"
|
||||
info "Testing mount of /mnt/users/edemilov..."
|
||||
if timeout 10 mount /mnt/users/edemilov 2>/dev/null; then
|
||||
success "Samba connection working! ✓"
|
||||
umount /mnt/users/edemilov
|
||||
else
|
||||
warn "Test mount failed — check credentials or server reachability"
|
||||
warn "Ping test: $(ping -c1 -W2 ${SAMBA_SERVER} &>/dev/null && echo OK || echo FAILED)"
|
||||
fi
|
||||
|
||||
# ── 2nd drive setup ──────────────────────────────────────────────────
|
||||
header "2nd drive — /nvme-data symlinks"
|
||||
|
||||
NVME_DATA="/nvme-data"
|
||||
HOME_DIR="/home/${REAL_USER}"
|
||||
|
||||
# Check if 2nd drive is mounted
|
||||
if [[ ! -d "$NVME_DATA" ]]; then
|
||||
warn "/nvme-data not found — skipping 2nd drive setup"
|
||||
warn "Mount your 2nd drive first, then re-run this script"
|
||||
else
|
||||
info "Found $NVME_DATA — setting up folder structure and symlinks..."
|
||||
|
||||
# Create standard folders on the nvme drive
|
||||
FOLDERS=("Documents" "Downloads" "Pictures" "Videos" "Projects" "Music" "Archive" "Scripts")
|
||||
for FOLDER in "${FOLDERS[@]}"; do
|
||||
mkdir -p "${NVME_DATA}/${FOLDER}"
|
||||
chown "${REAL_USER}:${REAL_USER}" "${NVME_DATA}/${FOLDER}" 2>/dev/null || true
|
||||
done
|
||||
success "Folder structure created on /nvme-data"
|
||||
|
||||
# Symlink ~/XDG dirs → /nvme-data/X
|
||||
SYMLINKS=(
|
||||
"Documents"
|
||||
"Downloads"
|
||||
"Pictures"
|
||||
"Videos"
|
||||
"Projects"
|
||||
"Music"
|
||||
)
|
||||
|
||||
info "Creating symlinks in ${HOME_DIR}..."
|
||||
for LINK in "${SYMLINKS[@]}"; do
|
||||
TARGET="${NVME_DATA}/${LINK}"
|
||||
LINKPATH="${HOME_DIR}/${LINK}"
|
||||
|
||||
if [[ -L "$LINKPATH" ]]; then
|
||||
warn " Symlink already exists: ${LINK} → $(readlink $LINKPATH)"
|
||||
elif [[ -d "$LINKPATH" ]]; then
|
||||
warn " Real directory exists: $LINKPATH"
|
||||
info " Moving contents to ${TARGET}..."
|
||||
mv "${LINKPATH}"/* "${TARGET}/" 2>/dev/null || true
|
||||
rmdir "${LINKPATH}" 2>/dev/null || rm -rf "${LINKPATH}"
|
||||
ln -s "${TARGET}" "${LINKPATH}"
|
||||
chown -h "${REAL_USER}:${REAL_USER}" "${LINKPATH}"
|
||||
success " Migrated & linked: ~/${LINK} → ${TARGET}"
|
||||
else
|
||||
ln -s "${TARGET}" "${LINKPATH}"
|
||||
chown -h "${REAL_USER}:${REAL_USER}" "${LINKPATH}"
|
||||
success " Linked: ~/${LINK} → ${TARGET}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Special: ~/Scripts → /nvme-data/Scripts
|
||||
SCRIPTS_LINK="${HOME_DIR}/Scripts"
|
||||
if [[ ! -L "$SCRIPTS_LINK" ]]; then
|
||||
ln -s "${NVME_DATA}/Scripts" "${SCRIPTS_LINK}"
|
||||
chown -h "${REAL_USER}:${REAL_USER}" "${SCRIPTS_LINK}"
|
||||
success " Linked: ~/Scripts → /nvme-data/Scripts"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Done ─────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${BOLD}${GREEN}═══════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BOLD} ✓ netdrivelink.sh complete!${NC}"
|
||||
echo -e "${GREEN}═══════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}Mounts are automounted on first access${NC}"
|
||||
echo -e " Try: ${YELLOW}ls /mnt/users/edemilov${NC}"
|
||||
echo -e " Or: ${YELLOW}ls ~/Documents${NC} (if 2nd drive was found)"
|
||||
echo ""
|
||||
echo -e " ${CYAN}To manually trigger all mounts:${NC}"
|
||||
echo -e " ${YELLOW}sudo mount -a${NC}"
|
||||
echo ""
|
||||
Reference in new issue
Block a user