Compare commits
153
Commits
release-26.05
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
153bad8fb5 | ||
|
|
e061b00885 | ||
|
|
55730a2e34 | ||
|
|
fea021792d | ||
|
|
6aaf73d18d | ||
|
|
9b2afbdd47 | ||
|
|
0043376f4e | ||
|
|
43f9f0abc6 | ||
|
|
c804fab681 | ||
|
|
266211649f | ||
|
|
3abeedcf40 | ||
|
|
0f4a317c06 | ||
|
|
c8f0b8ed44 | ||
|
|
4441446672 | ||
|
|
27a3154f42 | ||
|
|
4ce682641d | ||
|
|
4666af39e9 | ||
|
|
82432731d3 | ||
|
|
a786067676 | ||
|
|
9f9b23ae21 | ||
|
|
8af17160d1 | ||
|
|
d456f483f1 | ||
|
|
6274683ee4 | ||
|
|
5ccd408ca7 | ||
|
|
b9a29e51c5 | ||
|
|
6716d811e5 | ||
|
|
652f754686 | ||
|
|
5bcff43156 | ||
|
|
c187002980 | ||
|
|
eb0eb170bb | ||
|
|
a20c21b2fe | ||
|
|
c51ac59e59 | ||
|
|
a69317872e | ||
|
|
bfc003171b | ||
|
|
ea0bf49c9d | ||
|
|
50978d4bf6 | ||
|
|
b3f454e5c2 | ||
|
|
cbbddc4a6f | ||
|
|
b06723c4d4 | ||
|
|
9b262cb738 | ||
|
|
71b4b46f23 | ||
|
|
b8818d5a65 | ||
|
|
ce68ac6958 | ||
|
|
a69e55d05b | ||
|
|
fa4e2f7e10 | ||
|
|
d2d79394a4 | ||
|
|
a2e23ee8f8 | ||
|
|
89fda2248e | ||
|
|
524d73a903 | ||
|
|
7664e05e24 | ||
|
|
2a7b54df12 | ||
|
|
45e97d1e88 | ||
|
|
df4e046571 | ||
|
|
34dd288e65 | ||
|
|
c03e475289 | ||
|
|
2d4f4e2788 | ||
|
|
8aec76cc1e | ||
|
|
a813a04113 | ||
|
|
1285cd3d68 | ||
|
|
ed8263e7d7 | ||
|
|
1b7fb5e6b4 | ||
|
|
5b6f573372 | ||
|
|
c87a39aa97 | ||
|
|
486595d2cf | ||
|
|
19c41a5a6d | ||
|
|
d29e0ab7c2 | ||
|
|
bb68a1a2e7 | ||
|
|
7dbd305f8b | ||
|
|
f96f717a47 | ||
|
|
2b394beca7 | ||
|
|
74887eaee2 | ||
|
|
6cf5b64c12 | ||
|
|
cadadbc6f4 | ||
|
|
1ffdc28076 | ||
|
|
ebc55fd103 | ||
|
|
6a66db1360 | ||
|
|
c30f177760 | ||
|
|
bd4277722d | ||
|
|
d6f3413874 | ||
|
|
408a6918a0 | ||
|
|
8ff67d938c | ||
|
|
c58ead12ef | ||
|
|
449012836e | ||
|
|
df39142474 | ||
|
|
e9cbe69850 | ||
|
|
301871cd96 | ||
|
|
ec56189b4f | ||
|
|
78da10c4af | ||
|
|
5c34a3c759 | ||
|
|
4fe95527cb | ||
|
|
0c42c7a66f | ||
|
|
aa14fc7b46 | ||
|
|
e4b8f8c8bf | ||
|
|
d4d5d9bb4a | ||
|
|
ddb7082625 | ||
|
|
b2b7db486e | ||
|
|
e621f12c02 | ||
|
|
c321a1c8a7 | ||
|
|
6d4fb02e34 | ||
|
|
4b9a995519 | ||
|
|
09ff165ba6 | ||
|
|
3a647b158e | ||
|
|
d33c97ec1b | ||
|
|
eaca478c95 | ||
|
|
ff77a72fe5 | ||
|
|
0450574818 | ||
|
|
37e8eef933 | ||
|
|
5fadbec07a | ||
|
|
447fd9ff62 | ||
|
|
f1d5aa6f69 | ||
|
|
f4534a4f3c | ||
|
|
efe95f113a | ||
|
|
dc641330e2 | ||
|
|
18b4e1ea6f | ||
|
|
6459fc4aee | ||
|
|
6a6941ad20 | ||
|
|
c81a3d2383 | ||
|
|
9a638c1442 | ||
|
|
ff53fe10e0 | ||
|
|
5ded124f91 | ||
|
|
4c5c1e8ba1 | ||
|
|
f384af1bec | ||
|
|
a7c15f4f65 | ||
|
|
70fc4b4b5e | ||
|
|
58e05d16d7 | ||
|
|
f15c764b14 | ||
|
|
68227a9363 | ||
|
|
af1588ad6f | ||
|
|
c0436bc028 | ||
|
|
07c723c3fe | ||
|
|
a7a4158831 | ||
|
|
274299642b | ||
|
|
26e34a99e4 | ||
|
|
a6a13bb0a0 | ||
|
|
5148001968 | ||
|
|
c7e4087b4d | ||
|
|
c7c139f742 | ||
|
|
3e17edd5e6 | ||
|
|
d00b16c511 | ||
|
|
112a3a3783 | ||
|
|
04ec113f8b | ||
|
|
d0af9b8bf3 | ||
|
|
5a608a621b | ||
|
|
8962c2a0f4 | ||
|
|
3d64f2875e | ||
|
|
c53d0150e9 | ||
|
|
16110b65b2 | ||
|
|
7d8127d308 | ||
|
|
da8f8fcd69 | ||
|
|
948753061a | ||
|
|
61e2c96593 | ||
|
|
d17712ec7a | ||
|
|
874ae4cccf |
@@ -9,7 +9,7 @@ updates:
|
||||
prefix: "ci:"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
target-branch: "release-25.11"
|
||||
target-branch: "release-26.05"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
@@ -23,7 +23,7 @@ updates:
|
||||
prefix: "flake:"
|
||||
- package-ecosystem: "nix"
|
||||
directory: "/"
|
||||
target-branch: "release-25.11"
|
||||
target-branch: "release-26.05"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
- modules/programs/claude-code.nix
|
||||
- modules/programs/codex.nix
|
||||
- modules/programs/fabric-ai.nix
|
||||
- modules/programs/gemini-cli.nix
|
||||
- modules/programs/antigravity-cli.nix
|
||||
- modules/programs/mcp.nix
|
||||
- modules/programs/mistral-vibe.nix
|
||||
- modules/programs/mods.nix
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
nix_path: nixpkgs=channel:nixpkgs-unstable
|
||||
- uses: cachix/cachix-action@v17
|
||||
with:
|
||||
name: nix-community
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Contributing
|
||||
|
||||
Home Manager contribution guidelines live in the manual:
|
||||
|
||||
- [`docs/manual/contributing.md`](docs/manual/contributing.md)
|
||||
- <https://nix-community.github.io/home-manager/#ch-contributing>
|
||||
|
||||
Read those docs before changing modules, tests, news entries, or release notes.
|
||||
+2
-2
@@ -125,7 +125,7 @@ Reference commits:
|
||||
- Update the nixpkgs input to track the corresponding stable branch
|
||||
- Example: For `release-25.11`, change from:
|
||||
```nix
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
```
|
||||
to:
|
||||
```nix
|
||||
@@ -135,7 +135,7 @@ Reference commits:
|
||||
- Commit the flake.nix and flake.lock changes
|
||||
|
||||
**Note**: The release branch should track the stable NixOS release channel
|
||||
(e.g., `nixos-25.11`), while master continues to track `nixos-unstable`.
|
||||
(e.g., `nixos-25.11`), while master continues to track `nixpkgs-unstable`.
|
||||
|
||||
#### Step 3.5: On master - Update CI for the New Release Branch
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.PHONY: all all-tests test test-install format
|
||||
NIXPKGS_REV := nixos-unstable
|
||||
NIXPKGS_REV := nixpkgs-unstable
|
||||
NIX_PATH := nixpkgs=https://github.com/NixOS/nixpkgs/archive/${NIXPKGS_REV}.tar.gz
|
||||
|
||||
all: all-tests test-install
|
||||
|
||||
@@ -25,8 +25,9 @@ If you would like to contribute to Home Manager, then please have a look at
|
||||
Releases
|
||||
--------
|
||||
|
||||
Home Manager is developed against `nixos-unstable` branch, which often causes
|
||||
it to contain tweaks for changes/packages not yet released in stable [NixOS][].
|
||||
Home Manager is developed against the `nixpkgs-unstable` branch, which often
|
||||
causes it to contain tweaks for changes/packages not yet released in stable
|
||||
[NixOS][].
|
||||
To avoid breaking users' configurations, Home Manager is released in branches
|
||||
corresponding to NixOS releases (e.g. `release-26.05`). These branches get
|
||||
fixes, but usually not new modules. If you need a module to be backported, then
|
||||
@@ -49,7 +50,7 @@ dconf store and cannot tell whether a configuration that is about to be
|
||||
overwritten was from a previous Home Manager generation or from manual
|
||||
configuration.
|
||||
|
||||
Home Manager targets [NixOS][] unstable and NixOS version 26.05 (the current
|
||||
Home Manager targets `nixpkgs-unstable` and NixOS version 26.05 (the current
|
||||
stable version), it may or may not work on other Linux distributions and NixOS
|
||||
versions.
|
||||
|
||||
|
||||
+42
-8
@@ -119,6 +119,13 @@
|
||||
name = "Denis Kaynar";
|
||||
source = "home-manager";
|
||||
};
|
||||
ErinaYip = {
|
||||
email = "erinayip@outlook.com";
|
||||
github = "ErinaYip";
|
||||
githubId = 227132255;
|
||||
name = "erina";
|
||||
source = "home-manager";
|
||||
};
|
||||
Eveeifyeve = {
|
||||
email = "eveeg1971@gmail.com";
|
||||
github = "Eveeifyeve";
|
||||
@@ -522,14 +529,6 @@
|
||||
name = "David Chocholatý";
|
||||
source = "nixpkgs";
|
||||
};
|
||||
adisbladis = {
|
||||
email = "adisbladis@gmail.com";
|
||||
github = "adisbladis";
|
||||
githubId = 63286;
|
||||
matrix = "@adis:blad.is";
|
||||
name = "Adam Hose";
|
||||
source = "nixpkgs";
|
||||
};
|
||||
afresquet = {
|
||||
email = "alvarofresquet@gmail.com";
|
||||
github = "afresquet";
|
||||
@@ -686,6 +685,13 @@
|
||||
name = "Filippo Berto";
|
||||
source = "nixpkgs";
|
||||
};
|
||||
bittner = {
|
||||
email = "peter@painless.software";
|
||||
github = "bittner";
|
||||
githubId = 665072;
|
||||
name = "Peter Bittner";
|
||||
source = "home-manager";
|
||||
};
|
||||
bjpbakker = {
|
||||
email = "bart@thesoftwarecraft.com";
|
||||
github = "bjpbakker";
|
||||
@@ -1467,6 +1473,13 @@
|
||||
name = "Nikolaos Karaolidis";
|
||||
source = "nixpkgs";
|
||||
};
|
||||
kayskayskays = {
|
||||
email = "115312476+kayskayskays@users.noreply.github.com";
|
||||
github = "kayskayskays";
|
||||
githubId = 115312476;
|
||||
name = "Kays";
|
||||
source = "home-manager";
|
||||
};
|
||||
khaneliman = {
|
||||
email = "khaneliman12@gmail.com";
|
||||
github = "khaneliman";
|
||||
@@ -1618,6 +1631,13 @@
|
||||
name = "Manaiki Laut";
|
||||
source = "nixpkgs";
|
||||
};
|
||||
marijanp = {
|
||||
email = "marijan.petricevic94@gmail.com";
|
||||
github = "marijanp";
|
||||
githubId = 13599169;
|
||||
name = "Marijan Petričević";
|
||||
source = "nixpkgs";
|
||||
};
|
||||
mateusauler = {
|
||||
email = "mateus@auler.dev";
|
||||
github = "mateusauler";
|
||||
@@ -2171,6 +2191,13 @@
|
||||
name = "Seong Yong-ju";
|
||||
source = "nixpkgs";
|
||||
};
|
||||
semi710 = {
|
||||
email = "nik.singh710@gmail.com";
|
||||
github = "semi710";
|
||||
githubId = 60490474;
|
||||
name = "Nikhil Singh";
|
||||
source = "home-manager";
|
||||
};
|
||||
shikanime = {
|
||||
email = "william.phetsinorath@shikanime.studio";
|
||||
github = "shikanime";
|
||||
@@ -2250,6 +2277,13 @@
|
||||
name = "Sumner Evans";
|
||||
source = "nixpkgs";
|
||||
};
|
||||
superflash41 = {
|
||||
email = "saymon.nicho@pucp.edu.pe";
|
||||
github = "superflash41";
|
||||
githubId = 102434258;
|
||||
name = "Saymon Nicho";
|
||||
source = "home-manager";
|
||||
};
|
||||
surfaceflinger = {
|
||||
email = "nat@nekopon.pl";
|
||||
github = "surfaceflinger";
|
||||
|
||||
+9
-3
@@ -253,9 +253,15 @@ let
|
||||
# Generate the HTML manual pages
|
||||
home-manager-manual = pkgs.callPackage ./home-manager-manual.nix {
|
||||
home-manager-options = {
|
||||
home-manager = hmOptionsDocs.optionsJSON;
|
||||
nixos = nixosOptionsDocs.optionsJSON;
|
||||
nix-darwin = nixDarwinOptionsDocs.optionsJSON;
|
||||
home-manager = {
|
||||
json = hmOptionsDocs.optionsJSON;
|
||||
};
|
||||
nixos = {
|
||||
json = nixosOptionsDocs.optionsJSON;
|
||||
};
|
||||
nix-darwin = {
|
||||
json = nixDarwinOptionsDocs.optionsJSON;
|
||||
};
|
||||
};
|
||||
inherit revision;
|
||||
};
|
||||
|
||||
Generated
+4
-4
@@ -2,16 +2,16 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1773821835,
|
||||
"narHash": "sha256-wvfdLLWJ2I9oEpDd9PfMA8osfIZicoQ5MT1jIwNs9Tk=",
|
||||
"lastModified": 1779877693,
|
||||
"narHash": "sha256-NOF9NAREhxr50bbBfVcVOq+ArCMSoe8dP79Pk2uyARk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c06b4ae3d6599a672a6210b7021d699c351eebda",
|
||||
"rev": "4100e830e085863741bc69b156ec4ccd53ab5be0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
description = "Support developing Home Manager documentation";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
scss-reset = {
|
||||
url = "github:andreymatin/scss-reset/1.4.2";
|
||||
flake = false;
|
||||
|
||||
@@ -1,63 +1,99 @@
|
||||
{
|
||||
stdenv,
|
||||
lib,
|
||||
documentation-highlighter,
|
||||
callPackage,
|
||||
mdbook,
|
||||
python3,
|
||||
revision,
|
||||
home-manager-options,
|
||||
nixos-render-docs,
|
||||
}:
|
||||
let
|
||||
outputPath = "share/doc/home-manager";
|
||||
mdbookOptions = callPackage ./mdbook/options.nix {
|
||||
manpageUrls = ./manual/manpage-urls.json;
|
||||
inherit revision;
|
||||
optionDocs = {
|
||||
home-manager = {
|
||||
title = "Home Manager Configuration Options";
|
||||
path = "home-manager";
|
||||
prefix = "opt-";
|
||||
json = "${home-manager-options.home-manager.json}";
|
||||
};
|
||||
nixos = {
|
||||
title = "NixOS Configuration Options";
|
||||
path = "nixos";
|
||||
prefix = "nixos-opt-";
|
||||
json = "${home-manager-options.nixos.json}";
|
||||
};
|
||||
nix-darwin = {
|
||||
title = "nix-darwin Configuration Options";
|
||||
path = "nix-darwin";
|
||||
prefix = "nix-darwin-opt-";
|
||||
json = "${home-manager-options.nix-darwin.json}";
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "home-manager-manual";
|
||||
|
||||
nativeBuildInputs = [ nixos-render-docs ];
|
||||
nativeBuildInputs = [
|
||||
mdbook
|
||||
python3
|
||||
];
|
||||
|
||||
src = ./manual;
|
||||
src = ./.;
|
||||
|
||||
buildPhase = ''
|
||||
mkdir -p out/{highlightjs,media}
|
||||
runHook preBuild
|
||||
|
||||
cp -t out/highlightjs \
|
||||
${documentation-highlighter}/highlight.pack.js \
|
||||
${documentation-highlighter}/LICENSE \
|
||||
${documentation-highlighter}/mono-blue.css \
|
||||
${documentation-highlighter}/loader.js
|
||||
mkdir -p source
|
||||
python3 ${./mdbook/convert-markup.py} "$src/manual" source
|
||||
python3 ${./mdbook/convert-markup.py} \
|
||||
--base-depth 1 \
|
||||
"$src/release-notes" \
|
||||
source/release-notes
|
||||
|
||||
substituteInPlace ./options.md \
|
||||
--subst-var-by \
|
||||
OPTIONS_JSON \
|
||||
${home-manager-options.home-manager}/share/doc/nixos/options.json
|
||||
cp -r ${mdbookOptions}/options source/options
|
||||
|
||||
substituteInPlace ./nixos-options.md \
|
||||
--subst-var-by \
|
||||
OPTIONS_JSON \
|
||||
${home-manager-options.nixos}/share/doc/nixos/options.json
|
||||
python3 ${./mdbook/substitute-summary.py} \
|
||||
source/SUMMARY.md \
|
||||
${mdbookOptions}/summary/home-manager.md \
|
||||
${mdbookOptions}/summary/nixos.md \
|
||||
${mdbookOptions}/summary/nix-darwin.md
|
||||
|
||||
substituteInPlace ./nix-darwin-options.md \
|
||||
--subst-var-by \
|
||||
OPTIONS_JSON \
|
||||
${home-manager-options.nix-darwin}/share/doc/nixos/options.json
|
||||
mdbook build source --dest-dir book
|
||||
|
||||
cp ${./options.html} out/options.html
|
||||
mkdir -p out
|
||||
cp -r book/* out/
|
||||
|
||||
cp ${./static/style.css} out/style.css
|
||||
cp ${./static/anchor-links.js} out/anchor-links.js
|
||||
makeRedirect() {
|
||||
local target=$1
|
||||
local destination=$2
|
||||
printf '%s\n' \
|
||||
'<!doctype html>' \
|
||||
'<html lang="en">' \
|
||||
' <head>' \
|
||||
' <meta charset="utf-8">' \
|
||||
" <meta http-equiv=\"refresh\" content=\"0; url=$destination\">" \
|
||||
" <link rel=\"canonical\" href=\"$destination\">" \
|
||||
' <title>Redirecting...</title>' \
|
||||
' </head>' \
|
||||
' <body>' \
|
||||
" <p>Redirecting to <a href=\"$destination\">$destination</a>.</p>" \
|
||||
' </body>' \
|
||||
'</html>' \
|
||||
> "out/$target"
|
||||
}
|
||||
|
||||
cp -r ${./release-notes} release-notes
|
||||
makeRedirect index.xhtml index.html
|
||||
makeRedirect options.html options/home-manager/index.html
|
||||
makeRedirect options.xhtml options/home-manager/index.html
|
||||
makeRedirect nixos-options.xhtml options/nixos/index.html
|
||||
makeRedirect nix-darwin-options.xhtml options/nix-darwin/index.html
|
||||
makeRedirect release-notes.xhtml release-notes/release-notes.html
|
||||
|
||||
nixos-render-docs manual html \
|
||||
--manpage-urls ./manpage-urls.json \
|
||||
--revision ${lib.trivial.revisionWithDefault revision} \
|
||||
--stylesheet style.css \
|
||||
--script highlightjs/highlight.pack.js \
|
||||
--script highlightjs/loader.js \
|
||||
--script anchor-links.js \
|
||||
--toc-depth 1 \
|
||||
--section-toc-depth 1 \
|
||||
manual.md \
|
||||
out/index.xhtml
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
@@ -69,7 +105,9 @@ stdenv.mkDerivation {
|
||||
echo "doc manual $dest index.html" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
passthru = { inherit home-manager-options; };
|
||||
passthru = {
|
||||
inherit home-manager-options mdbookOptions;
|
||||
};
|
||||
|
||||
meta = {
|
||||
maintainers = [ lib.maintainers.considerate ];
|
||||
|
||||
@@ -25,9 +25,14 @@ let
|
||||
if [[ ! -v BROWSER || -z $BROWSER ]]; then
|
||||
echo "$0: unable to start a web browser; please set \$BROWSER"
|
||||
exit 1
|
||||
else
|
||||
exec "$BROWSER" "${html}/share/doc/${pathName}/index.xhtml"
|
||||
fi
|
||||
|
||||
manualPath="${html}/share/doc/${pathName}/index.html"
|
||||
if [[ ! -e $manualPath ]]; then
|
||||
manualPath="${html}/share/doc/${pathName}/index.xhtml"
|
||||
fi
|
||||
|
||||
exec "$BROWSER" "$manualPath"
|
||||
'';
|
||||
|
||||
desktopItem = makeDesktopItem {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Summary
|
||||
|
||||
- [Preface](preface.md)
|
||||
- [Introduction to Home Manager](introduction.md)
|
||||
- [Installing Home Manager](installation.md)
|
||||
- [Standalone installation](installation/standalone.md)
|
||||
- [NixOS module](installation/nixos.md)
|
||||
- [nix-darwin module](installation/nix-darwin.md)
|
||||
- [Using Home Manager](usage.md)
|
||||
- [Configuration Example](usage/configuration.md)
|
||||
- [Rollbacks](usage/rollbacks.md)
|
||||
- [Keeping your ~ safe from harm](usage/dotfiles.md)
|
||||
- [Graphical services](usage/graphical.md)
|
||||
- [GPU on non-NixOS systems](usage/gpu-non-nixos.md)
|
||||
- [Modular Services](usage/modular-services.md)
|
||||
- [Updating](usage/updating.md)
|
||||
- [Upgrading to a new Home Manager release](usage/upgrading.md)
|
||||
- [Nix Flakes](nix-flakes.md)
|
||||
- [Prerequisites](nix-flakes/prerequisites.md)
|
||||
- [Standalone setup](nix-flakes/standalone.md)
|
||||
- [NixOS module](nix-flakes/nixos.md)
|
||||
- [nix-darwin module](nix-flakes/nix-darwin.md)
|
||||
- [flake-parts module](nix-flakes/flake-parts.md)
|
||||
- [Writing Home Manager Modules](writing-modules.md)
|
||||
- [Option Types](writing-modules/types.md)
|
||||
- [Contributing](contributing.md)
|
||||
- [Getting started](contributing/getting-started.md)
|
||||
- [Guidelines](contributing/guidelines.md)
|
||||
- [Release Notes](contributing/release-notes.md)
|
||||
- [News](contributing/news.md)
|
||||
- [Tests](contributing/tests.md)
|
||||
- [Home Manager Internals](internals.md)
|
||||
- [Activation](internals/activation.md)
|
||||
- [Third-Party Tools and Extensions](3rd-party.md)
|
||||
- [Module Collections](3rd-party/collections.md)
|
||||
- [Frequently Asked Questions (FAQ)](faq.md)
|
||||
- [Why is there a collision error when switching generation?](faq/collision.md)
|
||||
- [Why are the session variables not set?](faq/session-variables.md)
|
||||
- [How to set up a configuration for multiple users/machines?](faq/multiple-users-machines.md)
|
||||
- [Why do I get an error message about `ca.desrt.dconf` or `dconf.service`?](faq/ca-desrt-dconf.md)
|
||||
- [How do I install packages from Nixpkgs unstable?](faq/unstable.md)
|
||||
- [How do I change the package used by a module?](faq/change-package-module.md)
|
||||
- [Options](options.md)
|
||||
@HOME_MANAGER_OPTIONS@
|
||||
@NIXOS_OPTIONS@
|
||||
@NIX_DARWIN_OPTIONS@
|
||||
- [Release Notes](release-notes/release-notes.md)
|
||||
- [Release 26.11](release-notes/rl-2611.md)
|
||||
- [Release 26.05](release-notes/rl-2605.md)
|
||||
- [Release 25.11](release-notes/rl-2511.md)
|
||||
- [Release 25.05](release-notes/rl-2505.md)
|
||||
- [Release 24.11](release-notes/rl-2411.md)
|
||||
- [Release 24.05](release-notes/rl-2405.md)
|
||||
- [Release 23.11](release-notes/rl-2311.md)
|
||||
- [Release 23.05](release-notes/rl-2305.md)
|
||||
- [Release 22.11](release-notes/rl-2211.md)
|
||||
- [Release 22.05](release-notes/rl-2205.md)
|
||||
- [Release 21.11](release-notes/rl-2111.md)
|
||||
- [Release 21.05](release-notes/rl-2105.md)
|
||||
- [Release 20.09](release-notes/rl-2009.md)
|
||||
- [Release 20.03](release-notes/rl-2003.md)
|
||||
- [Release 19.09](release-notes/rl-1909.md)
|
||||
- [Release 19.03](release-notes/rl-1903.md)
|
||||
- [Release 18.09](release-notes/rl-1809.md)
|
||||
@@ -0,0 +1,11 @@
|
||||
[book]
|
||||
title = "Home Manager Manual"
|
||||
language = "en"
|
||||
src = "."
|
||||
|
||||
[output.html]
|
||||
git-repository-url = "https://github.com/nix-community/home-manager"
|
||||
|
||||
[output.html.fold]
|
||||
enable = true
|
||||
level = 0
|
||||
@@ -22,6 +22,7 @@ configuration.
|
||||
```{=include=} sections
|
||||
contributing/getting-started.md
|
||||
contributing/guidelines.md
|
||||
contributing/release-notes.md
|
||||
contributing/news.md
|
||||
contributing/tests.md
|
||||
```
|
||||
|
||||
@@ -58,6 +58,33 @@ YAML, INI, TOML, or even a plain list of key/value pairs then consider
|
||||
using a `settings` option as described in [Nix RFC
|
||||
42](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md).
|
||||
|
||||
These guidelines describe the minimum option design requirements. Before
|
||||
submitting a module, compare it against the upstream documentation or
|
||||
source code and verify that the generated files, services, environment
|
||||
variables, and command line arguments all match the upstream behavior
|
||||
you intend to expose.
|
||||
|
||||
If a module installs a package, try to make the package option nullable,
|
||||
for example
|
||||
|
||||
``` nix
|
||||
package = lib.mkPackageOption pkgs "xdg-terminal-exec" { nullable = true; };
|
||||
```
|
||||
|
||||
This lets users keep installation outside Home Manager, for example via
|
||||
`apt` or because the program is built into macOS, while still using the
|
||||
module for configuration. Keeping the package non-nullable is fine when
|
||||
the enabled behavior structurally requires the executable or when
|
||||
package-less support would make the module significantly more complex.
|
||||
|
||||
Avoid generating files for empty settings, null packages, or optional
|
||||
features that are not configured.
|
||||
|
||||
If upstream does not use XDG paths by default but supports changing the
|
||||
configuration location with an environment variable, for example
|
||||
`FOO_HOME`, expose a `configDir` option and use it to respect
|
||||
`home.preferXdgDirectories`.
|
||||
|
||||
## Add relevant tests {#sec-guidelines-add-tests}
|
||||
|
||||
If at all possible, make sure to add new tests and expand existing tests
|
||||
@@ -81,7 +108,7 @@ Manager Git repository:
|
||||
|
||||
``` shell
|
||||
$ nix-build -A docs.html
|
||||
$ xdg-open ./result/share/doc/home-manager/index.xhtml
|
||||
$ xdg-open ./result/share/doc/home-manager/index.html
|
||||
```
|
||||
|
||||
When you have made changes to a module, it is a good idea to check that
|
||||
@@ -159,6 +186,13 @@ formatted as described in [News](#sec-news).
|
||||
|
||||
When new modules are added a news entry should be included.
|
||||
|
||||
News entries and release notes serve different purposes. Release notes
|
||||
should be updated separately when a change affects users migrating
|
||||
between stable releases, such as state version default changes, required
|
||||
migration steps, or broad behavior changes. See
|
||||
[Release Notes](#sec-contributing-release-notes) and [News](#sec-news)
|
||||
for more details.
|
||||
|
||||
## Use conditional modules and news {#sec-guidelines-conditional-modules}
|
||||
|
||||
Home Manager includes a number of modules that are only usable on some
|
||||
@@ -194,6 +228,11 @@ that is, each commit should make sense in isolation. In particular, you
|
||||
will be asked to amend any commit that introduces syntax errors or
|
||||
similar problems even if they are fixed in a later commit.
|
||||
|
||||
Keep commits atomic and separated by concern. For example, a new
|
||||
maintainer entry should be a separate first commit, and a shared module
|
||||
should be committed separately from integrations in existing modules.
|
||||
Pull requests should not include merge commits or fixup commits.
|
||||
|
||||
The commit messages should follow the [seven
|
||||
rules](https://chris.beams.io/posts/git-commit/#seven-rules), except for
|
||||
\"Capitalize the subject line\". We also ask you to include the affected
|
||||
|
||||
@@ -6,8 +6,17 @@ associated news entry. In general, a news entry should only be added for
|
||||
truly noteworthy news. For example, a bug fix or new option does
|
||||
generally not need a news entry.
|
||||
|
||||
Release notes and news entries serve different purposes. A news entry is
|
||||
shown during Home Manager activation and is useful for day-to-day
|
||||
communication about noteworthy changes, such as a new module, a new
|
||||
feature, or a specific deprecation. Release notes are read from the
|
||||
website documentation and should summarize what users need to know before
|
||||
or during a stable-release upgrade. See
|
||||
[Release Notes](#sec-contributing-release-notes) for guidance on changes
|
||||
that affect stable-release upgrades.
|
||||
|
||||
If you do have a change worthy of a news entry then please add one in
|
||||
[`news.nix`](https://github.com/nix-community/home-manager/blob/master/modules/misc/news.nix)
|
||||
[`news`](https://github.com/nix-community/home-manager/blob/master/modules/misc/news)
|
||||
but you should follow some basic guidelines:
|
||||
|
||||
- Use the included news entry generator to create a news entry file:
|
||||
@@ -28,6 +37,8 @@ but you should follow some basic guidelines:
|
||||
- The entry condition should be as specific as possible. For example,
|
||||
if you are changing or deprecating a specific option then you could
|
||||
restrict the news to those users who actually use this option.
|
||||
Prefer a targeted condition over skipping useful news only to avoid
|
||||
notifying unaffected users.
|
||||
|
||||
- Wrap the news message so that it will fit in the typical terminal,
|
||||
that is, at most 80 characters wide. Ideally a bit less.
|
||||
@@ -53,6 +64,10 @@ but you should follow some basic guidelines:
|
||||
|
||||
A new module is available: 'services.foo'.
|
||||
|
||||
Since this news is specific to the module, its condition should use
|
||||
the module enable option to avoid spamming non-users of the module,
|
||||
for example `condition = config.services.foo.enable;`.
|
||||
|
||||
If the module is platform specific, e.g., a service module using
|
||||
systemd, then a condition like
|
||||
|
||||
@@ -60,5 +75,7 @@ but you should follow some basic guidelines:
|
||||
condition = hostPlatform.isLinux;
|
||||
```
|
||||
|
||||
should be added. If you contribute a module then you don't need to
|
||||
add this entry, the merger will create an entry for you.
|
||||
should be added, either by itself for platform-scoped news or in
|
||||
combination with the module enable option. Use the `create-news-entry`
|
||||
generator described above to scaffold this entry as part of your
|
||||
contribution.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Release Notes {#sec-contributing-release-notes}
|
||||
|
||||
Home Manager release notes are published as part of the website documentation in
|
||||
`docs/release-notes/rl-*.md`. They are intended for users migrating from one
|
||||
stable release to another.
|
||||
|
||||
A change should be mentioned in the release notes when it affects migration
|
||||
between stable releases. This includes changes that require user action or
|
||||
awareness during an upgrade, such as:
|
||||
|
||||
- breaking changes;
|
||||
- default changes guarded by `home.stateVersion`;
|
||||
- required migration steps;
|
||||
- broad behavior changes across platform or integration boundaries;
|
||||
- compatibility changes that can affect existing configurations.
|
||||
|
||||
Do not use the presence or absence of a news entry to decide whether a change
|
||||
belongs in the release notes. A change can require a news entry, a release note,
|
||||
both, or neither.
|
||||
|
||||
When preparing a release, review the commits since the previous stable release
|
||||
and update the upcoming release note file with the migration impact. Prefer
|
||||
concise user-facing descriptions that name the affected option paths and explain
|
||||
required action.
|
||||
@@ -54,6 +54,13 @@ The `default.nix` file should list all test cases:
|
||||
}
|
||||
```
|
||||
|
||||
Prefer keeping related assertions in as few test files as practical.
|
||||
Exercising several cases in one evaluation keeps the test suite cheaper
|
||||
to evaluate and reduces maintenance burden. Split cases into separate
|
||||
files when they need incompatible module configuration, platform
|
||||
conditions, expected assertion failures, or otherwise cannot share one
|
||||
evaluation.
|
||||
|
||||
### Common NMT Assertions {#sec-tests-assertions}
|
||||
|
||||
NMT provides several assertion functions:
|
||||
@@ -162,6 +169,12 @@ For cross-platform modules that have packages which need to be stubbed on Darwin
|
||||
add the package names to `tests/darwinScrublist.nix` to prevent build failures
|
||||
during cross-platform test runs.
|
||||
|
||||
On Linux, packages are automatically scrubbed by the test infrastructure,
|
||||
so tests should normally use the module's default package. Use
|
||||
`test.stubs` or `config.lib.test.mkStubPackage` only when the automatic
|
||||
scrubbing does not model the behavior that the test needs, such as a
|
||||
package with additional files or a non-default executable layout.
|
||||
|
||||
## Using the tests command {#sec-tests-command}
|
||||
|
||||
Home Manager provides a convenient `tests` command for discovering and running tests:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Home Manager Manual {#home-manager-manual}
|
||||
|
||||
## Version 26.05
|
||||
## Version 26.11 (unstable)
|
||||
|
||||
|
||||
```{=include=} preface
|
||||
|
||||
@@ -8,7 +8,7 @@ Manager's flake module, `flakeModules.home-manager`.
|
||||
description = "flake-parts configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
home-manager.url = "github:nix-community/home-manager";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ to that of NixOS. The `flake.nix` would be:
|
||||
description = "Darwin configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
darwin.url = "github:nix-darwin/nix-darwin";
|
||||
darwin.inputs.nixpkgs.follows = "nixpkgs";
|
||||
home-manager.url = "github:nix-community/home-manager";
|
||||
|
||||
@@ -8,7 +8,7 @@ be as follows:
|
||||
description = "NixOS configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
home-manager.url = "github:nix-community/home-manager";
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ imported Home Manager modules, use `extraSpecialArgs` in the call to
|
||||
``` nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
home-manager.url = "github:nix-community/home-manager";
|
||||
};
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@ use Home Manager's `master` branch.
|
||||
|
||||
Home Manager follows NixOS release cycles and provides corresponding branches:
|
||||
|
||||
- **release-<version>**: Stable branch for the matching NixOS or
|
||||
- **release-\<version\>**: Stable branch for the matching NixOS or
|
||||
Nixpkgs release, such as `release-25.11`.
|
||||
|
||||
- **master**: Development branch (tracks nixos-unstable)
|
||||
- **master**: Development branch (tracks nixpkgs-unstable)
|
||||
|
||||
:::{.note}
|
||||
Use the Home Manager branch that matches the Nixpkgs branch used to
|
||||
@@ -61,7 +61,7 @@ instead:
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager";
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import html
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SIMPLE_ROLES = (
|
||||
"command",
|
||||
"component",
|
||||
"description",
|
||||
"file",
|
||||
"index",
|
||||
"system",
|
||||
"type",
|
||||
)
|
||||
|
||||
HEADING_ANCHOR = re.compile(r"^(#{1,6}\s+)(.*)\s+\{#([^}]+)\}\s*$")
|
||||
INLINE_ANCHOR = re.compile(r"\[\]\{#([^}]+)\}")
|
||||
OPTION_ROLE = re.compile(r"(?<![$`])\{option\}`([^`]*)`")
|
||||
SIMPLE_ROLE = re.compile(r"(?<![$`])\{(" + "|".join(SIMPLE_ROLES) + r")\}`([^`]*)`")
|
||||
OPTION_LINK = re.compile(
|
||||
r"\[(?P<label>[^\]]*)\]\(#(?P<anchor>(?:opt|nixos-opt|nix-darwin-opt)-[^)]+)\)"
|
||||
)
|
||||
LEFTOVER_ROLE = re.compile(
|
||||
r"(?<![$`])\{(" + "|".join(("option", *SIMPLE_ROLES)) + r")\}`[^`]*`"
|
||||
)
|
||||
FENCE = re.compile(r"^\s*(`{3,})(.*)$")
|
||||
FENCE_CLOSE = re.compile(r"^\s*`{3,}\s*$")
|
||||
ADMONITION_OPEN = re.compile(r"^\s*:::\s*\{\.(note|warning|example)\}\s*$")
|
||||
ADMONITION_CLOSE = re.compile(r"^\s*:::\s*$")
|
||||
DEEP_SPLIT_NAMESPACES = {"programs", "services"}
|
||||
|
||||
|
||||
def option_target(anchor: str, current_file: Path, base_depth: int) -> str:
|
||||
if anchor.startswith("nix-darwin-opt-"):
|
||||
option = anchor.removeprefix("nix-darwin-opt-")
|
||||
option = option.replace("<", "_").replace(">", "_")
|
||||
anchor = f"nix-darwin-opt-{option}"
|
||||
base = "options/nix-darwin"
|
||||
elif anchor.startswith("nixos-opt-"):
|
||||
option = anchor.removeprefix("nixos-opt-")
|
||||
option = option.replace("<", "_").replace(">", "_")
|
||||
anchor = f"nixos-opt-{option}"
|
||||
base = "options/nixos"
|
||||
else:
|
||||
option = anchor.removeprefix("opt-")
|
||||
option = option.replace("<", "_").replace(">", "_")
|
||||
anchor = f"opt-{option}"
|
||||
base = "options/home-manager"
|
||||
|
||||
page_parts = option_page_parts(option)
|
||||
prefix = "../" * (base_depth + len(current_file.parent.parts))
|
||||
return f"{prefix}{base}/{'/'.join(page_parts)}.md#{anchor}"
|
||||
|
||||
|
||||
def option_page_parts(option_name: str) -> list[str]:
|
||||
parts = option_name.split(".")
|
||||
namespace = parts[0]
|
||||
if namespace in DEEP_SPLIT_NAMESPACES and len(parts) > 1:
|
||||
return parts[:2]
|
||||
return [namespace]
|
||||
|
||||
|
||||
def option_label(anchor: str) -> str:
|
||||
if anchor.startswith("nix-darwin-opt-"):
|
||||
return anchor.removeprefix("nix-darwin-opt-")
|
||||
if anchor.startswith("nixos-opt-"):
|
||||
return anchor.removeprefix("nixos-opt-")
|
||||
return anchor.removeprefix("opt-")
|
||||
|
||||
|
||||
def markdown_label(value: str) -> str:
|
||||
return value.replace("<", "<").replace(">", ">")
|
||||
|
||||
|
||||
def convert_inline(line: str, current_file: Path, base_depth: int) -> str:
|
||||
line = line.replace("index.xhtml", "index.html")
|
||||
line = INLINE_ANCHOR.sub(
|
||||
lambda match: f'<a id="{html.escape(match.group(1), quote=True)}"></a>',
|
||||
line,
|
||||
)
|
||||
line = OPTION_ROLE.sub(
|
||||
lambda match: (
|
||||
f"[{markdown_label(match.group(1))}]"
|
||||
f"({option_target(f'opt-{match.group(1)}', current_file, base_depth)})"
|
||||
),
|
||||
line,
|
||||
)
|
||||
line = OPTION_LINK.sub(
|
||||
lambda match: (
|
||||
f"[{markdown_label(match.group('label') or option_label(match.group('anchor')))}]"
|
||||
f"({option_target(match.group('anchor'), current_file, base_depth)})"
|
||||
),
|
||||
line,
|
||||
)
|
||||
return SIMPLE_ROLE.sub(lambda match: f"`{match.group(2)}`", line)
|
||||
|
||||
|
||||
def convert_heading(line: str, current_file: Path, base_depth: int) -> str:
|
||||
match = HEADING_ANCHOR.match(line)
|
||||
if match is None:
|
||||
return convert_inline(line, current_file, base_depth)
|
||||
|
||||
prefix, title, anchor = match.groups()
|
||||
return (
|
||||
f'<a id="{html.escape(anchor, quote=True)}"></a>\n'
|
||||
f"{prefix}{convert_inline(title, current_file, base_depth)}"
|
||||
)
|
||||
|
||||
|
||||
def is_include_fence(line: str) -> tuple[bool, str]:
|
||||
match = FENCE.match(line)
|
||||
if match is None:
|
||||
return False, ""
|
||||
|
||||
info = match.group(2).strip()
|
||||
return info.startswith(("{=include=}", "include")), match.group(1)
|
||||
|
||||
|
||||
def convert_markdown(
|
||||
text: str,
|
||||
source: Path,
|
||||
current_file: Path,
|
||||
base_depth: int,
|
||||
) -> str:
|
||||
output: list[str] = []
|
||||
in_code_fence = False
|
||||
code_fence = ""
|
||||
in_include = False
|
||||
include_fence = ""
|
||||
in_admonition = False
|
||||
|
||||
for raw_line in text.splitlines(keepends=True):
|
||||
line = raw_line[:-1] if raw_line.endswith("\n") else raw_line
|
||||
newline = "\n" if raw_line.endswith("\n") else ""
|
||||
|
||||
if in_include:
|
||||
if FENCE_CLOSE.match(line) and len(line.strip()) >= len(include_fence):
|
||||
in_include = False
|
||||
include_fence = ""
|
||||
continue
|
||||
|
||||
if in_code_fence:
|
||||
output.append(raw_line)
|
||||
if FENCE_CLOSE.match(line) and len(line.strip()) >= len(code_fence):
|
||||
in_code_fence = False
|
||||
code_fence = ""
|
||||
continue
|
||||
|
||||
include, fence = is_include_fence(line)
|
||||
if include:
|
||||
in_include = True
|
||||
include_fence = fence
|
||||
continue
|
||||
|
||||
fence_match = FENCE.match(line)
|
||||
if fence_match is not None:
|
||||
in_code_fence = True
|
||||
code_fence = fence_match.group(1)
|
||||
output.append(raw_line)
|
||||
continue
|
||||
|
||||
if in_admonition:
|
||||
if ADMONITION_CLOSE.match(line):
|
||||
in_admonition = False
|
||||
continue
|
||||
converted = convert_inline(line, current_file, base_depth)
|
||||
output.append(f"> {converted}{newline}" if converted else ">\n")
|
||||
continue
|
||||
|
||||
admonition = ADMONITION_OPEN.match(line)
|
||||
if admonition is not None:
|
||||
in_admonition = True
|
||||
output.append(f"> **{admonition.group(1).title()}**\n")
|
||||
continue
|
||||
|
||||
output.append(convert_heading(line, current_file, base_depth) + newline)
|
||||
|
||||
if in_include:
|
||||
raise ValueError(f"{source}: unterminated include block")
|
||||
|
||||
converted = "".join(output)
|
||||
if LEFTOVER_ROLE.search(converted):
|
||||
raise ValueError(f"{source}: unconverted NixOS-render-docs role remains")
|
||||
if "```{=include=}" in converted:
|
||||
raise ValueError(f"{source}: unconverted include block remains")
|
||||
|
||||
return converted
|
||||
|
||||
|
||||
def convert_tree(source: Path, destination: Path, base_depth: int) -> None:
|
||||
for path in source.rglob("*"):
|
||||
if not path.is_file():
|
||||
continue
|
||||
|
||||
target = destination / path.relative_to(source)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if path.suffix == ".md":
|
||||
text = path.read_text(encoding="utf-8")
|
||||
target.write_text(
|
||||
convert_markdown(text, path, path.relative_to(source), base_depth),
|
||||
encoding="utf-8",
|
||||
)
|
||||
else:
|
||||
shutil.copy2(path, target)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--base-depth", type=int, default=0)
|
||||
parser.add_argument("source", type=Path)
|
||||
parser.add_argument("destination", type=Path)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.source.is_dir():
|
||||
print(f"missing source directory: {args.source}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
args.destination.mkdir(parents=True, exist_ok=True)
|
||||
convert_tree(args.source, args.destination, args.base_depth)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
pkgs,
|
||||
optionDocs,
|
||||
manpageUrls,
|
||||
revision,
|
||||
}:
|
||||
|
||||
pkgs.runCommand "home-manager-mdbook-options"
|
||||
{
|
||||
nativeBuildInputs = [
|
||||
pkgs.buildPackages.nixos-render-docs
|
||||
pkgs.buildPackages.python3
|
||||
];
|
||||
optionDocsJson = builtins.toJSON optionDocs;
|
||||
passAsFile = [ "optionDocsJson" ];
|
||||
}
|
||||
''
|
||||
python3 ${./render-options.py} \
|
||||
"$optionDocsJsonPath" \
|
||||
${manpageUrls} \
|
||||
${revision} \
|
||||
"$out"
|
||||
''
|
||||
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
OPTION_LINK = re.compile(
|
||||
r"\[(?P<label>[^\]]*)\]\(#(?P<anchor>(?:opt|nixos-opt|nix-darwin-opt)-[^)]+)\)"
|
||||
)
|
||||
OPTION_HREF = re.compile(r'href="#(?P<anchor>(?:opt|nixos-opt|nix-darwin-opt)-[^"]+)"')
|
||||
DEEP_SPLIT_NAMESPACES = {"programs", "services"}
|
||||
|
||||
|
||||
def option_label(anchor: str) -> str:
|
||||
if anchor.startswith("nix-darwin-opt-"):
|
||||
return anchor.removeprefix("nix-darwin-opt-")
|
||||
if anchor.startswith("nixos-opt-"):
|
||||
return anchor.removeprefix("nixos-opt-")
|
||||
return anchor.removeprefix("opt-")
|
||||
|
||||
|
||||
def option_target(anchor: str, current_file: Path) -> str:
|
||||
if anchor.startswith("nix-darwin-opt-"):
|
||||
option = anchor.removeprefix("nix-darwin-opt-")
|
||||
base = "options/nix-darwin"
|
||||
elif anchor.startswith("nixos-opt-"):
|
||||
option = anchor.removeprefix("nixos-opt-")
|
||||
base = "options/nixos"
|
||||
else:
|
||||
option = anchor.removeprefix("opt-")
|
||||
base = "options/home-manager"
|
||||
|
||||
page_parts = option_page_parts(option)
|
||||
prefix = "../" * len(current_file.parent.parts)
|
||||
return f"{prefix}{base}/{'/'.join(page_parts)}.md#{anchor}"
|
||||
|
||||
|
||||
def rewrite_option_links(text: str, current_file: Path) -> str:
|
||||
text = OPTION_LINK.sub(
|
||||
lambda match: (
|
||||
f"[{match.group('label') or option_label(match.group('anchor'))}]"
|
||||
f"({option_target(match.group('anchor'), current_file)})"
|
||||
),
|
||||
text,
|
||||
)
|
||||
return OPTION_HREF.sub(
|
||||
lambda match: f'href="{option_target(match.group("anchor"), current_file)}"',
|
||||
text,
|
||||
)
|
||||
|
||||
|
||||
def namespace_for(option_name: str) -> str:
|
||||
return option_name.split(".", 1)[0]
|
||||
|
||||
|
||||
def option_page_parts(option_name: str) -> list[str]:
|
||||
parts = option_name.split(".")
|
||||
namespace = parts[0]
|
||||
if namespace in DEEP_SPLIT_NAMESPACES and len(parts) > 1:
|
||||
return parts[:2]
|
||||
return [namespace]
|
||||
|
||||
|
||||
def option_group_for(option_name: str) -> str:
|
||||
return "/".join(option_page_parts(option_name))
|
||||
|
||||
|
||||
def render_namespace(
|
||||
options: dict[str, object],
|
||||
destination: Path,
|
||||
manpage_urls: Path,
|
||||
revision: str,
|
||||
anchor_prefix: str,
|
||||
current_file: Path,
|
||||
) -> None:
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
source = destination.with_suffix(".json")
|
||||
source.write_text(json.dumps(options, sort_keys=True), encoding="utf-8")
|
||||
|
||||
subprocess.run(
|
||||
[
|
||||
"nixos-render-docs",
|
||||
"options",
|
||||
"commonmark",
|
||||
"--manpage-urls",
|
||||
str(manpage_urls),
|
||||
"--revision",
|
||||
revision,
|
||||
"--anchor-style",
|
||||
"legacy",
|
||||
"--anchor-prefix",
|
||||
anchor_prefix,
|
||||
str(source),
|
||||
str(destination),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
source.unlink()
|
||||
destination.write_text(
|
||||
rewrite_option_links(destination.read_text(encoding="utf-8"), current_file),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def write_doc(
|
||||
name: str,
|
||||
doc: dict[str, str],
|
||||
manpage_urls: Path,
|
||||
revision: str,
|
||||
output: Path,
|
||||
) -> None:
|
||||
options_json = Path(doc["json"]) / "share/doc/nixos/options.json"
|
||||
options = json.loads(options_json.read_text(encoding="utf-8"))
|
||||
|
||||
grouped: dict[str, dict[str, object]] = {}
|
||||
for option_name, option in options.items():
|
||||
group = option_group_for(option_name)
|
||||
grouped.setdefault(group, {})[option_name] = option
|
||||
|
||||
doc_dir = output / "options" / doc["path"]
|
||||
doc_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
groups = sorted(grouped)
|
||||
namespaces = sorted({group.split("/", 1)[0] for group in groups})
|
||||
nested = {
|
||||
namespace: sorted(
|
||||
group for group in groups if group.startswith(f"{namespace}/")
|
||||
)
|
||||
for namespace in namespaces
|
||||
}
|
||||
index = [
|
||||
f"# {doc['title']}\n",
|
||||
"\n",
|
||||
"Generated from Home Manager option definitions.\n",
|
||||
"\n",
|
||||
"## Namespaces\n",
|
||||
"\n",
|
||||
]
|
||||
for namespace in namespaces:
|
||||
if nested[namespace]:
|
||||
index.append(f"- [{namespace}]({namespace}/index.md)\n")
|
||||
else:
|
||||
index.append(f"- [{namespace}]({namespace}.md)\n")
|
||||
(doc_dir / "index.md").write_text("".join(index), encoding="utf-8")
|
||||
|
||||
summary_dir = output / "summary"
|
||||
summary_dir.mkdir(parents=True, exist_ok=True)
|
||||
summary = [f" - [{doc['title']}](options/{doc['path']}/index.md)\n"]
|
||||
|
||||
for namespace in namespaces:
|
||||
if nested[namespace]:
|
||||
namespace_index = doc_dir / namespace / "index.md"
|
||||
namespace_index.parent.mkdir(parents=True, exist_ok=True)
|
||||
namespace_index.write_text(
|
||||
"".join(
|
||||
[
|
||||
f"# {namespace}\n",
|
||||
"\n",
|
||||
"## Modules\n",
|
||||
"\n",
|
||||
*(
|
||||
f"- [{group.split('/', 1)[1]}]({group.split('/', 1)[1]}.md)\n"
|
||||
for group in nested[namespace]
|
||||
),
|
||||
]
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
summary.append(
|
||||
f" - [{namespace}](options/{doc['path']}/{namespace}/index.md)\n"
|
||||
)
|
||||
for group in nested[namespace]:
|
||||
module_name = group.split("/", 1)[1]
|
||||
summary.append(
|
||||
f" - [{module_name}](options/{doc['path']}/{namespace}/{module_name}.md)\n"
|
||||
)
|
||||
else:
|
||||
summary.append(
|
||||
f" - [{namespace}](options/{doc['path']}/{namespace}.md)\n"
|
||||
)
|
||||
|
||||
for group in groups:
|
||||
page = doc_dir / f"{group}.md"
|
||||
current_file = Path("options") / doc["path"] / f"{group}.md"
|
||||
render_namespace(
|
||||
grouped[group],
|
||||
page,
|
||||
manpage_urls,
|
||||
revision,
|
||||
doc["prefix"],
|
||||
current_file,
|
||||
)
|
||||
|
||||
(summary_dir / f"{name}.md").write_text("".join(summary), encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) != 5:
|
||||
print(
|
||||
"usage: render-options.py OPTION_DOCS MANPAGE_URLS REVISION OUT",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
option_docs = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
manpage_urls = Path(sys.argv[2])
|
||||
revision = sys.argv[3]
|
||||
output = Path(sys.argv[4])
|
||||
|
||||
for name, doc in option_docs.items():
|
||||
write_doc(name, doc, manpage_urls, revision, output)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) != 5:
|
||||
print(
|
||||
"usage: substitute-summary.py SUMMARY HM_SUMMARY NIXOS_SUMMARY DARWIN_SUMMARY",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
summary = Path(sys.argv[1])
|
||||
replacements = {
|
||||
"@HOME_MANAGER_OPTIONS@": Path(sys.argv[2]).read_text(encoding="utf-8"),
|
||||
"@NIXOS_OPTIONS@": Path(sys.argv[3]).read_text(encoding="utf-8"),
|
||||
"@NIX_DARWIN_OPTIONS@": Path(sys.argv[4]).read_text(encoding="utf-8"),
|
||||
}
|
||||
|
||||
text = summary.read_text(encoding="utf-8")
|
||||
for needle, replacement in replacements.items():
|
||||
text = text.replace(needle, replacement.rstrip())
|
||||
|
||||
summary.write_text(text, encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,9 +1,10 @@
|
||||
# Release Notes {#ch-release-notes}
|
||||
|
||||
This section lists the release notes for stable versions of Home Manager
|
||||
and the current unstable version.
|
||||
This section lists the release notes for stable versions of Home Manager and the
|
||||
current unstable version.
|
||||
|
||||
```{=include=} chapters
|
||||
rl-2611.md
|
||||
rl-2605.md
|
||||
rl-2511.md
|
||||
rl-2505.md
|
||||
|
||||
+154
-36
@@ -6,41 +6,141 @@ The 26.05 release branch became stable in May, 2026.
|
||||
|
||||
This release has the following notable changes:
|
||||
|
||||
- WezTerm now supports declarative configuration via
|
||||
[](#opt-programs.wezterm.settings). Settings are expressed as a Nix
|
||||
attribute set and serialized to Lua using `lib.generators.toLua`.
|
||||
Raw Lua expressions such as `wezterm.font` and `wezterm.action.*`
|
||||
can be embedded using `lib.generators.mkLuaInline`. The existing
|
||||
[](#opt-programs.wezterm.extraConfig) option remains fully supported
|
||||
and can be combined with [](#opt-programs.wezterm.settings).
|
||||
- Home Manager now supports RFC 42-style SSH configuration through
|
||||
[](#opt-programs.ssh.settings). The legacy `programs.ssh.matchBlocks` format
|
||||
is deprecated and migrated automatically.
|
||||
|
||||
- The [](#opt-programs.anki.uiScale) option now expects a value in the
|
||||
range 1.0–2.0, previously it erroneously expected values in the
|
||||
range `0.0–1.0`.
|
||||
- The new `home-manager.startAsUserService` option runs user activation on
|
||||
demand at login instead of doing all setup during system boot, which helps on
|
||||
systems where home directories are mounted later (for example, pam_mount).
|
||||
|
||||
- New options were added for `services.podman.useDefaultMachine` and podman
|
||||
machine management on Darwin.
|
||||
|
||||
- The [](#opt-programs.rclone.enable) module now supports launchd-backed config,
|
||||
mount, and serve agents on Darwin. The new
|
||||
`programs.rclone.remotes.<name>.serve` option can manage `rclone serve`
|
||||
sidecar services on both Linux and Darwin. On non-systemd platforms,
|
||||
`programs.rclone.requiresUnit` is read-only and always `null` because there is
|
||||
no systemd ordering unit to depend on.
|
||||
|
||||
- The Syncthing credentials flow now uses `services.syncthing.guiCredentials`
|
||||
instead of the removed `services.syncthing.passwordFile` option.
|
||||
|
||||
- Firefox extensions are now managed per-profile through the
|
||||
`programs.firefox.profiles.<name>.extensions.packages` and
|
||||
`programs.firefox.profiles.<name>.extensions.settings` options. The top-level
|
||||
`programs.firefox.extensions = [ ... ]` list was removed; migrate it to
|
||||
`programs.firefox.profiles.<name>.extensions.packages`.
|
||||
|
||||
- Thunderbird now supports `accounts.email.accounts.<name>.ews`, the
|
||||
`outlook.office365.com-ews` account flavor, declarative language packs, and
|
||||
enterprise policies. Thunderbird accounts using the `outlook.office365.com`
|
||||
flavor now default IMAP and SMTP authentication to OAuth2 when the account
|
||||
does not set an authentication method explicitly.
|
||||
|
||||
- A new `systemd.user.packages` option provides a user-level equivalent of
|
||||
`systemd.packages` for installing unit files from packages.
|
||||
|
||||
- WezTerm now supports declarative configuration via
|
||||
[](#opt-programs.wezterm.settings). Settings are expressed as a Nix attribute
|
||||
set and serialized to Lua using `lib.generators.toLua`. Raw Lua expressions
|
||||
such as `wezterm.font` and `wezterm.action.*` can be embedded using
|
||||
`lib.generators.mkLuaInline`. The existing
|
||||
[](#opt-programs.wezterm.extraConfig) option remains fully supported and can
|
||||
be combined with [](#opt-programs.wezterm.settings).
|
||||
|
||||
- The [](#opt-programs.anki.uiScale) option now expects a value in the range
|
||||
1.0–2.0, previously it erroneously expected values in the range `0.0–1.0`.
|
||||
Anki sync options moved from `programs.anki.sync.*` to
|
||||
`programs.anki.profiles."User 1".sync.*`, and
|
||||
`programs.anki.sync.passwordFile` is migrated to
|
||||
`programs.anki.profiles."User 1".sync.keyFile`.
|
||||
|
||||
- New [](#opt-home.services) namespace for nixpkgs
|
||||
[modular services](https://nixos.org/manual/nixos/unstable/#modular-services).
|
||||
Service modules shipped with packages (e.g.
|
||||
`pkgs.<name>.passthru.services.default`) drop in unchanged and are
|
||||
lifted to user systemd units. See
|
||||
[Modular Services](#sec-usage-modular-services) for details.
|
||||
`pkgs.<name>.passthru.services.default`) drop in unchanged and are lifted to
|
||||
user systemd units. See [Modular Services](#sec-usage-modular-services) for
|
||||
details.
|
||||
|
||||
- Dedicated modules were added for several VSCode forks (`programs.cursor`,
|
||||
`programs.vscodium`, `programs.windsurf`, `programs.kiro`, and
|
||||
`programs.antigravity`), built from a shared `mkVscodeModule` factory. Each
|
||||
fork can now be configured independently and simultaneously. Users who
|
||||
previously selected a fork by setting `programs.vscode.package` should migrate
|
||||
to the matching module; the `programs.vscode.pname` option was removed.
|
||||
|
||||
- The AI coding-assistant modules ([](#opt-programs.claude-code.enable),
|
||||
[](#opt-programs.codex.enable), and [](#opt-programs.opencode.enable)) now
|
||||
share a unified `context` option for providing global instructions. The
|
||||
previous per-module options (`claude-code.memory`,
|
||||
`codex.custom-instructions`, and `opencode.rules`) are deprecated and migrated
|
||||
automatically. MCP server configuration via `programs.mcp.servers` was also
|
||||
extended to more assistant and editor modules through an
|
||||
`enableMcpIntegration` option.
|
||||
|
||||
- The `programs.man` module was split so the man viewer can be selected through
|
||||
the [](#opt-programs.man.man-db.enable) and
|
||||
[](#opt-programs.man.mandoc.enable) submodules, which are mutually exclusive.
|
||||
|
||||
- The `programs.neovim.extraLuaConfig` option was renamed to
|
||||
[](#opt-programs.neovim.initLua). Home Manager now writes generated Lua
|
||||
initialization to {file}`$XDG_CONFIG_HOME/nvim/init.lua` by default. If you
|
||||
manage that file outside Home Manager, set
|
||||
[](#opt-programs.neovim.sideloadInitLua) to `true` to load the generated Lua
|
||||
through the Neovim wrapper instead.
|
||||
|
||||
- A new `sshAuthSock` module manages the `SSH_AUTH_SOCK`
|
||||
environment variable. It is implicitly configured by the
|
||||
SSH-agent-providing modules (e.g. `services.ssh-agent`, `services.gpg-agent`).
|
||||
The old `services.ssh-agent.enable{Bash,Zsh,Fish,Nushell}Integration` options
|
||||
were removed; shell initialization is now provided through `sshAuthSock`.
|
||||
|
||||
- A new [](#opt-services.pipewire.enable) module provides client-side
|
||||
configuration for PipeWire, WirePlumber, and the PulseAudio/JACK compatibility
|
||||
layers. It does not install or manage the PipeWire daemon itself.
|
||||
|
||||
- The `services.swww` module was renamed to [](#opt-services.awww.enable)
|
||||
following the upstream project rename. The `enable`, `package`, and
|
||||
`extraArgs` options are migrated automatically from `services.swww.*` to
|
||||
`services.awww.*`.
|
||||
|
||||
- Several modules dropped their freeform `extraConfig` escape hatches in favor
|
||||
of RFC 42-style `settings`: `programs.aerospace.extraConfig` (and
|
||||
`userSettings`) and `programs.aria2.extraConfig` were removed.
|
||||
`programs.mise.settings` was renamed to `programs.mise.globalConfig.settings`.
|
||||
The `programs.eww.configDir` and
|
||||
`programs.eww.enable{Bash,Zsh,Fish}Integration` options were removed; use
|
||||
[](#opt-programs.eww.yuckConfig) and [](#opt-programs.eww.scssConfig) for
|
||||
declarative Eww config files. The `programs.niriswitcher` options were also
|
||||
removed.
|
||||
|
||||
- Darwin integration received several behavior changes. Home Manager launchd
|
||||
agents now wait for `/nix/store` before starting and use
|
||||
`launchctl bootout --wait` when replacing agents. nix-darwin activations now
|
||||
propagate dry-run mode into Home Manager user activations. On Darwin, Home
|
||||
Manager also exports `TERMINFO_DIRS` so terminfo entries from Home
|
||||
Manager-installed packages are visible to shells.
|
||||
|
||||
- The [](#opt-nix.assumeXdg) option was added for installations where Nix is
|
||||
configured to use XDG base directories outside Home Manager. Home Manager also
|
||||
detects `osConfig.nix.settings.use-xdg-base-directories` when deciding where
|
||||
to place declarative channel entries.
|
||||
|
||||
## State Version Changes {#sec-release-26.05-state-version-changes}
|
||||
|
||||
The state version in this release includes the changes below. These
|
||||
changes are only active if the `home.stateVersion` option is set to
|
||||
\"26.05\" or later.
|
||||
The state version in this release includes the changes below. These changes are
|
||||
only active if the `home.stateVersion` option is set to \"26.05\" or later.
|
||||
|
||||
- The [](#opt-gtk.gtk4.theme) option does not mirror
|
||||
[](#opt-gtk.theme) by default anymore.
|
||||
- The [](#opt-gtk.gtk4.theme) option does not mirror [](#opt-gtk.theme) by
|
||||
default anymore.
|
||||
|
||||
- The [](#opt-programs.zsh.dotDir) option now defaults to the XDG
|
||||
configuration directory (usually `~/.config/zsh`) when
|
||||
[](#opt-xdg.enable) is true.
|
||||
- The [](#opt-programs.zsh.dotDir) option now defaults to the XDG configuration
|
||||
directory (usually `~/.config/zsh`) when [](#opt-xdg.enable) is true.
|
||||
|
||||
- The [](#opt-programs.yazi.shellWrapperName) option now defaults to
|
||||
`y` instead of `yy`. For users with older `home.stateVersion` values,
|
||||
the legacy default `yy` is retained.
|
||||
- The [](#opt-programs.yazi.shellWrapperName) option now defaults to `y` instead
|
||||
of `yy`.
|
||||
|
||||
- The [](#opt-xdg.userDirs.setSessionVariables) option now defaults to `false`
|
||||
instead of `true`.
|
||||
@@ -51,21 +151,39 @@ changes are only active if the `home.stateVersion` option is set to
|
||||
`DESKTOP`. Home Manager 26.05 introduced a warning when the `XDG_<name>_DIR`
|
||||
form is used.
|
||||
|
||||
- The [](#opt-programs.man.package) option now defaults to `null` on
|
||||
Darwin because the GNU `man` from nixpkgs ships `apropos`/`man -k`
|
||||
and `whatis`/`man -f` binaries that don't work on Darwin. Nix-installed
|
||||
manual pages still work with macOS's built-in `man` via
|
||||
- The [](#opt-programs.man.package) option now defaults to `null` on Darwin
|
||||
because the GNU `man` from nixpkgs ships `apropos`/`man -k` and
|
||||
`whatis`/`man -f` binaries that don't work on Darwin. Nix-installed manual
|
||||
pages still work with macOS's built-in `man` via
|
||||
[](#opt-home.extraOutputsToInstall).
|
||||
|
||||
- The options [](#opt-programs.neovim.withPython3) and
|
||||
[](#opt-programs.neovim.withRuby) now default to `false` following nixpkgs
|
||||
[](#opt-programs.neovim.withRuby) now default to `false` following nixpkgs.
|
||||
|
||||
- Neovim plugin `config` fragments are assumed to be Lua by default through
|
||||
`programs.neovim.plugins.<name>.type = "lua"`, instead of the previous `viml`
|
||||
default.
|
||||
|
||||
- On Linux, `programs.firefox.configPath` now defaults to
|
||||
`"${config.xdg.configHome}/mozilla/firefox"` for `home.stateVersion = "26.05"` and later.
|
||||
Older state versions keep the legacy default of `".mozilla/firefox"`.
|
||||
`"${config.xdg.configHome}/mozilla/firefox"`, instead of the legacy
|
||||
`".mozilla/firefox"`.
|
||||
|
||||
- The [](#opt-wayland.windowManager.hyprland.configType) option now
|
||||
defaults to `"lua"` for `home.stateVersion = "26.05"` and later.
|
||||
Older state versions keep the legacy `"hyprlang"` default. Set
|
||||
`wayland.windowManager.hyprland.configType = "hyprlang"` to keep
|
||||
generating {file}`$XDG_CONFIG_HOME/hypr/hyprland.conf`.
|
||||
- The [](#opt-wayland.windowManager.hyprland.configType) option now defaults to
|
||||
`"lua"`, instead of the legacy `"hyprlang"`. Set
|
||||
`wayland.windowManager.hyprland.configType = "hyprlang"` to keep generating
|
||||
{file}`$XDG_CONFIG_HOME/hypr/hyprland.conf`.
|
||||
|
||||
- The [](#opt-programs.docker-cli.configDir) option now defaults to
|
||||
`"${config.xdg.configHome}/docker"` when [](#opt-xdg.enable) is true, instead
|
||||
of `~/.docker`. Similarly, [](#opt-services.colima.profiles) now sets
|
||||
`$DOCKER_HOST` for the default context.
|
||||
|
||||
- The [](#opt-services.home-manager.autoUpgrade.preSwitchCommands) option now
|
||||
defaults to an empty list. Previously, when
|
||||
[](#opt-services.home-manager.autoUpgrade.useFlake) was `true`, it defaulted
|
||||
to `[ "nix flake update" ]`. Set `preSwitchCommands = [ "nix flake update" ]`
|
||||
to keep updating flake inputs before each automatic switch.
|
||||
|
||||
- The [](#opt-programs.mergiraf.enableGitIntegration) and
|
||||
[](#opt-programs.mergiraf.enableJujutsuIntegration) options now default to
|
||||
`false`, so the integrations must be enabled explicitly.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Release 26.11 {#sec-release-26.11}
|
||||
|
||||
This is the current unstable branch and the information in this
|
||||
section is therefore not final.
|
||||
|
||||
## Highlights {#sec-release-26.11-highlights}
|
||||
|
||||
This release has the following notable changes:
|
||||
|
||||
- The [](#opt-programs.uv.enable) module can now install uv-managed Python
|
||||
versions and tools through the new `programs.uv.python.versions`,
|
||||
`programs.uv.python.default`, and `programs.uv.tool.packages` options. Unpinned
|
||||
entries track the latest release on each activation while pinned ones stay put,
|
||||
and `programs.uv.python.prune` / `programs.uv.tool.prune` make the managed set
|
||||
fully declarative by removing versions and tools that are no longer listed.
|
||||
|
||||
## State Version Changes {#sec-release-26.11-state-version-changes}
|
||||
|
||||
The state version in this release includes the changes below. These
|
||||
changes are only active if the `home.stateVersion` option is set to
|
||||
"26.11" or later.
|
||||
Generated
+4
-4
@@ -2,16 +2,16 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1779508470,
|
||||
"narHash": "sha256-Ap9KJX+5xHIn3bPIpfNgT6MEXdAECECwo4/rmlQD74M=",
|
||||
"lastModified": 1780365719,
|
||||
"narHash": "sha256-QfWfccTN+70ZQ4m2qlU9PiKfz2Yppq94058iJyARNwc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "29916453413845e54a65b8a1cf996842300cd299",
|
||||
"rev": "ffa10e26ae11d676b2db836259889f1f571cb14f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
description = "Home Manager for Nix";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
outputs =
|
||||
{
|
||||
@@ -45,9 +45,19 @@
|
||||
}
|
||||
// (
|
||||
let
|
||||
forAllPkgs =
|
||||
f:
|
||||
nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (system: f nixpkgs.legacyPackages.${system});
|
||||
supportedSystems = [
|
||||
"aarch64-darwin"
|
||||
"aarch64-linux"
|
||||
"i686-linux"
|
||||
"x86_64-darwin"
|
||||
"x86_64-linux"
|
||||
];
|
||||
|
||||
forSystems = systems: f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
|
||||
|
||||
forAllPkgs = forSystems nixpkgs.lib.systems.flakeExposed;
|
||||
|
||||
forSupportedPkgs = forSystems supportedSystems;
|
||||
|
||||
forCI = nixpkgs.lib.genAttrs [
|
||||
"aarch64-darwin"
|
||||
@@ -156,7 +166,7 @@
|
||||
lib.mapAttrs' renameTestPkg tests;
|
||||
in
|
||||
{
|
||||
formatter = forAllPkgs (pkgs: pkgs.callPackage ./home-manager/formatter.nix { });
|
||||
formatter = forSupportedPkgs (pkgs: pkgs.callPackage ./home-manager/formatter.nix { });
|
||||
|
||||
# TODO: increase buildbot testing scope
|
||||
buildbot = forCI (
|
||||
@@ -187,6 +197,8 @@
|
||||
{
|
||||
default = hmPkg;
|
||||
home-manager = hmPkg;
|
||||
}
|
||||
// nixpkgs.lib.optionalAttrs (nixpkgs.lib.elem pkgs.stdenv.hostPlatform.system supportedSystems) {
|
||||
|
||||
ci-parse = pkgs.callPackage ./ci/parse.nix { nix = pkgs.nixVersions.latest; };
|
||||
ci-parse-lix = pkgs.callPackage ./ci/parse.nix {
|
||||
@@ -212,11 +224,11 @@
|
||||
}
|
||||
);
|
||||
|
||||
devShells = forAllPkgs (pkgs: {
|
||||
devShells = forSupportedPkgs (pkgs: {
|
||||
default = pkgs.callPackage ./home-manager/devShell.nix { };
|
||||
});
|
||||
|
||||
legacyPackages = forAllPkgs (
|
||||
legacyPackages = forSupportedPkgs (
|
||||
pkgs:
|
||||
let
|
||||
inherit (pkgs.stdenv.hostPlatform) system;
|
||||
|
||||
@@ -18,6 +18,7 @@ pkgs.treefmt.withConfig {
|
||||
deadnix
|
||||
keep-sorted
|
||||
nixf-diagnose
|
||||
ruff
|
||||
];
|
||||
settings = pkgs.lib.importTOML ../treefmt.toml;
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ function doInit() {
|
||||
fi
|
||||
|
||||
local homeManagerUrl="github:nix-community/home-manager"
|
||||
local nixpkgsUrl="github:nixos/nixpkgs/nixos-unstable"
|
||||
local nixpkgsUrl="github:nixos/nixpkgs/nixpkgs-unstable"
|
||||
|
||||
while (( $# > 0 )); do
|
||||
local opt="$1"
|
||||
@@ -1279,7 +1279,7 @@ while [[ $# -gt 0 ]]; do
|
||||
export VERBOSE=1
|
||||
;;
|
||||
--version)
|
||||
echo 26.05-pre
|
||||
echo 26.11-pre
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR Home Manager contributors
|
||||
# This file is distributed under the same license as the Home Manager package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Home Manager\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0100\n"
|
||||
"PO-Revision-Date: 2026-05-31 21:01+0000\n"
|
||||
"Last-Translator: Tofaa <tofig.adigozalov.180904@gmail.com>\n"
|
||||
"Language-Team: Azerbaijani <https://hosted.weblate.org/projects/home-manager/"
|
||||
"cli/az/>\n"
|
||||
"Language: az\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 2026.6.dev0\n"
|
||||
|
||||
#. translators: For example: "home-manager: missing argument for --cores"
|
||||
#: home-manager/home-manager:16
|
||||
msgid "%s: missing argument for %s"
|
||||
msgstr "%s: {s} üçün tələb olunan arqument verilməyib"
|
||||
|
||||
#. translators: For example: "home-manager: --rollback can only be used after switch"
|
||||
#: home-manager/home-manager:22
|
||||
msgid "%s: %s can only be used after %s"
|
||||
msgstr "%s: %s ancaq %s'dən sonra işlənə bilər"
|
||||
|
||||
#: home-manager/home-manager:71
|
||||
msgid "No configuration file found at %s"
|
||||
msgstr "%s-də konfiqurasiya faylı tapılmadı"
|
||||
|
||||
#. translators: The first '%s' specifier will be replaced by either
|
||||
#. 'home.nix' or 'flake.nix'.
|
||||
#: home-manager/home-manager:88 home-manager/home-manager:92
|
||||
#: home-manager/home-manager:191
|
||||
msgid ""
|
||||
"Keeping your Home Manager %s in %s is deprecated,\n"
|
||||
"please move it to %s"
|
||||
msgstr ""
|
||||
"Home Manager %s faylının %s daxilində saxlanılması artıq dəstəklənmir.\n"
|
||||
"Zəhmət olmasa onu %s ünvanına köçürün"
|
||||
|
||||
#: home-manager/home-manager:99
|
||||
msgid "No configuration file found. Please create one at %s"
|
||||
msgstr "Konfiqurasiya faylı tapılmadı. Zəhmət olmasa fayli %s'də yaradin"
|
||||
|
||||
#: home-manager/home-manager:114
|
||||
msgid "Home Manager not found at %s."
|
||||
msgstr "Home Manager %s'də tapılmadı."
|
||||
|
||||
#. translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated.
|
||||
#: home-manager/home-manager:122
|
||||
msgid ""
|
||||
"The fallback Home Manager path %s has been deprecated and a file/directory "
|
||||
"was found there."
|
||||
msgstr ""
|
||||
"Ehtiyat Home Manager yolu %s artıq köhnəlmişdir və həmin ünvanda fayl və ya "
|
||||
"qovluq tapıldı."
|
||||
|
||||
#. translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated.
|
||||
#: home-manager/home-manager:125
|
||||
msgid ""
|
||||
"To remove this warning, do one of the following.\n"
|
||||
"\n"
|
||||
"1. Explicitly tell Home Manager to use the path, for example by adding\n"
|
||||
"\n"
|
||||
" { programs.home-manager.path = \"%s\"; }\n"
|
||||
"\n"
|
||||
" to your configuration.\n"
|
||||
"\n"
|
||||
" If you import Home Manager directly, you can use the `path` parameter\n"
|
||||
"\n"
|
||||
" pkgs.callPackage /path/to/home-manager-package { path = \"%s\"; }\n"
|
||||
"\n"
|
||||
" when calling the Home Manager package.\n"
|
||||
"\n"
|
||||
"2. Remove the deprecated path.\n"
|
||||
"\n"
|
||||
" $ rm -r \"%s\""
|
||||
msgstr ""
|
||||
"Bu xəbərdarlığı aradan qaldırmaq üçün aşağıdakılardan birini edin.\n"
|
||||
"\n"
|
||||
"1. Home Manager-ə bu yolu istifadə etməsini açıq şəkildə bildirin. Məsələn, "
|
||||
"konfiqurasiyanıza aşağıdakı sətri əlavə edin:\n"
|
||||
"\n"
|
||||
"{ programs.home-manager.path = \"%s\"; }\n"
|
||||
"\n"
|
||||
" Əgər Home Manager-i birbaşa import edirsinizsə, Home Manager paketini\n"
|
||||
" çağırarkən `path` parametrindən istifadə edə bilərsiniz:\n"
|
||||
"\n"
|
||||
" pkgs.callPackage /path/to/home-manager-package { path = \"%s\"; }\n"
|
||||
"\n"
|
||||
"2. Köhnəlmiş yolu silin.\n"
|
||||
"\n"
|
||||
" $ rm -r \"%s\""
|
||||
|
||||
#: home-manager/home-manager:153
|
||||
msgid "Sanity checking Nix"
|
||||
msgstr "Nix yoxlanılır"
|
||||
|
||||
#: home-manager/home-manager:173
|
||||
msgid "Could not find suitable profile directory, tried %s and %s"
|
||||
msgstr "Uyğun profil qovluğu tapılmadı; %s və %s yolları yoxlanıldı"
|
||||
|
||||
#. translators: Here "flake" is a noun that refers to the Nix Flakes feature.
|
||||
#: home-manager/home-manager:230
|
||||
msgid "Can't inspect options of a flake configuration"
|
||||
msgstr "Flake konfiqurasiyasının seçimlərinə baxış keçirmək mümkün deyil"
|
||||
|
||||
#: home-manager/home-manager:340 home-manager/home-manager:363
|
||||
#: home-manager/home-manager:769 home-manager/home-manager:1289
|
||||
msgid "%s: unknown option '%s'"
|
||||
msgstr "%s: bilinməyən seçim '%s'"
|
||||
|
||||
#: home-manager/home-manager:345 home-manager/home-manager:1290
|
||||
msgid "Run '%s --help' for usage help"
|
||||
msgstr "İstifadə köməyi üçün '%s --help' işlədin"
|
||||
|
||||
#: home-manager/home-manager:371 home-manager/home-manager:476
|
||||
msgid "The file %s already exists, leaving it unchanged..."
|
||||
msgstr "%s faylı artıq mövcuddur, dəyişdirilmədən saxlanılır..."
|
||||
|
||||
#: home-manager/home-manager:373 home-manager/home-manager:478
|
||||
msgid "Creating %s..."
|
||||
msgstr "%s yaradılır..."
|
||||
|
||||
#: home-manager/home-manager:522
|
||||
msgid "Creating initial Home Manager generation..."
|
||||
msgstr "Ilkin Home Manager nəsili yaradılır..."
|
||||
|
||||
#. translators: The "%s" specifier will be replaced by a file path.
|
||||
#: home-manager/home-manager:527
|
||||
msgid ""
|
||||
"All done! The home-manager tool should now be installed and you can edit\n"
|
||||
"\n"
|
||||
" %s\n"
|
||||
"\n"
|
||||
"to configure Home Manager. Run 'man home-configuration.nix' to\n"
|
||||
"see all available options."
|
||||
msgstr ""
|
||||
"Hər şey tamamlandı! İndi home-manager aləti quraşdırılıb və Home Manager-i "
|
||||
"konfiqurasiya etmək üçün %s faylını redaktə edə bilərsiniz.\n"
|
||||
"\n"
|
||||
"Mövcud bütün seçimləri görmək üçün 'man home-configuration.nix' əmrini icra "
|
||||
"edin."
|
||||
|
||||
#. translators: The "%s" specifier will be replaced by a URL.
|
||||
#: home-manager/home-manager:532
|
||||
msgid ""
|
||||
"Uh oh, the installation failed! Please create an issue at\n"
|
||||
"\n"
|
||||
" %s\n"
|
||||
"\n"
|
||||
"if the error seems to be the fault of Home Manager."
|
||||
msgstr ""
|
||||
"Quraşdırma uğursuz oldu! Əgər xəta Home Manager-in səbəbindən baş veribsə, "
|
||||
"%s ünvanında issue yaradın."
|
||||
|
||||
#. translators: Here "flake" is a noun that refers to the Nix Flakes feature.
|
||||
#: home-manager/home-manager:543
|
||||
msgid "Can't instantiate a flake configuration"
|
||||
msgstr "Flake konfiqurasiyasını yaratmaq mümkün deyil"
|
||||
|
||||
#: home-manager/home-manager:619
|
||||
msgid ""
|
||||
"There is %d unread and relevant news item.\n"
|
||||
"Read it by running the command \"%s news\"."
|
||||
msgid_plural ""
|
||||
"There are %d unread and relevant news items.\n"
|
||||
"Read them by running the command \"%s news\"."
|
||||
msgstr[0] ""
|
||||
"Oxunmamış və uyğun %d xəbər var.\n"
|
||||
"\n"
|
||||
"Onu oxumaq üçün \"%s news\" əmrini icra edin."
|
||||
msgstr[1] ""
|
||||
"Oxunmamış və uyğun %d xəbər var.\n"
|
||||
"\n"
|
||||
"Onları oxumaq üçün \"%s news\" əmrini icra edin."
|
||||
|
||||
#: home-manager/home-manager:633
|
||||
msgid "Unknown \"news.display\" setting \"%s\"."
|
||||
msgstr "Naməlum \"news.display\" ayarı: \"%s\"."
|
||||
|
||||
#: home-manager/home-manager:641
|
||||
#, sh-format
|
||||
msgid "Please set the $EDITOR or $VISUAL environment variable"
|
||||
msgstr "Zəhmət olmasa $EDITOR və ya $VISUAL mühit dəyişənini təyin edin"
|
||||
|
||||
#: home-manager/home-manager:659
|
||||
msgid "Cannot run build in read-only directory"
|
||||
msgstr "Yalnız oxuma rejimində qovluqda build icra etmək mümkün deyil"
|
||||
|
||||
#: home-manager/home-manager:822
|
||||
msgid "The configuration does not contain the specialisation \"%s\""
|
||||
msgstr "Konfiqurasiya \"%s\" specialisation-ını ehtiva etmir"
|
||||
|
||||
#: home-manager/home-manager:876
|
||||
msgid "No generation with ID %s"
|
||||
msgstr "%s ID-li nəsil mövcud deyil"
|
||||
|
||||
#: home-manager/home-manager:878
|
||||
msgid "Cannot remove the current generation %s"
|
||||
msgstr "Cari nəsli (%s) silmək mümkün deyil"
|
||||
|
||||
#: home-manager/home-manager:880
|
||||
msgid "Removing generation %s"
|
||||
msgstr "%s nəsli silinir"
|
||||
|
||||
#: home-manager/home-manager:901
|
||||
msgid "No generations to expire"
|
||||
msgstr "Müddəti bitəcək heç bir nəsil yoxdur"
|
||||
|
||||
#: home-manager/home-manager:912
|
||||
msgid "No home-manager packages seem to be installed."
|
||||
msgstr "Heç bir home-manager paketi quraşdırılmış görünmür."
|
||||
|
||||
#: home-manager/home-manager:997
|
||||
msgid "Unknown argument %s"
|
||||
msgstr "Naməlum arqument: %s"
|
||||
|
||||
#: home-manager/home-manager:1022
|
||||
msgid "This will remove Home Manager from your system."
|
||||
msgstr "Bu, Home Manager-i sisteminizdən siləcək."
|
||||
|
||||
#: home-manager/home-manager:1025
|
||||
msgid "This is a dry run, nothing will actually be uninstalled."
|
||||
msgstr "Bu sınaq icrasıdır, heç nə faktiki olaraq silinməyəcək."
|
||||
|
||||
#: home-manager/home-manager:1029
|
||||
msgid "Really uninstall Home Manager?"
|
||||
msgstr "Home Manager-i həqiqətən silmək istəyirsiniz?"
|
||||
|
||||
#: home-manager/home-manager:1035
|
||||
msgid "Switching to empty Home Manager configuration..."
|
||||
msgstr "Boş Home Manager konfiqurasiyasına keçid edilir..."
|
||||
|
||||
#: home-manager/home-manager:1050
|
||||
msgid "Yay!"
|
||||
msgstr "Əla!"
|
||||
|
||||
#: home-manager/home-manager:1055
|
||||
msgid "Home Manager is uninstalled but your home.nix is left untouched."
|
||||
msgstr "Home Manager silindi, lakin home.nix faylınız dəyişdirilmədi."
|
||||
|
||||
#: home-manager/home-manager:1334
|
||||
msgid "expire-generations expects one argument, got %d."
|
||||
msgstr "expire-generations bir arqument gözləyir, lakin %d arqument verilib."
|
||||
|
||||
#: home-manager/home-manager:1359
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Naməlum əmr: %s"
|
||||
|
||||
#: home-manager/install.nix:21
|
||||
msgid "This derivation is not buildable, please run it using nix-shell."
|
||||
msgstr ""
|
||||
"Bu derivasiya build edilə bilən deyil, zəhmət olmasa onu nix-shell ilə icra "
|
||||
"edin."
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: Home Manager\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0100\n"
|
||||
"PO-Revision-Date: 2026-03-31 16:16+0000\n"
|
||||
"Last-Translator: Lorenzo Bevilacqua <lorenzobevilacqua02@gmail.com>\n"
|
||||
"PO-Revision-Date: 2026-06-12 10:01+0000\n"
|
||||
"Last-Translator: Tommaso Soncin <soncintommaso@gmail.com>\n"
|
||||
"Language-Team: Italian <https://hosted.weblate.org/projects/home-manager/cli/"
|
||||
"it/>\n"
|
||||
"Language: it\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.17-dev\n"
|
||||
"X-Generator: Weblate 2026.7.dev0\n"
|
||||
|
||||
#. translators: For example: "home-manager: missing argument for --cores"
|
||||
#: home-manager/home-manager:16
|
||||
@@ -204,7 +204,7 @@ msgstr "Impossibile eseguire la build in una cartella in sola lettura"
|
||||
|
||||
#: home-manager/home-manager:822
|
||||
msgid "The configuration does not contain the specialisation \"%s\""
|
||||
msgstr ""
|
||||
msgstr "La configurazione non contiene la specializzazione \"%s\""
|
||||
|
||||
#: home-manager/home-manager:876
|
||||
msgid "No generation with ID %s"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: Home Manager\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0100\n"
|
||||
"PO-Revision-Date: 2026-05-15 00:11+0000\n"
|
||||
"Last-Translator: soweli Emi <emi@alchemi.dev>\n"
|
||||
"PO-Revision-Date: 2026-05-27 11:11+0000\n"
|
||||
"Last-Translator: qb114514 <GNUqb114514@outlook.com>\n"
|
||||
"Language-Team: Toki Pona <https://hosted.weblate.org/projects/home-manager/"
|
||||
"cli/tok/>\n"
|
||||
"Language: tok\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 2026.5.dev0\n"
|
||||
"X-Generator: Weblate 2026.6.dev0\n"
|
||||
|
||||
#. translators: For example: "home-manager: missing argument for --cores"
|
||||
#: home-manager/home-manager:16
|
||||
@@ -185,8 +185,9 @@ msgid "Cannot run build in read-only directory"
|
||||
msgstr "ma ni li ken ala ante. mi ken ala pali \"build\" lon ma ni"
|
||||
|
||||
#: home-manager/home-manager:822
|
||||
#, fuzzy
|
||||
msgid "The configuration does not contain the specialisation \"%s\""
|
||||
msgstr ""
|
||||
msgstr "lipu lawa jo ala lipu Specialisation \"%s\""
|
||||
|
||||
#: home-manager/home-manager:876
|
||||
msgid "No generation with ID %s"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: Home Manager\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0100\n"
|
||||
"PO-Revision-Date: 2026-03-31 16:13+0000\n"
|
||||
"Last-Translator: Zexin Yuan <yuan.zx@outlook.com>\n"
|
||||
"PO-Revision-Date: 2026-05-27 11:11+0000\n"
|
||||
"Last-Translator: qb114514 <GNUqb114514@outlook.com>\n"
|
||||
"Language-Team: Chinese (Simplified Han script) <https://hosted.weblate.org/"
|
||||
"projects/home-manager/cli/zh_Hans/>\n"
|
||||
"Language: zh_Hans\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 5.17-dev\n"
|
||||
"X-Generator: Weblate 2026.6.dev0\n"
|
||||
|
||||
#. translators: For example: "home-manager: missing argument for --cores"
|
||||
#: home-manager/home-manager:16
|
||||
@@ -27,7 +27,7 @@ msgstr "%s:缺少参数 %s"
|
||||
#. translators: For example: "home-manager: --rollback can only be used after switch"
|
||||
#: home-manager/home-manager:22
|
||||
msgid "%s: %s can only be used after %s"
|
||||
msgstr ""
|
||||
msgstr "%s: %s 只能在 %s 后使用"
|
||||
|
||||
#: home-manager/home-manager:71
|
||||
msgid "No configuration file found at %s"
|
||||
@@ -193,8 +193,9 @@ msgid "Cannot run build in read-only directory"
|
||||
msgstr "无法在只读目录中运行构建"
|
||||
|
||||
#: home-manager/home-manager:822
|
||||
#, fuzzy
|
||||
msgid "The configuration does not contain the specialisation \"%s\""
|
||||
msgstr ""
|
||||
msgstr "配置不包含 specialisation “%s”"
|
||||
|
||||
#: home-manager/home-manager:876
|
||||
msgid "No generation with ID %s"
|
||||
|
||||
+1
-34
@@ -2,38 +2,5 @@
|
||||
{
|
||||
inherit ((import ../modules/lib/stdlib-extended.nix lib)) hm;
|
||||
|
||||
homeManagerConfiguration =
|
||||
{
|
||||
check ? true,
|
||||
extraSpecialArgs ? { },
|
||||
lib ? pkgs.lib,
|
||||
modules ? [ ],
|
||||
pkgs,
|
||||
minimal ? false,
|
||||
}:
|
||||
import ../modules {
|
||||
inherit
|
||||
check
|
||||
extraSpecialArgs
|
||||
lib
|
||||
pkgs
|
||||
minimal
|
||||
;
|
||||
configuration = {
|
||||
imports = modules ++ [
|
||||
{
|
||||
programs.home-manager.path = builtins.path {
|
||||
path = ../.;
|
||||
name = "source";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
nixpkgs = {
|
||||
config = lib.mkDefault pkgs.config;
|
||||
|
||||
inherit (pkgs) overlays;
|
||||
};
|
||||
};
|
||||
};
|
||||
homeManagerConfiguration = import ./eval-config.nix;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
check ? true,
|
||||
extraSpecialArgs ? { },
|
||||
lib ? pkgs.lib,
|
||||
modules ? [ ],
|
||||
pkgs,
|
||||
minimal ? false,
|
||||
}:
|
||||
import ../modules {
|
||||
inherit
|
||||
check
|
||||
extraSpecialArgs
|
||||
lib
|
||||
pkgs
|
||||
minimal
|
||||
;
|
||||
configuration = {
|
||||
imports = modules ++ [
|
||||
{
|
||||
programs.home-manager.path = builtins.path {
|
||||
path = ../.;
|
||||
name = "source";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
nixpkgs = {
|
||||
config = lib.mkDefault pkgs.config;
|
||||
|
||||
inherit (pkgs) overlays;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -17,52 +17,67 @@ def main():
|
||||
print("🔍 Checking for duplicate maintainers between HM and nixpkgs...")
|
||||
|
||||
# Get home-manager maintainers
|
||||
hm_result = subprocess.run(['nix', 'eval', '--file', 'modules/lib/maintainers.nix', '--json'],
|
||||
capture_output=True, text=True, check=True)
|
||||
hm_result = subprocess.run(
|
||||
["nix", "eval", "--file", "modules/lib/maintainers.nix", "--json"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
hm_maintainers = json.loads(hm_result.stdout)
|
||||
hm_github_users = set()
|
||||
for name, data in hm_maintainers.items():
|
||||
if 'github' in data:
|
||||
hm_github_users.add(data['github'])
|
||||
if "github" in data:
|
||||
hm_github_users.add(data["github"])
|
||||
|
||||
# Read nixpkgs revision from flake.lock to ensure consistency between local and CI
|
||||
flake_lock_path = Path('flake.lock')
|
||||
with open(flake_lock_path, 'r') as f:
|
||||
flake_lock_path = Path("flake.lock")
|
||||
with open(flake_lock_path, "r") as f:
|
||||
flake_lock = json.load(f)
|
||||
nixpkgs_rev = flake_lock['nodes']['nixpkgs']['locked']['rev']
|
||||
nixpkgs_rev = flake_lock["nodes"]["nixpkgs"]["locked"]["rev"]
|
||||
|
||||
print(f"📌 Using nixpkgs from flake.lock: {nixpkgs_rev[:7]}")
|
||||
|
||||
# Get nixpkgs maintainers from the locked revision
|
||||
nixpkgs_result = subprocess.run(
|
||||
['nix', 'eval', f'github:NixOS/nixpkgs/{nixpkgs_rev}#lib.maintainers', '--json'],
|
||||
capture_output=True, text=True, check=True
|
||||
[
|
||||
"nix",
|
||||
"eval",
|
||||
f"github:NixOS/nixpkgs/{nixpkgs_rev}#lib.maintainers",
|
||||
"--json",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
nixpkgs_maintainers = json.loads(nixpkgs_result.stdout)
|
||||
nixpkgs_github_users = set()
|
||||
for name, data in nixpkgs_maintainers.items():
|
||||
if isinstance(data, dict) and 'github' in data:
|
||||
nixpkgs_github_users.add(data['github'])
|
||||
if isinstance(data, dict) and "github" in data:
|
||||
nixpkgs_github_users.add(data["github"])
|
||||
|
||||
# Find duplicates
|
||||
duplicates = hm_github_users.intersection(nixpkgs_github_users)
|
||||
|
||||
if duplicates:
|
||||
print(f'❌ Found {len(duplicates)} duplicate maintainers between HM and nixpkgs:')
|
||||
print(
|
||||
f"❌ Found {len(duplicates)} duplicate maintainers between HM and nixpkgs:"
|
||||
)
|
||||
for github_user in sorted(duplicates):
|
||||
# Find the HM attribute name for this github user
|
||||
hm_attr = None
|
||||
for attr_name, data in hm_maintainers.items():
|
||||
if data.get('github') == github_user:
|
||||
if data.get("github") == github_user:
|
||||
hm_attr = attr_name
|
||||
break
|
||||
print(f' - {github_user} (HM attribute: {hm_attr})')
|
||||
print(f" - {github_user} (HM attribute: {hm_attr})")
|
||||
print()
|
||||
print('These maintainers should be removed from HM maintainers file to avoid duplication.')
|
||||
print('They can be referenced directly from nixpkgs instead.')
|
||||
print(
|
||||
"These maintainers should be removed from HM maintainers file to avoid duplication."
|
||||
)
|
||||
print("They can be referenced directly from nixpkgs instead.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print('✅ No duplicate maintainers found')
|
||||
print("✅ No duplicate maintainers found")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -16,6 +16,7 @@ from pathlib import Path
|
||||
|
||||
class NixEvalError(Exception):
|
||||
"""Custom exception for errors during Nix evaluation."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@@ -39,7 +40,9 @@ def run_nix_eval(nix_file: Path, *args: str) -> str:
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except FileNotFoundError:
|
||||
logging.error("'nix-instantiate' command not found. Is Nix installed and in your PATH?")
|
||||
logging.error(
|
||||
"'nix-instantiate' command not found. Is Nix installed and in your PATH?"
|
||||
)
|
||||
raise NixEvalError("'nix-instantiate' not found")
|
||||
except subprocess.CalledProcessError as e:
|
||||
logging.error(f"Nix evaluation failed with exit code {e.returncode}")
|
||||
|
||||
@@ -40,6 +40,7 @@ def get_project_root() -> Path:
|
||||
# Assumes this script is at: <root>/flake/dev/generate-all-maintainers/
|
||||
return Path(__file__).parent.parent.parent.parent.resolve()
|
||||
|
||||
|
||||
class MetaMaintainerGenerator:
|
||||
"""Generates maintainers list using meta.maintainers from Home Manager evaluation."""
|
||||
|
||||
@@ -54,9 +55,12 @@ class MetaMaintainerGenerator:
|
||||
print("🔍 Extracting maintainers using meta.maintainers...")
|
||||
|
||||
try:
|
||||
result = subprocess.run([
|
||||
"nix", "eval", "--file", str(self.extractor_script), "--json"
|
||||
], capture_output=True, text=True, timeout=60)
|
||||
result = subprocess.run(
|
||||
["nix", "eval", "--file", str(self.extractor_script), "--json"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
data = json.loads(result.stdout)
|
||||
@@ -87,7 +91,7 @@ class MetaMaintainerGenerator:
|
||||
print(f"🏠 Home Manager maintainers: {len(hm_maintainers)}")
|
||||
print(f"📦 Nixpkgs maintainers: {len(nixpkgs_maintainers)}")
|
||||
|
||||
with open(self.output_file, 'w') as f:
|
||||
with open(self.output_file, "w") as f:
|
||||
f.write(
|
||||
inspect.cleandoc("""
|
||||
# Home Manager all maintainers list.
|
||||
@@ -114,9 +118,12 @@ class MetaMaintainerGenerator:
|
||||
def validate_generated_file(self) -> bool:
|
||||
"""Validate the generated Nix file syntax."""
|
||||
try:
|
||||
result = subprocess.run([
|
||||
'nix-instantiate', '--eval', str(self.output_file), '--strict'
|
||||
], capture_output=True, text=True, timeout=10)
|
||||
result = subprocess.run(
|
||||
["nix-instantiate", "--eval", str(self.output_file), "--strict"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("✅ Generated file has valid Nix syntax")
|
||||
@@ -148,16 +155,16 @@ def main():
|
||||
description="Generate Home Manager all-maintainers.nix using meta.maintainers"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--root',
|
||||
"--root",
|
||||
type=Path,
|
||||
default=None,
|
||||
help='Path to Home Manager root (default: auto-detect)'
|
||||
help="Path to Home Manager root (default: auto-detect)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
"--output",
|
||||
type=Path,
|
||||
default=None,
|
||||
help='Output file path (default: <root>/all-maintainers.nix)'
|
||||
help="Output file path (default: <root>/all-maintainers.nix)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
+108
-41
@@ -60,6 +60,7 @@ query($owner: String!, $repo: String!, $prNumber: Int!) {
|
||||
|
||||
class GHError(Exception):
|
||||
"""Custom exception for errors related to 'gh' CLI commands."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@@ -94,15 +95,28 @@ def get_manual_reviewer_actions(
|
||||
tuple: (manually_requested, manually_removed) sets of usernames
|
||||
"""
|
||||
try:
|
||||
result = run_gh_command([
|
||||
"api", "graphql",
|
||||
"-f", f"query={MANUAL_REVIEW_REQUEST_QUERY}",
|
||||
"-F", f"owner={owner}",
|
||||
"-F", f"repo={repo}",
|
||||
"-F", f"prNumber={pr_number}",
|
||||
])
|
||||
result = run_gh_command(
|
||||
[
|
||||
"api",
|
||||
"graphql",
|
||||
"-f",
|
||||
f"query={MANUAL_REVIEW_REQUEST_QUERY}",
|
||||
"-F",
|
||||
f"owner={owner}",
|
||||
"-F",
|
||||
f"repo={repo}",
|
||||
"-F",
|
||||
f"prNumber={pr_number}",
|
||||
]
|
||||
)
|
||||
data = json.loads(result.stdout)
|
||||
nodes = data.get("data", {}).get("repository", {}).get("pullRequest", {}).get("timelineItems", {}).get("nodes", [])
|
||||
nodes = (
|
||||
data.get("data", {})
|
||||
.get("repository", {})
|
||||
.get("pullRequest", {})
|
||||
.get("timelineItems", {})
|
||||
.get("nodes", [])
|
||||
)
|
||||
|
||||
manually_requested = set()
|
||||
manually_removed = set()
|
||||
@@ -143,7 +157,15 @@ def get_users_from_gh(args: list[str], error_message: str) -> set[str]:
|
||||
def get_pending_reviewers(pr_number: int) -> set[str]:
|
||||
"""Gets the set of currently pending reviewers for a PR."""
|
||||
return get_users_from_gh(
|
||||
["pr", "view", str(pr_number), "--json", "reviewRequests", "--jq", ".reviewRequests[].login"],
|
||||
[
|
||||
"pr",
|
||||
"view",
|
||||
str(pr_number),
|
||||
"--json",
|
||||
"reviewRequests",
|
||||
"--jq",
|
||||
".reviewRequests[].login",
|
||||
],
|
||||
"Error getting pending reviewers",
|
||||
)
|
||||
|
||||
@@ -151,7 +173,12 @@ def get_pending_reviewers(pr_number: int) -> set[str]:
|
||||
def get_past_reviewers(owner: str, repo: str, pr_number: int) -> set[str]:
|
||||
"""Gets the set of users who have already reviewed the PR."""
|
||||
return get_users_from_gh(
|
||||
["api", f"repos/{owner}/{repo}/pulls/{pr_number}/reviews", "--jq", ".[].user.login"],
|
||||
[
|
||||
"api",
|
||||
f"repos/{owner}/{repo}/pulls/{pr_number}/reviews",
|
||||
"--jq",
|
||||
".[].user.login",
|
||||
],
|
||||
"Error getting past reviewers",
|
||||
)
|
||||
|
||||
@@ -162,17 +189,14 @@ def is_collaborator(owner: str, repo: str, username: str) -> bool:
|
||||
Handles 404 as a non-collaborator, while other errors are raised.
|
||||
"""
|
||||
result = run_gh_command(
|
||||
["api", f"repos/{owner}/{repo}/collaborators/{username}"],
|
||||
check=False
|
||||
["api", f"repos/{owner}/{repo}/collaborators/{username}"], check=False
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
return True
|
||||
|
||||
if "HTTP 404" in result.stderr:
|
||||
logging.error(
|
||||
"'%s' is not a collaborator in this repository.", username
|
||||
)
|
||||
logging.error("'%s' is not a collaborator in this repository.", username)
|
||||
return False
|
||||
else:
|
||||
logging.error(
|
||||
@@ -195,22 +219,32 @@ def update_reviewers(
|
||||
if reviewers_to_add:
|
||||
logging.info("Requesting reviews from: %s", ", ".join(reviewers_to_add))
|
||||
try:
|
||||
run_gh_command([
|
||||
"pr", "edit", str(pr_number),
|
||||
"--add-reviewer", ",".join(reviewers_to_add)
|
||||
])
|
||||
run_gh_command(
|
||||
[
|
||||
"pr",
|
||||
"edit",
|
||||
str(pr_number),
|
||||
"--add-reviewer",
|
||||
",".join(reviewers_to_add),
|
||||
]
|
||||
)
|
||||
except GHError as e:
|
||||
logging.error("Failed to add reviewers: %s", e)
|
||||
|
||||
if reviewers_to_remove and owner and repo:
|
||||
logging.info("Removing review requests from: %s", ", ".join(reviewers_to_remove))
|
||||
logging.info(
|
||||
"Removing review requests from: %s", ", ".join(reviewers_to_remove)
|
||||
)
|
||||
payload = json.dumps({"reviewers": list(reviewers_to_remove)})
|
||||
try:
|
||||
run_gh_command(
|
||||
[
|
||||
"api", "--method", "DELETE",
|
||||
"api",
|
||||
"--method",
|
||||
"DELETE",
|
||||
f"repos/{owner}/{repo}/pulls/{pr_number}/requested_reviewers",
|
||||
"--input", "-",
|
||||
"--input",
|
||||
"-",
|
||||
],
|
||||
input_data=payload,
|
||||
)
|
||||
@@ -220,15 +254,33 @@ def update_reviewers(
|
||||
|
||||
def main() -> None:
|
||||
"""Main function to handle command-line arguments and manage reviewers."""
|
||||
parser = argparse.ArgumentParser(description="Manage pull request reviewers for Home Manager.")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Manage pull request reviewers for Home Manager."
|
||||
)
|
||||
parser.add_argument("--owner", required=True, help="Repository owner.")
|
||||
parser.add_argument("--repo", required=True, help="Repository name.")
|
||||
parser.add_argument("--pr-number", type=int, required=True, help="Pull request number.")
|
||||
parser.add_argument(
|
||||
"--pr-number", type=int, required=True, help="Pull request number."
|
||||
)
|
||||
parser.add_argument("--pr-author", required=True, help="PR author's username.")
|
||||
parser.add_argument("--current-maintainers", default="", help="Space-separated list of maintainers for the changed files.")
|
||||
parser.add_argument("--changed-files", default="", help="Newline-separated list of changed files.")
|
||||
parser.add_argument("--bot-user-name", default="", help="Bot user name to distinguish manual vs automated review requests.")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Show what would be done without making actual changes.")
|
||||
parser.add_argument(
|
||||
"--current-maintainers",
|
||||
default="",
|
||||
help="Space-separated list of maintainers for the changed files.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--changed-files", default="", help="Newline-separated list of changed files."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--bot-user-name",
|
||||
default="",
|
||||
help="Bot user name to distinguish manual vs automated review requests.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Show what would be done without making actual changes.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
no_changed_files = not args.changed_files.strip()
|
||||
@@ -237,13 +289,15 @@ def main() -> None:
|
||||
maintainers: set[str] = set(args.current_maintainers.split())
|
||||
pending_reviewers = get_pending_reviewers(args.pr_number)
|
||||
past_reviewers = get_past_reviewers(args.owner, args.repo, args.pr_number)
|
||||
manually_requested, manually_removed = get_manual_reviewer_actions(args.owner, args.repo, args.pr_number, args.bot_user_name)
|
||||
manually_requested, manually_removed = get_manual_reviewer_actions(
|
||||
args.owner, args.repo, args.pr_number, args.bot_user_name
|
||||
)
|
||||
|
||||
logging.info("File Maintainers: %s", ' '.join(maintainers) or "None")
|
||||
logging.info("Pending Reviewers: %s", ' '.join(pending_reviewers) or "None")
|
||||
logging.info("Past Reviewers: %s", ' '.join(past_reviewers) or "None")
|
||||
logging.info("Manually Requested: %s", ' '.join(manually_requested) or "None")
|
||||
logging.info("Manually Removed: %s", ' '.join(manually_removed) or "None")
|
||||
logging.info("File Maintainers: %s", " ".join(maintainers) or "None")
|
||||
logging.info("Pending Reviewers: %s", " ".join(pending_reviewers) or "None")
|
||||
logging.info("Past Reviewers: %s", " ".join(past_reviewers) or "None")
|
||||
logging.info("Manually Requested: %s", " ".join(manually_requested) or "None")
|
||||
logging.info("Manually Removed: %s", " ".join(manually_removed) or "None")
|
||||
|
||||
# --- 2. Determine reviewers to remove ---
|
||||
reviewers_to_remove: set[str] = set()
|
||||
@@ -257,13 +311,15 @@ def main() -> None:
|
||||
|
||||
if reviewers_to_remove:
|
||||
if args.dry_run:
|
||||
logging.info("DRY RUN: Would remove reviewers: %s", ", ".join(reviewers_to_remove))
|
||||
logging.info(
|
||||
"DRY RUN: Would remove reviewers: %s", ", ".join(reviewers_to_remove)
|
||||
)
|
||||
else:
|
||||
update_reviewers(
|
||||
args.pr_number,
|
||||
owner=args.owner,
|
||||
repo=args.repo,
|
||||
reviewers_to_remove=reviewers_to_remove
|
||||
reviewers_to_remove=reviewers_to_remove,
|
||||
)
|
||||
else:
|
||||
logging.info("No reviewers to remove.")
|
||||
@@ -278,20 +334,29 @@ def main() -> None:
|
||||
MAX_MAINTAINERS_THRESHOLD,
|
||||
)
|
||||
else:
|
||||
users_to_exclude = {args.pr_author} | past_reviewers | pending_reviewers | manually_removed
|
||||
users_to_exclude = (
|
||||
{args.pr_author} | past_reviewers | pending_reviewers | manually_removed
|
||||
)
|
||||
potential_reviewers = maintainers - users_to_exclude
|
||||
|
||||
reviewers_to_add = {
|
||||
user for user in potential_reviewers if is_collaborator(args.owner, args.repo, user)
|
||||
user
|
||||
for user in potential_reviewers
|
||||
if is_collaborator(args.owner, args.repo, user)
|
||||
}
|
||||
|
||||
non_collaborators = potential_reviewers - reviewers_to_add
|
||||
if non_collaborators:
|
||||
logging.warning("Ignoring non-collaborators: %s", ", ".join(non_collaborators))
|
||||
logging.warning(
|
||||
"Ignoring non-collaborators: %s", ", ".join(non_collaborators)
|
||||
)
|
||||
|
||||
manually_removed_maintainers = reviewers_to_add & manually_removed
|
||||
if manually_removed_maintainers:
|
||||
logging.info("Not re-adding manually removed maintainers: %s", ", ".join(manually_removed_maintainers))
|
||||
logging.info(
|
||||
"Not re-adding manually removed maintainers: %s",
|
||||
", ".join(manually_removed_maintainers),
|
||||
)
|
||||
reviewers_to_add -= manually_removed
|
||||
|
||||
if len(reviewers_to_add) > MAX_REVIEWERS:
|
||||
@@ -304,7 +369,9 @@ def main() -> None:
|
||||
|
||||
if reviewers_to_add:
|
||||
if args.dry_run:
|
||||
logging.info("DRY RUN: Would add reviewers: %s", ", ".join(reviewers_to_add))
|
||||
logging.info(
|
||||
"DRY RUN: Would add reviewers: %s", ", ".join(reviewers_to_add)
|
||||
)
|
||||
else:
|
||||
update_reviewers(args.pr_number, reviewers_to_add=reviewers_to_add)
|
||||
else:
|
||||
|
||||
@@ -14,32 +14,38 @@ import sys
|
||||
def main():
|
||||
print("🔍 Validating maintainer entries...")
|
||||
|
||||
result = subprocess.run(['nix', 'eval', '--file', 'modules/lib/maintainers.nix', '--json'],
|
||||
capture_output=True, text=True, check=True)
|
||||
result = subprocess.run(
|
||||
["nix", "eval", "--file", "modules/lib/maintainers.nix", "--json"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
maintainers = json.loads(result.stdout)
|
||||
errors = []
|
||||
|
||||
for name, data in maintainers.items():
|
||||
if 'github' not in data:
|
||||
if "github" not in data:
|
||||
errors.append(f'{name}: Missing required field "github"')
|
||||
if 'githubId' not in data:
|
||||
if "githubId" not in data:
|
||||
errors.append(f'{name}: Missing required field "githubId"')
|
||||
|
||||
if 'githubId' in data:
|
||||
github_id = data['githubId']
|
||||
if "githubId" in data:
|
||||
github_id = data["githubId"]
|
||||
if not isinstance(github_id, int):
|
||||
errors.append(f'{name}: githubId must be a number, not a string: {github_id} (type: {type(github_id).__name__})')
|
||||
errors.append(
|
||||
f"{name}: githubId must be a number, not a string: {github_id} (type: {type(github_id).__name__})"
|
||||
)
|
||||
elif github_id <= 0:
|
||||
errors.append(f'{name}: githubId must be positive: {github_id}')
|
||||
errors.append(f"{name}: githubId must be positive: {github_id}")
|
||||
|
||||
if errors:
|
||||
print('❌ Validation errors found:')
|
||||
print("❌ Validation errors found:")
|
||||
for error in errors:
|
||||
print(f' - {error}')
|
||||
print(f" - {error}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print('✅ All maintainer entries are valid')
|
||||
print(f'✅ Validated {len(maintainers)} maintainer entries')
|
||||
print("✅ All maintainer entries are valid")
|
||||
print(f"✅ Validated {len(maintainers)} maintainer entries")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -13,8 +13,12 @@ def main():
|
||||
print("🔍 Validating maintainers.nix syntax...")
|
||||
|
||||
try:
|
||||
subprocess.run(['nix', 'eval', '--file', 'modules/lib/maintainers.nix', '--json'],
|
||||
capture_output=True, text=True, check=True)
|
||||
subprocess.run(
|
||||
["nix", "eval", "--file", "modules/lib/maintainers.nix", "--json"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
print("✅ Valid Nix syntax")
|
||||
except subprocess.CalledProcessError:
|
||||
print("❌ Invalid Nix syntax")
|
||||
|
||||
@@ -46,7 +46,6 @@ let
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
da157
|
||||
kranzes
|
||||
];
|
||||
|
||||
|
||||
+40
-24
@@ -12,20 +12,32 @@
|
||||
let
|
||||
inherit (lib)
|
||||
all
|
||||
filterAttrs
|
||||
attrNames
|
||||
attrValues
|
||||
elem
|
||||
filter
|
||||
head
|
||||
hm
|
||||
isAttrs
|
||||
mapAttrs
|
||||
length
|
||||
tail
|
||||
toposort
|
||||
;
|
||||
|
||||
inherit (hm.dag)
|
||||
empty
|
||||
entriesBetween
|
||||
entryAfter
|
||||
entryBetween
|
||||
isEntry
|
||||
;
|
||||
in
|
||||
{
|
||||
empty = { };
|
||||
|
||||
isEntry = e: e ? data && e ? after && e ? before;
|
||||
isDag = dag: builtins.isAttrs dag && all hm.dag.isEntry (builtins.attrValues dag);
|
||||
isDag = dag: isAttrs dag && all isEntry (attrValues dag);
|
||||
|
||||
# Takes an attribute set containing entries built by entryAnywhere,
|
||||
# entryAfter, and entryBefore to a topologically sorted list of
|
||||
@@ -84,16 +96,19 @@ in
|
||||
# }
|
||||
# true
|
||||
topoSort =
|
||||
let
|
||||
before = a: b: elem a.name b.after;
|
||||
in
|
||||
dag:
|
||||
let
|
||||
dagBefore = dag: name: builtins.attrNames (filterAttrs (_n: v: builtins.elem name v.before) dag);
|
||||
names = attrNames dag;
|
||||
dagBefore = name: filter (n: elem name dag.${n}.before) names;
|
||||
normalizedDag = mapAttrs (n: v: {
|
||||
name = n;
|
||||
inherit (v) data;
|
||||
after = v.after ++ dagBefore dag n;
|
||||
after = v.after ++ dagBefore n;
|
||||
}) dag;
|
||||
before = a: b: builtins.elem a.name b.after;
|
||||
sorted = toposort before (builtins.attrValues normalizedDag);
|
||||
sorted = toposort before (attrValues normalizedDag);
|
||||
in
|
||||
if sorted ? result then
|
||||
{
|
||||
@@ -108,10 +123,10 @@ in
|
||||
entryBetween = before: after: data: { inherit data before after; };
|
||||
|
||||
# Create a DAG entry with no particular dependency information.
|
||||
entryAnywhere = hm.dag.entryBetween [ ] [ ];
|
||||
entryAnywhere = entryBetween [ ] [ ];
|
||||
|
||||
entryAfter = hm.dag.entryBetween [ ];
|
||||
entryBefore = before: hm.dag.entryBetween before [ ];
|
||||
entryAfter = entryBetween [ ];
|
||||
entryBefore = before: entryBetween before [ ];
|
||||
|
||||
# Given a list of entries, this function places them in order within the DAG.
|
||||
# Each entry is labeled "${tag}-${entry index}" and other DAG entries can be
|
||||
@@ -124,24 +139,25 @@ in
|
||||
let
|
||||
go =
|
||||
i: before: after: entries:
|
||||
let
|
||||
name = "${tag}-${toString i}";
|
||||
in
|
||||
if entries == [ ] then
|
||||
hm.dag.empty
|
||||
else if length entries == 1 then
|
||||
{
|
||||
"${name}" = hm.dag.entryBetween before after (head entries);
|
||||
}
|
||||
empty
|
||||
else
|
||||
{
|
||||
"${name}" = hm.dag.entryAfter after (head entries);
|
||||
}
|
||||
// go (i + 1) before [ name ] (tail entries);
|
||||
let
|
||||
name = "${tag}-${toString i}";
|
||||
in
|
||||
if length entries == 1 then
|
||||
{
|
||||
"${name}" = entryBetween before after (head entries);
|
||||
}
|
||||
else
|
||||
{
|
||||
"${name}" = entryAfter after (head entries);
|
||||
}
|
||||
// go (i + 1) before [ name ] (tail entries);
|
||||
in
|
||||
go 0;
|
||||
|
||||
entriesAnywhere = tag: hm.dag.entriesBetween tag [ ] [ ];
|
||||
entriesAfter = tag: hm.dag.entriesBetween tag [ ];
|
||||
entriesBefore = tag: before: hm.dag.entriesBetween tag before [ ];
|
||||
entriesAnywhere = tag: entriesBetween tag [ ] [ ];
|
||||
entriesAfter = tag: entriesBetween tag [ ];
|
||||
entriesBefore = tag: before: entriesBetween tag before [ ];
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ rec {
|
||||
git = import ./git.nix { inherit lib; };
|
||||
gvariant = import ./gvariant.nix { inherit lib; };
|
||||
maintainers = import ./maintainers.nix;
|
||||
mcp = import ./mcp.nix { inherit lib; };
|
||||
strings = import ./strings.nix { inherit lib; };
|
||||
types = import ./types.nix { inherit gvariant lib; };
|
||||
|
||||
|
||||
@@ -1,5 +1,47 @@
|
||||
{ lib }:
|
||||
{
|
||||
/*
|
||||
Builds a standard warning for an option value shape that is deprecated.
|
||||
|
||||
Example:
|
||||
mkDeprecatedOptionValueWarning {
|
||||
option = [ "programs" "example" "settings" ];
|
||||
old = "a list";
|
||||
replacement = "`programs.example.settings.items`";
|
||||
}
|
||||
|
||||
=> Using `programs.example.settings` as a list is deprecated and will be
|
||||
removed in a future release. Please use `programs.example.settings.items`
|
||||
instead.
|
||||
*/
|
||||
mkDeprecatedOptionValueWarning =
|
||||
{
|
||||
option,
|
||||
old,
|
||||
replacement,
|
||||
details ? "",
|
||||
}:
|
||||
''
|
||||
Using `${lib.showOption option}` as ${old} is deprecated and will be
|
||||
removed in a future release. Please use ${replacement} instead.
|
||||
''
|
||||
+ lib.optionalString (details != "") ''
|
||||
|
||||
${details}
|
||||
'';
|
||||
|
||||
# Builds a standard warning for an option value that has been renamed.
|
||||
mkDeprecatedOptionValueRenameWarning =
|
||||
{
|
||||
option,
|
||||
old,
|
||||
replacement,
|
||||
}:
|
||||
''
|
||||
The value ${old} for `${lib.showOption option}` is deprecated and will be
|
||||
removed in a future release. Please use ${replacement} instead.
|
||||
'';
|
||||
|
||||
/*
|
||||
Returns a function that maps
|
||||
[
|
||||
|
||||
+680
-3
@@ -1,6 +1,684 @@
|
||||
{ lib }:
|
||||
|
||||
let
|
||||
isDAGEntryWithOrdering' =
|
||||
entry: lib.hm.dag.isEntry entry && (entry.after != [ ] || entry.before != [ ]);
|
||||
hasDAGEntryWithOrdering' = attrs: lib.any isDAGEntryWithOrdering' (lib.attrValues attrs);
|
||||
sortDAGEntries' =
|
||||
{
|
||||
cycleErrorMessage ? "Dependency cycle in DAG entries",
|
||||
}:
|
||||
attrs:
|
||||
let
|
||||
dag = lib.mapAttrs (
|
||||
_: entry: if lib.hm.dag.isEntry entry then entry else lib.hm.dag.entryAnywhere entry
|
||||
) attrs;
|
||||
sortedDag = lib.hm.dag.topoSort dag;
|
||||
entries = sortedDag.result or (abort "${cycleErrorMessage}: ${builtins.toJSON sortedDag}");
|
||||
in
|
||||
map (entry: {
|
||||
inherit (entry) name;
|
||||
value = entry.data;
|
||||
}) entries;
|
||||
|
||||
# Freeform generators can use this to support DAG ordering while preserving
|
||||
# entryAnywhere-shaped plain values beside ordered DAG entries.
|
||||
toDAGOrderedAttrs' =
|
||||
{
|
||||
cycleErrorMessage ? "Dependency cycle in DAG entries",
|
||||
}:
|
||||
attrs:
|
||||
if hasDAGEntryWithOrdering' attrs then
|
||||
sortDAGEntries' { inherit cycleErrorMessage; } (
|
||||
lib.mapAttrs (
|
||||
_: entry: if isDAGEntryWithOrdering' entry then entry else lib.hm.dag.entryAnywhere entry
|
||||
) attrs
|
||||
)
|
||||
else
|
||||
lib.mapAttrsToList (name: value: { inherit name value; }) attrs;
|
||||
|
||||
toDAGOrderedText' =
|
||||
{
|
||||
renderAttrs,
|
||||
renderList,
|
||||
renderValue,
|
||||
cycleErrorMessage ? "Dependency cycle in DAG entries",
|
||||
}:
|
||||
let
|
||||
render =
|
||||
value:
|
||||
if lib.isDerivation value || builtins.isPath value then
|
||||
renderValue value
|
||||
else if builtins.isAttrs value then
|
||||
renderAttrs (
|
||||
map (entry: entry // { value = render entry.value; }) (
|
||||
toDAGOrderedAttrs' { inherit cycleErrorMessage; } value
|
||||
)
|
||||
)
|
||||
else if builtins.isList value then
|
||||
renderList (map render value)
|
||||
else
|
||||
renderValue value;
|
||||
in
|
||||
render;
|
||||
|
||||
toDAGOrderedJsonText' =
|
||||
{
|
||||
cycleErrorMessage ? "Dependency cycle in DAG entries",
|
||||
}:
|
||||
toDAGOrderedText' {
|
||||
inherit cycleErrorMessage;
|
||||
renderValue = builtins.toJSON;
|
||||
renderList = values: "[${lib.concatStringsSep "," values}]";
|
||||
renderAttrs =
|
||||
attrs:
|
||||
let
|
||||
renderAttr = entry: "${builtins.toJSON entry.name}:${entry.value}";
|
||||
in
|
||||
"{${lib.concatMapStringsSep "," renderAttr attrs}}";
|
||||
};
|
||||
|
||||
toDAGOrderedKeyValue' =
|
||||
{
|
||||
mkKeyValue ? lib.generators.mkKeyValueDefault { } "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
listToValue ? null,
|
||||
indent ? "",
|
||||
cycleErrorMessage ? "Dependency cycle in DAG entries",
|
||||
}:
|
||||
assert listsAsDuplicateKeys -> listToValue == null;
|
||||
attrs:
|
||||
let
|
||||
normalizeValue =
|
||||
value: if listToValue != null && builtins.isList value then listToValue value else value;
|
||||
mkLine = name: value: indent + mkKeyValue name value + "\n";
|
||||
mkLines =
|
||||
if listsAsDuplicateKeys then
|
||||
name: value: map (mkLine name) (if builtins.isList value then value else [ value ])
|
||||
else
|
||||
name: value: [ (mkLine name (normalizeValue value)) ];
|
||||
in
|
||||
lib.concatStrings (
|
||||
lib.concatMap (entry: mkLines entry.name entry.value) (
|
||||
toDAGOrderedAttrs' { inherit cycleErrorMessage; } attrs
|
||||
)
|
||||
);
|
||||
|
||||
toDAGOrderedINI' =
|
||||
{
|
||||
mkSectionName ? (name: lib.escape [ "[" "]" ] name),
|
||||
mkKeyValue ? lib.generators.mkKeyValueDefault { } "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
listToValue ? null,
|
||||
cycleErrorMessage ? "Dependency cycle in INI sections",
|
||||
}:
|
||||
assert listsAsDuplicateKeys -> listToValue == null;
|
||||
attrsOfAttrs:
|
||||
let
|
||||
mkSection =
|
||||
section:
|
||||
''
|
||||
[${mkSectionName section.name}]
|
||||
''
|
||||
+ toDAGOrderedKeyValue' {
|
||||
inherit
|
||||
cycleErrorMessage
|
||||
listToValue
|
||||
listsAsDuplicateKeys
|
||||
mkKeyValue
|
||||
;
|
||||
} section.value;
|
||||
in
|
||||
lib.concatStringsSep "\n" (
|
||||
map mkSection (toDAGOrderedAttrs' { inherit cycleErrorMessage; } attrsOfAttrs)
|
||||
);
|
||||
|
||||
mkDAGOrderedFormat' =
|
||||
{
|
||||
pkgs,
|
||||
format,
|
||||
generator,
|
||||
nativeBuildInputs ? [ ],
|
||||
buildCommand ? ''
|
||||
cp "$valuePath" "$out"
|
||||
'',
|
||||
cycleErrorMessage ? null,
|
||||
}:
|
||||
format
|
||||
// {
|
||||
generate =
|
||||
name: value:
|
||||
pkgs.runCommandLocal name {
|
||||
inherit nativeBuildInputs;
|
||||
value = generator {
|
||||
cycleErrorMessage =
|
||||
if cycleErrorMessage == null then "Dependency cycle in ${name}" else cycleErrorMessage;
|
||||
} value;
|
||||
# Nix does not populate $valuePath when __structuredAttrs is true.
|
||||
passAsFile = [ "value" ];
|
||||
} buildCommand;
|
||||
};
|
||||
in
|
||||
{
|
||||
/**
|
||||
Returns whether a value is a Home Manager DAG entry with ordering metadata.
|
||||
|
||||
# Inputs
|
||||
|
||||
`entry`
|
||||
|
||||
: 1\. Function argument
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
isDAGEntryWithOrdering :: Any -> Bool
|
||||
```
|
||||
*/
|
||||
isDAGEntryWithOrdering = isDAGEntryWithOrdering';
|
||||
|
||||
/**
|
||||
Returns whether an attribute set contains any Home Manager DAG entry with
|
||||
ordering metadata.
|
||||
|
||||
# Inputs
|
||||
|
||||
`attrs`
|
||||
|
||||
: 1\. Function argument
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
hasDAGEntryWithOrdering :: AttrSet -> Bool
|
||||
```
|
||||
*/
|
||||
hasDAGEntryWithOrdering = hasDAGEntryWithOrdering';
|
||||
|
||||
/**
|
||||
Topologically sort an attribute set that contains Home Manager DAG entries.
|
||||
Plain sibling values are treated as unordered DAG entries.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`cycleErrorMessage` (string; optional)
|
||||
: Message prefix to use when a dependency cycle is detected.
|
||||
|
||||
`attrs`
|
||||
|
||||
: Attribute set to sort
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
sortDAGEntries :: { cycleErrorMessage ? String } -> AttrSet -> [ { name :: String; value :: Any; } ]
|
||||
```
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.hm.generators.sortDAGEntries` usage example
|
||||
|
||||
```nix
|
||||
lib.hm.generators.sortDAGEntries { } {
|
||||
after = lib.hm.dag.entryAfter [ "before" ] "2";
|
||||
before = "1";
|
||||
}
|
||||
=> [
|
||||
{ name = "before"; value = "1"; }
|
||||
{ name = "after"; value = "2"; }
|
||||
]
|
||||
```
|
||||
|
||||
:::
|
||||
*/
|
||||
sortDAGEntries = sortDAGEntries';
|
||||
|
||||
/**
|
||||
Convert an attribute set to a list of name/value pairs, using topological
|
||||
ordering when any value is a Home Manager DAG entry with non-empty ordering
|
||||
metadata.
|
||||
|
||||
This is intended for freeform generators that should support optional DAG
|
||||
ordering while preserving entryAnywhere-shaped plain values.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`cycleErrorMessage` (string; optional)
|
||||
: Message prefix to use when a dependency cycle is detected.
|
||||
|
||||
`attrs`
|
||||
|
||||
: Attribute set to convert
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
toDAGOrderedAttrs :: { cycleErrorMessage ? String } -> AttrSet -> [ { name :: String; value :: Any; } ]
|
||||
```
|
||||
*/
|
||||
toDAGOrderedAttrs = toDAGOrderedAttrs';
|
||||
|
||||
/**
|
||||
Render Nix values to text using caller-provided render functions while
|
||||
preserving ordering from nested Home Manager DAG entries.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`renderAttrs` (function)
|
||||
: Render ordered attribute entries. Receives a list of
|
||||
`{ name, value }` entries where `value` is already rendered text.
|
||||
|
||||
`renderList` (function)
|
||||
: Render list values. Receives rendered element strings.
|
||||
|
||||
`renderValue` (function)
|
||||
: Render scalar values, paths, and derivations.
|
||||
|
||||
`cycleErrorMessage` (string; optional)
|
||||
: Message prefix to use when a dependency cycle is detected.
|
||||
|
||||
`value`
|
||||
|
||||
: Value to render
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
toDAGOrderedText :: { renderAttrs :: Function; renderList :: Function; renderValue :: Function; cycleErrorMessage ? String; } -> Any -> String
|
||||
```
|
||||
*/
|
||||
toDAGOrderedText = toDAGOrderedText';
|
||||
|
||||
/**
|
||||
Generate a key-value-style config file from an attribute set, preserving
|
||||
ordering from Home Manager DAG entries.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`mkKeyValue` (function; optional)
|
||||
: Format a setting line from name and value.
|
||||
|
||||
`listsAsDuplicateKeys` (boolean; optional)
|
||||
: Render list values as duplicate keys.
|
||||
|
||||
`listToValue` (function or null; optional)
|
||||
: Convert list values to scalar values before rendering.
|
||||
|
||||
`indent` (string; optional)
|
||||
: Initial indentation level.
|
||||
|
||||
`cycleErrorMessage` (string; optional)
|
||||
: Message prefix to use when a dependency cycle is detected.
|
||||
|
||||
`attrs`
|
||||
|
||||
: Attribute set to render
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
toDAGOrderedKeyValue :: { mkKeyValue ? Function; listsAsDuplicateKeys ? Bool; listToValue ? NullOr Function; indent ? String; cycleErrorMessage ? String; } -> AttrSet -> String
|
||||
```
|
||||
*/
|
||||
toDAGOrderedKeyValue = toDAGOrderedKeyValue';
|
||||
|
||||
/**
|
||||
Generate an INI-style config file from an attribute set of sections while
|
||||
preserving ordering from Home Manager DAG entries.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`mkSectionName` (function; optional)
|
||||
: Format a section name.
|
||||
|
||||
`mkKeyValue` (function; optional)
|
||||
: Format a setting line from name and value.
|
||||
|
||||
`listsAsDuplicateKeys` (boolean; optional)
|
||||
: Render list values as duplicate keys.
|
||||
|
||||
`listToValue` (function or null; optional)
|
||||
: Convert list values to scalar values before rendering.
|
||||
|
||||
`cycleErrorMessage` (string; optional)
|
||||
: Message prefix to use when a dependency cycle is detected.
|
||||
|
||||
`attrsOfAttrs`
|
||||
|
||||
: Attribute set of sections to render
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
toDAGOrderedINI :: { mkSectionName ? Function; mkKeyValue ? Function; listsAsDuplicateKeys ? Bool; listToValue ? NullOr Function; cycleErrorMessage ? String; } -> AttrSet -> String
|
||||
```
|
||||
*/
|
||||
toDAGOrderedINI = toDAGOrderedINI';
|
||||
|
||||
/**
|
||||
Wrap a `pkgs.formats` format so its `generate` function can render
|
||||
Home Manager DAG entries in order.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`pkgs` (attribute set)
|
||||
: Package set used to create the generated derivation.
|
||||
|
||||
`format` (attribute set)
|
||||
: Existing format value, for example `pkgs.formats.json { }`.
|
||||
|
||||
`generator` (function)
|
||||
: Text renderer. Receives `{ cycleErrorMessage }` and returns a function
|
||||
from value to rendered text.
|
||||
|
||||
`nativeBuildInputs` (list; optional)
|
||||
: Build inputs needed by `buildCommand`.
|
||||
|
||||
`buildCommand` (string; optional)
|
||||
: Builder script. Defaults to copying rendered text to `$out`.
|
||||
|
||||
`cycleErrorMessage` (string or null; optional)
|
||||
: Message prefix to use when a dependency cycle is detected. When `null`,
|
||||
the generated file name is used.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkDAGOrderedFormat :: { pkgs :: AttrSet; format :: AttrSet; generator :: Function; nativeBuildInputs ? [Derivation]; buildCommand ? String; cycleErrorMessage ? NullOr String; } -> AttrSet
|
||||
```
|
||||
*/
|
||||
mkDAGOrderedFormat = mkDAGOrderedFormat';
|
||||
|
||||
/**
|
||||
Create a JSON format whose `generate` function renders nested ordered Home
|
||||
Manager DAG entries in order.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`pkgs` (attribute set)
|
||||
: Package set used to create the generated derivation.
|
||||
|
||||
`jsonFormat` (attribute set; optional)
|
||||
: Existing JSON format value. Defaults to `pkgs.formats.json { }`.
|
||||
|
||||
`cycleErrorMessage` (string or null; optional)
|
||||
: Message prefix to use when a dependency cycle is detected. When `null`,
|
||||
the generated file name is used.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkDAGOrderedJsonFormat :: { pkgs :: AttrSet; jsonFormat ? AttrSet; cycleErrorMessage ? NullOr String; } -> AttrSet
|
||||
```
|
||||
*/
|
||||
mkDAGOrderedJsonFormat =
|
||||
{
|
||||
pkgs,
|
||||
jsonFormat ? pkgs.formats.json { },
|
||||
cycleErrorMessage ? null,
|
||||
}:
|
||||
mkDAGOrderedFormat' {
|
||||
inherit
|
||||
pkgs
|
||||
cycleErrorMessage
|
||||
;
|
||||
format = jsonFormat;
|
||||
generator = { cycleErrorMessage }: toDAGOrderedJsonText' { inherit cycleErrorMessage; };
|
||||
nativeBuildInputs = [ pkgs.buildPackages.jq ];
|
||||
buildCommand = ''
|
||||
jq . "$valuePath" > "$out"
|
||||
'';
|
||||
};
|
||||
|
||||
/**
|
||||
Create a YAML format whose `generate` function renders nested ordered Home
|
||||
Manager DAG entries in order.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`pkgs` (attribute set)
|
||||
: Package set used to create the generated derivation.
|
||||
|
||||
`yamlFormat` (attribute set; optional)
|
||||
: Existing YAML format value. Defaults to `pkgs.formats.yaml { }`.
|
||||
|
||||
`cycleErrorMessage` (string or null; optional)
|
||||
: Message prefix to use when a dependency cycle is detected. When `null`,
|
||||
the generated file name is used.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkDAGOrderedYamlFormat :: { pkgs :: AttrSet; yamlFormat ? AttrSet; cycleErrorMessage ? NullOr String; } -> AttrSet
|
||||
```
|
||||
*/
|
||||
mkDAGOrderedYamlFormat =
|
||||
{
|
||||
pkgs,
|
||||
yamlFormat ? pkgs.formats.yaml { },
|
||||
cycleErrorMessage ? null,
|
||||
}:
|
||||
mkDAGOrderedFormat' {
|
||||
inherit
|
||||
pkgs
|
||||
cycleErrorMessage
|
||||
;
|
||||
format = yamlFormat;
|
||||
generator = { cycleErrorMessage }: toDAGOrderedJsonText' { inherit cycleErrorMessage; };
|
||||
nativeBuildInputs = [ pkgs.buildPackages.remarshal ];
|
||||
buildCommand = ''
|
||||
json2yaml "$valuePath" "$out"
|
||||
'';
|
||||
};
|
||||
|
||||
/**
|
||||
Create a TOML format whose `generate` function renders nested ordered Home
|
||||
Manager DAG entries in order.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`pkgs` (attribute set)
|
||||
: Package set used to create the generated derivation.
|
||||
|
||||
`tomlFormat` (attribute set; optional)
|
||||
: Existing TOML format value. Defaults to `pkgs.formats.toml { }`.
|
||||
|
||||
`cycleErrorMessage` (string or null; optional)
|
||||
: Message prefix to use when a dependency cycle is detected. When `null`,
|
||||
the generated file name is used.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkDAGOrderedTomlFormat :: { pkgs :: AttrSet; tomlFormat ? AttrSet; cycleErrorMessage ? NullOr String; } -> AttrSet
|
||||
```
|
||||
*/
|
||||
mkDAGOrderedTomlFormat =
|
||||
{
|
||||
pkgs,
|
||||
tomlFormat ? pkgs.formats.toml { },
|
||||
cycleErrorMessage ? null,
|
||||
}:
|
||||
mkDAGOrderedFormat' {
|
||||
inherit
|
||||
pkgs
|
||||
cycleErrorMessage
|
||||
;
|
||||
format = tomlFormat;
|
||||
generator = { cycleErrorMessage }: toDAGOrderedJsonText' { inherit cycleErrorMessage; };
|
||||
nativeBuildInputs = [ pkgs.buildPackages.remarshal ];
|
||||
buildCommand = ''
|
||||
json2toml "$valuePath" "$out"
|
||||
'';
|
||||
};
|
||||
|
||||
/**
|
||||
Create an INI format whose `generate` function renders ordered Home Manager
|
||||
DAG entries in order.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`pkgs` (attribute set)
|
||||
: Package set used to create the generated derivation.
|
||||
|
||||
`iniFormat` (attribute set; optional)
|
||||
: Existing INI format value. Defaults to `pkgs.formats.ini { ... }`.
|
||||
|
||||
`listsAsDuplicateKeys`, `listToValue`, `atomsCoercedToLists`
|
||||
: Format options matching `pkgs.formats.ini`.
|
||||
|
||||
`mkSectionName`, `mkKeyValue`
|
||||
: Format options for rendering section and setting lines.
|
||||
|
||||
`cycleErrorMessage` (string or null; optional)
|
||||
: Message prefix to use when a dependency cycle is detected. When `null`,
|
||||
the generated file name is used.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkDAGOrderedIniFormat :: { pkgs :: AttrSet; iniFormat ? AttrSet; mkSectionName ? Function; mkKeyValue ? Function; listsAsDuplicateKeys ? Bool; listToValue ? NullOr Function; atomsCoercedToLists ? NullOr Bool; cycleErrorMessage ? NullOr String; } -> AttrSet
|
||||
```
|
||||
*/
|
||||
mkDAGOrderedIniFormat =
|
||||
{
|
||||
pkgs,
|
||||
mkSectionName ? (name: lib.escape [ "[" "]" ] name),
|
||||
mkKeyValue ? lib.generators.mkKeyValueDefault { } "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
listToValue ? null,
|
||||
atomsCoercedToLists ? null,
|
||||
iniFormat ? pkgs.formats.ini {
|
||||
inherit
|
||||
atomsCoercedToLists
|
||||
listsAsDuplicateKeys
|
||||
listToValue
|
||||
;
|
||||
},
|
||||
cycleErrorMessage ? null,
|
||||
}:
|
||||
assert listsAsDuplicateKeys -> listToValue == null;
|
||||
mkDAGOrderedFormat' {
|
||||
inherit
|
||||
pkgs
|
||||
cycleErrorMessage
|
||||
;
|
||||
format = iniFormat;
|
||||
generator =
|
||||
{ cycleErrorMessage }:
|
||||
toDAGOrderedINI' {
|
||||
inherit
|
||||
cycleErrorMessage
|
||||
listToValue
|
||||
listsAsDuplicateKeys
|
||||
mkKeyValue
|
||||
mkSectionName
|
||||
;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
Create a key-value format whose `generate` function renders ordered Home
|
||||
Manager DAG entries in order.
|
||||
|
||||
# Inputs
|
||||
|
||||
`options`
|
||||
|
||||
: Function options
|
||||
|
||||
`pkgs` (attribute set)
|
||||
: Package set used to create the generated derivation.
|
||||
|
||||
`keyValueFormat` (attribute set; optional)
|
||||
: Existing key-value format value. Defaults to `pkgs.formats.keyValue`.
|
||||
|
||||
`listsAsDuplicateKeys`, `listToValue`
|
||||
: Format options matching `pkgs.formats.keyValue`.
|
||||
|
||||
`mkKeyValue`
|
||||
: Format option for rendering setting lines.
|
||||
|
||||
`cycleErrorMessage` (string or null; optional)
|
||||
: Message prefix to use when a dependency cycle is detected. When `null`,
|
||||
the generated file name is used.
|
||||
|
||||
# Type
|
||||
|
||||
```
|
||||
mkDAGOrderedKeyValueFormat :: { pkgs :: AttrSet; keyValueFormat ? AttrSet; mkKeyValue ? Function; listsAsDuplicateKeys ? Bool; listToValue ? NullOr Function; cycleErrorMessage ? NullOr String; } -> AttrSet
|
||||
```
|
||||
*/
|
||||
mkDAGOrderedKeyValueFormat =
|
||||
{
|
||||
pkgs,
|
||||
mkKeyValue ? lib.generators.mkKeyValueDefault { } "=",
|
||||
listsAsDuplicateKeys ? false,
|
||||
listToValue ? null,
|
||||
keyValueFormat ? pkgs.formats.keyValue {
|
||||
inherit listsAsDuplicateKeys listToValue;
|
||||
},
|
||||
cycleErrorMessage ? null,
|
||||
}:
|
||||
assert listsAsDuplicateKeys -> listToValue == null;
|
||||
mkDAGOrderedFormat' {
|
||||
inherit
|
||||
pkgs
|
||||
cycleErrorMessage
|
||||
;
|
||||
format = keyValueFormat;
|
||||
generator =
|
||||
{ cycleErrorMessage }:
|
||||
toDAGOrderedKeyValue' {
|
||||
inherit
|
||||
cycleErrorMessage
|
||||
listToValue
|
||||
listsAsDuplicateKeys
|
||||
mkKeyValue
|
||||
;
|
||||
};
|
||||
};
|
||||
|
||||
toHyprconf =
|
||||
{
|
||||
attrs,
|
||||
@@ -10,11 +688,11 @@
|
||||
let
|
||||
inherit (lib)
|
||||
all
|
||||
any
|
||||
concatMapStringsSep
|
||||
concatStrings
|
||||
concatStringsSep
|
||||
filterAttrs
|
||||
foldl
|
||||
generators
|
||||
hasPrefix
|
||||
isAttrs
|
||||
@@ -29,8 +707,7 @@
|
||||
toHyprconf' =
|
||||
indent: attrs:
|
||||
let
|
||||
isImportantField =
|
||||
n: _: foldl (acc: prev: if hasPrefix prev n then true else acc) false importantPrefixes;
|
||||
isImportantField = n: _: any (prev: hasPrefix prev n) importantPrefixes;
|
||||
importantFields = filterAttrs isImportantField attrs;
|
||||
withoutImportantFields = fields: removeAttrs fields (attrNames importantFields);
|
||||
|
||||
|
||||
@@ -91,6 +91,12 @@
|
||||
github = "b1kku";
|
||||
githubId = 77858854;
|
||||
};
|
||||
bittner = {
|
||||
name = "Peter Bittner";
|
||||
email = "peter@painless.software";
|
||||
github = "bittner";
|
||||
githubId = 665072;
|
||||
};
|
||||
bjpbakker = {
|
||||
email = "bart@thesoftwarecraft.com";
|
||||
github = "bjpbakker";
|
||||
@@ -115,6 +121,12 @@
|
||||
github = "ckgxrg-salt";
|
||||
githubId = 165614491;
|
||||
};
|
||||
csanthiago = {
|
||||
name = "csanthiago";
|
||||
email = "git@csanthiago.dev";
|
||||
github = "csanthiago";
|
||||
githubId = 8346803;
|
||||
};
|
||||
d-dervishi = {
|
||||
email = "david.dervishi@epfl.ch";
|
||||
github = "d-dervishi";
|
||||
@@ -175,6 +187,12 @@
|
||||
github = "epixtm";
|
||||
githubId = 168025607;
|
||||
};
|
||||
ErinaYip = {
|
||||
name = "erina";
|
||||
email = "erinayip@outlook.com";
|
||||
github = "ErinaYip";
|
||||
githubId = 227132255;
|
||||
};
|
||||
exzombie = {
|
||||
name = "Jure Varlec";
|
||||
email = "jure@varlec.si";
|
||||
@@ -302,6 +320,12 @@
|
||||
github = "kalhauge";
|
||||
githubId = 1182166;
|
||||
};
|
||||
kayskayskays = {
|
||||
name = "Kays";
|
||||
email = "115312476+kayskayskays@users.noreply.github.com";
|
||||
github = "kayskayskays";
|
||||
githubId = 115312476;
|
||||
};
|
||||
kmaasrud = {
|
||||
name = "Knut Magnus Aasrud";
|
||||
email = "km@aasrud.com";
|
||||
@@ -523,6 +547,12 @@
|
||||
github = "rszamszur";
|
||||
githubId = 10353018;
|
||||
};
|
||||
semi710 = {
|
||||
name = "Nikhil Singh";
|
||||
email = "nik.singh710@gmail.com";
|
||||
github = "semi710";
|
||||
githubId = 60490474;
|
||||
};
|
||||
silmarp = {
|
||||
name = "Silmar Pereira da Silva Junior";
|
||||
email = "silmarjr2@gmail.com";
|
||||
@@ -540,6 +570,12 @@
|
||||
github = "SunOfLife1";
|
||||
githubId = 30405063;
|
||||
};
|
||||
superflash41 = {
|
||||
name = "Saymon Nicho";
|
||||
email = "saymon.nicho@pucp.edu.pe";
|
||||
github = "superflash41";
|
||||
githubId = 102434258;
|
||||
};
|
||||
yarn = {
|
||||
name = "yarncat";
|
||||
email = "30006414+yaaaarn@users.noreply.github.com";
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
{ lib }:
|
||||
|
||||
let
|
||||
/*
|
||||
Checks whether a value is a file reference submodule.
|
||||
|
||||
A file reference has the shape `{ file = "/path/to/file"; }` and is used
|
||||
to indicate that an environment variable's value should be read from a file
|
||||
at runtime (e.g. via sops-nix or systemd credentials).
|
||||
|
||||
Type: isFileRef :: Value -> Bool
|
||||
*/
|
||||
isFileRef = value: lib.isAttrs value && value ? file;
|
||||
|
||||
/*
|
||||
Filters an `env` attrset to contain only literal (non-file-ref) values.
|
||||
|
||||
This is useful when a transform needs to produce two separate outputs:
|
||||
one with the literal values and one with the file paths (for wrapping).
|
||||
|
||||
Type: literalEnv :: AttrSet -> AttrSet
|
||||
|
||||
Example:
|
||||
literalEnv { API_KEY = "secret"; TOKEN.file = "/run/secrets/token"; }
|
||||
=> { API_KEY = "secret"; }
|
||||
*/
|
||||
literalEnv = env: lib.filterAttrs (_: value: !isFileRef value) env;
|
||||
|
||||
/*
|
||||
Extracts only the file paths from file-ref entries in an `env` attrset.
|
||||
|
||||
Returns a flat `attrsOf str` mapping variable names to their file paths.
|
||||
Non-file-ref entries are excluded.
|
||||
|
||||
Type: fileRefEnv :: AttrSet -> AttrSet
|
||||
|
||||
Example:
|
||||
fileRefEnv { API_KEY = "secret"; TOKEN.file = "/run/secrets/token"; }
|
||||
=> { TOKEN = "/run/secrets/token"; }
|
||||
*/
|
||||
fileRefEnv = env: lib.mapAttrs (_: value: value.file) (lib.filterAttrs (_: isFileRef) env);
|
||||
|
||||
/*
|
||||
Render an `env` attrset (literals + file refs) as a flat `attrsOf str`
|
||||
by mapping file refs through `mkFileRef path` and leaving literals untouched.
|
||||
|
||||
Type: renderEnv :: (String -> String) -> AttrSet -> AttrSet
|
||||
|
||||
Example:
|
||||
renderEnv (path: "{file:${path}}") {
|
||||
API_KEY = "literal";
|
||||
SESSION_TOKEN.file = "/run/secrets/token";
|
||||
}
|
||||
=> { API_KEY = "literal"; SESSION_TOKEN = "{file:/run/secrets/token}"; }
|
||||
*/
|
||||
renderEnv =
|
||||
mkFileRef: env:
|
||||
lib.mapAttrs (_: value: if isFileRef value then mkFileRef value.file else value) env;
|
||||
|
||||
/*
|
||||
Wrap a local MCP server command in a shell script that reads file-backed
|
||||
env values into the environment before executing the original process.
|
||||
Compatible with file-based secret managers such as sops-nix or systemd
|
||||
credentials. Reads file refs from `server.env`.
|
||||
|
||||
Type: mkEnvFilesWrapper :: { pkgs, name, server } -> Derivation
|
||||
*/
|
||||
mkEnvFilesWrapper =
|
||||
{
|
||||
pkgs,
|
||||
name,
|
||||
server,
|
||||
}:
|
||||
let
|
||||
files = fileRefEnv (server.env or { });
|
||||
in
|
||||
pkgs.writeShellScript "mcp-${name}-wrapper" ''
|
||||
${lib.concatStrings (
|
||||
lib.mapAttrsToList (var: path: ''
|
||||
if ${var}=$(cat ${lib.escapeShellArg path}); then
|
||||
export ${var}
|
||||
else
|
||||
printf '[${name} wrapper ] Failed to read env var %s from %s\n' \
|
||||
${lib.escapeShellArg var} \
|
||||
${lib.escapeShellArg path} >&2
|
||||
fi
|
||||
'') files
|
||||
)}
|
||||
exec ${lib.escapeShellArgs ([ server.command ] ++ (server.args or [ ]))}
|
||||
'';
|
||||
|
||||
/*
|
||||
extraTransform factory: when the server has file refs in `env` and a
|
||||
local `command`, replace `command` with a wrapper script that reads
|
||||
those files at startup, reset `args` to `[ ]`, and drop the file refs
|
||||
from `env` (leaving only literal entries).
|
||||
|
||||
When there is nothing to wrap, returns the server unchanged.
|
||||
|
||||
Type: wrapEnvFilesCommand :: { pkgs, name } -> AttrSet -> AttrSet
|
||||
*/
|
||||
wrapEnvFilesCommand =
|
||||
{ pkgs, name }:
|
||||
server:
|
||||
let
|
||||
files = fileRefEnv (server.env or { });
|
||||
needsWrapping = files != { };
|
||||
in
|
||||
server
|
||||
// lib.optionalAttrs needsWrapping {
|
||||
command = mkEnvFilesWrapper { inherit pkgs name server; };
|
||||
args = [ ];
|
||||
env = literalEnv (server.env or { });
|
||||
};
|
||||
|
||||
/*
|
||||
Resolve the effective `enabled` state from a server config that may
|
||||
have either an `enabled` or a `disabled` field (but not both).
|
||||
|
||||
Returns `null` if neither field is present, allowing callers to omit
|
||||
the attribute from the output via the empty-value filter.
|
||||
|
||||
Type: resolveEnabled :: AttrSet -> Null | Bool
|
||||
*/
|
||||
resolveEnabled =
|
||||
server:
|
||||
let
|
||||
hasEnabled = server ? enabled && server.enabled != null;
|
||||
hasDisabled = server ? disabled && server.disabled != null;
|
||||
in
|
||||
if hasEnabled then
|
||||
server.enabled
|
||||
else if hasDisabled then
|
||||
!server.disabled
|
||||
else
|
||||
null;
|
||||
|
||||
/*
|
||||
extraTransform that adds `type = "stdio" | "http"` based on whether the
|
||||
server has a `url`.
|
||||
|
||||
Type: addType :: AttrSet -> AttrSet
|
||||
*/
|
||||
addType =
|
||||
server:
|
||||
if server ? type then
|
||||
server
|
||||
else
|
||||
server
|
||||
// {
|
||||
type = if server ? url && server.url != null then "http" else "stdio";
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit
|
||||
renderEnv
|
||||
mkEnvFilesWrapper
|
||||
wrapEnvFilesCommand
|
||||
addType
|
||||
;
|
||||
|
||||
/*
|
||||
Normalise an MCP server attribute set for consumption by a client
|
||||
program. Performs only universal steps:
|
||||
|
||||
1. Resolve `enabled` from optional `enabled`/`disabled` fields.
|
||||
2. Apply each function in `extraTransforms` to the server attrs, in
|
||||
order. Transforms run before `mkFileRef`, `exclude` and the
|
||||
empty-value filter, so a transform can still read attrs that will
|
||||
be excluded and operate on the raw env shape (with file refs).
|
||||
3. Render any remaining file refs in `env` through `mkFileRef path`.
|
||||
Callers that consume file refs themselves (e.g. via
|
||||
`wrapEnvFilesCommand`) will have stripped them in an
|
||||
extraTransform, so `mkFileRef` is a no-op for them.
|
||||
4. Remove `disabled` and any keys listed in `exclude`.
|
||||
5. Filter `null`, `[]`, and `{}` values.
|
||||
|
||||
Type: transformMcpServer ::
|
||||
{ server, exclude?, extraTransforms?, mkFileRef? }
|
||||
-> AttrSet
|
||||
*/
|
||||
transformMcpServer =
|
||||
{
|
||||
server,
|
||||
exclude ? [ ],
|
||||
extraTransforms ? [ ],
|
||||
mkFileRef ? (path: "{file:${path}}"),
|
||||
}:
|
||||
let
|
||||
enabled = resolveEnabled server;
|
||||
normalised = server // {
|
||||
inherit enabled;
|
||||
url = if (server.url or null) != null then server.url else server.serverUrl or null;
|
||||
};
|
||||
transformed = lib.foldl' (acc: transform: transform acc) normalised extraTransforms;
|
||||
withRenderedEnv =
|
||||
transformed
|
||||
// lib.optionalAttrs (transformed ? env) {
|
||||
env = renderEnv mkFileRef transformed.env;
|
||||
};
|
||||
cleaned = removeAttrs withRenderedEnv (
|
||||
[
|
||||
"disabled"
|
||||
"serverUrl"
|
||||
]
|
||||
++ exclude
|
||||
);
|
||||
in
|
||||
lib.filterAttrs (_: value: value != null && value != [ ] && value != { }) cleaned;
|
||||
}
|
||||
@@ -87,4 +87,11 @@ rec {
|
||||
# Returns true for strings like `SCREAMING_SNAKE_CASE`, `SOME_CONSTANT`.
|
||||
# Must be all uppercase letters/numbers, with words separated by single underscores.
|
||||
isScreamingSnakeCase = str: builtins.match "^[A-Z0-9]+(_[A-Z0-9]+)*$" str != null;
|
||||
|
||||
# Check if the content is path-like (a path, a Nix store path string, or a derivation).
|
||||
isPathLike =
|
||||
content:
|
||||
lib.isPath content
|
||||
|| (builtins.isString content && lib.hasPrefix "${builtins.storeDir}/" content)
|
||||
|| lib.isDerivation content;
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ rec {
|
||||
if builtins.isBool v then
|
||||
if v then "true" else "false"
|
||||
else if builtins.isString v then
|
||||
''"${v}"''
|
||||
''"${lib.escape [ "\\" "\"" "`" ] v}"''
|
||||
else if builtins.isList v then
|
||||
let
|
||||
shell = import ./shell.nix { inherit lib; };
|
||||
|
||||
+206
-124
@@ -14,38 +14,141 @@ let
|
||||
|
||||
cfg = config.fonts.fontconfig;
|
||||
|
||||
inherit (config.home) profileDirectory;
|
||||
xml = pkgs.formats.xml { };
|
||||
|
||||
globalConfig = config;
|
||||
fontConfigFileType = lib.types.submodule (
|
||||
{ name, ... }:
|
||||
let
|
||||
noteContentOptionsExclusivity = ''
|
||||
Note that {option}`source` will be derived from {option}`settings` and {option}`text` if they are
|
||||
set; with override priorities being preserved. As a consequence, exactly
|
||||
one of {option}`settings`, {option}`text`, and {option}`source` must be set with highest priority.
|
||||
'';
|
||||
in
|
||||
{
|
||||
config,
|
||||
name,
|
||||
options,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options = {
|
||||
enable = lib.mkEnableOption "Whether this font config file should be generated.";
|
||||
text = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.lines;
|
||||
default = null;
|
||||
description = "Verbatim contents of the config file. If this option is null then the 'source' option must be set.";
|
||||
};
|
||||
source = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
description = "Config file to source. Alternatively, use the 'text' option instead.";
|
||||
enable = lib.mkOption {
|
||||
description = ''
|
||||
Whether this file should be generated. This option allows specific
|
||||
files to be disabled.
|
||||
'';
|
||||
type = lib.types.bool;
|
||||
inherit
|
||||
(lib.hm.deprecations.mkStateVersionOptionDefault {
|
||||
inherit (globalConfig.home) stateVersion;
|
||||
since = "26.11";
|
||||
optionPath = [
|
||||
"fonts"
|
||||
"fontconfig"
|
||||
"configFile"
|
||||
name
|
||||
"enable"
|
||||
];
|
||||
legacy.value = false;
|
||||
current.value = true;
|
||||
})
|
||||
default
|
||||
defaultText
|
||||
;
|
||||
};
|
||||
label = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = name;
|
||||
defaultText = "<name>";
|
||||
description = "Label to use for the name of the config file.";
|
||||
default = name;
|
||||
defaultText = lib.literalExpression "<name>";
|
||||
type = lib.types.str;
|
||||
};
|
||||
priority = lib.mkOption {
|
||||
type = lib.types.ints.between 0 99;
|
||||
default = 90;
|
||||
description = ''
|
||||
Determines the order in which configs are loaded.
|
||||
Must be a value within the range of 0-99, where priority 0 is the highest priority and 99 is the lowest.
|
||||
Determines the order in which configuration files are loaded.
|
||||
|
||||
Must be a value within the range of 0-99, where priority 0 is the
|
||||
highest priority and 99 is the lowest.
|
||||
'';
|
||||
default = 90;
|
||||
type = lib.types.ints.between 0 99;
|
||||
};
|
||||
target = lib.mkOption {
|
||||
description = ''
|
||||
Path to the target file relative to
|
||||
{file}`''${config.xdg.configHome}/fontconfig/conf.d/`.
|
||||
'';
|
||||
default = "${toString config.priority}-hm-${config.label}.conf";
|
||||
defaultText = ''"''${toString config.priority}-hm-''${config.label}.conf"'';
|
||||
type = lib.types.nonEmptyStr;
|
||||
};
|
||||
settings = lib.mkOption {
|
||||
inherit (xml) type;
|
||||
default = { };
|
||||
example = {
|
||||
description = "Enable antialiasing";
|
||||
match = {
|
||||
"@target" = "font";
|
||||
edit = {
|
||||
"@mode" = "assign";
|
||||
"@name" = "antialias";
|
||||
bool = "true";
|
||||
};
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
Structured attrs of settings in the format of `pkgs.format.xml {}`. The
|
||||
settings are implicitly wrapped inside the `<fontconfig>` tag, i.e. as
|
||||
`{ fontconfig = <settings>; }`.
|
||||
''
|
||||
+ noteContentOptionsExclusivity;
|
||||
};
|
||||
text = lib.mkOption {
|
||||
description = ''
|
||||
Verbatim contents of the config file.
|
||||
''
|
||||
+ noteContentOptionsExclusivity;
|
||||
default = null;
|
||||
type = with lib.types; nullOr lines;
|
||||
};
|
||||
source = lib.mkOption {
|
||||
description = ''
|
||||
Path to the source file.
|
||||
''
|
||||
+ noteContentOptionsExclusivity;
|
||||
type =
|
||||
let
|
||||
thisOption = lib.showOption [
|
||||
"fonts"
|
||||
"fontconfig"
|
||||
"configFile"
|
||||
name
|
||||
"source"
|
||||
];
|
||||
abortOnNull =
|
||||
x:
|
||||
if x != null then
|
||||
x
|
||||
else
|
||||
abort ''
|
||||
Setting '${thisOption}' to null is not
|
||||
required anymore and disallowed. You can safely remove this definition.
|
||||
'';
|
||||
in
|
||||
with lib.types;
|
||||
coercedTo (nullOr path) abortOnNull path;
|
||||
};
|
||||
};
|
||||
config.source =
|
||||
let
|
||||
fileName = lib.hm.strings.storeFileName "hm-fontconfig-${config.label}.xml";
|
||||
mkSettingsFile = settings: xml.generate fileName { fontconfig = settings; };
|
||||
mkTextFile = pkgs.writeText fileName;
|
||||
in
|
||||
lib.mkMerge [
|
||||
(lib.mkIf (config.settings != { }) (lib.mkDerivedConfig options.settings mkSettingsFile))
|
||||
(lib.mkIf (config.text != null) (lib.mkDerivedConfig options.text mkTextFile))
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
@@ -175,13 +278,12 @@ in
|
||||
type = lib.types.attrsOf fontConfigFileType;
|
||||
default = { };
|
||||
description = ''
|
||||
Extra font config files that will be added to `~/.config/fontconfig/conf.d/`.
|
||||
Files are named like `fontconfig/conf.d/{priority}-{label}.conf`.
|
||||
Config files that will be installed to {file}`~/.config/fontconfig/conf.d/`.
|
||||
'';
|
||||
example = {
|
||||
tamzen = {
|
||||
tamzen-disable-antialiasing = {
|
||||
enable = true;
|
||||
label = "tamzen-disable-antialiasing";
|
||||
priority = 90;
|
||||
text = ''
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
@@ -198,13 +300,12 @@ in
|
||||
</match>
|
||||
</fontconfig>
|
||||
'';
|
||||
priority = 90;
|
||||
}; # => conf.d/90-tamzen-disable-antialiasing.conf
|
||||
};
|
||||
commit-mono-options = {
|
||||
enable = true;
|
||||
source = "./resources/fontconfig/commit-mono.conf";
|
||||
priority = 80;
|
||||
}; # => conf.d/80-commit-mono-options.conf
|
||||
source = "./resources/fontconfig/commit-mono.conf";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
@@ -248,112 +349,93 @@ in
|
||||
fi
|
||||
'';
|
||||
|
||||
fonts.fontconfig.configFile =
|
||||
let
|
||||
mkFontconfigConf = conf: ''
|
||||
<?xml version='1.0'?>
|
||||
|
||||
<!-- Generated by Home Manager. -->
|
||||
|
||||
<!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
|
||||
<fontconfig>
|
||||
${conf}
|
||||
</fontconfig>
|
||||
'';
|
||||
in
|
||||
{
|
||||
fonts = {
|
||||
fonts.fontconfig.configFile = {
|
||||
fonts =
|
||||
let
|
||||
mkInclude = path: {
|
||||
"@ignore_missing" = "yes";
|
||||
"#text" = path;
|
||||
};
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
priority = 10;
|
||||
source = null; # Set the source as null explicitly so that it cannot be overwritten by mistake by a user
|
||||
text = mkFontconfigConf ''
|
||||
<description>Add fonts in the Nix user profile</description>
|
||||
|
||||
<include ignore_missing="yes">${config.home.path}/etc/fonts/conf.d</include>
|
||||
<include ignore_missing="yes">${config.home.path}/etc/fonts/fonts.conf</include>
|
||||
|
||||
<dir>${config.home.path}/lib/X11/fonts</dir>
|
||||
<dir>${config.home.path}/share/fonts</dir>
|
||||
<dir>${profileDirectory}/lib/X11/fonts</dir>
|
||||
<dir>${profileDirectory}/share/fonts</dir>
|
||||
|
||||
<cachedir>${config.home.path}/lib/fontconfig/cache</cachedir>
|
||||
'';
|
||||
settings = {
|
||||
description = "Add fonts in the Nix user profile";
|
||||
include = map mkInclude [
|
||||
"${config.home.path}/etc/fonts/conf.d"
|
||||
"${config.home.path}/etc/fonts/fonts.conf"
|
||||
];
|
||||
dir = [
|
||||
"${config.home.path}/lib/X11/fonts"
|
||||
"${config.home.path}/share/fonts"
|
||||
"${config.home.profileDirectory}/lib/X11/fonts"
|
||||
"${config.home.profileDirectory}/share/fonts"
|
||||
];
|
||||
cachedir = "${config.home.path}/lib/fontconfig/cache";
|
||||
};
|
||||
};
|
||||
rendering =
|
||||
let
|
||||
set =
|
||||
name: value:
|
||||
let
|
||||
xmlValue =
|
||||
if builtins.isBool value then
|
||||
"<bool>${lib.boolToString value}</bool>"
|
||||
else if builtins.isString value then
|
||||
"<const>${value}</const>"
|
||||
else
|
||||
throw "expected bool or string but got ${builtins.typeOf value}: ${toString value}";
|
||||
in
|
||||
''
|
||||
<match target="font">
|
||||
<edit mode="assign" name="${name}">
|
||||
${xmlValue}
|
||||
</edit>
|
||||
</match>
|
||||
'';
|
||||
content =
|
||||
lib.optional (cfg.antialiasing != null) (set "antialias" cfg.antialiasing)
|
||||
++ lib.optionals (cfg.hinting != null) [
|
||||
(set "hinting" true)
|
||||
(set "hintstyle" ("hint" + cfg.hinting))
|
||||
]
|
||||
++ lib.optional (cfg.subpixelRendering != null) (
|
||||
set "rgba" (builtins.replaceStrings [ "ertical-" ] [ "" ] cfg.subpixelRendering)
|
||||
);
|
||||
in
|
||||
{
|
||||
enable = builtins.length content > 0;
|
||||
priority = 10;
|
||||
source = null;
|
||||
text = mkFontconfigConf (
|
||||
lib.concatStrings ([ "<description>Set the rendering mode</description>\n" ] ++ content)
|
||||
rendering =
|
||||
let
|
||||
toXmlValue =
|
||||
value:
|
||||
if lib.isBool value then
|
||||
{ bool = lib.boolToString value; }
|
||||
else if lib.isString value then
|
||||
{ const = value; }
|
||||
else
|
||||
throw "expected bool or string but got ${lib.typeOf value}: ${toString value}";
|
||||
assign =
|
||||
name: value:
|
||||
toXmlValue value
|
||||
// {
|
||||
"@mode" = "assign";
|
||||
"@name" = name;
|
||||
};
|
||||
content =
|
||||
lib.optional (cfg.antialiasing != null) (assign "antialias" cfg.antialiasing)
|
||||
++ lib.optionals (cfg.hinting != null) [
|
||||
(assign "hinting" true)
|
||||
(assign "hintstyle" ("hint" + cfg.hinting))
|
||||
]
|
||||
++ lib.optional (cfg.subpixelRendering != null) (
|
||||
assign "rgba" (lib.replaceStrings [ "ertical-" ] [ "" ] cfg.subpixelRendering)
|
||||
);
|
||||
in
|
||||
{
|
||||
enable = lib.length content > 0;
|
||||
priority = 10;
|
||||
settings = {
|
||||
description = "Set the rendering mode";
|
||||
match = {
|
||||
"@target" = "font";
|
||||
edit = content;
|
||||
};
|
||||
};
|
||||
default-fonts =
|
||||
let
|
||||
genDefault =
|
||||
fonts: name:
|
||||
lib.optionalString (fonts != [ ]) ''
|
||||
<alias binding="same">
|
||||
<family>${name}</family>
|
||||
<prefer>
|
||||
${lib.concatStringsSep "" (
|
||||
map (font: ''
|
||||
<family>${font}</family>
|
||||
'') fonts
|
||||
)}
|
||||
</prefer>
|
||||
</alias>
|
||||
'';
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
priority = 52;
|
||||
source = null;
|
||||
text = mkFontconfigConf ''
|
||||
<!-- Default fonts -->
|
||||
${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
|
||||
${genDefault cfg.defaultFonts.serif "serif"}
|
||||
${genDefault cfg.defaultFonts.monospace "monospace"}
|
||||
${genDefault cfg.defaultFonts.emoji "emoji"}
|
||||
'';
|
||||
};
|
||||
default-fonts =
|
||||
let
|
||||
filterNonEmpty = lib.filterAttrs (_: fonts: fonts != [ ]);
|
||||
mkAlias = name: fonts: {
|
||||
"@binding" = "same";
|
||||
family = if name == "sansSerif" then "sans-serif" else name;
|
||||
prefer.family = fonts;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
priority = 52;
|
||||
settings = {
|
||||
description = "Set default fonts";
|
||||
alias = lib.mapAttrsToList mkAlias (filterNonEmpty cfg.defaultFonts);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
xdg.configFile = lib.mapAttrs' (
|
||||
_name: config:
|
||||
lib.nameValuePair "fontconfig/conf.d/${toString config.priority}-hm-${config.label}.conf" {
|
||||
inherit (config) enable text;
|
||||
source = lib.mkIf (config.source != null) config.source;
|
||||
lib.nameValuePair "fontconfig/conf.d/${config.target}" {
|
||||
inherit (config) enable source;
|
||||
}
|
||||
) cfg.configFile;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ let
|
||||
cfg3 = config.gtk.gtk3;
|
||||
cfg4 = config.gtk.gtk4;
|
||||
|
||||
gtkLib = import ./gtk/lib.nix { inherit lib; };
|
||||
gtkLib = import ./lib.nix { inherit lib; };
|
||||
|
||||
themeType = gtkLib.mkThemeType {
|
||||
typeName = "theme";
|
||||
@@ -41,9 +41,9 @@ in
|
||||
];
|
||||
|
||||
imports = [
|
||||
./gtk/gtk2.nix
|
||||
./gtk/gtk3.nix
|
||||
./gtk/gtk4.nix
|
||||
./gtk2.nix
|
||||
./gtk3.nix
|
||||
./gtk4.nix
|
||||
];
|
||||
|
||||
options.gtk = {
|
||||
@@ -1,7 +1,7 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-03-13T03:09:51+00:00";
|
||||
condition = config.programs.gemini-cli.enable;
|
||||
condition = config.programs.antigravity-cli.enable;
|
||||
message = ''
|
||||
The `programs.gemini-cli.policies` option has been added to support configuring
|
||||
the Gemini CLI policy engine.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-03-31T17:22:36+00:00";
|
||||
condition = config.programs.gemini-cli.enable;
|
||||
condition = config.programs.antigravity-cli.enable;
|
||||
message = ''
|
||||
The `programs.gemini-cli.enableMcpIntegration` and `programs.gemini-cli.skills`
|
||||
options have been added to support configuring the Gemini CLI MCP servers and
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
config.programs.claude-code.enable
|
||||
|| config.programs.codex.enable
|
||||
|| config.programs.opencode.enable
|
||||
|| config.programs.gemini-cli.enable;
|
||||
|| config.programs.antigravity-cli.enable;
|
||||
message = ''
|
||||
The Claude Code, Codex, and OpenCode modules now expose a canonical
|
||||
`context` option for configuring their global assistant instructions.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
time = "2026-04-15T11:57:57+00:00";
|
||||
condition = pkgs.stdenv.hostPlatform.isDarwin;
|
||||
message = ''
|
||||
A new module is available: `programs.macos-terminal`.
|
||||
|
||||
This module allows for configuration of preferences for the macOS
|
||||
Terminal.app application.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
time = "2026-04-26T12:42:03+00:00";
|
||||
condition =
|
||||
let
|
||||
extraConfigFiles = lib.removeAttrs config.fonts.fontconfig.configFile [
|
||||
"fonts"
|
||||
"rendering"
|
||||
"default-fonts"
|
||||
];
|
||||
in
|
||||
config.fonts.fontconfig.enable && extraConfigFiles != { };
|
||||
message = ''
|
||||
There is a new `fonts.fontconfig.configFile.<name>.settings` option to
|
||||
define Fontconfig configuration files via a structured attrs in the
|
||||
format of `pkgs.formats.xml {}`.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
time = "2026-05-14T00:00:00+00:00";
|
||||
condition = config.wayland.windowManager.hyprland.enable;
|
||||
message = ''
|
||||
A new option `wayland.windowManager.hyprland.extraLuaFiles` is available for
|
||||
managing additional Lua files under `$XDG_CONFIG_HOME/hypr` when using
|
||||
`wayland.windowManager.hyprland.configType = "lua"`.
|
||||
|
||||
Files can be provided as paths or strings. Attribute names are treated as
|
||||
Lua module names, so `lib.helpers` writes `lib/helpers.lua`, and can be
|
||||
automatically loaded from the generated `hyprland.lua` with `require(...)`.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-05-18T17:00:00+00:00";
|
||||
condition = config.programs.zsh.enable;
|
||||
message = ''
|
||||
The `programs.zsh` module now sources Home Manager session variables from
|
||||
`.zprofile` for login shells. Non-login shells continue to source them from
|
||||
`.zshenv`.
|
||||
|
||||
As a result, Home Manager now always writes `.zprofile` when
|
||||
`programs.zsh.enable` is set, instead of only writing it when
|
||||
`programs.zsh.profileExtra` is configured.
|
||||
|
||||
This keeps `home.sessionPath` entries from being overwritten or reordered
|
||||
by system login startup files such as NixOS' `/etc/zprofile` or macOS'
|
||||
`path_helper`.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-05-20T12:00:00+00:00";
|
||||
condition = config.programs.antigravity-cli.enable;
|
||||
message = ''
|
||||
The `programs.gemini-cli` module has been renamed to
|
||||
`programs.antigravity-cli`.
|
||||
|
||||
Existing `programs.gemini-cli` configurations are migrated automatically
|
||||
and emit rename warnings. The Antigravity CLI module writes settings and
|
||||
permissions to `~/.gemini/antigravity-cli`, MCP server configuration to
|
||||
`~/.gemini/config/mcp_config.json`, and skills to
|
||||
`~/.gemini/antigravity-cli/skills`.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-05-22T20:55:03+00:00";
|
||||
condition = config.programs.mcp.enable;
|
||||
message = ''
|
||||
The {option}`programs.mcp.servers` schema is now typed.
|
||||
|
||||
Instead of freeform JSON objects, structured options are used:
|
||||
{option}`command` and {option}`args` for local servers,
|
||||
{option}`url` for remote servers,
|
||||
{option}`env` for environment variables.
|
||||
|
||||
Environment variables can be specified as literal strings or as file
|
||||
references that are read at startup:
|
||||
|
||||
```nix
|
||||
env.API_KEY = "literal";
|
||||
env.SESSION_TOKEN.file = "/run/secrets/token";
|
||||
```
|
||||
|
||||
The {var}`lib.hm.mcp` library provides helpers for transforming MCP
|
||||
server configurations and is used by opencode, claude-code, codex,
|
||||
antigravity-cli, zed-editor, and vscode for MCP integration.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-02T12:00:00+00:00";
|
||||
condition = config.programs.pi-coding-agent.enable;
|
||||
message = ''
|
||||
A new module is available: `programs.pi-coding-agent`.
|
||||
|
||||
The module supports declarative configuration of Pi Coding Agent
|
||||
including settings, keybindings, models, and global context
|
||||
(AGENTS.md).
|
||||
|
||||
For MCP server support, use the existing `programs.mcp` module
|
||||
which writes to `~/.config/mcp/mcp.json`.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-02T15:44:16+00:00";
|
||||
condition =
|
||||
config.services.gpg-agent.enable
|
||||
|| config.services.proton-pass-agent.enable
|
||||
|| config.services.ssh-agent.enable
|
||||
|| config.services.ssh-tpm-agent.enable
|
||||
|| config.services.yubikey-agent.enable;
|
||||
message = ''
|
||||
A new module is available: 'sshAuthSock'.
|
||||
|
||||
It takes care of setting the `SSH_AUTH_SOCK` environment variable
|
||||
properly and is implicitly enabled and configured by SSH agent modules.
|
||||
You can use the 'sshAuthSock.systemd.socketProviderUnit' option to make
|
||||
SSH agents accessible to systemd-managed applications.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-03T14:14:24+00:00";
|
||||
condition = config.programs.prismlauncher.enable;
|
||||
message = ''
|
||||
`programs.prismlauncher` now supports managing Prism Launcher
|
||||
themes through {option}`programs.prismlauncher.themes`.
|
||||
|
||||
Themes can be configured either as paths to complete theme directories, or
|
||||
as attribute sets used to generate `theme.json` and optionally
|
||||
`themeStyle.css`.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-04T17:10:21+00:00";
|
||||
condition = config.programs.zellij.enable;
|
||||
message = ''
|
||||
Added support for Zellij plugins using `programs.zellij.plugins`.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-08T12:00:00+00:00";
|
||||
condition = config.programs.codex.enable;
|
||||
message = ''
|
||||
The `programs.codex.profiles` option was added to manage Codex CLI
|
||||
profile files under `CODEX_HOME`.
|
||||
|
||||
These files are selected with `codex --profile <name>`, matching the
|
||||
profile behavior used by Codex 0.134.0 and later.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-09T12:00:00+00:00";
|
||||
condition = config.programs.kiro-cli.enable;
|
||||
message = ''
|
||||
A new module is available: `programs.kiro-cli`.
|
||||
|
||||
It installs the kiro-cli package and provides optional shell
|
||||
integration for Bash and Zsh.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-10T15:35:30+00:00";
|
||||
condition = config.qt.enable;
|
||||
message = ''
|
||||
The value `gtk` for `qt.platformTheme.name` is no longer rewritten to
|
||||
`gtk2` when setting the `QT_QPA_PLATFORMTHEME` session variable. It is
|
||||
now emitted as `gtk3`, selecting Qt's built-in GTK platform theme plugin,
|
||||
and no longer installs the `qtstyleplugins` and `qt6gtk2`
|
||||
packages.
|
||||
|
||||
If you relied on the previous behavior, set
|
||||
`qt.platformTheme.name = "gtk2"` explicitly, which sets
|
||||
`QT_QPA_PLATFORMTHEME=gtk2` and installs the `qtstyleplugins` and
|
||||
`qt6gtk2` packages providing that platform theme.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-10T16:05:00+00:00";
|
||||
condition = config.programs.difftastic.enable;
|
||||
message = ''
|
||||
The git integration of 'programs.difftastic' now uses a new option
|
||||
'programs.difftastic.git.mode' to select how difftastic is wired into
|
||||
git:
|
||||
|
||||
- "external" (default): set 'diff.external' so 'git diff' uses
|
||||
difftastic.
|
||||
- "difftool": only configure difftastic as a git difftool, leaving
|
||||
'git diff' untouched.
|
||||
- "both": configure both.
|
||||
|
||||
The boolean option 'programs.difftastic.git.diffToolMode' is deprecated;
|
||||
existing configurations are migrated automatically ('true' becomes
|
||||
"both" and 'false' becomes "external").
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-11T16:00:00+00:00";
|
||||
condition = config.programs.codex.enable;
|
||||
message = ''
|
||||
The `programs.codex.plugins` and `programs.codex.marketplaces` options
|
||||
were added to configure Codex plugins and local plugin marketplaces
|
||||
declaratively.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-12T12:00:00+00:00";
|
||||
condition = config.programs.opencode.enable;
|
||||
message = ''
|
||||
The 'programs.opencode.settings' option now supports ordered Home Manager
|
||||
DAG entries in nested attribute sets.
|
||||
|
||||
This allows order-sensitive OpenCode permission rules to be expressed with
|
||||
'lib.hm.dag.entryBefore' and 'lib.hm.dag.entryAfter'.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
time = "2026-06-16T00:27:55+00:00";
|
||||
condition = config.programs.television.enable;
|
||||
message = ''
|
||||
A new option is available: `programs.television.themes`.
|
||||
|
||||
This option allows you to define custom themes that will be written to
|
||||
`$XDG_CONFIG_HOME/television/themes/''${name}.toml`. Each theme accepts
|
||||
either inline TOML content or a path to a theme file.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
time = "2026-06-16T12:00:00+00:00";
|
||||
condition = config.programs.uv.enable;
|
||||
message = ''
|
||||
The 'programs.uv' module can now manage uv-installed Python versions and
|
||||
tools declaratively.
|
||||
|
||||
Use {option}`programs.uv.python.versions` to install Python versions
|
||||
(with {option}`programs.uv.python.default` selecting the default interpreter
|
||||
per implementation) and {option}`programs.uv.tool.packages` to install
|
||||
tools. Unpinned entries track the latest release on each activation, while
|
||||
pinned ones (e.g. `"3.12.4"` or `"black==24.1.0"`) stay put. Set
|
||||
{option}`programs.uv.python.prune` or {option}`programs.uv.tool.prune` to
|
||||
make the managed set fully declarative, removing versions and tools that are
|
||||
no longer listed.
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
requiresManagedBrowserPackage =
|
||||
browserCfg:
|
||||
browserCfg.globalExtensions != [ ]
|
||||
&& browserCfg.package == null
|
||||
&& (!pkgs.stdenv.hostPlatform.isDarwin || browserCfg.darwinDefaultsId == null);
|
||||
in
|
||||
{
|
||||
time = "2026-06-18T17:40:40+00:00";
|
||||
condition =
|
||||
requiresManagedBrowserPackage config.programs.firefox
|
||||
|| requiresManagedBrowserPackage config.programs.floorp
|
||||
|| requiresManagedBrowserPackage config.programs.librewolf;
|
||||
message = ''
|
||||
The `programs.firefox.globalExtensions`,
|
||||
`programs.floorp.globalExtensions`, and
|
||||
`programs.librewolf.globalExtensions` options now assert that the browser
|
||||
package is managed by Home Manager. On Darwin, setting
|
||||
`programs.<browser>.darwinDefaultsId` also satisfies this requirement.
|
||||
|
||||
If you used `globalExtensions` with `package = null`, set `package` to a
|
||||
non-null browser package or switch back to
|
||||
`profiles.<name>.extensions.packages`.
|
||||
'';
|
||||
}
|
||||
@@ -52,26 +52,26 @@ let
|
||||
}
|
||||
);
|
||||
|
||||
isNixFile = n: v: v == "regular" && lib.hasSuffix ".nix" n;
|
||||
isNewsFile = n: v: v == "regular" && lib.hasSuffix ".nix" n && n != "default.nix";
|
||||
isDirectory = _n: v: v == "directory";
|
||||
|
||||
# Recursively collect all .nix files from a directory
|
||||
collectNixFiles =
|
||||
# Recursively collect all news .nix files from a directory
|
||||
collectNewsFiles =
|
||||
dir:
|
||||
let
|
||||
contents = builtins.readDir dir;
|
||||
files = lib.filterAttrs isNixFile contents;
|
||||
files = lib.filterAttrs isNewsFile contents;
|
||||
fileList = map (file: dir + "/${file}") (builtins.attrNames files);
|
||||
|
||||
# Process subdirectories
|
||||
subdirs = lib.filterAttrs isDirectory contents;
|
||||
subdirFiles = lib.concatMap (subdir: collectNixFiles (dir + "/${subdir}")) (
|
||||
subdirFiles = lib.concatMap (subdir: collectNewsFiles (dir + "/${subdir}")) (
|
||||
builtins.attrNames subdirs
|
||||
);
|
||||
in
|
||||
fileList ++ subdirFiles;
|
||||
|
||||
newsFiles = collectNixFiles ./news;
|
||||
newsFiles = collectNewsFiles ./.;
|
||||
newsEntries = map (
|
||||
newsFile:
|
||||
let
|
||||
@@ -151,6 +151,8 @@ in
|
||||
{
|
||||
meta.maintainers = [ ];
|
||||
|
||||
imports = [ ./remote-build.nix ];
|
||||
|
||||
options.nix = {
|
||||
enable =
|
||||
mkEnableOption ''
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
config,
|
||||
options,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
@@ -8,6 +9,14 @@
|
||||
let
|
||||
|
||||
cfg = config.nixpkgs;
|
||||
opt = options.nixpkgs;
|
||||
|
||||
# Locations of the offending `nixpkgs.config`/`nixpkgs.overlays` definitions,
|
||||
# so users can find what set them.
|
||||
offendingFiles = lib.unique (
|
||||
lib.optionals (cfg.config != null) opt.config.files
|
||||
++ lib.optionals (cfg.overlays != null) opt.overlays.files
|
||||
);
|
||||
|
||||
# Copied from nixpkgs.nix.
|
||||
isConfig = x: builtins.isAttrs x || builtins.isFunction x;
|
||||
@@ -82,6 +91,7 @@ in
|
||||
assertion = cfg.config == null || cfg.overlays == null;
|
||||
message = ''
|
||||
`nixpkgs` options are disabled when `home-manager.useGlobalPkgs` is enabled.
|
||||
Definitions found in ${lib.showFiles offendingFiles}.
|
||||
'';
|
||||
}
|
||||
];
|
||||
@@ -89,6 +99,7 @@ in
|
||||
warnings = lib.optional ((cfg.config != null) || (cfg.overlays != null)) ''
|
||||
You have set either `nixpkgs.config` or `nixpkgs.overlays` while using `home-manager.useGlobalPkgs`.
|
||||
This will soon not be possible. Please remove all `nixpkgs` options when using `home-manager.useGlobalPkgs`.
|
||||
Definitions found in ${lib.showFiles offendingFiles}.
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ let
|
||||
qadwaitadecorations
|
||||
qadwaitadecorations-qt6
|
||||
];
|
||||
gtk = [
|
||||
gtk2 = [
|
||||
libsForQt5.qtstyleplugins
|
||||
qt6Packages.qt6gtk2
|
||||
];
|
||||
@@ -41,9 +41,9 @@ let
|
||||
];
|
||||
};
|
||||
|
||||
# Maps style names to their QT_QPA_PLATFORMTHEME, if necessary.
|
||||
# Maps platform theme names to their QT_QPA_PLATFORMTHEME, if necessary.
|
||||
styleNames = {
|
||||
gtk = "gtk2";
|
||||
gtk = "gtk3";
|
||||
qtct = "qt5ct";
|
||||
};
|
||||
|
||||
@@ -95,8 +95,17 @@ in
|
||||
];
|
||||
|
||||
imports = [
|
||||
./kconfig.nix
|
||||
./kvantum.nix
|
||||
|
||||
(lib.mkChangedOptionModule [ "qt" "useGtkTheme" ] [ "qt" "platformTheme" ] (
|
||||
config: if lib.getAttrFromPath [ "qt" "useGtkTheme" ] config then "gtk" else null
|
||||
config:
|
||||
if lib.getAttrFromPath [ "qt" "useGtkTheme" ] config then
|
||||
{
|
||||
name = "gtk2";
|
||||
}
|
||||
else
|
||||
null
|
||||
))
|
||||
];
|
||||
|
||||
@@ -163,8 +172,12 @@ in
|
||||
Some examples are
|
||||
|
||||
`gtk`
|
||||
: Use GTK theme with
|
||||
: Use Qt's [GTK platform theme plugin](https://github.com/qt/qtbase/tree/dev/src/plugins/platformthemes/gtk3)
|
||||
|
||||
`gtk2`
|
||||
: Use GTK2 theme with
|
||||
[`qtstyleplugins`](https://github.com/qt/qtstyleplugins)
|
||||
and [`qt6gtk2`](https://github.com/trialuser02/qt6gtk2)
|
||||
|
||||
`gtk3`
|
||||
: Use [GTK3 integration](https://github.com/qt/qtbase/tree/dev/src/plugins/platformthemes/gtk3)
|
||||
@@ -319,22 +332,31 @@ in
|
||||
|
||||
config =
|
||||
let
|
||||
deprecateKde6 =
|
||||
name: optionPath:
|
||||
if name == "kde6" then
|
||||
lib.warn ''
|
||||
The ${optionPath} value "kde6" has been deprecated and renamed to "kde".
|
||||
Please update your configuration:
|
||||
${optionPath} = "kde";
|
||||
'' "kde"
|
||||
deprecateKde6 = name: if name == "kde6" then "kde" else name;
|
||||
|
||||
deprecatedKde6PlatformThemeOption =
|
||||
if builtins.isString cfg.platformTheme then
|
||||
if cfg.platformTheme == "kde6" then
|
||||
[
|
||||
"qt"
|
||||
"platformTheme"
|
||||
]
|
||||
else
|
||||
null
|
||||
else if cfg.platformTheme != null && cfg.platformTheme.name == "kde6" then
|
||||
[
|
||||
"qt"
|
||||
"platformTheme"
|
||||
"name"
|
||||
]
|
||||
else
|
||||
name;
|
||||
null;
|
||||
|
||||
platformTheme =
|
||||
if (builtins.isString cfg.platformTheme) then
|
||||
{
|
||||
option = "qt.platformTheme";
|
||||
name = deprecateKde6 cfg.platformTheme "qt.platformTheme";
|
||||
name = deprecateKde6 cfg.platformTheme;
|
||||
package = null;
|
||||
}
|
||||
else if cfg.platformTheme == null then
|
||||
@@ -346,7 +368,7 @@ in
|
||||
else
|
||||
{
|
||||
option = "qt.platformTheme.name";
|
||||
name = deprecateKde6 cfg.platformTheme.name "qt.platformTheme.name";
|
||||
name = deprecateKde6 cfg.platformTheme.name;
|
||||
inherit (cfg.platformTheme) package;
|
||||
};
|
||||
|
||||
@@ -392,7 +414,17 @@ in
|
||||
) "The option `qt.platformTheme` has been renamed to `qt.platformTheme.name`.")
|
||||
++ (lib.lists.optional (
|
||||
platformTheme.name == "gnome" && platformTheme.package == null
|
||||
) "The value `gnome` for option `${platformTheme.option}` is deprecated. Use `adwaita` instead.");
|
||||
) "The value `gnome` for option `${platformTheme.option}` is deprecated. Use `adwaita` instead.")
|
||||
++ (lib.optional (deprecatedKde6PlatformThemeOption != null) (
|
||||
lib.hm.deprecations.mkDeprecatedOptionValueRenameWarning {
|
||||
option = deprecatedKde6PlatformThemeOption;
|
||||
old = ''"kde6"'';
|
||||
replacement = ''"kde"'';
|
||||
}
|
||||
))
|
||||
++ (lib.lists.optional (platformTheme.name == "gtk")
|
||||
"The value `gtk` for option `${platformTheme.option}` is deprecated. Use `gtk2` to keep the legacy qtstyleplugins or `gtk3` to use the modern native Qt GTK3 plugin."
|
||||
);
|
||||
|
||||
qt.style.package = lib.mkIf (cfg.style.name != null) (
|
||||
lib.mkDefault (stylePackages.${lib.toLower cfg.style.name} or null)
|
||||
+112
-61
@@ -1,73 +1,124 @@
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.sshAuthSock;
|
||||
|
||||
mkShellInitOption =
|
||||
shell:
|
||||
lib.mkOption {
|
||||
description = "Code that initializes {env}`SSH_AUTH_SOCK` in ${shell}.";
|
||||
type = lib.types.str;
|
||||
};
|
||||
|
||||
initSubmodule = {
|
||||
options.bash = mkShellInitOption "bash";
|
||||
options.fish = mkShellInitOption "fish";
|
||||
options.nushell = mkShellInitOption "nushell";
|
||||
};
|
||||
|
||||
# Preserve $SSH_AUTH_SOCK only if it stems from a forwarded agent which
|
||||
# is the case if both $SSH_AUTH_SOCK and $SSH_CONNECTION are set.
|
||||
bashIntegration = ''
|
||||
if [ -z "$SSH_AUTH_SOCK" -o -z "$SSH_CONNECTION" ]; then
|
||||
${cfg.initialization.bash}
|
||||
fi
|
||||
'';
|
||||
fishIntegration = ''
|
||||
if test -z "$SSH_AUTH_SOCK"; or test -z "$SSH_CONNECTION"
|
||||
${cfg.initialization.fish}
|
||||
end
|
||||
'';
|
||||
nushellIntegration =
|
||||
let
|
||||
unsetOrEmpty = var: ''("${var}" not-in $env) or ($env.${var} | is-empty)'';
|
||||
in
|
||||
''
|
||||
if ${unsetOrEmpty "SSH_AUTH_SOCK"} or ${unsetOrEmpty "SSH_CONNECTION"} {
|
||||
${cfg.initialization.nushell}
|
||||
}
|
||||
'';
|
||||
|
||||
socketProviderIsSocket = lib.hasSuffix ".socket" cfg.systemd.socketProviderUnit;
|
||||
orderedProviderUnits = lib.optional (!socketProviderIsSocket) cfg.systemd.socketProviderUnit;
|
||||
in
|
||||
{
|
||||
meta.maintainers = [ lib.maintainers.bmrips ];
|
||||
|
||||
options.sshAuthSock.initialization = lib.mkOption {
|
||||
description = ''
|
||||
Shell-specific code to initialize {env}`SSH_AUTH_SOCK`.
|
||||
options.sshAuthSock = {
|
||||
|
||||
RATIONALE: {env}`SSH_AUTH_SOCK` must not be set unconditionally through
|
||||
{option}`home.sessionVariables` since its value needs to be preserved if
|
||||
it stems from a forwarded agent. Hence, this option establishes a
|
||||
centralized interface for setting {env}`SSH_AUTH_SOCK`. It checks whether
|
||||
its value has to be preserved and injects the initialization code into the
|
||||
proper {option}`programs.(bash|fish|nushell|zsh).*` options.
|
||||
'';
|
||||
example = {
|
||||
bash = "export SSH_AUTH_SOCK=$HOME/.ssh/agent.sock";
|
||||
fish = "set -x SSH_AUTH_SOCK $HOME/.ssh/agent.sock";
|
||||
nushell = "$env.SSH_AUTH_SOCK = $HOME/.ssh/agent.sock";
|
||||
enable = lib.mkEnableOption "" // {
|
||||
description = ''
|
||||
Whether to set {env}`SSH_AUTH_SOCK` in shells, systemd, and the D-BUS daemon
|
||||
unless it was already defined through SSH agent forwarding.
|
||||
|
||||
Typically, this module will be implicitly enabled and configured by SSH
|
||||
agent modules.
|
||||
'';
|
||||
};
|
||||
default = null;
|
||||
internal = true;
|
||||
type = with lib.types; nullOr (submodule initSubmodule);
|
||||
|
||||
initialization =
|
||||
let
|
||||
mkShellInitOption =
|
||||
shell:
|
||||
lib.mkOption {
|
||||
description = "Code that initializes {env}`SSH_AUTH_SOCK` in ${shell}.";
|
||||
type = lib.types.str;
|
||||
};
|
||||
in
|
||||
{
|
||||
bash = mkShellInitOption "bash" // {
|
||||
example = "export SSH_AUTH_SOCK=$HOME/.ssh/agent.sock";
|
||||
};
|
||||
fish = mkShellInitOption "fish" // {
|
||||
example = "set -x SSH_AUTH_SOCK $HOME/.ssh/agent.sock";
|
||||
};
|
||||
nushell = mkShellInitOption "nushell" // {
|
||||
example = "$env.SSH_AUTH_SOCK = $HOME/.ssh/agent.sock";
|
||||
};
|
||||
zsh = mkShellInitOption "zsh" // {
|
||||
example = "export SSH_AUTH_SOCK=$HOME/.ssh/agent.sock";
|
||||
default = cfg.initialization.bash;
|
||||
defaultText = lib.literalExpression "config.sshAuthSock.initialization.bash";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.socketProviderUnit = lib.mkOption {
|
||||
description = ''
|
||||
The name of the systemd unit responsible for providing the {env}`SSH_AUTH_SOCK`.
|
||||
|
||||
Services that rely on an active SSH authentication agent can reference
|
||||
this option to declare a dependency onto this unit, ensuring that the
|
||||
socket is available and being served before they start.
|
||||
'';
|
||||
example = "ssh-agent.service";
|
||||
type = lib.types.str;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf (cfg.initialization != null) {
|
||||
# $SSH_AUTH_SOCK has to be set early since other tools rely on it
|
||||
programs.bash.profileExtra = lib.mkOrder 900 bashIntegration;
|
||||
programs.fish.shellInit = lib.mkOrder 900 fishIntegration;
|
||||
programs.nushell.extraConfig = lib.mkOrder 900 nushellIntegration;
|
||||
programs.zsh.envExtra = lib.mkOrder 900 bashIntegration;
|
||||
};
|
||||
config =
|
||||
let
|
||||
# Preserve $SSH_AUTH_SOCK if it stems from a forwarded agent which is the
|
||||
# case if both $SSH_AUTH_SOCK and $SSH_CONNECTION are set.
|
||||
mkShIntegration = code: ''
|
||||
if [ -z "$SSH_AUTH_SOCK" -o -z "$SSH_CONNECTION" ]; then
|
||||
${code}
|
||||
fi
|
||||
'';
|
||||
bashIntegration = mkShIntegration cfg.initialization.bash;
|
||||
zshIntegration = mkShIntegration cfg.initialization.zsh;
|
||||
fishIntegration = ''
|
||||
if test -z "$SSH_AUTH_SOCK"; or test -z "$SSH_CONNECTION"
|
||||
${cfg.initialization.fish}
|
||||
end
|
||||
'';
|
||||
nushellIntegration =
|
||||
let
|
||||
unsetOrEmpty = var: ''("${var}" not-in $env) or ($env.${var} | is-empty)'';
|
||||
in
|
||||
''
|
||||
if ${unsetOrEmpty "SSH_AUTH_SOCK"} or ${unsetOrEmpty "SSH_CONNECTION"} {
|
||||
${cfg.initialization.nushell}
|
||||
}
|
||||
'';
|
||||
in
|
||||
lib.mkIf cfg.enable {
|
||||
# $SSH_AUTH_SOCK has to be set early since other tools rely on it
|
||||
programs.bash.profileExtra = lib.mkOrder 900 bashIntegration;
|
||||
programs.fish.shellInit = lib.mkOrder 900 fishIntegration;
|
||||
programs.nushell.extraConfig = lib.mkOrder 900 nushellIntegration;
|
||||
programs.zsh.envExtra = lib.mkOrder 900 zshIntegration;
|
||||
|
||||
# Replace this service by an environment generator as soon as they are
|
||||
# available per-user. See https://github.com/systemd/systemd/issues/32423
|
||||
# for more information.
|
||||
systemd.user.services.set-SSH_AUTH_SOCK = {
|
||||
Unit = {
|
||||
Description = "Sets SSH_AUTH_SOCK in the D-BUS daemon and systemd";
|
||||
# Socket units are ordered before sockets.target by systemd. Ordering
|
||||
# this service before them creates a cycle through basic.target.
|
||||
Before = orderedProviderUnits;
|
||||
};
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = pkgs.writeShellScript "set-SSH_AUTH_SOCK" ''
|
||||
${bashIntegration}
|
||||
${pkgs.dbus}/bin/dbus-update-activation-environment --systemd SSH_AUTH_SOCK
|
||||
'';
|
||||
};
|
||||
Install.WantedBy = [
|
||||
"default.target"
|
||||
]
|
||||
++ orderedProviderUnits;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ in
|
||||
"25.05"
|
||||
"25.11"
|
||||
"26.05"
|
||||
"26.11"
|
||||
];
|
||||
description = ''
|
||||
It is occasionally necessary for Home Manager to change
|
||||
|
||||
@@ -18,7 +18,7 @@ let
|
||||
|
||||
inherit
|
||||
(
|
||||
(import ../lib/file-type.nix {
|
||||
(import ../../lib/file-type.nix {
|
||||
inherit (config.home) homeDirectory;
|
||||
inherit lib pkgs;
|
||||
})
|
||||
@@ -41,6 +41,17 @@ let
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./autostart.nix
|
||||
./desktop-entries.nix
|
||||
./mime.nix
|
||||
./mime-apps.nix
|
||||
./portal.nix
|
||||
./system-dirs.nix
|
||||
./terminal-exec.nix
|
||||
./user-dirs.nix
|
||||
];
|
||||
|
||||
options.xdg = {
|
||||
enable = lib.mkEnableOption "management of XDG base directories";
|
||||
|
||||
@@ -113,25 +113,6 @@ in
|
||||
MISC = "''${config.home.homeDirectory}/Misc";
|
||||
}
|
||||
'';
|
||||
apply =
|
||||
if lib.versionOlder config.home.stateVersion "26.05" then
|
||||
lib.mapAttrs' (
|
||||
k:
|
||||
let
|
||||
matches = lib.match "XDG_(.*)_DIR" k;
|
||||
in
|
||||
lib.nameValuePair (
|
||||
if matches == null then
|
||||
k
|
||||
else
|
||||
let
|
||||
name = lib.elemAt matches 0;
|
||||
in
|
||||
lib.warn "using keys like ‘${k}’ for xdg.userDirs.extraConfig is deprecated in favor of keys like ‘${name}’" name
|
||||
)
|
||||
)
|
||||
else
|
||||
lib.id;
|
||||
description = ''
|
||||
Other user directories.
|
||||
|
||||
@@ -175,6 +156,23 @@ in
|
||||
|
||||
config =
|
||||
let
|
||||
legacyExtraConfigKeys = lib.filter (key: lib.match "XDG_(.*)_DIR" key != null) (
|
||||
lib.attrNames cfg.extraConfig
|
||||
);
|
||||
|
||||
normalizeExtraConfigKey =
|
||||
key:
|
||||
let
|
||||
matches = lib.match "XDG_(.*)_DIR" key;
|
||||
in
|
||||
if matches == null then key else lib.elemAt matches 0;
|
||||
|
||||
extraConfig =
|
||||
if lib.versionOlder config.home.stateVersion "26.05" then
|
||||
lib.mapAttrs' (key: lib.nameValuePair (normalizeExtraConfigKey key)) cfg.extraConfig
|
||||
else
|
||||
cfg.extraConfig;
|
||||
|
||||
directories =
|
||||
(lib.filterAttrs (_n: v: !isNull v) {
|
||||
DESKTOP = cfg.desktop;
|
||||
@@ -187,11 +185,26 @@ in
|
||||
TEMPLATES = cfg.templates;
|
||||
VIDEOS = cfg.videos;
|
||||
})
|
||||
// cfg.extraConfig;
|
||||
// extraConfig;
|
||||
|
||||
bindings = lib.mapAttrs' (k: lib.nameValuePair "XDG_${k}_DIR") directories;
|
||||
in
|
||||
lib.mkIf cfg.enable {
|
||||
warnings = lib.optionals (lib.versionOlder config.home.stateVersion "26.05") (
|
||||
map (
|
||||
key:
|
||||
lib.hm.deprecations.mkDeprecatedOptionValueWarning {
|
||||
option = [
|
||||
"xdg"
|
||||
"userDirs"
|
||||
"extraConfig"
|
||||
];
|
||||
old = "keys like `${key}`";
|
||||
replacement = "keys like `${normalizeExtraConfigKey key}`";
|
||||
}
|
||||
) legacyExtraConfigKeys
|
||||
);
|
||||
|
||||
xdg.configFile."user-dirs.dirs".text =
|
||||
let
|
||||
# For some reason, these need to be wrapped with quotes to be valid.
|
||||
+5
-16
@@ -35,17 +35,14 @@ let
|
||||
./misc/debug.nix
|
||||
./misc/editorconfig.nix
|
||||
./misc/fontconfig.nix
|
||||
./misc/gtk.nix
|
||||
./misc/gtk
|
||||
./misc/lib.nix
|
||||
./misc/mozilla-messaging-hosts.nix
|
||||
./misc/news.nix
|
||||
./misc/nix-remote-build.nix
|
||||
./misc/nix.nix
|
||||
./misc/news
|
||||
./misc/nix
|
||||
./misc/numlock.nix
|
||||
./misc/pam.nix
|
||||
./misc/qt.nix
|
||||
./misc/qt/kconfig.nix
|
||||
./misc/qt/kvantum.nix
|
||||
./misc/qt
|
||||
./misc/shell.nix
|
||||
./misc/specialisation.nix
|
||||
./misc/ssh-auth-sock.nix
|
||||
@@ -54,15 +51,7 @@ let
|
||||
./misc/uninstall.nix
|
||||
./misc/version.nix
|
||||
./misc/vte.nix
|
||||
./misc/xdg-autostart.nix
|
||||
./misc/xdg-desktop-entries.nix
|
||||
./misc/xdg-mime-apps.nix
|
||||
./misc/xdg-mime.nix
|
||||
./misc/xdg-portal.nix
|
||||
./misc/xdg-system-dirs.nix
|
||||
./misc/xdg-terminal-exec.nix
|
||||
./misc/xdg-user-dirs.nix
|
||||
./misc/xdg.nix
|
||||
./misc/xdg
|
||||
./misc/xfconf.nix
|
||||
./services-modular
|
||||
./systemd.nix
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR Home Manager contributors
|
||||
# This file is distributed under the same license as the Home Manager Modules package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Home Manager Modules\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0100\n"
|
||||
"PO-Revision-Date: 2026-05-31 21:01+0000\n"
|
||||
"Last-Translator: Tofaa <tofig.adigozalov.180904@gmail.com>\n"
|
||||
"Language-Team: Azerbaijani <https://hosted.weblate.org/projects/home-manager/"
|
||||
"modules/az/>\n"
|
||||
"Language: az\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 2026.6.dev0\n"
|
||||
|
||||
#: modules/files.nix:214
|
||||
msgid "Creating home file links in %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/files.nix:227
|
||||
msgid "Cleaning up orphan links from %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/home-environment.nix:680
|
||||
msgid "Creating new profile generation"
|
||||
msgstr ""
|
||||
|
||||
#: modules/home-environment.nix:683
|
||||
msgid "No change so reusing latest profile generation"
|
||||
msgstr ""
|
||||
|
||||
#: modules/home-environment.nix:732
|
||||
msgid ""
|
||||
"Oops, Nix failed to install your new Home Manager profile!\n"
|
||||
"\n"
|
||||
"Perhaps there is a conflict with a package that was installed using\n"
|
||||
"\"%s\"? Try running\n"
|
||||
"\n"
|
||||
" %s\n"
|
||||
"\n"
|
||||
"and if there is a conflicting package you can remove it with\n"
|
||||
"\n"
|
||||
" %s\n"
|
||||
"\n"
|
||||
"Then try activating your Home Manager configuration again."
|
||||
msgstr ""
|
||||
|
||||
#: modules/home-environment.nix:768
|
||||
msgid "Activating %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/home-environment.nix:840
|
||||
msgid "%s: unknown option '%s'"
|
||||
msgstr "%s: bilinməyən seçim '%s'"
|
||||
|
||||
#: modules/lib-bash/activation-init.sh:22
|
||||
msgid "Migrating profile from %s to %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/lib-bash/activation-init.sh:54
|
||||
msgid "Could not find suitable profile directory, tried %s and %s"
|
||||
msgstr "Uyğun profil qovluğu tapılmadı; %s və %s yolları yoxlanıldı"
|
||||
|
||||
#. translators: For example: HOME is "/home/foo", expected "/home/bar"
|
||||
#: modules/lib-bash/activation-init.sh:109
|
||||
#: modules/lib-bash/activation-init.sh:121
|
||||
msgid "%s is \"%s\", expected \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: modules/lib-bash/activation-init.sh:138
|
||||
msgid "Starting Home Manager activation"
|
||||
msgstr ""
|
||||
|
||||
#: modules/lib-bash/activation-init.sh:142
|
||||
msgid "Sanity checking Nix"
|
||||
msgstr "Nix yoxlanılır"
|
||||
|
||||
#: modules/lib-bash/activation-init.sh:155
|
||||
msgid "This is a dry run"
|
||||
msgstr ""
|
||||
|
||||
#: modules/lib-bash/activation-init.sh:159
|
||||
msgid "This is a live run"
|
||||
msgstr ""
|
||||
|
||||
#: modules/lib-bash/activation-init.sh:165
|
||||
msgid "Using Nix version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/lib-bash/activation-init.sh:168
|
||||
msgid "Activation variables:"
|
||||
msgstr ""
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user