mirror of
https://github.com/nix-community/home-manager.git
synced 2025-11-08 11:36:05 +01:00
Compare commits
41 commits
a179f834ad
...
d6ea5063ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6ea5063ae | ||
|
|
34fe48801d | ||
|
|
1c75dd7022 | ||
|
|
0a5a165aca | ||
|
|
c39c07bf31 | ||
|
|
aa6936bb63 | ||
|
|
65bf99c579 | ||
|
|
6feb368511 | ||
|
|
5cdf9ef995 | ||
|
|
1342b821db | ||
|
|
9ff9a94fd4 | ||
|
|
c740351870 | ||
|
|
083b20c1a0 | ||
|
|
1f34c2c855 | ||
|
|
95d65dddae | ||
|
|
a5fee07792 | ||
|
|
9d6e28fd32 | ||
|
|
e22fb25cde | ||
|
|
3c16ac3646 | ||
|
|
64c49b1aa5 | ||
|
|
c93684cd87 | ||
|
|
8c824254b1 | ||
|
|
2318e30ea1 | ||
|
|
b5ed4afc22 | ||
|
|
7503ffb0b0 | ||
|
|
b4350d54c2 | ||
|
|
090aa14e5d | ||
|
|
32a671dce5 | ||
|
|
ab0d3db1aa | ||
|
|
9901bb6afc | ||
|
|
9f3a82bfd1 | ||
|
|
acf7743c89 | ||
|
|
983cbdc75c | ||
|
|
d9cd40d2da | ||
|
|
61f2cc5908 | ||
|
|
50a5766d51 | ||
|
|
0fe68257a9 | ||
|
|
371608e69c | ||
|
|
43e205606a | ||
|
|
5eaa0072ff | ||
|
|
363797f8a9 |
91 changed files with 2360 additions and 152 deletions
|
|
@ -24,7 +24,7 @@
|
||||||
.Cm | generations
|
.Cm | generations
|
||||||
.Cm | help
|
.Cm | help
|
||||||
.Cm | news
|
.Cm | news
|
||||||
.Cm | option Ar option.name
|
.Cm | option Oo Fl -recursive Oc Ar option.name
|
||||||
.Cm | packages
|
.Cm | packages
|
||||||
.Cm | remove-generations Ar ID \&...
|
.Cm | remove-generations Ar ID \&...
|
||||||
.Cm | switch
|
.Cm | switch
|
||||||
|
|
@ -138,10 +138,14 @@ Show news entries in a pager.
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
|
|
||||||
.It Cm option Ar option.name
|
.It Cm option Oo Fl -recursive Oc Ar option.name
|
||||||
.RS 4
|
.RS 4
|
||||||
Inspect the given option name in the home configuration, like
|
Inspect the given option name in the home configuration, like
|
||||||
\fBnixos-option\fR(8)\&.
|
\fBnixos-option\fR(8)\&.
|
||||||
|
.sp
|
||||||
|
If the
|
||||||
|
.Fl -recursive
|
||||||
|
option is given, print all the values at or below the option name recursively\&.
|
||||||
.RE
|
.RE
|
||||||
.Pp
|
.Pp
|
||||||
|
|
||||||
|
|
|
||||||
6
flake.lock
generated
6
flake.lock
generated
|
|
@ -2,11 +2,11 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761373498,
|
"lastModified": 1762111121,
|
||||||
"narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
|
"narHash": "sha256-4vhDuZ7OZaZmKKrnDpxLZZpGIJvAeMtK6FKLJYUtAdw=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
|
"rev": "b3d51a0365f6695e7dd5cdf3e180604530ed33b4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,29 @@ function doInspectOption() {
|
||||||
fi
|
fi
|
||||||
setConfigFile
|
setConfigFile
|
||||||
|
|
||||||
local extraArgs=("$@")
|
local paths=()
|
||||||
|
local recursive=false
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
local opt="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
case $opt in
|
||||||
|
--recursive)
|
||||||
|
recursive=true;;
|
||||||
|
*)
|
||||||
|
# Remove trailing dot if exists, match the behavior of
|
||||||
|
# old nixos-option and make shell completions happy
|
||||||
|
paths+=("${opt%.}")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#paths[@]} -eq 0 ]]; then
|
||||||
|
paths=("")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local extraArgs=()
|
||||||
|
|
||||||
for p in "${EXTRA_NIX_PATH[@]}"; do
|
for p in "${EXTRA_NIX_PATH[@]}"; do
|
||||||
extraArgs=("${extraArgs[@]}" "-I" "$p")
|
extraArgs=("${extraArgs[@]}" "-I" "$p")
|
||||||
|
|
@ -256,11 +278,24 @@ function doInspectOption() {
|
||||||
modulesExpr+=" configuration = if confAttr == \"\" then confPath else (import confPath).\${confAttr};"
|
modulesExpr+=" configuration = if confAttr == \"\" then confPath else (import confPath).\${confAttr};"
|
||||||
modulesExpr+=" pkgs = import <nixpkgs> {}; check = true; })"
|
modulesExpr+=" pkgs = import <nixpkgs> {}; check = true; })"
|
||||||
|
|
||||||
nixos-option \
|
local NIXOS_OPTION_CMD NIXOS_OPTION_REAL NIXOS_OPTION_DIR NIXOS_OPTION_NIX
|
||||||
--options_expr "$modulesExpr.options" \
|
NIXOS_OPTION_CMD=$(command -v nixos-option)
|
||||||
--config_expr "$modulesExpr.config" \
|
NIXOS_OPTION_REAL=$(realpath "${NIXOS_OPTION_CMD}")
|
||||||
"${extraArgs[@]}" \
|
NIXOS_OPTION_NIX=$(nix-store -q --references "${NIXOS_OPTION_REAL}" | grep 'nixos-option\.nix$')
|
||||||
"${PASSTHROUGH_OPTS[@]}"
|
|
||||||
|
if [[ ! -f "$NIXOS_OPTION_NIX" ]]; then
|
||||||
|
_iError "nixos-option.nix not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for path in "${paths[@]}"; do
|
||||||
|
nix-instantiate --eval --json \
|
||||||
|
--arg nixos "$modulesExpr" \
|
||||||
|
--argstr path "$path" \
|
||||||
|
--arg recursive "$recursive" \
|
||||||
|
"$NIXOS_OPTION_NIX" \
|
||||||
|
| jq -r
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
function doInit() {
|
function doInit() {
|
||||||
|
|
@ -1063,9 +1098,11 @@ function doHelp() {
|
||||||
echo
|
echo
|
||||||
echo " edit Open the home configuration in \$VISUAL or \$EDITOR"
|
echo " edit Open the home configuration in \$VISUAL or \$EDITOR"
|
||||||
echo
|
echo
|
||||||
echo " option OPTION.NAME"
|
echo " option [--recursive] OPTION.NAME"
|
||||||
echo " Inspect configuration option named OPTION.NAME."
|
echo " Inspect configuration option named OPTION.NAME."
|
||||||
echo
|
echo
|
||||||
|
echo " --recursive Print all the values at or below the option name recursively."
|
||||||
|
echo
|
||||||
echo " build Build configuration into result directory"
|
echo " build Build configuration into result directory"
|
||||||
echo
|
echo
|
||||||
echo " init [--switch] [DIR]"
|
echo " init [--switch] [DIR]"
|
||||||
|
|
@ -1211,6 +1248,16 @@ while [[ $# -gt 0 ]]; do
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
|
--recursive)
|
||||||
|
case $COMMAND in
|
||||||
|
option)
|
||||||
|
COMMAND_ARGS+=("$opt")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
errTopLevelSubcommandOpt "--recursive" "option"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
--option|--arg|--argstr)
|
--option|--arg|--argstr)
|
||||||
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
||||||
[[ -v 2 ]] || errMissingOptArg "$opt $1"
|
[[ -v 2 ]] || errMissingOptArg "$opt $1"
|
||||||
|
|
|
||||||
9
modules/misc/news/2025/10/2025-10-31_12-00-00.nix
Normal file
9
modules/misc/news/2025/10/2025-10-31_12-00-00.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
time = "2025-10-31T12:00:00+00:00";
|
||||||
|
condition = true;
|
||||||
|
message = ''
|
||||||
|
services.local-ai: new module
|
||||||
|
|
||||||
|
Added LocalAI, a free, Open Source OpenAI alternative.
|
||||||
|
'';
|
||||||
|
}
|
||||||
25
modules/misc/news/2025/11/2025-11-03_11-18-15.nix
Normal file
25
modules/misc/news/2025/11/2025-11-03_11-18-15.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
time = "2025-11-03T10:18:15+00:00";
|
||||||
|
condition = true;
|
||||||
|
message = ''
|
||||||
|
A new module 'programs.mcp' is now available for managing Model
|
||||||
|
Context Protocol (MCP) server configurations.
|
||||||
|
|
||||||
|
The 'programs.mcp.servers' option allows you to define MCP servers
|
||||||
|
in a central location. These configurations can be automatically
|
||||||
|
integrated into applications that support MCP.
|
||||||
|
|
||||||
|
Two modules now support MCP integration:
|
||||||
|
|
||||||
|
- 'programs.opencode.enableMcpIntegration': Integrates MCP servers
|
||||||
|
into OpenCode's configuration.
|
||||||
|
|
||||||
|
- 'programs.vscode.profiles.<name>.enableMcpIntegration': Integrates
|
||||||
|
MCP servers into VSCode profiles.
|
||||||
|
|
||||||
|
When integration is enabled, servers from 'programs.mcp.servers' are
|
||||||
|
merged with application-specific MCP settings, with the latter taking
|
||||||
|
precedence. This allows you to define MCP servers once and reuse them
|
||||||
|
across multiple applications.
|
||||||
|
'';
|
||||||
|
}
|
||||||
20
modules/misc/news/2025/11/2025-11-03_20-33-15.nix
Normal file
20
modules/misc/news/2025/11/2025-11-03_20-33-15.nix
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
time = "2025-11-04T02:33:15+00:00";
|
||||||
|
condition = pkgs.stdenv.hostPlatform.isLinux;
|
||||||
|
message = ''
|
||||||
|
A new program is available: 'programs.vicinae'.
|
||||||
|
|
||||||
|
Vicinae is a modern application launcher daemon for Linux with support for
|
||||||
|
extensions, custom themes, and layer shell integration.
|
||||||
|
|
||||||
|
The module provides:
|
||||||
|
- Systemd service integration with automatic start support
|
||||||
|
- Extension management with helpers for Vicinae and Raycast extensions
|
||||||
|
- Theme configuration support
|
||||||
|
- Declarative settings via 'programs.vicinae.settings'
|
||||||
|
- Layer shell integration for Wayland compositors
|
||||||
|
|
||||||
|
See the module options for more details on configuration.
|
||||||
|
'';
|
||||||
|
}
|
||||||
14
modules/misc/news/2025/11/2025-11-03_22-56-50.nix
Normal file
14
modules/misc/news/2025/11/2025-11-03_22-56-50.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
time = "2025-11-03T21:56:50+00:00";
|
||||||
|
condition = config.programs.ghostty.enable && pkgs.stdenv.hostPlatform.isLinux;
|
||||||
|
message = ''
|
||||||
|
Ghostty: now enables the user systemd service by default.
|
||||||
|
|
||||||
|
Running Ghostty via these systemd units is the recommended way to run
|
||||||
|
Ghostty. The two most important benefits provided by Ghostty's systemd
|
||||||
|
integrations are: instantaneous launching and centralized logging.
|
||||||
|
|
||||||
|
See https://ghostty.org/docs/linux/systemd for all details
|
||||||
|
'';
|
||||||
|
}
|
||||||
11
modules/misc/news/2025/11/2025-11-04_13-00-00.nix
Normal file
11
modules/misc/news/2025/11/2025-11-04_13-00-00.nix
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
time = "2025-11-04T13:00:00+00:00";
|
||||||
|
condition = true;
|
||||||
|
message = ''
|
||||||
|
A new module is available: 'programs.opkssh'.
|
||||||
|
|
||||||
|
opkssh is a tool which enables ssh to be used with OpenID Connect allowing SSH access to be managed via identities instead of long-lived SSH keys. It does not replace SSH, but instead generates SSH public keys containing PK Tokens and configures sshd to verify them. These PK Tokens contain standard OpenID Connect ID Tokens.
|
||||||
|
|
||||||
|
This protocol builds on the OpenPubkey which adds user public keys to OpenID Connect without breaking compatibility with existing OpenID Provider.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -9,38 +9,249 @@ let
|
||||||
|
|
||||||
cfg = config.systemd.user.tmpfiles;
|
cfg = config.systemd.user.tmpfiles;
|
||||||
|
|
||||||
in
|
ruleSubmodule = lib.types.submodule (
|
||||||
{
|
{ name, ... }:
|
||||||
meta.maintainers = [ lib.maintainers.dawidsowa ];
|
{
|
||||||
|
options.type = lib.mkOption {
|
||||||
options.systemd.user.tmpfiles.rules = lib.mkOption {
|
type = lib.types.str;
|
||||||
type = lib.types.listOf lib.types.str;
|
readOnly = true;
|
||||||
default = [ ];
|
default = name;
|
||||||
example = [ "L /home/user/Documents - - - - /mnt/data/Documents" ];
|
defaultText = "‹tmpfiles-type›";
|
||||||
|
example = "d";
|
||||||
description = ''
|
description = ''
|
||||||
Rules for creating and cleaning up temporary files
|
The type of operation to perform on the file.
|
||||||
automatically. See
|
|
||||||
{manpage}`tmpfiles.d(5)`
|
The type consists of a single letter and optionally one or more
|
||||||
for the exact format.
|
modifier characters.
|
||||||
|
|
||||||
|
Please see the upstream documentation for the available types and
|
||||||
|
more details: {manpage}`tmpfiles.d(5)`
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.mode = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "-";
|
||||||
|
example = "0755";
|
||||||
|
description = ''
|
||||||
|
The file access mode to use when creating this file or directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.user = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "-";
|
||||||
|
example = "root";
|
||||||
|
description = ''
|
||||||
|
The user of the file.
|
||||||
|
|
||||||
|
This may either be a numeric ID or a user/group name.
|
||||||
|
|
||||||
|
If omitted or when set to `"-"`, the user and group of the user who
|
||||||
|
invokes systemd-tmpfiles is used.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.group = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "-";
|
||||||
|
example = "root";
|
||||||
|
description = ''
|
||||||
|
The group of the file.
|
||||||
|
|
||||||
|
This may either be a numeric ID or a user/group name.
|
||||||
|
|
||||||
|
If omitted or when set to `"-"`, the user and group of the user who
|
||||||
|
invokes systemd-tmpfiles is used.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.age = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "-";
|
||||||
|
example = "10d";
|
||||||
|
description = ''
|
||||||
|
Delete a file when it reaches a certain age.
|
||||||
|
|
||||||
|
If a file or directory is older than the current time minus the age
|
||||||
|
field, it is deleted.
|
||||||
|
|
||||||
|
If set to `"-"`, no automatic clean-up is done.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
options.argument = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
example = "";
|
||||||
|
description = ''
|
||||||
|
An argument whose meaning depends on the type of operation.
|
||||||
|
|
||||||
|
Please see the upstream documentation for the meaning of this
|
||||||
|
parameter in different situations: {manpage}`tmpfiles.d(5)`
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
attrsWith' = placeholder: elemType: lib.types.attrsWith { inherit elemType placeholder; };
|
||||||
|
|
||||||
|
nonEmptyAttrsWith' =
|
||||||
|
placeholder: elemType:
|
||||||
|
let
|
||||||
|
attrs = lib.types.addCheck (attrsWith' placeholder elemType) (s: s != { });
|
||||||
|
in
|
||||||
|
attrs
|
||||||
|
// {
|
||||||
|
name = "nonEmptyAttrsOf";
|
||||||
|
description = "non-empty ${attrs.description}";
|
||||||
|
emptyValue = { }; # no .value attribute, meaning there is not empty value
|
||||||
|
substSubModules = m: nonEmptyAttrsWith' placeholder (elemType.substSubModules m);
|
||||||
|
};
|
||||||
|
|
||||||
|
configSubmodule = lib.types.submodule {
|
||||||
|
options.rules = lib.mkOption {
|
||||||
|
description = "The rules contained in this configuration.";
|
||||||
|
example = {
|
||||||
|
"%C".d = {
|
||||||
|
mode = "0755";
|
||||||
|
user = "alice";
|
||||||
|
group = "alice";
|
||||||
|
age = "4 weeks";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
type = nonEmptyAttrsWith' "path" (nonEmptyAttrsWith' "tmpfiles-type" ruleSubmodule);
|
||||||
|
};
|
||||||
|
options.purgeOnChange = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Whether the rules that are marked for purging, will automatically
|
||||||
|
be purged when the set of rules changes.
|
||||||
|
|
||||||
|
See {manpage}`systemd-tmpfiles(8)` for details about purging.
|
||||||
|
'';
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
modulePrefix = [
|
||||||
|
"systemd"
|
||||||
|
"user"
|
||||||
|
"tmpfiles"
|
||||||
|
"settings"
|
||||||
|
];
|
||||||
|
|
||||||
|
mkFileName = configName: "user-tmpfiles.d/home-manager-${configName}.conf";
|
||||||
|
|
||||||
|
mkConfigFile =
|
||||||
|
name: rules:
|
||||||
|
{
|
||||||
|
suffix ? [ name ],
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
escapeArgument = lib.strings.escapeC [
|
||||||
|
"\t"
|
||||||
|
"\n"
|
||||||
|
"\r"
|
||||||
|
" "
|
||||||
|
"\\"
|
||||||
|
];
|
||||||
|
mkRule = path: rule: ''
|
||||||
|
'${rule.type}' '${path}' '${rule.mode}' '${rule.user}' '${rule.group}' '${rule.age}' ${escapeArgument rule.argument}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
text = ''
|
||||||
|
# This file was generated by Home Manager and should not be modified.
|
||||||
|
# Please change the option '${lib.showAttrPath (modulePrefix ++ suffix)}' instead.
|
||||||
|
${lib.pipe rules [
|
||||||
|
(lib.mapAttrs (_path: lib.attrValues))
|
||||||
|
(lib.mapAttrsToList (path: map (mkRule path)))
|
||||||
|
lib.flatten
|
||||||
|
lib.concatStrings
|
||||||
|
]}
|
||||||
|
'';
|
||||||
|
onChange = ''
|
||||||
|
run ${pkgs.systemd}/bin/systemd-tmpfiles --user --remove --create ''${DRY_RUN:+--dry-run} '${config.xdg.configHome}/${mkFileName name}'
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (cfg.rules != [ ]) {
|
nonPurgedConfigs = lib.filterAttrs (_name: config: !config.purgeOnChange) cfg.settings;
|
||||||
|
purgedConfigs = lib.filterAttrs (_name: config: config.purgeOnChange) cfg.settings;
|
||||||
|
|
||||||
|
# WARNING: When changing this path, the next home-manager generation will
|
||||||
|
# not find and the rules of the old generation that are subject to purging.
|
||||||
|
purgedRulesConfigName = "purge-on-change";
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
meta.maintainers = with lib.maintainers; [
|
||||||
|
bmrips
|
||||||
|
dawidsowa
|
||||||
|
];
|
||||||
|
|
||||||
|
imports = [
|
||||||
|
(lib.mkRemovedOptionModule [ "systemd" "user" "tmpfiles" "rules" ] ''
|
||||||
|
It has been replaced by 'systemd.user.tmpfiles.settings'.
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
|
||||||
|
options.systemd.user.tmpfiles.settings = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Declare systemd-tmpfiles rules to create, delete, and clean up volatile
|
||||||
|
and temporary files and directories.
|
||||||
|
|
||||||
|
Even though the service is called `*tmp*files` you can also create
|
||||||
|
persistent files.
|
||||||
|
'';
|
||||||
|
example = {
|
||||||
|
cache.rules."%C".d = {
|
||||||
|
mode = "0755";
|
||||||
|
user = "alice";
|
||||||
|
group = "alice";
|
||||||
|
age = "4 weeks";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = { };
|
||||||
|
type = attrsWith' "config-name" configSubmodule;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
|
||||||
|
(lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
|
||||||
|
# The activation script must be enabled unconditionally in order to
|
||||||
|
# guarantee that the old rules are purged even if the new set of rules
|
||||||
|
# is empty, i.e. `cfg.rulesToPurgeOnChange == [ ]`.
|
||||||
|
home.activation.purgeTmpfiles = lib.hm.dag.entryAfter [ "writeBoundary" ] (
|
||||||
|
let
|
||||||
|
relativeXdgConfigHome = lib.strings.removePrefix "${config.home.homeDirectory}/" config.xdg.configHome;
|
||||||
|
configPath = "home-files/${relativeXdgConfigHome}/${purgedRulesConfigName}";
|
||||||
|
in
|
||||||
|
''
|
||||||
|
if [[ -v oldGenPath && -f $oldGenPath/${configPath} ]] &&
|
||||||
|
diff -q $oldGenPath/${configPath} $newGenPath/${configPath} &>/dev/null; then
|
||||||
|
verboseEcho "Purge old tmpfiles"
|
||||||
|
run ${pkgs.systemd}/bin/systemd-tmpfiles --user --purge ''${DRY_RUN:+--dry-run} $oldGenPath/${configPath}
|
||||||
|
fi
|
||||||
|
''
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
(lib.mkIf (cfg.settings != { }) {
|
||||||
assertions = [
|
assertions = [
|
||||||
(lib.hm.assertions.assertPlatform "systemd.user.tmpfiles" pkgs lib.platforms.linux)
|
(lib.hm.assertions.assertPlatform "systemd.user.tmpfiles" pkgs lib.platforms.linux)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
warnings = lib.flatten (
|
||||||
|
lib.mapAttrsToListRecursive (
|
||||||
|
path: value:
|
||||||
|
lib.optional
|
||||||
|
(lib.last path == "argument" && lib.match ''.*\\([nrt]|x[0-9A-Fa-f]{2}).*'' value != null)
|
||||||
|
''
|
||||||
|
The '${lib.showAttrPath (modulePrefix ++ path)}' option
|
||||||
|
appears to contain escape sequences, which will be escaped again.
|
||||||
|
Unescape them if this is not intended. The assigned string is:
|
||||||
|
"${value}"
|
||||||
|
''
|
||||||
|
) cfg.settings
|
||||||
|
);
|
||||||
|
|
||||||
xdg.configFile = {
|
xdg.configFile = {
|
||||||
"user-tmpfiles.d/home-manager.conf" = {
|
|
||||||
text = ''
|
|
||||||
# This file is created automatically and should not be modified.
|
|
||||||
# Please change the option ‘systemd.user.tmpfiles.rules’ instead.
|
|
||||||
${lib.concatStringsSep "\n" cfg.rules}
|
|
||||||
'';
|
|
||||||
onChange = ''
|
|
||||||
run ${pkgs.systemd}/bin/systemd-tmpfiles --user --remove --create ''${DRY_RUN:+--dry-run}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"systemd/user/basic.target.wants/systemd-tmpfiles-setup.service".source =
|
"systemd/user/basic.target.wants/systemd-tmpfiles-setup.service".source =
|
||||||
"${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-setup.service";
|
"${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-setup.service";
|
||||||
"systemd/user/systemd-tmpfiles-setup.service".source =
|
"systemd/user/systemd-tmpfiles-setup.service".source =
|
||||||
|
|
@ -49,6 +260,18 @@ in
|
||||||
"${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-clean.timer";
|
"${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-clean.timer";
|
||||||
"systemd/user/systemd-tmpfiles-clean.service".source =
|
"systemd/user/systemd-tmpfiles-clean.service".source =
|
||||||
"${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-clean.service";
|
"${pkgs.systemd}/example/systemd/user/systemd-tmpfiles-clean.service";
|
||||||
|
}
|
||||||
|
// lib.mapAttrs' (
|
||||||
|
name: config: lib.nameValuePair (mkFileName name) (mkConfigFile name config.rules { })
|
||||||
|
) nonPurgedConfigs
|
||||||
|
// lib.optionalAttrs (purgedConfigs != { }) {
|
||||||
|
${mkFileName purgedRulesConfigName} =
|
||||||
|
let
|
||||||
|
purgedConfigsMerged = lib.foldl' lib.recursiveUpdate { } (lib.attrValues purgedConfigs);
|
||||||
|
in
|
||||||
|
mkConfigFile purgedRulesConfigName purgedConfigsMerged.rules { suffix = [ ]; };
|
||||||
};
|
};
|
||||||
};
|
})
|
||||||
|
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,7 @@ let
|
||||||
};
|
};
|
||||||
erase = mkEnableOption "remove bind";
|
erase = mkEnableOption "remove bind";
|
||||||
silent = mkEnableOption "Operate silently";
|
silent = mkEnableOption "Operate silently";
|
||||||
|
repaint = mkEnableOption "redraw prompt after command";
|
||||||
operate = mkOption {
|
operate = mkOption {
|
||||||
description = "Operate on preset bindings or user bindings";
|
description = "Operate on preset bindings or user bindings";
|
||||||
type =
|
type =
|
||||||
|
|
@ -324,6 +325,7 @@ let
|
||||||
{
|
{
|
||||||
silent,
|
silent,
|
||||||
erase,
|
erase,
|
||||||
|
repaint,
|
||||||
operate,
|
operate,
|
||||||
mode,
|
mode,
|
||||||
setsMode,
|
setsMode,
|
||||||
|
|
@ -344,7 +346,11 @@ let
|
||||||
];
|
];
|
||||||
|
|
||||||
cmdNormal = lib.concatStringsSep " " (
|
cmdNormal = lib.concatStringsSep " " (
|
||||||
[ "bind" ] ++ opts ++ [ k ] ++ map lib.escapeShellArg (lib.flatten [ command ])
|
[ "bind" ]
|
||||||
|
++ opts
|
||||||
|
++ [ k ]
|
||||||
|
++ map lib.escapeShellArg (lib.flatten [ command ])
|
||||||
|
++ lib.optional repaint "repaint"
|
||||||
);
|
);
|
||||||
|
|
||||||
cmdErase = lib.concatStringsSep " " (
|
cmdErase = lib.concatStringsSep " " (
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,24 @@ in
|
||||||
defaultText = lib.literalMD "`true` if programs.ghostty.package is not null";
|
defaultText = lib.literalMD "`true` if programs.ghostty.package is not null";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = lib.mkEnableOption "the Ghostty systemd user service" // {
|
||||||
|
default = pkgs.stdenv.hostPlatform.isLinux;
|
||||||
|
defaultText = lib.literalMD "`true` on Linux, `false` otherwise";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Configuration for Ghostty's systemd integration.
|
||||||
|
This enables additional speed and features.
|
||||||
|
|
||||||
|
See <https://ghostty.org/docs/linux/systemd> for more information.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
enableBashIntegration = mkShellIntegrationOption (
|
enableBashIntegration = mkShellIntegrationOption (
|
||||||
lib.hm.shell.mkBashIntegrationOption { inherit config; }
|
lib.hm.shell.mkBashIntegrationOption { inherit config; }
|
||||||
);
|
);
|
||||||
|
|
@ -195,6 +213,22 @@ in
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
(lib.mkIf cfg.systemd.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.systemd.enable -> cfg.package != null;
|
||||||
|
message = "programs.ghostty.systemd.enable cannot be true when programs.ghostty.package is null";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.systemd.enable -> pkgs.stdenv.hostPlatform.isLinux;
|
||||||
|
message = "Ghostty systemd integration cannot be enabled for non-linux platforms";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
xdg.configFile."systemd/user/app-com.mitchellh.ghostty.service".source =
|
||||||
|
"${cfg.package}/share/systemd/user/app-com.mitchellh.ghostty.service";
|
||||||
|
dbus.packages = [ cfg.package ];
|
||||||
|
})
|
||||||
|
|
||||||
(lib.mkIf cfg.enableBashIntegration {
|
(lib.mkIf cfg.enableBashIntegration {
|
||||||
# Make order 101 to be placed exactly after bash completions, as Ghostty
|
# Make order 101 to be placed exactly after bash completions, as Ghostty
|
||||||
# documentation suggests sourcing the script as soon as possible
|
# documentation suggests sourcing the script as soon as possible
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,12 @@ in
|
||||||
|
|
||||||
# Use `systemd-tmpfiles` since glab requires its configuration file to have
|
# Use `systemd-tmpfiles` since glab requires its configuration file to have
|
||||||
# mode 0600.
|
# mode 0600.
|
||||||
systemd.user.tmpfiles.rules =
|
systemd.user.tmpfiles.settings.glab = lib.mkIf (cfg.settings != { }) {
|
||||||
let
|
rules."${config.xdg.configHome}/glab-cli/config.yml" = {
|
||||||
target = "${config.xdg.configHome}/glab-cli/config.yml";
|
"C+$".argument = "${yaml.generate "glab-config" cfg.settings}";
|
||||||
in
|
z.mode = "0600";
|
||||||
lib.mkIf (cfg.settings != { }) [
|
};
|
||||||
"C+ ${target} - - - - ${yaml.generate "glab-config" cfg.settings}"
|
};
|
||||||
"z ${target} 0600"
|
|
||||||
];
|
|
||||||
|
|
||||||
xdg.configFile."glab-cli/aliases.yml" = lib.mkIf (cfg.aliases != { }) {
|
xdg.configFile."glab-cli/aliases.yml" = lib.mkIf (cfg.aliases != { }) {
|
||||||
source = yaml.generate "glab-aliases" cfg.aliases;
|
source = yaml.generate "glab-aliases" cfg.aliases;
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ let
|
||||||
|
|
||||||
function importTrust() {
|
function importTrust() {
|
||||||
local keyIds trust
|
local keyIds trust
|
||||||
IFS='\n' read -ra keyIds <<< "$(gpgKeyId "$1")"
|
mapfile -t keyIds <<< "$(gpgKeyId "$1")"
|
||||||
trust="$2"
|
trust="$2"
|
||||||
for id in "''${keyIds[@]}" ; do
|
for id in "''${keyIds[@]}" ; do
|
||||||
{ echo trust; echo "$trust"; (( trust == 5 )) && echo y; echo quit; } \
|
{ echo trust; echo "$trust"; (( trust == 5 )) && echo y; echo quit; } \
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ let
|
||||||
mkKeyValue = key: command: "map ${key} ${command}";
|
mkKeyValue = key: command: "map ${key} ${command}";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
toKittyMouseBindings = lib.generators.toKeyValue {
|
||||||
|
mkKeyValue = key: command: "mouse_map ${key} ${command}";
|
||||||
|
};
|
||||||
|
|
||||||
toKittyActionAliases = lib.generators.toKeyValue {
|
toKittyActionAliases = lib.generators.toKeyValue {
|
||||||
mkKeyValue = alias_name: action: "action_alias ${alias_name} ${action}";
|
mkKeyValue = alias_name: action: "action_alias ${alias_name} ${action}";
|
||||||
};
|
};
|
||||||
|
|
@ -199,6 +203,18 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mouseBindings = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
default = { };
|
||||||
|
description = "Mapping of mouse bindings to actions.";
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
"ctrl+left click" = "ungrabbed mouse_handle_click selection link prompt";
|
||||||
|
"left click" = "ungrabbed no-op";
|
||||||
|
};
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
environment = mkOption {
|
environment = mkOption {
|
||||||
type = types.attrsOf types.str;
|
type = types.attrsOf types.str;
|
||||||
default = { };
|
default = { };
|
||||||
|
|
@ -316,7 +332,8 @@ in
|
||||||
(mkOrder 540 (toKittyConfig cfg.settings))
|
(mkOrder 540 (toKittyConfig cfg.settings))
|
||||||
(mkOrder 550 (toKittyActionAliases cfg.actionAliases))
|
(mkOrder 550 (toKittyActionAliases cfg.actionAliases))
|
||||||
(mkOrder 560 (toKittyKeybindings cfg.keybindings))
|
(mkOrder 560 (toKittyKeybindings cfg.keybindings))
|
||||||
(mkOrder 570 (toKittyEnv cfg.environment))
|
(mkOrder 570 (toKittyMouseBindings cfg.mouseBindings))
|
||||||
|
(mkOrder 580 (toKittyEnv cfg.environment))
|
||||||
];
|
];
|
||||||
|
|
||||||
xdg.configFile."kitty/kitty.conf" = {
|
xdg.configFile."kitty/kitty.conf" = {
|
||||||
|
|
|
||||||
64
modules/programs/mcp.nix
Normal file
64
modules/programs/mcp.nix
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
literalExpression
|
||||||
|
mkEnableOption
|
||||||
|
mkIf
|
||||||
|
mkOption
|
||||||
|
;
|
||||||
|
|
||||||
|
cfg = config.programs.mcp;
|
||||||
|
|
||||||
|
jsonFormat = pkgs.formats.json { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
meta.maintainers = with lib.maintainers; [ delafthi ];
|
||||||
|
|
||||||
|
options.programs.mcp = {
|
||||||
|
enable = mkEnableOption "mcp";
|
||||||
|
|
||||||
|
servers = mkOption {
|
||||||
|
inherit (jsonFormat) type;
|
||||||
|
default = { };
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
everything = {
|
||||||
|
command = "npx";
|
||||||
|
args = [
|
||||||
|
"-y"
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
context7 = {
|
||||||
|
url = "https://mcp.context7.com/mcp";
|
||||||
|
headers = {
|
||||||
|
CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
MCP server configurations written to
|
||||||
|
{file}`XDG_CONFIG_HOME/.config/mcp/mcp.json`
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
xdg.configFile = mkIf (cfg.servers != { }) (
|
||||||
|
let
|
||||||
|
mcp-config = {
|
||||||
|
mcpServers = cfg.servers;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
"mcp/mcp.json".source = jsonFormat.generate "mcp.json" mcp-config;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,35 @@ let
|
||||||
cfg = config.programs.opencode;
|
cfg = config.programs.opencode;
|
||||||
|
|
||||||
jsonFormat = pkgs.formats.json { };
|
jsonFormat = pkgs.formats.json { };
|
||||||
|
|
||||||
|
transformMcpServer = name: server: {
|
||||||
|
name = name;
|
||||||
|
value = {
|
||||||
|
enabled = !(server.disabled or false);
|
||||||
|
}
|
||||||
|
// (
|
||||||
|
if server ? url then
|
||||||
|
{
|
||||||
|
type = "remote";
|
||||||
|
url = server.url;
|
||||||
|
}
|
||||||
|
// (lib.optionalAttrs (server ? headers) { headers = server.headers; })
|
||||||
|
else if server ? command then
|
||||||
|
{
|
||||||
|
type = "local";
|
||||||
|
command = [ server.command ] ++ (server.args or [ ]);
|
||||||
|
}
|
||||||
|
// (lib.optionalAttrs (server ? env) { environment = server.env; })
|
||||||
|
else
|
||||||
|
{ }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
transformedMcpServers =
|
||||||
|
if cfg.enableMcpIntegration && config.programs.mcp.enable && config.programs.mcp.servers != { } then
|
||||||
|
lib.listToAttrs (lib.mapAttrsToList transformMcpServer config.programs.mcp.servers)
|
||||||
|
else
|
||||||
|
{ };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
meta.maintainers = with lib.maintainers; [ delafthi ];
|
meta.maintainers = with lib.maintainers; [ delafthi ];
|
||||||
|
|
@ -25,6 +54,20 @@ in
|
||||||
|
|
||||||
package = mkPackageOption pkgs "opencode" { nullable = true; };
|
package = mkPackageOption pkgs "opencode" { nullable = true; };
|
||||||
|
|
||||||
|
enableMcpIntegration = mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to integrate the MCP servers config from
|
||||||
|
{option}`programs.mcp.servers` into
|
||||||
|
{option}`programs.opencode.settings.mcp`.
|
||||||
|
|
||||||
|
Note: Settings defined in {option}`programs.mcp.servers` are merged
|
||||||
|
with {option}`programs.opencode.settings.mcp`, with OpenCode settings
|
||||||
|
taking precedence.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
settings = mkOption {
|
settings = mkOption {
|
||||||
inherit (jsonFormat) type;
|
inherit (jsonFormat) type;
|
||||||
default = { };
|
default = { };
|
||||||
|
|
@ -147,7 +190,7 @@ in
|
||||||
Custom themes for opencode. The attribute name becomes the theme
|
Custom themes for opencode. The attribute name becomes the theme
|
||||||
filename, and the value is either:
|
filename, and the value is either:
|
||||||
- An attribute set, that is converted to a json
|
- An attribute set, that is converted to a json
|
||||||
- A path to a file conaining the content
|
- A path to a file containing the content
|
||||||
Themes are stored in {file}`$XDG_CONFIG_HOME/opencode/themes/` directory.
|
Themes are stored in {file}`$XDG_CONFIG_HOME/opencode/themes/` directory.
|
||||||
Set `programs.opencode.settings.theme` to enable the custom theme.
|
Set `programs.opencode.settings.theme` to enable the custom theme.
|
||||||
See <https://opencode.ai/docs/themes/> for the documentation.
|
See <https://opencode.ai/docs/themes/> for the documentation.
|
||||||
|
|
@ -159,12 +202,20 @@ in
|
||||||
home.packages = mkIf (cfg.package != null) [ cfg.package ];
|
home.packages = mkIf (cfg.package != null) [ cfg.package ];
|
||||||
|
|
||||||
xdg.configFile = {
|
xdg.configFile = {
|
||||||
"opencode/config.json" = mkIf (cfg.settings != { }) {
|
"opencode/config.json" = mkIf (cfg.settings != { } || transformedMcpServers != { }) {
|
||||||
source = jsonFormat.generate "config.json" (
|
source =
|
||||||
|
let
|
||||||
|
# Merge MCP servers: transformed servers + user settings, with user settings taking precedence
|
||||||
|
mergedMcpServers = transformedMcpServers // (cfg.settings.mcp or { });
|
||||||
|
# Merge all settings
|
||||||
|
mergedSettings =
|
||||||
|
cfg.settings // (lib.optionalAttrs (mergedMcpServers != { }) { mcp = mergedMcpServers; });
|
||||||
|
in
|
||||||
|
jsonFormat.generate "config.json" (
|
||||||
{
|
{
|
||||||
"$schema" = "https://opencode.ai/config.json";
|
"$schema" = "https://opencode.ai/config.json";
|
||||||
}
|
}
|
||||||
// cfg.settings
|
// mergedSettings
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
59
modules/programs/opkssh.nix
Normal file
59
modules/programs/opkssh.nix
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.programs.opkssh;
|
||||||
|
|
||||||
|
yamlFormat = pkgs.formats.yaml { };
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
meta.maintainers = [ lib.maintainers.swarsel ];
|
||||||
|
|
||||||
|
options.programs.opkssh = {
|
||||||
|
enable = lib.mkEnableOption "enable the OpenPubkey SSH client";
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "opkssh" { nullable = true; };
|
||||||
|
|
||||||
|
settings = lib.mkOption {
|
||||||
|
inherit (yamlFormat) type;
|
||||||
|
default = { };
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
default_provider = "kanidm";
|
||||||
|
|
||||||
|
providers = [
|
||||||
|
{
|
||||||
|
alias = "kanidm";
|
||||||
|
issuer = "https://idm.example.com/oauth2/openid/opkssh";
|
||||||
|
client_id = "opkssh";
|
||||||
|
scopes = "openid email profile";
|
||||||
|
redirect_uris = [
|
||||||
|
"http://localhost:3000/login-callback"
|
||||||
|
"http://localhost:10001/login-callback"
|
||||||
|
"http://localhost:11110/login-callback"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
];
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Configuration written to {file}`$HOME/.opk/config.yml`.
|
||||||
|
See <https://github.com/openpubkey/opkssh/blob/main/docs/config.md#client-config-opkconfigyml>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
|
||||||
|
|
||||||
|
home.file."${config.home.homeDirectory}/.opk/config.yml" = lib.mkIf (cfg.settings != { }) {
|
||||||
|
source = yamlFormat.generate "opkssh-config-${config.home.username}.yml" cfg.settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -111,6 +111,23 @@ in
|
||||||
options = {
|
options = {
|
||||||
enable = lib.mkEnableOption "this mount";
|
enable = lib.mkEnableOption "this mount";
|
||||||
|
|
||||||
|
logLevel = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (
|
||||||
|
lib.types.enum [
|
||||||
|
"ERROR"
|
||||||
|
"NOTICE"
|
||||||
|
"INFO"
|
||||||
|
"DEBUG"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
default = null;
|
||||||
|
example = "INFO";
|
||||||
|
description = ''
|
||||||
|
Set the log-level.
|
||||||
|
See: https://rclone.org/docs/#logging
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
mountPoint = lib.mkOption {
|
mountPoint = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = null;
|
default = null;
|
||||||
|
|
@ -348,12 +365,13 @@ in
|
||||||
Environment = [
|
Environment = [
|
||||||
# fusermount/fusermount3
|
# fusermount/fusermount3
|
||||||
"PATH=/run/wrappers/bin"
|
"PATH=/run/wrappers/bin"
|
||||||
];
|
]
|
||||||
|
++ lib.optional (mount.logLevel != null) "RCLONE_LOG_LEVEL=${mount.logLevel}";
|
||||||
|
|
||||||
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${mount.mountPoint}";
|
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${mount.mountPoint}";
|
||||||
ExecStart = lib.concatStringsSep " " [
|
ExecStart = lib.concatStringsSep " " [
|
||||||
(lib.getExe cfg.package)
|
(lib.getExe cfg.package)
|
||||||
"mount"
|
"mount"
|
||||||
"-vv"
|
|
||||||
(lib.cli.toGNUCommandLineShell { } mount.options)
|
(lib.cli.toGNUCommandLineShell { } mount.options)
|
||||||
"${remote-name}:${mount-path}"
|
"${remote-name}:${mount-path}"
|
||||||
"${mount.mountPoint}"
|
"${mount.mountPoint}"
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,34 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
inherit (lib)
|
||||||
|
mkEnableOption
|
||||||
|
mkPackageOption
|
||||||
|
mkOption
|
||||||
|
mkIf
|
||||||
|
mkMerge
|
||||||
|
types
|
||||||
|
literalExpression
|
||||||
|
mapAttrs'
|
||||||
|
nameValuePair
|
||||||
|
;
|
||||||
|
|
||||||
cfg = config.programs.rio;
|
cfg = config.programs.rio;
|
||||||
|
|
||||||
settingsFormat = pkgs.formats.toml { };
|
settingsFormat = pkgs.formats.toml { };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.programs.rio = {
|
options.programs.rio = {
|
||||||
enable = lib.mkEnableOption null // {
|
enable = mkEnableOption null // {
|
||||||
description = ''
|
description = ''
|
||||||
Enable Rio, a terminal built to run everywhere, as a native desktop applications by
|
Enable Rio, a terminal built to run everywhere, as a native desktop applications by
|
||||||
Rust/WebGPU or even in the browsers powered by WebAssembly/WebGPU.
|
Rust/WebGPU or even in the browsers powered by WebAssembly/WebGPU.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "rio" { nullable = true; };
|
package = mkPackageOption pkgs "rio" { nullable = true; };
|
||||||
|
|
||||||
settings = lib.mkOption {
|
settings = mkOption {
|
||||||
type = settingsFormat.type;
|
type = settingsFormat.type;
|
||||||
default = { };
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
|
|
@ -28,20 +40,50 @@ in
|
||||||
<https://raphamorim.io/rio/docs/#configuration-file> for options.
|
<https://raphamorim.io/rio/docs/#configuration-file> for options.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
themes = mkOption {
|
||||||
|
type = with types; attrsOf (either settingsFormat.type path);
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Theme files written to {file}`$XDG_CONFIG_HOME/rio/themes/`. See
|
||||||
|
<https://rioterm.com/docs/config#building-your-own-theme> for
|
||||||
|
supported values.
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
foobar.colors = {
|
||||||
|
background = "#282a36";
|
||||||
|
green = "#50fa7b";
|
||||||
|
dim-green = "#06572f";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
meta.maintainers = [ lib.maintainers.otavio ];
|
meta.maintainers = [ lib.maintainers.otavio ];
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable (
|
config = mkIf cfg.enable (mkMerge [
|
||||||
lib.mkMerge [
|
|
||||||
{
|
{
|
||||||
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
|
home.packages = mkIf (cfg.package != null) [ cfg.package ];
|
||||||
}
|
}
|
||||||
|
|
||||||
# Only manage configuration if not empty
|
# Only manage configuration if not empty
|
||||||
(lib.mkIf (cfg.settings != { }) {
|
(mkIf (cfg.settings != { }) {
|
||||||
xdg.configFile."rio/config.toml".source =
|
xdg.configFile."rio/config.toml".source =
|
||||||
if lib.isPath cfg.settings then cfg.settings else settingsFormat.generate "rio.toml" cfg.settings;
|
if builtins.isPath cfg.settings then
|
||||||
|
cfg.settings
|
||||||
|
else
|
||||||
|
settingsFormat.generate "rio.toml" cfg.settings;
|
||||||
})
|
})
|
||||||
]
|
|
||||||
);
|
(mkIf (cfg.themes != { }) {
|
||||||
|
xdg.configFile = mapAttrs' (
|
||||||
|
name: value:
|
||||||
|
nameValuePair "rio/themes/${name}.toml" {
|
||||||
|
source =
|
||||||
|
if builtins.isPath value then value else settingsFormat.generate "rio-theme-${name}.toml" value;
|
||||||
|
}
|
||||||
|
) cfg.themes;
|
||||||
|
})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.programs.superfile;
|
cfg = config.programs.superfile;
|
||||||
|
|
||||||
tomlFormat = pkgs.formats.toml { };
|
tomlFormat = pkgs.formats.toml { };
|
||||||
|
jsonFormat = pkgs.formats.json { };
|
||||||
|
|
||||||
inherit (pkgs.stdenv.hostPlatform) isDarwin;
|
inherit (pkgs.stdenv.hostPlatform) isDarwin;
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
literalExpression
|
literalExpression
|
||||||
|
|
@ -23,6 +26,29 @@ let
|
||||||
types
|
types
|
||||||
hm
|
hm
|
||||||
;
|
;
|
||||||
|
|
||||||
|
pinnedFolderModule = types.submodule {
|
||||||
|
freeformType = jsonFormat.type;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "Nix Store";
|
||||||
|
description = ''
|
||||||
|
Name that will be shown.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
location = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
example = "/nix/store";
|
||||||
|
description = ''
|
||||||
|
Location of the pinned entry.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
meta.maintainers = [ hm.maintainers.LucasWagler ];
|
meta.maintainers = [ hm.maintainers.LucasWagler ];
|
||||||
|
|
@ -106,11 +132,38 @@ in
|
||||||
};
|
};
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
firstUseCheck = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Enables the first time use popup.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
pinnedFolders = mkOption {
|
||||||
|
type = types.listOf pinnedFolderModule;
|
||||||
|
default = [ ];
|
||||||
|
example = literalExpression ''
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name = "Nix Store";
|
||||||
|
location = "/nix/store";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Entries that get added to the pinned panel.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config =
|
config =
|
||||||
let
|
let
|
||||||
enableXdgConfig = !isDarwin || config.xdg.enable;
|
enableXdgConfig = !isDarwin || config.xdg.enable;
|
||||||
|
baseConfigPath = if enableXdgConfig then "superfile" else "Library/Application Support/superfile";
|
||||||
|
baseDataPath = if enableXdgConfig then "superfile" else "Library/Application Support/superfile";
|
||||||
|
|
||||||
themeSetting =
|
themeSetting =
|
||||||
if (!(cfg.settings ? theme) && cfg.themes != { }) then
|
if (!(cfg.settings ? theme) && cfg.themes != { }) then
|
||||||
{
|
{
|
||||||
|
|
@ -118,7 +171,6 @@ in
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ };
|
{ };
|
||||||
baseConfigPath = if enableXdgConfig then "superfile" else "Library/Application Support/superfile";
|
|
||||||
configFile = mkIf (cfg.settings != { }) {
|
configFile = mkIf (cfg.settings != { }) {
|
||||||
"${baseConfigPath}/config.toml".source = tomlFormat.generate "superfile-config.toml" (
|
"${baseConfigPath}/config.toml".source = tomlFormat.generate "superfile-config.toml" (
|
||||||
recursiveUpdate themeSetting cfg.settings
|
recursiveUpdate themeSetting cfg.settings
|
||||||
|
|
@ -139,14 +191,33 @@ in
|
||||||
(tomlFormat.generate "superfile-theme-${name}.toml" value);
|
(tomlFormat.generate "superfile-theme-${name}.toml" value);
|
||||||
}
|
}
|
||||||
) cfg.themes;
|
) cfg.themes;
|
||||||
|
|
||||||
|
firstUseCheckFile = mkIf (!cfg.firstUseCheck) { "${baseDataPath}/firstUseCheck".text = ""; };
|
||||||
|
pinnedFile = mkIf (cfg.pinnedFolders != [ ]) {
|
||||||
|
"${baseDataPath}/pinned.json".source = jsonFormat.generate "pinned.json" cfg.pinnedFolders;
|
||||||
|
};
|
||||||
|
|
||||||
|
files = mkMerge [
|
||||||
|
configFile
|
||||||
|
hotkeysFile
|
||||||
|
themeFiles
|
||||||
|
|
||||||
|
firstUseCheckFile
|
||||||
|
pinnedFile
|
||||||
|
];
|
||||||
configFiles = mkMerge [
|
configFiles = mkMerge [
|
||||||
configFile
|
configFile
|
||||||
hotkeysFile
|
hotkeysFile
|
||||||
themeFiles
|
themeFiles
|
||||||
];
|
];
|
||||||
|
dataFiles = mkMerge [
|
||||||
|
firstUseCheckFile
|
||||||
|
pinnedFile
|
||||||
|
];
|
||||||
in
|
in
|
||||||
mkIf cfg.enable {
|
mkIf cfg.enable {
|
||||||
home.packages = mkIf (cfg.package != null) (
|
home = {
|
||||||
|
packages = mkIf (cfg.package != null) (
|
||||||
[ cfg.package ]
|
[ cfg.package ]
|
||||||
++ optional (
|
++ optional (
|
||||||
cfg.metadataPackage != null && cfg.settings ? metadata && cfg.settings.metadata
|
cfg.metadataPackage != null && cfg.settings ? metadata && cfg.settings.metadata
|
||||||
|
|
@ -156,7 +227,12 @@ in
|
||||||
) cfg.zoxidePackage
|
) cfg.zoxidePackage
|
||||||
);
|
);
|
||||||
|
|
||||||
xdg.configFile = mkIf enableXdgConfig configFiles;
|
file = mkIf (!enableXdgConfig) files;
|
||||||
home.file = mkIf (!enableXdgConfig) configFiles;
|
};
|
||||||
|
|
||||||
|
xdg = {
|
||||||
|
configFile = mkIf enableXdgConfig configFiles;
|
||||||
|
dataFile = mkIf enableXdgConfig dataFiles;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "taskwarrior" {
|
package = lib.mkPackageOption pkgs "taskwarrior2" {
|
||||||
nullable = true;
|
nullable = true;
|
||||||
example = "pkgs.taskwarrior3";
|
example = "pkgs.taskwarrior3";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
244
modules/programs/vicinae.nix
Normal file
244
modules/programs/vicinae.nix
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.programs.vicinae;
|
||||||
|
|
||||||
|
jsonFormat = pkgs.formats.json { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
meta.maintainers = [ lib.maintainers.leiserfg ];
|
||||||
|
|
||||||
|
options.programs.vicinae = {
|
||||||
|
enable = lib.mkEnableOption "vicinae launcher daemon";
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "vicinae" { nullable = true; };
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
enable = lib.mkEnableOption "vicinae systemd integration";
|
||||||
|
|
||||||
|
autoStart = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "If the vicinae daemon should be started automatically";
|
||||||
|
};
|
||||||
|
|
||||||
|
target = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "graphical-session.target";
|
||||||
|
example = "sway-session.target";
|
||||||
|
description = ''
|
||||||
|
The systemd target that will automatically start the vicinae service.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
useLayerShell = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "If vicinae should use the layer shell";
|
||||||
|
};
|
||||||
|
|
||||||
|
extensions = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.package;
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
List of Vicinae extensions to install.
|
||||||
|
|
||||||
|
You can use the `config.lib.vicinae.mkExtension` and `config.lib.vicinae.mkRayCastExtension` functions to create them, like:
|
||||||
|
```nix
|
||||||
|
[
|
||||||
|
(config.lib.vicinae.mkExtension {
|
||||||
|
name = "test-extension";
|
||||||
|
src =
|
||||||
|
pkgs.fetchFromGitHub {
|
||||||
|
owner = "schromp";
|
||||||
|
repo = "vicinae-extensions";
|
||||||
|
rev = "f8be5c89393a336f773d679d22faf82d59631991";
|
||||||
|
sha256 = "sha256-zk7WIJ19ITzRFnqGSMtX35SgPGq0Z+M+f7hJRbyQugw=";
|
||||||
|
}
|
||||||
|
+ "/test-extension";
|
||||||
|
})
|
||||||
|
(config.lib.vicinae.mkRayCastExtension {
|
||||||
|
name = "gif-search";
|
||||||
|
sha256 = "sha256-G7il8T1L+P/2mXWJsb68n4BCbVKcrrtK8GnBNxzt73Q=";
|
||||||
|
rev = "4d417c2dfd86a5b2bea202d4a7b48d8eb3dbaeb1";
|
||||||
|
})
|
||||||
|
],
|
||||||
|
```
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
themes = lib.mkOption {
|
||||||
|
inherit (jsonFormat) type;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Theme settings to add to the themes folder in `~/.config/vicinae/themes`.
|
||||||
|
|
||||||
|
The attribute name of the theme will be the name of theme json file,
|
||||||
|
e.g. `base16-default-dark` will be `base16-default-dark.json`.
|
||||||
|
'';
|
||||||
|
example =
|
||||||
|
lib.literalExpression # nix
|
||||||
|
''
|
||||||
|
{
|
||||||
|
base16-default-dark = {
|
||||||
|
version = "1.0.0";
|
||||||
|
appearance = "dark";
|
||||||
|
icon = /path/to/icon.png;
|
||||||
|
name = "base16 default dark";
|
||||||
|
description = "base16 default dark by Chris Kempson";
|
||||||
|
palette = {
|
||||||
|
background = "#181818";
|
||||||
|
foreground = "#d8d8d8";
|
||||||
|
blue = "#7cafc2";
|
||||||
|
green = "#a3be8c";
|
||||||
|
magenta = "#ba8baf";
|
||||||
|
orange = "#dc9656";
|
||||||
|
purple = "#a16946";
|
||||||
|
red = "#ab4642";
|
||||||
|
yellow = "#f7ca88";
|
||||||
|
cyan = "#86c1b9";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = lib.mkOption {
|
||||||
|
inherit (jsonFormat) type;
|
||||||
|
default = { };
|
||||||
|
description = "Settings written as JSON to `~/.config/vicinae/vicinae.json.";
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
faviconService = "twenty";
|
||||||
|
font = {
|
||||||
|
size = 10;
|
||||||
|
};
|
||||||
|
popToRootOnClose = false;
|
||||||
|
rootSearch = {
|
||||||
|
searchFiles = false;
|
||||||
|
};
|
||||||
|
theme = {
|
||||||
|
name = "vicinae-dark";
|
||||||
|
};
|
||||||
|
window = {
|
||||||
|
csd = true;
|
||||||
|
opacity = 0.95;
|
||||||
|
rounding = 10;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
(lib.hm.assertions.assertPlatform "programs.vicinae" pkgs lib.platforms.linux)
|
||||||
|
{
|
||||||
|
assertion = cfg.systemd.enable -> cfg.package != null;
|
||||||
|
message = "{option}programs.vicinae.systemd.enable requires non null {option}programs.vicinae.package";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
lib.vicinae.mkExtension = (
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
src,
|
||||||
|
}:
|
||||||
|
(pkgs.buildNpmPackage {
|
||||||
|
inherit name src;
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r /build/.local/share/vicinae/extensions/${name}/* $out/
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
npmDeps = pkgs.importNpmLock { npmRoot = src; };
|
||||||
|
npmConfigHook = pkgs.importNpmLock.npmConfigHook;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
lib.vicinae.mkRayCastExtension = (
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
sha256,
|
||||||
|
rev,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
src =
|
||||||
|
pkgs.fetchgit {
|
||||||
|
inherit rev sha256;
|
||||||
|
url = "https://github.com/raycast/extensions";
|
||||||
|
sparseCheckout = [
|
||||||
|
"/extensions/${name}"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
+ "/extensions/${name}";
|
||||||
|
in
|
||||||
|
(pkgs.buildNpmPackage {
|
||||||
|
inherit name src;
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r /build/.config/raycast/extensions/${name}/* $out/
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
npmDeps = pkgs.importNpmLock { npmRoot = src; };
|
||||||
|
npmConfigHook = pkgs.importNpmLock.npmConfigHook;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
|
||||||
|
|
||||||
|
xdg = {
|
||||||
|
configFile = {
|
||||||
|
"vicinae/vicinae.json" = lib.mkIf (cfg.settings != { }) {
|
||||||
|
source = jsonFormat.generate "vicinae-settings" cfg.settings;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.mapAttrs' (
|
||||||
|
name: theme:
|
||||||
|
lib.nameValuePair "vicinae/themes/${name}.json" {
|
||||||
|
source = jsonFormat.generate "vicinae-${name}-theme" theme;
|
||||||
|
}
|
||||||
|
) cfg.themes;
|
||||||
|
|
||||||
|
dataFile = builtins.listToAttrs (
|
||||||
|
builtins.map (item: {
|
||||||
|
name = "vicinae/extensions/${item.name}";
|
||||||
|
value.source = item;
|
||||||
|
}) cfg.extensions
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.user.services.vicinae = lib.mkIf (cfg.systemd.enable && cfg.package != null) {
|
||||||
|
Unit = {
|
||||||
|
Description = "Vicinae server daemon";
|
||||||
|
Documentation = [ "https://docs.vicinae.com" ];
|
||||||
|
After = [ cfg.systemd.target ];
|
||||||
|
PartOf = [ cfg.systemd.target ];
|
||||||
|
BindsTo = [ cfg.systemd.target ];
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
EnvironmentFile = pkgs.writeText "vicinae-env" ''
|
||||||
|
USE_LAYER_SHELL=${if cfg.useLayerShell then builtins.toString 1 else builtins.toString 0}
|
||||||
|
'';
|
||||||
|
Type = "simple";
|
||||||
|
ExecStart = "${lib.getExe' cfg.package "vicinae"} server";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = 5;
|
||||||
|
KillMode = "process";
|
||||||
|
};
|
||||||
|
Install = lib.mkIf cfg.systemd.autoStart {
|
||||||
|
WantedBy = [ cfg.systemd.target ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -114,6 +114,33 @@ let
|
||||||
|
|
||||||
isPath = p: builtins.isPath p || lib.isStorePath p;
|
isPath = p: builtins.isPath p || lib.isStorePath p;
|
||||||
|
|
||||||
|
transformMcpServerForVscode =
|
||||||
|
name: server:
|
||||||
|
let
|
||||||
|
# Remove the disabled field from the server config
|
||||||
|
cleanServer = lib.filterAttrs (n: v: n != "disabled") server;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = name;
|
||||||
|
value = {
|
||||||
|
enabled = !(server.disabled or false);
|
||||||
|
}
|
||||||
|
// (
|
||||||
|
if server ? url then
|
||||||
|
{
|
||||||
|
type = "http";
|
||||||
|
}
|
||||||
|
// cleanServer
|
||||||
|
else if server ? command then
|
||||||
|
{
|
||||||
|
type = "stdio";
|
||||||
|
}
|
||||||
|
// cleanServer
|
||||||
|
else
|
||||||
|
{ }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
profileType = types.submodule {
|
profileType = types.submodule {
|
||||||
options = {
|
options = {
|
||||||
userSettings = mkOption {
|
userSettings = mkOption {
|
||||||
|
|
@ -154,6 +181,20 @@ let
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enableMcpIntegration = mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to integrate the MCP servers config from
|
||||||
|
{option}`programs.mcp.servers` into
|
||||||
|
{option}`programs.vscode.profiles.<name>.userMcp`.
|
||||||
|
|
||||||
|
Note: Settings defined in {option}`programs.mcp.servers` are merged
|
||||||
|
with {option}`programs.vscode.profiles.<name>.userMcp`, with VSCode
|
||||||
|
settings taking precedence.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
userMcp = mkOption {
|
userMcp = mkOption {
|
||||||
type = types.either types.path jsonFormat.type;
|
type = types.either types.path jsonFormat.type;
|
||||||
default = { };
|
default = { };
|
||||||
|
|
@ -418,7 +459,7 @@ in
|
||||||
existing_profiles=$(jq '.userDataProfiles // [] | map({ (.name): .location }) | add // {}' "$file")
|
existing_profiles=$(jq '.userDataProfiles // [] | map({ (.name): .location }) | add // {}' "$file")
|
||||||
|
|
||||||
for profile in "''${profiles[@]}"; do
|
for profile in "''${profiles[@]}"; do
|
||||||
if [[ "$(echo $existing_profiles | jq --arg profile $profile 'has ($profile)')" != "true" ]] || [[ "$(echo $existing_profiles | jq --arg profile $profile 'has ($profile)')" == "true" && "$(echo $existing_profiles | jq --arg profile $profile '.[$profile]')" != "\"$profile\"" ]]; then
|
if [[ "$(echo $existing_profiles | jq --arg profile "$profile" 'has ($profile)')" != "true" ]] || [[ "$(echo $existing_profiles | jq --arg profile "$profile" 'has ($profile)')" == "true" && "$(echo $existing_profiles | jq --arg profile "$profile" '.[$profile]')" != "\"$profile\"" ]]; then
|
||||||
file_write="$file_write$([ "$file_write" != "" ] && echo "...")$profile"
|
file_write="$file_write$([ "$file_write" != "" ] && echo "...")$profile"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
@ -459,10 +500,31 @@ in
|
||||||
if isPath v.userTasks then v.userTasks else jsonFormat.generate "vscode-user-tasks" v.userTasks;
|
if isPath v.userTasks then v.userTasks else jsonFormat.generate "vscode-user-tasks" v.userTasks;
|
||||||
})
|
})
|
||||||
|
|
||||||
(mkIf (v.userMcp != { }) {
|
(mkIf
|
||||||
|
(
|
||||||
|
v.userMcp != { }
|
||||||
|
|| (v.enableMcpIntegration && config.programs.mcp.enable && config.programs.mcp.servers != { })
|
||||||
|
)
|
||||||
|
{
|
||||||
"${mcpFilePath n}".source =
|
"${mcpFilePath n}".source =
|
||||||
if isPath v.userMcp then v.userMcp else jsonFormat.generate "vscode-user-mcp" v.userMcp;
|
if isPath v.userMcp then
|
||||||
})
|
v.userMcp
|
||||||
|
else
|
||||||
|
let
|
||||||
|
transformedMcpServers =
|
||||||
|
if v.enableMcpIntegration && config.programs.mcp.enable && config.programs.mcp.servers != { } then
|
||||||
|
lib.listToAttrs (lib.mapAttrsToList transformMcpServerForVscode config.programs.mcp.servers)
|
||||||
|
else
|
||||||
|
{ };
|
||||||
|
# Merge MCP servers: transformed servers + user servers, with user servers taking precedence
|
||||||
|
mergedServers = transformedMcpServers // ((v.userMcp.servers or { }));
|
||||||
|
# Merge all MCP config
|
||||||
|
mergedMcpConfig =
|
||||||
|
v.userMcp // (lib.optionalAttrs (mergedServers != { }) { servers = mergedServers; });
|
||||||
|
in
|
||||||
|
jsonFormat.generate "vscode-user-mcp" mergedMcpConfig;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
(mkIf (v.keybindings != [ ]) {
|
(mkIf (v.keybindings != [ ]) {
|
||||||
"${keybindingsFilePath n}".source =
|
"${keybindingsFilePath n}".source =
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ in
|
||||||
function ${cfg.shellWrapperName}() {
|
function ${cfg.shellWrapperName}() {
|
||||||
local tmp="$(mktemp -t "yazi-cwd.XXXXX")"
|
local tmp="$(mktemp -t "yazi-cwd.XXXXX")"
|
||||||
yazi "$@" --cwd-file="$tmp"
|
yazi "$@" --cwd-file="$tmp"
|
||||||
if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
|
if cwd="$(<"$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
|
||||||
builtin cd -- "$cwd"
|
builtin cd -- "$cwd"
|
||||||
fi
|
fi
|
||||||
rm -f -- "$tmp"
|
rm -f -- "$tmp"
|
||||||
|
|
@ -231,7 +231,7 @@ in
|
||||||
fishIntegration = ''
|
fishIntegration = ''
|
||||||
set -l tmp (mktemp -t "yazi-cwd.XXXXX")
|
set -l tmp (mktemp -t "yazi-cwd.XXXXX")
|
||||||
command yazi $argv --cwd-file="$tmp"
|
command yazi $argv --cwd-file="$tmp"
|
||||||
if set cwd (cat -- "$tmp"); and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
|
if read cwd < "$tmp"; and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
|
||||||
builtin cd -- "$cwd"
|
builtin cd -- "$cwd"
|
||||||
end
|
end
|
||||||
rm -f -- "$tmp"
|
rm -f -- "$tmp"
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,15 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mutableUserDebug = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
example = false;
|
||||||
|
description = ''
|
||||||
|
Whether user debug configurations (debug.json) can be updated by zed.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
userSettings = mkOption {
|
userSettings = mkOption {
|
||||||
type = jsonFormat.type;
|
type = jsonFormat.type;
|
||||||
default = { };
|
default = { };
|
||||||
|
|
@ -140,6 +149,27 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
userDebug = mkOption {
|
||||||
|
type = jsonFormat.type;
|
||||||
|
default = [ ];
|
||||||
|
example = literalExpression ''
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label = "Go (Delve)";
|
||||||
|
adapter = "Delve";
|
||||||
|
program = "$ZED_FILE";
|
||||||
|
request = "launch";
|
||||||
|
mode = "debug";
|
||||||
|
}
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Configuration written to Zed's {file}`debug.json`.
|
||||||
|
|
||||||
|
Global debug configurations for Zed's [Debugger](https://zed.dev/docs/debugger).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
extensions = mkOption {
|
extensions = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
|
|
@ -241,6 +271,14 @@ in
|
||||||
(jsonFormat.generate "zed-user-tasks" cfg.userTasks)
|
(jsonFormat.generate "zed-user-tasks" cfg.userTasks)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
(mkIf (cfg.mutableUserDebug && cfg.userDebug != [ ]) {
|
||||||
|
zedDebugActivation = lib.hm.dag.entryAfter [ "linkGeneration" ] (
|
||||||
|
impureConfigMerger "[]"
|
||||||
|
"$dynamic + $static | group_by(.label) | map(reduce .[] as $item ({}; . * $item))"
|
||||||
|
"${config.xdg.configHome}/zed/debug.json"
|
||||||
|
(jsonFormat.generate "zed-user-debug" cfg.userDebug)
|
||||||
|
);
|
||||||
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
xdg.configFile = mkMerge [
|
xdg.configFile = mkMerge [
|
||||||
|
|
@ -265,6 +303,9 @@ in
|
||||||
(mkIf (!cfg.mutableUserTasks && cfg.userTasks != [ ]) {
|
(mkIf (!cfg.mutableUserTasks && cfg.userTasks != [ ]) {
|
||||||
"zed/tasks.json".source = jsonFormat.generate "zed-user-tasks" cfg.userTasks;
|
"zed/tasks.json".source = jsonFormat.generate "zed-user-tasks" cfg.userTasks;
|
||||||
})
|
})
|
||||||
|
(mkIf (!cfg.mutableUserDebug && cfg.userDebug != [ ]) {
|
||||||
|
"zed/debug.json".source = jsonFormat.generate "zed-user-debug" cfg.userDebug;
|
||||||
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
assertions = [
|
assertions = [
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,13 @@ in
|
||||||
services.cbatticon = {
|
services.cbatticon = {
|
||||||
enable = lib.mkEnableOption "cbatticon";
|
enable = lib.mkEnableOption "cbatticon";
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "cbatticon" { };
|
package = lib.mkPackageOption pkgs "cbatticon" {
|
||||||
|
example = "pkgs.batticonplus";
|
||||||
|
extraDescription = ''
|
||||||
|
Use {var}`pkgs.batticonplus`
|
||||||
|
for wayland support.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
commandCriticalLevel = mkOption {
|
commandCriticalLevel = mkOption {
|
||||||
type = types.nullOr types.lines;
|
type = types.nullOr types.lines;
|
||||||
|
|
|
||||||
|
|
@ -19,25 +19,29 @@ let
|
||||||
|
|
||||||
inherit (config.programs.gpg) homedir;
|
inherit (config.programs.gpg) homedir;
|
||||||
|
|
||||||
gpgSshSupportStr = ''
|
gpgSshSupportStr = "${gpgPkg}/bin/gpg-connect-agent --quiet updatestartuptty /bye";
|
||||||
${gpgPkg}/bin/gpg-connect-agent --quiet updatestartuptty /bye
|
|
||||||
'';
|
|
||||||
|
|
||||||
gpgBashInitStr = ''
|
gpgBashInitStr = ''
|
||||||
GPG_TTY="$(tty)"
|
GPG_TTY="$(tty)"
|
||||||
export GPG_TTY
|
export GPG_TTY
|
||||||
''
|
''
|
||||||
+ optionalString cfg.enableSshSupport "${gpgSshSupportStr} > /dev/null";
|
+ optionalString cfg.enableSshSupport ''
|
||||||
|
${gpgSshSupportStr} > /dev/null
|
||||||
|
'';
|
||||||
|
|
||||||
gpgZshInitStr = ''
|
gpgZshInitStr = ''
|
||||||
export GPG_TTY=$TTY
|
export GPG_TTY=$TTY
|
||||||
''
|
''
|
||||||
+ optionalString cfg.enableSshSupport "${gpgSshSupportStr} > /dev/null";
|
+ optionalString cfg.enableSshSupport ''
|
||||||
|
${gpgSshSupportStr} > /dev/null
|
||||||
|
'';
|
||||||
|
|
||||||
gpgFishInitStr = ''
|
gpgFishInitStr = ''
|
||||||
set -gx GPG_TTY (tty)
|
set -gx GPG_TTY (tty)
|
||||||
''
|
''
|
||||||
+ optionalString cfg.enableSshSupport "${gpgSshSupportStr} > /dev/null";
|
+ optionalString cfg.enableSshSupport ''
|
||||||
|
${gpgSshSupportStr} > /dev/null
|
||||||
|
'';
|
||||||
|
|
||||||
gpgNushellInitStr = ''
|
gpgNushellInitStr = ''
|
||||||
$env.GPG_TTY = (tty)
|
$env.GPG_TTY = (tty)
|
||||||
|
|
|
||||||
48
modules/services/local-ai.nix
Normal file
48
modules/services/local-ai.nix
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib) types;
|
||||||
|
cfg = config.services.local-ai;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
meta.maintainers = [ lib.maintainers.ipsavitsky ];
|
||||||
|
|
||||||
|
options.services.local-ai = {
|
||||||
|
enable = lib.mkEnableOption "LocalAI is the free, Open Source OpenAI alternative.";
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "local-ai" { };
|
||||||
|
|
||||||
|
environment = lib.mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Additional environment passed to local-ai service. Used to configure local-ai
|
||||||
|
|
||||||
|
See <https://localai.io/basics> for available options.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
systemd.user.services.local-ai = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Server for local large language models";
|
||||||
|
After = [ "network.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
Service = {
|
||||||
|
ExecStart = lib.getExe cfg.package;
|
||||||
|
Environment = lib.mapAttrsToList (key: val: "${key}=${val}") cfg.environment;
|
||||||
|
};
|
||||||
|
|
||||||
|
Install = {
|
||||||
|
WantedBy = [ "default.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ in
|
||||||
options.services.taskwarrior-sync = {
|
options.services.taskwarrior-sync = {
|
||||||
enable = lib.mkEnableOption "Taskwarrior periodic sync";
|
enable = lib.mkEnableOption "Taskwarrior periodic sync";
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "taskwarrior" { example = "pkgs.taskwarrior3"; };
|
package = lib.mkPackageOption pkgs "taskwarrior2" { example = "pkgs.taskwarrior3"; };
|
||||||
|
|
||||||
frequency = lib.mkOption {
|
frequency = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ let
|
||||||
"spotify-player"
|
"spotify-player"
|
||||||
"starship"
|
"starship"
|
||||||
"superfile"
|
"superfile"
|
||||||
"taskwarrior"
|
"taskwarrior2"
|
||||||
"tealdeer"
|
"tealdeer"
|
||||||
"texlive"
|
"texlive"
|
||||||
"thefuck"
|
"thefuck"
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,7 @@ import nmtSrc {
|
||||||
./modules/misc/numlock
|
./modules/misc/numlock
|
||||||
./modules/misc/pam
|
./modules/misc/pam
|
||||||
./modules/misc/qt
|
./modules/misc/qt
|
||||||
|
./modules/misc/tmpfiles
|
||||||
./modules/misc/xdg/linux.nix
|
./modules/misc/xdg/linux.nix
|
||||||
./modules/misc/xsession
|
./modules/misc/xsession
|
||||||
./modules/systemd
|
./modules/systemd
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,12 @@ in
|
||||||
uid = 1000;
|
uid = 1000;
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.settings.age.rules."/home/alice/age-key".f = {
|
||||||
"f /home/alice/age-key 400 alice users - ${ageKey}"
|
mode = "400";
|
||||||
];
|
user = "alice";
|
||||||
|
group = "users";
|
||||||
|
argument = ageKey;
|
||||||
|
};
|
||||||
|
|
||||||
home-manager.users.alice =
|
home-manager.users.alice =
|
||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,12 @@
|
||||||
assert expected in actual, \
|
assert expected in actual, \
|
||||||
f"expected generations to contain {expected}, but found {actual}"
|
f"expected generations to contain {expected}, but found {actual}"
|
||||||
|
|
||||||
|
with subtest("Home Manager option"):
|
||||||
|
actual = succeed_as_alice("home-manager option home.username")
|
||||||
|
expected = "alice"
|
||||||
|
assert expected in actual, \
|
||||||
|
f"expected generations to contain {expected}, but found {actual}"
|
||||||
|
|
||||||
with subtest("Home Manager uninstallation"):
|
with subtest("Home Manager uninstallation"):
|
||||||
succeed_as_alice("yes | home-manager uninstall -L")
|
succeed_as_alice("yes | home-manager uninstall -L")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
font = {
|
font = {
|
||||||
name = "Ubuntu";
|
name = "Ubuntu";
|
||||||
size = 12;
|
size = 12;
|
||||||
package = pkgs.ubuntu_font_family;
|
package = pkgs.ubuntu-classic;
|
||||||
};
|
};
|
||||||
theme = {
|
theme = {
|
||||||
name = "Adwaita-dark";
|
name = "Adwaita-dark";
|
||||||
|
|
|
||||||
26
tests/modules/misc/tmpfiles/basic-rules.nix
Normal file
26
tests/modules/misc/tmpfiles/basic-rules.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
imports = [ ./common-stubs.nix ];
|
||||||
|
|
||||||
|
systemd.user.tmpfiles.settings = {
|
||||||
|
cache.rules."%C".d.age = "4 weeks";
|
||||||
|
myTool.rules."%h/.config/myTool.conf"."f+" = {
|
||||||
|
mode = "0644";
|
||||||
|
user = "alice";
|
||||||
|
group = "users";
|
||||||
|
argument = "my unescaped config";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertPathNotExists home-files/.config/user-tmpfiles.d/home-manager-purge-on-change.conf
|
||||||
|
|
||||||
|
cacheRulesFile=home-files/.config/user-tmpfiles.d/home-manager-cache.conf
|
||||||
|
assertFileExists $cacheRulesFile
|
||||||
|
assertFileRegex $cacheRulesFile "^'d' '%C' '-' '-' '-' '4 weeks' $"
|
||||||
|
|
||||||
|
myToolRulesFile=home-files/.config/user-tmpfiles.d/home-manager-myTool.conf
|
||||||
|
assertFileExists $myToolRulesFile
|
||||||
|
assertFileRegex $myToolRulesFile \
|
||||||
|
"^'f+' '%h/.config/myTool.conf' '0644' 'alice' 'users' '-' my\\\\x20unescaped\\\\x20config$"
|
||||||
|
'';
|
||||||
|
}
|
||||||
3
tests/modules/misc/tmpfiles/common-stubs.nix
Normal file
3
tests/modules/misc/tmpfiles/common-stubs.nix
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
test.stubs.systemd.outPath = null;
|
||||||
|
}
|
||||||
7
tests/modules/misc/tmpfiles/default.nix
Normal file
7
tests/modules/misc/tmpfiles/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
tmpfiles-no-rules = ./no-rules.nix;
|
||||||
|
tmpfiles-basic-rules = ./basic-rules.nix;
|
||||||
|
tmpfiles-rules-with-purging = ./rules-with-purging.nix;
|
||||||
|
|
||||||
|
tmpfiles-escaped-argument-warning = ./escaped-argument-warning.nix;
|
||||||
|
}
|
||||||
14
tests/modules/misc/tmpfiles/escaped-argument-warning.nix
Normal file
14
tests/modules/misc/tmpfiles/escaped-argument-warning.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
imports = [ ./common-stubs.nix ];
|
||||||
|
|
||||||
|
systemd.user.tmpfiles.settings.foo.rules.path.f.argument = "my\\x20unescaped\\x20config";
|
||||||
|
|
||||||
|
test.asserts.warnings.expected = [
|
||||||
|
''
|
||||||
|
The 'systemd.user.tmpfiles.settings.foo.rules.path.f.argument' option
|
||||||
|
appears to contain escape sequences, which will be escaped again.
|
||||||
|
Unescape them if this is not intended. The assigned string is:
|
||||||
|
"my\x20unescaped\x20config"
|
||||||
|
''
|
||||||
|
];
|
||||||
|
}
|
||||||
9
tests/modules/misc/tmpfiles/no-rules.nix
Normal file
9
tests/modules/misc/tmpfiles/no-rules.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
imports = [ ./common-stubs.nix ];
|
||||||
|
|
||||||
|
systemd.user.tmpfiles.settings = { };
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertPathNotExists home-files/.config/user-tmpfiles.d/
|
||||||
|
'';
|
||||||
|
}
|
||||||
28
tests/modules/misc/tmpfiles/rules-with-purging.nix
Normal file
28
tests/modules/misc/tmpfiles/rules-with-purging.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
imports = [ ./common-stubs.nix ];
|
||||||
|
|
||||||
|
systemd.user.tmpfiles.settings = {
|
||||||
|
cache.rules."%C".d.age = "4 weeks";
|
||||||
|
myTool = {
|
||||||
|
rules = {
|
||||||
|
"%h/.config/myTool.conf"."f+".argument = "my_config";
|
||||||
|
"%h/.config/myToolPurged.conf"."f+$".argument = "my_config_purged";
|
||||||
|
};
|
||||||
|
purgeOnChange = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
cacheRulesFile=home-files/.config/user-tmpfiles.d/home-manager-cache.conf
|
||||||
|
assertFileExists $cacheRulesFile
|
||||||
|
assertFileRegex $cacheRulesFile "^'d' '%C' '-' '-' '-' '4 weeks' $"
|
||||||
|
|
||||||
|
assertPathNotExists home-files/.config/user-tmpfiles.d/home-manager-myTool.conf
|
||||||
|
myToolRulesFile=home-files/.config/user-tmpfiles.d/home-manager-purge-on-change.conf
|
||||||
|
assertFileExists $myToolRulesFile
|
||||||
|
assertFileRegex $myToolRulesFile \
|
||||||
|
"^'f+' '%h/.config/myTool.conf' '-' '-' '-' '-' my_config$"
|
||||||
|
assertFileRegex $myToolRulesFile \
|
||||||
|
"^'f+$' '%h/.config/myToolPurged.conf' '-' '-' '-' '-' my_config_purged$"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,7 @@ in
|
||||||
nmt.script =
|
nmt.script =
|
||||||
let
|
let
|
||||||
binPath =
|
binPath =
|
||||||
if pkgs.hostPlatform.isDarwin then
|
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||||
"Applications/${cfg.darwinAppName}.app/Contents/MacOS"
|
"Applications/${cfg.darwinAppName}.app/Contents/MacOS"
|
||||||
else
|
else
|
||||||
"bin";
|
"bin";
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ in
|
||||||
nmt.script =
|
nmt.script =
|
||||||
let
|
let
|
||||||
binPath =
|
binPath =
|
||||||
if pkgs.hostPlatform.isDarwin then
|
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||||
"Applications/${cfg.darwinAppName}.app/Contents/MacOS"
|
"Applications/${cfg.darwinAppName}.app/Contents/MacOS"
|
||||||
else
|
else
|
||||||
"bin";
|
"bin";
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ in
|
||||||
nmt.script =
|
nmt.script =
|
||||||
let
|
let
|
||||||
binPath =
|
binPath =
|
||||||
if pkgs.hostPlatform.isDarwin then
|
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||||
"Applications/${cfg.darwinAppName}.app/Contents/MacOS"
|
"Applications/${cfg.darwinAppName}.app/Contents/MacOS"
|
||||||
else
|
else
|
||||||
"bin";
|
"bin";
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ in
|
||||||
{
|
{
|
||||||
imports = [ firefoxMockOverlay ];
|
imports = [ firefoxMockOverlay ];
|
||||||
|
|
||||||
config = lib.mkIf (config.test.enableBig && !pkgs.hostPlatform.isDarwin) (
|
config = lib.mkIf (config.test.enableBig && !pkgs.stdenv.hostPlatform.isDarwin) (
|
||||||
{
|
{
|
||||||
home.stateVersion = "19.09";
|
home.stateVersion = "19.09";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
|
{ config, ... }:
|
||||||
{
|
{
|
||||||
programs.ghostty.enable = true;
|
programs.ghostty = {
|
||||||
|
enable = true;
|
||||||
|
package = config.lib.test.mkStubPackage { outPath = null; };
|
||||||
|
};
|
||||||
|
|
||||||
nmt.script = ''
|
nmt.script = ''
|
||||||
assertPathNotExists home-files/.config/ghostty/config
|
assertPathNotExists home-files/.config/ghostty/config
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
{
|
{
|
||||||
programs.ghostty = {
|
programs.ghostty = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = config.lib.test.mkStubPackage { };
|
package = config.lib.test.mkStubPackage { outPath = null; };
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
theme = "catppuccin-mocha";
|
theme = "catppuccin-mocha";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
{ config, ... }:
|
||||||
{
|
{
|
||||||
programs.ghostty = {
|
programs.ghostty = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = config.lib.test.mkStubPackage { outPath = null; };
|
||||||
|
|
||||||
themes = {
|
themes = {
|
||||||
catppuccin-mocha = {
|
catppuccin-mocha = {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
gpg-immutable-keyfiles = ./immutable-keyfiles.nix;
|
gpg-immutable-keyfiles = ./immutable-keyfiles.nix;
|
||||||
gpg-mutable-keyfiles = ./mutable-keyfiles.nix;
|
gpg-mutable-keyfiles = ./mutable-keyfiles.nix;
|
||||||
|
gpg-multiple-keys-trust = ./multiple-keys-trust.nix;
|
||||||
gpg-override-defaults = ./override-defaults.nix;
|
gpg-override-defaults = ./override-defaults.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
61
tests/modules/programs/gpg/multiple-keys-trust.nix
Normal file
61
tests/modules/programs/gpg/multiple-keys-trust.nix
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{ realPkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
programs.gpg = {
|
||||||
|
enable = true;
|
||||||
|
package = realPkgs.gnupg;
|
||||||
|
|
||||||
|
mutableKeys = false;
|
||||||
|
mutableTrust = false;
|
||||||
|
|
||||||
|
publicKeys = [
|
||||||
|
{
|
||||||
|
# This file contains three public keys
|
||||||
|
# The bug causes only the first key to have trust set
|
||||||
|
source = ./test-keys/multiple-keys.asc;
|
||||||
|
trust = "ultimate"; # trust level 5
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileNotRegex activate "^export GNUPGHOME=/home/hm-user/.gnupg$"
|
||||||
|
|
||||||
|
assertFileRegex activate \
|
||||||
|
'^install -m 0700 /nix/store/[0-9a-z]*-gpg-pubring/trustdb.gpg "/home/hm-user/.gnupg/trustdb.gpg"$'
|
||||||
|
|
||||||
|
# Setup GPGHOME
|
||||||
|
export GNUPGHOME=$(mktemp -d)
|
||||||
|
cp -r $TESTED/home-files/.gnupg/* $GNUPGHOME
|
||||||
|
TRUSTDB=$(grep -o '/nix/store/[0-9a-z]*-gpg-pubring/trustdb.gpg' $TESTED/activate)
|
||||||
|
install -m 0700 $TRUSTDB $GNUPGHOME/trustdb.gpg
|
||||||
|
|
||||||
|
# Export Trust
|
||||||
|
export WORKDIR=$(mktemp -d)
|
||||||
|
${realPkgs.gnupg}/bin/gpg -q --export-ownertrust > $WORKDIR/gpgtrust.txt
|
||||||
|
|
||||||
|
echo "=== Trust database contents ==="
|
||||||
|
cat $WORKDIR/gpgtrust.txt
|
||||||
|
echo "=== End of trust database ==="
|
||||||
|
|
||||||
|
# The test file contains three keys:
|
||||||
|
# - 13B06D9193E01E0F (Test User One) - fingerprint: B07502E7B7ED0A4AA3BF191913B06D9193E01E0F
|
||||||
|
# - 42E7B990011430DE (Test User Two) - fingerprint: 6A2A713AE7F93C8EA6D264B642E7B990011430DE
|
||||||
|
# - DFC825F8209CE742 (Test User Three) - fingerprint: E66D263DC7174345AB102829DFC825F8209CE742
|
||||||
|
#
|
||||||
|
# All three keys should have ultimate trust (level 6 in ownertrust format)
|
||||||
|
# Due to the bug in importTrust function, only the first key gets trust set
|
||||||
|
|
||||||
|
# Check that first key has ultimate trust (this works with current code)
|
||||||
|
assertFileRegex $WORKDIR/gpgtrust.txt \
|
||||||
|
'^B07502E7B7ED0A4AA3BF191913B06D9193E01E0F:6:$'
|
||||||
|
|
||||||
|
# Check that second key has ultimate trust (this FAILS due to bug)
|
||||||
|
assertFileRegex $WORKDIR/gpgtrust.txt \
|
||||||
|
'^6A2A713AE7F93C8EA6D264B642E7B990011430DE:6:$'
|
||||||
|
|
||||||
|
# Check that third key has ultimate trust (this FAILS due to bug)
|
||||||
|
assertFileRegex $WORKDIR/gpgtrust.txt \
|
||||||
|
'^E66D263DC7174345AB102829DFC825F8209CE742:6:$'
|
||||||
|
'';
|
||||||
|
}
|
||||||
45
tests/modules/programs/gpg/test-keys/multiple-keys.asc
Normal file
45
tests/modules/programs/gpg/test-keys/multiple-keys.asc
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQENBGkHy/oBCADC4NT6P4eiOv1f9g8mhdLQlexO4Pefh33EicybD4tnlZZGVzYT
|
||||||
|
2J75slIGFV9+AOX/TXsws7+0IaZYB94a3p1NKoWeYh4XZy0HQ2HRJjNWeLQ41lFC
|
||||||
|
dCQ4A0JuqCurMFFdph59Xlh4ko3SXmPwNqXEmNX8LQlIDRNk+RiW+gJ4OC8DV6Do
|
||||||
|
YexeQHrHxtdGrStFmEygEAB5K1xqLRrzETvPubEmPEcrvhT/7W1+TwCb/haKo+Is
|
||||||
|
OgFcaJFv7CR6EbYh3DNZa4Zrd/WpNAL8+Kmz89VTdw0qaSYJxV9uR4DdmgX+2tAv
|
||||||
|
WmLuTuPMabU599p9nRUqk1Pj5fit6octCxX9ABEBAAG0IVRlc3QgVXNlciBPbmUg
|
||||||
|
PHRlc3QxQGV4YW1wbGUuY29tPokBTwQTAQoAORYhBLB1Aue37QpKo78ZGROwbZGT
|
||||||
|
4B4PBQJpB8v6AxsvBAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRATsG2Rk+Ae
|
||||||
|
D54fB/9EN7IjdwARheioFsZlifda5t31l084eYsq9kLzjCrxCXNlDZEIi6QrNBBA
|
||||||
|
CDZyv5bM+JLrZPbZ/1J1caoB6W9+ARPLiERWMhql7JNWSS/4Yhf/L0aD0C3pJFJf
|
||||||
|
h3bcSxhAzXBL3857cELR88UeV7NHPNdJsKVX0h7r1xe1D1oGZd19qbyZx3FJLzH8
|
||||||
|
p01ZkLoKdKAh42x+XN6KrOWGWFyvLX56pXjp9mjero2iDpUlBdIV15CFJ+aoVI3B
|
||||||
|
KG26z4B7/L8kQVO2eH41k/i39u9SuvuCinYcNQ/5/blpaIc7xqL5jI1gapzE4bBu
|
||||||
|
GzGOKJoWRgGJDUZzyvTtxbI/nsK6mQENBGkHy/oBCADHGrIJ1uTGWJvSt+2pmqxK
|
||||||
|
ruXQvVxQva3GbYIgePQa88PzhORYTnuskEdOhNhMTaxKWbxS1bfDXf3Akjis+kHb
|
||||||
|
xLK692XtKFf88ALV6ts0Rd4YRG6BCcwMPAfFuQhyQRxclNk5XHzaH6IvKvmrSkvG
|
||||||
|
wilLkrdj9hW32FvVYDyjdiDSbvs05d8EfRr7UF/fMQC5HOJJ6VSC7HJ7tQGWvtNG
|
||||||
|
eyr/I61OSDxhf6PF5CfuepajO0nzsVHvsXTxoJwYbx+zXSlGxTsHWYxp6r0MdPE/
|
||||||
|
vCNmvrfpz4PoTiE43Xa3XsYSO2gRCpMYJKQaxl5pCfBGSmKpCF1YDBSTrRYyacyv
|
||||||
|
ABEBAAG0IVRlc3QgVXNlciBUd28gPHRlc3QyQGV4YW1wbGUuY29tPokBTwQTAQoA
|
||||||
|
ORYhBGoqcTrn+TyOptJktkLnuZABFDDeBQJpB8v6AxsvBAULCQgHAgYVCgkICwIE
|
||||||
|
FgIDAQIeAQIXgAAKCRBC57mQARQw3nIGB/9/j1SIk+DxmCeT2fihQmS7lubDoq1I
|
||||||
|
FUdjb7cAGBs4KAmJh8MVMsYyB+EtaVC8qu4C5EgNNV0+c2H8UishGcZvMm9Qg7LQ
|
||||||
|
MTSGKLwXikaiIvyw3zlh1FpJn2rYUSvCplVswhF/dfSlenmU81eiPigYsvzVoa8h
|
||||||
|
xJNn01DLu4cd2VsBhWW/2w3DKSvVHRPdlPTPrqkjzMQRy2ULa2yTWiiuxWJxHuj0
|
||||||
|
3ocvLGlpyyvIwyoFVG4Lex4r+jSL3RCllEUjADAMgDPfhoTEerfgORCVEqGE/JLR
|
||||||
|
MVrTl6bMuodGehXgCRalcg9ChUADBHS4fZ0NiH46QhTblwRRFc2K6WbzmQENBGkH
|
||||||
|
y/oBCADAzZTgBmulUSr29gmBELA1gpMNHZ3J/2R3mTXMFaZAsi84uCZNyLLrDhU4
|
||||||
|
WaXVRURlwY4eHdvIMc3IM846s0SkLKDy3cIbusQK9NDVS/69LRyKNiZMjEbpODZl
|
||||||
|
fT5AtQUOL1jAIxy/wVEKzqih0so6mfNCwKFshWyi4p2+E8dFT8apTvhwJkdpptb6
|
||||||
|
q8Q1ABx+NRE1iSK+lFUw7xD7lLDvUYcHn6glpEMIGjg3/BLF74nVYFe6rCuFKgNt
|
||||||
|
GHLk1ZjoldbQRmTxdaKkb6vmfPWjbQuZCdNAUT87ljnrpdl3YxRN2ujQ1tHrWkby
|
||||||
|
C+anhmkdoQnqQPpICaeLe6NwHpPVABEBAAG0I1Rlc3QgVXNlciBUaHJlZSA8dGVz
|
||||||
|
dDNAZXhhbXBsZS5jb20+iQFPBBMBCgA5FiEE5m0mPccXQ0WrECgp38gl+CCc50IF
|
||||||
|
AmkHy/oDGy8EBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEN/IJfggnOdC7qEH
|
||||||
|
/idAjYhb9QNnOOu7lPkgLnPVanLCE20uHoGLeDUNkz2+2VFmkTu9poHKp4P7tW4e
|
||||||
|
/wMyy6uv4X1kcp6XcwVALx2HRU/PKLy1kNQFEeDocA1fx0wloJTfGfJpbxXWPFUG
|
||||||
|
oTVx0V2BwjiGK1+MTZCJQ+aqS2mXPLMPRv0ZKw8CQOeGHRJCD3NBEiWxpi5wncFM
|
||||||
|
DFDnaKrTCgmndRIafdXU3B7L4zZkNwcXRylkxVFjl938W5czbqa0o2LLadd/trJZ
|
||||||
|
YN/21BNkS/QmrH1Kapcgj5GvJp8ky4OpccrCTxfWLmRVfxtdo/N2woNyK9xvjiwd
|
||||||
|
TYMaXvrf93dAboJrOmiAtPA=
|
||||||
|
=tjTO
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
@ -19,5 +19,8 @@ action_alias launch_window launch --cwd=current --type=os-window
|
||||||
map ctrl+c copy_or_interrupt
|
map ctrl+c copy_or_interrupt
|
||||||
map ctrl+f>2 set_font_size 20
|
map ctrl+f>2 set_font_size 20
|
||||||
|
|
||||||
|
mouse_map ctrl+left click ungrabbed mouse_handle_click selection link prompt
|
||||||
|
mouse_map left click ungrabbed no-op
|
||||||
|
|
||||||
env LS_COLORS=1
|
env LS_COLORS=1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@
|
||||||
"ctrl+f>2" = "set_font_size 20";
|
"ctrl+f>2" = "set_font_size 20";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mouseBindings = {
|
||||||
|
"ctrl+left click" = "ungrabbed mouse_handle_click selection link prompt";
|
||||||
|
"left click" = "ungrabbed no-op";
|
||||||
|
};
|
||||||
|
|
||||||
actionAliases = {
|
actionAliases = {
|
||||||
"launch_tab" = "launch --cwd=current --type=tab";
|
"launch_tab" = "launch --cwd=current --type=tab";
|
||||||
"launch_window" = "launch --cwd=current --type=os-window";
|
"launch_window" = "launch --cwd=current --type=os-window";
|
||||||
|
|
|
||||||
|
|
@ -17,5 +17,8 @@ action_alias launch_window launch --cwd=current --type=os-window
|
||||||
map ctrl+c copy_or_interrupt
|
map ctrl+c copy_or_interrupt
|
||||||
map ctrl+f>2 set_font_size 20
|
map ctrl+f>2 set_font_size 20
|
||||||
|
|
||||||
|
mouse_map ctrl+left click ungrabbed mouse_handle_click selection link prompt
|
||||||
|
mouse_map left click ungrabbed no-op
|
||||||
|
|
||||||
env LS_COLORS=1
|
env LS_COLORS=1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@
|
||||||
"ctrl+f>2" = "set_font_size 20";
|
"ctrl+f>2" = "set_font_size 20";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mouseBindings = {
|
||||||
|
"ctrl+left click" = "ungrabbed mouse_handle_click selection link prompt";
|
||||||
|
"left click" = "ungrabbed no-op";
|
||||||
|
};
|
||||||
|
|
||||||
actionAliases = {
|
actionAliases = {
|
||||||
"launch_tab" = "launch --cwd=current --type=tab";
|
"launch_tab" = "launch --cwd=current --type=tab";
|
||||||
"launch_window" = "launch --cwd=current --type=os-window";
|
"launch_window" = "launch --cwd=current --type=os-window";
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
4
tests/modules/programs/mcp/default.nix
Normal file
4
tests/modules/programs/mcp/default.nix
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
mcp-servers = ./servers.nix;
|
||||||
|
mcp-empty-servers = ./empty-servers.nix;
|
||||||
|
}
|
||||||
9
tests/modules/programs/mcp/empty-servers.nix
Normal file
9
tests/modules/programs/mcp/empty-servers.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
programs.mcp = {
|
||||||
|
enable = true;
|
||||||
|
servers = { };
|
||||||
|
};
|
||||||
|
nmt.script = ''
|
||||||
|
assertPathNotExists home-files/.config/mcp/mcp.json
|
||||||
|
'';
|
||||||
|
}
|
||||||
17
tests/modules/programs/mcp/mcp.json
Normal file
17
tests/modules/programs/mcp/mcp.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"context7": {
|
||||||
|
"headers": {
|
||||||
|
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
|
||||||
|
},
|
||||||
|
"serverUrl": "https://mcp.context7.com/mcp"
|
||||||
|
},
|
||||||
|
"everything": {
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
],
|
||||||
|
"command": "npx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
tests/modules/programs/mcp/servers.nix
Normal file
25
tests/modules/programs/mcp/servers.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
programs.mcp = {
|
||||||
|
enable = true;
|
||||||
|
servers = {
|
||||||
|
everything = {
|
||||||
|
command = "npx";
|
||||||
|
args = [
|
||||||
|
"-y"
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
context7 = {
|
||||||
|
serverUrl = "https://mcp.context7.com/mcp";
|
||||||
|
headers = {
|
||||||
|
CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/mcp/mcp.json
|
||||||
|
assertFileContent home-files/.config/mcp/mcp.json \
|
||||||
|
${./mcp.json}
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -11,4 +11,6 @@
|
||||||
opencode-mixed-content = ./mixed-content.nix;
|
opencode-mixed-content = ./mixed-content.nix;
|
||||||
opencode-themes-inline = ./themes-inline.nix;
|
opencode-themes-inline = ./themes-inline.nix;
|
||||||
opencode-themes-path = ./themes-path.nix;
|
opencode-themes-path = ./themes-path.nix;
|
||||||
|
opencode-mcp-integration = ./mcp-integration.nix;
|
||||||
|
opencode-mcp-integration-with-override = ./mcp-integration-with-override.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"mcp": {
|
||||||
|
"context7": {
|
||||||
|
"enabled": true,
|
||||||
|
"headers": {
|
||||||
|
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
|
||||||
|
},
|
||||||
|
"type": "remote",
|
||||||
|
"url": "https://mcp.context7.com/mcp"
|
||||||
|
},
|
||||||
|
"custom-server": {
|
||||||
|
"enabled": true,
|
||||||
|
"type": "remote",
|
||||||
|
"url": "https://example.com"
|
||||||
|
},
|
||||||
|
"everything": {
|
||||||
|
"command": [
|
||||||
|
"custom-command"
|
||||||
|
],
|
||||||
|
"enabled": false,
|
||||||
|
"type": "local"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"model": "anthropic/claude-sonnet-4-20250514",
|
||||||
|
"theme": "opencode"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
programs.mcp = {
|
||||||
|
enable = true;
|
||||||
|
servers = {
|
||||||
|
everything = {
|
||||||
|
command = "npx";
|
||||||
|
args = [
|
||||||
|
"-y"
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
context7 = {
|
||||||
|
url = "https://mcp.context7.com/mcp";
|
||||||
|
headers = {
|
||||||
|
CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.opencode = {
|
||||||
|
enable = true;
|
||||||
|
enableMcpIntegration = true;
|
||||||
|
settings = {
|
||||||
|
theme = "opencode";
|
||||||
|
model = "anthropic/claude-sonnet-4-20250514";
|
||||||
|
# User's custom MCP settings should override generated ones
|
||||||
|
mcp = {
|
||||||
|
everything = {
|
||||||
|
enabled = false; # Override to disable
|
||||||
|
command = [ "custom-command" ];
|
||||||
|
type = "local";
|
||||||
|
};
|
||||||
|
custom-server = {
|
||||||
|
enabled = true;
|
||||||
|
type = "remote";
|
||||||
|
url = "https://example.com";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/opencode/config.json
|
||||||
|
assertFileContent home-files/.config/opencode/config.json \
|
||||||
|
${./mcp-integration-with-override.json}
|
||||||
|
'';
|
||||||
|
}
|
||||||
30
tests/modules/programs/opencode/mcp-integration.json
Normal file
30
tests/modules/programs/opencode/mcp-integration.json
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"mcp": {
|
||||||
|
"context7": {
|
||||||
|
"enabled": true,
|
||||||
|
"headers": {
|
||||||
|
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
|
||||||
|
},
|
||||||
|
"type": "remote",
|
||||||
|
"url": "https://mcp.context7.com/mcp"
|
||||||
|
},
|
||||||
|
"disabled-server": {
|
||||||
|
"command": [
|
||||||
|
"echo",
|
||||||
|
"test"
|
||||||
|
],
|
||||||
|
"enabled": false,
|
||||||
|
"type": "local"
|
||||||
|
},
|
||||||
|
"everything": {
|
||||||
|
"command": [
|
||||||
|
"npx",
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
],
|
||||||
|
"enabled": true,
|
||||||
|
"type": "local"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
tests/modules/programs/opencode/mcp-integration.nix
Normal file
36
tests/modules/programs/opencode/mcp-integration.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
programs.mcp = {
|
||||||
|
enable = true;
|
||||||
|
servers = {
|
||||||
|
everything = {
|
||||||
|
command = "npx";
|
||||||
|
args = [
|
||||||
|
"-y"
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
context7 = {
|
||||||
|
url = "https://mcp.context7.com/mcp";
|
||||||
|
headers = {
|
||||||
|
CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
disabled-server = {
|
||||||
|
command = "echo";
|
||||||
|
args = [ "test" ];
|
||||||
|
disabled = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.opencode = {
|
||||||
|
enable = true;
|
||||||
|
enableMcpIntegration = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/opencode/config.json
|
||||||
|
assertFileContent home-files/.config/opencode/config.json \
|
||||||
|
${./mcp-integration.json}
|
||||||
|
'';
|
||||||
|
}
|
||||||
3
tests/modules/programs/opkssh/default.nix
Normal file
3
tests/modules/programs/opkssh/default.nix
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
opkssh-basic-config = ./opkssh-basic-config.nix;
|
||||||
|
}
|
||||||
42
tests/modules/programs/opkssh/opkssh-basic-config.nix
Normal file
42
tests/modules/programs/opkssh/opkssh-basic-config.nix
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
_: {
|
||||||
|
programs.opkssh = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
default_provider = "test-provider";
|
||||||
|
providers = [
|
||||||
|
{
|
||||||
|
alias = "test-provider";
|
||||||
|
issuer = "https://test.domain/oauth2/openid/opkssh";
|
||||||
|
client_id = "opkssh";
|
||||||
|
scopes = "openid email profile";
|
||||||
|
redirect_uris = [
|
||||||
|
"http://localhost:3000/login-callback"
|
||||||
|
"http://localhost:10001/login-callback"
|
||||||
|
"http://localhost:11110/login-callback"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
configFile=home-files/.opk/config.yml
|
||||||
|
|
||||||
|
assertFileExists "$configFile"
|
||||||
|
|
||||||
|
configFileNormalized="$(normalizeStorePaths "$configFile")"
|
||||||
|
|
||||||
|
assertFileContent "$configFileNormalized" ${builtins.toFile "expected.service" ''
|
||||||
|
default_provider: test-provider
|
||||||
|
providers:
|
||||||
|
- alias: test-provider
|
||||||
|
client_id: opkssh
|
||||||
|
issuer: https://test.domain/oauth2/openid/opkssh
|
||||||
|
redirect_uris:
|
||||||
|
- http://localhost:3000/login-callback
|
||||||
|
- http://localhost:10001/login-callback
|
||||||
|
- http://localhost:11110/login-callback
|
||||||
|
scopes: openid email profile
|
||||||
|
''}
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
rio-example-settings = ./example-settings.nix;
|
rio-example-settings = ./example-settings.nix;
|
||||||
rio-empty-settings = ./empty-settings.nix;
|
rio-empty-settings = ./empty-settings.nix;
|
||||||
|
rio-themes = ./example-themes.nix;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
{ config, ... }:
|
||||||
{
|
{
|
||||||
programs.rio.enable = true;
|
programs.rio.enable = true;
|
||||||
|
programs.rio.package = config.lib.test.mkStubPackage { };
|
||||||
|
|
||||||
nmt.script = ''
|
nmt.script = ''
|
||||||
assertPathNotExists home-files/.config/rio
|
assertPathNotExists home-files/.config/rio
|
||||||
|
|
|
||||||
30
tests/modules/programs/rio/example-themes.nix
Normal file
30
tests/modules/programs/rio/example-themes.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
programs.rio = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
package = config.lib.test.mkStubPackage { };
|
||||||
|
|
||||||
|
themes = {
|
||||||
|
foobar.colors = {
|
||||||
|
cyan = "#8be9fd";
|
||||||
|
green = "#50fa7b";
|
||||||
|
background = "#282a36";
|
||||||
|
};
|
||||||
|
|
||||||
|
foobar2 = ./foobar.toml;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/rio/themes/foobar.toml
|
||||||
|
assertFileExists home-files/.config/rio/themes/foobar2.toml
|
||||||
|
|
||||||
|
assertFileContent \
|
||||||
|
home-files/.config/rio/themes/foobar.toml \
|
||||||
|
${./foobar.toml}
|
||||||
|
assertFileContent \
|
||||||
|
home-files/.config/rio/themes/foobar2.toml \
|
||||||
|
${./foobar.toml}
|
||||||
|
'';
|
||||||
|
}
|
||||||
4
tests/modules/programs/rio/foobar.toml
Normal file
4
tests/modules/programs/rio/foobar.toml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
[colors]
|
||||||
|
background = "#282a36"
|
||||||
|
cyan = "#8be9fd"
|
||||||
|
green = "#50fa7b"
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"location": "/nix/store",
|
||||||
|
"name": "Nix Store"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -53,6 +53,13 @@
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
firstUseCheck = false;
|
||||||
|
pinnedFolders = [
|
||||||
|
{
|
||||||
|
name = "Nix Store";
|
||||||
|
location = "/nix/store";
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
nmt.script =
|
nmt.script =
|
||||||
|
|
@ -60,6 +67,10 @@
|
||||||
configSubPath =
|
configSubPath =
|
||||||
if !pkgs.stdenv.isDarwin then ".config/superfile" else "Library/Application Support/superfile";
|
if !pkgs.stdenv.isDarwin then ".config/superfile" else "Library/Application Support/superfile";
|
||||||
configBasePath = "home-files/" + configSubPath;
|
configBasePath = "home-files/" + configSubPath;
|
||||||
|
|
||||||
|
dataSubPath =
|
||||||
|
if !pkgs.stdenv.isDarwin then ".local/share/superfile" else "Library/Application Support/superfile";
|
||||||
|
dataBasePath = "home-files/" + dataSubPath;
|
||||||
in
|
in
|
||||||
''
|
''
|
||||||
assertFileExists "${configBasePath}/config.toml"
|
assertFileExists "${configBasePath}/config.toml"
|
||||||
|
|
@ -82,5 +93,10 @@
|
||||||
assertFileContent \
|
assertFileContent \
|
||||||
"${configBasePath}/theme/test2.toml" \
|
"${configBasePath}/theme/test2.toml" \
|
||||||
${./example-theme2-expected.toml}
|
${./example-theme2-expected.toml}
|
||||||
|
assertFileExists "${dataBasePath}/firstUseCheck"
|
||||||
|
assertFileExists "${dataBasePath}/pinned.json"
|
||||||
|
assertFileContent \
|
||||||
|
"${dataBasePath}/pinned.json" \
|
||||||
|
${./example-pinned-folders.json}
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
tests/modules/programs/vicinae/default.nix
Normal file
4
tests/modules/programs/vicinae/default.nix
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{ lib, pkgs, ... }:
|
||||||
|
lib.optionalAttrs (pkgs.stdenv.hostPlatform.isLinux) {
|
||||||
|
vicinae-example-settings = ./example-settings.nix;
|
||||||
|
}
|
||||||
77
tests/modules/programs/vicinae/example-settings.nix
Normal file
77
tests/modules/programs/vicinae/example-settings.nix
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
{
|
||||||
|
programs.vicinae = {
|
||||||
|
enable = true;
|
||||||
|
systemd.enable = true;
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
faviconService = "twenty";
|
||||||
|
font = {
|
||||||
|
size = 10;
|
||||||
|
};
|
||||||
|
popToRootOnClose = false;
|
||||||
|
rootSearch = {
|
||||||
|
searchFiles = false;
|
||||||
|
};
|
||||||
|
theme = {
|
||||||
|
name = "vicinae-dark";
|
||||||
|
};
|
||||||
|
window = {
|
||||||
|
csd = true;
|
||||||
|
opacity = 0.95;
|
||||||
|
rounding = 10;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
themes = {
|
||||||
|
base16-default-dark = {
|
||||||
|
version = "1.0.0";
|
||||||
|
appearance = "dark";
|
||||||
|
name = "base16 default dark";
|
||||||
|
description = "base16 default dark by Chris Kempson";
|
||||||
|
palette = {
|
||||||
|
background = "#181818";
|
||||||
|
foreground = "#d8d8d8";
|
||||||
|
blue = "#7cafc2";
|
||||||
|
green = "#a3be8c";
|
||||||
|
magenta = "#ba8baf";
|
||||||
|
orange = "#dc9656";
|
||||||
|
purple = "#a16946";
|
||||||
|
red = "#ab4642";
|
||||||
|
yellow = "#f7ca88";
|
||||||
|
cyan = "#86c1b9";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
(config.lib.vicinae.mkRayCastExtension {
|
||||||
|
name = "gif-search";
|
||||||
|
sha256 = "sha256-G7il8T1L+P/2mXWJsb68n4BCbVKcrrtK8GnBNxzt73Q=";
|
||||||
|
rev = "4d417c2dfd86a5b2bea202d4a7b48d8eb3dbaeb1";
|
||||||
|
})
|
||||||
|
(config.lib.vicinae.mkExtension {
|
||||||
|
name = "test-extension";
|
||||||
|
src =
|
||||||
|
pkgs.fetchFromGitHub {
|
||||||
|
owner = "schromp";
|
||||||
|
repo = "vicinae-extensions";
|
||||||
|
rev = "f8be5c89393a336f773d679d22faf82d59631991";
|
||||||
|
sha256 = "sha256-zk7WIJ19ITzRFnqGSMtX35SgPGq0Z+M+f7hJRbyQugw=";
|
||||||
|
}
|
||||||
|
+ "/test-extension";
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists "home-files/.config/vicinae/vicinae.json"
|
||||||
|
assertFileExists "home-files/.config/systemd/user/vicinae.service"
|
||||||
|
assertFileExists "home-files/.local/share/vicinae/extensions/gif-search/package.json"
|
||||||
|
assertFileExists "home-files/.local/share/vicinae/extensions/test-extension/package.json"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,8 @@ let
|
||||||
keybindings = import ./keybindings.nix;
|
keybindings = import ./keybindings.nix;
|
||||||
tasks = import ./tasks.nix;
|
tasks = import ./tasks.nix;
|
||||||
mcp = import ./mcp.nix;
|
mcp = import ./mcp.nix;
|
||||||
|
mcp-integration = import ./mcp-integration.nix;
|
||||||
|
mcp-integration-with-override = import ./mcp-integration-with-override.nix;
|
||||||
update-checks = import ./update-checks.nix;
|
update-checks = import ./update-checks.nix;
|
||||||
snippets = import ./snippets.nix;
|
snippets = import ./snippets.nix;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
26
tests/modules/programs/vscode/mcp-integration-default.json
Normal file
26
tests/modules/programs/vscode/mcp-integration-default.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"servers": {
|
||||||
|
"context7": {
|
||||||
|
"enabled": true,
|
||||||
|
"headers": {
|
||||||
|
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
|
||||||
|
},
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://mcp.context7.com/mcp"
|
||||||
|
},
|
||||||
|
"disabled-server": {
|
||||||
|
"command": "echo",
|
||||||
|
"enabled": false,
|
||||||
|
"type": "stdio"
|
||||||
|
},
|
||||||
|
"everything": {
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
],
|
||||||
|
"command": "npx",
|
||||||
|
"enabled": true,
|
||||||
|
"type": "stdio"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
tests/modules/programs/vscode/mcp-integration-test.json
Normal file
7
tests/modules/programs/vscode/mcp-integration-test.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"servers": {
|
||||||
|
"Github": {
|
||||||
|
"url": "https://api.githubcopilot.com/mcp/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"servers": {
|
||||||
|
"CustomServer": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://example.com/mcp"
|
||||||
|
},
|
||||||
|
"context7": {
|
||||||
|
"enabled": true,
|
||||||
|
"headers": {
|
||||||
|
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
|
||||||
|
},
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://mcp.context7.com/mcp"
|
||||||
|
},
|
||||||
|
"everything": {
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
],
|
||||||
|
"command": "custom-npx",
|
||||||
|
"enabled": false,
|
||||||
|
"type": "stdio"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package:
|
||||||
|
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.programs.vscode;
|
||||||
|
willUseIfd = package.pname != "vscode";
|
||||||
|
|
||||||
|
mcpFilePath =
|
||||||
|
name:
|
||||||
|
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||||
|
"Library/Application Support/${cfg.nameShort}/User/${
|
||||||
|
lib.optionalString (name != "default") "profiles/${name}/"
|
||||||
|
}mcp.json"
|
||||||
|
else
|
||||||
|
".config/${cfg.nameShort}/User/${
|
||||||
|
lib.optionalString (name != "default") "profiles/${name}/"
|
||||||
|
}mcp.json";
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
lib.mkIf (willUseIfd -> config.test.enableLegacyIfd) {
|
||||||
|
programs.mcp = {
|
||||||
|
enable = true;
|
||||||
|
servers = {
|
||||||
|
everything = {
|
||||||
|
command = "npx";
|
||||||
|
args = [
|
||||||
|
"-y"
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
context7 = {
|
||||||
|
url = "https://mcp.context7.com/mcp";
|
||||||
|
headers = {
|
||||||
|
CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.vscode = {
|
||||||
|
enable = true;
|
||||||
|
inherit package;
|
||||||
|
profiles = {
|
||||||
|
default = {
|
||||||
|
enableMcpIntegration = true;
|
||||||
|
# User MCP settings should override generated ones
|
||||||
|
userMcp = {
|
||||||
|
servers = {
|
||||||
|
everything = {
|
||||||
|
command = "custom-npx";
|
||||||
|
args = [
|
||||||
|
"-y"
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
];
|
||||||
|
enabled = false;
|
||||||
|
type = "stdio";
|
||||||
|
};
|
||||||
|
CustomServer = {
|
||||||
|
type = "http";
|
||||||
|
url = "https://example.com/mcp";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists "home-files/${mcpFilePath "default"}"
|
||||||
|
assertFileContent "home-files/${mcpFilePath "default"}" ${./mcp-integration-with-override.json}
|
||||||
|
'';
|
||||||
|
}
|
||||||
73
tests/modules/programs/vscode/mcp-integration.nix
Normal file
73
tests/modules/programs/vscode/mcp-integration.nix
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
package:
|
||||||
|
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.programs.vscode;
|
||||||
|
willUseIfd = package.pname != "vscode";
|
||||||
|
|
||||||
|
mcpFilePath =
|
||||||
|
name:
|
||||||
|
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||||
|
"Library/Application Support/${cfg.nameShort}/User/${
|
||||||
|
lib.optionalString (name != "default") "profiles/${name}/"
|
||||||
|
}mcp.json"
|
||||||
|
else
|
||||||
|
".config/${cfg.nameShort}/User/${
|
||||||
|
lib.optionalString (name != "default") "profiles/${name}/"
|
||||||
|
}mcp.json";
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
lib.mkIf (willUseIfd -> config.test.enableLegacyIfd) {
|
||||||
|
programs.mcp = {
|
||||||
|
enable = true;
|
||||||
|
servers = {
|
||||||
|
everything = {
|
||||||
|
command = "npx";
|
||||||
|
args = [
|
||||||
|
"-y"
|
||||||
|
"@modelcontextprotocol/server-everything"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
context7 = {
|
||||||
|
url = "https://mcp.context7.com/mcp";
|
||||||
|
headers = {
|
||||||
|
CONTEXT7_API_KEY = "{env:CONTEXT7_API_KEY}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
disabled-server = {
|
||||||
|
command = "echo";
|
||||||
|
disabled = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.vscode = {
|
||||||
|
enable = true;
|
||||||
|
inherit package;
|
||||||
|
profiles = {
|
||||||
|
default.enableMcpIntegration = true;
|
||||||
|
test.userMcp = {
|
||||||
|
servers = {
|
||||||
|
Github = {
|
||||||
|
url = "https://api.githubcopilot.com/mcp/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists "home-files/${mcpFilePath "default"}"
|
||||||
|
assertFileContent "home-files/${mcpFilePath "default"}" ${./mcp-integration-default.json}
|
||||||
|
|
||||||
|
assertFileExists "home-files/${mcpFilePath "test"}"
|
||||||
|
assertFileContent "home-files/${mcpFilePath "test"}" ${./mcp-integration-test.json}
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,3 @@
|
||||||
let
|
|
||||||
shellIntegration = ''
|
|
||||||
function yy() {
|
|
||||||
local tmp="$(mktemp -t "yazi-cwd.XXXXX")"
|
|
||||||
yazi "$@" --cwd-file="$tmp"
|
|
||||||
if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
|
|
||||||
builtin cd -- "$cwd"
|
|
||||||
fi
|
|
||||||
rm -f -- "$tmp"
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
programs.bash.enable = true;
|
programs.bash.enable = true;
|
||||||
|
|
||||||
|
|
@ -19,6 +7,12 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
nmt.script = ''
|
nmt.script = ''
|
||||||
assertFileContains home-files/.bashrc '${shellIntegration}'
|
assertFileExists home-files/.bashrc
|
||||||
|
assertFileContains home-files/.bashrc 'function yy() {'
|
||||||
|
assertFileContains home-files/.bashrc 'local tmp="$(mktemp -t "yazi-cwd.XXXXX")"'
|
||||||
|
assertFileContains home-files/.bashrc 'yazi "$@" --cwd-file="$tmp"'
|
||||||
|
assertFileContains home-files/.bashrc 'if cwd="$(<"$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then'
|
||||||
|
assertFileContains home-files/.bashrc 'builtin cd -- "$cwd"'
|
||||||
|
assertFileContains home-files/.bashrc 'rm -f -- "$tmp"'
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
function yy
|
function yy
|
||||||
set -l tmp (mktemp -t "yazi-cwd.XXXXX")
|
set -l tmp (mktemp -t "yazi-cwd.XXXXX")
|
||||||
command yazi $argv --cwd-file="$tmp"
|
command yazi $argv --cwd-file="$tmp"
|
||||||
if set cwd (cat -- "$tmp"); and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
|
if read cwd <"$tmp"; and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
|
||||||
builtin cd -- "$cwd"
|
builtin cd -- "$cwd"
|
||||||
end
|
end
|
||||||
rm -f -- "$tmp"
|
rm -f -- "$tmp"
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,18 @@
|
||||||
let
|
|
||||||
shellIntegration = ''
|
|
||||||
function yy() {
|
|
||||||
local tmp="$(mktemp -t "yazi-cwd.XXXXX")"
|
|
||||||
yazi "$@" --cwd-file="$tmp"
|
|
||||||
if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
|
|
||||||
builtin cd -- "$cwd"
|
|
||||||
fi
|
|
||||||
rm -f -- "$tmp"
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
programs.zsh.enable = true;
|
programs.zsh.enable = true;
|
||||||
|
|
||||||
programs.yazi = {
|
programs.yazi = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableBashIntegration = true;
|
enableZshIntegration = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
nmt.script = ''
|
nmt.script = ''
|
||||||
assertFileContains home-files/.zshrc '${shellIntegration}'
|
assertFileExists home-files/.zshrc
|
||||||
|
assertFileContains home-files/.zshrc 'function yy() {'
|
||||||
|
assertFileContains home-files/.zshrc 'local tmp="$(mktemp -t "yazi-cwd.XXXXX")"'
|
||||||
|
assertFileContains home-files/.zshrc 'yazi "$@" --cwd-file="$tmp"'
|
||||||
|
assertFileContains home-files/.zshrc 'if cwd="$(<"$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then'
|
||||||
|
assertFileContains home-files/.zshrc 'builtin cd -- "$cwd"'
|
||||||
|
assertFileContains home-files/.zshrc 'rm -f -- "$tmp"'
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
83
tests/modules/programs/zed-editor/debug-empty.nix
Normal file
83
tests/modules/programs/zed-editor/debug-empty.nix
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
{
|
||||||
|
programs.zed-editor = {
|
||||||
|
enable = true;
|
||||||
|
package = config.lib.test.mkStubPackage { };
|
||||||
|
userDebug = [
|
||||||
|
{
|
||||||
|
label = "PHP: Listen to Xdebug";
|
||||||
|
adapter = "Xdebug";
|
||||||
|
request = "launch";
|
||||||
|
port = 9003;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
label = "PHP: Debug this test";
|
||||||
|
adapter = "Xdebug";
|
||||||
|
request = "launch";
|
||||||
|
program = "vendor/bin/phpunit";
|
||||||
|
args = [
|
||||||
|
"--filter"
|
||||||
|
"$ZED_SYMBOL"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
home.homeDirectory = lib.mkForce "/@TMPDIR@/hm-user";
|
||||||
|
|
||||||
|
nmt.script =
|
||||||
|
let
|
||||||
|
preexistingDebug = builtins.toFile "preexisting.json" "";
|
||||||
|
|
||||||
|
expectedContent = builtins.toFile "expected.json" ''
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"adapter": "Xdebug",
|
||||||
|
"args": [
|
||||||
|
"--filter",
|
||||||
|
"$ZED_SYMBOL"
|
||||||
|
],
|
||||||
|
"label": "PHP: Debug this test",
|
||||||
|
"program": "vendor/bin/phpunit",
|
||||||
|
"request": "launch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"adapter": "Xdebug",
|
||||||
|
"label": "PHP: Listen to Xdebug",
|
||||||
|
"port": 9003,
|
||||||
|
"request": "launch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
|
||||||
|
debugPath = ".config/zed/debug.json";
|
||||||
|
activationScript = pkgs.writeScript "activation" config.home.activation.zedDebugActivation.data;
|
||||||
|
in
|
||||||
|
''
|
||||||
|
export HOME=$TMPDIR/hm-user
|
||||||
|
|
||||||
|
# Simulate preexisting debug
|
||||||
|
mkdir -p $HOME/.config/zed
|
||||||
|
cat ${preexistingDebug} > $HOME/${debugPath}
|
||||||
|
|
||||||
|
# Run the activation script
|
||||||
|
substitute ${activationScript} $TMPDIR/activate --subst-var TMPDIR
|
||||||
|
chmod +x $TMPDIR/activate
|
||||||
|
$TMPDIR/activate
|
||||||
|
|
||||||
|
# Validate the merged debug
|
||||||
|
assertFileExists "$HOME/${debugPath}"
|
||||||
|
assertFileContent "$HOME/${debugPath}" "${expectedContent}"
|
||||||
|
|
||||||
|
# Test idempotency
|
||||||
|
$TMPDIR/activate
|
||||||
|
assertFileExists "$HOME/${debugPath}"
|
||||||
|
assertFileContent "$HOME/${debugPath}" "${expectedContent}"
|
||||||
|
'';
|
||||||
|
}
|
||||||
58
tests/modules/programs/zed-editor/debug-immutable.nix
Normal file
58
tests/modules/programs/zed-editor/debug-immutable.nix
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Test custom keymap functionality
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
programs.zed-editor = {
|
||||||
|
enable = true;
|
||||||
|
package = config.lib.test.mkStubPackage { };
|
||||||
|
mutableUserDebug = false;
|
||||||
|
userDebug = [
|
||||||
|
{
|
||||||
|
label = "PHP: Listen to Xdebug";
|
||||||
|
adapter = "Xdebug";
|
||||||
|
request = "launch";
|
||||||
|
port = 9003;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
label = "PHP: Debug this test";
|
||||||
|
adapter = "Xdebug";
|
||||||
|
request = "launch";
|
||||||
|
program = "vendor/bin/phpunit";
|
||||||
|
args = [
|
||||||
|
"--filter"
|
||||||
|
"$ZED_SYMBOL"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script =
|
||||||
|
let
|
||||||
|
expectedContent = builtins.toFile "expected.json" ''
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"adapter": "Xdebug",
|
||||||
|
"label": "PHP: Listen to Xdebug",
|
||||||
|
"port": 9003,
|
||||||
|
"request": "launch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"adapter": "Xdebug",
|
||||||
|
"args": [
|
||||||
|
"--filter",
|
||||||
|
"$ZED_SYMBOL"
|
||||||
|
],
|
||||||
|
"label": "PHP: Debug this test",
|
||||||
|
"program": "vendor/bin/phpunit",
|
||||||
|
"request": "launch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
|
||||||
|
settingsPath = ".config/zed/debug.json";
|
||||||
|
in
|
||||||
|
''
|
||||||
|
assertFileExists "home-files/${settingsPath}"
|
||||||
|
assertFileContent "home-files/${settingsPath}" "${expectedContent}"
|
||||||
|
'';
|
||||||
|
}
|
||||||
100
tests/modules/programs/zed-editor/debug.nix
Normal file
100
tests/modules/programs/zed-editor/debug.nix
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
{
|
||||||
|
programs.zed-editor = {
|
||||||
|
enable = true;
|
||||||
|
package = config.lib.test.mkStubPackage { };
|
||||||
|
userDebug = [
|
||||||
|
{
|
||||||
|
label = "PHP: Listen to Xdebug";
|
||||||
|
adapter = "Xdebug";
|
||||||
|
request = "launch";
|
||||||
|
port = 9003;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
label = "PHP: Debug this test";
|
||||||
|
adapter = "Xdebug";
|
||||||
|
request = "launch";
|
||||||
|
program = "vendor/bin/phpunit";
|
||||||
|
args = [
|
||||||
|
"--filter"
|
||||||
|
"$ZED_SYMBOL"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
home.homeDirectory = lib.mkForce "/@TMPDIR@/hm-user";
|
||||||
|
|
||||||
|
nmt.script =
|
||||||
|
let
|
||||||
|
preexistingDebug = builtins.toFile "preexisting.json" ''
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"label": "Debug active Python file",
|
||||||
|
"adapter": "Debugpy",
|
||||||
|
"program": "$ZED_FILE",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "$ZED_WORKTREE_ROOT"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
|
||||||
|
expectedContent = builtins.toFile "expected.json" ''
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"label": "Debug active Python file",
|
||||||
|
"adapter": "Debugpy",
|
||||||
|
"program": "$ZED_FILE",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "$ZED_WORKTREE_ROOT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"adapter": "Xdebug",
|
||||||
|
"args": [
|
||||||
|
"--filter",
|
||||||
|
"$ZED_SYMBOL"
|
||||||
|
],
|
||||||
|
"label": "PHP: Debug this test",
|
||||||
|
"program": "vendor/bin/phpunit",
|
||||||
|
"request": "launch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"adapter": "Xdebug",
|
||||||
|
"label": "PHP: Listen to Xdebug",
|
||||||
|
"port": 9003,
|
||||||
|
"request": "launch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
|
||||||
|
debugPath = ".config/zed/debug.json";
|
||||||
|
activationScript = pkgs.writeScript "activation" config.home.activation.zedDebugActivation.data;
|
||||||
|
in
|
||||||
|
''
|
||||||
|
export HOME=$TMPDIR/hm-user
|
||||||
|
|
||||||
|
# Simulate preexisting debug
|
||||||
|
mkdir -p $HOME/.config/zed
|
||||||
|
cat ${preexistingDebug} > $HOME/${debugPath}
|
||||||
|
|
||||||
|
# Run the activation script
|
||||||
|
substitute ${activationScript} $TMPDIR/activate --subst-var TMPDIR
|
||||||
|
chmod +x $TMPDIR/activate
|
||||||
|
$TMPDIR/activate
|
||||||
|
|
||||||
|
# Validate the merged debug
|
||||||
|
assertFileExists "$HOME/${debugPath}"
|
||||||
|
assertFileContent "$HOME/${debugPath}" "${expectedContent}"
|
||||||
|
|
||||||
|
# Test idempotency
|
||||||
|
$TMPDIR/activate
|
||||||
|
assertFileExists "$HOME/${debugPath}"
|
||||||
|
assertFileContent "$HOME/${debugPath}" "${expectedContent}"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -11,5 +11,8 @@
|
||||||
zed-tasks = ./tasks.nix;
|
zed-tasks = ./tasks.nix;
|
||||||
zed-tasks-immutable = ./tasks-immutable.nix;
|
zed-tasks-immutable = ./tasks-immutable.nix;
|
||||||
zed-tasks-empty = ./tasks-empty.nix;
|
zed-tasks-empty = ./tasks-empty.nix;
|
||||||
|
zed-debug = ./debug.nix;
|
||||||
|
zed-debug-immutable = ./debug-immutable.nix;
|
||||||
|
zed-debug-empty = ./debug-empty.nix;
|
||||||
zed-themes = ./themes;
|
zed-themes = ./themes;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
'';
|
'';
|
||||||
|
|
||||||
lightModeScripts.color-scheme-light = pkgs.writeScript "my-python-script" ''
|
lightModeScripts.color-scheme-light = pkgs.writeScript "my-python-script" ''
|
||||||
#!${pkgs.python}/bin/python
|
#!${pkgs.python2}/bin/python
|
||||||
|
|
||||||
print('Do something!')
|
print('Do something!')
|
||||||
'';
|
'';
|
||||||
|
|
|
||||||
6
tests/modules/services/local-ai/default.nix
Normal file
6
tests/modules/services/local-ai/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
|
||||||
|
local-ai-enabled = ./enabled.nix;
|
||||||
|
local-ai-enabled-with-environment = ./enabled-with-environment.nix;
|
||||||
|
}
|
||||||
18
tests/modules/services/local-ai/enabled-with-environment.nix
Normal file
18
tests/modules/services/local-ai/enabled-with-environment.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
services.local-ai = {
|
||||||
|
enable = true;
|
||||||
|
environment = {
|
||||||
|
MODELS_PATH = "/tmp/models";
|
||||||
|
PRELOAD_MODELS = "[{ \"url\": \"https://huggingface.co/TheBloke/Mistral-7B-Instruct-v0.2-GGUF/resolve/main/mistral-7b-instruct-v0.2.Q4_K_M.gguf\", \"name\": \"mistral-7b-instruct-v0.2.Q4_K_M.gguf\" }]";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileContains \
|
||||||
|
home-files/.config/systemd/user/local-ai.service \
|
||||||
|
"Environment=MODELS_PATH=/tmp/models"
|
||||||
|
assertFileContains \
|
||||||
|
home-files/.config/systemd/user/local-ai.service \
|
||||||
|
"Environment=PRELOAD_MODELS=[{ \"url\": \"https://huggingface.co/TheBloke/Mistral-7B-Instruct-v0.2-GGUF/resolve/main/mistral-7b-instruct-v0.2.Q4_K_M.gguf\", \"name\": \"mistral-7b-instruct-v0.2.Q4_K_M.gguf\" }]"
|
||||||
|
'';
|
||||||
|
}
|
||||||
7
tests/modules/services/local-ai/enabled.nix
Normal file
7
tests/modules/services/local-ai/enabled.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
services.local-ai.enable = true;
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/systemd/user/local-ai.service
|
||||||
|
'';
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue