# 🚀 G-Launcher Updater & Bootstrap Build System เอกสารนี้อธิบายรายละเอียดเชิงลึกของระบบ **Auto-Update & Local Build Bootstrap** ของ G-Launcher ซึ่งถูกออกแบบมาเพื่อรองรับกรณีที่ผู้ใช้หรือเซิร์ฟเวอร์ Git (เช่น Gitea) ไม่มี CI/CD Runner บนคลาวด์สำหรับคอมไพล์ไบนารีล่วงหน้า --- ## 🎯 กฎเหล็กของระบบอัปเดต (The Golden Updater Rule) > **"โหลด Temp มา -> Build ใหม่ใน Temp -> นำมาแทนของเก่า -> ลบ Temp ทิ้ง 100%"** ### เหตุผลทางสถาปัตยกรรม (Architectural Rationale) 1. **ป้องกัน Windows File Lock:** ระบบปฏิบัติการ Windows จะล็อกไฟล์ `.exe` ที่กำลังทำงานอยู่ทันที ทำให้ไม่สามารถเขียนทับ (overwrite) ตัวเองได้โดยตรง แต่ Windows อนุญาตให้ **เปลี่ยนชื่อ (Rename)** ไฟล์ executable ที่กำลังทำงานอยู่ได้ 2. **รักษาความสะอาดของโปรเจกต์ (Zero Pollution):** ไม่ทำการ build หรือ pull โค้ดทับโฟลเดอร์ทำงานหลักโดยตรง เพื่อป้องกันไฟล์ขยะหรือ node_modules ปนเปื้อน 3. **Atomic Rollback:** หากกระบวนการคอมไพล์ใน Temp ล้มเหลว โปรแกรมเวอร์ชันเดิมจะยังคงทำงานได้สมบูรณ์ ไม่เสียหาย --- ## 🔄 วงจรชีวิตของระบบอัปเดต (Updater Lifecycle) ```mermaid sequenceDiagram autonumber actor User as ผู้ใช้งาน / Contributor participant Bootstrap as bootstrap.ps1 / Go GitService participant TempDir as โฟลเดอร์ชั่วคราว (Temp) participant Git as Git Repository (Dev Branch) participant Target as bin/G-Launcher.exe User->>Bootstrap: รัน update-and-build.bat หรือกด Update ใน UI Note over Bootstrap: ตรวจสอบและเตรียม Portable Git, Go, Node.js (tools/) Bootstrap->>TempDir: 1. สร้างโฟลเดอร์ชั่วคราว (temp/build_workspace) Bootstrap->>Git: 2. Clone โค้ดสาขา Dev ลงใน TempDir Git-->>TempDir: ดาวน์โหลดซอร์สโค้ดสำเร็จ rect rgb(30, 40, 60) Note over TempDir: 3. บิลด์ทั้งหมดภายใน TempDir TempDir->>TempDir: npm install & npm run build (React Frontend) TempDir->>TempDir: go mod tidy TempDir->>TempDir: wails3 generate syso (Manifest & Icon) TempDir->>TempDir: go build -tags production -> bin/G-Launcher.exe end rect rgb(40, 60, 40) Note over Bootstrap,Target: 4. สลับไฟล์ไบนารี (Atomic Swap & Rollback) Bootstrap->>Target: เปลี่ยนชื่อตัวเก่าเป็น G-Launcher.exe.old Bootstrap->>Target: ย้ายตัวใหม่จาก TempDir มาแทนที่ Target alt ย้ายไฟล์สำเร็จ Bootstrap->>Target: ลบไฟล์ G-Launcher.exe.old else เกิดข้อผิดพลาด Bootstrap->>Target: Rollback ย้าย .old กลับมาเป็นตัวหลัก end end rect rgb(60, 30, 30) Note over Bootstrap,TempDir: 5. ล้างและทำลายโฟลเดอร์ชั่วคราว (finally / defer) Bootstrap->>TempDir: ลบโฟลเดอร์ TempDir ทั้งหมด (Purge 100%) end Bootstrap->>Target: 6. รัน G-Launcher.exe เวอร์ชันใหม่ ``` --- ## 🛠️ ไฟล์ที่เกี่ยวข้องและการทำงาน ### 1. [`update-and-build.bat`](../update-and-build.bat) * สคริปต์ Batch สั้นๆ สำหรับดับเบิลคลิกบน Windows * ตั้งค่า UTF-8 (`chcp 65001`) และส่งต่อการทำงานไปยัง `scripts/bootstrap.ps1` ผ่านคำสั่ง: ```cmd powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\bootstrap.ps1" %* ``` ### 2. [`scripts/bootstrap.ps1`](../scripts/bootstrap.ps1) * **การตรวจหาและดาวน์โหลดเครื่องมือพกพา (Prerequisites):** * `Git`: หากในเครื่องไม่มี จะดาวน์โหลด **Portable MinGit** มาไว้ที่ `tools/mingit/` * `Go`: หากไม่มี Go compiler จะดาวน์โหลด **Portable Go 1.24** มาไว้ที่ `tools/go/` * `Node.js & npm`: หากไม่มี Node.js จะดาวน์โหลด **Portable Node.js v22** มาไว้ที่ `tools/node/` * `Wails v3 CLI`: ติดตั้งผ่าน `go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha.74` * **Isolated Temp Build:** * ใช้โฟลเดอร์ `$tempBuildDir = Join-Path $rootDir "temp\build_workspace"` * สั่ง `git clone --depth 1 --branch Dev $tempBuildDir` * รัน `npm install`, `npm run build`, และ `go build` ภายใน Temp * **Atomic Replacement & Rollback:** * เปลี่ยนชื่อ `bin/G-Launcher.exe` เดิมเป็น `.old` * ย้ายตัวใหม่เข้ามาแทนที่ หากล้มเหลวจะคืนค่า `.old` ทันที * **Guaranteed Cleanup:** * วางคำสั่งลบโฟลเดอร์ชั่วคราวไว้ในบล็อก `finally { ... }` เพื่อรับประกันว่าจะถูกลบออก 100% แม้สคริปต์จะหยุดทำงานกลางคัน ### 3. Go Backend: [`internal/git-service/launcher.go`](../internal/git-service/launcher.go) * ฟังก์ชัน [`BuildAndUpdateFromSource(branch string)`](../internal/git-service/launcher.go#L95) ใน `GitService`: ```go // 1. สร้างโฟลเดอร์ชั่วคราว tempDir, err := os.MkdirTemp("", "g-launcher-build-*") defer os.RemoveAll(tempDir) // รับประกันการลบโฟลเดอร์ Temp เสมอ // 2. Clone โค้ดต้นทาง cloneCmd := exec.Command(gitExe, "clone", "--depth", "1", "--branch", branch, constant.LauncherRepoURL, tempDir) ... // 3. Build ใน Temp ... // 4. สลับไฟล์ที่กำลังรันอยู่ด้วย selfupdate.Apply err = selfupdate.Apply(builtFile, selfupdate.Options{}) ```