7.3 KiB
7.3 KiB
🚀 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)
- ป้องกัน Windows File Lock: ระบบปฏิบัติการ Windows จะล็อกไฟล์
.exeที่กำลังทำงานอยู่ทันที ทำให้ไม่สามารถเขียนทับ (overwrite) ตัวเองได้โดยตรง แต่ Windows อนุญาตให้ เปลี่ยนชื่อ (Rename) ไฟล์ executable ที่กำลังทำงานอยู่ได้ - รักษาความสะอาดของโปรเจกต์ (Zero Pollution): ไม่ทำการ build หรือ pull โค้ดทับโฟลเดอร์ทำงานหลักโดยตรง เพื่อป้องกันไฟล์ขยะหรือ node_modules ปนเปื้อน
- Atomic Rollback: หากกระบวนการคอมไพล์ใน Temp ล้มเหลว โปรแกรมเวอร์ชันเดิมจะยังคงทำงานได้สมบูรณ์ ไม่เสียหาย
🔄 วงจรชีวิตของระบบอัปเดต (Updater Lifecycle)
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
- สคริปต์ Batch สั้นๆ สำหรับดับเบิลคลิกบน Windows
- ตั้งค่า UTF-8 (
chcp 65001) และส่งต่อการทำงานไปยังscripts/bootstrap.ps1ผ่านคำสั่ง:powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\bootstrap.ps1" %*
2. 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 <RepoUrl> $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
- ฟังก์ชัน
BuildAndUpdateFromSource(branch string)ในGitService:// 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{})