Skip to content
Open
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
2 changes: 1 addition & 1 deletion build/scripts/linux/build-avalonia-packages.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -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}"
Expand Down
69 changes: 69 additions & 0 deletions build/scripts/linux/build-avalonia-packages.sh.orig
Original file line number Diff line number Diff line change
@@ -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" <<EOF
name: gregmodmanager
arch: amd64
platform: linux
version: ${VERSION}
section: utils
priority: optional
maintainer: teamGreg <noreply@gregframework.eu>
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"
11 changes: 11 additions & 0 deletions build/scripts/linux/build-avalonia-packages.sh.rej
Original file line number Diff line number Diff line change
@@ -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}"
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.313",
"version": "10.0.103",
"rollForward": "minor"
}
}
11 changes: 11 additions & 0 deletions patch.diff
Original file line number Diff line number Diff line change
@@ -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}"
238 changes: 120 additions & 118 deletions src/GregModmanager.Core/Services/SafeProcess.cs
Original file line number Diff line number Diff line change
@@ -1,118 +1,120 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace GregModmanager.Services;

public static class SafeProcess
{
/// <summary>
/// Opens a URL in the default browser safely, ensuring only http and https schemes are allowed.
/// </summary>
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;
}

/// <summary>
/// Opens a folder in the system's file explorer.
/// </summary>
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);
}
}

/// <summary>
/// Specifically for Windows, opens explorer and selects a file.
/// </summary>
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);
}
}

/// <summary>
/// Launches an executable with UseShellExecute = false.
/// </summary>
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
{
/// <summary>
/// Opens a URL in the default browser safely, ensuring only http and https schemes are allowed.
/// </summary>
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;
}

/// <summary>
/// Opens a folder in the system's file explorer.
/// </summary>
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);
}
}

/// <summary>
/// Specifically for Windows, opens explorer and selects a file.
/// </summary>
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);
}
}

/// <summary>
/// Launches an executable with UseShellExecute = false.
/// </summary>
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);
}
}
}
Loading