install-nerd-fonts.sh
· 5.0 KiB · Bash
Eredeti
#!/usr/bin/env bash
set -Eeuo pipefail
DEST='/usr/local/share/fonts/nerd-fonts'
API='https://api.github.com/repos/ryanoasis/nerd-fonts/releases/latest'
if [[ ! -t 0 ]]; then echo 'Jalankan secara interaktif di terminal.' >&2; exit 1; fi
if [[ $EUID -eq 0 ]]; then echo 'Jalankan sebagai user biasa (tanpa sudo); script akan meminta sudo saat perlu.' >&2; exit 1; fi
for cmd in curl python3 tar find mktemp sudo fc-cache; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Dependency belum tersedia: $cmd" >&2
echo 'Install dahulu: sudo apt update && sudo apt install curl python3 xz-utils fontconfig sudo ca-certificates' >&2
exit 1
fi
done
if ! tar --help 2>/dev/null | grep -q -- '--xz'; then
echo 'tar tidak mendukung xz. Install: sudo apt install xz-utils' >&2; exit 1
fi
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
printf 'Mengambil katalog rilis Nerd Fonts resmi...\n'
if ! curl -fLsS --retry 3 --connect-timeout 15 --max-time 90 \
-H 'Accept: application/vnd.github+json' -H 'User-Agent: Debian-Nerd-Fonts-Installer' \
"$API" -o "$WORK/release.json"; then
echo 'Gagal mengambil data GitHub API (periksa internet atau rate limit).' >&2; exit 1
fi
if ! python3 - "$WORK/release.json" "$WORK/catalog.tsv" "$WORK/version" <<'PY'
import json,sys
with open(sys.argv[1],encoding='utf-8') as f: data=json.load(f)
assets=[]
for a in data.get('assets',[]):
name=a.get('name','')
if name.endswith('.tar.xz') and not name.endswith('xv.tar.xz'):
url=a.get('browser_download_url','')
if url.startswith('https://github.com/ryanoasis/nerd-fonts/releases/download/'):
assets.append((name[:-7],url,a.get('digest') or ''))
assets.sort(key=lambda x:x[0].lower())
if not assets: raise SystemExit('Tidak ada arsip font .tar.xz pada rilis.')
with open(sys.argv[2],'w',encoding='utf-8') as f:
for name,url,digest in assets: f.write(f'{name}\t{url}\t{digest}\n')
with open(sys.argv[3],'w') as f:f.write(data.get('tag_name','unknown'))
PY
then echo 'Katalog rilis tidak valid.' >&2; exit 1; fi
mapfile -t LINES < "$WORK/catalog.tsv"
VERSION=$(cat "$WORK/version")
printf '\nNerd Fonts %s | Tujuan: %s\n\n' "$VERSION" "$DEST"
for i in "${!LINES[@]}"; do
IFS=$'\t' read -r name url digest <<< "${LINES[$i]}"
status=''
[[ -d "$DEST/$name" ]] && status=' [TERPASANG]'
printf '%3d. %-28s%s\n' "$((i+1))" "$name" "$status"
done
printf '\nMasukkan nomor, contoh 1,2,4 (atau all untuk semua; q untuk keluar): '
read -r selection
[[ $selection == q || $selection == Q ]] && exit 0
indices=()
if [[ $selection == all || $selection == ALL ]]; then
for i in "${!LINES[@]}"; do indices+=("$i"); done
else
selection=${selection//[[:space:]]/}
if [[ ! $selection =~ ^[0-9]+(,[0-9]+)*$ ]]; then echo 'Format pilihan tidak valid.' >&2; exit 1; fi
declare -A seen=()
IFS=',' read -ra choices <<< "$selection"
for n in "${choices[@]}"; do
n=$((10#$n))
if (( n < 1 || n > ${#LINES[@]} )); then echo "Nomor tidak tersedia: $n" >&2; exit 1; fi
if [[ ! ${seen[$n]+x} ]]; then indices+=("$((n-1))"); seen[$n]=1; fi
done
fi
printf '\nAkan memasang %d keluarga font secara GLOBAL. Lanjutkan? [y/N]: ' "${#indices[@]}"
read -r confirm
[[ $confirm == y || $confirm == Y ]] || exit 0
sudo -v
sudo install -d -m 755 "$DEST"
installed=0; failed=0
for idx in "${indices[@]}"; do
IFS=$'\t' read -r name url digest <<< "${LINES[$idx]}"
printf '\n==> %s\n' "$name"
archive="$WORK/$name.tar.xz"
stage="$WORK/stage-$idx"
mkdir -p "$stage"
if ! curl -fL --retry 3 --connect-timeout 15 "$url" -o "$archive"; then
echo "GAGAL download: $name" >&2; ((failed+=1)); continue
fi
if [[ $digest == sha256:* ]]; then
if ! printf '%s %s\n' "${digest#sha256:}" "$archive" | sha256sum -c --status; then
echo "GAGAL checksum SHA256: $name" >&2; ((failed+=1)); continue
fi
fi
if ! tar -tJf "$archive" >/dev/null 2>&1 || ! tar -xJf "$archive" -C "$stage" --no-same-owner --no-same-permissions; then
echo "GAGAL ekstraksi: $name" >&2; ((failed+=1)); continue
fi
mapfile -d '' fontfiles < <(find "$stage" -type f \( -iname '*.ttf' -o -iname '*.otf' \) -print0)
if (( ${#fontfiles[@]} == 0 )); then echo "Tidak ada file font: $name" >&2; ((failed+=1)); continue; fi
target="$DEST/$name"
# Staging pada filesystem tujuan, lalu ganti direktori font setelah semua file disalin.
tmp_target="$DEST/.${name}.new.$$"
sudo install -d -m 755 "$tmp_target"
ok=1
for font in "${fontfiles[@]}"; do
if ! sudo install -m 644 "$font" "$tmp_target/$(basename "$font")"; then ok=0; break; fi
done
if (( ok == 0 )); then sudo rm -rf -- "$tmp_target"; echo "GAGAL copy: $name" >&2; ((failed+=1)); continue; fi
if ! sudo rm -rf -- "$target" || ! sudo mv -- "$tmp_target" "$target"; then
echo "GAGAL finalisasi: $name" >&2; ((failed+=1)); continue
fi
printf 'OK: %s (%d file font)\n' "$name" "${#fontfiles[@]}"
((installed+=1))
done
printf '\nMemperbarui font cache...\n'
sudo fc-cache -f "$DEST"
printf '\nSelesai. Berhasil: %d | Gagal: %d\n' "$installed" "$failed"
printf 'Verifikasi: fc-list | grep -i "Nerd Font"\n'
(( failed == 0 ))
| 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 )) |
| 115 |