#!/usr/bin/env bash set -u cd "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" || exit 1 # ============================================================ # Handcrafted - Unified Workflow # Compile + Git Push + Mod Testing + Gitea Release # Linux / macOS # ============================================================ GITEA_BASE_URL="${GITEA_BASE_URL:-https://git.darto.id}" GITEA_OWNER="${GITEA_OWNER:-dartokloning}" GITEA_REPO="${GITEA_REPO:-handcrafted}" [[ -f gradle.properties ]] || { echo "[ERROR] gradle.properties tidak ditemukan."; exit 1; } get_prop() { sed -n "s/^$1=//p" gradle.properties | tail -n 1 | tr -d '\r'; } refresh_properties() { MOD_VERSION="$(get_prop mod_version)" MINECRAFT_VERSION="$(get_prop minecraft_version)" ARCHIVES_BASE_NAME="$(get_prop archives_base_name)" [[ -n "$MOD_VERSION" ]] || { echo "[ERROR] mod_version tidak ditemukan."; return 1; } MINECRAFT_VERSION="${MINECRAFT_VERSION:-26.2}" ARCHIVES_BASE_NAME="${ARCHIVES_BASE_NAME:-handcrafted}" TAG="v$MOD_VERSION" JAR_NAME="$ARCHIVES_BASE_NAME-$MOD_VERSION.jar" JAR_PATH="$PWD/build/libs/$JAR_NAME" PRERELEASE=false [[ "$MOD_VERSION" =~ (alpha|beta|rc) ]] && PRERELEASE=true } compile_mod() { refresh_properties || return 1 echo echo "============================================" echo " COMPILE" echo " Handcrafted $MOD_VERSION / Minecraft $MINECRAFT_VERSION" echo "============================================" [[ -f ./gradlew ]] || { echo "[ERROR] gradlew tidak ditemukan."; return 1; } chmod +x ./gradlew 2>/dev/null || true ./gradlew clean build --console=plain || { echo "[ERROR] Compile gagal."; return 1; } refresh_properties || return 1 [[ -f "$JAR_PATH" ]] || { echo "[ERROR] JAR tidak ditemukan: $JAR_PATH"; return 1; } echo "[OK] JAR: $JAR_PATH" } current_branch() { git branch --show-current } push_branch_3x() { local branch="$1" attempt=1 while (( attempt <= 3 )); do echo "[INFO] Push branch attempt $attempt/3..." git push -u origin "$branch" && return 0 ((attempt++)) (( attempt <= 3 )) && sleep 2 done return 1 } push_failure_menu() { local branch="$1" while true; do echo echo "============================================" echo " Push masih gagal" echo "============================================" echo "[1] Coba Push Git lagi" echo "[2] Mod Testing" echo "[3] Make Release" echo "[4] Exit" read -r -p "Pilih: " x case "$x" in 1) push_branch_3x "$branch" && return 0 ;; 2) test_mod; return 1 ;; 3) make_release; return $? ;; *) return 1 ;; esac done } push_git() { refresh_properties || return 1 echo echo "============================================" echo " GIT PUSH" echo "============================================" command -v git >/dev/null 2>&1 || { echo "[ERROR] Git tidak ditemukan."; return 1; } git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "[ERROR] Folder ini bukan Git repository." echo "[INFO] Jika repository baru, jalankan: git init" return 1 } git remote get-url origin >/dev/null 2>&1 || { echo "[ERROR] Remote origin belum dikonfigurasi."; return 1; } local branch branch="$(current_branch)" [[ -n "$branch" ]] || { echo "[ERROR] Branch aktif tidak ditemukan."; return 1; } if [[ -n "$(git status --porcelain)" ]]; then echo echo "Perubahan yang akan di-commit:" git status --short echo read -r -p "Commit message [Handcrafted $MOD_VERSION]: " msg msg="${msg:-Handcrafted $MOD_VERSION}" git add -A || return 1 git commit -m "$msg" || return 1 else echo "[INFO] Working tree bersih. Tidak ada commit baru." fi if push_branch_3x "$branch"; then echo "[OK] Push branch $branch selesai." return 0 fi echo "[ERROR] Push Git belum berhasil." push_failure_menu "$branch" } test_mod() { echo echo "============================================" echo " MOD TESTING" echo "============================================" [[ -f ./gradlew ]] || { echo "[ERROR] gradlew tidak ditemukan."; return 1; } chmod +x ./gradlew 2>/dev/null || true ./gradlew runClient --console=plain local rc=$? [[ $rc -eq 0 ]] && echo "[OK] Test client selesai." || echo "[WARN] Test client selesai dengan exit code $rc." return 0 } push_tag_with_retry() { local tag="$1" attempt while true; do attempt=1 while (( attempt <= 3 )); do echo "[INFO] Push tag attempt $attempt/3..." git push origin "$tag" && return 0 ((attempt++)) (( attempt <= 3 )) && sleep 2 done read -r -p "Push tag gagal 3x. Coba 3x lagi? [Y/N]: " ans [[ "$ans" =~ ^[Yy]$ ]] || return 1 done } json_escape() { python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))' } make_release() { refresh_properties || return 1 echo echo "============================================" echo " GITEA RELEASE" echo " Minecraft : $MINECRAFT_VERSION" echo " Version : $MOD_VERSION" echo " Tag : $TAG" echo " Asset : $JAR_NAME" echo " Gitea : $GITEA_BASE_URL/$GITEA_OWNER/$GITEA_REPO" echo "============================================" [[ -f "$JAR_PATH" ]] || { echo "[ERROR] JAR release tidak ditemukan: $JAR_PATH"; return 1; } command -v git >/dev/null 2>&1 || { echo "[ERROR] Git tidak ditemukan."; return 1; } command -v curl >/dev/null 2>&1 || { echo "[ERROR] curl tidak ditemukan."; return 1; } command -v python3 >/dev/null 2>&1 || { echo "[ERROR] python3 diperlukan untuk parsing JSON Gitea."; return 1; } git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "[ERROR] Bukan Git repository."; return 1; } git remote get-url origin >/dev/null 2>&1 || { echo "[ERROR] Remote origin belum dikonfigurasi."; return 1; } local branch branch="$(current_branch)" [[ -n "$branch" ]] || { echo "[ERROR] Branch aktif tidak ditemukan."; return 1; } if [[ -n "$(git status --porcelain)" ]]; then echo "[WARN] Ada perubahan source yang belum di-commit:" git status --short read -r -p "Commit dan push sebelum release? [Y/N]: " ans [[ "$ans" =~ ^[Yy]$ ]] || { echo "[INFO] Release dibatalkan."; return 0; } read -r -p "Commit message [Prepare Handcrafted $TAG release]: " msg msg="${msg:-Prepare Handcrafted $TAG release}" git add -A || return 2 git commit -m "$msg" || return 2 fi if ! push_branch_3x "$branch"; then echo "[ERROR] Push branch gagal 3x. Release belum dibuat." while true; do echo "[1] Coba Push Git lagi" echo "[2] Mod Testing" echo "[3] Exit" read -r -p "Pilih: " x case "$x" in 1) push_branch_3x "$branch" && break ;; 2) test_mod; return 1 ;; *) return 1 ;; esac done fi local head_commit tag_commit head_commit="$(git rev-parse HEAD)" tag_commit="$(git rev-list -n 1 "$TAG" 2>/dev/null || true)" if [[ -n "$tag_commit" && "$tag_commit" != "$head_commit" ]]; then echo "[ERROR] Tag $TAG sudah ada tetapi menunjuk commit lain." echo "Tag : $tag_commit" echo "HEAD: $head_commit" echo "[INFO] Naikkan mod_version sebelum membuat release baru." return 2 elif [[ -z "$tag_commit" ]]; then echo "[INFO] Membuat tag $TAG..." git tag -a "$TAG" -m "Handcrafted $TAG" || return 2 else echo "[INFO] Tag lokal $TAG sudah benar pada HEAD saat ini." fi if ! git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then push_tag_with_retry "$TAG" || return 2 else echo "[INFO] Tag $TAG sudah ada di origin." fi read -r -p "Buat Gitea Release $TAG dan upload $JAR_NAME? [Y/N]: " ans [[ "$ans" =~ ^[Yy]$ ]] || { echo "[INFO] Gitea Release dilewati."; return 0; } if [[ -z "${GITEA_TOKEN:-}" ]]; then read -r -s -p "Gitea Personal Access Token: " GITEA_TOKEN echo fi [[ -n "${GITEA_TOKEN:-}" ]] || { echo "[ERROR] Token Gitea kosong."; return 2; } local api_base tmpdir release_json create_json asset_json http_code release_id asset_exists api_base="$GITEA_BASE_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO" tmpdir="$(mktemp -d)" release_json="$tmpdir/release.json" create_json="$tmpdir/create.json" asset_json="$tmpdir/asset.json" trap 'rm -rf "$tmpdir"' RETURN http_code="$(curl -sS -o "$release_json" -w "%{http_code}" -H "Authorization: token $GITEA_TOKEN" "$api_base/releases/tags/$TAG")" if [[ "$http_code" == "404" ]]; then python3 - "$create_json" "$TAG" "$branch" "$MINECRAFT_VERSION" "$PRERELEASE" <<'PY' import json,sys path,tag,branch,mc,pre=sys.argv[1:] data={ "tag_name":tag, "target_commitish":branch, "name":f"Handcrafted {tag} for Fabric {mc}", "body":f"Fabric port of Handcrafted for Minecraft {mc}.", "draft":False, "prerelease":pre.lower()=="true", } with open(path,"w",encoding="utf-8") as f: json.dump(data,f) PY echo "[INFO] Membuat Gitea Release $TAG..." http_code="$(curl -sS -o "$release_json" -w "%{http_code}" -X POST \ -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" \ --data-binary "@$create_json" "$api_base/releases")" [[ "$http_code" == "201" ]] || { echo "[ERROR] Gitea gagal membuat release. HTTP $http_code" cat "$release_json"; return 2; } elif [[ "$http_code" == "200" ]]; then echo "[INFO] Gitea Release $TAG sudah ada. Release tersebut akan digunakan." else echo "[ERROR] Gagal memeriksa Gitea Release. HTTP $http_code" cat "$release_json" return 2 fi release_id="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["id"])' "$release_json")" asset_exists="$(python3 - "$release_json" "$JAR_NAME" <<'PY' import json,sys r=json.load(open(sys.argv[1])) print("yes" if any(a.get("name")==sys.argv[2] for a in r.get("assets",[])) else "no") PY )" if [[ "$asset_exists" == "yes" ]]; then echo "[INFO] Asset $JAR_NAME sudah ada. Tidak diupload ulang." echo "[SUCCESS] Gitea Release $TAG siap." return 0 fi echo "[INFO] Release ID: $release_id" echo "[INFO] Mengupload $JAR_NAME..." local asset_code asset_code="$(curl -sS -o "$asset_json" -w "%{http_code}" -X POST \ -H "Authorization: token $GITEA_TOKEN" \ -F "attachment=@$JAR_PATH" \ "$api_base/releases/$release_id/assets?name=$JAR_NAME")" if [[ "$asset_code" == "201" ]]; then echo "[SUCCESS] Release $TAG berhasil dibuat dan JAR berhasil diupload." elif [[ "$asset_code" == "409" ]]; then echo "[WARN] Asset $JAR_NAME sudah ada di release." else echo "[ERROR] Upload JAR gagal. HTTP $asset_code" cat "$asset_json" return 2 fi } after_push() { while true; do echo; echo "[1] Mod Testing"; echo "[2] Make Release"; echo "[3] Exit" read -r -p "Pilih: " x case "$x" in 1) test_mod; after_test; return ;; 2) make_release; after_release; return ;; *) exit 0 ;; esac done } after_test() { while true; do echo; echo "[1] Push Git"; echo "[2] Make Release"; echo "[3] Exit" read -r -p "Pilih: " x case "$x" in 1) push_git; after_push; return ;; 2) make_release; after_release; return ;; *) exit 0 ;; esac done } after_release() { while true; do echo; echo "[1] Push Git"; echo "[2] Mod Testing"; echo "[3] Exit" read -r -p "Pilih: " x case "$x" in 1) push_git; after_push; return ;; 2) test_mod; after_test; return ;; *) exit 0 ;; esac done } main_menu() { while true; do refresh_properties || exit 1 echo echo "============================================" echo " Handcrafted Workflow" echo " Minecraft : $MINECRAFT_VERSION" echo " Version : $MOD_VERSION" echo " JAR : $JAR_NAME" echo "============================================" echo "[1] Push Git" echo "[2] Mod Testing" echo "[3] Make Release" echo "[4] Exit" read -r -p "Pilih: " x case "$x" in 1) push_git; after_push; return ;; 2) test_mod; after_test; return ;; 3) make_release; after_release; return ;; *) exit 0 ;; esac done } refresh_properties || exit 1 if [[ ! -f "$JAR_PATH" ]]; then echo "[INFO] JAR untuk versi $MOD_VERSION belum ditemukan. Compile otomatis..." until compile_mod; do echo echo "[1] Coba Compile lagi" echo "[2] Push Git" echo "[3] Mod Testing" echo "[4] Exit" read -r -p "Pilih: " x case "$x" in 1) ;; 2) push_git; after_push; exit 0 ;; 3) test_mod; after_test; exit 0 ;; *) exit 1 ;; esac done else echo "[INFO] JAR untuk versi $MOD_VERSION sudah ditemukan." fi main_menu