dartokloning a révisé ce gist . Aller à la révision
1 file changed, 114 insertions
install-nerd-fonts.sh(fichier créé)
| @@ -0,0 +1,114 @@ | |||
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | set -Eeuo pipefail | |
| 3 | + | ||
| 4 | + | DEST='/usr/local/share/fonts/nerd-fonts' | |
| 5 | + | API='https://api.github.com/repos/ryanoasis/nerd-fonts/releases/latest' | |
| 6 | + | ||
| 7 | + | if [[ ! -t 0 ]]; then echo 'Jalankan secara interaktif di terminal.' >&2; exit 1; fi | |
| 8 | + | if [[ $EUID -eq 0 ]]; then echo 'Jalankan sebagai user biasa (tanpa sudo); script akan meminta sudo saat perlu.' >&2; exit 1; fi | |
| 9 | + | for cmd in curl python3 tar find mktemp sudo fc-cache; do | |
| 10 | + | if ! command -v "$cmd" >/dev/null 2>&1; then | |
| 11 | + | echo "Dependency belum tersedia: $cmd" >&2 | |
| 12 | + | echo 'Install dahulu: sudo apt update && sudo apt install curl python3 xz-utils fontconfig sudo ca-certificates' >&2 | |
| 13 | + | exit 1 | |
| 14 | + | fi | |
| 15 | + | done | |
| 16 | + | if ! tar --help 2>/dev/null | grep -q -- '--xz'; then | |
| 17 | + | echo 'tar tidak mendukung xz. Install: sudo apt install xz-utils' >&2; exit 1 | |
| 18 | + | fi | |
| 19 | + | WORK=$(mktemp -d) | |
| 20 | + | trap 'rm -rf "$WORK"' EXIT | |
| 21 | + | printf 'Mengambil katalog rilis Nerd Fonts resmi...\n' | |
| 22 | + | if ! curl -fLsS --retry 3 --connect-timeout 15 --max-time 90 \ | |
| 23 | + | -H 'Accept: application/vnd.github+json' -H 'User-Agent: Debian-Nerd-Fonts-Installer' \ | |
| 24 | + | "$API" -o "$WORK/release.json"; then | |
| 25 | + | echo 'Gagal mengambil data GitHub API (periksa internet atau rate limit).' >&2; exit 1 | |
| 26 | + | fi | |
| 27 | + | if ! python3 - "$WORK/release.json" "$WORK/catalog.tsv" "$WORK/version" <<'PY' | |
| 28 | + | import json,sys | |
| 29 | + | with open(sys.argv[1],encoding='utf-8') as f: data=json.load(f) | |
| 30 | + | assets=[] | |
| 31 | + | for a in data.get('assets',[]): | |
| 32 | + | name=a.get('name','') | |
| 33 | + | if name.endswith('.tar.xz') and not name.endswith('xv.tar.xz'): | |
| 34 | + | url=a.get('browser_download_url','') | |
| 35 | + | if url.startswith('https://github.com/ryanoasis/nerd-fonts/releases/download/'): | |
| 36 | + | assets.append((name[:-7],url,a.get('digest') or '')) | |
| 37 | + | assets.sort(key=lambda x:x[0].lower()) | |
| 38 | + | if not assets: raise SystemExit('Tidak ada arsip font .tar.xz pada rilis.') | |
| 39 | + | with open(sys.argv[2],'w',encoding='utf-8') as f: | |
| 40 | + | for name,url,digest in assets: f.write(f'{name}\t{url}\t{digest}\n') | |
| 41 | + | with open(sys.argv[3],'w') as f:f.write(data.get('tag_name','unknown')) | |
| 42 | + | PY | |
| 43 | + | then echo 'Katalog rilis tidak valid.' >&2; exit 1; fi | |
| 44 | + | mapfile -t LINES < "$WORK/catalog.tsv" | |
| 45 | + | VERSION=$(cat "$WORK/version") | |
| 46 | + | printf '\nNerd Fonts %s | Tujuan: %s\n\n' "$VERSION" "$DEST" | |
| 47 | + | for i in "${!LINES[@]}"; do | |
| 48 | + | IFS=$'\t' read -r name url digest <<< "${LINES[$i]}" | |
| 49 | + | status='' | |
| 50 | + | [[ -d "$DEST/$name" ]] && status=' [TERPASANG]' | |
| 51 | + | printf '%3d. %-28s%s\n' "$((i+1))" "$name" "$status" | |
| 52 | + | done | |
| 53 | + | printf '\nMasukkan nomor, contoh 1,2,4 (atau all untuk semua; q untuk keluar): ' | |
| 54 | + | read -r selection | |
| 55 | + | [[ $selection == q || $selection == Q ]] && exit 0 | |
| 56 | + | indices=() | |
| 57 | + | if [[ $selection == all || $selection == ALL ]]; then | |
| 58 | + | for i in "${!LINES[@]}"; do indices+=("$i"); done | |
| 59 | + | else | |
| 60 | + | selection=${selection//[[:space:]]/} | |
| 61 | + | if [[ ! $selection =~ ^[0-9]+(,[0-9]+)*$ ]]; then echo 'Format pilihan tidak valid.' >&2; exit 1; fi | |
| 62 | + | declare -A seen=() | |
| 63 | + | IFS=',' read -ra choices <<< "$selection" | |
| 64 | + | for n in "${choices[@]}"; do | |
| 65 | + | n=$((10#$n)) | |
| 66 | + | if (( n < 1 || n > ${#LINES[@]} )); then echo "Nomor tidak tersedia: $n" >&2; exit 1; fi | |
| 67 | + | if [[ ! ${seen[$n]+x} ]]; then indices+=("$((n-1))"); seen[$n]=1; fi | |
| 68 | + | done | |
| 69 | + | fi | |
| 70 | + | printf '\nAkan memasang %d keluarga font secara GLOBAL. Lanjutkan? [y/N]: ' "${#indices[@]}" | |
| 71 | + | read -r confirm | |
| 72 | + | [[ $confirm == y || $confirm == Y ]] || exit 0 | |
| 73 | + | sudo -v | |
| 74 | + | sudo install -d -m 755 "$DEST" | |
| 75 | + | installed=0; failed=0 | |
| 76 | + | for idx in "${indices[@]}"; do | |
| 77 | + | IFS=$'\t' read -r name url digest <<< "${LINES[$idx]}" | |
| 78 | + | printf '\n==> %s\n' "$name" | |
| 79 | + | archive="$WORK/$name.tar.xz" | |
| 80 | + | stage="$WORK/stage-$idx" | |
| 81 | + | mkdir -p "$stage" | |
| 82 | + | if ! curl -fL --retry 3 --connect-timeout 15 "$url" -o "$archive"; then | |
| 83 | + | echo "GAGAL download: $name" >&2; ((failed+=1)); continue | |
| 84 | + | fi | |
| 85 | + | if [[ $digest == sha256:* ]]; then | |
| 86 | + | if ! printf '%s %s\n' "${digest#sha256:}" "$archive" | sha256sum -c --status; then | |
| 87 | + | echo "GAGAL checksum SHA256: $name" >&2; ((failed+=1)); continue | |
| 88 | + | fi | |
| 89 | + | fi | |
| 90 | + | if ! tar -tJf "$archive" >/dev/null 2>&1 || ! tar -xJf "$archive" -C "$stage" --no-same-owner --no-same-permissions; then | |
| 91 | + | echo "GAGAL ekstraksi: $name" >&2; ((failed+=1)); continue | |
| 92 | + | fi | |
| 93 | + | mapfile -d '' fontfiles < <(find "$stage" -type f \( -iname '*.ttf' -o -iname '*.otf' \) -print0) | |
| 94 | + | if (( ${#fontfiles[@]} == 0 )); then echo "Tidak ada file font: $name" >&2; ((failed+=1)); continue; fi | |
| 95 | + | target="$DEST/$name" | |
| 96 | + | # Staging pada filesystem tujuan, lalu ganti direktori font setelah semua file disalin. | |
| 97 | + | tmp_target="$DEST/.${name}.new.$$" | |
| 98 | + | sudo install -d -m 755 "$tmp_target" | |
| 99 | + | ok=1 | |
| 100 | + | for font in "${fontfiles[@]}"; do | |
| 101 | + | if ! sudo install -m 644 "$font" "$tmp_target/$(basename "$font")"; then ok=0; break; fi | |
| 102 | + | done | |
| 103 | + | if (( ok == 0 )); then sudo rm -rf -- "$tmp_target"; echo "GAGAL copy: $name" >&2; ((failed+=1)); continue; fi | |
| 104 | + | if ! sudo rm -rf -- "$target" || ! sudo mv -- "$tmp_target" "$target"; then | |
| 105 | + | echo "GAGAL finalisasi: $name" >&2; ((failed+=1)); continue | |
| 106 | + | fi | |
| 107 | + | printf 'OK: %s (%d file font)\n' "$name" "${#fontfiles[@]}" | |
| 108 | + | ((installed+=1)) | |
| 109 | + | done | |
| 110 | + | printf '\nMemperbarui font cache...\n' | |
| 111 | + | sudo fc-cache -f "$DEST" | |
| 112 | + | printf '\nSelesai. Berhasil: %d | Gagal: %d\n' "$installed" "$failed" | |
| 113 | + | printf 'Verifikasi: fc-list | grep -i "Nerd Font"\n' | |
| 114 | + | (( failed == 0 )) | |
Plus récent
Plus ancien