diff --git a/build/scripts/linux/build-avalonia-packages.sh b/build/scripts/linux/build-avalonia-packages.sh old mode 100644 new mode 100755 index 6a35814..8a3b868 --- a/build/scripts/linux/build-avalonia-packages.sh +++ b/build/scripts/linux/build-avalonia-packages.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" PROJECT_PATH="$REPO_ROOT/src/GregModmanager.Avalonia/GregModmanager.Avalonia.csproj" OUTPUT_ROOT="${1:-$REPO_ROOT/artifacts/avalonia-linux}" VERSION="${2:-1.1.0}" diff --git a/build/scripts/linux/build-avalonia-packages.sh.orig b/build/scripts/linux/build-avalonia-packages.sh.orig new file mode 100644 index 0000000..cf25f82 --- /dev/null +++ b/build/scripts/linux/build-avalonia-packages.sh.orig @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." BASH_SOURCE[0]}")/../../.." BASH_SOURCE[0]}")/../.." && pwdBASH_SOURCE[0]}")/../.." && pwd pwd)BASH_SOURCE[0]}")/../.." && pwd) pwd)" +PROJECT_PATH="$REPO_ROOT/src/GregModmanager.Avalonia/GregModmanager.Avalonia.csproj" +OUTPUT_ROOT="${1:-$REPO_ROOT/artifacts/avalonia-linux}" +VERSION="${2:-1.1.0}" +IS_PRE="${3:-false}" +RID="linux-x64" + +PRE_SUFFIX="" +if [ "$IS_PRE" = "true" ] || [ "$IS_PRE" = "1" ]; then + PRE_SUFFIX="-pre" +fi + +PUBLISH_DIR="$OUTPUT_ROOT/publish" +PKG_DIR="$OUTPUT_ROOT/packages" +mkdir -p "$PUBLISH_DIR" "$PKG_DIR" + +dotnet publish "$PROJECT_PATH" -c Release -r "$RID" --self-contained true -o "$PUBLISH_DIR" + +tar -C "$PUBLISH_DIR" -czf "$PKG_DIR/gregModmanager-${VERSION}${PRE_SUFFIX}-Linux.tar.gz" . + +NFP_CONFIG="$OUTPUT_ROOT/nfpm.yaml" +cat > "$NFP_CONFIG" < +description: gregModmanager desktop client built with Avalonia UI. +vendor: gregFramework +homepage: https://github.com/mleem97/gregModmanager +license: Proprietary +depends: + - libicu +contents: + - src: ${PUBLISH_DIR}/ + dst: /opt/gregmodmanager/ + - src: ${REPO_ROOT}/build/scripts/linux/gregmodmanager.desktop + dst: /usr/share/applications/gregmodmanager.desktop + file_info: + mode: 0644 + - src: ${REPO_ROOT}/build/scripts/linux/gregmodmanager + dst: /usr/bin/gregmodmanager + file_info: + mode: 0755 +EOF + +build_nfpm() { + local packager="$1" + local target="$2" + + if command -v nfpm >/dev/null 2>&1; then + nfpm package --packager "$packager" --config "$NFP_CONFIG" --target "$target" + return + fi + + echo "nfpm not found in PATH. Install with: echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list && sudo apt update && sudo apt install nfpm" >&2 + exit 1 +} + +build_nfpm deb "$PKG_DIR/gregModmanager-${VERSION}${PRE_SUFFIX}-Linux.deb" +build_nfpm rpm "$PKG_DIR/gregModmanager-${VERSION}${PRE_SUFFIX}-Linux.rpm" +build_nfpm archlinux "$PKG_DIR/gregModmanager-${VERSION}${PRE_SUFFIX}-Linux.pkg.tar.zst" + +echo "Artifacts ready in: $OUTPUT_ROOT" diff --git a/build/scripts/linux/build-avalonia-packages.sh.rej b/build/scripts/linux/build-avalonia-packages.sh.rej new file mode 100644 index 0000000..e0aa946 --- /dev/null +++ b/build/scripts/linux/build-avalonia-packages.sh.rej @@ -0,0 +1,11 @@ +--- build-avalonia-packages.sh ++++ build-avalonia-packages.sh +@@ -1,7 +1,7 @@ + #!/usr/bin/env bash + set -euo pipefail + +-REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" ++REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" + PROJECT_PATH="$REPO_ROOT/src/GregModmanager.Avalonia/GregModmanager.Avalonia.csproj" + OUTPUT_ROOT="${1:-$REPO_ROOT/artifacts/avalonia-linux}" + VERSION="${2:-1.1.0}" diff --git a/global.json b/global.json index c071e6f..badcd44 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.313", + "version": "10.0.103", "rollForward": "minor" } } diff --git a/patch.diff b/patch.diff new file mode 100644 index 0000000..9901d22 --- /dev/null +++ b/patch.diff @@ -0,0 +1,11 @@ +--- build/scripts/linux/build-avalonia-packages.sh ++++ build/scripts/linux/build-avalonia-packages.sh +@@ -1,7 +1,7 @@ + #!/usr/bin/env bash + set -euo pipefail + +-REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" ++REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" + PROJECT_PATH="$REPO_ROOT/src/GregModmanager.Avalonia/GregModmanager.Avalonia.csproj" + OUTPUT_ROOT="${1:-$REPO_ROOT/artifacts/avalonia-linux}" + VERSION="${2:-1.1.0}" diff --git a/src/GregModmanager.Core/Services/SafeProcess.cs b/src/GregModmanager.Core/Services/SafeProcess.cs index 12f4ec5..fc3c425 100644 --- a/src/GregModmanager.Core/Services/SafeProcess.cs +++ b/src/GregModmanager.Core/Services/SafeProcess.cs @@ -1,118 +1,120 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; - -namespace GregModmanager.Services; - -public static class SafeProcess -{ - /// - /// Opens a URL in the default browser safely, ensuring only http and https schemes are allowed. - /// - public static Task OpenUrlAsync(string url) - { - if (string.IsNullOrWhiteSpace(url)) return Task.CompletedTask; - - try - { - if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && - (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) - { - Process.Start(new ProcessStartInfo - { - FileName = uri.ToString(), - UseShellExecute = true - }); - } - else - { - AppFileLog.Warn($"Blocked attempt to open insecure or invalid URL: {url}"); - } - } - catch (Exception ex) - { - AppFileLog.Error($"Failed to open URL: {url}", ex); - } - - return Task.CompletedTask; - } - - /// - /// Opens a folder in the system's file explorer. - /// - public static void OpenFolder(string path) - { - if (string.IsNullOrWhiteSpace(path)) return; - - try - { - if (OperatingSystem.IsWindows()) - { - Process.Start(new ProcessStartInfo - { - FileName = "explorer.exe", - Arguments = $"\"{path}\"", - UseShellExecute = false - }); - } - else - { - // For other OS, we might still need UseShellExecute for some scenarios, - // but we should be careful. MAUI doesn't have a direct "OpenFolder" that works everywhere. - Process.Start(new ProcessStartInfo - { - FileName = path, - UseShellExecute = true - }); - } - } - catch (Exception ex) - { - AppFileLog.Error($"Failed to open folder: {path}", ex); - } - } - - /// - /// Specifically for Windows, opens explorer and selects a file. - /// - public static void OpenExplorerAndSelect(string filePath) - { - if (!OperatingSystem.IsWindows() || string.IsNullOrWhiteSpace(filePath)) return; - - try - { - Process.Start(new ProcessStartInfo - { - FileName = "explorer.exe", - Arguments = $"/select,\"{filePath}\"", - UseShellExecute = false - }); - } - catch (Exception ex) - { - AppFileLog.Error($"Failed to open explorer and select: {filePath}", ex); - } - } - - /// - /// Launches an executable with UseShellExecute = false. - /// - public static void LaunchApp(string exePath, string arguments = "") - { - if (string.IsNullOrWhiteSpace(exePath)) return; - - try - { - Process.Start(new ProcessStartInfo - { - FileName = exePath, - Arguments = arguments, - UseShellExecute = false - }); - } - catch (Exception ex) - { - AppFileLog.Error($"Failed to launch app: {exePath}", ex); - } - } -} +using System; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace GregModmanager.Services; + +public static class SafeProcess +{ + /// + /// Opens a URL in the default browser safely, ensuring only http and https schemes are allowed. + /// + public static Task OpenUrlAsync(string url) + { + if (string.IsNullOrWhiteSpace(url)) return Task.CompletedTask; + + try + { + if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && + (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) + { + Process.Start(new ProcessStartInfo + { + FileName = uri.ToString(), + UseShellExecute = true + }); + } + else + { + AppFileLog.Warn($"Blocked attempt to open insecure or invalid URL: {url}"); + } + } + catch (Exception ex) + { + AppFileLog.Error($"Failed to open URL: {url}", ex); + } + + return Task.CompletedTask; + } + + /// + /// Opens a folder in the system's file explorer. + /// + public static void OpenFolder(string path) + { + if (string.IsNullOrWhiteSpace(path)) return; + + try + { + if (OperatingSystem.IsWindows()) + { + var psi = new ProcessStartInfo + { + FileName = "explorer.exe", + UseShellExecute = false + }; + psi.ArgumentList.Add(path); + Process.Start(psi); + } + else + { + // For other OS, we might still need UseShellExecute for some scenarios, + // but we should be careful. MAUI doesn't have a direct "OpenFolder" that works everywhere. + Process.Start(new ProcessStartInfo + { + FileName = path, + UseShellExecute = true + }); + } + } + catch (Exception ex) + { + AppFileLog.Error($"Failed to open folder: {path}", ex); + } + } + + /// + /// Specifically for Windows, opens explorer and selects a file. + /// + public static void OpenExplorerAndSelect(string filePath) + { + if (!OperatingSystem.IsWindows() || string.IsNullOrWhiteSpace(filePath)) return; + + try + { + var psi = new ProcessStartInfo + { + FileName = "explorer.exe", + UseShellExecute = false + }; + psi.ArgumentList.Add($"/select,{filePath}"); + Process.Start(psi); + } + catch (Exception ex) + { + AppFileLog.Error($"Failed to open explorer and select: {filePath}", ex); + } + } + + /// + /// Launches an executable with UseShellExecute = false. + /// + public static void LaunchApp(string exePath, string arguments = "") + { + if (string.IsNullOrWhiteSpace(exePath)) return; + + try + { + Process.Start(new ProcessStartInfo + { + FileName = exePath, + Arguments = arguments, + UseShellExecute = false + }); + } + catch (Exception ex) + { + AppFileLog.Error($"Failed to launch app: {exePath}", ex); + } + } +}