Laptop hilang, disk rusak, atau sekadar ganti mesin — bagi pengembang, ketiganya berujung pada ritual yang sama: pasang ulang tool satu per satu, coba ingat alias mana yang dulu dibuat, cari-cari salinan .gitconfig lama, dan baru seminggu kemudian sadar ada konfigurasi yang tertinggal. Proyek terakhir course ini menghapus ritual itu dengan mengubah lingkungan development menjadi sesuatu yang bisa dibangun ulang dalam satu perintah: satu skrip setup yang idempoten memeriksa toolchain, satu folder dotfiles yang terkelola git, satu skrip scaffolding untuk memulai proyek baru, dan test suite yang menjaga semuanya tetap benar.
Kerangka yang kita bangun terdiri dari empat komponen yang saling melengkapi:
scripts/setup.sh— pemeriksa toolchain inti (gcc, make, git, python3, curl) beserta versi minimum, lalu pemasangan dotfiles dengan backup.bakuntuk yang lama. Idempoten: dijalankan berapa kali pun, keadaan akhirnya sama; mode--checkmelaporkan tanpa mengubah apa pun.config/— dotfile template:.bashrc-dev(PATH, alias, fungsimkcd, prompt dengan branch git),.gitconfig(identitas, editor, aliass/co/br),.editorconfig(konvensi format lintas editor), plus template.gitignoreper jenis proyek.scripts/proyek-baru.sh— scaffold proyek siap kerja: repo git dengan satu commit awal, README,.gitignore, Makefile, dansrc/berisi berkas awal.tests/run-tests.sh— test suite yang membuktikan seluruh janji di atas: idempotensi, backup, laporan mode--check, scaffold yang benar-benar bisa dibangun dan dijalankan.
Tampilan Program Akhir #
Sesi berikut menunjukkan kerangka yang sudah terpasang pada komputer contoh. Tiga perintah pertama adalah siklus lengkap setup: lihat dulu apa yang akan terjadi, pasang sungguhan, lalu buktikan eksekusi ulang tidak mengubah apa pun:
$ bash scripts/setup.sh --check
mode --check: laporan saja, tidak ada yang diubah
== tahap 1: tool development ==
[OK] gcc versi 14.2.0 (minimum 10.0) — /usr/bin/gcc
[OK] make versi 4.4.1 (minimum 4.0) — /usr/bin/make
[OK] git versi 2.47.3 (minimum 2.30) — /usr/bin/git
[OK] python3 versi 3.13.5 (minimum 3.8) — /usr/bin/python3
[OK] curl versi 8.14.1 (minimum 7.70) — /usr/bin/curl
ringkasan: 5 tool diperiksa, 0 bermasalah
== tahap 2: dotfiles ==
[BARU] /home/budi/.bashrc-dev akan dipasang
[BARU] /home/budi/.gitconfig akan dipasang
[BARU] /home/budi/.editorconfig akan dipasang
[BARU] hook .bashrc akan memuat .bashrc-dev
setup.sh: selesai — lingkungan siap
$ bash scripts/setup.sh | tail -n 6
== tahap 2: dotfiles ==
[PASANG] /home/budi/.bashrc-dev
[PASANG] /home/budi/.gitconfig
[PASANG] /home/budi/.editorconfig
[PASANG] hook .bashrc memuat .bashrc-dev
setup.sh: selesai — lingkungan siap
$ bash scripts/setup.sh | tail -n 6
== tahap 2: dotfiles ==
[OK] /home/budi/.bashrc-dev sudah terpasang
[OK] /home/budi/.gitconfig sudah terpasang
[OK] /home/budi/.editorconfig sudah terpasang
[OK] /home/budi/.bashrc sudah memuat .bashrc-dev
setup.sh: selesai — lingkungan siap
$ bash scripts/proyek-baru.sh aplikasi-tes c
proyek siap: aplikasi-tes (jenis c)
cd aplikasi-tes && make run
$ cd aplikasi-tes && make run
halo, dunia
$ git s
## main
Bagian yang tidak terlihat dari sesi itu justru yang paling penting: ketiga eksekusi setup.sh menghasilkan keadaan home yang identik, setiap penimpaan dotfile didahului salinan .bak, dan commit pertama aplikasi-tes sudah ada di riwayat git sebelum kita mengetik satu baris kode pun.
Tujuan Pembelajaran #
Setelah menyelesaikan proyek ini, kita mampu:
- menulis skrip setup yang idempoten dan membuktikannya lewat pembandingan keadaan antar eksekusi,
- memakai mode
--checksebagai dry-run yang dijamin tidak berefek samping, - memeriksa keberadaan dan versi tool secara terprogram dengan
command -vdan pembandingan versi, - mengelola dotfiles di bawah git lalu memasangnya ke home dengan backup otomatis,
- menimbang trade-off antara symlink dan copy untuk pendistribusian dotfile,
- membuat skrip scaffolding yang menyiapkan repo git lengkap dengan commit awal,
- menjaga kebiasaan setup tetap mutakhir lewat penjadwalan dan exit code yang bermakna.
Prasyarat #
Proyek ini tidak memperkenalkan alat baru, melainkan merangkai materi yang sudah dibahas. Chapter yang paling sering dirujuk:
- Compilers dan Build Tools — peran gcc, make, dan toolchain di Debian,
- Git dan Version Control
— identitas git,
~/.gitconfig, dan siklus commit, - Environment Isolation — mengapa konfigurasi per proyek tidak dicampur ke sistem,
- Functions
— fungsi shell seperti
mkcdyang kita pakai di.bashrc-dev.
Tahapan Pengerjaan #
Proyek dikerjakan dalam tiga bagian berurutan. Setiap bagian diakhiri dengan keadaan yang bisa dijalankan dan diamati:
- Bagian 1: Instalasi Tool Development — menyusun daftar tool esensial, menulis fungsi pemeriksa tool beserta logika versi minimum, dan menjalankan
setup.sh --checkpada sistem nyata. - Bagian 2: Konfigurasi Environment — menulis tiga dotfile inti, membedah isi masing-masing, lalu memperluas
setup.shagar memasang semuanya dengan backup dan tetap idempoten. - Bagian 3: Otomasi Workflow — membangun
proyek-baru.shyang men-scaffold repo siap kerja, memakai alias git dan fungsi shell sehari-hari, serta menjaga setup tetap mutakhir.
Mulailah dari Bagian 1 karena skrip setup adalah fondasi yang dipakai dua bagian berikutnya — dotfile yang dipasang Bagian 2 dan workflow di Bagian 3 sama-sama berdiri di atas laporan tool dan mode --check yang kita bangun lebih dulu. Berkas sumber lengkap proyek (setup.sh, proyek-baru.sh, dotfile di config/, dan test suite di tests/) tersedia pada bagian Berkas Sumber di bawah halaman setiap bagian, sehingga hasil pekerjaan bisa dibandingkan dengan versi referensi kapan saja.
Susun skrip instalasi compiler, bahasa, dan editor beserta versinya.
Buka → # Bagian 2: Konfigurasi EnvironmentAtur shell, git, dan environment variable agar konsisten di setiap sesi.
Buka → # Bagian 3: Otomasi WorkflowOtomatiskan pembuatan proyek baru dengan template dan alias produktif.
Buka →Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
scripts/proyek-baru.sh
Unduh berkas#!/usr/bin/env bash
# proyek-baru.sh — scaffold proyek baru siap kerja dalam satu perintah.
#
# Pemakaian:
# proyek-baru.sh <nama> [jenis]
# nama : nama folder proyek, kebab-case disarankan
# jenis : c | python | node (default: c)
#
# Membuat folder berisi repo git (branch main, satu commit awal),
# README.md, .gitignore, Makefile, dan src/ dengan berkas awal sesuai
# jenis. Tidak pernah menimpa: nama yang sudah dipakai ditolak.
#
# Variabel lingkungan:
# DEV_SRC folder config berisi template (default: <skrip>/../config)
# GIT_AUTHOR_NAME / GIT_AUTHOR_EMAIL — identitas commit bila
# konfigurasi global git belum diisi (default: Pengembang)
#
# Kode keluar:
# 0 sukses 1 pemakaian salah
# 2 nama sudah dipakai / template tidak ada
set -u
BASE=$(cd "$(dirname "$0")" && pwd)
DEV_SRC="${DEV_SRC:-$BASE/../config}"
gagal() {
printf 'proyek-baru.sh: %s\n' "$1" >&2
exit "$2"
}
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
gagal 'pemakaian: proyek-baru.sh <nama> [c|python|node]' 1
fi
NAMA=$1
JENIS=${2:-c}
case $JENIS in
c|python|node) ;;
*) gagal "jenis tidak dikenal: $JENIS (pilih c, python, atau node)" 1 ;;
esac
case $NAMA in
''|-*) gagal "nama tidak valid: '$NAMA' (jangan kosong atau diawali -)" 1 ;;
*/*) gagal "nama tidak boleh memuat '/'" 1 ;;
esac
[ -e "$NAMA" ] && gagal "sudah ada: $NAMA — pilih nama lain" 2
template="$DEV_SRC/gitignore-$JENIS"
[ -f "$template" ] || gagal "template tidak ditemukan: $template" 2
mkdir -p "$NAMA/src"
cd "$NAMA" || exit 1
git init -q -b main
# --- berkas dasar ----------------------------------------------------------
cp "$template" .gitignore
if [ "$JENIS" = c ]; then
# Nama binary hasil build mengikuti nama proyek — abaikan lewat pola
# yang dijangkar ke akar repo (/<nama>) agar hanya binary di root yang
# tidak dilacak git.
printf '# binary hasil build make\n/%s\n' "$NAMA" >> .gitignore
fi
printf '# %s\n\nProyek %s (jenis: %s) dibuat oleh proyek-baru.sh.\n\n## Menjalankan\n\n make run\n' \
"$NAMA" "$NAMA" "$JENIS" > README.md
# --- Makefile dan berkas awal sesuai jenis -----------------------------------
if [ "$JENIS" = c ]; then
cat > Makefile <<MAKE
# Makefile — tugas build untuk proyek C.
BIN = $NAMA
CC = gcc
CFLAGS = -Wall -Wextra -std=c17
\$(BIN): src/main.c
\$(CC) \$(CFLAGS) -o \$(BIN) src/main.c
run: \$(BIN)
./\$(BIN)
clean:
rm -f \$(BIN)
.PHONY: run clean
MAKE
cat > src/main.c <<'C'
/* main.c — titik masuk proyek. */
#include <stdio.h>
int main(void)
{
printf("halo, dunia\n");
return 0;
}
C
elif [ "$JENIS" = python ]; then
cat > Makefile <<'MAKE'
# Makefile — tugas umum proyek Python.
PY = python3
run:
$(PY) src/main.py
.PHONY: run
MAKE
cat > src/main.py <<'PY'
"""main.py — titik masuk proyek."""
def main() -> None:
print("halo, dunia")
if __name__ == "__main__":
main()
PY
else
cat > Makefile <<'MAKE'
# Makefile — tugas umum proyek Node.js.
NODE = node
run:
$(NODE) src/index.js
.PHONY: run
MAKE
cat > src/index.js <<'JS'
// index.js — titik masuk proyek.
console.log("halo, dunia");
JS
fi
# --- identitas commit --------------------------------------------------------
# Pakai konfigurasi git bila sudah ada; jika belum, nilai bawaan yang
# bisa di-override lewat environment (berguna untuk pengujian).
if [ -z "$(git config --get user.name 2>/dev/null)" ]; then
: "${GIT_AUTHOR_NAME:=Pengembang}"
: "${GIT_AUTHOR_EMAIL:[email protected]}"
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
fi
git add -A
git commit -q -m "commit awal: kerangka $JENIS"
printf 'proyek siap: %s (jenis %s)\n' "$NAMA" "$JENIS"
printf ' cd %s && make run\n' "$NAMA"
exit 0
scripts/setup.sh
Unduh berkas#!/usr/bin/env bash
# setup.sh — bangun lingkungan development dalam satu perintah.
#
# Memeriksa toolchain inti (gcc, make, git, python3, curl), memasang
# dotfiles dari config/ dengan backup .bak untuk yang lama, lalu
# melaporkan hasilnya. Idempoten: dijalankan berapa kali pun dengan isi
# config/ yang sama, keadaan akhirnya selalu sama.
#
# Pemakaian:
# setup.sh periksa tool lalu pasang dotfiles
# setup.sh --check dry-run: laporan saja, tidak mengubah apa pun
#
# Variabel lingkungan (berguna untuk pengujian):
# DEV_HOME folder home tujuan pemasangan (default: $HOME)
# DEV_SRC folder config berisi dotfile (default: <skrip>/../config)
#
# Kode keluar:
# 0 sukses 1 opsi tidak dikenal
# 2 config/ tidak ditemukan 3 tool inti hilang/terlalu tua
set -u
MODE=pasang
for arg in "$@"; do
case $arg in
--check) MODE=cek ;;
*)
printf 'setup.sh: opsi tidak dikenal: %s\n' "$arg" >&2
printf 'pemakaian: setup.sh [--check]\n' >&2
exit 1
;;
esac
done
BASE=$(cd "$(dirname "$0")" && pwd)
DEV_SRC="${DEV_SRC:-$BASE/../config}"
DEV_HOME="${DEV_HOME:-$HOME}"
if [ ! -d "$DEV_SRC" ]; then
printf 'setup.sh: folder config tidak ditemukan: %s\n' "$DEV_SRC" >&2
exit 2
fi
if [ "$MODE" = cek ]; then
printf 'mode --check: laporan saja, tidak ada yang diubah\n'
fi
# --- tool inti: nama:versi-minimum ---------------------------------------
DAFTAR_TOOL="gcc:10.0 make:4.0 git:2.30 python3:3.8 curl:7.70"
# versi_cukup <versi> <minimum>: keluar 0 bila versi >= minimum.
# Hanya major.minor yang dibandingkan — patch tidak menentukan layak/tidak.
versi_cukup() {
awk -v v="$1" -v m="$2" 'BEGIN {
split(v, a, "."); split(m, b, ".");
if (a[1] + 0 < b[1] + 0) exit 1;
if (a[1] + 0 == b[1] + 0 && a[2] + 0 < b[2] + 0) exit 1;
}'
}
# versi_tool <nama>: cetak nomor versi untuk tool yang dikenal.
versi_tool() {
case $1 in
gcc) gcc -dumpfullversion ;;
make) make --version | sed -n '1s/^GNU Make //p' ;;
git) git --version | sed -n 's/^git version //p' ;;
python3) python3 --version | sed -n 's/^Python //p' ;;
curl) curl --version | awk 'NR==1 {print $2}' ;;
*) printf '0' ;;
esac
}
# cek_tool <nama> <minimum>: laporan satu baris + usul instalasi bila hilang.
# exit 0 siap, 1 hilang, 2 versi terlalu tua.
cek_tool() {
nama=$1
min=$2
if ! jalur=$(command -v "$nama" 2>/dev/null); then
printf '[HILANG] %-8s tidak ditemukan\n' "$nama"
printf ' usul: sudo apt-get install %s\n' "$nama"
return 1
fi
versi=$(versi_tool "$nama")
if versi_cukup "$versi" "$min"; then
printf '[OK] %-8s versi %s (minimum %s) — %s\n' \
"$nama" "$versi" "$min" "$jalur"
return 0
fi
printf '[TUA] %-8s versi %s di bawah minimum %s\n' \
"$nama" "$versi" "$min"
return 2
}
# --- tahap 1: periksa tool ------------------------------------------------
printf '== tahap 1: tool development ==\n'
hilang=0
# shellcheck disable=SC2086 # daftar tool memang dipisah spasi
for item in $DAFTAR_TOOL; do
nama=${item%%:*}
min=${item#*:}
cek_tool "$nama" "$min" || hilang=$((hilang + 1))
done
# shellcheck disable=SC2086
printf 'ringkasan: %d tool diperiksa, %d bermasalah\n' \
"$(printf '%s\n' $DAFTAR_TOOL | wc -w)" "$hilang"
# --- tahap 2: pasang dotfiles ----------------------------------------------
# pasang_dotfile <sumber> <target>: salin dengan backup; idempoten.
pasang_dotfile() {
sumber=$1
target=$2
if [ -f "$target" ] && cmp -s "$sumber" "$target"; then
printf '[OK] %s sudah terpasang\n' "$target"
return 0
fi
if [ "$MODE" = cek ]; then
printf '[BARU] %s akan dipasang\n' "$target"
return 0
fi
if [ -f "$target" ]; then
cp "$target" "$target.bak"
printf '[BAK] %s -> %s.bak\n' "$target" "$target"
fi
cp "$sumber" "$target"
printf '[PASANG] %s\n' "$target"
}
# pasang_hook_bashrc: pastikan ~/.bashrc memuat ~/.bashrc-dev (sekali saja).
pasang_hook_bashrc() {
bashrc="$DEV_HOME/.bashrc"
if [ -f "$bashrc" ] && grep -qF '.bashrc-dev' "$bashrc"; then
printf '[OK] %s sudah memuat .bashrc-dev\n' "$bashrc"
return 0
fi
if [ "$MODE" = cek ]; then
printf '[BARU] hook .bashrc akan memuat .bashrc-dev\n'
return 0
fi
if [ -f "$bashrc" ]; then
cp "$bashrc" "$bashrc.bak"
printf '[BAK] %s -> %s.bak\n' "$bashrc" "$bashrc"
fi
{
printf '\n'
printf '# >>> lingkungan-dev: muat kustomisasi shell >>>\n'
printf '[ -f "$HOME/.bashrc-dev" ] && . "$HOME/.bashrc-dev"\n'
printf '# <<< lingkungan-dev <<<\n'
} >> "$bashrc"
printf '[PASANG] hook .bashrc memuat .bashrc-dev\n'
}
printf '== tahap 2: dotfiles ==\n'
pasang_dotfile "$DEV_SRC/.bashrc-dev" "$DEV_HOME/.bashrc-dev"
pasang_dotfile "$DEV_SRC/.gitconfig" "$DEV_HOME/.gitconfig"
pasang_dotfile "$DEV_SRC/.editorconfig" "$DEV_HOME/.editorconfig"
pasang_hook_bashrc
if [ "$hilang" -gt 0 ]; then
printf 'setup.sh: selesai dengan catatan: %d tool bermasalah\n' "$hilang"
exit 3
fi
printf 'setup.sh: selesai — lingkungan siap\n'
exit 0
tests/run-tests.sh
Unduh berkas#!/usr/bin/env bash
# run-tests.sh — test suite untuk paket lingkungan-development-linux.
#
# Suite menyiapkan folder kerja terisolasi (mktemp), menyalin skrip
# dari scripts/ dan dotfile dari config/, lalu menjalankan rangkaian
# pengujian dengan assertion sederhana:
# - setup.sh --check melaporkan tanpa mengubah apa pun,
# - setup.sh idempoten: dua eksekusi, keadaan akhir identik,
# - dotfiles terpasang dengan isi identik + hook .bashrc sekali saja,
# - backup .bak menyimpan versi lama sebelum ditimpa,
# - tool inti hilang dilaporkan dengan keluar 3 (PATH tiruan),
# - proyek-baru.sh membuat struktur + repo git dengan commit awal,
# - proyek hasil scaffold bisa dibangun dan dijalankan (make run),
# - penolakan: nama terpakai, tanpa nama, jenis tak dikenal.
#
# 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
}
# --- folder kerja terisolasi + salin paket ----------------------------------
WORK=$(mktemp -d)
mkdir -p "$WORK/paket/scripts" "$WORK/paket/config"
cp "$PROJ_DIR/scripts/setup.sh" "$PROJ_DIR/scripts/proyek-baru.sh" \
"$WORK/paket/scripts/"
cp -R "$PROJ_DIR/config/." "$WORK/paket/config/"
SETUP="$WORK/paket/scripts/setup.sh"
BARU="$WORK/paket/scripts/proyek-baru.sh"
RUMAH="$WORK/rumah"
mkdir -p "$RUMAH"
# potret: checksum seluruh berkas di $RUMAH — bahan uji idempotensi.
potret() {
(cd "$RUMAH" && find . -type f -print | sort | xargs sha256sum)
}
# --- uji 1: --check melaporkan tanpa mengubah ------------------------------
DEV_HOME="$RUMAH" bash "$SETUP" --check > "$WORK/cek1.txt"
assert_eq 0 "$?" "setup --check keluar 0"
grep -q '^mode --check' "$WORK/cek1.txt"
assert_eq 0 "$?" "mode --check diumumkan di laporan"
grep -q '^\[OK\] gcc' "$WORK/cek1.txt"
assert_eq 0 "$?" "gcc dilaporkan OK beserta versinya"
n_baru=$(grep -c '^\[BARU\]' "$WORK/cek1.txt")
assert_eq 4 "$n_baru" "--check melaporkan 4 item akan dipasang"
n_berkas=$(find "$RUMAH" -type f | wc -l)
assert_eq 0 "$n_berkas" "--check tidak membuat berkas apa pun"
# --- uji 2: pemasangan pertama ----------------------------------------------
DEV_HOME="$RUMAH" bash "$SETUP" > "$WORK/pasang1.txt"
assert_eq 0 "$?" "setup pertama keluar 0"
[ -f "$RUMAH/.gitconfig" ]
assert_eq 0 "$?" "gitconfig terpasang"
cmp -s "$WORK/paket/config/.gitconfig" "$RUMAH/.gitconfig"
assert_eq 0 "$?" "isi gitconfig identik dengan template"
[ -f "$RUMAH/.editorconfig" ]
assert_eq 0 "$?" "editorconfig terpasang"
[ -f "$RUMAH/.bashrc-dev" ]
assert_eq 0 "$?" "bashrc-dev terpasang"
grep -qF '. "$HOME/.bashrc-dev"' "$RUMAH/.bashrc"
assert_eq 0 "$?" "hook .bashrc memuat bashrc-dev"
n=$(grep -c '^\[PASANG\]' "$WORK/pasang1.txt")
assert_eq 4 "$n" "empat item terpasang pada eksekusi pertama"
# --- uji 3: idempoten — eksekusi ulang tidak mengubah keadaan -----------------
potret > "$WORK/state1.txt"
DEV_HOME="$RUMAH" bash "$SETUP" > "$WORK/pasang2.txt"
assert_eq 0 "$?" "setup kedua keluar 0"
potret > "$WORK/state2.txt"
diff "$WORK/state1.txt" "$WORK/state2.txt" > /dev/null 2>&1
assert_eq 0 "$?" "keadaan identik setelah eksekusi kedua"
n=$(grep -c 'sudah terpasang' "$WORK/pasang2.txt")
assert_eq 3 "$n" "eksekusi kedua melaporkan 3 dotfile sudah terpasang"
grep -q 'sudah memuat .bashrc-dev' "$WORK/pasang2.txt"
assert_eq 0 "$?" "hook .bashrc tidak ditulis dua kali"
# --- uji 4: --check pada keadaan terpasang tetap tidak mengubah ----------------
potret > "$WORK/state3.txt"
DEV_HOME="$RUMAH" bash "$SETUP" --check > "$WORK/cek2.txt"
assert_eq 0 "$?" "setup --check (sudah terpasang) keluar 0"
potret > "$WORK/state4.txt"
diff "$WORK/state3.txt" "$WORK/state4.txt" > /dev/null 2>&1
assert_eq 0 "$?" "--check tidak mengubah keadaan terpasang"
n=$(grep -c 'sudah terpasang' "$WORK/cek2.txt")
assert_eq 3 "$n" "--check melaporkan 3 dotfile sudah terpasang"
# --- uji 5: backup .bak menyimpan versi lama -----------------------------------
printf '[user]\n\tname = Budi Lama\n' > "$RUMAH/.gitconfig"
DEV_HOME="$RUMAH" bash "$SETUP" > "$WORK/pasang3.txt"
assert_eq 0 "$?" "setup ulang setelah perubahan keluar 0"
grep -q '^\[BAK\]' "$WORK/pasang3.txt"
assert_eq 0 "$?" "backup .bak dibuat sebelum menimpa"
grep -q 'name = Budi Lama' "$RUMAH/.gitconfig.bak"
assert_eq 0 "$?" "isi lama tersimpan di .gitconfig.bak"
cmp -s "$WORK/paket/config/.gitconfig" "$RUMAH/.gitconfig"
assert_eq 0 "$?" "gitconfig kembali identik dengan template"
# --- uji 6: tool inti hilang dilaporkan keluar 3 --------------------------------
# PATH tiruan tanpa gcc: skrip harus melaporkan [HILANG] lalu keluar 3.
FAKEBIN="$WORK/fakebin"
mkdir -p "$FAKEBIN"
for t in bash awk sed grep wc cmp cp dirname make git python3 curl; do
ln -s "$(command -v "$t")" "$FAKEBIN/$t"
done
PATH="$FAKEBIN" DEV_HOME="$WORK/rumah-fake" bash "$SETUP" --check \
> "$WORK/cek3.txt"
assert_eq 3 "$?" "tool inti hilang dilaporkan keluar 3"
grep -q '^\[HILANG\] gcc' "$WORK/cek3.txt"
assert_eq 0 "$?" "gcc hilang terlihat di laporan"
# --- uji 7: opsi salah ditolak ---------------------------------------------------
bash "$SETUP" --salah > /dev/null 2>&1
assert_eq 1 "$?" "opsi tidak dikenal keluar 1"
# --- uji 8: proyek-baru membuat struktur + repo git -------------------------------
cd "$WORK" || exit 1
GIT_AUTHOR_NAME="Budi Santoso" GIT_AUTHOR_EMAIL="[email protected]" \
GIT_COMMITTER_NAME="Budi Santoso" GIT_COMMITTER_EMAIL="[email protected]" \
GIT_AUTHOR_DATE="2026-01-15 09:00:00 +0000" \
GIT_COMMITTER_DATE="2026-01-15 09:00:00 +0000" \
bash "$BARU" aplikasi-tes c > "$WORK/baru1.txt"
assert_eq 0 "$?" "proyek-baru (c) keluar 0"
for f in README.md .gitignore Makefile src/main.c; do
[ -f "aplikasi-tes/$f" ]
assert_eq 0 "$?" "$f dibuat"
done
[ -d aplikasi-tes/.git ]
assert_eq 0 "$?" "repo git dibuat"
cabang=$(git -C aplikasi-tes symbolic-ref --short HEAD)
assert_eq main "$cabang" "branch utama bernama main"
n_commit=$(git -C aplikasi-tes log --oneline | wc -l)
assert_eq 1 "$n_commit" "satu commit awal dibuat"
n_kotor=$(git -C aplikasi-tes status --porcelain | wc -l)
assert_eq 0 "$n_kotor" "git status bersih setelah commit awal"
# --- uji 9: proyek hasil scaffold benar-benar bisa dijalankan ----------------------
make -s -C aplikasi-tes run > "$WORK/run-c.txt"
assert_eq 0 "$?" "make run (c) keluar 0"
grep -q 'halo, dunia' "$WORK/run-c.txt"
assert_eq 0 "$?" "program c menyapa halo, dunia"
n_kotor=$(git -C aplikasi-tes status --porcelain | wc -l)
assert_eq 0 "$n_kotor" "binary hasil build diabaikan .gitignore"
GIT_AUTHOR_NAME="Budi Santoso" GIT_AUTHOR_EMAIL="[email protected]" \
GIT_COMMITTER_NAME="Budi Santoso" GIT_COMMITTER_EMAIL="[email protected]" \
GIT_AUTHOR_DATE="2026-01-15 09:00:00 +0000" \
GIT_COMMITTER_DATE="2026-01-15 09:00:00 +0000" \
bash "$BARU" web-tes python > "$WORK/baru2.txt"
assert_eq 0 "$?" "proyek-baru (python) keluar 0"
[ -f web-tes/src/main.py ]
assert_eq 0 "$?" "main.py dibuat"
make -s -C web-tes run > "$WORK/run-py.txt"
assert_eq 0 "$?" "make run (python) keluar 0"
grep -q 'halo, dunia' "$WORK/run-py.txt"
assert_eq 0 "$?" "program python menyapa halo, dunia"
# --- uji 10: penolakan --------------------------------------------------------------
bash "$BARU" aplikasi-tes c > /dev/null 2>&1
assert_eq 2 "$?" "nama yang sudah dipakai ditolak keluar 2"
bash "$BARU" > /dev/null 2>&1
assert_eq 1 "$?" "tanpa nama ditolak keluar 1"
bash "$BARU" tes-zig zig > /dev/null 2>&1
assert_eq 1 "$?" "jenis tidak dikenal ditolak 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 ]