Pemeriksaan menghasilkan angka, pembersihan menghasilkan daftar — dan keduanya belum berarti apa-apa sampai ada yang membacanya. Laporan adalah bagian proyek yang ditujukan untuk manusia: administrator yang punya dua menit sebelum rapat dan perlu tahu apakah ada yang perlu ditindaklanjuti. Pada bagian ini kita merangkai hasil Bagian 1 dan Bagian 2 menjadi skrip final yang menulis laporan terstruktur ke stdout sekaligus berkas, dengan rekomendasi yang berubah mengikuti temuan, lalu membuktikan seluruh janji desain lewat test suite.
Empat Seksi, Empat Pertanyaan #
Laporan yang mudah dipindai punya seksi tetap — pembaca yang sama selalu tahu ke mana melompat. Empat seksi kita menjawab empat pertanyaan berurutan:
| Seksi | Pertanyaan yang dijawab | Isi |
|---|---|---|
PEMERIKSAAN | apa yang dicek dan bagaimana hasilnya? | disk per mount dengan status, jumlah paket tertunda, service gagal |
PEMBERSIHAN | apa yang dibersihkan? | kandidat atau file terhapus per whitelist, dengan ukuran byte |
HEMAT RUANG | berapa untungnya? | total file dan byte — potensi di dry-run, nyata setelah apply |
REKOMENDASI | apa selanjutnya? | langkah lanjutan yang diturunkan dari temuan |
Header laporan menyimpan dua hal yang menentukan cara membacanya: tanggal acuan (dari HARI_INI) dan mode — supaya tidak ada keraguan apakah baris [dry-run] berarti “sudah terhapus” atau “akan dihapus”. Karena formatnya berulang, laporan juga mudah disaring: grep WASPADA untuk temuan, grep terhapus untuk yang sudah dibersihkan, dan seterusnya.
Skrip Final dan Sesi Lengkap #
Skrip final pemeliharaan.sh menyatukan semua bagian: pembacaan argumen dan konfigurasi, tiga pemeriksaan dari Bagian 1, pembersihan ber-whitelist dari Bagian 2, lalu penyusunan laporan. Blok berikut menyiapkan ulang fixture lengkap server-kantor, menuliskan skrip dan konfigurasi, menjalankan dry-run, apply, dan satu dry-run lagi setelah pembersihan — versi finalnya juga tersedia sebagai src/pemeliharaan.sh pada bagian Berkas Sumber di bawah halaman ini:
mkdir -p data/cache data/log data/tmp
cat > data/df.txt <<'EOF'
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 35G 13G 73% /
/dev/sdb1 200G 170G 21G 89% /data
tmpfs 7.8G 1.1M 7.8G 1% /dev/shm
EOF
cat > data/paket.txt <<'EOF'
Reading package lists... Done
Building dependency tree... Done
Calculating upgrade... Done
Reading state information... Done
The following packages will be upgraded:
curl libcurl4 libssl3 openssl
4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Inst curl [7.88.1-10] (7.88.1-11)
Inst libcurl4 [7.88.1-10] (7.88.1-11)
Inst libssl3 [3.0.8-1] (3.0.8-2)
Inst openssl [3.0.8-1] (3.0.8-2)
Conf curl (7.88.1-11)
Conf libcurl4 (7.88.1-11)
Conf libssl3 (3.0.8-2)
Conf openssl (3.0.8-2)
EOF
cat > data/services.txt <<'EOF'
nginx.service failed
postgresql.service failed
sshd.service running
cron.service running
EOF
printf 'isi cache lama\n' > data/cache/lama.txt; touch -d "2026-01-10" data/cache/lama.txt
printf 'isi cache baru\n' > data/cache/baru.txt; touch -d "2026-02-10" data/cache/baru.txt
printf 'isi log lama terkompresi\n' > data/log/app.log.1.gz; touch -d "2026-01-25" data/log/app.log.1.gz
printf 'isi log lama tapi bukan gz\n' > data/log/app.log; touch -d "2026-01-20" data/log/app.log
printf 'isi log baru\n' > data/log/app.log.2.gz; touch -d "2026-02-12" data/log/app.log.2.gz
printf 'sisa instalasi lama\n' > data/tmp/sisa.tmp; touch -d "2026-02-01" data/tmp/sisa.tmp
printf 'file tmp aktif\n' > data/tmp/aktif.tmp; touch -d "2026-02-13" data/tmp/aktif.tmp
cat > pemeliharaan.conf <<'CONF'
# pemeliharaan.conf — konfigurasi skrip pemeliharaan.
# Sunting nilai di bawah ini sesuai komputer Anda; skrip pemeliharaan.sh
# tidak perlu disentuh. Jalur whitelist dihitung dari direktori kerja
# saat skrip dijalankan; jalur absolut (mis. /var/cache/myapp) juga
# boleh dipakai.
# Whitelist folder yang BOLEH dibersihkan, dipisah spasi.
# Folder di luar tiga daftar ini tidak pernah disentuh skrip.
CACHE_DIRS="data/cache"
LOG_DIRS="data/log"
TMP_DIRS="data/tmp"
# Umur maksimum file (hari) sebelum layak dibersihkan.
CACHE_MAX_DAYS=30
LOG_MAX_DAYS=14
TMP_MAX_DAYS=7
# Ambang pemakaian disk (%) yang menyalakan status WASPADA (1-99).
DISK_WARN_PCT=80
# Berkas laporan (opsional; default: laporan-pemeliharaan.txt).
REPORT_FILE="laporan-pemeliharaan.txt"
CONF
cat > pemeliharaan.sh <<'BASH'
#!/usr/bin/env bash
# pemeliharaan.sh — pemeliharaan berkala: periksa, bersihkan, laporkan.
#
# Membaca snapshot data sistem (df.txt, paket.txt, services.txt) dari
# folder data, memeriksa tiga hal — pemakaian disk, paket update
# tertunda, service gagal — lalu membersihkan file cache, log terkompresi
# (*.gz), dan file tmp usang HANYA di dalam folder whitelist yang
# tercantum di berkas konfigurasi. Mode default DRY-RUN: tidak ada
# file yang dihapus sampai --apply diberikan, dan mode tidak bisa
# diubah dari konfigurasi. Hasil akhirnya adalah laporan terstruktur
# yang ditulis ke stdout sekaligus berkas laporan.
#
# Pemakaian:
# bash pemeliharaan.sh [--data-dir DIR] [--config FILE] [--apply]
#
# Variabel lingkungan (berguna untuk pengujian):
# CONF berkas konfigurasi yang dibaca (default: pemeliharaan.conf)
# SOURCE_DIR folder data; kalah oleh --data-dir (default: data)
# HARI_INI tanggal acuan umur file, format YYYY-MM-DD (default: hari ini)
#
# Kode keluar:
# 0 sukses 1 konfigurasi tidak valid
# 2 berkas snapshot tidak ada 3 folder whitelist tidak ada
set -u
CONF="${CONF:-pemeliharaan.conf}"
DATA_DIR="${SOURCE_DIR:-data}"
HARI_INI="${HARI_INI:-$(date +%F)}"
APPLY=0
# --- 1. Baca argumen -----------------------------------------------------
while [ $# -gt 0 ]; do
case "$1" in
--apply) APPLY=1; shift ;;
--data-dir|--config)
if [ $# -lt 2 ]; then
printf 'pemeliharaan.sh: %s butuh nilai\n' "$1" >&2
exit 1
fi
if [ "$1" = "--data-dir" ]; then
DATA_DIR="$2"
else
CONF="$2"
fi
shift 2
;;
*)
printf 'pemeliharaan.sh: argumen tidak dikenal: %s\n' "$1" >&2
exit 1
;;
esac
done
# --- 2. Baca dan validasi konfigurasi ------------------------------------
if [ ! -f "$CONF" ]; then
printf 'pemeliharaan.sh: berkas konfigurasi %s tidak ditemukan\n' "$CONF" >&2
exit 1
fi
# shellcheck source=/dev/null
. "$CONF"
if [ -z "${CACHE_DIRS:-}" ] && [ -z "${LOG_DIRS:-}" ] && [ -z "${TMP_DIRS:-}" ]; then
printf 'pemeliharaan.sh: whitelist kosong (CACHE_DIRS/LOG_DIRS/TMP_DIRS) di %s\n' "$CONF" >&2
exit 1
fi
valid_umur() {
case $1 in
''|*[!0-9]*|0) return 1 ;;
esac
return 0
}
valid_umur "${CACHE_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: CACHE_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
valid_umur "${LOG_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: LOG_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
valid_umur "${TMP_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: TMP_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
case ${DISK_WARN_PCT:-} in
''|*[!0-9]*|0)
printf 'pemeliharaan.sh: DISK_WARN_PCT harus angka 1-99\n' >&2
exit 1
;;
esac
if [ "$DISK_WARN_PCT" -gt 99 ]; then
printf 'pemeliharaan.sh: DISK_WARN_PCT harus angka 1-99\n' >&2
exit 1
fi
# Mode sengaja di set SETELAH konfigurasi dibaca: walau berkas
# konfigurasi berisi "MODE=apply", nilainya tetap ditimpa di sini.
# Hanya argumen --apply yang boleh menyalakan pembersihan sungguhan.
MODE="dry-run"
if [ "$APPLY" -eq 1 ]; then
MODE="apply"
fi
# --- 3. Validasi snapshot data --------------------------------------------
for f in df.txt paket.txt services.txt; do
if [ ! -f "$DATA_DIR/$f" ]; then
printf 'pemeliharaan.sh: snapshot %s/%s tidak ditemukan\n' "$DATA_DIR" "$f" >&2
exit 2
fi
done
# --- 4. Pemeriksaan -------------------------------------------------------
# Temuan pemeriksaan terkumpul di sini, lalu menjadi bagian laporan.
# Disk: kolom ke-5 df adalah Use%; "+0" di awk membuang karakter '%'
# sehingga "89%" terbaca sebagai angka 89.
DISK_BARIS=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 {
pct = $5 + 0
if (pct >= ambang)
printf " %-10s %3d%% WASPADA\n", $6, pct
else
printf " %-10s %3d%% OK\n", $6, pct
}' "$DATA_DIR/df.txt")
DISK_MELEBIHI=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 && ($5 + 0) >= ambang {
printf "%s%s", pisah, $6
pisah = ", "
}' "$DATA_DIR/df.txt")
DISK_N=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 && ($5 + 0) >= ambang { n++ } END { print n + 0 }' "$DATA_DIR/df.txt")
# Paket tertunda: output simulasi apt-get -s upgrade menandai tiap paket
# dengan satu baris "Inst <nama> [versi lama] (versi baru)".
PAKET_DAFTAR=$(awk '/^Inst / {
printf "%s%s", pisah, $2
pisah = ", "
}' "$DATA_DIR/paket.txt")
PAKET_N=$(awk '/^Inst / { n++ } END { print n + 0 }' "$DATA_DIR/paket.txt")
# Service gagal: snapshot berformat "unit substate" per baris.
SVC_DAFTAR=$(awk '$2 == "failed" {
printf "%s%s", pisah, $1
pisah = ", "
}' "$DATA_DIR/services.txt")
SVC_N=$(awk '$2 == "failed" { n++ } END { print n + 0 }' "$DATA_DIR/services.txt")
# --- 5. Pembersihan (hanya di dalam whitelist) -----------------------------
# Hasil pembersihan terkumpul di BAG_BERSIH; penghitung JUMLAH_FILE dan
# HEMAT_BYTES dipakai seksi HEMAT RUANG di laporan.
JUMLAH_FILE=0
HEMAT_BYTES=0
BAG_BERSIH=""
# bersihkan <judul> <dirs> <umur-hari> <pola-nama>:
# kumpulkan file di folder whitelist yang lebih tua dari batas umur,
# catat ukurannya untuk laporan, dan hapus hanya bila mode apply.
# -type f menjaga folder tetap utuh; find hanya berangkat dari folder
# whitelist, jadi file di luar daftar tidak pernah tersentuh.
bersihkan() {
judul="$1"
dirs="$2"
umur="$3"
pola="${4:-*}"
batas=$(date -d "$HARI_INI -$umur days" +%F)
if [ "$pola" = "*" ]; then
label="file"
else
label="file $pola"
fi
terpakai=0
if [ -n "$BAG_BERSIH" ]; then
BAG_BERSIH+=$'\n'
fi
BAG_BERSIH+="$judul — $label lebih tua dari $batas (whitelist: $dirs)"$'\n'
# shellcheck disable=SC2086 # whitelist memang dipisah spasi
for dir in $dirs; do
if [ ! -d "$dir" ]; then
printf 'pemeliharaan.sh: folder whitelist %s tidak ditemukan\n' "$dir" >&2
exit 3
fi
# Process substitution < <(...) menjaga penghitung tetap hidup
# di shell utama; pipeline biasa akan menjalankan while di
# subshell sehingga JUMLAH_FILE hilang.
while IFS= read -r f; do
[ -n "$f" ] || continue
ukuran=$(stat -c %s "$f")
JUMLAH_FILE=$((JUMLAH_FILE + 1))
HEMAT_BYTES=$((HEMAT_BYTES + ukuran))
terpakai=$((terpakai + 1))
if [ "$MODE" = "apply" ]; then
rm -f -- "$f"
BAG_BERSIH+=" [terhapus] $f (${ukuran} byte)"$'\n'
else
BAG_BERSIH+=" [dry-run] $f (${ukuran} byte)"$'\n'
fi
done < <(find "$dir" -type f -name "$pola" ! -newermt "$batas" | sort)
done
if [ "$terpakai" -eq 0 ]; then
BAG_BERSIH+=" (tidak ada kandidat)"$'\n'
fi
}
if [ -n "${CACHE_DIRS:-}" ]; then
bersihkan "cache" "$CACHE_DIRS" "$CACHE_MAX_DAYS"
fi
if [ -n "${LOG_DIRS:-}" ]; then
bersihkan "log" "$LOG_DIRS" "$LOG_MAX_DAYS" "*.gz"
fi
if [ -n "${TMP_DIRS:-}" ]; then
bersihkan "tmp" "$TMP_DIRS" "$TMP_MAX_DAYS"
fi
# --- 6. Susun laporan -----------------------------------------------------
if [ "$MODE" = "apply" ]; then
LABEL_MODE="APPLY (pembersihan benar-benar dijalankan)"
LABEL_HEMAT="benar-benar dibebaskan dari disk"
else
LABEL_MODE="DRY-RUN (tidak ada file yang dihapus)"
LABEL_HEMAT="potensi hemat; jalankan --apply untuk benar-benar menghapus"
fi
LAPORAN=""
tambah() {
LAPORAN+="$1"
LAPORAN+=$'\n'
}
tambah "================================================================"
tambah "LAPORAN PEMELIHARAAN SISTEM"
tambah "Tanggal acuan : $HARI_INI"
tambah "Mode : $LABEL_MODE"
tambah "================================================================"
tambah ""
tambah "PEMERIKSAAN"
tambah "----------------------------------------------------------------"
tambah "1. Pemakaian disk (ambang $DISK_WARN_PCT%)"
tambah "$DISK_BARIS"
tambah "2. Paket update tertunda"
if [ "$PAKET_N" -gt 0 ]; then
tambah " $PAKET_N paket siap di-upgrade: $PAKET_DAFTAR"
else
tambah " tidak ada paket tertunda"
fi
tambah "3. Service gagal"
if [ "$SVC_N" -gt 0 ]; then
tambah " $SVC_N service failed: $SVC_DAFTAR"
else
tambah " semua service berjalan normal"
fi
tambah ""
tambah "PEMBERSIHAN"
tambah "----------------------------------------------------------------"
tambah "${BAG_BERSIH%$'\n'}"
tambah ""
tambah "HEMAT RUANG"
tambah "----------------------------------------------------------------"
tambah "Total: $JUMLAH_FILE file, $HEMAT_BYTES byte ($LABEL_HEMAT)"
tambah ""
tambah "REKOMENDASI"
tambah "----------------------------------------------------------------"
nomor=1
if [ "$DISK_N" -gt 0 ]; then
tambah "$nomor. Pemakaian disk melewati ambang $DISK_WARN_PCT% di $DISK_MELEBIHI:"
tambah " cari penyumbang terbesar dengan du -h, lalu bersihkan atau pindahkan."
nomor=$((nomor + 1))
fi
if [ "$PAKET_N" -gt 0 ]; then
tambah "$nomor. Ada $PAKET_N paket update tertunda:"
tambah " jalankan apt upgrade pada jadwal maintenance berikutnya."
nomor=$((nomor + 1))
fi
if [ "$SVC_N" -gt 0 ]; then
tambah "$nomor. Ada $SVC_N service gagal ($SVC_DAFTAR):"
tambah " periksa journalctl -u <unit> untuk penyebabnya, lalu restart bila aman."
nomor=$((nomor + 1))
fi
if [ "$MODE" = "dry-run" ] && [ "$JUMLAH_FILE" -gt 0 ]; then
tambah "$nomor. DRY-RUN menemukan $JUMLAH_FILE file ($HEMAT_BYTES byte) layak dibersihkan:"
tambah " periksa daftar di bagian PEMBERSIHAN, lalu jalankan ulang dengan --apply."
nomor=$((nomor + 1))
fi
if [ "$nomor" -eq 1 ]; then
tambah "1. Tidak ada temuan: sistem dalam kondisi baik. Ulangi pemeliharaan sesuai jadwal."
fi
# --- 7. Tulis laporan ke stdout dan berkas ---------------------------------
REPORT_FILE="${REPORT_FILE:-laporan-pemeliharaan.txt}"
mkdir -p "$(dirname "$REPORT_FILE")"
printf '%s' "$LAPORAN" | tee "$REPORT_FILE"
exit 0
BASH
echo "== DRY-RUN: laporan penuh (mode default) =="
HARI_INI=2026-02-15 bash pemeliharaan.sh
echo "== APPLY: seksi akhir laporan, dibaca dari berkas =="
HARI_INI=2026-02-15 bash pemeliharaan.sh --apply > /dev/null
sed -n '/^PEMBERSIHAN/,$p' laporan-pemeliharaan.txt
echo "== folder setelah apply =="
ls -1 data/cache data/log data/tmp
echo "== dry-run berikutnya: kandidat sudah bersih =="
HARI_INI=2026-02-15 bash pemeliharaan.sh | sed -n '/^PEMBERSIHAN/,$p'Output yang Dihasilkan:
== DRY-RUN: laporan penuh (mode default) ==
================================================================
LAPORAN PEMELIHARAAN SISTEM
Tanggal acuan : 2026-02-15
Mode : DRY-RUN (tidak ada file yang dihapus)
================================================================
PEMERIKSAAN
----------------------------------------------------------------
1. Pemakaian disk (ambang 80%)
/ 73% OK
/data 89% WASPADA
/dev/shm 1% OK
2. Paket update tertunda
4 paket siap di-upgrade: curl, libcurl4, libssl3, openssl
3. Service gagal
2 service failed: nginx.service, postgresql.service
PEMBERSIHAN
----------------------------------------------------------------
cache — file lebih tua dari 2026-01-16 (whitelist: data/cache)
[dry-run] data/cache/lama.txt (15 byte)
log — file *.gz lebih tua dari 2026-02-01 (whitelist: data/log)
[dry-run] data/log/app.log.1.gz (25 byte)
tmp — file lebih tua dari 2026-02-08 (whitelist: data/tmp)
[dry-run] data/tmp/sisa.tmp (20 byte)
HEMAT RUANG
----------------------------------------------------------------
Total: 3 file, 60 byte (potensi hemat; jalankan --apply untuk benar-benar menghapus)
REKOMENDASI
----------------------------------------------------------------
1. Pemakaian disk melewati ambang 80% di /data:
cari penyumbang terbesar dengan du -h, lalu bersihkan atau pindahkan.
2. Ada 4 paket update tertunda:
jalankan apt upgrade pada jadwal maintenance berikutnya.
3. Ada 2 service gagal (nginx.service, postgresql.service):
periksa journalctl -u <unit> untuk penyebabnya, lalu restart bila aman.
4. DRY-RUN menemukan 3 file (60 byte) layak dibersihkan:
periksa daftar di bagian PEMBERSIHAN, lalu jalankan ulang dengan --apply.
== APPLY: seksi akhir laporan, dibaca dari berkas ==
PEMBERSIHAN
----------------------------------------------------------------
cache — file lebih tua dari 2026-01-16 (whitelist: data/cache)
[terhapus] data/cache/lama.txt (15 byte)
log — file *.gz lebih tua dari 2026-02-01 (whitelist: data/log)
[terhapus] data/log/app.log.1.gz (25 byte)
tmp — file lebih tua dari 2026-02-08 (whitelist: data/tmp)
[terhapus] data/tmp/sisa.tmp (20 byte)
HEMAT RUANG
----------------------------------------------------------------
Total: 3 file, 60 byte (benar-benar dibebaskan dari disk)
REKOMENDASI
----------------------------------------------------------------
1. Pemakaian disk melewati ambang 80% di /data:
cari penyumbang terbesar dengan du -h, lalu bersihkan atau pindahkan.
2. Ada 4 paket update tertunda:
jalankan apt upgrade pada jadwal maintenance berikutnya.
3. Ada 2 service gagal (nginx.service, postgresql.service):
periksa journalctl -u <unit> untuk penyebabnya, lalu restart bila aman.
== folder setelah apply ==
data/cache:
baru.txt
data/log:
app.log
app.log.2.gz
data/tmp:
aktif.tmp
== dry-run berikutnya: kandidat sudah bersih ==
PEMBERSIHAN
----------------------------------------------------------------
cache — file lebih tua dari 2026-01-16 (whitelist: data/cache)
(tidak ada kandidat)
log — file *.gz lebih tua dari 2026-02-01 (whitelist: data/log)
(tidak ada kandidat)
tmp — file lebih tua dari 2026-02-08 (whitelist: data/tmp)
(tidak ada kandidat)
HEMAT RUANG
----------------------------------------------------------------
Total: 0 file, 0 byte (potensi hemat; jalankan --apply untuk benar-benar menghapus)
REKOMENDASI
----------------------------------------------------------------
1. Pemakaian disk melewati ambang 80% di /data:
cari penyumbang terbesar dengan du -h, lalu bersihkan atau pindahkan.
2. Ada 4 paket update tertunda:
jalankan apt upgrade pada jadwal maintenance berikutnya.
3. Ada 2 service gagal (nginx.service, postgresql.service):
periksa journalctl -u <unit> untuk penyebabnya, lalu restart bila aman.Sesi itu menampilkan seluruh siklus pemeliharaan dalam satu blok. Mari kita bedah bagian-bagian yang khusus menyusun laporan:
tambah(): satu-satunya cara menulis laporan — menambah satu baris ke variabelLAPORANbeserta baris barunya ($'\n'). Laporan dirakit dari potongan-potongan kecil, bukan satuechoraksasa, sehingga seksi bisa disisipkan kondisi apa pun.BAG_BERSIH: variabel ini terisi selama fungsibersihkan()bekerja — tiap kandidat menambah satu baris[dry-run]atau[terhapus]beserta ukurannya daristat -c %s, dan whitelist tanpa kandidat menulis(tidak ada kandidat)agar laporan tidak pernah menampilkan seksi kosong tanpa penjelasan.LABEL_MODEdanLABEL_HEMAT: dua kalimat yang berbeda mengikuti mode — di dry-run seksi hemat menulis “potensi hemat”, setelah--applymenjadi “benar-benar dibebaskan dari disk”. Satu sumber data, dua cara membaca.- Rantai rekomendasi: setiap temuan menambah satu butir bernomor —
nomordiinkremen setiap kali — dan butir “jalankan ulang dengan –apply” hanya muncul di dry-run yang punya kandidat. Saat tidak ada temuan sama sekali, laporan tidak kosong melainkan menulis satu kalimat “sistem dalam kondisi baik”. printf '%s' "$LAPORAN" | tee "$REPORT_FILE":teemenyalin stdin ke berkas dan stdout sekaligus — satu konten, dua tujuan, tanpa menulis laporan dua kali; perilakunya terdokumentasi di tee(1) .
Perhatikan dry-run terakhir pada sesi di atas: setelah apply, semua whitelist menulis (tidak ada kandidat), total hemat kembali ke nol, dan rekomendasi menyusut dari empat butir menjadi tiga — persis laporan yang akan dibaca administrator minggu berikutnya jika pembersihan ini dijadwalkan.
Rekomendasi Berbasis Temuan #
Rekomendasi skrip tidak dibuat dari template tetap, melainkan diturunkan dari temuan lewat rantai kondisi — setiap baris tabel berikut adalah satu if di skrip:
| Temuan | Rekomendasi yang ditulis |
|---|---|
| disk melewati ambang | cari penyumbang terbesar dengan du -h, lalu bersihkan atau pindahkan |
| ada paket tertunda | jalankan apt upgrade pada jadwal maintenance berikutnya |
| ada service gagal | periksa journalctl -u <unit> untuk penyebabnya, lalu restart bila aman |
| dry-run menemukan kandidat | periksa daftar, lalu jalankan ulang dengan --apply |
| tidak ada temuan | ulangi pemeliharaan sesuai jadwal |
Setiap rekomendasi menyebut perintah yang tepat untuk temuan yang tepat, bukan saran umum. Ambang disk memakai DISK_WARN_PCT dari konfigurasi; saran paket mengarah ke jadwal maintenance, bukan “segera update”, karena skrip tidak tahu kebijakan jendela layanan mesin Anda — keputusan seperti itu memang ditinggalkan untuk manusia yang membaca laporan.
Test Suite dengan Assertion #
Laporan yang bagus di sesi demo belum menjadi jaminan; jaminan datang dari pengujian yang mengulang janji-janji desain setiap kali skrip berubah. Test suite kita memeriksa: dry-run sukses dan tidak menghapus, laporan dibuat dengan empat seksi, parsing ketiga cek benar, apply hanya menghapus file yang benar sementara file sehat tetap ada, angka laporan akurat, dua run dry-run menghasilkan laporan identik, dan setiap jenis kegagalan keluar dengan kode sesuai kontrak. Blok berikut menyusun tata letak proyek persis seperti paket yang menyertai halaman ini, lalu menjalankan suitenya:
mkdir -p proyek/src proyek/config proyek/tests
cat > proyek/src/pemeliharaan.sh <<'BASH'
#!/usr/bin/env bash
# pemeliharaan.sh — pemeliharaan berkala: periksa, bersihkan, laporkan.
#
# Membaca snapshot data sistem (df.txt, paket.txt, services.txt) dari
# folder data, memeriksa tiga hal — pemakaian disk, paket update
# tertunda, service gagal — lalu membersihkan file cache, log terkompresi
# (*.gz), dan file tmp usang HANYA di dalam folder whitelist yang
# tercantum di berkas konfigurasi. Mode default DRY-RUN: tidak ada
# file yang dihapus sampai --apply diberikan, dan mode tidak bisa
# diubah dari konfigurasi. Hasil akhirnya adalah laporan terstruktur
# yang ditulis ke stdout sekaligus berkas laporan.
#
# Pemakaian:
# bash pemeliharaan.sh [--data-dir DIR] [--config FILE] [--apply]
#
# Variabel lingkungan (berguna untuk pengujian):
# CONF berkas konfigurasi yang dibaca (default: pemeliharaan.conf)
# SOURCE_DIR folder data; kalah oleh --data-dir (default: data)
# HARI_INI tanggal acuan umur file, format YYYY-MM-DD (default: hari ini)
#
# Kode keluar:
# 0 sukses 1 konfigurasi tidak valid
# 2 berkas snapshot tidak ada 3 folder whitelist tidak ada
set -u
CONF="${CONF:-pemeliharaan.conf}"
DATA_DIR="${SOURCE_DIR:-data}"
HARI_INI="${HARI_INI:-$(date +%F)}"
APPLY=0
# --- 1. Baca argumen -----------------------------------------------------
while [ $# -gt 0 ]; do
case "$1" in
--apply) APPLY=1; shift ;;
--data-dir|--config)
if [ $# -lt 2 ]; then
printf 'pemeliharaan.sh: %s butuh nilai\n' "$1" >&2
exit 1
fi
if [ "$1" = "--data-dir" ]; then
DATA_DIR="$2"
else
CONF="$2"
fi
shift 2
;;
*)
printf 'pemeliharaan.sh: argumen tidak dikenal: %s\n' "$1" >&2
exit 1
;;
esac
done
# --- 2. Baca dan validasi konfigurasi ------------------------------------
if [ ! -f "$CONF" ]; then
printf 'pemeliharaan.sh: berkas konfigurasi %s tidak ditemukan\n' "$CONF" >&2
exit 1
fi
# shellcheck source=/dev/null
. "$CONF"
if [ -z "${CACHE_DIRS:-}" ] && [ -z "${LOG_DIRS:-}" ] && [ -z "${TMP_DIRS:-}" ]; then
printf 'pemeliharaan.sh: whitelist kosong (CACHE_DIRS/LOG_DIRS/TMP_DIRS) di %s\n' "$CONF" >&2
exit 1
fi
valid_umur() {
case $1 in
''|*[!0-9]*|0) return 1 ;;
esac
return 0
}
valid_umur "${CACHE_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: CACHE_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
valid_umur "${LOG_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: LOG_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
valid_umur "${TMP_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: TMP_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
case ${DISK_WARN_PCT:-} in
''|*[!0-9]*|0)
printf 'pemeliharaan.sh: DISK_WARN_PCT harus angka 1-99\n' >&2
exit 1
;;
esac
if [ "$DISK_WARN_PCT" -gt 99 ]; then
printf 'pemeliharaan.sh: DISK_WARN_PCT harus angka 1-99\n' >&2
exit 1
fi
# Mode sengaja di set SETELAH konfigurasi dibaca: walau berkas
# konfigurasi berisi "MODE=apply", nilainya tetap ditimpa di sini.
# Hanya argumen --apply yang boleh menyalakan pembersihan sungguhan.
MODE="dry-run"
if [ "$APPLY" -eq 1 ]; then
MODE="apply"
fi
# --- 3. Validasi snapshot data --------------------------------------------
for f in df.txt paket.txt services.txt; do
if [ ! -f "$DATA_DIR/$f" ]; then
printf 'pemeliharaan.sh: snapshot %s/%s tidak ditemukan\n' "$DATA_DIR" "$f" >&2
exit 2
fi
done
# --- 4. Pemeriksaan -------------------------------------------------------
# Temuan pemeriksaan terkumpul di sini, lalu menjadi bagian laporan.
# Disk: kolom ke-5 df adalah Use%; "+0" di awk membuang karakter '%'
# sehingga "89%" terbaca sebagai angka 89.
DISK_BARIS=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 {
pct = $5 + 0
if (pct >= ambang)
printf " %-10s %3d%% WASPADA\n", $6, pct
else
printf " %-10s %3d%% OK\n", $6, pct
}' "$DATA_DIR/df.txt")
DISK_MELEBIHI=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 && ($5 + 0) >= ambang {
printf "%s%s", pisah, $6
pisah = ", "
}' "$DATA_DIR/df.txt")
DISK_N=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 && ($5 + 0) >= ambang { n++ } END { print n + 0 }' "$DATA_DIR/df.txt")
# Paket tertunda: output simulasi apt-get -s upgrade menandai tiap paket
# dengan satu baris "Inst <nama> [versi lama] (versi baru)".
PAKET_DAFTAR=$(awk '/^Inst / {
printf "%s%s", pisah, $2
pisah = ", "
}' "$DATA_DIR/paket.txt")
PAKET_N=$(awk '/^Inst / { n++ } END { print n + 0 }' "$DATA_DIR/paket.txt")
# Service gagal: snapshot berformat "unit substate" per baris.
SVC_DAFTAR=$(awk '$2 == "failed" {
printf "%s%s", pisah, $1
pisah = ", "
}' "$DATA_DIR/services.txt")
SVC_N=$(awk '$2 == "failed" { n++ } END { print n + 0 }' "$DATA_DIR/services.txt")
# --- 5. Pembersihan (hanya di dalam whitelist) -----------------------------
# Hasil pembersihan terkumpul di BAG_BERSIH; penghitung JUMLAH_FILE dan
# HEMAT_BYTES dipakai seksi HEMAT RUANG di laporan.
JUMLAH_FILE=0
HEMAT_BYTES=0
BAG_BERSIH=""
# bersihkan <judul> <dirs> <umur-hari> <pola-nama>:
# kumpulkan file di folder whitelist yang lebih tua dari batas umur,
# catat ukurannya untuk laporan, dan hapus hanya bila mode apply.
# -type f menjaga folder tetap utuh; find hanya berangkat dari folder
# whitelist, jadi file di luar daftar tidak pernah tersentuh.
bersihkan() {
judul="$1"
dirs="$2"
umur="$3"
pola="${4:-*}"
batas=$(date -d "$HARI_INI -$umur days" +%F)
if [ "$pola" = "*" ]; then
label="file"
else
label="file $pola"
fi
terpakai=0
if [ -n "$BAG_BERSIH" ]; then
BAG_BERSIH+=$'\n'
fi
BAG_BERSIH+="$judul — $label lebih tua dari $batas (whitelist: $dirs)"$'\n'
# shellcheck disable=SC2086 # whitelist memang dipisah spasi
for dir in $dirs; do
if [ ! -d "$dir" ]; then
printf 'pemeliharaan.sh: folder whitelist %s tidak ditemukan\n' "$dir" >&2
exit 3
fi
# Process substitution < <(...) menjaga penghitung tetap hidup
# di shell utama; pipeline biasa akan menjalankan while di
# subshell sehingga JUMLAH_FILE hilang.
while IFS= read -r f; do
[ -n "$f" ] || continue
ukuran=$(stat -c %s "$f")
JUMLAH_FILE=$((JUMLAH_FILE + 1))
HEMAT_BYTES=$((HEMAT_BYTES + ukuran))
terpakai=$((terpakai + 1))
if [ "$MODE" = "apply" ]; then
rm -f -- "$f"
BAG_BERSIH+=" [terhapus] $f (${ukuran} byte)"$'\n'
else
BAG_BERSIH+=" [dry-run] $f (${ukuran} byte)"$'\n'
fi
done < <(find "$dir" -type f -name "$pola" ! -newermt "$batas" | sort)
done
if [ "$terpakai" -eq 0 ]; then
BAG_BERSIH+=" (tidak ada kandidat)"$'\n'
fi
}
if [ -n "${CACHE_DIRS:-}" ]; then
bersihkan "cache" "$CACHE_DIRS" "$CACHE_MAX_DAYS"
fi
if [ -n "${LOG_DIRS:-}" ]; then
bersihkan "log" "$LOG_DIRS" "$LOG_MAX_DAYS" "*.gz"
fi
if [ -n "${TMP_DIRS:-}" ]; then
bersihkan "tmp" "$TMP_DIRS" "$TMP_MAX_DAYS"
fi
# --- 6. Susun laporan -----------------------------------------------------
if [ "$MODE" = "apply" ]; then
LABEL_MODE="APPLY (pembersihan benar-benar dijalankan)"
LABEL_HEMAT="benar-benar dibebaskan dari disk"
else
LABEL_MODE="DRY-RUN (tidak ada file yang dihapus)"
LABEL_HEMAT="potensi hemat; jalankan --apply untuk benar-benar menghapus"
fi
LAPORAN=""
tambah() {
LAPORAN+="$1"
LAPORAN+=$'\n'
}
tambah "================================================================"
tambah "LAPORAN PEMELIHARAAN SISTEM"
tambah "Tanggal acuan : $HARI_INI"
tambah "Mode : $LABEL_MODE"
tambah "================================================================"
tambah ""
tambah "PEMERIKSAAN"
tambah "----------------------------------------------------------------"
tambah "1. Pemakaian disk (ambang $DISK_WARN_PCT%)"
tambah "$DISK_BARIS"
tambah "2. Paket update tertunda"
if [ "$PAKET_N" -gt 0 ]; then
tambah " $PAKET_N paket siap di-upgrade: $PAKET_DAFTAR"
else
tambah " tidak ada paket tertunda"
fi
tambah "3. Service gagal"
if [ "$SVC_N" -gt 0 ]; then
tambah " $SVC_N service failed: $SVC_DAFTAR"
else
tambah " semua service berjalan normal"
fi
tambah ""
tambah "PEMBERSIHAN"
tambah "----------------------------------------------------------------"
tambah "${BAG_BERSIH%$'\n'}"
tambah ""
tambah "HEMAT RUANG"
tambah "----------------------------------------------------------------"
tambah "Total: $JUMLAH_FILE file, $HEMAT_BYTES byte ($LABEL_HEMAT)"
tambah ""
tambah "REKOMENDASI"
tambah "----------------------------------------------------------------"
nomor=1
if [ "$DISK_N" -gt 0 ]; then
tambah "$nomor. Pemakaian disk melewati ambang $DISK_WARN_PCT% di $DISK_MELEBIHI:"
tambah " cari penyumbang terbesar dengan du -h, lalu bersihkan atau pindahkan."
nomor=$((nomor + 1))
fi
if [ "$PAKET_N" -gt 0 ]; then
tambah "$nomor. Ada $PAKET_N paket update tertunda:"
tambah " jalankan apt upgrade pada jadwal maintenance berikutnya."
nomor=$((nomor + 1))
fi
if [ "$SVC_N" -gt 0 ]; then
tambah "$nomor. Ada $SVC_N service gagal ($SVC_DAFTAR):"
tambah " periksa journalctl -u <unit> untuk penyebabnya, lalu restart bila aman."
nomor=$((nomor + 1))
fi
if [ "$MODE" = "dry-run" ] && [ "$JUMLAH_FILE" -gt 0 ]; then
tambah "$nomor. DRY-RUN menemukan $JUMLAH_FILE file ($HEMAT_BYTES byte) layak dibersihkan:"
tambah " periksa daftar di bagian PEMBERSIHAN, lalu jalankan ulang dengan --apply."
nomor=$((nomor + 1))
fi
if [ "$nomor" -eq 1 ]; then
tambah "1. Tidak ada temuan: sistem dalam kondisi baik. Ulangi pemeliharaan sesuai jadwal."
fi
# --- 7. Tulis laporan ke stdout dan berkas ---------------------------------
REPORT_FILE="${REPORT_FILE:-laporan-pemeliharaan.txt}"
mkdir -p "$(dirname "$REPORT_FILE")"
printf '%s' "$LAPORAN" | tee "$REPORT_FILE"
exit 0
BASH
cat > proyek/config/pemeliharaan.conf <<'CONF'
# pemeliharaan.conf — konfigurasi skrip pemeliharaan.
# Sunting nilai di bawah ini sesuai komputer Anda; skrip pemeliharaan.sh
# tidak perlu disentuh. Jalur whitelist dihitung dari direktori kerja
# saat skrip dijalankan; jalur absolut (mis. /var/cache/myapp) juga
# boleh dipakai.
# Whitelist folder yang BOLEH dibersihkan, dipisah spasi.
# Folder di luar tiga daftar ini tidak pernah disentuh skrip.
CACHE_DIRS="data/cache"
LOG_DIRS="data/log"
TMP_DIRS="data/tmp"
# Umur maksimum file (hari) sebelum layak dibersihkan.
CACHE_MAX_DAYS=30
LOG_MAX_DAYS=14
TMP_MAX_DAYS=7
# Ambang pemakaian disk (%) yang menyalakan status WASPADA (1-99).
DISK_WARN_PCT=80
# Berkas laporan (opsional; default: laporan-pemeliharaan.txt).
REPORT_FILE="laporan-pemeliharaan.txt"
CONF
cat > proyek/tests/run-tests.sh <<'TESTS'
#!/usr/bin/env bash
# run-tests.sh — test suite untuk pemeliharaan.sh.
# Suite menyiapkan sendiri folder kerja terisolasi (mktemp) beserta
# data contohnya — snapshot beku (df.txt, paket.txt, services.txt) dan
# file cache/log/tmp dengan mtime beku lewat touch -d — lalu menjalankan
# rangkaian pengujian dengan assertion sederhana. Jalankan dari mana
# saja: bash tests/run-tests.sh
set -u
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
PROJ_DIR=$(dirname "$SCRIPT_DIR")
PASS=0
FAIL=0
# assert_eq <harapan> <nyata> <pesan>: bandingkan dua nilai.
assert_eq() {
if [ "$1" = "$2" ]; then
PASS=$((PASS + 1))
printf 'OK %s\n' "$3"
else
FAIL=$((FAIL + 1))
printf 'GAGAL %s: harapan %s, nyata %s\n' "$3" "$1" "$2"
fi
}
# --- siapkan folder kerja terisolasi ------------------------------------
WORK=$(mktemp -d)
cp "$PROJ_DIR/src/pemeliharaan.sh" "$PROJ_DIR/config/pemeliharaan.conf" "$WORK/"
cd "$WORK" || exit 1
# --- fixture: snapshot data beku ----------------------------------------
mkdir -p data/cache data/log data/tmp
cat > data/df.txt <<'EOF'
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 35G 13G 73% /
/dev/sdb1 200G 170G 21G 89% /data
tmpfs 7.8G 1.1M 7.8G 1% /dev/shm
EOF
cat > data/paket.txt <<'EOF'
Reading package lists... Done
Building dependency tree... Done
Calculating upgrade... Done
Reading state information... Done
The following packages will be upgraded:
curl libcurl4 libssl3 openssl
4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Inst curl [7.88.1-10] (7.88.1-11)
Inst libcurl4 [7.88.1-10] (7.88.1-11)
Inst libssl3 [3.0.8-1] (3.0.8-2)
Inst openssl [3.0.8-1] (3.0.8-2)
Conf curl (7.88.1-11)
Conf libcurl4 (7.88.1-11)
Conf libssl3 (3.0.8-2)
Conf openssl (3.0.8-2)
EOF
cat > data/services.txt <<'EOF'
nginx.service failed
postgresql.service failed
sshd.service running
cron.service running
EOF
# --- fixture: file dengan mtime beku -------------------------------------
# Tanggal acuan pengujian: 2026-02-15. Batas umur menjadi
# cache 30 hari -> 2026-01-16, log 14 hari -> 2026-02-01,
# tmp 7 hari -> 2026-02-08.
printf 'isi cache lama\n' > data/cache/lama.txt
touch -d "2026-01-10" data/cache/lama.txt
printf 'isi cache baru\n' > data/cache/baru.txt
touch -d "2026-02-10" data/cache/baru.txt
printf 'isi log lama terkompresi\n' > data/log/app.log.1.gz
touch -d "2026-01-25" data/log/app.log.1.gz
printf 'isi log lama tapi bukan gz\n' > data/log/app.log
touch -d "2026-01-20" data/log/app.log
printf 'isi log baru\n' > data/log/app.log.2.gz
touch -d "2026-02-12" data/log/app.log.2.gz
printf 'sisa instalasi lama\n' > data/tmp/sisa.tmp
touch -d "2026-02-01" data/tmp/sisa.tmp
printf 'file tmp aktif\n' > data/tmp/aktif.tmp
touch -d "2026-02-13" data/tmp/aktif.tmp
# Ukuran total yang harus dibebaskan mode apply: tiga file tua.
total_hemat=$(stat -c %s data/cache/lama.txt data/log/app.log.1.gz \
data/tmp/sisa.tmp | awk '{ s += $1 } END { print s }')
# --- uji 1: dry-run sukses dan tidak menghapus ---------------------------
HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 0 "$?" "dry-run sukses keluar 0"
[ -f data/cache/lama.txt ]
assert_eq 0 "$?" "dry-run tidak menghapus cache lama"
[ -f data/log/app.log.1.gz ]
assert_eq 0 "$?" "dry-run tidak menghapus log lama"
# --- uji 2: laporan dibuat dengan empat seksi ----------------------------
[ -f laporan-pemeliharaan.txt ]
assert_eq 0 "$?" "berkas laporan dibuat"
grep -q '^PEMERIKSAAN$' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan memuat seksi PEMERIKSAAN"
grep -q '^PEMBERSIHAN$' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan memuat seksi PEMBERSIHAN"
grep -q '^HEMAT RUANG$' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan memuat seksi HEMAT RUANG"
grep -q '^REKOMENDASI$' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan memuat seksi REKOMENDASI"
# --- uji 3: parsing ketiga pemeriksaan benar ------------------------------
grep -q '89% WASPADA' laporan-pemeliharaan.txt
assert_eq 0 "$?" "disk 89% ditandai WASPADA"
grep -q '73% OK' laporan-pemeliharaan.txt
assert_eq 0 "$?" "disk 73% ditandai OK"
grep -q '4 paket siap di-upgrade: curl, libcurl4, libssl3, openssl' \
laporan-pemeliharaan.txt
assert_eq 0 "$?" "jumlah dan daftar paket tertunda benar"
grep -q '2 service failed: nginx.service, postgresql.service' \
laporan-pemeliharaan.txt
assert_eq 0 "$?" "jumlah dan daftar service gagal benar"
# --- uji 4: daftar kandidat dan rekomendasi dry-run ------------------------
grep -Fq '[dry-run] data/cache/lama.txt' laporan-pemeliharaan.txt
assert_eq 0 "$?" "dry-run menampilkan kandidat cache"
grep -Fq '[dry-run] data/log/app.log.1.gz' laporan-pemeliharaan.txt
assert_eq 0 "$?" "dry-run menampilkan kandidat log"
grep -Fq '[dry-run] data/tmp/sisa.tmp' laporan-pemeliharaan.txt
assert_eq 0 "$?" "dry-run menampilkan kandidat tmp"
grep -q 'jalankan ulang dengan --apply' laporan-pemeliharaan.txt
assert_eq 0 "$?" "rekomendasi menyarankan --apply"
# --- uji 5: dua run dry-run menghasilkan laporan identik -------------------
cp laporan-pemeliharaan.txt laporan-run1.txt
HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 0 "$?" "dry-run kedua sukses keluar 0"
diff laporan-run1.txt laporan-pemeliharaan.txt > /dev/null 2>&1
assert_eq 0 "$?" "dua laporan dry-run identik"
# --- uji 6: apply menghapus yang benar saja -------------------------------
HARI_INI=2026-02-15 bash pemeliharaan.sh --apply > /dev/null 2>&1
assert_eq 0 "$?" "apply sukses keluar 0"
[ ! -f data/cache/lama.txt ]
assert_eq 0 "$?" "apply menghapus cache lama"
[ ! -f data/log/app.log.1.gz ]
assert_eq 0 "$?" "apply menghapus log gz lama"
[ ! -f data/tmp/sisa.tmp ]
assert_eq 0 "$?" "apply menghapus tmp lama"
[ -f data/cache/baru.txt ]
assert_eq 0 "$?" "cache baru tetap ada"
[ -f data/log/app.log ]
assert_eq 0 "$?" "log lama non-gz tetap ada (pola hanya *.gz)"
[ -f data/log/app.log.2.gz ]
assert_eq 0 "$?" "log gz baru tetap ada"
[ -f data/tmp/aktif.tmp ]
assert_eq 0 "$?" "tmp baru tetap ada"
# --- uji 7: laporan apply akurat -------------------------------------------
terhapus=$(grep -Fc '[terhapus]' laporan-pemeliharaan.txt)
assert_eq 3 "$terhapus" "laporan apply mencatat 3 file terhapus"
grep -q "Total: 3 file, $total_hemat byte (benar-benar dibebaskan dari disk)" \
laporan-pemeliharaan.txt
assert_eq 0 "$?" "total hemat ruang cocok dengan jumlah byte fixture"
! grep -q 'jalankan ulang dengan --apply' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan apply tidak menyarankan --apply lagi"
# --- uji 8: exit code untuk tiap jenis kegagalan ----------------------------
CONF=nihil.conf HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 1 "$?" "konfigurasi hilang keluar 1"
printf 'CACHE_DIRS="data/cache"\nLOG_DIRS=""\nTMP_DIRS=""\nCACHE_MAX_DAYS=abc\nLOG_MAX_DAYS=14\nTMP_MAX_DAYS=7\nDISK_WARN_PCT=80\n' > umur-rusak.conf
CONF=umur-rusak.conf HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 1 "$?" "umur tidak angka keluar 1"
mkdir -p data-kosong
SOURCE_DIR=data-kosong HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 2 "$?" "snapshot hilang keluar 2"
printf 'CACHE_DIRS="data/cache"\nLOG_DIRS="data/log"\nTMP_DIRS="data/tidak-ada"\nCACHE_MAX_DAYS=30\nLOG_MAX_DAYS=14\nTMP_MAX_DAYS=7\nDISK_WARN_PCT=80\n' > whitelist-rusak.conf
CONF=whitelist-rusak.conf HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 3 "$?" "folder whitelist hilang keluar 3"
HARI_INI=2026-02-15 bash pemeliharaan.sh --bogus > /dev/null 2>&1
assert_eq 1 "$?" "argumen tak dikenal keluar 1"
# --- ringkasan --------------------------------------------------------------
if [ "$FAIL" -eq 0 ]; then
printf 'PASSED: %d/%d test lulus\n' "$PASS" "$PASS"
else
printf 'FAILED: %d/%d test lulus\n' "$PASS" "$((PASS + FAIL))"
fi
cd / && rm -rf "$WORK"
[ "$FAIL" -eq 0 ]
TESTS
bash proyek/tests/run-tests.shOutput yang Dihasilkan:
OK dry-run sukses keluar 0
OK dry-run tidak menghapus cache lama
OK dry-run tidak menghapus log lama
OK berkas laporan dibuat
OK laporan memuat seksi PEMERIKSAAN
OK laporan memuat seksi PEMBERSIHAN
OK laporan memuat seksi HEMAT RUANG
OK laporan memuat seksi REKOMENDASI
OK disk 89% ditandai WASPADA
OK disk 73% ditandai OK
OK jumlah dan daftar paket tertunda benar
OK jumlah dan daftar service gagal benar
OK dry-run menampilkan kandidat cache
OK dry-run menampilkan kandidat log
OK dry-run menampilkan kandidat tmp
OK rekomendasi menyarankan --apply
OK dry-run kedua sukses keluar 0
OK dua laporan dry-run identik
OK apply sukses keluar 0
OK apply menghapus cache lama
OK apply menghapus log gz lama
OK apply menghapus tmp lama
OK cache baru tetap ada
OK log lama non-gz tetap ada (pola hanya *.gz)
OK log gz baru tetap ada
OK tmp baru tetap ada
OK laporan apply mencatat 3 file terhapus
OK total hemat ruang cocok dengan jumlah byte fixture
OK laporan apply tidak menyarankan --apply lagi
OK konfigurasi hilang keluar 1
OK umur tidak angka keluar 1
OK snapshot hilang keluar 2
OK folder whitelist hilang keluar 3
OK argumen tak dikenal keluar 1
PASSED: 34/34 test lulusPola-pola penting di dalam suite:
assert_eq <harapan> <nyata> <pesan>: pengujian sebagai perbandingan nilai;$?ditangkap tepat setelah perintah yang diperiksa, dan pesan kegagalan menyebut kedua nilai sehingga penyebabnya terbaca tanpa menjalankan ulang.- Fixture beku: snapshot ditulis dengan heredoc, umur file dibekukan dengan
touch -d, danHARI_INI=2026-02-15dikunci di setiap pemanggilan — tiga lapis yang membuat suite memberi hasil identik di mesin mana pun, kapan pun. - Uji dry-run yang tegas: setelah eksekusi dry-run, suite memeriksa file lama masih ada — bukan hanya memeriksa exit code. Mode default diuji lewat akibatnya, bukan lewat klaimnya.
- Uji apply yang selektif: tiga file tua dinyatakan hilang, empat file sehat dinyatakan masih ada — termasuk
app.logyang sama tua denganapp.log.1.gztapi bukan*.gz. Penghapusan yang “terlalu rajin” akan tertangkap di sini. - Akurasi angka: total hemat diharapkan sama dengan jumlah byte ketiga file yang dihitung ulang lewat
stat— laporan tidak boleh mengarang angka, dan pengujian memastikannya. - Determinisme: dua run dry-run dibandingkan dengan
diff— kalau laporan memuat sesuatu yang berubah antar eksekusi (jam, PID, urutan acak), suite gagal. - Kontrak exit code: 0 sukses, 1 konfigurasi tidak valid atau argumen salah, 2 snapshot hilang, 3 folder whitelist hilang — masing-masing dipicu dengan sengaja dan diperiksa kodenya; konvensi kode keluar sendiri sudah dibahas di Chapter Exit Status dan Error Handling .
Dari Skrip ke Jadwal #
Skrip yang teruji tinggal dijadwalkan. Satu baris cron seperti 30 7 * * 1 cd /home/budi/pemeliharaan && bash pemeliharaan.sh menjalankan pemeliharaan tiap Senin pagi dan meninggalkan laporan di berkas yang bisa dibaca kapan saja — pola penjadwalannya dibahas tuntas di Chapter Scheduling Scripts
. Dua kebiasaan penutup yang layak dipertahankan: biarkan cron menjalankan dry-run dan jalankan --apply sendiri setelah membaca laporan, setidaknya sampai whitelist Anda terbukti benar dalam beberapa minggu; dan sisihkan waktu membaca rekomendasi — skrip menemukan gejalanya, manusia yang memutuskan prioritasnya. Panduan ringkas pemasangan, pembacaan laporan, dan tabel kode keluar tersedia di docs/README.txt pada paket berkas halaman ini.
Proyek pemeliharaan selesai: data dibekukan lalu diperiksa, pembersihan dibatasi whitelist-umur-dry-run, laporan menyusun temuan menjadi keputusan, dan test suite menjaga semuanya. Pola rancangannya — kumpulkan, periksa, bertindak dengan aman, laporkan — akan terpakai lagi dalam skala yang lebih besar pada proyek berikutnya, Server Web Linux , tempat pemeliharaan bukan lagi skrip mingguan melainkan bagian dari merawat layanan yang berjalan terus.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
src/pemeliharaan.sh
Unduh berkas#!/usr/bin/env bash
# pemeliharaan.sh — pemeliharaan berkala: periksa, bersihkan, laporkan.
#
# Membaca snapshot data sistem (df.txt, paket.txt, services.txt) dari
# folder data, memeriksa tiga hal — pemakaian disk, paket update
# tertunda, service gagal — lalu membersihkan file cache, log terkompresi
# (*.gz), dan file tmp usang HANYA di dalam folder whitelist yang
# tercantum di berkas konfigurasi. Mode default DRY-RUN: tidak ada
# file yang dihapus sampai --apply diberikan, dan mode tidak bisa
# diubah dari konfigurasi. Hasil akhirnya adalah laporan terstruktur
# yang ditulis ke stdout sekaligus berkas laporan.
#
# Pemakaian:
# bash pemeliharaan.sh [--data-dir DIR] [--config FILE] [--apply]
#
# Variabel lingkungan (berguna untuk pengujian):
# CONF berkas konfigurasi yang dibaca (default: pemeliharaan.conf)
# SOURCE_DIR folder data; kalah oleh --data-dir (default: data)
# HARI_INI tanggal acuan umur file, format YYYY-MM-DD (default: hari ini)
#
# Kode keluar:
# 0 sukses 1 konfigurasi tidak valid
# 2 berkas snapshot tidak ada 3 folder whitelist tidak ada
set -u
CONF="${CONF:-pemeliharaan.conf}"
DATA_DIR="${SOURCE_DIR:-data}"
HARI_INI="${HARI_INI:-$(date +%F)}"
APPLY=0
# --- 1. Baca argumen -----------------------------------------------------
while [ $# -gt 0 ]; do
case "$1" in
--apply) APPLY=1; shift ;;
--data-dir|--config)
if [ $# -lt 2 ]; then
printf 'pemeliharaan.sh: %s butuh nilai\n' "$1" >&2
exit 1
fi
if [ "$1" = "--data-dir" ]; then
DATA_DIR="$2"
else
CONF="$2"
fi
shift 2
;;
*)
printf 'pemeliharaan.sh: argumen tidak dikenal: %s\n' "$1" >&2
exit 1
;;
esac
done
# --- 2. Baca dan validasi konfigurasi ------------------------------------
if [ ! -f "$CONF" ]; then
printf 'pemeliharaan.sh: berkas konfigurasi %s tidak ditemukan\n' "$CONF" >&2
exit 1
fi
# shellcheck source=/dev/null
. "$CONF"
if [ -z "${CACHE_DIRS:-}" ] && [ -z "${LOG_DIRS:-}" ] && [ -z "${TMP_DIRS:-}" ]; then
printf 'pemeliharaan.sh: whitelist kosong (CACHE_DIRS/LOG_DIRS/TMP_DIRS) di %s\n' "$CONF" >&2
exit 1
fi
valid_umur() {
case $1 in
''|*[!0-9]*|0) return 1 ;;
esac
return 0
}
valid_umur "${CACHE_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: CACHE_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
valid_umur "${LOG_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: LOG_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
valid_umur "${TMP_MAX_DAYS:-}" || {
printf 'pemeliharaan.sh: TMP_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
case ${DISK_WARN_PCT:-} in
''|*[!0-9]*|0)
printf 'pemeliharaan.sh: DISK_WARN_PCT harus angka 1-99\n' >&2
exit 1
;;
esac
if [ "$DISK_WARN_PCT" -gt 99 ]; then
printf 'pemeliharaan.sh: DISK_WARN_PCT harus angka 1-99\n' >&2
exit 1
fi
# Mode sengaja di set SETELAH konfigurasi dibaca: walau berkas
# konfigurasi berisi "MODE=apply", nilainya tetap ditimpa di sini.
# Hanya argumen --apply yang boleh menyalakan pembersihan sungguhan.
MODE="dry-run"
if [ "$APPLY" -eq 1 ]; then
MODE="apply"
fi
# --- 3. Validasi snapshot data --------------------------------------------
for f in df.txt paket.txt services.txt; do
if [ ! -f "$DATA_DIR/$f" ]; then
printf 'pemeliharaan.sh: snapshot %s/%s tidak ditemukan\n' "$DATA_DIR" "$f" >&2
exit 2
fi
done
# --- 4. Pemeriksaan -------------------------------------------------------
# Temuan pemeriksaan terkumpul di sini, lalu menjadi bagian laporan.
# Disk: kolom ke-5 df adalah Use%; "+0" di awk membuang karakter '%'
# sehingga "89%" terbaca sebagai angka 89.
DISK_BARIS=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 {
pct = $5 + 0
if (pct >= ambang)
printf " %-10s %3d%% WASPADA\n", $6, pct
else
printf " %-10s %3d%% OK\n", $6, pct
}' "$DATA_DIR/df.txt")
DISK_MELEBIHI=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 && ($5 + 0) >= ambang {
printf "%s%s", pisah, $6
pisah = ", "
}' "$DATA_DIR/df.txt")
DISK_N=$(awk -v ambang="$DISK_WARN_PCT" '
NR > 1 && ($5 + 0) >= ambang { n++ } END { print n + 0 }' "$DATA_DIR/df.txt")
# Paket tertunda: output simulasi apt-get -s upgrade menandai tiap paket
# dengan satu baris "Inst <nama> [versi lama] (versi baru)".
PAKET_DAFTAR=$(awk '/^Inst / {
printf "%s%s", pisah, $2
pisah = ", "
}' "$DATA_DIR/paket.txt")
PAKET_N=$(awk '/^Inst / { n++ } END { print n + 0 }' "$DATA_DIR/paket.txt")
# Service gagal: snapshot berformat "unit substate" per baris.
SVC_DAFTAR=$(awk '$2 == "failed" {
printf "%s%s", pisah, $1
pisah = ", "
}' "$DATA_DIR/services.txt")
SVC_N=$(awk '$2 == "failed" { n++ } END { print n + 0 }' "$DATA_DIR/services.txt")
# --- 5. Pembersihan (hanya di dalam whitelist) -----------------------------
# Hasil pembersihan terkumpul di BAG_BERSIH; penghitung JUMLAH_FILE dan
# HEMAT_BYTES dipakai seksi HEMAT RUANG di laporan.
JUMLAH_FILE=0
HEMAT_BYTES=0
BAG_BERSIH=""
# bersihkan <judul> <dirs> <umur-hari> <pola-nama>:
# kumpulkan file di folder whitelist yang lebih tua dari batas umur,
# catat ukurannya untuk laporan, dan hapus hanya bila mode apply.
# -type f menjaga folder tetap utuh; find hanya berangkat dari folder
# whitelist, jadi file di luar daftar tidak pernah tersentuh.
bersihkan() {
judul="$1"
dirs="$2"
umur="$3"
pola="${4:-*}"
batas=$(date -d "$HARI_INI -$umur days" +%F)
if [ "$pola" = "*" ]; then
label="file"
else
label="file $pola"
fi
terpakai=0
if [ -n "$BAG_BERSIH" ]; then
BAG_BERSIH+=$'\n'
fi
BAG_BERSIH+="$judul — $label lebih tua dari $batas (whitelist: $dirs)"$'\n'
# shellcheck disable=SC2086 # whitelist memang dipisah spasi
for dir in $dirs; do
if [ ! -d "$dir" ]; then
printf 'pemeliharaan.sh: folder whitelist %s tidak ditemukan\n' "$dir" >&2
exit 3
fi
# Process substitution < <(...) menjaga penghitung tetap hidup
# di shell utama; pipeline biasa akan menjalankan while di
# subshell sehingga JUMLAH_FILE hilang.
while IFS= read -r f; do
[ -n "$f" ] || continue
ukuran=$(stat -c %s "$f")
JUMLAH_FILE=$((JUMLAH_FILE + 1))
HEMAT_BYTES=$((HEMAT_BYTES + ukuran))
terpakai=$((terpakai + 1))
if [ "$MODE" = "apply" ]; then
rm -f -- "$f"
BAG_BERSIH+=" [terhapus] $f (${ukuran} byte)"$'\n'
else
BAG_BERSIH+=" [dry-run] $f (${ukuran} byte)"$'\n'
fi
done < <(find "$dir" -type f -name "$pola" ! -newermt "$batas" | sort)
done
if [ "$terpakai" -eq 0 ]; then
BAG_BERSIH+=" (tidak ada kandidat)"$'\n'
fi
}
if [ -n "${CACHE_DIRS:-}" ]; then
bersihkan "cache" "$CACHE_DIRS" "$CACHE_MAX_DAYS"
fi
if [ -n "${LOG_DIRS:-}" ]; then
bersihkan "log" "$LOG_DIRS" "$LOG_MAX_DAYS" "*.gz"
fi
if [ -n "${TMP_DIRS:-}" ]; then
bersihkan "tmp" "$TMP_DIRS" "$TMP_MAX_DAYS"
fi
# --- 6. Susun laporan -----------------------------------------------------
if [ "$MODE" = "apply" ]; then
LABEL_MODE="APPLY (pembersihan benar-benar dijalankan)"
LABEL_HEMAT="benar-benar dibebaskan dari disk"
else
LABEL_MODE="DRY-RUN (tidak ada file yang dihapus)"
LABEL_HEMAT="potensi hemat; jalankan --apply untuk benar-benar menghapus"
fi
LAPORAN=""
tambah() {
LAPORAN+="$1"
LAPORAN+=$'\n'
}
tambah "================================================================"
tambah "LAPORAN PEMELIHARAAN SISTEM"
tambah "Tanggal acuan : $HARI_INI"
tambah "Mode : $LABEL_MODE"
tambah "================================================================"
tambah ""
tambah "PEMERIKSAAN"
tambah "----------------------------------------------------------------"
tambah "1. Pemakaian disk (ambang $DISK_WARN_PCT%)"
tambah "$DISK_BARIS"
tambah "2. Paket update tertunda"
if [ "$PAKET_N" -gt 0 ]; then
tambah " $PAKET_N paket siap di-upgrade: $PAKET_DAFTAR"
else
tambah " tidak ada paket tertunda"
fi
tambah "3. Service gagal"
if [ "$SVC_N" -gt 0 ]; then
tambah " $SVC_N service failed: $SVC_DAFTAR"
else
tambah " semua service berjalan normal"
fi
tambah ""
tambah "PEMBERSIHAN"
tambah "----------------------------------------------------------------"
tambah "${BAG_BERSIH%$'\n'}"
tambah ""
tambah "HEMAT RUANG"
tambah "----------------------------------------------------------------"
tambah "Total: $JUMLAH_FILE file, $HEMAT_BYTES byte ($LABEL_HEMAT)"
tambah ""
tambah "REKOMENDASI"
tambah "----------------------------------------------------------------"
nomor=1
if [ "$DISK_N" -gt 0 ]; then
tambah "$nomor. Pemakaian disk melewati ambang $DISK_WARN_PCT% di $DISK_MELEBIHI:"
tambah " cari penyumbang terbesar dengan du -h, lalu bersihkan atau pindahkan."
nomor=$((nomor + 1))
fi
if [ "$PAKET_N" -gt 0 ]; then
tambah "$nomor. Ada $PAKET_N paket update tertunda:"
tambah " jalankan apt upgrade pada jadwal maintenance berikutnya."
nomor=$((nomor + 1))
fi
if [ "$SVC_N" -gt 0 ]; then
tambah "$nomor. Ada $SVC_N service gagal ($SVC_DAFTAR):"
tambah " periksa journalctl -u <unit> untuk penyebabnya, lalu restart bila aman."
nomor=$((nomor + 1))
fi
if [ "$MODE" = "dry-run" ] && [ "$JUMLAH_FILE" -gt 0 ]; then
tambah "$nomor. DRY-RUN menemukan $JUMLAH_FILE file ($HEMAT_BYTES byte) layak dibersihkan:"
tambah " periksa daftar di bagian PEMBERSIHAN, lalu jalankan ulang dengan --apply."
nomor=$((nomor + 1))
fi
if [ "$nomor" -eq 1 ]; then
tambah "1. Tidak ada temuan: sistem dalam kondisi baik. Ulangi pemeliharaan sesuai jadwal."
fi
# --- 7. Tulis laporan ke stdout dan berkas ---------------------------------
REPORT_FILE="${REPORT_FILE:-laporan-pemeliharaan.txt}"
mkdir -p "$(dirname "$REPORT_FILE")"
printf '%s' "$LAPORAN" | tee "$REPORT_FILE"
exit 0
tests/run-tests.sh
Unduh berkas#!/usr/bin/env bash
# run-tests.sh — test suite untuk pemeliharaan.sh.
# Suite menyiapkan sendiri folder kerja terisolasi (mktemp) beserta
# data contohnya — snapshot beku (df.txt, paket.txt, services.txt) dan
# file cache/log/tmp dengan mtime beku lewat touch -d — lalu menjalankan
# rangkaian pengujian dengan assertion sederhana. Jalankan dari mana
# saja: bash tests/run-tests.sh
set -u
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
PROJ_DIR=$(dirname "$SCRIPT_DIR")
PASS=0
FAIL=0
# assert_eq <harapan> <nyata> <pesan>: bandingkan dua nilai.
assert_eq() {
if [ "$1" = "$2" ]; then
PASS=$((PASS + 1))
printf 'OK %s\n' "$3"
else
FAIL=$((FAIL + 1))
printf 'GAGAL %s: harapan %s, nyata %s\n' "$3" "$1" "$2"
fi
}
# --- siapkan folder kerja terisolasi ------------------------------------
WORK=$(mktemp -d)
cp "$PROJ_DIR/src/pemeliharaan.sh" "$PROJ_DIR/config/pemeliharaan.conf" "$WORK/"
cd "$WORK" || exit 1
# --- fixture: snapshot data beku ----------------------------------------
mkdir -p data/cache data/log data/tmp
cat > data/df.txt <<'EOF'
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 35G 13G 73% /
/dev/sdb1 200G 170G 21G 89% /data
tmpfs 7.8G 1.1M 7.8G 1% /dev/shm
EOF
cat > data/paket.txt <<'EOF'
Reading package lists... Done
Building dependency tree... Done
Calculating upgrade... Done
Reading state information... Done
The following packages will be upgraded:
curl libcurl4 libssl3 openssl
4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Inst curl [7.88.1-10] (7.88.1-11)
Inst libcurl4 [7.88.1-10] (7.88.1-11)
Inst libssl3 [3.0.8-1] (3.0.8-2)
Inst openssl [3.0.8-1] (3.0.8-2)
Conf curl (7.88.1-11)
Conf libcurl4 (7.88.1-11)
Conf libssl3 (3.0.8-2)
Conf openssl (3.0.8-2)
EOF
cat > data/services.txt <<'EOF'
nginx.service failed
postgresql.service failed
sshd.service running
cron.service running
EOF
# --- fixture: file dengan mtime beku -------------------------------------
# Tanggal acuan pengujian: 2026-02-15. Batas umur menjadi
# cache 30 hari -> 2026-01-16, log 14 hari -> 2026-02-01,
# tmp 7 hari -> 2026-02-08.
printf 'isi cache lama\n' > data/cache/lama.txt
touch -d "2026-01-10" data/cache/lama.txt
printf 'isi cache baru\n' > data/cache/baru.txt
touch -d "2026-02-10" data/cache/baru.txt
printf 'isi log lama terkompresi\n' > data/log/app.log.1.gz
touch -d "2026-01-25" data/log/app.log.1.gz
printf 'isi log lama tapi bukan gz\n' > data/log/app.log
touch -d "2026-01-20" data/log/app.log
printf 'isi log baru\n' > data/log/app.log.2.gz
touch -d "2026-02-12" data/log/app.log.2.gz
printf 'sisa instalasi lama\n' > data/tmp/sisa.tmp
touch -d "2026-02-01" data/tmp/sisa.tmp
printf 'file tmp aktif\n' > data/tmp/aktif.tmp
touch -d "2026-02-13" data/tmp/aktif.tmp
# Ukuran total yang harus dibebaskan mode apply: tiga file tua.
total_hemat=$(stat -c %s data/cache/lama.txt data/log/app.log.1.gz \
data/tmp/sisa.tmp | awk '{ s += $1 } END { print s }')
# --- uji 1: dry-run sukses dan tidak menghapus ---------------------------
HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 0 "$?" "dry-run sukses keluar 0"
[ -f data/cache/lama.txt ]
assert_eq 0 "$?" "dry-run tidak menghapus cache lama"
[ -f data/log/app.log.1.gz ]
assert_eq 0 "$?" "dry-run tidak menghapus log lama"
# --- uji 2: laporan dibuat dengan empat seksi ----------------------------
[ -f laporan-pemeliharaan.txt ]
assert_eq 0 "$?" "berkas laporan dibuat"
grep -q '^PEMERIKSAAN$' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan memuat seksi PEMERIKSAAN"
grep -q '^PEMBERSIHAN$' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan memuat seksi PEMBERSIHAN"
grep -q '^HEMAT RUANG$' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan memuat seksi HEMAT RUANG"
grep -q '^REKOMENDASI$' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan memuat seksi REKOMENDASI"
# --- uji 3: parsing ketiga pemeriksaan benar ------------------------------
grep -q '89% WASPADA' laporan-pemeliharaan.txt
assert_eq 0 "$?" "disk 89% ditandai WASPADA"
grep -q '73% OK' laporan-pemeliharaan.txt
assert_eq 0 "$?" "disk 73% ditandai OK"
grep -q '4 paket siap di-upgrade: curl, libcurl4, libssl3, openssl' \
laporan-pemeliharaan.txt
assert_eq 0 "$?" "jumlah dan daftar paket tertunda benar"
grep -q '2 service failed: nginx.service, postgresql.service' \
laporan-pemeliharaan.txt
assert_eq 0 "$?" "jumlah dan daftar service gagal benar"
# --- uji 4: daftar kandidat dan rekomendasi dry-run ------------------------
grep -Fq '[dry-run] data/cache/lama.txt' laporan-pemeliharaan.txt
assert_eq 0 "$?" "dry-run menampilkan kandidat cache"
grep -Fq '[dry-run] data/log/app.log.1.gz' laporan-pemeliharaan.txt
assert_eq 0 "$?" "dry-run menampilkan kandidat log"
grep -Fq '[dry-run] data/tmp/sisa.tmp' laporan-pemeliharaan.txt
assert_eq 0 "$?" "dry-run menampilkan kandidat tmp"
grep -q 'jalankan ulang dengan --apply' laporan-pemeliharaan.txt
assert_eq 0 "$?" "rekomendasi menyarankan --apply"
# --- uji 5: dua run dry-run menghasilkan laporan identik -------------------
cp laporan-pemeliharaan.txt laporan-run1.txt
HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 0 "$?" "dry-run kedua sukses keluar 0"
diff laporan-run1.txt laporan-pemeliharaan.txt > /dev/null 2>&1
assert_eq 0 "$?" "dua laporan dry-run identik"
# --- uji 6: apply menghapus yang benar saja -------------------------------
HARI_INI=2026-02-15 bash pemeliharaan.sh --apply > /dev/null 2>&1
assert_eq 0 "$?" "apply sukses keluar 0"
[ ! -f data/cache/lama.txt ]
assert_eq 0 "$?" "apply menghapus cache lama"
[ ! -f data/log/app.log.1.gz ]
assert_eq 0 "$?" "apply menghapus log gz lama"
[ ! -f data/tmp/sisa.tmp ]
assert_eq 0 "$?" "apply menghapus tmp lama"
[ -f data/cache/baru.txt ]
assert_eq 0 "$?" "cache baru tetap ada"
[ -f data/log/app.log ]
assert_eq 0 "$?" "log lama non-gz tetap ada (pola hanya *.gz)"
[ -f data/log/app.log.2.gz ]
assert_eq 0 "$?" "log gz baru tetap ada"
[ -f data/tmp/aktif.tmp ]
assert_eq 0 "$?" "tmp baru tetap ada"
# --- uji 7: laporan apply akurat -------------------------------------------
terhapus=$(grep -Fc '[terhapus]' laporan-pemeliharaan.txt)
assert_eq 3 "$terhapus" "laporan apply mencatat 3 file terhapus"
grep -q "Total: 3 file, $total_hemat byte (benar-benar dibebaskan dari disk)" \
laporan-pemeliharaan.txt
assert_eq 0 "$?" "total hemat ruang cocok dengan jumlah byte fixture"
! grep -q 'jalankan ulang dengan --apply' laporan-pemeliharaan.txt
assert_eq 0 "$?" "laporan apply tidak menyarankan --apply lagi"
# --- uji 8: exit code untuk tiap jenis kegagalan ----------------------------
CONF=nihil.conf HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 1 "$?" "konfigurasi hilang keluar 1"
printf 'CACHE_DIRS="data/cache"\nLOG_DIRS=""\nTMP_DIRS=""\nCACHE_MAX_DAYS=abc\nLOG_MAX_DAYS=14\nTMP_MAX_DAYS=7\nDISK_WARN_PCT=80\n' > umur-rusak.conf
CONF=umur-rusak.conf HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 1 "$?" "umur tidak angka keluar 1"
mkdir -p data-kosong
SOURCE_DIR=data-kosong HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 2 "$?" "snapshot hilang keluar 2"
printf 'CACHE_DIRS="data/cache"\nLOG_DIRS="data/log"\nTMP_DIRS="data/tidak-ada"\nCACHE_MAX_DAYS=30\nLOG_MAX_DAYS=14\nTMP_MAX_DAYS=7\nDISK_WARN_PCT=80\n' > whitelist-rusak.conf
CONF=whitelist-rusak.conf HARI_INI=2026-02-15 bash pemeliharaan.sh > /dev/null 2>&1
assert_eq 3 "$?" "folder whitelist hilang keluar 3"
HARI_INI=2026-02-15 bash pemeliharaan.sh --bogus > /dev/null 2>&1
assert_eq 1 "$?" "argumen tak dikenal keluar 1"
# --- ringkasan --------------------------------------------------------------
if [ "$FAIL" -eq 0 ]; then
printf 'PASSED: %d/%d test lulus\n' "$PASS" "$PASS"
else
printf 'FAILED: %d/%d test lulus\n' "$PASS" "$((PASS + FAIL))"
fi
cd / && rm -rf "$WORK"
[ "$FAIL" -eq 0 ]