Files
G-Launcher/Doc/UPDATER_SYSTEM.md
T

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)

  1. ป้องกัน Windows File Lock: ระบบปฏิบัติการ Windows จะล็อกไฟล์ .exe ที่กำลังทำงานอยู่ทันที ทำให้ไม่สามารถเขียนทับ (overwrite) ตัวเองได้โดยตรง แต่ Windows อนุญาตให้ เปลี่ยนชื่อ (Rename) ไฟล์ executable ที่กำลังทำงานอยู่ได้
  2. รักษาความสะอาดของโปรเจกต์ (Zero Pollution): ไม่ทำการ build หรือ pull โค้ดทับโฟลเดอร์ทำงานหลักโดยตรง เพื่อป้องกันไฟล์ขยะหรือ node_modules ปนเปื้อน
  3. 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{})