diff --git a/.github/workflows/desktop_gui_release.yml b/.github/workflows/desktop_gui_release.yml index 29edc343..d708d75c 100644 --- a/.github/workflows/desktop_gui_release.yml +++ b/.github/workflows/desktop_gui_release.yml @@ -16,7 +16,7 @@ on: publish: description: "Publish GitHub release" required: false - default: true + default: false type: boolean permissions: @@ -32,13 +32,16 @@ jobs: include: - os: windows-latest platform: windows - archive-name: lion-vpn-windows.zip + artifact-name: lion-vpn-windows + upload-path: cmp/desktopApp/build/compose/binaries/main/app/lion-vpn - os: ubuntu-latest platform: linux - archive-name: lion-vpn-linux.tar.gz + artifact-name: lion-vpn-linux + upload-path: cmp/desktopApp/build/compose/binaries/main/app/lion-vpn - os: macos-latest platform: macos - archive-name: lion-vpn-macos.zip + artifact-name: lion-vpn-macos + upload-path: cmp/desktopApp/build/compose/binaries/main/app/lion-vpn.app steps: - name: Checkout Repository @@ -62,35 +65,24 @@ jobs: python -m pip install --upgrade pip python -m pip install -r requirements.txt pyinstaller - - name: Bundle Python Executable into Compose Resources - shell: bash - run: | - python cmp/bundle_for_gui.py --force - - - name: Build Desktop Application Distributable + - name: Bundle Python Executable shell: bash working-directory: cmp run: | chmod +x gradlew - ./gradlew :desktopApp:createDistributableForCurrentOS --no-daemon + ./gradlew :desktopApp:bundlePythonExecutable --no-daemon - - name: Compress Distributable + - name: Build Desktop Application Distributable shell: bash + working-directory: cmp run: | - if [ "${{ matrix.platform }}" = "windows" ]; then - powershell -Command "Compress-Archive -Path cmp/desktopApp/build/compose/binaries/main/app -DestinationPath ${{ matrix.archive-name }}" - elif [ "${{ matrix.platform }}" = "linux" ]; then - tar -czf ${{ matrix.archive-name }} -C cmp/desktopApp/build/compose/binaries/main app - elif [ "${{ matrix.platform }}" = "macos" ]; then - cd cmp/desktopApp/build/compose/binaries/main/app - zip -r -y ../../../../../../../${{ matrix.archive-name }} lion-vpn.app - fi + ./gradlew :desktopApp:createDistributable --no-daemon - - name: Upload Compressed Distributable Artifact + - name: Upload Raw Distributable Artifact uses: actions/upload-artifact@v4 with: - name: gui-${{ matrix.platform }} - path: ${{ matrix.archive-name }} + name: ${{ matrix.artifact-name }} + path: ${{ matrix.upload-path }} if-no-files-found: error publish-gui-release: @@ -123,11 +115,35 @@ jobs: with: path: final-gui-assets - - name: Flatten directories + - name: Prepare and Compress Assets for Release run: | mkdir -p release-dist - find final-gui-assets -type f -exec cp {} release-dist/ \; - echo "Files to publish:" + + # Compress Windows + mkdir -p temp-win + mv final-gui-assets/lion-vpn-windows temp-win/lion-vpn + cd temp-win + zip -r ../release-dist/lion-vpn-windows.zip lion-vpn + cd .. + rm -rf temp-win + + # Compress Linux + mkdir -p temp-linux + mv final-gui-assets/lion-vpn-linux temp-linux/lion-vpn + cd temp-linux + tar -czf ../release-dist/lion-vpn-linux.tar.gz lion-vpn + cd .. + rm -rf temp-linux + + # Compress macOS + mkdir -p temp-mac + mv final-gui-assets/lion-vpn-macos temp-mac/lion-vpn.app + cd temp-mac + zip -r -y ../release-dist/lion-vpn-macos.zip lion-vpn.app + cd .. + rm -rf temp-mac + + echo "Compressed release files:" ls -lah release-dist - name: Create GitHub Release diff --git a/cmp/bundle_for_gui.py b/cmp/bundle_for_gui.py index aa55778b..fde93f71 100644 --- a/cmp/bundle_for_gui.py +++ b/cmp/bundle_for_gui.py @@ -128,10 +128,9 @@ def main() -> int: log(f"Target path: {dest_path}") # Copy config.example.json to common resources so it is always packaged - common_resources_dir = repo_root / "cmp" / "desktopApp" / "src" / "main" / "resources" - common_resources_dir.mkdir(parents=True, exist_ok=True) + dest_dir.mkdir(parents=True, exist_ok=True) example_src = repo_root / "config.example.json" - example_dest = common_resources_dir / "config.example.json" + example_dest = dest_dir / "config.example.json" if example_src.exists(): log(f"Copying config.example.json to {example_dest}") shutil.copy2(example_src, example_dest) @@ -174,9 +173,13 @@ def main() -> int: ] try: - subprocess.run(cmd, check=True) + subprocess.run(cmd, check=True, capture_output=True, text=True) except subprocess.CalledProcessError as e: error(f"PyInstaller execution failed: {e}") + print("--- PYINSTALLER STDOUT ---", file=sys.stderr) + print(e.stdout, file=sys.stderr) + print("--- PYINSTALLER STDERR ---", file=sys.stderr) + print(e.stderr, file=sys.stderr) return 1 # 5. Move executable to resources diff --git a/cmp/desktopApp/build.gradle.kts b/cmp/desktopApp/build.gradle.kts index 537d23ac..1cfba4b5 100644 --- a/cmp/desktopApp/build.gradle.kts +++ b/cmp/desktopApp/build.gradle.kts @@ -31,9 +31,9 @@ compose.desktop { jvmArgs("-Dsun.awt.wmclass=lion-vpn") nativeDistributions { - targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) packageName = "lion-vpn" - packageVersion = rootProject.extra["versionName"] as String + val rawVersion = rootProject.extra["versionName"] as String + packageVersion = rawVersion.split("-")[0] appResourcesRootDir.set(project.layout.projectDirectory.dir("src/main/resources")) macOS { diff --git a/cmp/desktopApp/src/main/kotlin/com/darius/lionvpn/ConfigProvider.kt b/cmp/desktopApp/src/main/kotlin/com/darius/lionvpn/ConfigProvider.kt index c2f8f21a..829b02aa 100644 --- a/cmp/desktopApp/src/main/kotlin/com/darius/lionvpn/ConfigProvider.kt +++ b/cmp/desktopApp/src/main/kotlin/com/darius/lionvpn/ConfigProvider.kt @@ -5,67 +5,25 @@ import java.io.File import kotlinx.serialization.json.Json import com.darius.lionvpn.ui.model.SavedConfig -fun loadSavedConfig(): Pair { - return try { - val binaryPath = getPythonExecutablePath() - val binaryFile = File(binaryPath) - val configFile = if (binaryFile.parentFile != null) { - File(binaryFile.parentFile, "config.json") - } else { - File(findResourcesDir(), "config.json") - } - - if (configFile.exists()) { - val content = configFile.readText(Charsets.UTF_8) - - // Clean extraction of script_id and auth_key values using simple regex - val scriptIdRegex = "\"script_id\"\\s*:\\s*\"([^\"]+)\"".toRegex() - val authKeyRegex = "\"auth_key\"\\s*:\\s*\"([^\"]+)\"".toRegex() - - val scriptId = scriptIdRegex.find(content)?.groupValues?.get(1) ?: "" - val authKey = authKeyRegex.find(content)?.groupValues?.get(1) ?: "" - - // Filter out default placeholder values to keep text inputs clean - val filteredScriptId = if (scriptId == "YOUR_APPS_SCRIPT_DEPLOYMENT_ID") "" else scriptId - val filteredAuthKey = if (authKey == "CHANGE_ME_TO_A_STRONG_SECRET") "" else authKey - - return Pair(filteredScriptId, filteredAuthKey) - } - throw IOException("File does not exist") - } catch (e: IOException) { - println("[Config JVM] Error loading saved config: ${e.message}") - Pair("", "") - } -} - fun saveConfigLocally(deploymentId: String, authKey: String): Boolean { return try { - val resourcesDir = findResourcesDir() - val exampleFile = File(resourcesDir, "config.example.json") + val root = findResourcesDir() + var exampleFile = File(root, "config.example.json") + if (!exampleFile.exists()) { + exampleFile = File(findRepoRoot(), "config.example.json") + } val binaryPath = getPythonExecutablePath() val binaryFile = File(binaryPath) - // Write config.json in the exact same platform-specific directory containing the python executable - val configFile = if (binaryFile.parentFile != null) { - File(binaryFile.parentFile, "config.json") - } else { - File(resourcesDir, "config.json") - } - - // If config.example.json doesn't exist in resourcesDir, check fallback for dev mode - val resolvedExampleFile = if (exampleFile.exists()) { - exampleFile - } else { - File(findRepoRoot(), "config.example.json") - } + val configFile = File(binaryFile.parentFile, "config.json") - if (!resolvedExampleFile.exists()) { + if (!exampleFile.exists()) { println("[Config JVM] ERROR: config.example.json not found!") return false } - var content = resolvedExampleFile.readText(Charsets.UTF_8) + var content = exampleFile.readText(Charsets.UTF_8) // Safe drop-in replacements for the script ID and Auth Key placeholders content = content.replace("YOUR_APPS_SCRIPT_DEPLOYMENT_ID", deploymentId) diff --git a/cmp/desktopApp/src/main/kotlin/com/darius/lionvpn/PythonProvider.kt b/cmp/desktopApp/src/main/kotlin/com/darius/lionvpn/PythonProvider.kt index 5f3654db..ec8c1244 100644 --- a/cmp/desktopApp/src/main/kotlin/com/darius/lionvpn/PythonProvider.kt +++ b/cmp/desktopApp/src/main/kotlin/com/darius/lionvpn/PythonProvider.kt @@ -1,19 +1,12 @@ package com.darius.lionvpn +import kotlinx.io.files.FileNotFoundException import java.io.File -private const val DIR_LEVEL = 4 fun findRepoRoot(): File { - var dir = File(System.getProperty("user.dir")).absoluteFile - var dirLevel = DIR_LEVEL - while (dirLevel > 0) { - dirLevel-- - if (File(dir, "config.example.json").exists()) { - return dir - } - dir = dir.parentFile ?: break - } - return File(System.getProperty("user.dir")).absoluteFile + val dir = File(System.getProperty("user.dir")).parentFile + return if (File(dir, "config.example.json").exists()) dir + else throw FileNotFoundException() } fun findResourcesDir(): File {