mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 08:36:11 +02:00
Merge pull request #27 from HueByte/dev_distribution_update
Dev distribution update
This commit is contained in:
@@ -157,3 +157,22 @@ jobs:
|
||||
EchoHub-Client-linux-arm64.zip
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish Chocolatey package
|
||||
if: steps.changes.outputs.src_changed == 'true' && steps.check_release.outputs.exists == 'false'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
CHECKSUM=$(sha256sum EchoHub-Client-win-x64.zip | awk '{print $1}')
|
||||
|
||||
# Stamp version and checksum into package templates
|
||||
sed -i "s/__VERSION__/$VERSION/g" packaging/choco/echohub.nuspec
|
||||
sed -i "s/__VERSION__/$VERSION/g" packaging/choco/tools/chocolateyInstall.ps1
|
||||
sed -i "s/__CHECKSUM64__/$CHECKSUM/g" packaging/choco/tools/chocolateyInstall.ps1
|
||||
|
||||
# Build and push the package
|
||||
mkdir -p /tmp/choco-out
|
||||
cd packaging/choco
|
||||
choco pack echohub.nuspec --output-directory /tmp/choco-out
|
||||
choco push /tmp/choco-out/echohub.*.nupkg --source https://push.chocolatey.org/ --api-key "$CHOCO_API_KEY"
|
||||
env:
|
||||
CHOCO_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }}
|
||||
|
||||
@@ -1,12 +1,33 @@
|
||||
# Getting Started
|
||||
|
||||
## Prerequisites
|
||||
## Install the Client
|
||||
|
||||
- [.NET 10 SDK](https://dotnet.microsoft.com/download)
|
||||
### Windows (Chocolatey)
|
||||
|
||||
Or grab a self-contained binary from [Releases](https://github.com/HueByte/EchoHub/releases) -- no runtime needed.
|
||||
```bash
|
||||
choco install echohub
|
||||
```
|
||||
|
||||
## Docker
|
||||
### Linux / macOS
|
||||
|
||||
```bash
|
||||
curl -sSfL https://raw.githubusercontent.com/HueByte/EchoHub/master/scripts/install.sh | sh
|
||||
```
|
||||
|
||||
To install a specific version or to a custom directory:
|
||||
|
||||
```bash
|
||||
curl -sSfL .../install.sh | sh -s -- --version 0.2.8
|
||||
curl -sSfL .../install.sh | sh -s -- --install-dir /opt/echohub
|
||||
```
|
||||
|
||||
### Manual Download
|
||||
|
||||
Grab a self-contained binary from [Releases](https://github.com/HueByte/EchoHub/releases) -- no runtime needed.
|
||||
|
||||
## Host a Server
|
||||
|
||||
### Docker
|
||||
|
||||
The quickest way to host a server:
|
||||
|
||||
@@ -17,24 +38,23 @@ docker compose up -d
|
||||
|
||||
See the [Docker guide](docker.md) for configuration, pre-built images, and more.
|
||||
|
||||
## Run the Server
|
||||
### From Source
|
||||
|
||||
```bash
|
||||
dotnet run --project src/EchoHub.Server
|
||||
```
|
||||
|
||||
Requires [.NET 10 SDK](https://dotnet.microsoft.com/download).
|
||||
|
||||
On first run, the server automatically:
|
||||
|
||||
1. Creates `appsettings.json` from the example config
|
||||
2. Generates a secure JWT secret
|
||||
3. Creates the SQLite database with a `#general` channel
|
||||
|
||||
## Run the Client
|
||||
|
||||
```bash
|
||||
dotnet run --project src/EchoHub.Client
|
||||
```
|
||||
## Usage
|
||||
|
||||
After installing the client, run `echohub` (or `dotnet run --project src/EchoHub.Client` from source).
|
||||
Connect to a server, register an account, and start chatting.
|
||||
|
||||
## Connect via IRC
|
||||
|
||||
@@ -21,7 +21,13 @@
|
||||
- Extract `IUserService`/`UserService` — consolidate user registration, authentication, and profile management into a dedicated service, eliminating duplicated logic between `AuthController` and `ChatService`
|
||||
- IRC gateway now checks ban status during authentication (previously skipped)
|
||||
|
||||
## Distribution
|
||||
|
||||
- Add Chocolatey package — `choco install echohub` for Windows users, auto-published from CI on each release
|
||||
- Add Linux/macOS install script — `curl -sSfL .../install.sh | sh` with automatic OS/arch detection
|
||||
|
||||
## CI
|
||||
|
||||
- Add Docker workflow — builds and pushes multi-arch (`amd64`/`arm64`) server image to GHCR on release
|
||||
- Add `linux-arm64` builds to release pipeline — server and client binaries for ARM Linux (Raspberry Pi, cloud ARM instances)
|
||||
- Automate Chocolatey package publishing in release workflow (checksum calculation, pack, push)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>echohub</id>
|
||||
<version>__VERSION__</version>
|
||||
<title>EchoHub</title>
|
||||
<authors>HueByte</authors>
|
||||
<owners>HueByte</owners>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<licenseUrl>https://github.com/HueByteByte/EchoHub/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/HueByteByte/EchoHub</projectUrl>
|
||||
<projectSourceUrl>https://github.com/HueByteByte/EchoHub</projectSourceUrl>
|
||||
<docsUrl>https://HueBytebyte.github.io/EchoHub</docsUrl>
|
||||
<bugTrackerUrl>https://github.com/HueByteByte/EchoHub/issues</bugTrackerUrl>
|
||||
<packageSourceUrl>https://github.com/HueByteByte/EchoHub/tree/master/packaging/choco</packageSourceUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/HueByteByte/EchoHub/master/assets/HueByte_icon.png</iconUrl>
|
||||
<description>EchoHub is a decentralized IRC-style chat application with a terminal user interface (TUI). Connect to any EchoHub server, join channels, and chat — all from your terminal. Features include SignalR real-time messaging, file sharing, custom themes, and IRC gateway compatibility.</description>
|
||||
<summary>Decentralized terminal chat client with IRC gateway support</summary>
|
||||
<tags>chat irc decentralized tui terminal signalr echohub</tags>
|
||||
<releaseNotes>https://HueBytebyte.github.io/EchoHub/changelog/v__VERSION__.html</releaseNotes>
|
||||
<copyright>Copyright (c) 2026 HueByte</copyright>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="tools\**" target="tools" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Hue
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,17 @@
|
||||
VERIFICATION
|
||||
|
||||
To verify the package contents:
|
||||
|
||||
1. Download the official release from:
|
||||
https://github.com/HueByte/EchoHub/releases
|
||||
|
||||
2. Download: EchoHub-Client-win-x64.zip
|
||||
|
||||
3. Calculate the SHA256 checksum:
|
||||
- PowerShell: Get-FileHash EchoHub-Client-win-x64.zip -Algorithm SHA256
|
||||
- Linux/macOS: sha256sum EchoHub-Client-win-x64.zip
|
||||
|
||||
4. Compare with the checksum in chocolateyInstall.ps1 (checksum64 value).
|
||||
|
||||
LICENSE: MIT License - see LICENSE.txt in this directory or
|
||||
https://github.com/HueByte/EchoHub/blob/master/LICENSE
|
||||
@@ -0,0 +1,18 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$toolsDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$version = $env:chocolateyPackageVersion
|
||||
|
||||
$packageArgs = @{
|
||||
packageName = $env:chocolateyPackageName
|
||||
unzipLocation = $toolsDir
|
||||
url64bit = "https://github.com/HueByte/EchoHub/releases/download/v$version/EchoHub-Client-win-x64.zip"
|
||||
checksum64 = '__CHECKSUM64__'
|
||||
checksumType64 = 'sha256'
|
||||
}
|
||||
|
||||
Install-ChocolateyZipPackage @packageArgs
|
||||
|
||||
# Create a shim so 'echohub' is available on PATH
|
||||
$exePath = Join-Path $toolsDir 'client-win-x64' 'EchoHub.Client.exe'
|
||||
Install-BinFile -Name 'echohub' -Path $exePath
|
||||
@@ -0,0 +1,3 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
Uninstall-BinFile -Name 'echohub'
|
||||
@@ -0,0 +1,223 @@
|
||||
#!/bin/sh
|
||||
# EchoHub Client Installer
|
||||
# Usage: curl -sSfL https://raw.githubusercontent.com/HueByte/EchoHub/master/scripts/install.sh | sh
|
||||
#
|
||||
# Options (pass as arguments or environment variables):
|
||||
# --version X.Y.Z Install a specific version (default: latest)
|
||||
# --install-dir DIR Install to a custom directory
|
||||
# --help Show this help message
|
||||
|
||||
set -eu
|
||||
|
||||
REPO="HueByte/EchoHub"
|
||||
BINARY_NAME="echohub"
|
||||
INSTALL_DIR=""
|
||||
VERSION=""
|
||||
|
||||
# ── Argument parsing ──────────────────────────────────────────────────────
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--version)
|
||||
VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--install-dir)
|
||||
INSTALL_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help)
|
||||
sed -n '2,8p' "$0" 2>/dev/null || true
|
||||
echo ""
|
||||
echo " curl -sSfL https://raw.githubusercontent.com/$REPO/master/scripts/install.sh | sh"
|
||||
echo " curl ... | sh -s -- --version 0.2.8"
|
||||
echo " curl ... | sh -s -- --install-dir /opt/echohub"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Platform detection ────────────────────────────────────────────────────
|
||||
|
||||
detect_os() {
|
||||
case "$(uname -s)" in
|
||||
Linux*) echo "linux" ;;
|
||||
Darwin*) echo "osx" ;;
|
||||
*)
|
||||
echo "Error: Unsupported operating system: $(uname -s)" >&2
|
||||
echo "EchoHub supports Linux and macOS. For Windows, use: choco install echohub" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) echo "x64" ;;
|
||||
aarch64|arm64) echo "arm64" ;;
|
||||
*)
|
||||
echo "Error: Unsupported architecture: $(uname -m)" >&2
|
||||
echo "EchoHub supports x64 and arm64." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ── Version resolution ────────────────────────────────────────────────────
|
||||
|
||||
resolve_version() {
|
||||
if [ -n "$VERSION" ]; then
|
||||
echo "$VERSION"
|
||||
return
|
||||
fi
|
||||
|
||||
# Fetch latest release tag from GitHub API
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
tag=$(curl -sSf "https://api.github.com/repos/$REPO/releases/latest" \
|
||||
| grep '"tag_name"' | head -1 | sed 's/.*"tag_name"[[:space:]]*:[[:space:]]*"v\?\([^"]*\)".*/\1/')
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
tag=$(wget -qO- "https://api.github.com/repos/$REPO/releases/latest" \
|
||||
| grep '"tag_name"' | head -1 | sed 's/.*"tag_name"[[:space:]]*:[[:space:]]*"v\?\([^"]*\)".*/\1/')
|
||||
else
|
||||
echo "Error: curl or wget is required to detect the latest version." >&2
|
||||
echo "Install curl/wget or specify a version with --version X.Y.Z" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$tag" ]; then
|
||||
echo "Error: Could not determine latest version from GitHub." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$tag"
|
||||
}
|
||||
|
||||
# ── Install directory resolution ──────────────────────────────────────────
|
||||
|
||||
resolve_install_dir() {
|
||||
if [ -n "$INSTALL_DIR" ]; then
|
||||
echo "$INSTALL_DIR"
|
||||
return
|
||||
fi
|
||||
|
||||
# Prefer /usr/local/bin if writable, otherwise ~/.local/bin
|
||||
if [ -w "/usr/local/bin" ]; then
|
||||
echo "/usr/local/bin"
|
||||
else
|
||||
local_bin="$HOME/.local/bin"
|
||||
mkdir -p "$local_bin"
|
||||
echo "$local_bin"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Download helper ───────────────────────────────────────────────────────
|
||||
|
||||
download() {
|
||||
url="$1"
|
||||
output="$2"
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -sSfL "$url" -o "$output"
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -qO "$output" "$url"
|
||||
else
|
||||
echo "Error: curl or wget is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── PATH setup ────────────────────────────────────────────────────────────
|
||||
|
||||
add_to_path() {
|
||||
dir="$1"
|
||||
export_line="export PATH=\"${dir}:\$PATH\" # Added by EchoHub"
|
||||
|
||||
added=0
|
||||
for profile in "$HOME/.profile" "$HOME/.bashrc" "$HOME/.zshrc"; do
|
||||
if [ -f "$profile" ]; then
|
||||
if grep -q "$dir" "$profile" 2>/dev/null; then
|
||||
continue # Already present
|
||||
fi
|
||||
printf '\n%s\n' "$export_line" >> "$profile"
|
||||
echo " Added to PATH in $(basename "$profile")"
|
||||
added=1
|
||||
fi
|
||||
done
|
||||
|
||||
# If no profile existed, create .profile
|
||||
if [ "$added" -eq 0 ]; then
|
||||
printf '\n%s\n' "$export_line" >> "$HOME/.profile"
|
||||
echo " Added to PATH in .profile"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────────
|
||||
|
||||
main() {
|
||||
os=$(detect_os)
|
||||
arch=$(detect_arch)
|
||||
version=$(resolve_version)
|
||||
install_dir=$(resolve_install_dir)
|
||||
|
||||
artifact="EchoHub-Client-${os}-${arch}.zip"
|
||||
url="https://github.com/$REPO/releases/download/v${version}/${artifact}"
|
||||
|
||||
echo "EchoHub Installer"
|
||||
echo " Version: v${version}"
|
||||
echo " Platform: ${os}-${arch}"
|
||||
echo " Install to: ${install_dir}"
|
||||
echo ""
|
||||
|
||||
# Create temp directory with cleanup trap
|
||||
tmpdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
echo "Downloading ${artifact}..."
|
||||
download "$url" "$tmpdir/echohub.zip"
|
||||
|
||||
echo "Extracting..."
|
||||
if command -v unzip >/dev/null 2>&1; then
|
||||
unzip -qo "$tmpdir/echohub.zip" -d "$tmpdir/extract"
|
||||
else
|
||||
echo "Error: unzip is required to extract the archive." >&2
|
||||
echo "Install it with: apt install unzip / brew install unzip" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The ZIP contains a client-{os}-{arch}/ subdirectory
|
||||
src_dir="$tmpdir/extract/client-${os}-${arch}"
|
||||
if [ ! -d "$src_dir" ]; then
|
||||
# Fallback: look for any directory containing the binary
|
||||
src_dir=$(find "$tmpdir/extract" -name "EchoHub.Client" -type f -printf '%h' -quit 2>/dev/null || true)
|
||||
if [ -z "$src_dir" ]; then
|
||||
echo "Error: Could not find EchoHub.Client binary in the archive." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install the binary
|
||||
mkdir -p "$install_dir"
|
||||
cp "$src_dir/EchoHub.Client" "$install_dir/$BINARY_NAME"
|
||||
chmod +x "$install_dir/$BINARY_NAME"
|
||||
|
||||
echo ""
|
||||
|
||||
# Ensure install directory is on PATH
|
||||
if ! command -v "$BINARY_NAME" >/dev/null 2>&1; then
|
||||
add_to_path "$install_dir"
|
||||
fi
|
||||
|
||||
echo "Installed successfully! Run 'echohub' to start."
|
||||
echo ""
|
||||
if ! echo "$PATH" | tr ':' '\n' | grep -qx "$install_dir"; then
|
||||
echo "NOTE: Restart your shell or run the following to use echohub now:"
|
||||
echo ""
|
||||
echo " export PATH=\"${install_dir}:\$PATH\""
|
||||
fi
|
||||
}
|
||||
|
||||
main
|
||||
@@ -77,6 +77,9 @@ Log.Logger = new LoggerConfiguration()
|
||||
|
||||
Log.Information("EchoHub client starting");
|
||||
|
||||
// == Ensure echohub is on PATH for convenient terminal access ============
|
||||
PathSetup.EnsureOnPath();
|
||||
|
||||
// == Post-update detection: stale backup cleanup or flag for rollback menu ================
|
||||
if (UpdateBackupService.BackupExists())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
using Serilog;
|
||||
|
||||
namespace EchoHub.Client.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the application's directory is on the system PATH so users
|
||||
/// can run 'echohub' from any terminal session.
|
||||
/// </summary>
|
||||
public static class PathSetup
|
||||
{
|
||||
private const string PathMarker = "# Added by EchoHub";
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the app directory is on PATH; if not, adds it persistently.
|
||||
/// On Windows: modifies user-level PATH environment variable.
|
||||
/// On Linux/macOS: appends an export line to shell profile files.
|
||||
/// </summary>
|
||||
public static void EnsureOnPath()
|
||||
{
|
||||
try
|
||||
{
|
||||
var appDir = AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
|
||||
if (IsOnPath(appDir))
|
||||
return;
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
AddToWindowsPath(appDir);
|
||||
else
|
||||
AddToUnixPath(appDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Debug(ex, "Could not add app directory to PATH");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsOnPath(string directory)
|
||||
{
|
||||
var pathVar = Environment.GetEnvironmentVariable("PATH") ?? "";
|
||||
var separator = OperatingSystem.IsWindows() ? ';' : ':';
|
||||
|
||||
return pathVar
|
||||
.Split(separator, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Any(p => string.Equals(
|
||||
p.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar),
|
||||
directory,
|
||||
OperatingSystem.IsWindows()
|
||||
? StringComparison.OrdinalIgnoreCase
|
||||
: StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private static void AddToWindowsPath(string directory)
|
||||
{
|
||||
var userPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User) ?? "";
|
||||
|
||||
// Double-check against user PATH specifically (process PATH includes system + user)
|
||||
if (userPath.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Any(p => string.Equals(p.TrimEnd('\\', '/'), directory, StringComparison.OrdinalIgnoreCase)))
|
||||
return;
|
||||
|
||||
var newPath = string.IsNullOrEmpty(userPath) ? directory : userPath + ";" + directory;
|
||||
Environment.SetEnvironmentVariable("PATH", newPath, EnvironmentVariableTarget.User);
|
||||
Log.Information("Added {Directory} to user PATH", directory);
|
||||
}
|
||||
|
||||
private static void AddToUnixPath(string directory)
|
||||
{
|
||||
var exportLine = $"export PATH=\"{directory}:$PATH\" {PathMarker}";
|
||||
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
|
||||
// Target the most common shell profiles
|
||||
string[] profiles = [
|
||||
Path.Combine(home, ".profile"),
|
||||
Path.Combine(home, ".bashrc"),
|
||||
Path.Combine(home, ".zshrc")
|
||||
];
|
||||
|
||||
var added = false;
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
if (!File.Exists(profile))
|
||||
continue;
|
||||
|
||||
var content = File.ReadAllText(profile);
|
||||
if (content.Contains(directory))
|
||||
continue; // Already present (manual or previous run)
|
||||
|
||||
File.AppendAllText(profile, $"\n{exportLine}\n");
|
||||
added = true;
|
||||
Log.Information("Added PATH export to {Profile}", profile);
|
||||
}
|
||||
|
||||
// If no profile existed, create .profile
|
||||
if (!added)
|
||||
{
|
||||
var fallback = Path.Combine(home, ".profile");
|
||||
File.AppendAllText(fallback, $"\n{exportLine}\n");
|
||||
Log.Information("Created PATH export in {Profile}", fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user