nixos/timekpr: init at 0.5.8 (#419487)

Changed files
+251
nixos
doc
manual
modules
services
security
tests
pkgs
by-name
ti
timekpr
+13
nixos/doc/manual/configuration/user-mgmt.chapter.md
···
Userborn implements immutable users by re-mounting the password files
read-only. This means that unlike when using the Perl script, trying to add a
new user (e.g. via `useradd`) will fail right away.
+
+
## Restrict usage time {#sec-restrict-usage-time}
+
+
[Timekpr-nExT](https://mjasnik.gitlab.io/timekpr-next/) is a screen time managing application that helps optimizing time spent at computer for your subordinates, children or even for yourself.
+
+
You can enable it via:
+
+
```nix
+
{ services.timekpr.enable = true; }
+
```
+
+
This will install the `timekpr` package and start the `timekpr` service.
+
You can then use the `timekpra` application to configure time limits for users.
+3
nixos/doc/manual/redirects.json
···
"sec-userborn": [
"index.html#sec-userborn"
],
+
"sec-restrict-usage-time": [
+
"index.html#sec-restrict-usage-time"
+
],
"ch-file-systems": [
"index.html#ch-file-systems"
],
+2
nixos/doc/manual/release-notes/rl-2511.section.md
···
- [mautrix-discord](https://github.com/mautrix/discord), a Matrix-Discord puppeting/relay bridge. Available as [services.mautrix-discord](#opt-services.mautrix-discord.enable).
+
- [Timekpr-nExT](https://mjasnik.gitlab.io/timekpr-next/), a time managing application that helps optimizing time spent at computer for your subordinates, children or even for yourself. Available as [](#opt-services.timekpr.enable).
+
- [SuiteNumérique Meet](https://github.com/suitenumerique/meet) is an open source alternative to Google Meet and Zoom powered by LiveKit: HD video calls, screen sharing, and chat features. Built with Django and React. Available as [services.lasuite-meet](#opt-services.lasuite-meet.enable).
- [lemurs](https://github.com/coastalwhite/lemurs), a customizable TUI display/login manager. Available at [services.displayManager.lemurs](#opt-services.displayManager.lemurs.enable).
+1
nixos/modules/module-list.nix
···
./services/security/sslmate-agent.nix
./services/security/step-ca.nix
./services/security/tang.nix
+
./services/security/timekpr.nix
./services/security/tor.nix
./services/security/torify.nix
./services/security/torsocks.nix
+65
nixos/modules/services/security/timekpr.nix
···
+
{
+
pkgs,
+
lib,
+
config,
+
...
+
}:
+
let
+
cfg = config.services.timekpr;
+
targetBaseDir = "/var/lib/timekpr";
+
daemonUser = "root";
+
daemonGroup = "root";
+
in
+
{
+
options = {
+
services.timekpr = {
+
package = lib.mkPackageOption pkgs "timekpr" { };
+
enable = lib.mkEnableOption "Timekpr-nExT, a screen time managing application that helps optimizing time spent at computer for your subordinates, children or even for yourself";
+
adminUsers = lib.mkOption {
+
type = lib.types.listOf lib.types.str;
+
default = [ ];
+
example = [
+
"alice"
+
"bob"
+
];
+
description = ''
+
All listed users will become part of the `timekpr` group so they can manage timekpr settings without requiring sudo.
+
'';
+
};
+
};
+
};
+
+
config = lib.mkIf cfg.enable {
+
users.groups.timekpr = {
+
gid = 2000;
+
members = cfg.adminUsers;
+
};
+
+
environment.systemPackages = [
+
# Add timekpr to system packages so that polkit can find it
+
cfg.package
+
];
+
services.dbus.enable = true;
+
services.dbus.packages = [
+
cfg.package
+
];
+
environment.etc."timekpr" = {
+
source = "${cfg.package}/etc/timekpr";
+
};
+
systemd.packages = [
+
cfg.package
+
];
+
systemd.services.timekpr = {
+
enable = true;
+
wantedBy = [ "multi-user.target" ];
+
};
+
security.polkit.enable = true;
+
systemd.tmpfiles.rules = [
+
"d ${targetBaseDir} 0755 ${daemonUser} ${daemonGroup} -"
+
"d ${targetBaseDir}/config 0755 ${daemonUser} ${daemonGroup} -"
+
"d ${targetBaseDir}/work 0755 ${daemonUser} ${daemonGroup} -"
+
];
+
};
+
+
meta.maintainers = [ lib.maintainers.atry ];
+
}
+17
nixos/tests/timekpr.nix
···
+
{ pkgs, lib, ... }:
+
{
+
name = "timekpr";
+
meta.maintainers = [ lib.maintainers.atry ];
+
+
nodes.machine =
+
{ pkgs, lib, ... }:
+
{
+
services.timekpr.enable = true;
+
};
+
+
testScript = ''
+
start_all()
+
machine.wait_for_file("/etc/timekpr/timekpr.conf")
+
machine.wait_for_unit("timekpr.service")
+
'';
+
}
+150
pkgs/by-name/ti/timekpr/package.nix
···
+
{
+
fetchgit,
+
gitUpdater,
+
glib,
+
gobject-introspection,
+
gtk3,
+
lib,
+
python3Packages,
+
sound-theme-freedesktop,
+
stdenv,
+
wrapGAppsHook4,
+
}:
+
python3Packages.buildPythonApplication rec {
+
pname = "timekpr";
+
version = "0.5.8";
+
+
src = fetchgit {
+
url = "https://git.launchpad.net/timekpr-next";
+
tag = "v${version}";
+
hash = "sha256-Y0jAKl553HjoP59wJnKBKq4Ogko1cs8uazW2dy7AlBo=";
+
};
+
+
buildInputs = [
+
glib
+
gtk3
+
];
+
+
nativeBuildInputs = [
+
gobject-introspection
+
wrapGAppsHook4
+
];
+
+
pyproject = true;
+
+
build-system = with python3Packages; [
+
setuptools
+
];
+
+
dependencies = with python3Packages; [
+
dbus-python
+
pygobject3
+
psutil
+
];
+
+
# Generate setup.py because the upstream repository does not include it
+
SETUP_PY = ''
+
from setuptools import setup, find_namespace_packages
+
+
package_dir={"timekpr": "."}
+
setup(
+
name="timekpr-next",
+
version="${version}",
+
package_dir=package_dir,
+
packages=[
+
f"{package_prefix}.{package_suffix}"
+
for package_prefix, where in package_dir.items()
+
for package_suffix in find_namespace_packages(where=where)
+
],
+
install_requires=[
+
${lib.concatMapStringsSep ", " (dependency: "'${dependency.pname}'") dependencies}
+
],
+
)
+
'';
+
+
postPatch = ''
+
shopt -s globstar extglob nullglob
+
+
substituteInPlace bin/* **/*.py resource/server/systemd/timekpr.service \
+
--replace-quiet /usr/lib/python3/dist-packages "$out"/${lib.escapeShellArg python3Packages.python.sitePackages}
+
+
substituteInPlace **/*.desktop **/*.policy **/*.service \
+
--replace-fail /usr/bin/timekpr "$out"/bin/timekpr
+
+
substituteInPlace common/constants/constants.py \
+
--replace-fail /usr/share/sounds/freedesktop ${lib.escapeShellArg sound-theme-freedesktop}/share/sounds/freedesktop \
+
--replace-fail /usr/share/timekpr "$out"/share/timekpr \
+
--replace-fail /usr/share/locale "$out"/share/locale
+
+
substituteInPlace resource/server/timekpr.conf \
+
--replace-fail /usr/share/timekpr "$out"/share/timekpr \
+
+
# The original file name `timekpra` is renamed to `..timekpra-wrapped-wrapped` because `makeCWrapper` was used multiple times.
+
substituteInPlace client/admin/adminprocessor.py \
+
--replace-fail '"/timekpra" in ' '"/..timekpra-wrapped-wrapped" in '
+
+
printf %s "$SETUP_PY" > setup.py
+
'';
+
+
# We need to manually inject $PYTHONPATH here, because `buildPythonApplication` does not recognize timekpr's executables as Python scripts, and therefore it does not automatically inject $PYTHONPATH into them.
+
postFixup = ''
+
for executable in $out/bin/*
+
do
+
wrapProgram "$executable" --prefix PYTHONPATH : "$PYTHONPATH"
+
done
+
'';
+
+
preInstall = ''
+
while IFS= read -r line
+
do
+
# Trim leading/trailing whitespace
+
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
+
+
# Skip empty lines and comments
+
if [[ -z "$line" || "$line" =~ ^# ]]; then
+
continue
+
fi
+
+
# Separate source and destination
+
# This assumes the destination is the last field and source path doesn't contain problematic spaces
+
# More robust parsing might be needed if source paths have spaces.
+
source_path=$(echo "$line" | awk '{ $NF=""; print $0 }' | sed 's/[[:space:]]*$//')
+
dest_path=$(echo "$line" | awk '{ print $NF }')
+
+
# Check destination path prefix and map to $out/*
+
case "$dest_path" in
+
usr/share/*)
+
# Remove "usr/" prefix and prepend "$out/"
+
install -D --mode=444 "$source_path" --target-directory="$out/''${dest_path#usr/}"
+
;;
+
usr/bin/*)
+
# Remove "usr/" prefix and prepend "$out/"
+
install -D --mode=555 "$source_path" --target-directory="$out/''${dest_path#usr/}"
+
;;
+
etc/*|lib/*|var/*)
+
# Prepend "$out/"
+
install -D --mode=444 "$source_path" --target-directory="$out/$dest_path"
+
;;
+
usr/lib/python3/dist-packages/*)
+
# Skip this line if the destination is a Python module
+
# because it will be handled by the Python build process
+
continue
+
;;
+
*)
+
echo "Error: Unknown destination prefix: '$dest_path'" >&2
+
exit 1
+
;;
+
esac
+
done < debian/install
+
'';
+
+
passthru.updateScript = gitUpdater { rev-prefix = "v"; };
+
+
meta = {
+
description = "Manages and restricts user screen time by enforcing time limits";
+
homepage = "https://mjasnik.gitlab.io/timekpr-next/";
+
license = lib.licenses.gpl3;
+
maintainers = [ lib.maintainers.atry ];
+
platforms = lib.platforms.linux;
+
};
+
}