Compare commits

..
Author SHA1 Message Date
dependabot[bot]andGitHub 621d9b169b ci: bump actions/labeler from 6 to 7
Bumps [actions/labeler](https://github.com/actions/labeler) from 6 to 7.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-25 01:52:19 +00:00
dependabot[bot]andAustin Horstman 4ce190229c flake: bump nixpkgs from 74cc63f to 293d6ab
Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `74cc63f` to `293d6ab`.
- [Commits](https://github.com/NixOS/nixpkgs/compare/74cc63f702f7d60a557e152a57b40fb1fd0f72ac...293d6abedf0478e681a4dfcfcb35b30fc796a32f)

---
updated-dependencies:
- dependency-name: nixpkgs
  dependency-version: 293d6abedf0478e681a4dfcfcb35b30fc796a32f
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-18 00:01:49 -05:00
dependabot[bot]andAustin Horstman 3cd22efe64 flake: bump nixpkgs from 95ca1e2 to 74cc63f
Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `95ca1e2` to `74cc63f`.
- [Commits](https://github.com/NixOS/nixpkgs/compare/95ca1e203c0750115fd4a6f17d5a245dfe6b1edd...74cc63f702f7d60a557e152a57b40fb1fd0f72ac)

---
updated-dependencies:
- dependency-name: nixpkgs
  dependency-version: 74cc63f702f7d60a557e152a57b40fb1fd0f72ac
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-10 22:21:25 -05:00
dependabot[bot]andAustin Horstman af2beae5f0 flake: bump nixpkgs from a037402 to 95ca1e2
Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `a037402` to `95ca1e2`.
- [Commits](https://github.com/NixOS/nixpkgs/compare/a0374025a863d007d98e3297f6aa46cc3141c2f0...95ca1e203c0750115fd4a6f17d5a245dfe6b1edd)

---
updated-dependencies:
- dependency-name: nixpkgs
  dependency-version: 95ca1e203c0750115fd4a6f17d5a245dfe6b1edd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-04 22:14:08 -05:00
Jure VarlecandAustin Horstman 868d0a692d generic-linux-gpu: add EGL support for Nvidia
(cherry picked from commit 4ad9aaae70)
2026-06-28 22:34:17 -05:00
Austin Horstman 7bfff44b46 tests/darwinScrublist: add parallel-full
Scrub the default GNU Parallel package on Darwin so the module test does not build the failing DBD::CSV dependency.

(cherry picked from commit 04063004a3)
2026-06-20 13:45:05 -05:00
dependabot[bot]andAustin Horstman 78671d3ce3 flake: bump nixpkgs from bd0ff2d to a037402
Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `bd0ff2d` to `a037402`.
- [Commits](https://github.com/NixOS/nixpkgs/compare/bd0ff2d3eac24699c3664d5966b9ef36f388e2ca...a0374025a863d007d98e3297f6aa46cc3141c2f0)

---
updated-dependencies:
- dependency-name: nixpkgs
  dependency-version: a0374025a863d007d98e3297f6aa46cc3141c2f0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-20 13:45:05 -05:00
dependabot[bot]andAustin Horstman ad026da57b ci: bump actions/checkout from 6 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-20 12:34:54 -05:00
dependabot[bot]andAustin Horstman 8355f0a16b flake: bump nixpkgs from 6b31628 to bd0ff2d
Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `6b31628` to `bd0ff2d`.
- [Commits](https://github.com/NixOS/nixpkgs/compare/6b316287bae2ee04c9b93c8c858d930fd07d7338...bd0ff2d3eac24699c3664d5966b9ef36f388e2ca)

---
updated-dependencies:
- dependency-name: nixpkgs
  dependency-version: bd0ff2d3eac24699c3664d5966b9ef36f388e2ca
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-12 22:02:04 -05:00
MalikandAustin Horstman ea6d221d7a news: add entry for programs.mcp typed schema
(cherry picked from commit 1ffdc28076)
2026-06-11 08:25:46 -05:00
MalikandAustin Horstman 005c72cc1a claude-code, codex, antigravity-cli, github-copilot-cli, opencode, vscode, zed-editor: use mcp lib
- use lib.hm.mcp.transformMcpServer for MCP integration
- Update tests to reflect new merged server configuration

(cherry picked from commit ebc55fd103)
2026-06-11 08:25:46 -05:00
MalikandAustin Horstman 22d5ec3d12 mcp: extract helper functions into lib/mcp.nix
Extract MCP-related helper functions (renderEnv, mkEnvFilesWrapper,
wrapEnvFilesCommand, addType, transformMcpServer) into a new shared
library module at modules/lib/mcp.nix.
- Add lib.hm.mcp for sharing transformMcpServer logic across consumers
- Add enabled/disabled conflict detection assertion
- Add tests for new library functions

(cherry picked from commit 6a66db1360)
2026-06-11 08:25:46 -05:00
Austin Horstman d899b01766 opencode: use lib.hm.strings.isPathLike
(cherry picked from commit c321a1c8a7)
2026-06-10 15:56:25 -05:00
Austin Horstman d136d10d61 github-copilot-cli: use lib.hm.strings.isPathLike
(cherry picked from commit 6d4fb02e34)
2026-06-10 15:56:25 -05:00
Austin Horstman 55cf7c9428 codex: use lib.hm.strings.isPathLike
(cherry picked from commit 4b9a995519)
2026-06-10 15:56:25 -05:00
Austin Horstman c88f017a22 claude-code: use lib.hm.strings.isPathLike
(cherry picked from commit 09ff165ba6)
2026-06-10 15:56:25 -05:00
Austin Horstman d48a1e6ad6 antigravity-cli: use lib.hm.strings.isPathLike
(cherry picked from commit 3a647b158e)
2026-06-10 15:56:25 -05:00
Austin Horstman 8982f45135 lib/strings: add isPathLike helper
(cherry picked from commit d33c97ec1b)
2026-06-10 15:56:25 -05:00
Austin Horstman ba6a244f3b antigravity-cli: add migration news
(cherry picked from commit eaca478c95)
2026-06-10 15:56:25 -05:00
Austin Horstman cbc74b9371 antigravity-cli: rename gemini-cli module
(cherry picked from commit ff77a72fe5)
2026-06-10 15:56:25 -05:00
Austin Horstman fcbbf5484a docs: fix sshAuthSock reference in rl-2605
The link to `#opt-sshAuthSock.enable` broke the manual build on
release-26.05, where the `enable` option does not exist. Drop the
broken reference and the inaccurate "enabled and" wording.

(cherry picked from commit 2b394beca7)
2026-06-10 13:10:13 -05:00
Austin Horstman baa46aeb6d docs: update release notes for 26.05
Lots of missing information about major changes throughout the last
release.

(cherry picked from commit c30f177760)
2026-06-09 22:45:20 -05:00
dependabot[bot]andAustin Horstman 4eb4fec416 flake: bump nixpkgs from ec942ba to 6b31628
Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `ec942ba` to `6b31628`.
- [Commits](https://github.com/NixOS/nixpkgs/compare/ec942ba042dad5ef097e2ef3a3effc034241f011...6b316287bae2ee04c9b93c8c858d930fd07d7338)

---
updated-dependencies:
- dependency-name: nixpkgs
  dependency-version: 6b316287bae2ee04c9b93c8c858d930fd07d7338
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-07 20:59:21 -05:00
Austin Horstman e28654b710 claude-code: mark hook scripts executable
Inline hooks are scripts, so write them with the executable bit set. Add NMT assertions covering default and custom config directories.

(cherry picked from commit d0af9b8bf3)
2026-06-01 19:47:05 -05:00
dependabot[bot]andAustin Horstman 4baa8ac595 flake: bump nixpkgs from 705e992 to ec942ba
Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `705e992` to `ec942ba`.
- [Commits](https://github.com/NixOS/nixpkgs/compare/705e9929918b43bd7b715dc0a878ac870449bb03...ec942ba042dad5ef097e2ef3a3effc034241f011)

---
updated-dependencies:
- dependency-name: nixpkgs
  dependency-version: ec942ba042dad5ef097e2ef3a3effc034241f011
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-01 14:14:08 -05:00
Austin Horstman b179bde238 flake: lock to stable branch
Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-05-25 11:33:45 -05:00
Austin Horstman 0a56fc5a57 release: mark as release branch
Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
2026-05-25 11:32:30 -05:00
620 changed files with 3298 additions and 15255 deletions
+2 -2
View File
@@ -24,8 +24,8 @@ Also make sure to read the guidelines found at
- [ ] Code formatted with `nix fmt` or
`nix-shell -A dev --run treefmt`.
- [ ] Code tested through `nix build .#test-all`
or a targeted `nix run .#tests -- <pattern>`.
- [ ] Code tested through `nix run .#tests -- test-all` or
`nix-shell --pure tests -A run.all`.
- [ ] Test cases updated/added. See [example](https://github.com/nix-community/home-manager/commit/f3fbb50b68df20da47f9b0def5607857fcc0d021#diff-b61a6d542f9036550ba9c401c80f00ef).
+2 -2
View File
@@ -9,7 +9,7 @@ updates:
prefix: "ci:"
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "release-26.05"
target-branch: "release-25.11"
schedule:
interval: "weekly"
commit-message:
@@ -23,7 +23,7 @@ updates:
prefix: "flake:"
- package-ecosystem: "nix"
directory: "/"
target-branch: "release-26.05"
target-branch: "release-25.11"
schedule:
interval: "weekly"
commit-message:
+1 -1
View File
@@ -7,7 +7,7 @@
- modules/programs/aiac.nix
- modules/programs/aichat.nix
- modules/programs/aider-chat.nix
- modules/programs/claude-code/**/*
- modules/programs/claude-code.nix
- modules/programs/codex.nix
- modules/programs/fabric-ai.nix
- modules/programs/antigravity-cli.nix
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
- uses: actions/checkout@v7
with:
token: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
ref: ${{ github.event.pull_request.base.sha }}
ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs
id: backport
uses: korthout/backport-action@v4
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@v7
- uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixpkgs-unstable
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v17
with:
name: nix-community
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
echo "Found $COUNT changed module files."
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
- uses: actions/labeler@v6
- uses: actions/labeler@v7
if: steps.count.outputs.count <= 10
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
-8
View File
@@ -1,8 +0,0 @@
# 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.
+1 -5
View File
@@ -11,13 +11,9 @@ list *pattern:
test *pattern:
nix run .#tests -- {{pattern}}
# List integration tests
list_integration_tests:
nix run .#tests -- -t -l
# Run integration tests
integration_tests:
nix run .#tests -- -t integration-test-
nix run .#tests -- -t -l
# Create a news entry
create-news-entry:
+2 -2
View File
@@ -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/nixpkgs-unstable";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-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 `nixpkgs-unstable`.
(e.g., `nixos-25.11`), while master continues to track `nixos-unstable`.
#### Step 3.5: On master - Update CI for the New Release Branch
+1 -1
View File
@@ -1,5 +1,5 @@
.PHONY: all all-tests test test-install format
NIXPKGS_REV := nixpkgs-unstable
NIXPKGS_REV := nixos-unstable
NIX_PATH := nixpkgs=https://github.com/NixOS/nixpkgs/archive/${NIXPKGS_REV}.tar.gz
all: all-tests test-install
+3 -4
View File
@@ -25,9 +25,8 @@ If you would like to contribute to Home Manager, then please have a look at
Releases
--------
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][].
Home Manager is developed against `nixos-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
@@ -50,7 +49,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 `nixpkgs-unstable` and NixOS version 26.05 (the current
Home Manager targets [NixOS][] unstable and NixOS version 26.05 (the current
stable version), it may or may not work on other Linux distributions and NixOS
versions.
+31 -103
View File
@@ -26,7 +26,7 @@
github = "0xdsqr";
githubId = 99584622;
name = "Dave Dennis";
source = "home-manager";
source = "nixpkgs";
};
"3ulalia" = {
email = "3ulalia@proton.me";
@@ -39,14 +39,8 @@
email = "git.t@betsumei.com";
github = "74k1";
githubId = 49000471;
keys = [
{
fingerprint = "46F3 422F 63A3 1369 7EAB 83D5 1CF1 55F7 6F21 3503";
}
];
matrix = "@74k1:matrix.org";
name = "Tim";
source = "nixpkgs";
source = "home-manager";
};
"9p4" = {
email = "vcs@ersei.net";
@@ -81,14 +75,14 @@
github = "AndreasMager";
githubId = 5646732;
name = "Andreas Mager";
source = "home-manager";
source = "nixpkgs";
};
Austreelis = {
email = "github@accounts.austreelis.net";
github = "Austreelis";
githubId = 56743515;
name = "Morgane Austreelis";
source = "home-manager";
source = "nixpkgs";
};
CarlosLoboxyz = {
email = "86011416+CarlosLoboxyz@users.noreply.github.com";
@@ -125,13 +119,6 @@
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";
@@ -145,7 +132,7 @@
github = "Fendse";
githubId = 46252070;
name = "Sara Johnsson";
source = "home-manager";
source = "nixpkgs";
};
Flameopathic = {
email = "flameopathic@gmail.com";
@@ -208,7 +195,7 @@
githubId = 80165193;
matrix = "@janik0:matrix.org";
name = "Janik";
source = "home-manager";
source = "nixpkgs";
};
JasmineLowen = {
email = "robwalter96@gmail.com";
@@ -234,7 +221,7 @@
github = "Joker9944";
githubId = 9194199;
name = "Felix von Arx";
source = "home-manager";
source = "nixpkgs";
};
JustinLovinger = {
email = "git@justinlovinger.com";
@@ -311,7 +298,7 @@
github = "MForster";
githubId = 4067975;
name = "Michael Forster";
source = "home-manager";
source = "nixpkgs";
};
Misterio77 = {
email = "eu@misterio.me";
@@ -390,25 +377,12 @@
name = "Philipp Mildenberger";
source = "nixpkgs";
};
ReStranger = {
github = "ReStranger";
githubId = 69393944;
name = "ReStranger";
source = "nixpkgs";
};
ReeSilva = {
email = "reesilva@proton.me";
github = "ReeSilva";
githubId = 3605032;
name = "Renato da Silva";
source = "home-manager";
};
Rosuavio = {
email = "RosarioPulella@gmail.com";
github = "Rosuavio";
githubId = 7164552;
name = "Rosario Pulella";
source = "home-manager";
source = "nixpkgs";
};
S0AndS0 = {
email = "S0AndS0@digital-mercenaries.com";
@@ -548,6 +522,14 @@
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";
@@ -597,13 +579,6 @@
name = "Alin";
source = "nixpkgs";
};
amadejkastelic = {
email = "amadejkastelic7@gmail.com";
github = "amadejkastelic";
githubId = 26391003;
name = "Amadej Kastelic";
source = "nixpkgs";
};
ambroisie = {
email = "bruno.nixpkgs@belanyi.fr";
github = "ambroisie";
@@ -671,7 +646,7 @@
github = "b1kku";
githubId = 77858854;
name = "Bikku";
source = "home-manager";
source = "nixpkgs";
};
bamhm182 = {
email = "bamhm182@gmail.com";
@@ -711,13 +686,6 @@
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";
@@ -881,7 +849,7 @@
github = "ckgxrg-salt";
githubId = 165614491;
name = "ckgxrg";
source = "home-manager";
source = "nixpkgs";
};
cmacrae = {
email = "hi@cmacr.ae";
@@ -910,13 +878,6 @@
name = "Phillip Cloud";
source = "nixpkgs";
};
csanthiago = {
email = "git@csanthiago.dev";
github = "csanthiago";
githubId = 8346803;
name = "csanthiago";
source = "home-manager";
};
cwyc = {
email = "hello@cwyc.page";
github = "cwyc";
@@ -997,6 +958,13 @@
name = "Robin Stumm";
source = "nixpkgs";
};
diniamo = {
email = "diniamo53@gmail.com";
github = "diniamo";
githubId = 55629891;
name = "diniamo";
source = "nixpkgs";
};
donovanglover = {
github = "donovanglover";
githubId = 2374245;
@@ -1471,7 +1439,7 @@
github = "kaleocheng";
githubId = 7939352;
name = "Kaleo Cheng";
source = "home-manager";
source = "nixpkgs";
};
kalhauge = {
email = "kalhauge@users.noreply.github.com";
@@ -1499,13 +1467,6 @@
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";
@@ -1657,13 +1618,6 @@
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";
@@ -1765,7 +1719,7 @@
github = "miku4k";
githubId = 89653242;
name = "Miku B";
source = "home-manager";
source = "nixpkgs";
};
minijackson = {
email = "minijackson@riseup.net";
@@ -1864,7 +1818,7 @@
github = "natecox";
githubId = 2782695;
name = "Nate Cox";
source = "home-manager";
source = "nixpkgs";
};
nbp = {
email = "nixos@nbp.name";
@@ -2085,18 +2039,6 @@
name = "Rasmus Kirk";
source = "home-manager";
};
ratakor = {
email = "ratakor@disroot.org";
github = "ratakor";
githubId = 45130910;
keys = [
{
fingerprint = "B60F 8F80 D6CD C5D2 58CF 14C3 241B 1CBE 567B 287E";
}
];
name = "Ratakor";
source = "nixpkgs";
};
ravenjoad = {
email = "raven@hallsby.com";
github = "ravenjoad";
@@ -2208,7 +2150,7 @@
github = "s0racat";
githubId = 125882337;
name = "soracat";
source = "home-manager";
source = "nixpkgs";
};
sableseyler = {
email = "sable@seyleri.us";
@@ -2229,13 +2171,6 @@
name = "Seong Yong-ju";
source = "nixpkgs";
};
semi710 = {
email = "nik.singh710@gmail.com";
github = "semi710";
githubId = 60490474;
name = "Nikhil Singh";
source = "nixpkgs";
};
shikanime = {
email = "william.phetsinorath@shikanime.studio";
github = "shikanime";
@@ -2315,13 +2250,6 @@
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";
@@ -2497,7 +2425,7 @@
source = "nixpkgs";
};
yaaaarn = {
email = "nix@yarncat.moe";
email = "30006414+yaaaarn@users.noreply.github.com";
github = "yaaaarn";
githubId = 30006414;
name = "yarncat";
+3 -9
View File
@@ -253,15 +253,9 @@ let
# Generate the HTML manual pages
home-manager-manual = pkgs.callPackage ./home-manager-manual.nix {
home-manager-options = {
home-manager = {
json = hmOptionsDocs.optionsJSON;
};
nixos = {
json = nixosOptionsDocs.optionsJSON;
};
nix-darwin = {
json = nixDarwinOptionsDocs.optionsJSON;
};
home-manager = hmOptionsDocs.optionsJSON;
nixos = nixosOptionsDocs.optionsJSON;
nix-darwin = nixDarwinOptionsDocs.optionsJSON;
};
inherit revision;
};
+4 -4
View File
@@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1779877693,
"narHash": "sha256-NOF9NAREhxr50bbBfVcVOq+ArCMSoe8dP79Pk2uyARk=",
"lastModified": 1779622335,
"narHash": "sha256-ViA62qtL5za7V3d5I8OA9q9JcFhsVAiL5jVHwEclWqk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4100e830e085863741bc69b156ec4ccd53ab5be0",
"rev": "705e9929918b43bd7b715dc0a878ac870449bb03",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
+1 -1
View File
@@ -2,7 +2,7 @@
description = "Support developing Home Manager documentation";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
scss-reset = {
url = "github:andreymatin/scss-reset/1.4.2";
flake = false;
+39 -53
View File
@@ -1,75 +1,63 @@
{
stdenv,
lib,
callPackage,
mdbook,
python3,
documentation-highlighter,
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 = [
mdbook
python3
];
nativeBuildInputs = [ nixos-render-docs ];
src = ./.;
src = ./manual;
buildPhase = ''
runHook preBuild
mkdir -p out/{highlightjs,media}
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
cp -t out/highlightjs \
${documentation-highlighter}/highlight.pack.js \
${documentation-highlighter}/LICENSE \
${documentation-highlighter}/mono-blue.css \
${documentation-highlighter}/loader.js
cp -r ${mdbookOptions}/options source/options
substituteInPlace ./options.md \
--subst-var-by \
OPTIONS_JSON \
${home-manager-options.home-manager}/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 ./nixos-options.md \
--subst-var-by \
OPTIONS_JSON \
${home-manager-options.nixos}/share/doc/nixos/options.json
mdbook build source --dest-dir book
substituteInPlace ./nix-darwin-options.md \
--subst-var-by \
OPTIONS_JSON \
${home-manager-options.nix-darwin}/share/doc/nixos/options.json
mkdir -p out
cp -r book/* out/
cp ${./options.html} out/options.html
python3 ${./mdbook/legacy-redirects.py} source out
cp ${./static/style.css} out/style.css
cp ${./static/anchor-links.js} out/anchor-links.js
runHook postBuild
cp -r ${./release-notes} release-notes
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
'';
installPhase = ''
@@ -81,9 +69,7 @@ stdenv.mkDerivation {
echo "doc manual $dest index.html" >> $out/nix-support/hydra-build-products
'';
passthru = {
inherit home-manager-options mdbookOptions;
};
passthru = { inherit home-manager-options; };
meta = {
maintainers = [ lib.maintainers.considerate ];
+2 -7
View File
@@ -25,14 +25,9 @@ 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 {
-64
View File
@@ -1,64 +0,0 @@
# 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)
-11
View File
@@ -1,11 +0,0 @@
[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
+2 -3
View File
@@ -3,8 +3,8 @@
Contributions to Home Manager are very welcome. To make the process as
smooth as possible for both you and the Home Manager maintainers we
provide some guidelines that we ask you to follow. See [Getting
started](contributing/getting-started.md#sec-contrib-getting-started) for information on how to set up
a suitable development environment and [Guidelines](contributing/guidelines.md#sec-guidelines) for
started](#sec-contrib-getting-started) for information on how to set up
a suitable development environment and [Guidelines](#sec-guidelines) for
the actual guidelines.
This text is mainly directed at those who would like to make code
@@ -22,7 +22,6 @@ configuration.
```{=include=} sections
contributing/getting-started.md
contributing/guidelines.md
contributing/release-notes.md
contributing/news.md
contributing/tests.md
```
+2 -2
View File
@@ -8,7 +8,7 @@ how to do this.
Once you have a fork of Home Manager you should create a branch starting
at the most recent `master` branch. Give your branch a reasonably
descriptive name. Commit your changes to this branch and when you are
happy with the result and it fulfills [Guidelines](guidelines.md#sec-guidelines) then
happy with the result and it fulfills [Guidelines](#sec-guidelines) then
push the branch to GitHub and [create a pull
request](https://help.github.com/articles/creating-a-pull-request/).
@@ -21,7 +21,7 @@ the `home-manager` command use it by either
$ home-manager -I home-manager=$HOME/devel/home-manager
```
or, if using [flakes](../nix-flakes/standalone.md#sec-flakes-standalone):
or, if using [flakes](#sec-flakes-standalone):
``` shell
$ home-manager --override-input home-manager ~/devel/home-manager
+8 -51
View File
@@ -58,38 +58,11 @@ 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
so that your change will keep working in the future. See
[Tests](tests.md#sec-tests) for more information about the Home Manager test
[Tests](#sec-tests) for more information about the Home Manager test
suite.
All contributed code *must* pass the test suite.
@@ -108,7 +81,7 @@ Manager Git repository:
``` shell
$ nix-build -A docs.html
$ xdg-open ./result/share/doc/home-manager/index.html
$ xdg-open ./result/share/doc/home-manager/index.xhtml
```
When you have made changes to a module, it is a good idea to check that
@@ -116,7 +89,7 @@ the man page version of the module options looks good:
``` shell
$ nix-build -A docs.manPages
$ man ./result/share/man/man5/home-configuration.nix.5
$ man ./result/share/man/man5/home-configuration.nix.5.gz
```
## Module Auto-importing {#sec-module-auto-importing}
@@ -182,17 +155,10 @@ consistent commit message format as described in
If your contribution includes a change that should be communicated to
users of Home Manager then you can add a news entry. The entry must be
formatted as described in [News](news.md#sec-news).
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](release-notes.md#sec-contributing-release-notes) and [News](news.md#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
@@ -200,16 +166,12 @@ of the supported platforms. The most common example of platform specific
modules are those that define systemd user services, which only works on
Linux systems.
If you add a module that is platform specific then make sure the module
guards platform-specific configuration with an appropriate condition, for
example `pkgs.stdenv.hostPlatform.isLinux` or
`pkgs.stdenv.hostPlatform.isDarwin`. Modules in `modules/programs/` and
`modules/services/` are auto-imported, so the platform condition should live
in the module behavior and in any platform-specific tests rather than in a
separate module discovery call.
If you add a module that is platform specific then make sure to include
a condition in the `loadModule` function call. This will make the module
accessible only on systems where the condition evaluates to `true`.
Similarly, if you are adding a news entry then it should be shown only
to users that may find it relevant, see [News](news.md#sec-news) for a
to users that may find it relevant, see [News](#sec-news) for a
description of conditional news.
## Mind the license {#sec-guidelines-licensing}
@@ -232,11 +194,6 @@ 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 -23
View File
@@ -6,17 +6,8 @@ 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](release-notes.md#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`](https://github.com/nix-community/home-manager/blob/master/modules/misc/news)
[`news.nix`](https://github.com/nix-community/home-manager/blob/master/modules/misc/news.nix)
but you should follow some basic guidelines:
- Use the included news entry generator to create a news entry file:
@@ -34,12 +25,9 @@ but you should follow some basic guidelines:
This will create a new file inside the `modules/misc/news` directory
with some placeholder information that you can edit.
- The entry condition should be as specific as possible for changes
affecting existing functionality. 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.
- 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.
- Wrap the news message so that it will fit in the typical terminal,
that is, at most 80 characters wide. Ideally a bit less.
@@ -65,10 +53,6 @@ but you should follow some basic guidelines:
A new module is available: 'services.foo'.
Since this news announces newly available functionality, its
condition should not require `config.services.foo.enable`; otherwise
users who may want the new module will not see the news.
If the module is platform specific, e.g., a service module using
systemd, then a condition like
@@ -76,6 +60,5 @@ but you should follow some basic guidelines:
condition = hostPlatform.isLinux;
```
should be added to avoid showing the news on unsupported platforms.
Use the `create-news-entry` generator described above to scaffold
this entry as part of your contribution.
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.
-24
View File
@@ -1,24 +0,0 @@
# 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.
+5 -24
View File
@@ -54,13 +54,6 @@ 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:
@@ -169,12 +162,6 @@ 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:
@@ -192,22 +179,16 @@ $ nix run .#tests -- alacritty
# Run a specific test
$ nix run .#tests -- test-alacritty-empty-settings
# List integration tests
# Run integration tests
$ nix run .#tests -- -t -l
# Run all integration tests
$ nix run .#tests -- -t integration-test-
# Interactive test selection (requires fzf)
$ nix run .#tests -- -i
$ python3 tests/tests.py -i
# Pass additional nix build flags
$ nix run .#tests -- alacritty -- --verbose
```
Integration tests are only exposed on Linux. On other platforms, integration
test discovery may report no matching tests.
## Manual test commands {#sec-tests-manual}
For advanced usage or CI environments, you can also run tests manually using nix build commands.
@@ -221,7 +202,7 @@ $ nix-build --pure --option allow-import-from-derivation false tests -A build.al
in the project root. List all test cases through
``` shell
$ nix run .#tests -- -l
$ nix-build --pure tests --option allow-import-from-derivation false -A list
```
and run an individual test, for example `alacritty-empty-settings`,
@@ -236,13 +217,13 @@ and may cause failures. To run against the Nixpkgs from the `flake.lock` file,
use instead e.g.
``` shell
$ nix build .#test-all
$ nix build --option allow-import-from-derivation false .#test-all
```
or
``` shell
$ nix build .#test-alacritty-empty-settings
$ nix build --option allow-import-from-derivation false .#test-alacritty-empty-settings
```
Some tests may be marked with `enableLegacyIfd`, those may be run by run with e.g.
+1 -1
View File
@@ -31,7 +31,7 @@ this package. This can typically be done in two ways.
should work OK. With flakes, pass the unstable package set as
described in
[How do I install packages from Nixpkgs unstable?](unstable.md#_how_do_i_install_packages_from_nixpkgs_unstable)
[How do I install packages from Nixpkgs unstable?](#_how_do_i_install_packages_from_nixpkgs_unstable)
and then use the extra module argument:
``` nix
+4 -4
View File
@@ -6,26 +6,26 @@ Home Manager can be used in three primary ways:
NixOS and Darwin, this is the only available choice. It is also
recommended for people on NixOS or Darwin that want to manage their
home directory independently of the system as a whole. See
[Standalone installation](installation/standalone.md#sec-install-standalone) for instructions
[Standalone installation](#sec-install-standalone) for instructions
on how to perform this installation.
2. As a module within a NixOS system configuration. This allows the
user profiles to be built together with the system when running
`nixos-rebuild`. See [NixOS module](installation/nixos.md#sec-install-nixos-module) for a
`nixos-rebuild`. See [NixOS module](#sec-install-nixos-module) for a
description of this setup.
3. As a module within a
[nix-darwin](https://github.com/nix-darwin/nix-darwin/) system
configuration. This allows the user profiles to be built together
with the system when running `darwin-rebuild`. See [nix-darwin
module](installation/nix-darwin.md#sec-install-nix-darwin-module) for a description of this
module](#sec-install-nix-darwin-module) for a description of this
setup.
:::{.note}
In this chapter we describe how to install Home Manager in the standard
way using channels. If you prefer to use [Nix
Flakes](https://wiki.nixos.org/wiki/Flakes) then please see the instructions
in [nix flakes](nix-flakes.md#ch-nix-flakes).
in [nix flakes](#ch-nix-flakes).
:::
```{=include=} sections
+4 -17
View File
@@ -52,9 +52,6 @@ home-manager.users.eve = { pkgs, ... }: {
and after a `darwin-rebuild switch` the user eve's environment should
include a basic Bash configuration and the packages atool and httpie.
Home Manager activation runs as part of nix-darwin activation for each
configured user.
If you do not plan on having Home Manager manage your shell
configuration then you must add either
@@ -106,23 +103,13 @@ Nixpkgs.
:::
:::{.note}
Home Manager passes extra module arguments to each
`home-manager.users.<name>` module:
Home Manager will pass `osConfig` as a module argument to any modules
you create. This contains the system's nix-darwin configuration.
``` nix
{ lib, pkgs, osConfig, darwinConfig, osClass, modulesPath, ... }:
{ lib, pkgs, osConfig, ... }:
```
Here `osConfig` contains the system's nix-darwin configuration and
`darwinConfig` is a Darwin-specific alias for the same value. The `lib`
argument is Home Manager's extended library. You can pass additional module
arguments with `home-manager.extraSpecialArgs`.
:::
:::{.note}
Use `home-manager.sharedModules` to add Home Manager modules to every user
declared under `home-manager.users`.
:::
Once installed you can see [Using Home Manager](../usage.md#ch-usage) for a more detailed
Once installed you can see [Using Home Manager](#ch-usage) for a more detailed
description of Home Manager and how to use it.
+6 -25
View File
@@ -85,21 +85,12 @@ httpie.
:::{.note}
If `nixos-rebuild switch` does not result in the environment you expect,
then the service to inspect depends on the activation mode.
By default, Home Manager activates each configured user during boot and
system rebuilds through a NixOS system service:
you can take a look at the output of the Home Manager activation script
output using
``` shell
$ systemctl status "home-manager-$USER.service"
```
If `home-manager.startAsUserService = true` is set, Home Manager instead
activates through the user's systemd service:
``` shell
$ systemctl --user status home-manager.service
```
:::
If you do not plan on having Home Manager manage your shell
@@ -153,23 +144,13 @@ Nixpkgs.
:::
:::{.note}
Home Manager passes extra module arguments to each
`home-manager.users.<name>` module:
Home Manager will pass `osConfig` as a module argument to any modules
you create. This contains the system's NixOS configuration.
``` nix
{ lib, pkgs, osConfig, nixosConfig, osClass, modulesPath, ... }:
{ lib, pkgs, osConfig, ... }:
```
Here `osConfig` contains the system's NixOS configuration and `nixosConfig`
is a NixOS-specific alias for the same value. The `lib` argument is Home
Manager's extended library. You can pass additional module arguments with
`home-manager.extraSpecialArgs`.
:::
:::{.note}
Use `home-manager.sharedModules` to add Home Manager modules to every user
declared under `home-manager.users`.
:::
Once installed you can see [Using Home Manager](../usage.md#ch-usage) for a more detailed
Once installed you can see [Using Home Manager](#ch-usage) for a more detailed
description of Home Manager and how to use it.
+1 -1
View File
@@ -71,5 +71,5 @@ checkout of the repository then you can use the
[home-manager.path](#opt-programs.home-manager.path) option to specify the absolute
path to the repository.
Once installed you can see [Using Home Manager](../usage.md#ch-usage) for a more detailed
Once installed you can see [Using Home Manager](#ch-usage) for a more detailed
description of Home Manager and how to use it.
+1 -1
View File
@@ -1,6 +1,6 @@
# Home Manager Manual {#home-manager-manual}
## Version 26.11 (unstable)
## Version 26.05
```{=include=} preface
+3 -3
View File
@@ -12,18 +12,18 @@ flake in three ways:
NixOS and Darwin, this is the only available choice. It is also
recommended for people on NixOS or Darwin that want to manage their
home directory independently of the system as a whole. See
[Standalone setup](nix-flakes/standalone.md#sec-flakes-standalone) for instructions on how
[Standalone setup](#sec-flakes-standalone) for instructions on how
to perform this installation.
2. As a module within a NixOS system configuration. This allows the
user profiles to be built together with the system when running
`nixos-rebuild`. See [NixOS module](nix-flakes/nixos.md#sec-flakes-nixos-module) for a
`nixos-rebuild`. See [NixOS module](#sec-flakes-nixos-module) for a
description of this setup.
3. As a module within a [nix-darwin](https://github.com/nix-darwin/nix-darwin/)
system configuration. This allows the user profiles to be built
together with the system when running `darwin-rebuild`. See
[nix-darwin module](nix-flakes/nix-darwin.md#sec-flakes-nix-darwin-module) for a
[nix-darwin module](#sec-flakes-nix-darwin-module) for a
description of this setup.
Advanced users may want Home Manager's `nixpkgs` input to follow the
+1 -1
View File
@@ -8,7 +8,7 @@ Manager's flake module, `flakeModules.home-manager`.
description = "flake-parts configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager.url = "github:nix-community/home-manager";
flake-parts.url = "github:hercules-ci/flake-parts";
};
+3 -7
View File
@@ -8,7 +8,7 @@ to that of NixOS. The `flake.nix` would be:
description = "Darwin configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
darwin.url = "github:nix-darwin/nix-darwin";
darwin.inputs.nixpkgs.follows = "nixpkgs";
home-manager.url = "github:nix-community/home-manager";
@@ -45,12 +45,8 @@ module argument from within the module graph itself. For values that originate
outside the module graph, such as flake inputs, prefer
`home-manager.extraSpecialArgs`.
Use `home-manager.sharedModules` for Home Manager modules or settings that
should be imported by every user declared in `home-manager.users`.
The Home Manager configuration is also rebuilt with the nix-darwin
generations. The rebuild command here may be
`darwin-rebuild switch --flake ~/.config/darwin`.
and it is also rebuilt with the nix-darwin generations. The rebuild
command here may be `darwin-rebuild switch --flake ~/.config/darwin`.
You can use the above `flake.nix` as a template in `~/.config/darwin` by
+1 -4
View File
@@ -8,7 +8,7 @@ be as follows:
description = "NixOS configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager.url = "github:nix-community/home-manager";
};
@@ -43,9 +43,6 @@ module argument from within the module graph itself. For values that originate
outside the module graph, such as flake inputs, prefer
`home-manager.extraSpecialArgs`.
Use `home-manager.sharedModules` for Home Manager modules or settings that
should be imported by every user declared in `home-manager.users`.
The Home Manager configuration is then part of the NixOS configuration
and is automatically rebuilt with the system when using the appropriate
command for the system, such as
+1 -1
View File
@@ -33,5 +33,5 @@
Unlike the channel-based setup, `home.nix` will be evaluated when
the flake is built, so it must be present before bootstrap of Home
Manager from the flake. See [Configuration Example](../usage/configuration.md#sec-usage-configuration) for
Manager from the flake. See [Configuration Example](#sec-usage-configuration) for
introduction about writing a Home Manager configuration.
+1 -1
View File
@@ -27,7 +27,7 @@ imported Home Manager modules, use `extraSpecialArgs` in the call to
``` nix
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager.url = "github:nix-community/home-manager";
};
+1 -1
View File
@@ -43,7 +43,7 @@ option will state the expected type, for
[programs.emacs.enable](#opt-programs.emacs.enable) you will see "Type: boolean". You
there also find information about the default value and a description of
the option. You can find the complete option documentation in
[Home Manager Configuration Options](options.md#ch-options) or directly in the terminal by running
[Home Manager Configuration Options](#ch-options) or directly in the terminal by running
``` shell
man home-configuration.nix
+1 -1
View File
@@ -39,7 +39,7 @@ This error is about a file that Home Manager wants to manage as a
symbolic link in your home directory. It is separate from package
profile collisions, which usually mention `installPackages` or a
`collision between .../bin/...` path. For package collisions, see
[Why is there a collision error when switching generation?](../faq/collision.md#_why_is_there_a_collision_error_when_switching_generation).
[Why is there a collision error when switching generation?](#_why_is_there_a_collision_error_when_switching_generation).
## Resolving file collisions {#sec-usage-dotfiles-collisions}
+1 -1
View File
@@ -8,7 +8,7 @@ without changing release branches.
If you want to move from one release branch to another, such as
`release-25.05` to `release-25.11`, or between a release branch and
`master`, see
[Upgrading to a new Home Manager release](upgrading.md#sec-upgrade-release).
[Upgrading to a new Home Manager release](#sec-upgrade-release).
## Flake-Based Configurations {#sec-updating-flakes}
+5 -5
View File
@@ -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 nixpkgs-unstable)
- **master**: Development branch (tracks nixos-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/nixpkgs-unstable";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
@@ -169,7 +169,7 @@ affected configuration.
### Common Issues {#sec-upgrade-release-state-troubleshooting-common-issues}
Check the [Home Manager Release Notes](../release-notes/release-notes.md#ch-release-notes) for breaking changes.
Check the [Home Manager Release Notes](#ch-release-notes) for breaking changes.
1. **Version Mismatch Warning**: If you see warnings about version
mismatches, ensure your Home Manager branch matches the Nixpkgs
@@ -199,4 +199,4 @@ the expected release branch.
## Additional Resources {#sec-upgrade-release-resources}
- [NixOS Stable Release Notes](https://nixos.org/manual/nixos/stable/release-notes)
- [Home Manager Release Notes](../release-notes/release-notes.md#ch-release-notes)
- [Home Manager Release Notes](#ch-release-notes)
-231
View File
@@ -1,231 +0,0 @@
#!/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("<", "&lt;").replace(">", "&gt;")
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())
-132
View File
@@ -1,132 +0,0 @@
#!/usr/bin/env python3
import html
import json
import re
import sys
from pathlib import Path
ANCHOR = re.compile(r'<a id="([^"]+)"></a>')
SPECIAL_PAGES = {
Path("manual.md"): "index.html",
Path("preface.md"): "index.html",
Path("options.md"): "options/home-manager/index.html",
Path("nixos-options.md"): "options/nixos/index.html",
Path("nix-darwin-options.md"): "options/nix-darwin/index.html",
}
OPTION_PREFIXES = {
"opt-": "options/home-manager",
"nixos-opt-": "options/nixos",
"nix-darwin-opt-": "options/nix-darwin",
}
ROUTER = """
(function () {
var h = location.hash || "", a = h.slice(1), t = __DEFAULT__;
var m = __ANCHORS__, p = __PREFIXES__;
function o(a) {
for (var k in p) {
if (a.indexOf(k) !== 0) continue;
var s = a.slice(k.length).replace(/[<>]/g, "_").split("."), q = s[0];
if ((q === "programs" || q === "services") && s.length > 1) q += "/" + s[1];
return p[k] + "/" + q + ".html";
}
return null;
}
if (a) t = Object.prototype.hasOwnProperty.call(m, a) ? m[a] : o(a) || t;
if (t === null) return;
var n = t + (location.search || "") + h;
var c = location.pathname.split("/").pop() + location.search + h;
if (n !== c) location.replace(n);
}());
""".strip()
def page_for(source, path):
relative = path.relative_to(source)
return SPECIAL_PAGES.get(relative, relative.with_suffix(".html").as_posix())
def anchors_for(source, roots):
anchors = {}
for root in roots:
files = [root] if root.is_file() else sorted(root.rglob("*.md"))
for path in files:
page = page_for(source, path)
for anchor in ANCHOR.findall(path.read_text(encoding="utf-8")):
anchors.setdefault(anchor, page)
return anchors
def script(default, anchors):
js = (
ROUTER.replace("__DEFAULT__", json.dumps(default))
.replace(
"__ANCHORS__", json.dumps(anchors, sort_keys=True, separators=(",", ":"))
)
.replace("__PREFIXES__", json.dumps(OPTION_PREFIXES, separators=(",", ":")))
)
return "\n".join(
[
" <script>",
" //<![CDATA[",
*[f" {line}" for line in js.splitlines()],
" //]]>",
" </script>",
]
)
def write_redirect(output, name, destination, anchors=None):
escaped = html.escape(destination, quote=True)
(output / name).write_text(
f"""<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta charset="utf-8" />
{script(destination, anchors or {})}
<meta http-equiv="refresh" content="0; url={escaped}" />
<link rel="canonical" href="{escaped}" />
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="{escaped}">{escaped}</a>.</p>
</body>
</html>
""",
encoding="utf-8",
)
def inject_root_router(output, anchors):
index = output / "index.html"
text = index.read_text(encoding="utf-8")
index.write_text(text.replace("<head>", f"<head>\n{script(None, anchors)}", 1))
def main():
source, output = map(Path, sys.argv[1:])
manual_roots = [
path
for path in source.iterdir()
if path.name != "options" and (path.is_dir() or path.suffix == ".md")
]
manual_anchors = anchors_for(source, manual_roots)
release_anchors = anchors_for(source, [source / "release-notes"])
inject_root_router(output, manual_anchors)
for name, destination, anchors in [
("index.xhtml", "index.html", manual_anchors),
("options.html", "options/home-manager/index.html", {}),
("options.xhtml", "options/home-manager/index.html", {}),
("nixos-options.xhtml", "options/nixos/index.html", {}),
("nix-darwin-options.xhtml", "options/nix-darwin/index.html", {}),
("release-notes.xhtml", "release-notes/release-notes.html", release_anchors),
]:
write_redirect(output, name, destination, anchors)
if __name__ == "__main__":
main()
-23
View File
@@ -1,23 +0,0 @@
{
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"
''
-221
View File
@@ -1,221 +0,0 @@
#!/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())
-32
View File
@@ -1,32 +0,0 @@
#!/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())
+2 -3
View File
@@ -1,10 +1,9 @@
# 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
+1 -1
View File
@@ -36,7 +36,7 @@ This release has the following notable changes:
optionally also activate it. The recommended installation method for
a standalone Home Manager setup with Nix flakes uses this new
command. The standard installation method remains the same but uses
the new command internally. See [sec-flakes-standalone](../nix-flakes/standalone.md#sec-flakes-standalone) for
the new command internally. See [sec-flakes-standalone](#sec-flakes-standalone) for
more.
## State Version Changes {#sec-release-23.05-state-version-changes}
+1 -1
View File
@@ -19,7 +19,7 @@ This release has the following notable changes:
- The Home Manager test suite has been removed from the main Nix Flake
since it caused unnecessary evaluations and downloads. Instead the
tests are available through a Nix Flake file inside the `tests`
directory. See [Tests](../contributing/tests.md#sec-tests) for example commands.
directory. See [](#sec-tests) for example commands.
## State Version Changes {#sec-release-25.05-state-version-changes}
+2 -2
View File
@@ -16,7 +16,7 @@ This release has the following notable changes:
term removed all together. If you have developed tooling that
directly call the generated activation script, then you are
encouraged to adapt to the new behavior. See the
[Activation](../internals/activation.md#sec-internals-activation) section in the manual for
[Activation](#sec-internals-activation) section in the manual for
details on how to call the activation script.
- The `home-manager switch` command now offers a `--rollback` option.
@@ -25,7 +25,7 @@ This release has the following notable changes:
previously possible to accomplish this by manually activating an old
generation, it always created a new profile generation. The new
behavior mirrors the behavior of `nixos-rebuild switch --rollback`.
See the [Rollbacks](../usage/rollbacks.md#sec-usage-rollbacks) section for more.
See the [Rollbacks](#sec-usage-rollbacks) section for more.
- The `home-manager switch` command now offers a
`--specialisation NAME` option. When given, the switch activates the
+1 -1
View File
@@ -61,7 +61,7 @@ This release has the following notable changes:
[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](../usage/modular-services.md#sec-usage-modular-services) for
user systemd units. See [Modular Services](#sec-usage-modular-services) for
details.
- Dedicated modules were added for several VSCode forks (`programs.cursor`,
-35
View File
@@ -1,35 +0,0 @@
# 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.
- On Darwin, Home Manager launchd agents now support
[](#opt-launchd.agents._name_.domain) to choose either the user's GUI or
background launchd domain. Agents use the GUI domain by default. Set the
domain to `user` for agents that should run without an active graphical login
session.
## 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.
- The KDL options under `programs.zellij` will now correctly escape backslashes
in string values. For example, the Nix string `"\\"` will now correctly
generate the KDL string `"\\"`. Previously, this would have generated `"\"`,
which is invalid KDL. The following options are affected:
- `programs.zellij.settings`
- `programs.zellij.themes`
- `programs.zellij.layouts`
Generated
+4 -4
View File
@@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1783604885,
"narHash": "sha256-tzMgSkV7kljEkqIjlgV6F+n+xD+/a35Db8bs7a4BFAo=",
"lastModified": 1784280462,
"narHash": "sha256-DtoqIqM7VkR6NxAkcLpMwmi02USwWb3JdmNGLyhthc0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "767b0d3ec98a143ad9ed7dfc0d5553510ac27133",
"rev": "293d6abedf0478e681a4dfcfcb35b30fc796a32f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
+13 -38
View File
@@ -1,7 +1,7 @@
{
description = "Home Manager for Nix";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
outputs =
{
@@ -45,34 +45,15 @@
}
// (
let
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;
forAllPkgs =
f:
nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (system: f nixpkgs.legacyPackages.${system});
forCI = nixpkgs.lib.genAttrs [
"aarch64-darwin"
"x86_64-linux"
];
releaseInfo = nixpkgs.lib.importJSON ./release.json;
docsFor =
pkgs:
import ./docs {
inherit pkgs;
inherit (releaseInfo) release isReleaseBranch;
};
testChunks =
system:
let
@@ -175,13 +156,12 @@
lib.mapAttrs' renameTestPkg tests;
in
{
formatter = forSupportedPkgs (pkgs: pkgs.callPackage ./home-manager/formatter.nix { });
formatter = forAllPkgs (pkgs: pkgs.callPackage ./home-manager/formatter.nix { });
# TODO: increase buildbot testing scope
buildbot = forCI (
system:
let
docs = docsFor nixpkgs.legacyPackages.${system};
allIntegrationTests = integrationTests system;
workingIntegrationTests = nixpkgs.lib.filterAttrs (
name: _:
@@ -191,27 +171,22 @@
]
) allIntegrationTests;
in
(testChunks system)
// workingIntegrationTests
// {
docs-html = docs.manual.html;
docs-json = docs.options.json;
docs-jsonModuleMaintainers = docs.jsonModuleMaintainers;
docs-manpages = docs.manPages;
}
(testChunks system) // workingIntegrationTests
);
packages = forAllPkgs (
pkgs:
let
docs = docsFor pkgs;
releaseInfo = nixpkgs.lib.importJSON ./release.json;
docs = import ./docs {
inherit pkgs;
inherit (releaseInfo) release isReleaseBranch;
};
hmPkg = pkgs.callPackage ./home-manager { path = "${self}"; };
in
{
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 {
@@ -237,11 +212,11 @@
}
);
devShells = forSupportedPkgs (pkgs: {
devShells = forAllPkgs (pkgs: {
default = pkgs.callPackage ./home-manager/devShell.nix { };
});
legacyPackages = forSupportedPkgs (
legacyPackages = forAllPkgs (
pkgs:
let
inherit (pkgs.stdenv.hostPlatform) system;
-1
View File
@@ -18,7 +18,6 @@ pkgs.treefmt.withConfig {
deadnix
keep-sorted
nixf-diagnose
ruff
];
settings = pkgs.lib.importTOML ../treefmt.toml;
}
+2 -3
View File
@@ -291,7 +291,6 @@ function doInspectOption() {
for path in "${paths[@]}"; do
nix-instantiate --eval --json \
--arg nixos "$modulesExpr" \
"${extraArgs[@]}" \
--argstr path "$path" \
--arg recursive "$recursive" \
"$NIXOS_OPTION_NIX" \
@@ -314,7 +313,7 @@ function doInit() {
fi
local homeManagerUrl="github:nix-community/home-manager"
local nixpkgsUrl="github:nixos/nixpkgs/nixpkgs-unstable"
local nixpkgsUrl="github:nixos/nixpkgs/nixos-unstable"
while (( $# > 0 )); do
local opt="$1"
@@ -1280,7 +1279,7 @@ while [[ $# -gt 0 ]]; do
export VERBOSE=1
;;
--version)
echo 26.11-pre
echo 26.05-pre
exit 0
;;
*)
-261
View File
@@ -1,261 +0,0 @@
# 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."
+5 -5
View File
@@ -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-07-19 08:01+0000\n"
"Last-Translator: Artur Sannikov <weblate.rbpxz@8shield.net>\n"
"PO-Revision-Date: 2026-03-31 16:16+0000\n"
"Last-Translator: Lorenzo Bevilacqua <lorenzobevilacqua02@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 2026.8.dev0\n"
"X-Generator: Weblate 5.17-dev\n"
#. translators: For example: "home-manager: missing argument for --cores"
#: home-manager/home-manager:16
@@ -104,7 +104,7 @@ msgstr ""
#: home-manager/home-manager:153
msgid "Sanity checking Nix"
msgstr "Verifica di correttezza di Nix"
msgstr "Controllando Nix"
#: home-manager/home-manager:173
msgid "Could not find suitable profile directory, tried %s and %s"
@@ -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 "La configurazione non contiene la specializzazione \"%s\""
msgstr ""
#: home-manager/home-manager:876
msgid "No generation with ID %s"
+4 -4
View File
@@ -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-07-19 08:01+0000\n"
"Last-Translator: Artur Sannikov <weblate.rbpxz@8shield.net>\n"
"PO-Revision-Date: 2026-03-31 16:16+0000\n"
"Last-Translator: NikSne <commits@niksne.ru>\n"
"Language-Team: Russian <https://hosted.weblate.org/projects/home-manager/cli/"
"ru/>\n"
"Language: ru\n"
@@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Weblate 2026.8.dev0\n"
"X-Generator: Weblate 5.17-dev\n"
#. translators: For example: "home-manager: missing argument for --cores"
#: home-manager/home-manager:16
@@ -205,7 +205,7 @@ msgstr "Не могу начать сборку в директории, защ
#: home-manager/home-manager:822
msgid "The configuration does not contain the specialisation \"%s\""
msgstr "В конфигурации отсутствует специализация «%s»"
msgstr ""
#: home-manager/home-manager:876
msgid "No generation with ID %s"
+4 -5
View File
@@ -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-27 11:11+0000\n"
"Last-Translator: qb114514 <GNUqb114514@outlook.com>\n"
"PO-Revision-Date: 2026-05-15 00:11+0000\n"
"Last-Translator: soweli Emi <emi@alchemi.dev>\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.6.dev0\n"
"X-Generator: Weblate 2026.5.dev0\n"
#. translators: For example: "home-manager: missing argument for --cores"
#: home-manager/home-manager:16
@@ -185,9 +185,8 @@ 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 "lipu lawa jo ala lipu Specialisation \"%s\""
msgstr ""
#: home-manager/home-manager:876
msgid "No generation with ID %s"
+5 -6
View File
@@ -8,9 +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-07-19 08:01+0000\n"
"Last-Translator: reducedradius "
"<137701630+flytothehighest@users.noreply.github.com>\n"
"PO-Revision-Date: 2026-03-31 16:13+0000\n"
"Last-Translator: Zexin Yuan <yuan.zx@outlook.com>\n"
"Language-Team: Chinese (Simplified Han script) <https://hosted.weblate.org/"
"projects/home-manager/cli/zh_Hans/>\n"
"Language: zh_Hans\n"
@@ -18,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.8.dev0\n"
"X-Generator: Weblate 5.17-dev\n"
#. translators: For example: "home-manager: missing argument for --cores"
#: home-manager/home-manager:16
@@ -28,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 "%s: %s 只能在 %s 后使用"
msgstr ""
#: home-manager/home-manager:71
msgid "No configuration file found at %s"
@@ -195,7 +194,7 @@ msgstr "无法在只读目录中运行构建"
#: home-manager/home-manager:822
msgid "The configuration does not contain the specialisation \"%s\""
msgstr "配置不包含专门化的 “%s”"
msgstr ""
#: home-manager/home-manager:876
msgid "No generation with ID %s"
+34 -1
View File
@@ -2,5 +2,38 @@
{
inherit ((import ../modules/lib/stdlib-extended.nix lib)) hm;
homeManagerConfiguration = import ./eval-config.nix;
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;
};
};
};
}
-33
View File
@@ -1,33 +0,0 @@
{
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;
};
};
}
+20 -35
View File
@@ -1,14 +1,7 @@
# Extract maintainers from Home Manager modules using meta.maintainers
# This script evaluates all Home Manager modules and extracts the merged maintainer information
let
flakeLock = builtins.fromJSON (builtins.readFile ../../flake.lock);
nixpkgsNodeName = flakeLock.nodes.root.inputs.nixpkgs;
nixpkgsLocked = flakeLock.nodes.${nixpkgsNodeName}.locked;
pkgs = import (fetchTarball {
url = "https://github.com/${nixpkgsLocked.owner}/${nixpkgsLocked.repo}/archive/${nixpkgsLocked.rev}.tar.gz";
sha256 = nixpkgsLocked.narHash;
}) { };
pkgs = import <nixpkgs> { };
lib = import ../../modules/lib/stdlib-extended.nix pkgs.lib;
releaseInfo = pkgs.lib.importJSON ../../release.json;
@@ -32,23 +25,18 @@ let
file:
let
fileContent = import file;
# Arguments expected by docs/home-manager-manual.nix.
evaluated =
if lib.isFunction fileContent then
fileContent {
inherit lib;
inherit (pkgs)
callPackage
mdbook
python3
stdenv
;
inherit (pkgs) stdenv lib;
documentation-highlighter = { };
revision = "unknown";
home-manager-options = {
home-manager = { };
nixos = { };
nix-darwin = { };
};
nixos-render-docs = { };
}
else
fileContent;
@@ -71,30 +59,27 @@ let
allMaintainerObjects = extractMaintainerObjects maintainers ++ additionalMaintainerObjects;
getMaintainerName = maintainer: maintainer.github or maintainer.name or null;
allMaintainerNames = lib.filter (name: name != null) (map getMaintainerName allMaintainerObjects);
allMaintainerNames = lib.filter (name: name != null) (
map (maintainer: maintainer.github or maintainer.name or null) allMaintainerObjects
);
hmMaintainers = import ../../modules/lib/maintainers.nix;
hmMaintainerNames = lib.filter (name: name != null) (
map getMaintainerName (lib.attrValues hmMaintainers)
);
hmMaintainerNames = lib.attrNames hmMaintainers;
maintainerDetails = lib.pipe allMaintainerObjects [
(lib.filter (obj: (obj.github or obj.name or null) != null))
(map (
obj:
let
maintainerName = obj.github or obj.name;
in
{
name = maintainerName;
value = obj // {
source =
if categorizedMaintainers.home-manager ? ${maintainerName} then "home-manager" else "nixpkgs";
};
}
))
(map (obj: {
name = obj.github or obj.name;
value = obj // {
source =
if categorizedMaintainers.home-manager ? ${obj.github} then
"home-manager"
else if categorizedMaintainers.nixpkgs ? ${obj.github} then
"nixpkgs"
else
throw "${obj.github} is neither a home-manager or nixpkgs maintainer";
};
}))
lib.listToAttrs
];
+17 -32
View File
@@ -17,67 +17,52 @@ 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__":
+1 -4
View File
@@ -16,7 +16,6 @@ from pathlib import Path
class NixEvalError(Exception):
"""Custom exception for errors during Nix evaluation."""
pass
@@ -40,9 +39,7 @@ 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}")
+11 -18
View File
@@ -40,7 +40,6 @@ 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."""
@@ -55,12 +54,9 @@ 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)
@@ -91,7 +87,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.
@@ -118,12 +114,9 @@ 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")
@@ -155,16 +148,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()
+41 -108
View File
@@ -60,7 +60,6 @@ query($owner: String!, $repo: String!, $prNumber: Int!) {
class GHError(Exception):
"""Custom exception for errors related to 'gh' CLI commands."""
pass
@@ -95,28 +94,15 @@ 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()
@@ -157,15 +143,7 @@ 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",
)
@@ -173,12 +151,7 @@ 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",
)
@@ -189,14 +162,17 @@ 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(
@@ -219,32 +195,22 @@ 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,
)
@@ -254,33 +220,15 @@ 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()
@@ -289,15 +237,13 @@ 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()
@@ -311,15 +257,13 @@ 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.")
@@ -334,29 +278,20 @@ 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:
@@ -369,9 +304,7 @@ 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:
+12 -18
View File
@@ -14,38 +14,32 @@ 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__":
+2 -6
View File
@@ -13,12 +13,8 @@ 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")
+74 -10
View File
@@ -13,10 +13,12 @@ let
mkIf
mkMerge
mkDefault
mkAliasOptionModule
types
literalExpression
escapeShellArg
hm
getAttrFromPath
any
optional
;
@@ -138,10 +140,46 @@ in
{
meta.maintainers = [ lib.maintainers.league ];
imports = [
(mkAliasOptionModule
[ "xsession" "pointerCursor" "package" ]
[
"home"
"pointerCursor"
"package"
]
)
(mkAliasOptionModule
[ "xsession" "pointerCursor" "name" ]
[
"home"
"pointerCursor"
"name"
]
)
(mkAliasOptionModule
[ "xsession" "pointerCursor" "size" ]
[
"home"
"pointerCursor"
"size"
]
)
(mkAliasOptionModule
[ "xsession" "pointerCursor" "defaultCursor" ]
[
"home"
"pointerCursor"
"x11"
"defaultCursor"
]
)
];
options = {
home.pointerCursor = mkOption {
type = pointerCursorModule;
default = { };
type = types.nullOr pointerCursorModule;
default = null;
description = ''
Cursor configuration.
@@ -166,10 +204,9 @@ in
let
# Check if enable option was explicitly defined by the user
enableDefined = any (x: x ? enable) opts.definitions;
pointerCursorDefined = opts.highestPrio != (lib.mkOptionDefault { }).priority;
# Determine if cursor configuration should be enabled
enable = if enableDefined then cfg.enable else pointerCursorDefined;
enable = if enableDefined then cfg.enable else cfg != null;
in
mkMerge [
(mkIf enable (mkMerge [
@@ -203,7 +240,7 @@ in
(mkIf cfg.dotIcons.enable {
# Add symlink of cursor icon directory to $HOME/.icons, needed for
# backwards compatibility with some applications. See:
# https://specifications.freedesktop.org/icon-theme/latest/#directory_layout
# https://specifications.freedesktop.org/icon-theme-spec/latest/ar01s03.html
home.file.".icons/default/index.theme".source =
"${defaultIndexThemePackage}/share/icons/default/index.theme";
home.file.".icons/${cfg.name}".source = "${cfg.package}/share/icons/${cfg.name}";
@@ -248,12 +285,39 @@ in
]))
{
warnings = optional (pointerCursorDefined && !enableDefined) ''
Relying on `home.pointerCursor` to enable cursor config generation is deprecated.
Please update your configuration to explicitly set:
warnings =
(optional
(any
(
x:
getAttrFromPath (
[
"xsession"
"pointerCursor"
]
++ [ x ]
++ [ "isDefined" ]
) options
)
[
"package"
"name"
"size"
"defaultCursor"
]
)
''
The option `xsession.pointerCursor` has been merged into `home.pointerCursor` and will be removed
in the future. Please change to set `home.pointerCursor` directly and enable `home.pointerCursor.x11.enable`
to generate x11 specific cursor configurations. You can refer to the documentation for more details.
''
)
++ (optional (opts.highestPrio != (lib.mkOptionDefault { }).priority && cfg == null) ''
Setting home.pointerCursor to null is deprecated.
Please update your configuration to explicitly set:
home.pointerCursor.enable = ${lib.boolToString enable};
'';
home.pointerCursor.enable = false;
'');
}
];
}
+22 -68
View File
@@ -196,8 +196,8 @@ in
targetPath="$HOME/$relativePath"
if [[ -e "$targetPath" && ! -L "$targetPath" ]] ; then
if [[ -n "$HOME_MANAGER_BACKUP_COMMAND" ]] ; then
verboseEcho "Running '$HOME_MANAGER_BACKUP_COMMAND' on '$targetPath'."
run $HOME_MANAGER_BACKUP_COMMAND "$targetPath" || errorEcho "Running '$HOME_MANAGER_BACKUP_COMMAND' on '$targetPath' failed."
verboseEcho "Running $HOME_MANAGER_BACKUP_COMMAND $targetPath."
run $HOME_MANAGER_BACKUP_COMMAND "$targetPath" || errorEcho "Running `$HOME_MANAGER_BACKUP_COMMAND` on '$targetPath' failed."
elif [[ -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then
# The target exists, back it up
backup="$targetPath.$HOME_MANAGER_BACKUP_EXT"
@@ -354,63 +354,6 @@ in
# contain the recursion root, not the visited files.
declare -A seenTargets
function setExecutableBit() {
local target="$1"
local executable="$2"
if [[ $executable == inherit ]]; then
# Don't change file mode if it should match the source.
:
elif [[ $executable ]]; then
chmod +x "$target"
else
chmod -x "$target"
fi
}
function insertFileEntry() {
local source="$1"
local target="$2"
local executable="$3"
local isExecutable
[[ -x $source ]] && isExecutable=1 || isExecutable=""
# Link the file into the home file directory if possible,
# i.e., if the executable bit of the source is the same we
# expect for the target. Otherwise, we copy the file and
# set the executable bit to the expected value.
if [[ $executable == inherit || $isExecutable == $executable ]]; then
ln -s "$source" "$target"
else
cp "$source" "$target"
setExecutableBit "$target" "$executable"
fi
}
function setLinkedFileExecutableBit() {
local target="$1"
local executable="$2"
local isExecutable
if [[ -d $target || ! -e $target ]]; then
return
fi
[[ -x $target ]] && isExecutable=1 || isExecutable=""
if [[ $executable == inherit || $isExecutable == $executable ]]; then
return
fi
local tmp
tmp="$(mktemp "$target.XXXXXX")"
cp "$target" "$tmp"
setExecutableBit "$tmp" "$executable"
mv -f "$tmp" "$target"
}
function insertFile() {
local source="$1"
local relTarget="$2"
@@ -466,19 +409,30 @@ in
else
lndir -silent "$source" "$target"
fi
if [[ $executable != inherit ]]; then
local linkedFile
while IFS= read -r -d "" linkedFile; do
setLinkedFileExecutableBit "$linkedFile" "$executable"
done < <(find "$target" \( -type f -or -type l \) -print0)
fi
else
ln -s "$source" "$target"
fi
else
insertFileEntry "$source" "$target" "$executable"
[[ -x $source ]] && isExecutable=1 || isExecutable=""
# Link the file into the home file directory if possible,
# i.e., if the executable bit of the source is the same we
# expect for the target. Otherwise, we copy the file and
# set the executable bit to the expected value.
if [[ $executable == inherit || $isExecutable == $executable ]]; then
ln -s "$source" "$target"
else
cp "$source" "$target"
if [[ $executable == inherit ]]; then
# Don't change file mode if it should match the source.
:
elif [[ $executable ]]; then
chmod +x "$target"
else
chmod -x "$target"
fi
fi
fi
}
''
+1 -1
View File
@@ -32,7 +32,7 @@ for sourcePath in "$@" ; do
warnEcho "Existing file '$targetPath' is in the way of '$sourcePath', will be skipped since they are the same"
elif [[ ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_COMMAND" ]] ; then
# Next, try to run the custom backup command. Assume this always succeeds.
verboseEcho "Existing file '$targetPath' exists and differs from '$sourcePath'. '$HOME_MANAGER_BACKUP_COMMAND' will be used to backup the file."
verboseEcho "Existing file '$targetPath' exists and differs from '$sourcePath'. `$HOME_MANAGER_BACKUP_COMMAND` will be used to backup the file."
elif [[ ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then
# Next, try to move the file to a backup location if configured and possible
backup="$targetPath.$HOME_MANAGER_BACKUP_EXT"
+32 -37
View File
@@ -184,62 +184,38 @@ in
options = {
home.username = mkOption {
type = types.nonEmptyStr;
defaultText = lib.literalMD ''
`$USER`, for state version < 20.09,
undefined, otherwise.
type = types.str;
defaultText = literalExpression ''
"$USER" for state version < 20.09,
undefined for state version 20.09
'';
example = "jane.doe";
description = ''
The user's username.
If Home Manager is installed as a NixOS or nix-darwin submodule, it is
set to `osConfig.users.users.<name>.name`.
'';
description = "The user's username.";
};
home.uid = mkOption {
type = types.nullOr types.ints.unsigned;
default = null;
example = 1000;
description = ''
The user's uid.
If Home Manager is installed as a NixOS or nix-darwin submodule, it is
set to `osConfig.users.users.<name>.uid`.
'';
description = "The user's uid.";
};
home.homeDirectory = mkOption {
type = types.path;
defaultText = lib.literalMD ''
`$HOME`, for state version < 20.09
undefined, otherwise.
defaultText = literalExpression ''
"$HOME" for state version < 20.09,
undefined for state version 20.09
'';
apply = toString;
example = "/home/jane.doe";
description = ''
The user's home directory. Must be an absolute path.
If Home Manager is installed as a NixOS or nix-darwin submodule, it is
set to `osConfig.users.users.<name>.home`.
'';
description = "The user's home directory. Must be an absolute path.";
};
home.profileDirectory = mkOption {
type = types.path;
default =
if config.submoduleSupport.enable && config.submoduleSupport.externalPackageInstall then
"/etc/profiles/per-user/${cfg.username}"
else if config.nix.useXdg then
"${config.xdg.stateHome}/nix/profile"
else
cfg.homeDirectory + "/.nix-profile";
defaultText = lib.literalMD ''
- `/etc/profiles/per-user/''${home.username}`, if Home Manager is installed as
a submodule and `home-manager.useUserPackages` is enabled, else
- `''${xdg.stateHome}/nix/profile`, if `nix.useXdg` is enabled, else
- `''${home.homeDirectory}/.nix-profile`.
defaultText = literalExpression ''
"''${home.homeDirectory}/.nix-profile" or
"/etc/profiles/per-user/''${home.username}"
'';
readOnly = true;
description = ''
@@ -593,6 +569,17 @@ in
};
config = {
assertions = [
{
assertion = config.home.username != "";
message = "Username could not be determined";
}
{
assertion = config.home.homeDirectory != "";
message = "Home directory could not be determined";
}
];
warnings =
let
hmRelease = config.home.version.release;
@@ -634,6 +621,14 @@ in
lib.mkDefault (builtins.getEnv "HOME")
);
home.profileDirectory =
if config.submoduleSupport.enable && config.submoduleSupport.externalPackageInstall then
"/etc/profiles/per-user/${cfg.username}"
else if config.nix.useXdg then
"${config.xdg.stateHome}/nix/profile"
else
cfg.homeDirectory + "/.nix-profile";
programs.bash.shellAliases = cfg.shellAliases;
programs.zsh.shellAliases = cfg.shellAliases;
programs.fish.shellAliases = cfg.shellAliases;
+1
View File
@@ -46,6 +46,7 @@ let
in
{
meta.maintainers = with lib.maintainers; [
da157
kranzes
];
+39 -261
View File
@@ -14,26 +14,10 @@ let
dstDir = "${config.home.homeDirectory}/Library/LaunchAgents";
launchdConfig =
{ name, config, ... }:
{ name, ... }:
{
options = {
enable = lib.mkEnableOption name;
domain = lib.mkOption {
type = lib.types.enum [
"gui"
"user"
];
default = "gui";
example = "user";
description = ''
The launchd domain to bootstrap this agent into.
The `gui` domain is appropriate for agents that need the user's
Aqua session, such as window managers, hotkey daemons, and other
graphical tools. The `user` domain is appropriate for background
services that do not require a graphical login session.
'';
};
config = lib.mkOption {
type = lib.types.submodule (import ./launchd.nix);
default = { };
@@ -55,14 +39,9 @@ let
};
};
config = lib.mkMerge [
{
config.Label = lib.mkDefault "${labelPrefix}${name}";
}
(lib.mkIf (config.domain == "user") {
config.LimitLoadToSessionType = lib.mkDefault "Background";
})
];
config = {
config.Label = lib.mkDefault "${labelPrefix}${name}";
};
};
# mutateConfig calls /bin/sh with /bin/wait4path to wait for /nix/store before
@@ -95,13 +74,6 @@ let
_n: v: lib.nameValuePair "${v.config.Label}.plist" (toAgent v.config)
) (lib.filterAttrs (_n: v: v.enable) cfg.agents);
agentDomains = lib.mapAttrs' (
_n: v:
lib.nameValuePair "${v.config.Label}.domain" (
pkgs.writeText "${v.config.Label}.domain" "${v.domain}\n"
)
) (lib.filterAttrs (_n: v: v.enable) cfg.agents);
agentsDrv = pkgs.runCommand "home-manager-agents" { } ''
mkdir -p "$out"
@@ -115,20 +87,6 @@ let
ln -s "$src" "$out/$dest"
done
'';
agentDomainsDrv = pkgs.runCommand "home-manager-agent-domains" { } ''
mkdir -p "$out"
declare -A domains
domains=(${
lib.concatStringsSep " " (lib.mapAttrsToList (name: value: "['${name}']='${value}'") agentDomains)
})
for dest in "''${!domains[@]}"; do
src="''${domains[$dest]}"
ln -s "$src" "$out/$dest"
done
'';
in
{
meta.maintainers = with lib.maintainers; [
@@ -171,7 +129,6 @@ in
(lib.mkIf isDarwin {
home.extraBuilderCommands = ''
ln -s "${agentsDrv}" $out/LaunchAgents
ln -s "${agentDomainsDrv}" $out/LaunchAgentDomains
'';
# NOTE: Launch Agent configurations can't be symlinked from the Nix store
@@ -179,53 +136,9 @@ in
home.activation.setupLaunchAgents =
lib.hm.dag.entryAfter [ "writeBoundary" ] # Bash
''
# Get the OS version to see if we can use --wait
version="$(/usr/bin/sw_vers --productVersion | cut -d. -f 1)"
# Disable errexit to ensure we process all agents even if some fail
set +e
readAgentDomain() {
local domainsDir="$1"
local agentName="$2"
local domainFile="$domainsDir/$agentName.domain"
if [[ -n "$domainsDir" && -f "$domainFile" ]]; then
local domainName
domainName="$(<"$domainFile")"
case "$domainName" in
gui|user)
printf '%s\n' "$domainName"
;;
*)
printf 'gui\n'
;;
esac
else
printf 'gui\n'
fi
}
resolveDomain() {
local domainName="$1"
case "$domainName" in
gui)
printf 'gui/%s\n' "$UID"
;;
user)
printf 'user/%s\n' "$UID"
;;
esac
}
agentIsLoaded() {
local domain="$1"
local agentName="$2"
run /bin/launchctl print "$domain/$agentName" >/dev/null 2>&1
}
# Stop an agent if it's running
bootoutAgent() {
local domain="$1"
@@ -233,37 +146,21 @@ in
verboseEcho "Stopping agent '$domain/$agentName'..."
local bootout_output
if [[ "$version" -ge "26" ]]; then
if bootout_output=$(run /bin/launchctl bootout --wait "$domain/$agentName" 2>&1); then
# --wait already makes sure it was unloaded (BUT we can only use it in >=26)
return 0
bootout_output=$(run /bin/launchctl bootout --wait "$domain/$agentName" 2>&1) || {
# Only show warning if it's not the common "No such process" error
if [[ "$bootout_output" != *"No such process"* ]]; then
warnEcho "Failed to stop agent '$domain/$agentName': $bootout_output"
else
verboseEcho "Agent '$domain/$agentName' was not running"
fi
else
if bootout_output=$(run /bin/launchctl bootout "$domain/$agentName" 2>&1); then
# Otherwise on <26 give the system a moment to fully unload the agent
run sleep 1
return 0
fi
fi
# At this point we know exit code is not 0 because otherwise we
# would have returned by now
# Only show warning if it's not a common harmless error
if [[ "$bootout_output" != *"No such process"* && "$bootout_output" != *"Domain does not support specified action"* ]]; then
warnEcho "Failed to stop agent '$domain/$agentName': $bootout_output"
return 1
else
verboseEcho "Agent '$domain/$agentName' was not running"
return 2
fi
}
}
installAgentFile() {
installAndBootstrapAgent() {
local srcPath="$1"
local dstPath="$2"
local agentName="$3"
local domain="$3"
local agentName="$4"
verboseEcho "Installing agent file to $dstPath"
if ! run install -Dm444 -T "$srcPath" "$dstPath"; then
@@ -271,17 +168,11 @@ in
return 1
fi
return 0
}
bootstrapAgent() {
local domain="$1"
local dstPath="$2"
local agentName="$3"
verboseEcho "Starting agent '$domain/$agentName'"
local bootstrap_output
bootstrap_output=$(run /bin/launchctl bootstrap "$domain" "$dstPath" 2>&1) || {
local error_code=$?
if [[ "$bootstrap_output" == *"Bootstrap failed: 5: Input/output error"* ]]; then
errorEcho "Failed to start agent '$domain/$agentName' with I/O error (code 5)"
errorEcho "This typically happens when the agent wasn't unloaded before attempting to bootstrap the new agent."
@@ -296,120 +187,42 @@ in
return 0
}
restoreAgent() {
local oldSrcPath="$1"
local dstPath="$2"
local oldDomain="$3"
local agentName="$4"
if [[ ! -f "$oldSrcPath" ]]; then
warnEcho "Cannot restore previous agent '$oldDomain/$agentName', old file '$oldSrcPath' is missing"
return 1
fi
warnEcho "Restoring previous agent '$oldDomain/$agentName'"
installAgentFile "$oldSrcPath" "$dstPath" "$agentName" \
&& bootstrapAgent "$oldDomain" "$dstPath" "$agentName"
}
processAgent() {
local srcPath="$1"
local dstDir="$2"
local oldDir="$3"
local oldDomainsDir="$4"
local newDomainsDir="$5"
local domain="$3"
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
local oldSrcPath=""
local oldDomainName
local newDomainName
local oldDomain
local newDomain
local oldAgentBootedOut=0
oldDomainName="$(readAgentDomain "$oldDomainsDir" "$agentName")"
newDomainName="$(readAgentDomain "$newDomainsDir" "$agentName")"
oldDomain="$(resolveDomain "$oldDomainName")"
newDomain="$(resolveDomain "$newDomainName")"
if [[ -n "$oldDir" ]]; then
oldSrcPath="$oldDir/$agentFile"
fi
# Skip if unchanged
if cmp -s "$srcPath" "$dstPath" && [[ "$oldDomainName" == "$newDomainName" ]]; then
if agentIsLoaded "$newDomain" "$agentName"; then
verboseEcho "Agent '$newDomain/$agentName' is already up-to-date"
return 0
else
verboseEcho "Agent '$newDomain/$agentName' is up-to-date but not loaded"
fi
fi
verboseEcho "Processing agent '$newDomain/$agentName'"
# Stop/Unload agent if it's already running
if [[ -f "$dstPath" ]]; then
bootoutAgent "$oldDomain" "$agentName"
case "$?" in
0)
oldAgentBootedOut=1
;;
2)
;;
*)
return 1
;;
esac
fi
if [[ "$oldDomainName" != "$newDomainName" ]]; then
bootoutAgent "$newDomain" "$agentName"
case "$?" in
0|2)
;;
*)
if [[ "$oldAgentBootedOut" -eq 1 ]]; then
restoreAgent "$oldSrcPath" "$dstPath" "$oldDomain" "$agentName"
fi
return 1
;;
esac
fi
if ! installAgentFile "$srcPath" "$dstPath" "$agentName"; then
if [[ "$oldAgentBootedOut" -eq 1 ]]; then
restoreAgent "$oldSrcPath" "$dstPath" "$oldDomain" "$agentName"
fi
return 1
fi
if bootstrapAgent "$newDomain" "$dstPath" "$agentName"; then
if cmp -s "$srcPath" "$dstPath"; then
verboseEcho "Agent '$agentName' is already up-to-date"
return 0
fi
if [[ "$oldAgentBootedOut" -eq 1 ]]; then
restoreAgent "$oldSrcPath" "$dstPath" "$oldDomain" "$agentName"
verboseEcho "Processing agent '$agentName'"
# Stop/Unload agent if it's already running
if [[ -f "$dstPath" ]]; then
bootoutAgent "$domain" "$agentName"
fi
return 1
installAndBootstrapAgent "$srcPath" "$dstPath" "$domain" "$agentName"
# Note: We continue processing even if this agent fails
return 0
}
removeAgent() {
local srcPath="$1"
local dstDir="$2"
local newDir="$3"
local oldDomainsDir="$4"
local domain="$4"
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
local domainName
local domain
domainName="$(readAgentDomain "$oldDomainsDir" "$agentName")"
domain="$(resolveDomain "$domainName")"
if [[ -e "$newDir/$agentFile" ]]; then
verboseEcho "Agent '$agentName' still exists in new generation, skipping cleanup"
@@ -428,32 +241,23 @@ in
# Stop and remove the agent
bootoutAgent "$domain" "$agentName"
case "$?" in
0|2)
;;
*)
return 1
;;
esac
verboseEcho "Removing agent file '$dstPath'"
if run rm -f $VERBOSE_ARG "$dstPath"; then
verboseEcho "Successfully removed agent file for '$agentName'"
else
warnEcho "Failed to remove agent file '$dstPath'"
return 1
fi
return 0
}
setupLaunchAgents() {
local oldDir newDir oldDomainsDir newDomainsDir dstDir launchdStatus
local oldDir newDir dstDir domain
newDir="$(readlink -m "$newGenPath/LaunchAgents")"
newDomainsDir="$(readlink -m "$newGenPath/LaunchAgentDomains")"
dstDir=${lib.escapeShellArg dstDir}
launchdStatus=0
domain="gui/$UID"
if [[ -n "''${oldGenPath:-}" ]]; then
oldDir="$(readlink -m "$oldGenPath/LaunchAgents")"
@@ -461,60 +265,34 @@ in
verboseEcho "No previous LaunchAgents directory found"
oldDir=""
fi
oldDomainsDir="$(readlink -m "$oldGenPath/LaunchAgentDomains")"
if [[ ! -d "$oldDomainsDir" ]]; then
oldDomainsDir=""
fi
else
oldDir=""
oldDomainsDir=""
fi
verboseEcho "Setting up LaunchAgents in $dstDir"
if [[ ! -d "$dstDir" ]] && ! run mkdir -p "$dstDir"; then
errorEcho "Failed to create LaunchAgents directory '$dstDir'"
return 1
fi
[[ -d "$dstDir" ]] || run mkdir -p "$dstDir"
verboseEcho "Processing new/updated LaunchAgents..."
while IFS= read -r srcPath; do
processAgent "$srcPath" "$dstDir" "$oldDir" "$oldDomainsDir" "$newDomainsDir" \
|| launchdStatus=1
done < <(find -L "$newDir" -maxdepth 1 -name '*.plist' -type f)
find -L "$newDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
processAgent "$srcPath" "$dstDir" "$domain"
done
# Skip cleanup if there's no previous generation
if [[ -z "$oldDir" || ! -d "$oldDir" ]]; then
if [[ "$launchdStatus" -ne 0 ]]; then
errorEcho "Failed to activate one or more LaunchAgents"
fi
verboseEcho "LaunchAgents setup complete"
return "$launchdStatus"
return
fi
verboseEcho "Cleaning up removed LaunchAgents..."
while IFS= read -r srcPath; do
removeAgent "$srcPath" "$dstDir" "$newDir" "$oldDomainsDir" \
|| launchdStatus=1
done < <(find -L "$oldDir" -maxdepth 1 -name '*.plist' -type f)
if [[ "$launchdStatus" -ne 0 ]]; then
errorEcho "Failed to activate one or more LaunchAgents"
fi
return "$launchdStatus"
find -L "$oldDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
removeAgent "$srcPath" "$dstDir" "$newDir" "$domain"
done
}
launchdStatus=0
setupLaunchAgents || launchdStatus=$?
setupLaunchAgents
# Restore errexit
set -e
if [[ "$launchdStatus" -ne 0 ]]; then
exit "$launchdStatus"
fi
'';
})
];
+24 -40
View File
@@ -12,32 +12,20 @@
let
inherit (lib)
all
attrNames
attrValues
elem
filter
filterAttrs
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: isAttrs dag && all isEntry (attrValues dag);
isDag = dag: builtins.isAttrs dag && all hm.dag.isEntry (builtins.attrValues dag);
# Takes an attribute set containing entries built by entryAnywhere,
# entryAfter, and entryBefore to a topologically sorted list of
@@ -96,19 +84,16 @@ in
# }
# true
topoSort =
let
before = a: b: elem a.name b.after;
in
dag:
let
names = attrNames dag;
dagBefore = name: filter (n: elem name dag.${n}.before) names;
dagBefore = dag: name: builtins.attrNames (filterAttrs (_n: v: builtins.elem name v.before) dag);
normalizedDag = mapAttrs (n: v: {
name = n;
inherit (v) data;
after = v.after ++ dagBefore n;
after = v.after ++ dagBefore dag n;
}) dag;
sorted = toposort before (attrValues normalizedDag);
before = a: b: builtins.elem a.name b.after;
sorted = toposort before (builtins.attrValues normalizedDag);
in
if sorted ? result then
{
@@ -123,10 +108,10 @@ in
entryBetween = before: after: data: { inherit data before after; };
# Create a DAG entry with no particular dependency information.
entryAnywhere = entryBetween [ ] [ ];
entryAnywhere = hm.dag.entryBetween [ ] [ ];
entryAfter = entryBetween [ ];
entryBefore = before: entryBetween before [ ];
entryAfter = hm.dag.entryBetween [ ];
entryBefore = before: hm.dag.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
@@ -139,25 +124,24 @@ in
let
go =
i: before: after: entries:
let
name = "${tag}-${toString i}";
in
if entries == [ ] then
empty
hm.dag.empty
else if length entries == 1 then
{
"${name}" = hm.dag.entryBetween before after (head entries);
}
else
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);
{
"${name}" = hm.dag.entryAfter after (head entries);
}
// go (i + 1) before [ name ] (tail entries);
in
go 0;
entriesAnywhere = tag: entriesBetween tag [ ] [ ];
entriesAfter = tag: entriesBetween tag [ ];
entriesBefore = tag: before: entriesBetween tag before [ ];
entriesAnywhere = tag: hm.dag.entriesBetween tag [ ] [ ];
entriesAfter = tag: hm.dag.entriesBetween tag [ ];
entriesBefore = tag: before: hm.dag.entriesBetween tag before [ ];
}
-42
View File
@@ -1,47 +1,5 @@
{ 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
[
+2 -4
View File
@@ -15,8 +15,6 @@ let
removePrefix
types
;
isAbsolutePath = hasPrefix "/";
removeHomePrefix = removePrefix (homeDirectory + "/");
in
{
# Constructs a type suitable for a `home.file` like option. The
@@ -48,9 +46,9 @@ in
apply =
p:
let
absPath = if isAbsolutePath p then p else "${basePath}/${p}";
absPath = if hasPrefix "/" p then p else "${basePath}/${p}";
in
removeHomePrefix absPath;
removePrefix (homeDirectory + "/") absPath;
defaultText = literalExpression "name";
description = ''
Path to target file relative to ${basePathDesc}.
+5 -699
View File
@@ -1,684 +1,6 @@
{ 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,
@@ -688,11 +10,11 @@ in
let
inherit (lib)
all
any
concatMapStringsSep
concatStrings
concatStringsSep
filterAttrs
foldl
generators
hasPrefix
isAttrs
@@ -707,7 +29,8 @@ in
toHyprconf' =
indent: attrs:
let
isImportantField = n: _: any (prev: hasPrefix prev n) importantPrefixes;
isImportantField =
n: _: foldl (acc: prev: if hasPrefix prev n then true else acc) false importantPrefixes;
importantFields = filterAttrs isImportantField attrs;
withoutImportantFields = fields: removeAttrs fields (attrNames importantFields);
@@ -744,9 +67,7 @@ in
toHyprconf' initialIndent attrs;
toKDL =
{
escapeBackslashes ? false,
}:
_:
let
inherit (lib)
concatStringsSep
@@ -768,22 +89,7 @@ in
stringsWithNewlines: indentAll (unlines (lines stringsWithNewlines));
# String -> String
sanitizeString =
replaceStrings
(
[
"\n"
''"''
]
++ (lib.optional escapeBackslashes "\\")
)
(
[
"\\n"
''\"''
]
++ (lib.optional escapeBackslashes "\\\\")
);
sanitizeString = replaceStrings [ "\n" ''"'' ] [ "\\n" ''\"'' ];
# OneOf [Int Float String Bool Null] -> String
literalValueToString =
+19 -43
View File
@@ -7,18 +7,30 @@
# [1] https://github.com/NixOS/nixpkgs/blob/737449404589e4a80b3fa99ecbc6d4d803c1f6dc/maintainers/maintainer-list.nix#LC1
{
# keep-sorted start case=no numeric=no block=yes
_3ulalia = {
"3ulalia" = {
name = "Eulalia del Sol";
email = "3ulalia@proton.me";
github = "3ulalia";
githubId = 179992797;
};
_9p4 = {
"74k1" = {
name = "Tim";
email = "git.t@betsumei.com";
github = "74k1";
githubId = 49000471;
};
"9p4" = {
name = "9p4";
email = "vcs@ersei.net";
github = "9p4";
githubId = 17993169;
};
"will-lol" = {
name = "William Bradshaw";
email = "will.bradshaw50@gmail.com";
github = "will-lol";
githubId = 65345408;
};
aabccd021 = {
name = "Muhamad Abdurahman";
email = "aabccd021@gmail.com";
@@ -79,12 +91,6 @@
github = "b1kku";
githubId = 77858854;
};
bittner = {
name = "Peter Bittner";
email = "peter@painless.software";
github = "bittner";
githubId = 665072;
};
bjpbakker = {
email = "bart@thesoftwarecraft.com";
github = "bjpbakker";
@@ -109,12 +115,6 @@
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,12 +175,6 @@
github = "epixtm";
githubId = 168025607;
};
ErinaYip = {
name = "erina";
email = "erinayip@outlook.com";
github = "ErinaYip";
githubId = 227132255;
};
exzombie = {
name = "Jure Varlec";
email = "jure@varlec.si";
@@ -308,12 +302,6 @@
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";
@@ -517,12 +505,6 @@
}
];
};
reesilva = {
name = "Renato da Silva";
email = "reesilva@proton.me";
github = "ReeSilva";
githubId = 3605032;
};
rosuavio = {
name = "Rosario Pulella";
email = "RosarioPulella@gmail.com";
@@ -558,17 +540,11 @@
github = "SunOfLife1";
githubId = 30405063;
};
superflash41 = {
name = "Saymon Nicho";
email = "saymon.nicho@pucp.edu.pe";
github = "superflash41";
githubId = 102434258;
};
will-lol = {
name = "William Bradshaw";
email = "will.bradshaw50@gmail.com";
github = "will-lol";
githubId = 65345408;
yarn = {
name = "yarncat";
email = "30006414+yaaaarn@users.noreply.github.com";
github = "yaaaarn";
githubId = 30006414;
};
zorrobert = {
name = "zorrobert";
+1 -1
View File
@@ -7,7 +7,7 @@ rec {
if builtins.isBool v then
if v then "true" else "false"
else if builtins.isString v then
''"${lib.escape [ "\\" "\"" "`" ] v}"''
''"${v}"''
else if builtins.isList v then
let
shell = import ./shell.nix { inherit lib; };
+126 -208
View File
@@ -14,141 +14,38 @@ let
cfg = config.fonts.fontconfig;
xml = pkgs.formats.xml { };
inherit (config.home) profileDirectory;
globalConfig = config;
fontConfigFileType = lib.types.submodule (
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,
...
}:
{ name, ... }:
{
options = {
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 {
description = "Label to use for the name of the config file.";
default = name;
defaultText = lib.literalExpression "<name>";
type = lib.types.str;
};
priority = lib.mkOption {
description = ''
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;
};
enable = lib.mkEnableOption "Whether this font config file should be generated.";
text = lib.mkOption {
description = ''
Verbatim contents of the config file.
''
+ noteContentOptionsExclusivity;
type = lib.types.nullOr lib.types.lines;
default = null;
type = with lib.types; nullOr lines;
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.";
};
label = lib.mkOption {
type = lib.types.str;
default = name;
defaultText = "<name>";
description = "Label to use for the name of the config file.";
};
priority = lib.mkOption {
type = lib.types.ints.between 0 99;
default = 90;
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;
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.
'';
};
};
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))
];
}
);
@@ -278,12 +175,13 @@ in
type = lib.types.attrsOf fontConfigFileType;
default = { };
description = ''
Config files that will be installed to {file}`~/.config/fontconfig/conf.d/`.
Extra font config files that will be added to `~/.config/fontconfig/conf.d/`.
Files are named like `fontconfig/conf.d/{priority}-{label}.conf`.
'';
example = {
tamzen-disable-antialiasing = {
tamzen = {
enable = true;
priority = 90;
label = "tamzen-disable-antialiasing";
text = ''
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
@@ -300,12 +198,13 @@ in
</match>
</fontconfig>
'';
};
priority = 90;
}; # => conf.d/90-tamzen-disable-antialiasing.conf
commit-mono-options = {
enable = true;
priority = 80;
source = "./resources/fontconfig/commit-mono.conf";
};
priority = 80;
}; # => conf.d/80-commit-mono-options.conf
};
};
@@ -349,93 +248,112 @@ in
fi
'';
fonts.fontconfig.configFile = {
fonts =
let
mkInclude = path: {
"@ignore_missing" = "yes";
"#text" = path;
};
in
{
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 = {
enable = true;
priority = 10;
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";
};
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>
'';
};
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)
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)
);
in
{
enable = lib.length content > 0;
priority = 10;
settings = {
description = "Set the rendering mode";
match = {
"@target" = "font";
edit = content;
};
};
};
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;
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"}
'';
};
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/${config.target}" {
inherit (config) enable source;
lib.nameValuePair "fontconfig/conf.d/${toString config.priority}-hm-${config.label}.conf" {
inherit (config) enable text;
source = lib.mkIf (config.source != null) config.source;
}
) cfg.configFile;
};
@@ -13,7 +13,7 @@ let
cfg3 = config.gtk.gtk3;
cfg4 = config.gtk.gtk4;
gtkLib = import ./lib.nix { inherit lib; };
gtkLib = import ./gtk/lib.nix { inherit lib; };
themeType = gtkLib.mkThemeType {
typeName = "theme";
@@ -41,9 +41,9 @@ in
];
imports = [
./gtk2.nix
./gtk3.nix
./gtk4.nix
./gtk/gtk2.nix
./gtk/gtk3.nix
./gtk/gtk4.nix
];
options.gtk = {
@@ -52,26 +52,26 @@ let
}
);
isNewsFile = n: v: v == "regular" && lib.hasSuffix ".nix" n && n != "default.nix";
isNixFile = n: v: v == "regular" && lib.hasSuffix ".nix" n;
isDirectory = _n: v: v == "directory";
# Recursively collect all news .nix files from a directory
collectNewsFiles =
# Recursively collect all .nix files from a directory
collectNixFiles =
dir:
let
contents = builtins.readDir dir;
files = lib.filterAttrs isNewsFile contents;
files = lib.filterAttrs isNixFile contents;
fileList = map (file: dir + "/${file}") (builtins.attrNames files);
# Process subdirectories
subdirs = lib.filterAttrs isDirectory contents;
subdirFiles = lib.concatMap (subdir: collectNewsFiles (dir + "/${subdir}")) (
subdirFiles = lib.concatMap (subdir: collectNixFiles (dir + "/${subdir}")) (
builtins.attrNames subdirs
);
in
fileList ++ subdirFiles;
newsFiles = collectNewsFiles ./.;
newsFiles = collectNixFiles ./news;
newsEntries = map (
newsFile:
let
@@ -0,0 +1,10 @@
{ pkgs, ... }:
{
time = "2024-03-28T17:02:19+00:00";
condition = pkgs.stdenv.hostPlatform.isLinux;
message = ''
A new module is available: 'services.amberol'.
'';
}
@@ -0,0 +1,10 @@
{
time = "2025-01-02T11:21:19+00:00";
condition = true;
message = ''
A new module is available: 'services.mpdscribble'.
A MPD client which submits information about tracks being played to a
scrobbler (e.g. last.fm)
'';
}
@@ -1,14 +0,0 @@
{
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.
'';
}
@@ -1,18 +0,0 @@
{ 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 {}`.
'';
}
@@ -1,15 +0,0 @@
{ 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(...)`.
'';
}
@@ -1,18 +0,0 @@
{ 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`.
'';
}
@@ -10,6 +10,6 @@
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`.
`~/.gemini/config/skills`.
'';
}
@@ -1,15 +0,0 @@
{ 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`.
'';
}
@@ -1,18 +0,0 @@
{ 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.
'';
}
@@ -1,13 +0,0 @@
{ 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`.
'';
}
@@ -1,8 +0,0 @@
{ config, ... }:
{
time = "2026-06-04T17:10:21+00:00";
condition = config.programs.zellij.enable;
message = ''
Added support for Zellij plugins using `programs.zellij.plugins`.
'';
}
@@ -1,12 +0,0 @@
{ 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.
'';
}
@@ -1,11 +0,0 @@
{ 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.
'';
}
@@ -1,17 +0,0 @@
{ 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.
'';
}
@@ -1,20 +0,0 @@
{ 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").
'';
}

Some files were not shown because too many files have changed in this diff Show More