Merge pull request #303745 from quantenzitrone/ydotool

ydotool: refactor ; nixos/ydotool: init module & nixosTest

Changed files
+251 -33
nixos
doc
manual
release-notes
modules
tests
pkgs
by-name
yd
ydotool
tools
wayland
ydotool
top-level
+2
nixos/doc/manual/release-notes/rl-2405.section.md
···
- [isolate](https://github.com/ioi/isolate), a sandbox for securely executing untrusted programs. Available as [security.isolate](#opt-security.isolate.enable).
+
- [ydotool](https://github.com/ReimuNotMoe/ydotool), a generic command-line automation tool now has a module. Available as [programs.ydotool](#opt-programs.ydotool.enable).
+
- [private-gpt](https://github.com/zylon-ai/private-gpt), a service to interact with your documents using the power of LLMs, 100% privately, no data leaks. Available as [services.private-gpt](#opt-services.private-gpt.enable).
- [keto](https://www.ory.sh/keto/), a permission & access control server, the first open source implementation of ["Zanzibar: Google's Consistent, Global Authorization System"](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/).
+1
nixos/modules/module-list.nix
···
./programs/xwayland.nix
./programs/yabar.nix
./programs/yazi.nix
+
./programs/ydotool.nix
./programs/yubikey-touch-detector.nix
./programs/zmap.nix
./programs/zsh/oh-my-zsh.nix
+83
nixos/modules/programs/ydotool.nix
···
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
let
+
cfg = config.programs.ydotool;
+
in
+
{
+
meta = {
+
maintainers = with lib.maintainers; [ quantenzitrone ];
+
};
+
+
options.programs.ydotool = {
+
enable = lib.mkEnableOption ''
+
ydotoold system service and install ydotool.
+
Add yourself to the 'ydotool' group to be able to use it.
+
'';
+
};
+
+
config = lib.mkIf cfg.enable {
+
users.groups.ydotool = { };
+
+
systemd.services.ydotoold = {
+
description = "ydotoold - backend for ydotool";
+
wantedBy = [ "multi-user.target" ];
+
partOf = [ "multi-user.target" ];
+
serviceConfig = {
+
Group = "ydotool";
+
RuntimeDirectory = "ydotoold";
+
RuntimeDirectoryMode = "0750";
+
ExecStart = "${lib.getExe' pkgs.ydotool "ydotoold"} --socket-path=/run/ydotoold/socket --socket-perm=0660";
+
+
# hardening
+
+
## allow access to uinput
+
DeviceAllow = [ "/dev/uinput" ];
+
DevicePolicy = "closed";
+
+
## allow creation of unix sockets
+
RestrictAddressFamilies = [ "AF_UNIX" ];
+
+
CapabilityBoundingSet = "";
+
IPAddressDeny = "any";
+
LockPersonality = true;
+
MemoryDenyWriteExecute = true;
+
NoNewPrivileges = true;
+
PrivateNetwork = true;
+
PrivateTmp = true;
+
PrivateUsers = true;
+
ProcSubset = "pid";
+
ProtectClock = true;
+
ProtectControlGroups = true;
+
ProtectHome = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
ProtectProc = "invisible";
+
ProtectSystem = "strict";
+
ProtectUser = true;
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
RestrictSUIDSGID = true;
+
SystemCallArchitectures = "native";
+
SystemCallFilter = [
+
"@system-service"
+
"~@privileged"
+
"~@resources"
+
];
+
UMask = "0077";
+
+
# -> systemd-analyze security score 0.7 SAFE 😀
+
};
+
};
+
+
environment.variables = {
+
YDOTOOL_SOCKET = "/run/ydotoold/socket";
+
};
+
environment.systemPackages = with pkgs; [ ydotool ];
+
};
+
}
+1
nixos/tests/all-tests.nix
···
xterm = handleTest ./xterm.nix {};
xxh = handleTest ./xxh.nix {};
yabar = handleTest ./yabar.nix {};
+
ydotool = handleTest ./ydotool.nix {};
yggdrasil = handleTest ./yggdrasil.nix {};
zammad = handleTest ./zammad.nix {};
zeronet-conservancy = handleTest ./zeronet-conservancy.nix {};
+115
nixos/tests/ydotool.nix
···
+
import ./make-test-python.nix (
+
{ pkgs, lib, ... }:
+
let
+
textInput = "This works.";
+
inputBoxText = "Enter input";
+
inputBox = pkgs.writeShellScript "zenity-input" ''
+
${lib.getExe pkgs.gnome.zenity} --entry --text '${inputBoxText}:' > /tmp/output &
+
'';
+
in
+
{
+
name = "ydotool";
+
+
meta = {
+
maintainers = with lib.maintainers; [
+
OPNA2608
+
quantenzitrone
+
];
+
};
+
+
nodes = {
+
headless =
+
{ config, ... }:
+
{
+
imports = [ ./common/user-account.nix ];
+
+
users.users.alice.extraGroups = [ "ydotool" ];
+
+
programs.ydotool.enable = true;
+
+
services.getty.autologinUser = "alice";
+
};
+
+
x11 =
+
{ config, ... }:
+
{
+
imports = [
+
./common/user-account.nix
+
./common/auto.nix
+
./common/x11.nix
+
];
+
+
users.users.alice.extraGroups = [ "ydotool" ];
+
+
programs.ydotool.enable = true;
+
+
test-support.displayManager.auto = {
+
enable = true;
+
user = "alice";
+
};
+
+
services.xserver.windowManager.dwm.enable = true;
+
services.displayManager.defaultSession = lib.mkForce "none+dwm";
+
};
+
+
wayland =
+
{ config, ... }:
+
{
+
imports = [ ./common/user-account.nix ];
+
+
services.cage = {
+
enable = true;
+
user = "alice";
+
};
+
+
programs.ydotool.enable = true;
+
+
services.cage.program = inputBox;
+
};
+
};
+
+
enableOCR = true;
+
+
testScript =
+
{ nodes, ... }:
+
''
+
def as_user(cmd: str):
+
"""
+
Return a shell command for running a shell command as a specific user.
+
"""
+
return f"sudo -u alice -i {cmd}"
+
+
start_all()
+
+
# Headless
+
headless.wait_for_unit("multi-user.target")
+
headless.wait_for_text("alice")
+
headless.succeed(as_user("ydotool type 'echo ${textInput} > /tmp/output'")) # text input
+
headless.succeed(as_user("ydotool key 28:1 28:0")) # text input
+
headless.screenshot("headless_input")
+
headless.wait_for_file("/tmp/output")
+
headless.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input
+
+
# X11
+
x11.wait_for_x()
+
x11.execute(as_user("${inputBox}"))
+
x11.wait_for_text("${inputBoxText}")
+
x11.succeed(as_user("ydotool type '${textInput}'")) # text input
+
x11.screenshot("x11_input")
+
x11.succeed(as_user("ydotool mousemove -a 400 110")) # mouse input
+
x11.succeed(as_user("ydotool click 0xC0")) # mouse input
+
x11.wait_for_file("/tmp/output")
+
x11.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input
+
+
# Wayland
+
wayland.wait_for_unit("graphical.target")
+
wayland.wait_for_text("${inputBoxText}")
+
wayland.succeed("ydotool type '${textInput}'") # text input
+
wayland.screenshot("wayland_input")
+
wayland.succeed("ydotool mousemove -a 100 100") # mouse input
+
wayland.succeed("ydotool click 0xC0") # mouse input
+
wayland.wait_for_file("/tmp/output")
+
wayland.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input
+
'';
+
}
+
)
+49
pkgs/by-name/yd/ydotool/package.nix
···
+
{
+
lib,
+
stdenv,
+
fetchFromGitHub,
+
cmake,
+
scdoc,
+
util-linux,
+
xorg,
+
nixosTests,
+
}:
+
+
stdenv.mkDerivation (finalAttrs: {
+
pname = "ydotool";
+
version = "1.0.4";
+
+
src = fetchFromGitHub {
+
owner = "ReimuNotMoe";
+
repo = "ydotool";
+
rev = "v${finalAttrs.version}";
+
hash = "sha256-MtanR+cxz6FsbNBngqLE+ITKPZFHmWGsD1mBDk0OVng=";
+
};
+
+
postPatch = ''
+
substituteInPlace Daemon/ydotoold.c \
+
--replace "/usr/bin/xinput" "${xorg.xinput}/bin/xinput"
+
substituteInPlace Daemon/ydotool.service.in \
+
--replace "/usr/bin/kill" "${util-linux}/bin/kill"
+
'';
+
+
strictDeps = true;
+
nativeBuildInputs = [
+
cmake
+
scdoc
+
];
+
+
passthru.tests.basic = nixosTests.ydotool;
+
+
meta = {
+
description = "Generic Linux command-line automation tool";
+
homepage = "https://github.com/ReimuNotMoe/ydotool";
+
license = lib.licenses.agpl3Plus;
+
mainProgram = "ydotool";
+
maintainers = with lib.maintainers; [
+
willibutz
+
kraem
+
];
+
platforms = lib.platforms.linux;
+
};
+
})
-31
pkgs/tools/wayland/ydotool/default.nix
···
-
{ lib, stdenv, fetchFromGitHub, cmake, scdoc, util-linux, xorg }:
-
-
stdenv.mkDerivation rec {
-
pname = "ydotool";
-
version = "1.0.4";
-
-
src = fetchFromGitHub {
-
owner = "ReimuNotMoe";
-
repo = "ydotool";
-
rev = "v${version}";
-
hash = "sha256-MtanR+cxz6FsbNBngqLE+ITKPZFHmWGsD1mBDk0OVng=";
-
};
-
-
postPatch = ''
-
substituteInPlace Daemon/ydotoold.c \
-
--replace "/usr/bin/xinput" "${xorg.xinput}/bin/xinput"
-
substituteInPlace Daemon/ydotool.service.in \
-
--replace "/usr/bin/kill" "${util-linux}/bin/kill"
-
'';
-
-
strictDeps = true;
-
nativeBuildInputs = [ cmake scdoc ];
-
-
meta = with lib; {
-
homepage = "https://github.com/ReimuNotMoe/ydotool";
-
description = "Generic Linux command-line automation tool";
-
license = licenses.agpl3Plus;
-
maintainers = with maintainers; [ willibutz kraem ];
-
platforms = with platforms; linux;
-
};
-
}
-2
pkgs/top-level/all-packages.nix
···
wtype = callPackage ../tools/wayland/wtype { };
-
ydotool = callPackage ../tools/wayland/ydotool { };
-
cambalache = callPackage ../development/tools/cambalache { };
cambrinary = python3Packages.callPackage ../applications/misc/cambrinary { };