From 5d28bc72ad184f1f532f9ce924bc88539fa29460 Mon Sep 17 00:00:00 2001 From: Dmitry Fedotov Date: Sat, 27 Dec 2025 12:05:29 +0300 Subject: [PATCH] feat: v0.1.0 Version I use myself. Fully functional, may contain bugs, use at your own risk. Co-authored-by: Dmitry Fedotov Co-committed-by: Dmitry Fedotov --- .gitignore | 2 + LICENSE | 2 +- Makefile | 4 + README.md | 205 ++++++++++++++++++++++++++++++++++- config.toml | 181 +++++++++++++++++++++++++++++++ userctx.py | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 692 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 config.toml create mode 100755 userctx.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..834b16b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +test diff --git a/LICENSE b/LICENSE index 8f002ff..a15fac3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 dmitry +Copyright (c) 2025 Dmitry Fedotov 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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d393e8e --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +.PNONY: install + +install: + sudo install userctx.py /usr/local/bin/userctx diff --git a/README.md b/README.md index 0377cca..dc71f45 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,206 @@ # userctx -A tool for switching user's software configs \ No newline at end of file +Manage you configurations and themes with ease. + +## Installation +You just need to copy the file to a directory +in your $PATH (for example: /usr/loca/bin) and +copy/create a config file. +```bash +sudo install userctx.py /usr/local/bin/userctx +mkdir -p ~/.config/userctx +cp config.toml ~/.config/userctx +``` + +## userctx basics +**userctx** manages configuration *contexts*. A context is a directory somewhere +in your system which stores actual configuration files for your apps +similar to what resides in ~/.config. + +Typically a context directory looks like this. +```bash +/home/dmitry/.config/userctx/Goldfish +├── sway +│   └── theme.conf +└── wofi + └── style.css + +``` +In the above example, context "Goldfish" contains configs for apps "sway" and "wofi". +These configs will be applied when you apply context "Goldfish". + +When a context is applied the app configs from context directory are get +symlinked to youe ~/.config/ folder for each managed app. Goldfish/wofi/style.css symlinks to +\~/.config/wofi/style.css, Goldfish/sway/theme.conf symlinks to \~/.config/sway/theme.conf etc. + +This default behaviour can be changed and specific actions or customizations +can be configured on per-app basis by editing **userctx** config file. +The default location for the config is ~/.config/userctx/config.toml. + +**userctx** can also be configured to run scripts, trigger your apps to +reload configs etc. For a detailed overview of all config options see below. + +There are two commands **userctx** understands: *list* and *apply*. +- *list* lists available contexts in a manner suitable for dmenu-like apps +- *apply* \ applies named context + +To test your configuration run: +```bash +userctx --nop apply +``` +The program will then only output what it will do without changing +anything in your home directory. + +For a quickstart and simple examples jump to "Examples" section below. + +## Configuring userctx +**userctx** is configured through a TOML file located at `~/.config/userctx/config.toml` by default. The configuration is split into two main sections: `[general]` and `[apps]`. + +### The `[general]` section +This section contains global settings for **userctx**. + +- `dry_run` (boolean, optional): If set to `true`, **userctx** will only print the actions it would take without making any changes to the filesystem. This is useful for testing your configuration. The command-line flag `--nop` overrides this setting. +- `apps` (array of strings, required): A list of application names that **userctx** should manage. For each application in this list, **userctx** will look for a corresponding configuration section in the `[apps]` table. +- `source_path` (string, optional): The base path where your contexts are stored. Defaults to `$XDG_CONFIG_HOME/userctx` or `~/.config/userctx`. +- `target_path` (string, optional): The base path where application configurations are located. Defaults to `$XDG_CONFIG_HOME` or `~/.config`. + +### The `[apps]` section +For each application defined in the `general.apps` array, you can have a dedicated section to specify its configuration. + +You can create **[apps.]** section in the config to specify **userctx** behaviour for the app. The following options are supported. + +- `source_path` (string, optional): Overrides the global `source_path` for this specific application. +- `target_path` (string, optional): Overrides the global `target_path` for this specific application. The final destination path for an application's configuration will be `/`. +- `actions` (array of strings, optional): A list of actions to perform for the application. The available actions are `symlink`, `script`, `exec`, and `reload`. If not specified, the default actions are `[symlink, script, exec, reload]` if the corresponding keys (`symlink`, `script`, `exec`, `reload`) are present in the app's configuration. +- `symlink` (table, optional): A map of source files to destination files for creating symlinks. The source is relative to the context's application directory (e.g., `//`), and the destination is relative to the application's `target_path`. You can use `*` as a wildcard to match all files in the source directory. +- `exec` (string, optional): A shell command or script to execute. The script is executed in a shell environment with the following environment variables set: + - `CONTEXT_NAME`: The name of the context being applied. + - `CONTEXT_SRC`: The source directory for the current application's context. + - `CONTEXT_DST`: The target directory for the current application's configuration. +- `script` (array of strings, optional): A list of script files to execute. The scripts are looked for in the application's context directory. If the list is empty or not provided, **userctx** will look for and execute any file ending with `.sh` in the context directory. +- `reload` (string, optional): A shell command to execute to reload the application's configuration. This is typically used to make the application aware of the changes applied by **userctx**. + +TODO: a more detailed explanation of wildcard symlinking. + +## Examples +### Basic usecase: we only need symlinks +Let's add configuration for "foot" terminal emulator, which +will be applied when we apply context "Goldfish" assuming +we would like to switch foot's visual theme when swithching context. + +1. First we need to create a separate file for visuals config in our +context directory. + +```bash +mkdir ~/.config/userctx/Goldfish/foot/ +vim ~/.config/userctx/Goldfish/foot/theme.ini +``` +And put the awesome "Tempus Day" into theme.ini: +``` +# -*- conf -*- +# theme: Tempus Day +# author: Protesilaos Stavrou (https://protesilaos.com) +# description: Light theme with warm colours (WCAG AA compliant) + +[colors] +foreground = 464340 +# original background +# background = f8f2e5 +background = ffffff +regular0 = 464340 +regular1 = c81000 +regular2 = 107410 +regular3 = 806000 +regular4 = 385dc4 +regular5 = b63052 +regular6 = 007070 +regular7 = e7e3d7 +bright0 = 68607d +bright1 = b24000 +bright2 = 427040 +bright3 = 6f6600 +bright4 = 0f64c4 +bright5 = 8050a7 +bright6 = 336c87 +bright7 = f8f2e5 +``` +You could as well symlink any file (pre-existing theme for foot) +to ~/.config/userctx/Goldfish/foot/theme.ini + +2. To include this theme file from main foot config add the following line +to $HOME/.config/foot/foot.ini (edit to match your user's homedir): +``` +include=/home/dmitry/.config/foot/theme.ini +``` +Do not forget to remove style settings from foot.ini so they do not +conflict with separate theme.ini. + +3. Finally, tell userctx to manage foot config for you. +Edit ~/.config/userctx/config.toml and add "foot" to "apps" +array in "general" section of the config. +``` +[general] +apps = [ + "foot", + "sway", + "wofi", +] +``` +4. Test you configuration +```bash +userctx --nop apply Goldfish +``` +The **--nop** (no-op) flag tells **userctx** to perform a dry-run. It will just output +what it is going to do when you actually apply context. + +If all looks good - that's it. When you issue **userctx apply Goldfish** +a symlink will be created in your homedir: + +~/.config/foot/theme.ini -> ~/.config/userctx/Goldfish/foot/theme.ini + +### More advanced usecase: run command and hot-reload +Let's configure **userctx** to apply theme to helix editor. + +1. Similar to the above section, create ~/.config/userctx/Goldfish/helix/helix.toml +with the following contents: +```toml +inherits = "github_light" +"ui.background" = {} +``` +Now edit ~/.config/userctx/config.toml. + +Add "helix" to "general" section. +```toml +[general] +apps = [ + "foot", + "sway", + "helix", + "wofi", +] +``` +2. Add "apps.helix" section: +```toml +[apps.helix] +symlink."*" = "themes/current_theme.toml" +exec = """sed -i -E 's/^theme = (.+)/theme = "current_theme"/' ~/.config/helix/config.toml""" +reload = "pkill -USR1 hx" +``` +Here we instruct **userctx** to symlink any (single) file it finds in "helix" subdirectory of context folder +to ~/.config/helix/themes/current_theme.toml + +Then we run sed to change the config file. This part is not really necessare if helix is +configured to use theme named "current_theme" and you're sure that config won't change. +We could just replace the file and issue USR1. The sed part if for the case when config +is changed by user or other app. + +Finally we set the reload command which +will tell helix to reload config. + +3. Test your configuration, +```bash +userctx --nop apply Goldfish +``` + +See also template "config.toml" with numerous app settings. + diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..509adf8 --- /dev/null +++ b/config.toml @@ -0,0 +1,181 @@ +[general] +# if dry run is set to true +# userctx will only validate config +# and tell you what it will do to apply context +# tasks +dry_run = true + +# "apps" array tells userctx what to do (which apps +# should be actually managed). If it is empty or not +# set, then userctx will do nothing. +# Uncomment apps below. +apps = [ + "sway", + "wofi", +] + +# target directory where managed configs +# are located +# defaults to $XDG_CONFIG_HOME if specifically set +# or ~/.config if $XGD_CONFIG_HOME is not set +target_path = "~/.config" + +# directory where contexts are stored +# default is "~/.config/userctx" +source_path = "~/.config/userctx" + +# section that demonstrates all the available +# features of userctx +[apps.example] +# The default path to apply your context to is +# ~/.config/example (for app name "example") +# if not overridden with general.target_path. +# Setting "target_path" overrides this. +# $CONTEXT_DST env will hold the actual destination path. +# symlinks will be created relative to this specified +# path. +target_path = "~/.config/example/configs" + +# "symlink" map defines +# which files will be symlinked to users home +# directory. +# Names of files in this mapping are relative to +# context directory and target directory +# by default source is ~/.config/userctx//example +# and target directory is ~/.config/example +# for app named "example" +# +# this will link file from context dir to destination dir +# $CONTEXT_SRC/context_file.conf -> $CONTEXT_DST/symlink_to_context_file.conf +symlink."context_file.conf" = "symlink_to_context_file.conf" + +# this will link file to subdirectory inside destination dir +# Note that destination subdir must be created beforehand. +# $CONTEXT_SRC/other_file.conf -> $CONTEXT_DST/subdir/other_file.conf +symlink."other_file.conf" = "subdir/other_file.conf" +symlink."subdir/yet_another_file.conf" = "yet_another_file.conf" + +# If symlinks mapping is not empty then only instructions from the map +# will be applied. If you want to redefine linking rules only for single +# file and symlink others according to default rules then add the following +# rule as well. +symlink."*" = "*" + +# As a special case, providing the following mapping symlinks +# a SINGLE file (the first one found) from context directory +# to a specified name (see helix section below). +# This reads "symlinks ANY file you find to this path". +# symlink."*" = "some/destination.conf" + + +# "exec" let's you write a reload command or +# even a bigger custom script that will apply contents of +# your context. Note the triple double-qoutes for multiline +# strings (see TOML spec). +# This string will be passed as is to $SHELL for execution. +exec = """ + # this is a normal shell script which will + # be executed by user's $SHELL or 'bash' binary + # if users $SHELL is empty + + # users envs are available + echo "$HOME" + + # additional envs are available + echo "$CONTEXT_SRC" + echo "$CONTEXT_DST" + echo "$CONTEXT_NAME" +""" + +# if "script" array is not empty +# then userctx will try to find +# named files in $CONTEXT_SRC and +# execute them with $SHELL