Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 42 additions & 26 deletions .github/workflows/desktop_gui_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
publish:
description: "Publish GitHub release"
required: false
default: true
default: false
type: boolean

permissions:
Expand All @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down
11 changes: 7 additions & 4 deletions cmp/bundle_for_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions cmp/desktopApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,25 @@ import java.io.File
import kotlinx.serialization.json.Json
import com.darius.lionvpn.ui.model.SavedConfig

fun loadSavedConfig(): Pair<String, String> {
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
Loading