feat: v0.1.0 #1

Merged
dmitry merged 12 commits from dev into main 2025-12-27 12:05:30 +03:00
3 changed files with 82 additions and 39 deletions
Showing only changes of commit 84ea17c4b2 - Show all commits

View File

@@ -29,8 +29,8 @@ Typically a context directory looks like this.
In the above example, context "Goldfish" contains configs for apps "sway" and "wofi". In the above example, context "Goldfish" contains configs for apps "sway" and "wofi".
These configs will be applied when you apply context "Goldfish". These configs will be applied when you apply context "Goldfish".
When a context is applied the app configs from context directory are When a context is applied the app configs from context directory are get
symlinked you ~/.config/ folder for each managed app. Goldfish/wofi/style.css symlinks to 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. \~/.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 This default behaviour can be changed and specific actions or customizations
@@ -41,7 +41,7 @@ The default location for the config is ~/.config/userctx/config.toml.
reload configs etc. For a detailed overview of all config options see below. reload configs etc. For a detailed overview of all config options see below.
There are two commands **userctx** understands: *list* and *apply*. There are two commands **userctx** understands: *list* and *apply*.
- *list* lists available contexts in a manner suitable for dmenu-like menu apps - *list* lists available contexts in a manner suitable for dmenu-like apps
- *apply* \<context\> applies named context - *apply* \<context\> applies named context
To test your configuration run: To test your configuration run:
@@ -54,7 +54,33 @@ anything in your home directory.
For a quickstart and simple examples jump to "Examples" section below. For a quickstart and simple examples jump to "Examples" section below.
## Configuring userctx ## Configuring userctx
TODO: full description of config options. **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.<you_app>]' section in the config to specify **userctx** behaviour for the app. The following option 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 `<target_path>/<app_name>`.
- `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., `<source_path>/<context_name>/<app_name>`), 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 ## Examples
### Basic usecase: we only need symlinks ### Basic usecase: we only need symlinks
@@ -62,7 +88,7 @@ Let's add configuration for "foot" terminal emulator, which
will be applied when we apply context "Goldfish" assuming will be applied when we apply context "Goldfish" assuming
we would like to switch foot's visual theme when swithching context. 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 out 1. First we need to create a separate file for visuals config in our
context directory. context directory.
```bash ```bash
@@ -143,7 +169,7 @@ inherits = "github_light"
``` ```
Now edit ~/.config/userctx/config.toml. Now edit ~/.config/userctx/config.toml.
Add the "helix" to "general" section. Add "helix" to "general" section.
```toml ```toml
[general] [general]
apps = [ apps = [

View File

@@ -1,7 +1,7 @@
[general] [general]
# if dry run is set to true # if dry run is set to true
# userctx will only validate config # userctx will only validate config
# and tell you what it will do to execute # and tell you what it will do to apply context
# tasks # tasks
dry_run = true dry_run = true
@@ -10,16 +10,16 @@ dry_run = true
# set, then userctx will do nothing. # set, then userctx will do nothing.
# Uncomment apps below. # Uncomment apps below.
apps = [ apps = [
#"niri", "niri",
#"fuzzel", "fuzzel",
#"gtk", "gtk",
#"helix", "helix",
#"kitty", "kitty",
#"mako", "mako",
#"sway", "sway",
#"swaybg", "swaybg",
#"waybar", "waybar",
#"wofi", "wofi",
] ]
# target directory where managed configs # target directory where managed configs
@@ -30,24 +30,11 @@ target_path = "~/.config"
# directory where contexts are stored # directory where contexts are stored
# default is "~/.config/userctx" # default is "~/.config/userctx"
source_path = "~/code/userctx/test" source_path = "~/.config/userctx"
# section that demonstrates all the available # section that demonstrates all the available
# features of userctx # features of userctx
[apps.example] [apps.example]
# Path to appliaction configs is read from
# general.source_path. The default source path is
# ~/.config/userctx/<context_name>/example for this
# app.
# Individual path for app can be provided via
# "source_path" for particular app. Then userctx will try to lookup
# app settings in /source_path/<context_name>
# Default is general.source_path/<context_name>/<app_name>.
# For this example it will expand to
# ~/.config/userctx/<context_name/example.
# TODO: do we really need this?
source_path = "~/tmp/contexts"
# The default path to apply your context to is # The default path to apply your context to is
# ~/.config/example (for app name "example") # ~/.config/example (for app name "example")
# if not overridden with general.target_path. # if not overridden with general.target_path.
@@ -60,7 +47,7 @@ target_path = "~/.config/example/configs"
# "symlink" map defines # "symlink" map defines
# which files will be symlinked to users home # which files will be symlinked to users home
# directory. # directory.
# Names of files in thes mapping are relative to # Names of files in this mapping are relative to
# context directory and target directory # context directory and target directory
# by default source is ~/.config/userctx/<context_name>/example # by default source is ~/.config/userctx/<context_name>/example
# and target directory is ~/.config/example # and target directory is ~/.config/example
@@ -74,7 +61,19 @@ symlink."context_file.conf" = "symlink_to_context_file.conf"
# Note that destination subdir must be created beforehand. # Note that destination subdir must be created beforehand.
# $CONTEXT_SRC/other_file.conf -> $CONTEXT_DST/subdir/other_file.conf # $CONTEXT_SRC/other_file.conf -> $CONTEXT_DST/subdir/other_file.conf
symlink."other_file.conf" = "subdir/other_file.conf" symlink."other_file.conf" = "subdir/other_file.conf"
symlink."subdir/yet_another_file.conf" = "yet_another_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 # "exec" let's you write a reload command or
@@ -96,7 +95,8 @@ exec = """
echo "$CONTEXT_NAME" echo "$CONTEXT_NAME"
""" """
# scripts array will try to find # if "script" array is not empty
# then userctx will try to find
# named files in $CONTEXT_SRC and # named files in $CONTEXT_SRC and
# execute them with $SHELL <script> # execute them with $SHELL <script>
# IMPORTANT: if array is empty # IMPORTANT: if array is empty
@@ -104,13 +104,30 @@ exec = """
# "actions" array above then all files # "actions" array above then all files
# in $CONTEXT_SRC will be treated as scripts # in $CONTEXT_SRC will be treated as scripts
# (note the "apps.gtk section below") # (note the "apps.gtk section below")
scripts = [ script = [
"script1.sh", "script1.sh",
"script2.sh", "script2.sh",
] ]
# if "reload" is present, the command will be executed after everythong alse is done
reload = "pkill -USR1 example" reload = "pkill -USR1 example"
# "actions" array specifies the order of actions when
# applying context. If omitted, the default behaviour
# it to symlink, run scripts, run exec part then run reload
# command (if relevant actions are specified).
# If nothig has been customized in apps configuration here
# (no commands array, not symlinks mapping etc.) then userctx
# will just symlink anythong it finds to target_path.
# If actions array is declared empty (actions = []) then
# userctx will do nothig.
actions = [
"symlink",
"exec",
"script",
"reload",
]
######################### #########################
# settings for some # settings for some
# commonly used apps # commonly used apps

View File

@@ -232,10 +232,10 @@ class ContextManager(object):
def __init__(self, config: ContextManagerConfig): def __init__(self, config: ContextManagerConfig):
self.config = config self.config = config
def print_contexts_list(self) -> None: def list_contexts(self) -> None:
print('\n'.join(self.config.get_contexts_list())) print('\n'.join(self.config.get_contexts_list()))
def load_context(self, context_name: str) -> None: def apply_context(self, context_name: str) -> None:
print(f'loading and applying context: "{context_name}"') print(f'loading and applying context: "{context_name}"')
try: try:
tasks = self.config.get_tasks_for_context(context_name) tasks = self.config.get_tasks_for_context(context_name)
@@ -284,9 +284,9 @@ class Runner(object):
try: try:
ctxmgr = ContextManager(config) ctxmgr = ContextManager(config)
if args.command == COMMAND_LIST: if args.command == COMMAND_LIST:
ctxmgr.print_contexts_list() ctxmgr.list_contexts()
elif args.command == COMMAND_APPLY: elif args.command == COMMAND_APPLY:
ctxmgr.load_context(args.__dict__[COMMAND_CTX_NAME]) # guaranteed to be present by argparse ctxmgr.apply_context(args.__dict__[COMMAND_CTX_NAME]) # guaranteed to be present by argparse
except Exception as e: except Exception as e:
print(f'error executing command:', e) print(f'error executing command:', e)
return 3 return 3