feat: add Nix flake package, AppImage and .deb packaging
- flake: build teleportfling and teleportfling-gui as nixpkgs packages (buildGoModule) with a .desktop entry and icon for the GUI - packaging/appimage.sh: bundle the GUI + runtime libs (recursive ldd) into a self-contained AppImage via appimagetool - packaging/deb.sh: build a .deb for Debian/Ubuntu with both binaries, desktop entry, icon and declared runtime dependencies - README: document all three packaging methods - .gitignore: ignore dist/
This commit is contained in:
@@ -6,3 +6,4 @@
|
|||||||
# nix
|
# nix
|
||||||
result
|
result
|
||||||
result-*
|
result-*
|
||||||
|
dist/
|
||||||
|
|||||||
@@ -78,6 +78,47 @@ TeleportFling itself.
|
|||||||
See `contrib/README.md` — `contrib/install-daemon.sh` installs a per-user
|
See `contrib/README.md` — `contrib/install-daemon.sh` installs a per-user
|
||||||
systemd unit that runs the headless CLI with its own config.
|
systemd unit that runs the headless CLI with its own config.
|
||||||
|
|
||||||
|
## Packaging
|
||||||
|
|
||||||
|
Three distribution formats are provided:
|
||||||
|
|
||||||
|
### Nix / NixOS
|
||||||
|
|
||||||
|
The flake builds both binaries as packages:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix build .#teleportfling # headless CLI
|
||||||
|
nix build .#teleportfling-gui # desktop GUI + tray (default)
|
||||||
|
nix run .#teleportfling-gui
|
||||||
|
```
|
||||||
|
|
||||||
|
The GUI package also installs a `.desktop` entry and icon.
|
||||||
|
|
||||||
|
### AppImage
|
||||||
|
|
||||||
|
A self-contained AppImage of the GUI (bundles the runtime libraries):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./packaging/appimage.sh
|
||||||
|
# → dist/TeleportFling-0.1.0-x86_64.AppImage
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires `appimagetool` in `dist/` (see the script header) and the flake dev
|
||||||
|
shell for building.
|
||||||
|
|
||||||
|
### Debian / Ubuntu
|
||||||
|
|
||||||
|
A `.deb` for Debian/Ubuntu (both binaries + desktop entry + icon):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./packaging/deb.sh
|
||||||
|
# → dist/teleportfling_0.1.0_amd64.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
Install with `sudo dpkg -i dist/teleportfling_0.1.0_amd64.deb` (then
|
||||||
|
`sudo apt-get install -f` to pull dependencies if needed). The binaries
|
||||||
|
require glibc ≥ 2.34, so Ubuntu 22.04+ / Debian 12+ are supported.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
- `go build ./...` — build
|
- `go build ./...` — build
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
description = "TeleportFling Development Environment";
|
description = "TeleportFling - standalone screen + audio sender for the OBS Teleport protocol";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
@@ -10,8 +10,111 @@
|
|||||||
flake-utils.lib.eachDefaultSystem (system:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
# Native/cgo build-time dependencies shared by both binaries.
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
go
|
||||||
|
pkg-config
|
||||||
|
];
|
||||||
|
|
||||||
|
# Libraries linked (or dlopened) by the capture/encode stack.
|
||||||
|
# libpipewire is dlopened at runtime; libturbojpeg is linked.
|
||||||
|
streamLibs = with pkgs; [
|
||||||
|
libjpeg_turbo
|
||||||
|
pipewire
|
||||||
|
xdg-desktop-portal
|
||||||
|
];
|
||||||
|
|
||||||
|
# Libraries required by the Fyne/GLFW GUI (cgo).
|
||||||
|
guiLibs = with pkgs; [
|
||||||
|
libGL
|
||||||
|
mesa
|
||||||
|
wayland
|
||||||
|
libxkbcommon
|
||||||
|
libX11
|
||||||
|
libXrandr
|
||||||
|
libXi
|
||||||
|
libXcursor
|
||||||
|
libXinerama
|
||||||
|
libXxf86vm
|
||||||
|
];
|
||||||
|
|
||||||
|
# Nix's libspa-0.2.pc emits -fno-strict-aliasing/-fno-strict-overflow
|
||||||
|
# which Go's cgo rejects unless explicitly allowed.
|
||||||
|
cgoFlagsAllow = "-fno-strict-overflow|-fno-strict-aliasing";
|
||||||
|
|
||||||
|
# Common build environment so cgo finds headers/libs.
|
||||||
|
buildEnv = pkg: {
|
||||||
|
nativeBuildInputs = nativeBuildInputs;
|
||||||
|
buildInputs = pkg;
|
||||||
|
# Make turbojpeg resolve via pkg-config for cgo builds.
|
||||||
|
PKG_CONFIG_PATH = "${pkgs.libjpeg_turbo.dev}/lib/pkgconfig";
|
||||||
|
# Fyne/GLFW link against X11, Wayland, GL and misc libs. Go's cgo
|
||||||
|
# linker does not read NIX_LDFLAGS, so expose the runtime lib dirs
|
||||||
|
# via LIBRARY_PATH for the link step.
|
||||||
|
LIBRARY_PATH = with pkgs; lib.makeLibraryPath (pkg);
|
||||||
|
CGO_CFLAGS_ALLOW = cgoFlagsAllow;
|
||||||
|
};
|
||||||
|
|
||||||
|
# The headless CLI (no GLFW needed).
|
||||||
|
teleportfling = pkgs.buildGoModule (buildEnv streamLibs // {
|
||||||
|
pname = "teleportfling";
|
||||||
|
version = "0.1.0";
|
||||||
|
src = self;
|
||||||
|
sourceRoot = self.sourceRoot or null;
|
||||||
|
subPackages = [ "cmd/teleportfling" ];
|
||||||
|
vendorHash = "sha256-1PDoL45BWhWNVX9wktR3WXpNbXJ+7V4mfp3la+Nv0wI=";
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
mkdir -p $out/share/applications
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with pkgs.lib; {
|
||||||
|
description = "Stream a screen and audio to OBS Teleport receivers";
|
||||||
|
homepage = "http://homeserver:3050/pedley/TeleportFling";
|
||||||
|
license = licenses.gpl2;
|
||||||
|
mainProgram = "teleportfling";
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
# The desktop GUI + system tray.
|
||||||
|
teleportfling-gui = pkgs.buildGoModule (buildEnv (streamLibs ++ guiLibs) // {
|
||||||
|
pname = "teleportfling-gui";
|
||||||
|
version = "0.1.0";
|
||||||
|
src = self;
|
||||||
|
sourceRoot = self.sourceRoot or null;
|
||||||
|
subPackages = [ "cmd/teleportfling-gui" ];
|
||||||
|
vendorHash = "sha256-1PDoL45BWhWNVX9wktR3WXpNbXJ+7V4mfp3la+Nv0wI=";
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
mkdir -p $out/share/applications
|
||||||
|
install -m 0644 ${./assets/teleportfling.desktop} $out/share/applications/teleportfling.desktop
|
||||||
|
mkdir -p $out/share/icons/hicolor/512x512/apps
|
||||||
|
install -m 0644 ${./assets/teleportfling.png} $out/share/icons/hicolor/512x512/apps/teleportfling.png
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with pkgs.lib; {
|
||||||
|
description = "TeleportFling desktop GUI and system tray";
|
||||||
|
homepage = "http://homeserver:3050/pedley/TeleportFling";
|
||||||
|
license = licenses.gpl2;
|
||||||
|
mainProgram = "teleportfling-gui";
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
});
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
packages = {
|
||||||
|
default = teleportfling-gui;
|
||||||
|
inherit teleportfling teleportfling-gui;
|
||||||
|
};
|
||||||
|
|
||||||
|
apps = {
|
||||||
|
teleportfling = flake-utils.lib.mkApp { drv = teleportfling; };
|
||||||
|
teleportfling-gui = flake-utils.lib.mkApp { drv = teleportfling-gui; };
|
||||||
|
default = self.apps.${system}.teleportfling-gui;
|
||||||
|
};
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
inputsFrom = [];
|
inputsFrom = [];
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
@@ -27,21 +130,7 @@
|
|||||||
git
|
git
|
||||||
direnv
|
direnv
|
||||||
nix-direnv
|
nix-direnv
|
||||||
libjpeg_turbo
|
] ++ streamLibs ++ guiLibs;
|
||||||
pipewire
|
|
||||||
xdg-desktop-portal
|
|
||||||
# Fyne / GLFW (cgo) native dependencies.
|
|
||||||
libGL
|
|
||||||
mesa
|
|
||||||
wayland
|
|
||||||
libxkbcommon
|
|
||||||
libX11
|
|
||||||
libXrandr
|
|
||||||
libXi
|
|
||||||
libXcursor
|
|
||||||
libXinerama
|
|
||||||
libXxf86vm
|
|
||||||
];
|
|
||||||
|
|
||||||
# Make turbojpeg resolve via pkg-config for cgo builds.
|
# Make turbojpeg resolve via pkg-config for cgo builds.
|
||||||
PKG_CONFIG_PATH = "${pkgs.libjpeg_turbo.dev}/lib/pkgconfig";
|
PKG_CONFIG_PATH = "${pkgs.libjpeg_turbo.dev}/lib/pkgconfig";
|
||||||
@@ -50,26 +139,15 @@
|
|||||||
# Fyne/GLFW link against X11, Wayland, GL and misc libs. Go's cgo
|
# Fyne/GLFW link against X11, Wayland, GL and misc libs. Go's cgo
|
||||||
# linker does not read NIX_LDFLAGS, so expose the runtime lib dirs
|
# linker does not read NIX_LDFLAGS, so expose the runtime lib dirs
|
||||||
# via LIBRARY_PATH for the link step.
|
# via LIBRARY_PATH for the link step.
|
||||||
LIBRARY_PATH = with pkgs; lib.makeLibraryPath [
|
LIBRARY_PATH = with pkgs; lib.makeLibraryPath (streamLibs ++ guiLibs);
|
||||||
libGL
|
|
||||||
mesa
|
|
||||||
wayland
|
|
||||||
libxkbcommon
|
|
||||||
libX11
|
|
||||||
libXrandr
|
|
||||||
libXi
|
|
||||||
libXcursor
|
|
||||||
libXinerama
|
|
||||||
libXxf86vm
|
|
||||||
];
|
|
||||||
|
|
||||||
# Nix's libspa-0.2.pc emits -fno-strict-aliasing/-fno-strict-overflow
|
# Nix's libspa-0.2.pc emits -fno-strict-aliasing/-fno-strict-overflow
|
||||||
# which Go's cgo rejects unless explicitly allowed.
|
# which Go's cgo rejects unless explicitly allowed.
|
||||||
CGO_CFLAGS_ALLOW = "-fno-strict-overflow|-fno-strict-aliasing";
|
CGO_CFLAGS_ALLOW = cgoFlagsAllow;
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
echo "TeleportFling dev shell ready!"
|
echo "TeleportFling dev shell ready!"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# AppImage AppRun: locate bundled libraries and launch the GUI.
|
||||||
|
HERE="$(dirname "$(readlink -f "$0")")"
|
||||||
|
export LD_LIBRARY_PATH="${HERE}/usr/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||||
|
exec "${HERE}/usr/bin/teleportfling-gui" "$@"
|
||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build a self-contained AppImage of the TeleportFling GUI.
|
||||||
|
#
|
||||||
|
# Approach: build the GUI binary, then bundle it plus every shared library it
|
||||||
|
# needs (from ldd, recursively) plus the dlopened PipeWire stack into an
|
||||||
|
# AppDir, then package it with appimagetool. Output:
|
||||||
|
# dist/TeleportFling-<version>-x86_64.AppImage
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./packaging/appimage.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
OUT="${HERE}/dist"
|
||||||
|
APPDIR="${OUT}/AppDir"
|
||||||
|
LIBDIR="${APPDIR}/usr/lib"
|
||||||
|
|
||||||
|
VERSION="0.1.0"
|
||||||
|
ARCH="x86_64"
|
||||||
|
|
||||||
|
echo "Building teleportfling-gui..."
|
||||||
|
nix develop --command bash -c "go build -o '${OUT}/teleportfling-gui' ./cmd/teleportfling-gui"
|
||||||
|
|
||||||
|
echo "Preparing AppDir..."
|
||||||
|
rm -rf "${APPDIR}"
|
||||||
|
mkdir -p "${LIBDIR}" \
|
||||||
|
"${APPDIR}/usr/bin" \
|
||||||
|
"${APPDIR}/usr/share/applications" \
|
||||||
|
"${APPDIR}/usr/share/icons/hicolor/512x512/apps"
|
||||||
|
|
||||||
|
install -m 0755 "${OUT}/teleportfling-gui" "${APPDIR}/usr/bin/teleportfling-gui"
|
||||||
|
install -m 0644 "${HERE}/assets/teleportfling.desktop" "${APPDIR}/usr/share/applications/teleportfling.desktop"
|
||||||
|
install -m 0644 "${HERE}/assets/teleportfling.png" "${APPDIR}/usr/share/icons/hicolor/512x512/apps/teleportfling.png"
|
||||||
|
# appimagetool requires the desktop file and icon in the AppDir root.
|
||||||
|
install -m 0644 "${HERE}/assets/teleportfling.desktop" "${APPDIR}/teleportfling.desktop"
|
||||||
|
install -m 0644 "${HERE}/assets/teleportfling.png" "${APPDIR}/teleportfling.png"
|
||||||
|
|
||||||
|
echo "Bundling runtime libraries..."
|
||||||
|
# Copy every library the binary links against, resolving dependencies
|
||||||
|
# iteratively (ldd output may reference libs that themselves need libs).
|
||||||
|
declare -A seen
|
||||||
|
collect_libs() {
|
||||||
|
local bin="$1"
|
||||||
|
while IFS= read -r lib; do
|
||||||
|
local name
|
||||||
|
name="$(basename "${lib}")"
|
||||||
|
if [[ -z "${seen[${name}]:-}" && -f "${lib}" ]]; then
|
||||||
|
seen["${name}"]=1
|
||||||
|
install -m 0755 "${lib}" "${LIBDIR}/${name}" 2>/dev/null || true
|
||||||
|
collect_libs "${lib}"
|
||||||
|
fi
|
||||||
|
done < <(ldd "${bin}" 2>/dev/null | grep -oE '/[^ ]+\.so[^ ]*' | sort -u)
|
||||||
|
}
|
||||||
|
collect_libs "${APPDIR}/usr/bin/teleportfling-gui"
|
||||||
|
echo " bundled $(ls "${LIBDIR}" | wc -l) libraries"
|
||||||
|
|
||||||
|
# The screencast backend dlopens libpipewire at runtime; ensure it and its
|
||||||
|
# SPA plugin libs are present (they are pulled in via the recursive ldd walk
|
||||||
|
# above, but guard against a missing libspa here).
|
||||||
|
if [[ -f "${LIBDIR}/libpipewire-0.3.so.0" ]]; then
|
||||||
|
echo " bundled libpipewire-0.3.so.0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Writing AppRun..."
|
||||||
|
install -m 0755 "${HERE}/packaging/AppRun" "${APPDIR}/AppRun"
|
||||||
|
|
||||||
|
echo "Packaging AppImage with appimagetool..."
|
||||||
|
if [[ ! -x "${OUT}/appimagetool" ]]; then
|
||||||
|
echo "appimagetool not found at ${OUT}/appimagetool"
|
||||||
|
echo "Download: https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
"${OUT}/appimagetool" --appimage-extract-and-run "${APPDIR}" "${OUT}/TeleportFling-${VERSION}-${ARCH}.AppImage"
|
||||||
|
|
||||||
|
echo "Done: ${OUT}/TeleportFling-${VERSION}-${ARCH}.AppImage"
|
||||||
Executable
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build a .deb package of TeleportFling for Debian/Ubuntu.
|
||||||
|
#
|
||||||
|
# The binary is built with cgo; runtime libraries (libturbojpeg, libpipewire,
|
||||||
|
# and the GLFW/GL stack for the GUI) are declared as Debian package
|
||||||
|
# dependencies so the .deb works on a stock Debian/Ubuntu install. Output:
|
||||||
|
# dist/teleportfling_<version>_amd64.deb
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./packaging/deb.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
OUT="${HERE}/dist"
|
||||||
|
ROOT="${OUT}/deb-root"
|
||||||
|
|
||||||
|
VERSION="0.1.0"
|
||||||
|
ARCH="amd64"
|
||||||
|
PKG_NAME="teleportfling"
|
||||||
|
|
||||||
|
echo "Building teleportfling and teleportfling-gui..."
|
||||||
|
nix develop --command bash -c "go build -o '${OUT}/teleportfling' ./cmd/teleportfling && go build -o '${OUT}/teleportfling-gui' ./cmd/teleportfling-gui"
|
||||||
|
|
||||||
|
echo "Assembling .deb root..."
|
||||||
|
rm -rf "${ROOT}"
|
||||||
|
mkdir -p "${ROOT}/DEBIAN" \
|
||||||
|
"${ROOT}/usr/bin" \
|
||||||
|
"${ROOT}/usr/share/applications" \
|
||||||
|
"${ROOT}/usr/share/icons/hicolor/512x512/apps"
|
||||||
|
|
||||||
|
install -m 0755 "${OUT}/teleportfling" "${ROOT}/usr/bin/teleportfling"
|
||||||
|
install -m 0755 "${OUT}/teleportfling-gui" "${ROOT}/usr/bin/teleportfling-gui"
|
||||||
|
install -m 0644 "${HERE}/assets/teleportfling.desktop" "${ROOT}/usr/share/applications/teleportfling.desktop"
|
||||||
|
install -m 0644 "${HERE}/assets/teleportfling.png" "${ROOT}/usr/share/icons/hicolor/512x512/apps/teleportfling.png"
|
||||||
|
|
||||||
|
# Debian package control file.
|
||||||
|
cat > "${ROOT}/DEBIAN/control" <<EOF
|
||||||
|
Package: ${PKG_NAME}
|
||||||
|
Version: ${VERSION}
|
||||||
|
Section: video
|
||||||
|
Priority: optional
|
||||||
|
Architecture: ${ARCH}
|
||||||
|
Maintainer: Pete Edley <[email protected]>
|
||||||
|
Depends: libjpeg-turbo8 (>= 2.0), libpipewire-0.3-0 (>= 0.3), libgl1, libglx0,
|
||||||
|
libwayland-client0, libwayland-cursor0, libwayland-egl1, libxkbcommon0,
|
||||||
|
libx11-6, libxrandr2, libxi6, libxcursor1, libxinerama1, libxxf86vm1,
|
||||||
|
libxrender1, libxext6, libxfixes3
|
||||||
|
Description: Stream a screen and audio to OBS Teleport receivers
|
||||||
|
Standalone Linux sender for the OBS Teleport protocol. Captures a Wayland
|
||||||
|
screen and system audio, encodes video to JPEG, and streams them over TCP
|
||||||
|
so OBS with the obs-teleport plugin can discover and display the stream.
|
||||||
|
.
|
||||||
|
Provides a headless CLI (teleportfling) and a desktop GUI with system tray
|
||||||
|
(teleportfling-gui).
|
||||||
|
Homepage: http://homeserver:3050/pedley/TeleportFling
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Building .deb..."
|
||||||
|
nix shell nixpkgs#dpkg --command dpkg-deb --build --root-owner-group "${ROOT}" "${OUT}/${PKG_NAME}_${VERSION}_${ARCH}.deb"
|
||||||
|
|
||||||
|
echo "Done: ${OUT}/${PKG_NAME}_${VERSION}_${ARCH}.deb"
|
||||||
@@ -18,10 +18,18 @@ Backlog of deferred / planned work, tracked outside of milestone milestones.
|
|||||||
systemd unit (`contrib/teleportfling.service`) that starts the headless
|
systemd unit (`contrib/teleportfling.service`) that starts the headless
|
||||||
CLI with its own config at `~/.config/teleportfling/daemon.json`.
|
CLI with its own config at `~/.config/teleportfling/daemon.json`.
|
||||||
|
|
||||||
## Ideas (not yet scoped)
|
## Future / Ideas (not yet scoped)
|
||||||
|
|
||||||
- Live settings changes mid-stream (`flinger.SetConfig` for FPS/quality).
|
- [ ] **Live settings changes mid-stream** — `flinger.SetConfig` for FPS/quality
|
||||||
- Bitrate/bandwidth tuning beyond the balanced 1080p30 default.
|
without a restart.
|
||||||
- Screen capture scaling / downsampling.
|
- [ ] **Bitrate / bandwidth tuning** — options beyond the balanced 1080p30
|
||||||
- Packaging (Nix package / AppImage / release binaries) — deliberately
|
default.
|
||||||
deferred.
|
- [ ] **Screen capture scaling / downsampling** — capture a scaled region rather
|
||||||
|
than the monitor's native resolution.
|
||||||
|
- [ ] **Multi-monitor verification** — the GUI monitor picker enumerates via
|
||||||
|
hyprctl; confirm behaviour on a real multi-monitor setup.
|
||||||
|
- [ ] **Non-Hyprland monitor enumeration** — portal-based fallback so the picker
|
||||||
|
works on GNOME/KDE etc. (currently falls back to a single indexed option).
|
||||||
|
- [ ] **Packaging** — Nix flake package, AppImage, and `.deb` for Debian/Ubuntu
|
||||||
|
(in progress: flake package, AppImage and .deb scripts added under
|
||||||
|
packaging/; verify on a real Debian/Ubuntu machine).
|
||||||
Reference in New Issue
Block a user