fix(updater): build in temp directory, replace old launcher binary, and cleanup temp

This commit is contained in:
CAtlas0784 committed 2026-10-30 21:31:53 +07:00
1 parent 0ec9f62857
commit 0a97749a1c
8 files changed
+281 -98

No files matched your search

@@ -10,7 +10,7 @@ import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Cr
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function CloseApp() {
return $Call.ByID(3422460836);
return $Call.ByID(1501559548);
}
/**
@@ -18,40 +18,40 @@ export function CloseApp() {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function CloseAppAfterTimeout(timeout) {
return $Call.ByID(1705931481, timeout);
return $Call.ByID(1916027393, timeout);
}
/**
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function GetCurrentLauncherVersion() {
return $Call.ByID(3575133982);
return $Call.ByID(1802499222);
}
/**
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function HideApp() {
return $Call.ByID(88003266);
return $Call.ByID(2987475338);
}
/**
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function MaximizeApp() {
return $Call.ByID(1257306588);
return $Call.ByID(1960157700);
}
/**
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function MinimizeApp() {
return $Call.ByID(3434614194);
return $Call.ByID(227494378);
}
/**
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function RestoreApp() {
return $Call.ByID(3115625834);
return $Call.ByID(229693554);
}
@@ -6,12 +6,21 @@
// @ts-ignore: Unused imports
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
/**
* @param {string} gamePath
* @param {string} patchPath
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function ApplyDiffZiff(gamePath, patchPath) {
return $Call.ByID(3398476317, gamePath, patchPath);
}
/**
* @param {string} patchPath
* @returns {$CancellablePromise<[boolean, string, string]>}
*/
export function CheckTypeHDiff(patchPath) {
return $Call.ByID(3717449114, patchPath);
return $Call.ByID(2449959810, patchPath);
}
/**
@@ -19,7 +28,7 @@ export function CheckTypeHDiff(patchPath) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function CutData(gamePath) {
return $Call.ByID(2019290107, gamePath);
return $Call.ByID(279510819, gamePath);
}
/**
@@ -28,7 +37,7 @@ export function CutData(gamePath) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function DataExtract(gamePath, patchPath) {
return $Call.ByID(2161622254, gamePath, patchPath);
return $Call.ByID(3151294534, gamePath, patchPath);
}
/**
@@ -36,7 +45,7 @@ export function DataExtract(gamePath, patchPath) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function DeleteFiles(gamePath) {
return $Call.ByID(1103091613, gamePath);
return $Call.ByID(308950549, gamePath);
}
/**
@@ -44,7 +53,7 @@ export function DeleteFiles(gamePath) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function HDiffPatchData(gamePath) {
return $Call.ByID(3944051994, gamePath);
return $Call.ByID(4008196450, gamePath);
}
/**
@@ -52,7 +61,7 @@ export function HDiffPatchData(gamePath) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function LDiffPatchData(gamePath) {
return $Call.ByID(360123238, gamePath);
return $Call.ByID(2041639182, gamePath);
}
/**
@@ -61,5 +70,5 @@ export function LDiffPatchData(gamePath) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function VersionValidate(gamePath, patchPath) {
return $Call.ByID(2105077257, gamePath, patchPath);
return $Call.ByID(169848129, gamePath, patchPath);
}
@@ -11,7 +11,7 @@ import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Cr
* @returns {$CancellablePromise<boolean>}
*/
export function DirExists(path) {
return $Call.ByID(1772289644, path);
return $Call.ByID(4102029604, path);
}
/**
@@ -19,7 +19,7 @@ export function DirExists(path) {
* @returns {$CancellablePromise<boolean>}
*/
export function FileExists(path) {
return $Call.ByID(1782610747, path);
return $Call.ByID(865459, path);
}
/**
@@ -28,7 +28,7 @@ export function FileExists(path) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function FileExistsInZip(archivePath, fileInside) {
return $Call.ByID(2509699047, archivePath, fileInside);
return $Call.ByID(3506095695, archivePath, fileInside);
}
/**
@@ -36,7 +36,14 @@ export function FileExistsInZip(archivePath, fileInside) {
* @returns {$CancellablePromise<string>}
*/
export function GetDir(path) {
return $Call.ByID(1744445742, path);
return $Call.ByID(1567262998, path);
}
/**
* @returns {$CancellablePromise<[string, string]>}
*/
export function GetSavedGameSelection() {
return $Call.ByID(858061230);
}
/**
@@ -44,7 +51,7 @@ export function GetDir(path) {
* @returns {$CancellablePromise<string>}
*/
export function Join(...paths) {
return $Call.ByID(2460588289, paths);
return $Call.ByID(3036412633, paths);
}
/**
@@ -52,7 +59,7 @@ export function Join(...paths) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function OpenFolder(path) {
return $Call.ByID(1635714001, path);
return $Call.ByID(1601714793, path);
}
/**
@@ -60,14 +67,23 @@ export function OpenFolder(path) {
* @returns {$CancellablePromise<string>}
*/
export function PickFile(filter) {
return $Call.ByID(3756474934, filter);
return $Call.ByID(2493433102, filter);
}
/**
* @returns {$CancellablePromise<string>}
*/
export function PickFolder() {
return $Call.ByID(3654471460);
return $Call.ByID(1673632540);
}
/**
* @param {string} gameDir
* @param {string} fileName
* @returns {$CancellablePromise<string>}
*/
export function ReadConfigFile(gameDir, fileName) {
return $Call.ByID(3834168665, gameDir, fileName);
}
/**
@@ -75,7 +91,16 @@ export function PickFolder() {
* @returns {$CancellablePromise<void>}
*/
export function RemoveFile(path) {
return $Call.ByID(3206735043, path);
return $Call.ByID(2698409755, path);
}
/**
* @param {string} gamePath
* @param {string} gameDir
* @returns {$CancellablePromise<void>}
*/
export function SaveGameSelection(gamePath, gameDir) {
return $Call.ByID(1414185756, gamePath, gameDir);
}
/**
@@ -83,7 +108,7 @@ export function RemoveFile(path) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function StartApp(path) {
return $Call.ByID(1267568402, path);
return $Call.ByID(2875051690, path);
}
/**
@@ -91,5 +116,15 @@ export function StartApp(path) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function StartWithConsole(path) {
return $Call.ByID(3249271428, path);
return $Call.ByID(3393986364, path);
}
/**
* @param {string} gameDir
* @param {string} fileName
* @param {string} content
* @returns {$CancellablePromise<void>}
*/
export function WriteConfigFile(gameDir, fileName, content) {
return $Call.ByID(1143842060, gameDir, fileName, content);
}
@@ -6,12 +6,20 @@
// @ts-ignore: Unused imports
import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime";
/**
* @param {string} branch
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function BuildAndUpdateFromSource(branch) {
return $Call.ByID(4215502398, branch);
}
/**
* @param {string} version
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function DownloadProxyProgress(version) {
return $Call.ByID(3559275334, version);
return $Call.ByID(681315470, version);
}
/**
@@ -19,35 +27,35 @@ export function DownloadProxyProgress(version) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function DownloadServerProgress(version) {
return $Call.ByID(1954767259, version);
return $Call.ByID(1818966979, version);
}
/**
* @returns {$CancellablePromise<[boolean, string, string]>}
*/
export function GetLatestLauncherVersion() {
return $Call.ByID(262637602);
return $Call.ByID(977883722);
}
/**
* @returns {$CancellablePromise<[boolean, string, string]>}
*/
export function GetLatestProxyVersion() {
return $Call.ByID(289488362);
return $Call.ByID(2666043362);
}
/**
* @returns {$CancellablePromise<[boolean, string, string]>}
*/
export function GetLatestServerVersion() {
return $Call.ByID(2918980975);
return $Call.ByID(1731469511);
}
/**
* @returns {$CancellablePromise<void>}
*/
export function UnzipServer() {
return $Call.ByID(1126363284);
return $Call.ByID(2625719532);
}
/**
@@ -55,5 +63,5 @@ export function UnzipServer() {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function UpdateLauncherProgress(version) {
return $Call.ByID(97272861, version);
return $Call.ByID(1270188021, version);
}
@@ -11,7 +11,16 @@ import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Cr
* @returns {$CancellablePromise<[boolean, string, string, string]>}
*/
export function GetLanguage(path) {
return $Call.ByID(3574191687, path);
return $Call.ByID(2603098415, path);
}
/**
* @param {string} gameRoot
* @param {string} packageRoot
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function InstallThaiPatch(gameRoot, packageRoot) {
return $Call.ByID(1436799318, gameRoot, packageRoot);
}
/**
@@ -21,5 +30,5 @@ export function GetLanguage(path) {
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function SetLanguage(path, text, voice) {
return $Call.ByID(2768939795, path, text, voice);
return $Call.ByID(1811821051, path, text, voice);
}
+68 -1
View File
@@ -10,6 +10,8 @@ import (
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
@@ -87,7 +89,72 @@ func (g *GitService) UpdateLauncherProgress(version string) (bool, string) {
}
}
return false, "could not locate valid launcher update package"
// Mode 3: Build from source inside a temporary workspace, replace binary, and delete temp
return g.BuildAndUpdateFromSource(constant.LauncherBranch)
}
func (g *GitService) BuildAndUpdateFromSource(branch string) (bool, string) {
if branch == "" {
branch = constant.LauncherBranch
}
gitExe, err := ensureGit()
if err != nil {
return false, fmt.Sprintf("git is required for source update: %v", err)
}
// 1. Create clean temporary directory
tempDir, err := os.MkdirTemp("", "g-launcher-build-*")
if err != nil {
return false, fmt.Sprintf("create temp build directory: %v", err)
}
defer os.RemoveAll(tempDir) // Guarantee temporary directory is completely deleted
// 2. Clone repository into temp directory
cloneCmd := exec.Command(gitExe, "clone", "--depth", "1", "--branch", branch, constant.LauncherRepoURL, tempDir)
cloneCmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
if out, err := cloneCmd.CombinedOutput(); err != nil {
return false, fmt.Sprintf("clone repository to temp: %v (%s)", err, strings.TrimSpace(string(out)))
}
// 3. Build frontend inside temp
frontendDir := filepath.Join(tempDir, "frontend")
npmInstall := exec.Command("npm", "install", "--no-audit", "--no-fund")
npmInstall.Dir = frontendDir
if out, err := npmInstall.CombinedOutput(); err != nil {
return false, fmt.Sprintf("npm install in temp: %v (%s)", err, strings.TrimSpace(string(out)))
}
npmBuild := exec.Command("npm", "run", "build")
npmBuild.Dir = frontendDir
if out, err := npmBuild.CombinedOutput(); err != nil {
return false, fmt.Sprintf("npm run build in temp: %v (%s)", err, strings.TrimSpace(string(out)))
}
// 4. Build Go binary inside temp
builtExePath := filepath.Join(tempDir, "bin", "G-Launcher.exe")
if err := os.MkdirAll(filepath.Dir(builtExePath), 0755); err != nil {
return false, fmt.Sprintf("create temp bin directory: %v", err)
}
goBuild := exec.Command("go", "build", "-tags", "production", "-trimpath", "-buildvcs=false", "-ldflags=-w -s -H windowsgui", "-o", builtExePath, ".")
goBuild.Dir = tempDir
if out, err := goBuild.CombinedOutput(); err != nil {
return false, fmt.Sprintf("go build in temp: %v (%s)", err, strings.TrimSpace(string(out)))
}
// 5. Replace current running binary with newly built binary
builtFile, err := os.Open(builtExePath)
if err != nil {
return false, fmt.Sprintf("open built launcher in temp: %v", err)
}
defer builtFile.Close()
if err := selfupdate.Apply(builtFile, selfupdate.Options{}); err != nil {
return false, fmt.Sprintf("apply launcher update: %v", err)
}
return true, ""
}
func (g *GitService) applyManifestUpdate(manifest launcherManifest) (bool, string) {
+3
View File
@@ -5,6 +5,9 @@ const ServerGitUrl = "https://git.kain.io.vn/api/v1/repos/Firefly-Shelter/Firefl
const LauncherGitUrl = "https://gaytea.cod-algieba.ts.net/api/v1/repos/GaCy/G-Launcher/releases"
const LauncherManifestURL = "https://gaytea.cod-algieba.ts.net/GaCy/G-Launcher/releases/download/latest/latest.json"
const LauncherDownloadURL = "https://gaytea.cod-algieba.ts.net/GaCy/G-Launcher/releases/download/latest/G-Launcher.exe"
const LauncherRepoURL = "https://gaytea.cod-algieba.ts.net/GaCy/G-Launcher.git"
const LauncherBranch = "Dev"
const LauncherBuildTempDir = "temp/updater_build"
const ServerStorageUrl = "./server"
const ProxyStorageUrl = "./proxy"
+115 -63
View File
@@ -1,5 +1,6 @@
# ==============================================================================
# G-Launcher Auto-Updater & Environment Bootstrap Script
# Architecture: Download to Temp -> Build in Temp -> Replace Old -> Delete Temp
# ==============================================================================
param(
[string]$Branch = "Dev",
@@ -13,7 +14,11 @@ $ErrorActionPreference = "Stop"
$rootDir = Split-Path -Parent $PSScriptRoot
Set-Location $rootDir
$toolsDir = Join-Path $rootDir "tools"
$binDir = Join-Path $rootDir "bin"
$targetExe = Join-Path $binDir "G-Launcher.exe"
if (!(Test-Path $toolsDir)) { New-Item -ItemType Directory -Path $toolsDir -Force | Out-Null }
if (!(Test-Path $binDir)) { New-Item -ItemType Directory -Path $binDir -Force | Out-Null }
Write-Host "==========================================================" -ForegroundColor Cyan
Write-Host " G-Launcher: Auto-Update & Local Build System" -ForegroundColor Cyan
@@ -123,79 +128,126 @@ if (-not $wailsCmd) {
Write-Host " -> Wails v3 CLI is ready" -ForegroundColor Gray
# ------------------------------------------------------------------------------
# 5. Git Pull latest code
# 5. Download to Temp -> Build in Temp -> Replace Old -> Delete Temp
# ------------------------------------------------------------------------------
Write-Host "[5/6] Syncing latest code from branch '$Branch'..." -ForegroundColor White
$env:GIT_TERMINAL_PROMPT = "0"
if (Test-Path (Join-Path $rootDir ".git")) {
try {
& git fetch origin $Branch --progress
& git checkout $Branch
$pullOutput = & git pull origin $Branch
Write-Host " -> $pullOutput" -ForegroundColor Gray
} catch {
Write-Host "Warning: Could not pull latest changes (offline or local modifications). Proceeding with current local code." -ForegroundColor DarkYellow
}
} else {
Write-Host ">> Cloning repository from $RepoUrl..." -ForegroundColor Yellow
& git clone --branch $Branch $RepoUrl .
}
# ------------------------------------------------------------------------------
# 6. Build Project (Frontend + Go Backend)
# ------------------------------------------------------------------------------
Write-Host "[6/6] Compiling G-Launcher..." -ForegroundColor White
Write-Host " [6.1] Installing Frontend dependencies..." -ForegroundColor Gray
& npm --prefix frontend install --no-audit --no-fund
Write-Host " [6.2] Building React 19 Frontend..." -ForegroundColor Gray
& npm --prefix frontend run build
Write-Host " [6.3] Running go mod tidy..." -ForegroundColor Gray
& go mod tidy
$binDir = Join-Path $rootDir "bin"
if (!(Test-Path $binDir)) { New-Item -ItemType Directory -Path $binDir -Force | Out-Null }
$outputExe = Join-Path $binDir "G-Launcher.exe"
$sysoPath = Join-Path $rootDir "wails_windows_amd64.syso"
$tempBuildDir = Join-Path $rootDir "temp\build_workspace"
$tempExe = Join-Path $tempBuildDir "bin\G-Launcher.exe"
try {
Write-Host " [6.4] Generating Windows Manifest & Syso..." -ForegroundColor Gray
if (Get-Command wails3 -ErrorAction SilentlyContinue) {
& wails3 generate syso -arch amd64 -icon build/windows/icon.ico -manifest build/windows/wails.exe.manifest -info build/windows/info.json -out $sysoPath
Write-Host "[5/6] Preparing clean temporary workspace..." -ForegroundColor White
if (Test-Path $tempBuildDir) {
Remove-Item -Path $tempBuildDir -Recurse -Force -ErrorAction SilentlyContinue
}
} catch {
Write-Host "Warning: Syso generation skipped, building executable without embedded syso." -ForegroundColor DarkYellow
}
New-Item -ItemType Directory -Path $tempBuildDir -Force | Out-Null
Write-Host " [6.5] Compiling Go backend binary..." -ForegroundColor Gray
& go build -tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui" -o $outputExe .
Write-Host ">> Downloading latest source code from branch '$Branch' into temp..." -ForegroundColor Yellow
$env:GIT_TERMINAL_PROMPT = "0"
$cloned = $false
try {
& git clone --depth 1 --branch $Branch $RepoUrl $tempBuildDir
$cloned = $true
} catch {
Write-Host "Warning: Remote clone failed. Attempting local repository clone..." -ForegroundColor DarkYellow
}
if (Test-Path $sysoPath) {
Remove-Item -Path $sysoPath -Force -ErrorAction SilentlyContinue
}
if (-not $cloned -and (Test-Path (Join-Path $rootDir ".git"))) {
& git clone --depth 1 --branch $Branch $rootDir $tempBuildDir
$cloned = $true
}
if ($LASTEXITCODE -ne 0 -or !(Test-Path $outputExe)) {
if (-not $cloned) {
throw "Failed to download repository source into temporary directory!"
}
Write-Host "[6/6] Building new G-Launcher in temporary workspace..." -ForegroundColor White
Push-Location $tempBuildDir
Write-Host " [6.1] Installing Frontend dependencies in temp..." -ForegroundColor Gray
& npm --prefix frontend install --no-audit --no-fund
Write-Host " [6.2] Compiling React 19 Frontend in temp..." -ForegroundColor Gray
& npm --prefix frontend run build
Write-Host " [6.3] Running go mod tidy in temp..." -ForegroundColor Gray
& go mod tidy
$tempBinDir = Join-Path $tempBuildDir "bin"
if (!(Test-Path $tempBinDir)) { New-Item -ItemType Directory -Path $tempBinDir -Force | Out-Null }
$sysoPath = Join-Path $tempBuildDir "wails_windows_amd64.syso"
try {
Write-Host " [6.4] Generating Windows Manifest & Syso in temp..." -ForegroundColor Gray
if (Get-Command wails3 -ErrorAction SilentlyContinue) {
& wails3 generate syso -arch amd64 -icon build/windows/icon.ico -manifest build/windows/wails.exe.manifest -info build/windows/info.json -out $sysoPath
}
} catch {
Write-Host "Warning: Syso generation skipped, building executable without embedded syso." -ForegroundColor DarkYellow
}
Write-Host " [6.5] Compiling Go backend binary in temp..." -ForegroundColor Gray
& go build -tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui" -o $tempExe .
if (Test-Path $sysoPath) {
Remove-Item -Path $sysoPath -Force -ErrorAction SilentlyContinue
}
if ($LASTEXITCODE -ne 0 -or !(Test-Path $tempExe)) {
throw "Compilation inside temporary workspace failed!"
}
$upxCmd = Get-Command upx -ErrorAction SilentlyContinue
if ($upxCmd) {
Write-Host " [6.6] Compressing binary with UPX in temp..." -ForegroundColor Gray
& upx --best --lzma $tempExe
}
Pop-Location
# --------------------------------------------------------------------------
# Replace old executable with newly built binary (with rollback protection)
# --------------------------------------------------------------------------
Write-Host ">> Replacing old launcher binary with new build..." -ForegroundColor Cyan
$backupExe = "$targetExe.old"
if (Test-Path $targetExe) {
if (Test-Path $backupExe) {
Remove-Item -Path $backupExe -Force -ErrorAction SilentlyContinue
}
Rename-Item -LiteralPath $targetExe -NewName (Split-Path -Leaf $backupExe) -Force
try {
Move-Item -LiteralPath $tempExe -Destination $targetExe -Force
Remove-Item -LiteralPath $backupExe -Force -ErrorAction SilentlyContinue
} catch {
Write-Host "Error during replacement! Rolling back..." -ForegroundColor Red
if (Test-Path $backupExe) {
Move-Item -LiteralPath $backupExe -Destination $targetExe -Force -ErrorAction SilentlyContinue
}
throw "Replacement failed and was rolled back: $_"
}
} else {
Move-Item -LiteralPath $tempExe -Destination $targetExe -Force
}
$exeSize = (Get-Item $targetExe).Length / 1MB
Write-Host ""
Write-Host "==========================================================" -ForegroundColor Green
Write-Host ("Build & Update Successful! File: bin/G-Launcher.exe ({0:N2} MB)" -f $exeSize) -ForegroundColor Green
Write-Host "==========================================================" -ForegroundColor Green
Write-Host ""
Write-Host "[ERROR] Compilation failed!" -ForegroundColor Red
exit 1
}
$upxCmd = Get-Command upx -ErrorAction SilentlyContinue
if ($upxCmd) {
Write-Host " [6.6] Compressing binary with UPX..." -ForegroundColor Gray
& upx --best --lzma $outputExe
} finally {
# --------------------------------------------------------------------------
# Always delete temporary workspace
# --------------------------------------------------------------------------
Set-Location $rootDir
if (Test-Path $tempBuildDir) {
Write-Host "🧹 Cleaning up and deleting temporary workspace ($tempBuildDir)..." -ForegroundColor Yellow
Remove-Item -LiteralPath $tempBuildDir -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "[OK] Temporary workspace deleted!" -ForegroundColor Green
}
}
$exeSize = (Get-Item $outputExe).Length / 1MB
Write-Host ""
Write-Host "==========================================================" -ForegroundColor Green
Write-Host ("Build Successful! Output: bin/G-Launcher.exe ({0:N2} MB)" -f $exeSize) -ForegroundColor Green
Write-Host "==========================================================" -ForegroundColor Green
Write-Host ""
if (-not $NoLaunch) {
Write-Host "Launching G-Launcher..." -ForegroundColor Cyan
Start-Process -FilePath $outputExe
Write-Host "🎮 Launching G-Launcher..." -ForegroundColor Cyan
Start-Process -FilePath $targetExe
}