Compare commits

..

1 commit

Author SHA1 Message Date
Robert Helgesson 4aa9eb327d
WIP home-manager: avoid profile management during activation
This commit deprecates profile management from the activation script.
The profile management is instead the responsibility of the driving
software, for example, the `home-manager` tool in the case of
standalone installs.

The legacy behavior is still available for backwards compatibility but
may be removed in the future.

The new behavior resolves (or moves us closer to resolving) a number
of long standing open issues:

- `home-manager switch --rollback`, which performs a rollback to the
  previous Home Manager generation before activating. While it was
  previously possible to accomplish this by activating an old
  generation, it did always create a new profile generation.

  This option has been implemented as part of this commit.

- `home-manager switch --test`, which activates the configuration but
  does not create a new profile generation.

  This option has _not_ been implemented here since it relies on the
  current configuration being activated on login, which we do not
  currently do.

- When using the "Home Manager as a NixOS module" installation method
  we previously created an odd `home-manager` per-user "shadow
  profile" for the user. This is no longer necessary.

  This has been implemented as part of this commit.

Fixes #3450
2024-06-22 15:15:55 +02:00
133 changed files with 1725 additions and 2283 deletions

View file

@ -14,7 +14,7 @@ jobs:
- name: Install Nix
uses: cachix/install-nix-action@v27
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v23
uses: DeterminateSystems/update-flake-lock@v22
with:
token: ${{ secrets.GH_TOKEN_FOR_UPDATES }}
pr-labels: dependencies

View file

@ -53,11 +53,6 @@ Home Manager targets [NixOS][] unstable and NixOS version 24.05 (the current
stable version), it may or may not work on other Linux distributions and NixOS
versions.
Also, the `home-manager` tool does not explicitly support rollbacks at the
moment so if your home directory gets messed up you'll have to fix it yourself.
See the [rollbacks][] section for instructions on how to manually perform a
rollback.
Now when your expectations have been built up and you are eager to try all this
out you can go ahead and read the rest of this text.

View file

@ -27,6 +27,7 @@
.Cm | option Ar option.name
.Cm | packages
.Cm | remove-generations Ar ID \&...
.Cm | switch Op Fl -rollback
.Cm | uninstall
.Brc
.Op Fl A Ar attrPath
@ -155,9 +156,14 @@ sub-command to find suitable generation numbers.
.RE
.Pp
.It Cm switch
.It Cm switch Op Fl -rollback
.RS 4
Build and activate the configuration\&.
.sp
If the
.Fl -rollback
option is given, then the build is not done, instead roll back to and
activate the configuration prior to the current configuration\&.
.RE
.Pp

11
docs/manual/internals.md Normal file
View file

@ -0,0 +1,11 @@
# Home Manager Internals {#ch-internals}
This chapter collects some documentation about the internal workings
of Home Manager. The information here is mostly aimed to developers of
Home Manager and those who do non-trivial integration with Home
Manager.
```{=include=} sections
internals/activation.md
```

View file

@ -0,0 +1,41 @@
# Activation {#sec-internals-activation}
Activating a Home Manager configuration ensures that the built
configuration is introduced into the user's environment. The
activation is performed by a suitably named script
{command}`activate`. This script is generated as part of the
configuration build and will be placed in the root of the build
output.
The activation script is implemented in the Bash language and consists
of initialization code followed by a number of _activation script
blocks_. These blocks are specified using the
[home.activation](#opt-home.activation) option. The blocks may have
dependencies among themselves and the generated activation script will
contain the blocks serialized such that the dependencies are
satisfied. A dependency cycle causes a failure when the configuration
is built.
Historically, the activation script has been responsible for creating
a new generation of the `home-manager` Nix profile. The more modern
way, however, is to let the _activation driver_ that is, the
software calling the activation script manage the profile. Indeed,
in some cases we may not have a `home-manager` profile at all! This is
the case when Home Manager is used as a NixOS or nix-darwin module, in
these cases the system profile will contain references to the
corresponding Home Manager configurations.
Note, to maintain backwards compatibility, the old activation script
behavior is still the default. To choose the new mode of operation you
have to call the activation script with the command line option
`--driver-version 1`. The old behavior is available using
`--driver-version 0`, or simply omit it entirely.
Unfortunately, driver software need to support both modes of operation
for the time being since a user may wish to activate an old generation
that contains an activation script that does not support
`--driver-version`. To determine whether support is available, check
the {file}`gen-version` file in the configuration build output root.
If the file is missing then the activation script does not support
`--driver-version`. If the file exists and contains the integer 1 or
higher, then `--driver-version 1` is supported.

View file

@ -1,32 +0,0 @@
# Introduction to Home Manager {#ch-introduction}
Home Manager is a [Nix](https://nix.dev/)-powered tool for reproducible management of the contents of users' home directories.
This includes programs, configuration files, environment variables and, well… arbitrary files.
The following example snippet of Nix code:
```nix
programs.git = {
enable = true;
userEmail = "joe@example.org";
userName = "joe";
};
```
would make available to a user the `git` executable and man pages and a configuration file `~/.config/git/config`:
```ini
[user]
email = "joe@example.org"
name = "joe"
```
Since Home Manager is implemented in Nix, it provides several benefits:
- Contents are reproducible — a home will be the exact same every time it is built, unless of course, an intentional change is made.
This also means you can have the exact same home on different hosts.
- Significantly faster and more powerful than various backup strategies.
- Unlike "dotfiles" repositories, Home Manager supports specifying programs, as well as their configurations.
- Supported by <http://cache.nixos.org/>, so that you don't have to build from source.
- If you do want to build some programs from source, there is hardly a tool more useful than Nix for that, and the build instructions can be neatly integrated in your Home Manager usage.
- Infinitely composable, so that values in different configuration files and build instructions can share a source of truth.
- Connects you with the [most extensive](https://repology.org/repositories/statistics/total) and [most up-to-date](https://repology.org/repositories/statistics/newest) software package repository on earth, [Nixpkgs](https://github.com/NixOS/nixpkgs).

View file

@ -8,12 +8,12 @@ preface.md
```
```{=include=} parts
introduction.md
installation.md
usage.md
nix-flakes.md
writing-modules.md
contributing.md
internals.md
3rd-party.md
faq.md
```

View file

@ -1,32 +1,45 @@
# Rollbacks {#sec-usage-rollbacks}
While the `home-manager` tool does not explicitly support rollbacks at
the moment it is relatively easy to perform one manually. The steps to
do so are
When you perform a `home-manager switch` and discover a problem then
it is possible to _roll back_ to the previous version of your
configuration using `home-manager switch --rollback`. This will turn
the previous configuration into the current configuration.
1. Run `home-manager generations` to determine which generation you
wish to rollback to:
::: {.example #ex-rollback-scenario}
### Home Manager Rollback
``` shell
$ home-manager generations
2018-01-04 11:56 : id 765 -> /nix/store/kahm1rxk77mnvd2l8pfvd4jkkffk5ijk-home-manager-generation
2018-01-03 10:29 : id 764 -> /nix/store/2wsmsliqr5yynqkdyjzb1y57pr5q2lsj-home-manager-generation
2018-01-01 12:21 : id 763 -> /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
2017-12-29 21:03 : id 762 -> /nix/store/6c0k1r03fxckql4vgqcn9ccb616ynb94-home-manager-generation
2017-12-25 18:51 : id 761 -> /nix/store/czc5y6vi1rvnkfv83cs3rn84jarcgsgh-home-manager-generation
```
Imagine you have just updated Nixpkgs and switched to a new Home
Manager configuration. You discover that a package update included in
your new configuration has a bug that was not present in the previous
configuration.
2. Copy the Nix store path of the generation you chose, e.g.,
You can then run `home-manager switch --rollback` to recover your
previous configuration, which includes the working version of the
package.
/nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
To see what happened above we can observe the list of Home Manager
generations before and after the rollback:
for generation 763.
``` shell
$ home-manager generations
2024-01-04 11:56 : id 765 -> /nix/store/kahm1rxk77mnvd2l8pfvd4jkkffk5ijk-home-manager-generation (current)
2024-01-03 10:29 : id 764 -> /nix/store/2wsmsliqr5yynqkdyjzb1y57pr5q2lsj-home-manager-generation
2024-01-01 12:21 : id 763 -> /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
2023-12-29 21:03 : id 762 -> /nix/store/6c0k1r03fxckql4vgqcn9ccb616ynb94-home-manager-generation
2023-12-25 18:51 : id 761 -> /nix/store/czc5y6vi1rvnkfv83cs3rn84jarcgsgh-home-manager-generation
3. Run the `activate` script inside the copied store path:
$ home-manager switch --rollback
Starting home manager activation
``` shell
$ /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation/activate
Starting home manager activation
```
$ home-manager generations
2024-01-04 11:56 : id 765 -> /nix/store/kahm1rxk77mnvd2l8pfvd4jkkffk5ijk-home-manager-generation
2024-01-03 10:29 : id 764 -> /nix/store/2wsmsliqr5yynqkdyjzb1y57pr5q2lsj-home-manager-generation (current)
2024-01-01 12:21 : id 763 -> /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
2023-12-29 21:03 : id 762 -> /nix/store/6c0k1r03fxckql4vgqcn9ccb616ynb94-home-manager-generation
2023-12-25 18:51 : id 761 -> /nix/store/czc5y6vi1rvnkfv83cs3rn84jarcgsgh-home-manager-generation
```
:::

View file

@ -7,7 +7,29 @@ is therefore not final.
This release has the following notable changes:
- No changes.
- The `home-manager` Nix profile update that the Home Manager
activation script has previously performed is now deprecated. The
profile update is instead the responsibility of the software calling
the activation script, such as the `home-manager` tool..
The legacy behavior is the default for backwards compatibility but
may be emit a deprecation warning in the future, for eventual
removal. If you have developed tooling that directly call the
generated activation script, then you are encouraged to adapt to the
new behavior. See [Activation](#sec-internals-activation) for
details on how to call the activation script.
- The `home-manager switch` command now offers a `--rollback` option.
When given, the switch performs a rollback to the Home Manager
generation prior to the current before activating. While it was
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](#sec-usage-rollbacks) section for more.
- When using Home Manager as a NixOS or nix-darwin module we
previously created an unnecessary `home-manager` per-user "shadow
profile" for the user. This no longer happens.
## State Version Changes {#sec-release-24.11-state-version-changes}

View file

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1722185531,
"narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
"lastModified": 1718530797,
"narHash": "sha256-pup6cYwtgvzDpvpSCFh1TEUjw2zkNpk8iolbKnyFmmU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
"rev": "b60ebf54c15553b393d144357375ea956f89e9a9",
"type": "github"
},
"original": {

View file

@ -198,19 +198,9 @@ function setFlakeAttribute() {
;;
*)
local name="$USER"
local hostnameArray=()
# FQDN lookup can fail depending on the resolver.
local fqdn
fqdn="$(hostname -f 2> /dev/null)"
if [[ $? -eq 0 ]]; then
hostnameArray+=( "$USER@$fqdn" )
fi
# Check FQDN, long, and short hostnames; long first to preserve
# pre-existing behaviour in case both happen to be defined.
hostnameArray+=( "$USER@$(hostname)" "$USER@$(hostname -s)" )
for n in "${hostnameArray[@]}"; do
for n in "$USER@$(hostname -f)" "$USER@$(hostname)" "$USER@$(hostname -s)"; do
if [[ "$(nix eval "$flake#homeConfigurations" --apply "x: x ? \"$n\"")" == "true" ]]; then
name="$n"
if [[ -v VERBOSE ]]; then
@ -486,7 +476,7 @@ EOF
_i "Creating initial Home Manager generation..."
echo
if doSwitch; then
if doSwitch --switch; then
# translators: The "%s" specifier will be replaced by a file path.
_i $'All done! The home-manager tool should now be installed and you can edit\n\n %s\n\nto configure Home Manager. Run \'man home-configuration.nix\' to\nsee all available options.' \
"$confFile"
@ -645,31 +635,89 @@ function doBuild() {
}
function doSwitch() {
setHomeManagerPathVariables
setVerboseArg
setWorkDir
local action
while (( $# > 0 )); do
local opt="$1"
shift
case $opt in
--switch)
action='switch'
;;
--test)
action='test'
;;
--rollback)
action='rollback'
;;
*)
errorEcho "home-manager switch: unknown option '%s'" "$opt" >&2
return 1
;;
esac
done
if [[ ! -v action ]]; then
errorEcho "home-manager switch: missing required option" >&2
return 1
fi
local generation
# Build the generation and run the activate script. Note, we
# specify an output link so that it is treated as a GC root. This
# prevents an unfortunately timed GC from removing the generation
# before activation completes.
generation="$WORK_DIR/generation"
case $action in
switch|test)
# Build the generation and run the activate script. Note, we
# specify an output link so that it is treated as a GC root. This
# prevents an unfortunately timed GC from removing the generation
# before activation completes.
generation="$WORK_DIR/generation"
setFlakeAttribute
if [[ -v FLAKE_CONFIG_URI ]]; then
doBuildFlake \
"$FLAKE_CONFIG_URI.activationPackage" \
--out-link "$generation" \
${PRINT_BUILD_LOGS+--print-build-logs} \
&& "$generation/activate" || return
else
doBuildAttr \
--out-link "$generation" \
--attr activationPackage \
&& "$generation/activate" || return
setFlakeAttribute
if [[ -v FLAKE_CONFIG_URI ]]; then
doBuildFlake \
"$FLAKE_CONFIG_URI.activationPackage" \
--out-link "$generation" \
${PRINT_BUILD_LOGS+--print-build-logs}
else
doBuildAttr \
--out-link "$generation" \
--attr activationPackage
fi
;;
rollback)
generation="$HM_PROFILE_DIR/home-manager"
;;
esac
# If we are doing a switch but built a legacy configuration, where the
# activation script manages the profile, then we instead perform a test
# action.
#
# The migration away from legacy activation scripts happened when
# introducing the gen-version file, hence the existence check.
if [[ $action == 'switch' && ! -e "$generation/gen-version" ]]; then
action='test'
fi
presentNews
case $action in
switch)
run nix-env $VERBOSE_ARG --profile "$HM_PROFILE_DIR/home-manager" --set "$generation"
;;
rollback)
run nix-env $VERBOSE_ARG --profile "$HM_PROFILE_DIR/home-manager" --rollback
;;
esac
"$generation"/activate --driver-version 1 || return
if [[ $action == 'switch' || $action == 'test' ]]; then
presentNews
fi
}
function doListGens() {
@ -682,10 +730,14 @@ function doListGens() {
fi
pushd "$HM_PROFILE_DIR" > /dev/null
local curProfile
curProfile=$(readlink home-manager)
# shellcheck disable=2012
ls --color=$color -gG --time-style=long-iso --sort time home-manager-*-link \
| cut -d' ' -f 4- \
| sed -E 's/home-manager-([[:digit:]]*)-link/: id \1/'
| sed -E -e "/$curProfile/ { s/\$/ \(current\)/ }" \
-e 's/home-manager-([[:digit:]]*)-link/: id \1/'
popd > /dev/null
}
@ -939,7 +991,11 @@ function doHelp() {
echo
echo " instantiate Instantiate the configuration and print the resulting derivation"
echo
echo " switch Build and activate configuration"
echo " switch [OPTION]"
echo " Build and activate configuration"
echo
echo " --rollback Do not build a new configuration, instead roll back to"
echo " the configuration prior to the current configuration."
echo
echo " generations List all home environment generations"
echo
@ -970,7 +1026,7 @@ while [[ $# -gt 0 ]]; do
opt="$1"
shift
case $opt in
build|init|instantiate|option|edit|expire-generations|generations|help|news|packages|remove-generations|switch|uninstall)
build|init|instantiate|option|edit|expire-generations|generations|help|news|packages|remove-generations|rollback|switch|test|uninstall)
COMMAND="$opt"
;;
-A)
@ -1035,6 +1091,17 @@ while [[ $# -gt 0 ]]; do
-n|--dry-run)
export DRY_RUN=1
;;
--rollback)
case $COMMAND in
switch)
COMMAND_ARGS+=("$opt")
;;
*)
_iError 'home-manager: "--rollback" can only be used after "switch"' >&2
exit 1
;;
esac
;;
--option|--arg|--argstr)
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
[[ -v 2 ]] || errMissingOptArg "$opt $1"
@ -1093,14 +1160,22 @@ case $COMMAND in
doInstantiate
;;
switch)
doSwitch
doSwitch --switch "${COMMAND_ARGS[@]}"
;;
# TODO: The test functionality is not really sensible until we perform
# activation through some form of systemd unit.
# test)
# doSwitch --test
# ;;
generations)
doListGens
;;
remove-generations)
doRmGenerations "${COMMAND_ARGS[@]}"
;;
rollback)
doRollback
;;
expire-generations)
if [[ ${#COMMAND_ARGS[@]} != 1 ]]; then
_i 'expire-generations expects one argument, got %d.' "${#COMMAND_ARGS[@]}" >&2

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: 2024-04-17 23:19+0200\n"
"PO-Revision-Date: 2024-07-05 14:09+0000\n"
"Last-Translator: Ferenci Ákos <synthetace@gmail.com>\n"
"PO-Revision-Date: 2024-06-14 20:40+0000\n"
"Last-Translator: Balint Barna Kovari <balint+weblate@kovari.cc>\n"
"Language-Team: Hungarian <https://hosted.weblate.org/projects/home-manager/"
"cli/hu/>\n"
"Language: hu\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.7-dev\n"
"X-Generator: Weblate 5.6-dev\n"
#. translators: For example: "home-manager: missing argument for --cores"
#: home-manager/home-manager:16
@ -36,17 +36,14 @@ msgid ""
"Keeping your Home Manager %s in %s is deprecated,\n"
"please move it to %s"
msgstr ""
"A Home Manager %s %s elérési úton való tárolása elavult. Kérem helyezze át a "
"%s elérési úthoz"
#: home-manager/home-manager:92
msgid "No configuration file found. Please create one at %s"
msgstr ""
"Nem található konfigurációs fájl. Kérem hozza létre a fájlt a %s elérési úton"
#: home-manager/home-manager:107
msgid "Home Manager not found at %s."
msgstr "Home Manager nem található a %s elérési úton."
msgstr ""
#. 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:115
@ -93,23 +90,23 @@ msgstr ""
#: home-manager/home-manager:296 home-manager/home-manager:319
#: home-manager/home-manager:1051
msgid "%s: unknown option '%s'"
msgstr "%s: ismeretlen opció '%s'"
msgstr ""
#: home-manager/home-manager:301 home-manager/home-manager:1052
msgid "Run '%s --help' for usage help"
msgstr "Futtasa a '%s --help' parancsot a használattal kapcsolatos segítségért"
msgstr ""
#: home-manager/home-manager:327 home-manager/home-manager:431
msgid "The file %s already exists, leaving it unchanged..."
msgstr "A fájl %s már létezik és változatlan marad..."
msgstr ""
#: home-manager/home-manager:329 home-manager/home-manager:433
msgid "Creating %s..."
msgstr "%s létrehozása..."
msgstr ""
#: home-manager/home-manager:475
msgid "Creating initial Home Manager generation..."
msgstr "Kezdeti Home Manager generáció létrehozása..."
msgstr ""
#. translators: The "%s" specifier will be replaced by a file path.
#: home-manager/home-manager:480
@ -135,7 +132,7 @@ msgstr ""
#. translators: Here "flake" is a noun that refers to the Nix Flakes feature.
#: home-manager/home-manager:496
msgid "Can't instantiate a flake configuration"
msgstr "Flake konfiguráció nem példányosítható"
msgstr ""
#: home-manager/home-manager:572
msgid ""
@ -158,7 +155,7 @@ msgstr ""
#: home-manager/home-manager:612
msgid "Cannot run build in read-only directory"
msgstr "Build futtatása nem lehetséges csak-olvasható könyvtárban"
msgstr ""
#: home-manager/home-manager:693
msgid "No generation with ID %s"
@ -170,7 +167,7 @@ msgstr ""
#: home-manager/home-manager:697
msgid "Removing generation %s"
msgstr "%s generáció eltávolítása"
msgstr ""
#: home-manager/home-manager:718
msgid "No generations to expire"
@ -182,7 +179,7 @@ msgstr ""
#: home-manager/home-manager:811
msgid "Unknown argument %s"
msgstr "Ismeretlen argumentum %s"
msgstr ""
#: home-manager/home-manager:835
msgid "This will remove Home Manager from your system."
@ -202,7 +199,7 @@ msgstr ""
#: home-manager/home-manager:863
msgid "Yay!"
msgstr "Hurrá!"
msgstr ""
#: home-manager/home-manager:868
msgid "Home Manager is uninstalled but your home.nix is left untouched."
@ -214,7 +211,7 @@ msgstr ""
#: home-manager/home-manager:1113
msgid "Unknown command: %s"
msgstr "Ismeretlen parancs: %s"
msgstr ""
#: home-manager/install.nix:18
msgid "This derivation is not buildable, please run it using nix-shell."

View file

@ -67,7 +67,7 @@ let
};
in {
meta.maintainers = [ maintainers.league ];
meta.maintainers = [ maintainers.polykernel maintainers.league ];
imports = [
(mkAliasOptionModule [ "xsession" "pointerCursor" "package" ] [

View file

@ -105,10 +105,7 @@ in
# 1. Remove files from the old generation that are not in the new
# generation.
#
# 2. Switch over the Home Manager gcroot and current profile
# links.
#
# 3. Symlink files from the new generation into $HOME.
# 2. Symlink files from the new generation into $HOME.
#
# This order is needed to ensure that we always know which links
# belong to which generation. Specifically, if we're moving from
@ -215,28 +212,6 @@ in
}
cleanOldGen
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
_i "Creating profile generation %s" $newGenNum
if [[ -e "$genProfilePath"/manifest.json ]] ; then
# Remove all packages from "$genProfilePath"
# `nix profile remove '.*' --profile "$genProfilePath"` was not working, so here is a workaround:
nix profile list --profile "$genProfilePath" \
| cut -d ' ' -f 4 \
| xargs -rt $DRY_RUN_CMD nix profile remove $VERBOSE_ARG --profile "$genProfilePath"
run nix profile install $VERBOSE_ARG --profile "$genProfilePath" "$newGenPath"
else
run nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
fi
run --quiet nix-store --realise "$newGenPath" --add-root "$newGenGcPath" --indirect
if [[ -e "$legacyGenGcPath" ]]; then
run rm $VERBOSE_ARG "$legacyGenGcPath"
fi
else
_i "No change so reusing latest profile generation %s" "$oldGenNum"
fi
linkNewGen
''
);

View file

@ -431,6 +431,18 @@ in
description = "The package containing the complete activation script.";
};
home.activationGenerateGcRoot = mkOption {
internal = true;
type = types.bool;
default = true;
description = ''
Whether the activation script should create a GC root to avoid being
garbage collected. Typically you want this but if you know for certain
that the Home Manager generation is referenced from some other GC root,
then it may be appropriate to not create our own root.
'';
};
home.extraActivationPath = mkOption {
internal = true;
type = types.listOf types.package;
@ -582,9 +594,21 @@ in
home.packages = [ config.home.sessionVariablesPackage ];
# A dummy entry acting as a boundary between the activation
# script's "check" and the "write" phases.
home.activation.writeBoundary = hm.dag.entryAnywhere "";
# The entry acting as a boundary between the activation script's "check" and
# the "write" phases. This is where we commit to attempting to actually
# activate the configuration.
#
# Note, if we are run by a version 0 driver then we update the profile here.
home.activation.writeBoundary = hm.dag.entryAnywhere ''
if (( $hmDriverVersion < 1 )); then
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
_i "Creating new profile generation"
run nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
else
_i "No change so reusing latest profile generation"
fi
fi
'';
# Install packages to the user environment.
#
@ -710,6 +734,38 @@ in
export PATH="${activationBinPaths}"
${config.lib.bash.initHomeManagerLib}
# The driver version indicates the behavior expected by the caller of
# this script.
#
# - 0 : legacy behavior
# - 1 : the script will not attempt to update the Home Manager Nix profile.
hmDriverVersion=0
while (( $# > 0 )); do
opt="$1"
shift
case $opt in
--driver-version)
if (( $# == 0 )); then
errorEcho "$0: no driver version specified" >&2
exit 1
elif (( 0 <= $1 && $1 <= 1 )); then
hmDriverVersion=$1
else
errorEcho "$0: unexpected driver version $1" >&2
exit 1
fi
shift
;;
*)
_iError "%s: unknown option '%s'" "$0" "$opt" >&2
exit 1
;;
esac
done
unset opt
${builtins.readFile ./lib-bash/activation-init.sh}
if [[ ! -v SKIP_SANITY_CHECKS ]]; then
@ -717,7 +773,22 @@ in
checkHomeDirectory ${escapeShellArg config.home.homeDirectory}
fi
${optionalString config.home.activationGenerateGcRoot ''
# Create a temporary GC root to prevent collection during activation.
trap 'run rm -f $VERBOSE_ARG "$newGenGcPath"' EXIT
run --silence nix-store --realise "$newGenPath" --add-root "$newGenGcPath"
''}
${activationCmds}
${optionalString (config.home.activationGenerateGcRoot && !config.uninstall) ''
# Create the "current generation" GC root.
run --silence nix-store --realise "$newGenPath" --add-root "$currentGenGcPath"
if [[ -e "$legacyGenGcPath" ]]; then
run rm $VERBOSE_ARG "$legacyGenGcPath"
fi
''}
'';
in
pkgs.runCommand
@ -730,6 +801,11 @@ in
echo "${config.home.version.full}" > $out/hm-version
# The gen-version indicates the format of the generation package
# itself. It allows us to make backwards incompatible changes in the
# package output and have surrounding tooling adapt.
echo 1 > $out/gen-version
cp ${activationScript} $out/activate
mkdir $out/bin

View file

@ -59,34 +59,13 @@ function setupVars() {
declare -gr hmDataPath="${XDG_DATA_HOME:-$HOME/.local/share}/home-manager"
declare -gr genProfilePath="$profilesDir/home-manager"
declare -gr newGenPath="@GENERATION_DIR@";
declare -gr newGenGcPath="$hmGcrootsDir/current-home"
declare -gr newGenGcPath="$hmGcrootsDir/new-home"
declare -gr currentGenGcPath="$hmGcrootsDir/current-home"
declare -gr legacyGenGcPath="$globalGcrootsDir/current-home"
declare greatestGenNum
greatestGenNum=$( \
nix-env --list-generations --profile "$genProfilePath" \
| tail -1 \
| sed -E 's/ *([[:digit:]]+) .*/\1/')
if [[ -n $greatestGenNum ]] ; then
declare -gr oldGenNum=$greatestGenNum
declare -gr newGenNum=$((oldGenNum + 1))
else
declare -gr newGenNum=1
fi
if [[ -e $genProfilePath ]] ; then
if [[ -e $currentGenGcPath ]] ; then
declare -g oldGenPath
oldGenPath="$(readlink -e "$genProfilePath")"
fi
_iVerbose "Sanity checking oldGenNum and oldGenPath"
if [[ -v oldGenNum && ! -v oldGenPath
|| ! -v oldGenNum && -v oldGenPath ]]; then
_i $'The previous generation number and path are in conflict! These\nmust be either both empty or both set but are now set to\n\n \'%s\' and \'%s\'\n\nIf you don\'t mind losing previous profile generations then\nthe easiest solution is probably to run\n\n rm %s/home-manager*\n rm %s/current-home\n\nand trying home-manager switch again. Good luck!' \
"${oldGenNum:-}" "${oldGenPath:-}" \
"$profilesDir" "$hmGcrootsDir"
exit 1
oldGenPath="$(readlink -e "$currentGenGcPath")"
fi
}
@ -181,15 +160,13 @@ if [[ -v VERBOSE ]]; then
fi
_iVerbose "Activation variables:"
if [[ -v oldGenNum ]] ; then
verboseEcho " oldGenNum=$oldGenNum"
if [[ -v oldGenPath ]] ; then
verboseEcho " oldGenPath=$oldGenPath"
else
verboseEcho " oldGenNum undefined (first run?)"
verboseEcho " oldGenPath undefined (first run?)"
fi
verboseEcho " newGenPath=$newGenPath"
verboseEcho " newGenNum=$newGenNum"
verboseEcho " genProfilePath=$genProfilePath"
verboseEcho " newGenGcPath=$newGenGcPath"
verboseEcho " currentGenGcPath=$currentGenGcPath"
verboseEcho " legacyGenGcPath=$legacyGenGcPath"

View file

@ -49,12 +49,6 @@
github = "bertof";
githubId = 9915675;
};
bricked = {
name = "Bricked";
email = "hello@bricked.dev";
github = "brckd";
githubId = 92804487;
};
CarlosLoboxyz = {
name = "Carlos Lobo";
email = "86011416+CarlosLoboxyz@users.noreply.github.com";
@ -119,13 +113,6 @@
github = "jack5079";
githubId = 29169102;
};
janik = {
name = "Janik";
email = "janik@aq0.de";
matrix = "@janik0:matrix.org";
github = "Janik-Haag";
githubId = 80165193;
};
jkarlson = {
email = "jekarlson@gmail.com";
github = "jkarlson";
@ -186,13 +173,6 @@
github = "iosmanthus";
githubId = 16307070;
};
jonringer = {
email = "jonringer117@gmail.com";
matrix = "@jonringer:matrix.org";
github = "jonringer";
githubId = 7673602;
name = "Jonathan Ringer";
};
kalhauge = {
name = "Christian Gram Kalhauge";
email = "kalhauge@users.noreply.github.com";
@ -221,19 +201,6 @@
github = "kubukoz";
githubId = 894884;
};
lheckemann = {
name = "Linus Heckemann";
email = "git@sphalerite.org";
github = "lheckemann";
githubId = 341954;
};
lilyinstarlight = {
email = "lily@lily.flowers";
matrix = "@lily:lily.flowers";
github = "lilyinstarlight";
githubId = 298109;
name = "Lily Foster";
};
loicreynier = {
name = "Loïc Reynier";
email = "loic@loireynier.fr";

View file

@ -1679,30 +1679,6 @@ in {
https://github.com/rafaelmardojai/blanket for more.
'';
}
{
time = "2024-06-26T07:07:17+00:00";
condition = with config.programs.yazi;
enable && (enableBashIntegration || enableZshIntegration
|| enableFishIntegration || enableNushellIntegration);
message = ''
Yazi's shell integration wrappers have been renamed from 'ya' to 'yy'.
A new option `programs.yazi.shellWrapperName` is also available that
allows you to override this name.
'';
}
{
time = "2024-06-28T14:18:16+00:00";
condition = hostPlatform.isLinux;
message = ''
A new module is available: 'services.glance'.
Glance is a self-hosted dashboard that puts all your feeds in
one place. See https://github.com/glanceapp/glance for more.
'';
}
];
};
}

View file

@ -311,5 +311,5 @@ in {
})
]);
meta.maintainers = [ ];
meta.maintainers = [ maintainers.polykernel ];
}

View file

@ -51,7 +51,7 @@ let
terminal = mkOption {
description = "Whether the program runs in a terminal window.";
type = types.nullOr types.bool;
type = types.bool;
default = false;
};

View file

@ -301,7 +301,6 @@ let
./services/fusuma.nix
./services/getmail.nix
./services/git-sync.nix
./services/glance.nix
./services/gnome-keyring.nix
./services/gpg-agent.nix
./services/grobi.nix

View file

@ -110,18 +110,18 @@ in {
programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
if [[ :$SHELLOPTS: =~ :(vi|emacs): ]]; then
source "${pkgs.bash-preexec}/share/bash/bash-preexec.sh"
eval "$(${lib.getExe cfg.package} init bash ${flagsStr})"
eval "$(${cfg.package}/bin/atuin init bash ${flagsStr})"
fi
'';
programs.zsh.initExtra = mkIf cfg.enableZshIntegration ''
if [[ $options[zle] = on ]]; then
eval "$(${lib.getExe cfg.package} init zsh ${flagsStr})"
eval "$(${cfg.package}/bin/atuin init zsh ${flagsStr})"
fi
'';
programs.fish.interactiveShellInit = mkIf cfg.enableFishIntegration ''
${lib.getExe cfg.package} init fish ${flagsStr} | source
${cfg.package}/bin/atuin init fish ${flagsStr} | source
'';
programs.nushell = mkIf cfg.enableNushellIntegration {
@ -130,9 +130,7 @@ in {
if not ($atuin_cache | path exists) {
mkdir $atuin_cache
}
${
lib.getExe cfg.package
} init nu ${flagsStr} | save --force ${config.xdg.cacheHome}/atuin/init.nu
${cfg.package}/bin/atuin init nu ${flagsStr} | save --force ${config.xdg.cacheHome}/atuin/init.nu
'';
extraConfig = ''
source ${config.xdg.cacheHome}/atuin/init.nu

View file

@ -56,5 +56,5 @@ in {
};
};
meta.maintainers = [ ];
meta.maintainers = [ maintainers.polykernel ];
}

View file

@ -95,7 +95,6 @@ in {
package = mkPackageOption pkgs "nix-direnv" { };
};
silent = mkEnableOption "silent mode, that is, disabling direnv logging";
};
config = mkIf cfg.enable {
@ -164,7 +163,5 @@ in {
}
)
'');
home.sessionVariables = lib.mkIf cfg.silent { DIRENV_LOG_FORMAT = ""; };
};
}

View file

@ -5,7 +5,6 @@ with lib;
let
cfg = config.programs.eww;
ewwCmd = "${cfg.package}/bin/eww";
in {
meta.maintainers = [ hm.maintainers.mainrs ];
@ -31,40 +30,10 @@ in {
{file}`$XDG_CONFIG_HOME/eww`.
'';
};
enableBashIntegration = mkEnableOption "Bash integration" // {
default = true;
};
enableZshIntegration = mkEnableOption "Zsh integration" // {
default = true;
};
enableFishIntegration = mkEnableOption "Fish integration" // {
default = true;
};
};
config = mkIf cfg.enable {
home.packages = [ cfg.package ];
xdg.configFile."eww".source = cfg.configDir;
programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
if [[ $TERM != "dumb" ]]; then
eval "$(${ewwCmd} shell-completions --shell bash)"
fi
'';
programs.zsh.initExtra = mkIf cfg.enableZshIntegration ''
if [[ $TERM != "dumb" ]]; then
eval "$(${ewwCmd} shell-completions --shell zsh)"
fi
'';
programs.fish.interactiveShellInit = mkIf cfg.enableFishIntegration ''
if test "$TERM" != "dumb"
eval "$(${ewwCmd} shell-completions --shell fish)"
end
'';
};
}

View file

@ -1,51 +1,945 @@
{ lib, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
modulePath = [ "programs" "firefox" ];
inherit (pkgs.stdenv.hostPlatform) isDarwin;
moduleName = concatStringsSep "." modulePath;
cfg = config.programs.firefox;
mkFirefoxModule = import ./firefox/mkFirefoxModule.nix;
jsonFormat = pkgs.formats.json { };
mozillaConfigPath =
if isDarwin then "Library/Application Support/Mozilla" else ".mozilla";
firefoxConfigPath = if isDarwin then
"Library/Application Support/Firefox"
else
"${mozillaConfigPath}/firefox";
profilesPath =
if isDarwin then "${firefoxConfigPath}/Profiles" else firefoxConfigPath;
nativeMessagingHostsPath = if isDarwin then
"${mozillaConfigPath}/NativeMessagingHosts"
else
"${mozillaConfigPath}/native-messaging-hosts";
nativeMessagingHostsJoined = pkgs.symlinkJoin {
name = "ff_native-messaging-hosts";
paths = [
# Link a .keep file to keep the directory around
(pkgs.writeTextDir "lib/mozilla/native-messaging-hosts/.keep" "")
# Link package configured native messaging hosts (entire Firefox actually)
cfg.finalPackage
]
# Link user configured native messaging hosts
++ cfg.nativeMessagingHosts;
};
# The extensions path shared by all profiles; will not be supported
# by future Firefox versions.
extensionPath = "extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
profiles = flip mapAttrs' cfg.profiles (_: profile:
nameValuePair "Profile${toString profile.id}" {
Name = profile.name;
Path = if isDarwin then "Profiles/${profile.path}" else profile.path;
IsRelative = 1;
Default = if profile.isDefault then 1 else 0;
}) // {
General = { StartWithLastProfile = 1; };
};
profilesIni = generators.toINI { } profiles;
userPrefValue = pref:
builtins.toJSON (if isBool pref || isInt pref || isString pref then
pref
else
builtins.toJSON pref);
mkUserJs = prefs: extraPrefs: bookmarks:
let
prefs' = lib.optionalAttrs ([ ] != bookmarks) {
"browser.bookmarks.file" = toString (firefoxBookmarksFile bookmarks);
"browser.places.importBookmarksHTML" = true;
} // prefs;
in ''
// Generated by Home Manager.
${concatStrings (mapAttrsToList (name: value: ''
user_pref("${name}", ${userPrefValue value});
'') prefs')}
${extraPrefs}
'';
mkContainersJson = containers:
let
containerToIdentity = _: container: {
userContextId = container.id;
name = container.name;
icon = container.icon;
color = container.color;
public = true;
};
in ''
${builtins.toJSON {
version = 4;
lastUserContextId =
elemAt (mapAttrsToList (_: container: container.id) containers) 0;
identities = mapAttrsToList containerToIdentity containers ++ [
{
userContextId = 4294967294; # 2^32 - 2
name = "userContextIdInternal.thumbnail";
icon = "";
color = "";
accessKey = "";
public = false;
}
{
userContextId = 4294967295; # 2^32 - 1
name = "userContextIdInternal.webextStorageLocal";
icon = "";
color = "";
accessKey = "";
public = false;
}
];
}}
'';
firefoxBookmarksFile = bookmarks:
let
indent = level:
lib.concatStringsSep "" (map (lib.const " ") (lib.range 1 level));
bookmarkToHTML = indentLevel: bookmark:
''
${indent indentLevel}<DT><A HREF="${
escapeXML bookmark.url
}" ADD_DATE="1" LAST_MODIFIED="1"${
lib.optionalString (bookmark.keyword != null)
" SHORTCUTURL=\"${escapeXML bookmark.keyword}\""
}${
lib.optionalString (bookmark.tags != [ ])
" TAGS=\"${escapeXML (concatStringsSep "," bookmark.tags)}\""
}>${escapeXML bookmark.name}</A>'';
directoryToHTML = indentLevel: directory: ''
${indent indentLevel}<DT>${
if directory.toolbar then
''
<H3 ADD_DATE="1" LAST_MODIFIED="1" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar''
else
''<H3 ADD_DATE="1" LAST_MODIFIED="1">${escapeXML directory.name}''
}</H3>
${indent indentLevel}<DL><p>
${allItemsToHTML (indentLevel + 1) directory.bookmarks}
${indent indentLevel}</DL><p>'';
itemToHTMLOrRecurse = indentLevel: item:
if item ? "url" then
bookmarkToHTML indentLevel item
else
directoryToHTML indentLevel item;
allItemsToHTML = indentLevel: bookmarks:
lib.concatStringsSep "\n"
(map (itemToHTMLOrRecurse indentLevel) bookmarks);
bookmarkEntries = allItemsToHTML 1 bookmarks;
in pkgs.writeText "firefox-bookmarks.html" ''
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks Menu</H1>
<DL><p>
${bookmarkEntries}
</DL>
'';
mkNoDuplicateAssertion = entities: entityKind:
(let
# Return an attribute set with entity IDs as keys and a list of
# entity names with corresponding ID as value. An ID is present in
# the result only if more than one entity has it. The argument
# entities is a list of AttrSet of one id/name pair.
findDuplicateIds = entities:
filterAttrs (_entityId: entityNames: length entityNames != 1)
(zipAttrs entities);
duplicates = findDuplicateIds (mapAttrsToList
(entityName: entity: { "${toString entity.id}" = entityName; })
entities);
mkMsg = entityId: entityNames:
" - ID ${entityId} is used by " + concatStringsSep ", " entityNames;
in {
assertion = duplicates == { };
message = ''
Must not have a Firefox ${entityKind} with an existing ID but
'' + concatStringsSep "\n" (mapAttrsToList mkMsg duplicates);
});
wrapPackage = package:
let
# The configuration expected by the Firefox wrapper.
fcfg = { enableGnomeExtensions = cfg.enableGnomeExtensions; };
# A bit of hackery to force a config into the wrapper.
browserName =
package.browserName or (builtins.parseDrvName package.name).name;
# The configuration expected by the Firefox wrapper builder.
bcfg = setAttrByPath [ browserName ] fcfg;
in if package == null then
null
else if isDarwin then
package
else if versionAtLeast config.home.stateVersion "19.09" then
package.override (old: {
cfg = old.cfg or { } // fcfg;
extraPolicies = (old.extraPolicies or { }) // cfg.policies;
})
else
(pkgs.wrapFirefox.override { config = bcfg; }) package { };
in {
meta.maintainers =
[ maintainers.rycee maintainers.kira-bruneau hm.maintainers.bricked ];
meta.maintainers = [ maintainers.rycee maintainers.kira-bruneau ];
imports = [
(mkFirefoxModule {
inherit modulePath;
name = "Firefox";
wrappedPackageName = "firefox";
unwrappedPackageName = "firefox-unwrapped";
visible = true;
platforms.linux = rec {
vendorPath = ".mozilla";
configPath = "${vendorPath}/firefox";
};
platforms.darwin = {
vendorPath = "Library/Application Support/Mozilla";
configPath = "Library/Application Support/Firefox";
};
})
(mkRemovedOptionModule (modulePath ++ [ "extensions" ]) ''
(mkRemovedOptionModule [ "programs" "firefox" "extensions" ] ''
Extensions are now managed per-profile. That is, change from
${moduleName}.extensions = [ foo bar ];
programs.firefox.extensions = [ foo bar ];
to
${moduleName}.profiles.myprofile.extensions = [ foo bar ];'')
(mkRemovedOptionModule (modulePath ++ [ "enableAdobeFlash" ])
programs.firefox.profiles.myprofile.extensions = [ foo bar ];'')
(mkRemovedOptionModule [ "programs" "firefox" "enableAdobeFlash" ]
"Support for this option has been removed.")
(mkRemovedOptionModule (modulePath ++ [ "enableGoogleTalk" ])
(mkRemovedOptionModule [ "programs" "firefox" "enableGoogleTalk" ]
"Support for this option has been removed.")
(mkRemovedOptionModule (modulePath ++ [ "enableIcedTea" ])
(mkRemovedOptionModule [ "programs" "firefox" "enableIcedTea" ]
"Support for this option has been removed.")
];
options = {
programs.firefox = {
enable = mkEnableOption "Firefox";
package = mkOption {
type = with types; nullOr package;
default = if versionAtLeast config.home.stateVersion "19.09" then
pkgs.firefox
else
pkgs.firefox-unwrapped;
defaultText = literalExpression "pkgs.firefox";
example = literalExpression ''
pkgs.firefox.override {
# See nixpkgs' firefox/wrapper.nix to check which options you can use
nativeMessagingHosts = [
# Gnome shell native connector
pkgs.gnome-browser-connector
# Tridactyl native connector
pkgs.tridactyl-native
];
}
'';
description = ''
The Firefox package to use. If state version  19.09 then
this should be a wrapped Firefox package. For earlier state
versions it should be an unwrapped Firefox package.
Set to `null` to disable installing Firefox.
'';
};
nativeMessagingHosts = mkOption {
type = types.listOf types.package;
default = [ ];
description = ''
Additional packages containing native messaging hosts that should be
made available to Firefox extensions.
'';
};
finalPackage = mkOption {
type = with types; nullOr package;
readOnly = true;
description = "Resulting Firefox package.";
};
policies = mkOption {
type = types.attrsOf jsonFormat.type;
default = { };
description =
"[See list of policies](https://mozilla.github.io/policy-templates/).";
example = {
DefaultDownloadDirectory = "\${home}/Downloads";
BlockAboutConfig = true;
};
};
profiles = mkOption {
type = types.attrsOf (types.submodule ({ config, name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Profile name.";
};
id = mkOption {
type = types.ints.unsigned;
default = 0;
description = ''
Profile ID. This should be set to a unique number per profile.
'';
};
settings = mkOption {
type = types.attrsOf (jsonFormat.type // {
description =
"Firefox preference (int, bool, string, and also attrs, list, float as a JSON string)";
});
default = { };
example = literalExpression ''
{
"browser.startup.homepage" = "https://nixos.org";
"browser.search.region" = "GB";
"browser.search.isUS" = false;
"distribution.searchplugins.defaultLocale" = "en-GB";
"general.useragent.locale" = "en-GB";
"browser.bookmarks.showMobileBookmarks" = true;
"browser.newtabpage.pinned" = [{
title = "NixOS";
url = "https://nixos.org";
}];
}
'';
description = ''
Attribute set of Firefox preferences.
Firefox only supports int, bool, and string types for
preferences, but home-manager will automatically
convert all other JSON-compatible values into strings.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = ''
Extra preferences to add to {file}`user.js`.
'';
};
userChrome = mkOption {
type = types.lines;
default = "";
description = "Custom Firefox user chrome CSS.";
example = ''
/* Hide tab bar in FF Quantum */
@-moz-document url("chrome://browser/content/browser.xul") {
#TabsToolbar {
visibility: collapse !important;
margin-bottom: 21px !important;
}
#sidebar-box[sidebarcommand="treestyletab_piro_sakura_ne_jp-sidebar-action"] #sidebar-header {
visibility: collapse !important;
}
}
'';
};
userContent = mkOption {
type = types.lines;
default = "";
description = "Custom Firefox user content CSS.";
example = ''
/* Hide scrollbar in FF Quantum */
*{scrollbar-width:none !important}
'';
};
bookmarks = mkOption {
type = let
bookmarkSubmodule = types.submodule ({ config, name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Bookmark name.";
};
tags = mkOption {
type = types.listOf types.str;
default = [ ];
description = "Bookmark tags.";
};
keyword = mkOption {
type = types.nullOr types.str;
default = null;
description = "Bookmark search keyword.";
};
url = mkOption {
type = types.str;
description = "Bookmark url, use %s for search terms.";
};
};
}) // {
description = "bookmark submodule";
};
bookmarkType = types.addCheck bookmarkSubmodule (x: x ? "url");
directoryType = types.submodule ({ config, name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Directory name.";
};
bookmarks = mkOption {
type = types.listOf nodeType;
default = [ ];
description = "Bookmarks within directory.";
};
toolbar = mkOption {
type = types.bool;
default = false;
description = ''
Make this the toolbar directory. Note, this does _not_
mean that this directory will be added to the toolbar,
this directory _is_ the toolbar.
'';
};
};
}) // {
description = "directory submodule";
};
nodeType = types.either bookmarkType directoryType;
in with types;
coercedTo (attrsOf nodeType) attrValues (listOf nodeType);
default = [ ];
example = literalExpression ''
[
{
name = "wikipedia";
tags = [ "wiki" ];
keyword = "wiki";
url = "https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
}
{
name = "kernel.org";
url = "https://www.kernel.org";
}
{
name = "Nix sites";
toolbar = true;
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
tags = [ "wiki" "nix" ];
url = "https://wiki.nixos.org/";
}
];
}
]
'';
description = ''
Preloaded bookmarks. Note, this may silently overwrite any
previously existing bookmarks!
'';
};
path = mkOption {
type = types.str;
default = name;
description = "Profile path.";
};
isDefault = mkOption {
type = types.bool;
default = config.id == 0;
defaultText = "true if profile ID is 0";
description = "Whether this is a default profile.";
};
search = {
force = mkOption {
type = with types; bool;
default = false;
description = ''
Whether to force replace the existing search
configuration. This is recommended since Firefox will
replace the symlink for the search configuration on every
launch, but note that you'll lose any existing
configuration by enabling this.
'';
};
default = mkOption {
type = with types; nullOr str;
default = null;
example = "DuckDuckGo";
description = ''
The default search engine used in the address bar and search bar.
'';
};
privateDefault = mkOption {
type = with types; nullOr str;
default = null;
example = "DuckDuckGo";
description = ''
The default search engine used in the Private Browsing.
'';
};
order = mkOption {
type = with types; uniq (listOf str);
default = [ ];
example = [ "DuckDuckGo" "Google" ];
description = ''
The order the search engines are listed in. Any engines
that aren't included in this list will be listed after
these in an unspecified order.
'';
};
engines = mkOption {
type = with types; attrsOf (attrsOf jsonFormat.type);
default = { };
example = literalExpression ''
{
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{ name = "type"; value = "packages"; }
{ name = "query"; value = "{searchTerms}"; }
];
}];
icon = "''${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
definedAliases = [ "@np" ];
};
"NixOS Wiki" = {
urls = [{ template = "https://wiki.nixos.org/index.php?search={searchTerms}"; }];
iconUpdateURL = "https://wiki.nixos.org/favicon.png";
updateInterval = 24 * 60 * 60 * 1000; # every day
definedAliases = [ "@nw" ];
};
"Bing".metaData.hidden = true;
"Google".metaData.alias = "@g"; # builtin engines only support specifying one additional alias
}
'';
description = ''
Attribute set of search engine configurations. Engines
that only have {var}`metaData` specified will
be treated as builtin to Firefox.
See [SearchEngine.jsm](https://searchfox.org/mozilla-central/rev/669329e284f8e8e2bb28090617192ca9b4ef3380/toolkit/components/search/SearchEngine.jsm#1138-1177)
in Firefox's source for available options. We maintain a
mapping to let you specify all options in the referenced
link without underscores, but it may fall out of date with
future options.
Note, {var}`icon` is also a special option
added by Home Manager to make it convenient to specify
absolute icon paths.
'';
};
};
containersForce = mkOption {
type = types.bool;
default = false;
description = ''
Whether to force replace the existing containers
configuration. This is recommended since Firefox will
replace the symlink on every launch, but note that you'll
lose any existing configuration by enabling this.
'';
};
containers = mkOption {
type = types.attrsOf (types.submodule ({ name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Container name, e.g., shopping.";
};
id = mkOption {
type = types.ints.unsigned;
default = 0;
description = ''
Container ID. This should be set to a unique number per container in this profile.
'';
};
# List of colors at
# https://searchfox.org/mozilla-central/rev/5ad226c7379b0564c76dc3b54b44985356f94c5a/toolkit/components/extensions/parent/ext-contextualIdentities.js#32
color = mkOption {
type = types.enum [
"blue"
"turquoise"
"green"
"yellow"
"orange"
"red"
"pink"
"purple"
"toolbar"
];
default = "pink";
description = "Container color.";
};
icon = mkOption {
type = types.enum [
"briefcase"
"cart"
"circle"
"dollar"
"fence"
"fingerprint"
"gift"
"vacation"
"food"
"fruit"
"pet"
"tree"
"chill"
];
default = "fruit";
description = "Container icon.";
};
};
}));
default = { };
example = {
"shopping" = {
id = 1;
color = "blue";
icon = "cart";
};
"dangerous" = {
id = 2;
color = "red";
icon = "fruit";
};
};
description = ''
Attribute set of container configurations. See
[Multi-Account
Containers](https://support.mozilla.org/en-US/kb/containers)
for more information.
'';
};
extensions = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExpression ''
with pkgs.nur.repos.rycee.firefox-addons; [
privacy-badger
]
'';
description = ''
List of Firefox add-on packages to install for this profile.
Some pre-packaged add-ons are accessible from the
[Nix User Repository](https://github.com/nix-community/NUR).
Once you have NUR installed run
```console
$ nix-env -f '<nixpkgs>' -qaP -A nur.repos.rycee.firefox-addons
```
to list the available Firefox add-ons.
Note that it is necessary to manually enable these extensions
inside Firefox after the first installation.
To automatically enable extensions add
`"extensions.autoDisableScopes" = 0;`
to
[{option}`programs.firefox.profiles.<profile>.settings`](#opt-programs.firefox.profiles._name_.settings)
'';
};
};
}));
default = { };
description = "Attribute set of Firefox profiles.";
};
enableGnomeExtensions = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable the GNOME Shell native host connector. Note, you
also need to set the NixOS option
`services.gnome.gnome-browser-connector.enable` to
`true`.
'';
};
};
};
config = mkIf cfg.enable {
assertions = [
(let
defaults =
catAttrs "name" (filter (a: a.isDefault) (attrValues cfg.profiles));
in {
assertion = cfg.profiles == { } || length defaults == 1;
message = "Must have exactly one default Firefox profile but found "
+ toString (length defaults) + optionalString (length defaults > 1)
(", namely " + concatStringsSep ", " defaults);
})
(let
getContainers = profiles:
flatten
(mapAttrsToList (_: value: (attrValues value.containers)) profiles);
findInvalidContainerIds = profiles:
filter (container: container.id >= 4294967294)
(getContainers profiles);
in {
assertion = cfg.profiles == { }
|| length (findInvalidContainerIds cfg.profiles) == 0;
message = "Container id must be smaller than 4294967294 (2^32 - 2)";
})
(mkNoDuplicateAssertion cfg.profiles "profile")
] ++ (mapAttrsToList
(_: profile: mkNoDuplicateAssertion profile.containers "container")
cfg.profiles);
warnings = optional (cfg.enableGnomeExtensions or false) ''
Using 'programs.firefox.enableGnomeExtensions' has been deprecated and
will be removed in the future. Please change to overriding the package
configuration using 'programs.firefox.package' instead. You can refer to
its example for how to do this.
'';
programs.firefox.finalPackage = wrapPackage cfg.package;
home.packages = lib.optional (cfg.finalPackage != null) cfg.finalPackage;
home.file = mkMerge ([{
"${firefoxConfigPath}/profiles.ini" =
mkIf (cfg.profiles != { }) { text = profilesIni; };
"${nativeMessagingHostsPath}" = {
source =
"${nativeMessagingHostsJoined}/lib/mozilla/native-messaging-hosts";
recursive = true;
};
}] ++ flip mapAttrsToList cfg.profiles (_: profile: {
"${profilesPath}/${profile.path}/.keep".text = "";
"${profilesPath}/${profile.path}/chrome/userChrome.css" =
mkIf (profile.userChrome != "") { text = profile.userChrome; };
"${profilesPath}/${profile.path}/chrome/userContent.css" =
mkIf (profile.userContent != "") { text = profile.userContent; };
"${profilesPath}/${profile.path}/user.js" = mkIf (profile.settings != { }
|| profile.extraConfig != "" || profile.bookmarks != [ ]) {
text =
mkUserJs profile.settings profile.extraConfig profile.bookmarks;
};
"${profilesPath}/${profile.path}/containers.json" =
mkIf (profile.containers != { }) {
force = profile.containersForce;
text = mkContainersJson profile.containers;
};
"${profilesPath}/${profile.path}/search.json.mozlz4" = mkIf
(profile.search.default != null || profile.search.privateDefault != null
|| profile.search.order != [ ] || profile.search.engines != { }) {
force = profile.search.force;
source = let
settings = {
version = 6;
engines = let
# Map of nice field names to internal field names.
# This is intended to be exhaustive and should be
# updated at every version bump.
internalFieldNames = (genAttrs [
"name"
"isAppProvided"
"loadPath"
"hasPreferredIcon"
"updateInterval"
"updateURL"
"iconUpdateURL"
"iconURL"
"iconMapObj"
"metaData"
"orderHint"
"definedAliases"
"urls"
] (name: "_${name}")) // {
searchForm = "__searchForm";
};
processCustomEngineInput = input:
(removeAttrs input [ "icon" ])
// optionalAttrs (input ? icon) {
# Convenience to specify absolute path to icon
iconURL = "file://${input.icon}";
} // (optionalAttrs (input ? iconUpdateURL) {
# Convenience to default iconURL to iconUpdateURL so
# the icon is immediately downloaded from the URL
iconURL = input.iconURL or input.iconUpdateURL;
} // {
# Required for custom engine configurations, loadPaths
# are unique identifiers that are generally formatted
# like: [source]/path/to/engine.xml
loadPath = ''
[home-manager]/programs.firefox.profiles.${profile.name}.search.engines."${
replaceStrings [ "\\" ] [ "\\\\" ] input.name
}"'';
});
processEngineInput = name: input:
let
requiredInput = {
inherit name;
isAppProvided = input.isAppProvided or removeAttrs input
[ "metaData" ] == { };
metaData = input.metaData or { };
};
in if requiredInput.isAppProvided then
requiredInput
else
processCustomEngineInput (input // requiredInput);
buildEngineConfig = name: input:
mapAttrs' (name: value: {
name = internalFieldNames.${name} or name;
inherit value;
}) (processEngineInput name input);
sortEngineConfigs = configs:
let
buildEngineConfigWithOrder = order: name:
let
config = configs.${name} or {
_name = name;
_isAppProvided = true;
_metaData = { };
};
in config // {
_metaData = config._metaData // { inherit order; };
};
engineConfigsWithoutOrder =
attrValues (removeAttrs configs profile.search.order);
sortedEngineConfigs =
(imap buildEngineConfigWithOrder profile.search.order)
++ engineConfigsWithoutOrder;
in sortedEngineConfigs;
engineInput = profile.search.engines // {
# Infer profile.search.default as an app provided
# engine if it's not in profile.search.engines
${profile.search.default} =
profile.search.engines.${profile.search.default} or { };
} // {
${profile.search.privateDefault} =
profile.search.engines.${profile.search.privateDefault} or { };
};
in sortEngineConfigs (mapAttrs buildEngineConfig engineInput);
metaData = optionalAttrs (profile.search.default != null) {
current = profile.search.default;
hash = "@hash@";
} // optionalAttrs (profile.search.privateDefault != null) {
private = profile.search.privateDefault;
privateHash = "@privateHash@";
} // {
useSavedOrder = profile.search.order != [ ];
};
};
# Home Manager doesn't circumvent user consent and isn't acting
# maliciously. We're modifying the search outside of Firefox, but
# a claim by Mozilla to remove this would be very anti-user, and
# is unlikely to be an issue for our use case.
disclaimer = appName:
"By modifying this file, I agree that I am doing so "
+ "only within ${appName} itself, using official, user-driven search "
+ "engine selection processes, and in a way which does not circumvent "
+ "user consent. I acknowledge that any attempt to change this file "
+ "from outside of ${appName} is a malicious act, and will be responded "
+ "to accordingly.";
salt = if profile.search.default != null then
profile.path + profile.search.default + disclaimer "Firefox"
else
null;
privateSalt = if profile.search.privateDefault != null then
profile.path + profile.search.privateDefault
+ disclaimer "Firefox"
else
null;
in pkgs.runCommand "search.json.mozlz4" {
nativeBuildInputs = with pkgs; [ mozlz4a openssl ];
json = builtins.toJSON settings;
inherit salt privateSalt;
} ''
if [[ -n $salt ]]; then
export hash=$(echo -n "$salt" | openssl dgst -sha256 -binary | base64)
export privateHash=$(echo -n "$privateSalt" | openssl dgst -sha256 -binary | base64)
mozlz4a <(substituteStream json search.json.in --subst-var hash --subst-var privateHash) "$out"
else
mozlz4a <(echo "$json") "$out"
fi
'';
};
"${profilesPath}/${profile.path}/extensions" =
mkIf (profile.extensions != [ ]) {
source = let
extensionsEnvPkg = pkgs.buildEnv {
name = "hm-firefox-extensions";
paths = profile.extensions;
};
in "${extensionsEnvPkg}/share/mozilla/${extensionPath}";
recursive = true;
force = true;
};
}));
};
}

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ let
yamlFormat = pkgs.formats.yaml { };
in {
meta.maintainers = [ lib.hm.maintainers.janik ];
meta.maintainers = [ lib.maintainers.janik ];
options.programs.gh-dash = {
enable = lib.mkEnableOption "GitHub CLI dashboard plugin";

View file

@ -316,7 +316,7 @@ in {
})
];
home.packages = [ pkgs.gnome-terminal ];
home.packages = [ pkgs.gnome.gnome-terminal ];
dconf.settings = let dconfPath = "org/gnome/terminal/legacy";
in {

View file

@ -7,7 +7,7 @@ let
jsonFormat = pkgs.formats.json { };
in {
meta.maintainers = [ hm.maintainers.lilyinstarlight ];
meta.maintainers = [ maintainers.lilyinstarlight ];
options.programs.hyfetch = {
enable = mkEnableOption "hyfetch";

View file

@ -7,11 +7,6 @@ let
cfg = config.programs.jujutsu;
tomlFormat = pkgs.formats.toml { };
configDir = if pkgs.stdenv.isDarwin then
"Library/Application Support"
else
config.xdg.configHome;
in {
meta.maintainers = [ maintainers.shikanime ];
@ -56,7 +51,7 @@ in {
config = mkIf cfg.enable {
home.packages = [ cfg.package ];
home.file."${configDir}/jj/config.toml" = mkIf (cfg.settings != { }) {
xdg.configFile."jj/config.toml" = mkIf (cfg.settings != { }) {
source = tomlFormat.generate "jujutsu-config" (cfg.settings
// optionalAttrs (cfg.ediff) (let
emacsDiffScript = pkgs.writeShellScriptBin "emacs-ediff" ''

View file

@ -68,11 +68,6 @@ in {
type = with lib.types;
attrsOf (submodule {
options.khard.enable = lib.mkEnableOption "khard access";
options.khard.defaultCollection = lib.mkOption {
type = types.str;
default = "";
description = "VCARD collection to be searched by khard.";
};
});
};
};
@ -80,17 +75,11 @@ in {
config = lib.mkIf cfg.enable {
home.packages = [ pkgs.khard ];
xdg.configFile."khard/khard.conf".text = let
makePath = anAccount:
builtins.toString (/. + lib.concatStringsSep "/" [
anAccount.local.path
anAccount.khard.defaultCollection
]);
in ''
xdg.configFile."khard/khard.conf".text = ''
[addressbooks]
${lib.concatMapStringsSep "\n" (acc: ''
[[${acc.name}]]
path = ${makePath acc}
path = ${acc.local.path}
'') (lib.attrValues accounts)}
${renderSettings cfg.settings}

View file

@ -5,8 +5,6 @@ let
cfg = config.programs.mcfly;
tomlFormat = pkgs.formats.toml { };
bashIntegration = ''
eval "$(${getExe pkgs.mcfly} init bash)"
'' + optionalString cfg.fzf.enable ''
@ -42,37 +40,6 @@ in {
options.programs.mcfly = {
enable = mkEnableOption "mcfly";
settings = mkOption {
type = tomlFormat.type;
default = { };
example = literalExpression ''
{
colors = {
menubar = {
bg = "black";
fg = "red";
};
darkmode = {
prompt = "cyan";
timing = "yellow";
results_selection_fg = "cyan";
results_selection_bg = "black";
results_selection_hl = "red";
};
};
}
'';
description = ''
Settings written to {file}`~/.config/mcfly/config.toml`.
Note, if your McFly database is currently in {file}`~/.mcfly`,
then this option has no effect.
Move the database to {file}`$XDG_DATA_DIR/mcfly/history.db` and
remove {file}`~/.mcfly` to make the settings take effect. See
<https://github.com/cantino/mcfly#database-location>.
'';
};
keyScheme = mkOption {
type = types.enum [ "emacs" "vim" ];
default = "emacs";
@ -138,11 +105,6 @@ in {
{
home.packages = [ pkgs.mcfly ] ++ optional cfg.fzf.enable pkgs.mcfly-fzf;
# Oddly enough, McFly expects this in the data path, not in config.
xdg.dataFile."mcfly/config.toml" = mkIf (cfg.settings != { }) {
source = tomlFormat.generate "mcfly-config.toml" cfg.settings;
};
programs.bash.initExtra = mkIf cfg.enableBashIntegration bashIntegration;
programs.zsh.initExtra = mkIf cfg.enableZshIntegration zshIntegration;

View file

@ -15,8 +15,6 @@ in {
programs.micro = {
enable = mkEnableOption "micro, a terminal-based text editor";
package = mkPackageOption pkgs "micro" { };
settings = mkOption {
type = jsonFormat.type;
default = { };
@ -37,7 +35,7 @@ in {
};
config = mkIf cfg.enable {
home.packages = [ cfg.package ];
home.packages = [ pkgs.micro ];
xdg.configFile."micro/settings.json".source =
jsonFormat.generate "micro-settings" cfg.settings;

View file

@ -229,5 +229,5 @@ in {
}
]);
meta.maintainers = with maintainers; [ thiagokokada chuangzhu ];
meta.maintainers = with maintainers; [ tadeokondrak thiagokokada chuangzhu ];
}

View file

@ -19,8 +19,6 @@ in {
options.programs.papis = {
enable = mkEnableOption "papis";
package = mkPackageOption pkgs "papis" { };
settings = mkOption {
type = with types; attrsOf (oneOf [ bool int str ]);
default = { };
@ -86,7 +84,7 @@ in {
(", namely " + concatStringsSep "," defaultLibraries);
}];
home.packages = [ cfg.package ];
home.packages = [ pkgs.papis ];
xdg.configFile."papis/config" =
mkIf (cfg.libraries != { }) { text = generators.toINI { } settingsIni; };

View file

@ -19,7 +19,7 @@ let
base_url = mkOption {
type = with types; nullOr str;
default = null;
example = "https://bitwarden.example.com/";
example = "bitwarden.example.com";
description =
"The base-url for a self-hosted bitwarden installation.";
};
@ -27,7 +27,7 @@ let
identity_url = mkOption {
type = with types; nullOr str;
default = null;
example = "https://identity.example.com/";
example = "identity.example.com";
description = "The identity url for your bitwarden installation.";
};

View file

@ -1,7 +1,7 @@
{ config, pkgs, lib, ... }:
let cfg = config.programs.script-directory;
in {
meta.maintainers = [ lib.hm.maintainers.janik ];
meta.maintainers = [ lib.maintainers.janik ];
options.programs.script-directory = {
enable = lib.mkEnableOption "script-directory";

View file

@ -29,7 +29,7 @@ let
in "${key} ${generatedValue}";
in {
meta.maintainers = [ ];
meta.maintainers = [ maintainers.ivar ];
options.programs.sm64ex = {
enable = mkEnableOption "sm64ex";

View file

@ -24,7 +24,14 @@ in {
};
settings = mkOption {
type = tomlFormat.type;
type = with types;
let
prim = either bool (either int str);
primOrPrimAttrs = either prim (attrsOf prim);
entry = either prim (listOf primOrPrimAttrs);
entryOrAttrsOf = t: either entry (attrsOf t);
entries = entryOrAttrsOf (entryOrAttrsOf entry);
in attrsOf entries // { description = "Starship configuration"; };
default = { };
example = literalExpression ''
{

View file

@ -14,7 +14,7 @@ let
config.xdg.configHome;
in {
meta.maintainers = [ ];
meta.maintainers = [ maintainers.polykernel ];
options.programs.watson = {
enable = mkEnableOption "watson, a wonderful CLI to track your time";

View file

@ -7,7 +7,7 @@ let
tomlFormat = pkgs.formats.toml { };
bashIntegration = ''
function ${cfg.shellWrapperName}() {
function ya() {
local tmp="$(mktemp -t "yazi-cwd.XXXXX")"
yazi "$@" --cwd-file="$tmp"
if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
@ -18,7 +18,7 @@ let
'';
fishIntegration = ''
function ${cfg.shellWrapperName}
function ya
set tmp (mktemp -t "yazi-cwd.XXXXX")
yazi $argv --cwd-file="$tmp"
if set cwd (cat -- "$tmp"); and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
@ -29,7 +29,7 @@ let
'';
nushellIntegration = ''
def --env ${cfg.shellWrapperName} [...args] {
def --env ya [...args] {
let tmp = (mktemp -t "yazi-cwd.XXXXX")
yazi ...$args --cwd-file $tmp
let cwd = (open $tmp)
@ -40,22 +40,13 @@ let
}
'';
in {
meta.maintainers = with maintainers; [ xyenon eljamm ];
meta.maintainers = with maintainers; [ xyenon ];
options.programs.yazi = {
enable = mkEnableOption "yazi";
package = mkPackageOption pkgs "yazi" { };
shellWrapperName = mkOption {
type = types.str;
default = "yy";
example = "y";
description = ''
Name of the shell wrapper to be called.
'';
};
enableBashIntegration = mkEnableOption "Bash integration";
enableZshIntegration = mkEnableOption "Zsh integration";
@ -141,7 +132,7 @@ in {
};
initLua = mkOption {
type = with types; nullOr (either path lines);
type = with types; nullOr path;
default = null;
description = ''
The init.lua for Yazi itself.
@ -154,11 +145,8 @@ in {
default = { };
description = ''
Lua plugins.
Values should be a package or path containing an `init.lua` file.
Will be linked to {file}`$XDG_CONFIG_HOME/yazi/plugins/<name>.yazi`.
See <https://yazi-rs.github.io/docs/plugins/overview>
for documentation.
See https://yazi-rs.github.io/docs/plugins/overview/ for documentation.
'';
example = literalExpression ''
{
@ -173,10 +161,8 @@ in {
default = { };
description = ''
Pre-made themes.
Values should be a package or path containing the required files.
Will be linked to {file}`$XDG_CONFIG_HOME/yazi/flavors/<name>.yazi`.
See <https://yazi-rs.github.io/docs/flavors/overview/> for documentation.
See https://yazi-rs.github.io/docs/flavors/overview/ for documentation.
'';
example = literalExpression ''
{
@ -185,6 +171,7 @@ in {
}
'';
};
};
config = mkIf cfg.enable {
@ -210,69 +197,11 @@ in {
"yazi/theme.toml" = mkIf (cfg.theme != { }) {
source = tomlFormat.generate "yazi-theme" cfg.theme;
};
"yazi/init.lua" = mkIf (cfg.initLua != null)
(if builtins.isPath cfg.initLua then {
source = cfg.initLua;
} else {
text = cfg.initLua;
});
} // (mapAttrs' (name: value:
nameValuePair "yazi/flavors/${name}.yazi" { source = value; })
cfg.flavors) // (mapAttrs' (name: value:
nameValuePair "yazi/plugins/${name}.yazi" { source = value; })
cfg.plugins);
warnings = filter (s: s != "") (concatLists [
(mapAttrsToList (name: value:
optionalString (hasSuffix ".yazi" name) ''
Flavors like `programs.yazi.flavors."${name}"` should no longer have the suffix ".yazi" in their attribute name.
The flavor will be linked to `$XDG_CONFIG_HOME/yazi/flavors/${name}.yazi`.
You probably want to rename it to `programs.yazi.flavors."${
removeSuffix ".yazi" name
}"`.
'') cfg.flavors)
(mapAttrsToList (name: value:
optionalString (hasSuffix ".yazi" name) ''
Plugins like `programs.yazi.plugins."${name}"` should no longer have the suffix ".yazi" in their attribute name.
The plugin will be linked to `$XDG_CONFIG_HOME/yazi/plugins/${name}.yazi`.
You probably want to rename it to `programs.yazi.plugins."${
removeSuffix ".yazi" name
}"`.
'') cfg.plugins)
]);
assertions = let
mkAsserts = opt: requiredFiles:
mapAttrsToList (name: value:
let
isDir = pathIsDirectory "${value}";
msgNotDir = optionalString (!isDir)
"The path or package should be a directory, not a single file.";
isFileMissing = file:
!(pathExists "${value}/${file}")
|| pathIsDirectory "${value}/${file}";
missingFiles = filter isFileMissing requiredFiles;
msgFilesMissing = optionalString (missingFiles != [ ])
"The ${singularOpt} is missing these files: ${
toString missingFiles
}";
singularOpt = removeSuffix "s" opt;
in {
assertion = isDir && missingFiles == [ ];
message = ''
Value at `programs.yazi.${opt}.${name}` is not a valid yazi ${singularOpt}.
${msgNotDir}
${msgFilesMissing}
Evaluated value: `${value}`
'';
}) cfg.${opt};
in (mkAsserts "flavors" [
"flavor.toml"
"tmtheme.xml"
"README.md"
"preview.png"
"LICENSE"
"LICENSE-tmtheme"
]) ++ (mkAsserts "plugins" [ "init.lua" ]);
"yazi/init.lua" = mkIf (cfg.initLua != null) { source = cfg.initLua; };
} // (mapAttrs'
(name: value: nameValuePair "yazi/plugins/${name}" { source = value; })
cfg.plugins) // (mapAttrs'
(name: value: nameValuePair "yazi/flavors/${name}" { source = value; })
cfg.flavors);
};
}

View file

@ -398,22 +398,6 @@ in
{manpage}`zshzle(1)` for syntax.
'';
};
strategy = mkOption {
type = types.listOf (types.enum [ "history" "completion" "match_prev_cmd" ]);
default = [ "history" ];
description = ''
`ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated.
The strategies in the array are tried successively until a suggestion is found.
There are currently three built-in strategies to choose from:
- `history`: Chooses the most recent match from history.
- `completion`: Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module)
- `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches
the most recently executed command. Note that this strategy won't work as expected with ZSH options that
don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`.
'';
};
};
history = mkOption {
@ -626,7 +610,6 @@ in
(optionalString cfg.autosuggestion.enable ''
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
ZSH_AUTOSUGGEST_STRATEGY=(${concatStringsSep " " cfg.autosuggestion.strategy})
'')
(optionalString (cfg.autosuggestion.enable && cfg.autosuggestion.highlight != null) ''
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="${cfg.autosuggestion.highlight}"

View file

@ -1,7 +1,7 @@
{ config, lib, pkgs, ... }:
let cfg = config.services.cliphist;
in {
meta.maintainers = [ lib.hm.maintainers.janik ];
meta.maintainers = [ lib.maintainers.janik ];
options.services.cliphist = {
enable =

View file

@ -172,7 +172,7 @@ in {
mkPath = { basePath, theme, category }:
"${basePath}/share/icons/${theme.name}/${theme.size}/${category}";
in concatMapStringsSep ":" mkPath (cartesianProduct {
in concatMapStringsSep ":" mkPath (cartesianProductOfSets {
basePath = basePaths;
theme = themes;
category = categories;

View file

@ -9,7 +9,7 @@ let
iniFormat = pkgs.formats.ini { };
in {
meta.maintainers = [ ];
meta.maintainers = with maintainers; [ polykernel ];
options = {
services.fnott = {

View file

@ -1,77 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.glance;
inherit (lib) mkEnableOption mkPackageOption mkOption mkIf getExe;
settingsFormat = pkgs.formats.yaml { };
settingsFile = settingsFormat.generate "glance.yml" cfg.settings;
configFilePath = "${config.xdg.configHome}/glance/glance.yml";
in {
meta.maintainers = [ pkgs.lib.maintainers.gepbird ];
options.services.glance = {
enable = mkEnableOption "glance";
package = mkPackageOption pkgs "glance" { };
settings = mkOption {
type = settingsFormat.type;
default = {
pages = [{
name = "Calendar";
columns = [{
size = "full";
widgets = [{ type = "calendar"; }];
}];
}];
};
example = {
server.port = 5678;
pages = [{
name = "Home";
columns = [{
size = "full";
widgets = [
{ type = "calendar"; }
{
type = "weather";
location = "London, United Kingdom";
}
];
}];
}];
};
description = ''
Configuration written to a yaml file that is read by glance. See
<https://github.com/glanceapp/glance/blob/main/docs/configuration.md>
for more.
'';
};
};
config = mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "services.glance" pkgs
lib.platforms.linux)
];
home.packages = [ cfg.package ];
xdg.configFile."glance/glance.yml".source = settingsFile;
systemd.user.services.glance = {
Unit = {
Description = "Glance feed dashboard server";
PartOf = [ "graphical-session.target" ];
};
Install.WantedBy = [ "graphical-session.target" ];
Service.ExecStart = "${getExe cfg.package} --config ${configFilePath}";
};
};
}

View file

@ -49,7 +49,7 @@ in {
args = concatStringsSep " " ([ "--start" "--foreground" ]
++ optional (cfg.components != [ ])
("--components=" + concatStringsSep "," cfg.components));
in "${pkgs.gnome-keyring}/bin/gnome-keyring-daemon ${args}";
in "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon ${args}";
Restart = "on-abort";
};

View file

@ -52,6 +52,7 @@ in {
"${pkgs.kbfs}/bin/kbfsfuse ${toString cfg.extraFlags} ${mountPoint}";
ExecStopPost = "/run/wrappers/bin/fusermount -u ${mountPoint}";
Restart = "on-failure";
PrivateTmp = true;
};
Install.WantedBy = [ "default.target" ];

View file

@ -171,17 +171,10 @@ in {
];
systemd.user.services.mpd = {
Unit = mkMerge [
{
Description = "Music Player Daemon";
After = [ "network.target" "sound.target" ];
}
(mkIf cfg.network.startWhenNeeded {
Requires = [ "mpd.socket" ];
After = [ "mpd.socket" ];
})
];
Unit = {
After = [ "network.target" "sound.target" ];
Description = "Music Player Daemon";
};
Install = mkIf (!cfg.network.startWhenNeeded) {
WantedBy = [ "default.target" ];

View file

@ -110,11 +110,9 @@ in {
systemd.user.services.nix-gc = {
Unit = { Description = "Nix Garbage Collector"; };
Service = {
Type = "oneshot";
ExecStart = toString (pkgs.writeShellScript "nix-gc"
"exec ${nixPackage}/bin/nix-collect-garbage ${
ExecStart = "${nixPackage}/bin/nix-collect-garbage ${
lib.optionalString (cfg.options != null) cfg.options
}");
}";
};
};
systemd.user.timers.nix-gc = {

View file

@ -15,7 +15,7 @@ let
"--basic-auth ${escapeShellArg cfg.httpAuth}");
in {
meta.maintainers = [ ];
meta.maintainers = [ maintainers.ivar ];
options.services.pbgopy = {
enable = mkEnableOption "pbgopy";

View file

@ -9,7 +9,7 @@ let
presetOpts = optionalString (cfg.preset != "") "--load-preset ${cfg.preset}";
in {
meta.maintainers = [ hm.maintainers.jonringer ];
meta.maintainers = [ maintainers.jonringer ];
options.services.pulseeffects = {
enable = mkEnableOption ''

View file

@ -5,7 +5,7 @@ let
cfg = config.services.ssh-agent;
in {
meta.maintainers = [ lib.hm.maintainers.lheckemann ];
meta.maintainers = [ lib.maintainers.lheckemann ];
options = {
services.ssh-agent = {

View file

@ -74,7 +74,7 @@ in {
}
(mkIf (cfg.config != { }) {
xdg.configFile."stalonetrayrc".text = let
home.file.".stalonetrayrc".text = let
valueToString = v:
if isBool v then
(if v then "true" else "false")
@ -88,7 +88,7 @@ in {
})
(mkIf (cfg.extraConfig != "") {
xdg.configFile."stalonetrayrc".text = cfg.extraConfig;
home.file.".stalonetrayrc".text = cfg.extraConfig;
})
]);
}

View file

@ -60,8 +60,6 @@ in {
After = [ "graphical-session.target" ];
ConditionEnvironment = "WAYLAND_DISPLAY";
Documentation = "man:swayosd(1)";
StartLimitBurst = 5;
StartLimitIntervalSec = 10;
};
Service = {
@ -73,7 +71,6 @@ in {
+ (optionalString (cfg.topMargin != null)
" --top-margin ${toString cfg.topMargin}");
Restart = "always";
RestartSec = "2s";
};
Install = { WantedBy = [ "graphical-session.target" ]; };

View file

@ -40,21 +40,7 @@ in {
];
options.wayland.windowManager.hyprland = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to enable configuration for Hyprland, a tiling Wayland
compositor that doesn't sacrifice on its looks.
::: {.note}
This module configures Hyprland and adds it to your user's {env}`PATH`,
but does not make certain system-level changes. NixOS users should
enable the NixOS module with {option}`programs.hyprland.enable`, which
makes system-level changes such as adding a desktop session entry.
:::
'';
};
enable = lib.mkEnableOption "Hyprland wayland compositor";
package = lib.mkPackageOption pkgs "hyprland" { };

View file

@ -403,7 +403,7 @@ in {
"XCURSOR_THEME"
"XCURSOR_SIZE"
];
example = [ "--all" ];
example = [ "-all" ];
description = ''
Environment variables imported into the systemd and D-Bus user environment.
'';
@ -411,12 +411,7 @@ in {
extraCommands = mkOption {
type = types.listOf types.str;
default = [
"systemctl --user reset-failed"
"systemctl --user start sway-session.target"
"swaymsg -mt subscribe '[]' || true"
"systemctl --user stop sway-session.target"
];
default = [ "systemctl --user start sway-session.target" ];
description = ''
Extra commands to run after D-Bus activation.
'';

View file

@ -14,7 +14,7 @@ let
};
in attrsOf confAtom;
in {
meta.maintainers = [ ];
meta.maintainers = with maintainers; [ polykernel ];
options = {
wayland.windowManager.sway.swaynag = {

View file

@ -34,7 +34,7 @@ function systemdPostReload() {
touch "$oldServiceFiles"
else
find "$oldUserServicePath" \
-maxdepth 1 -name '*.service' \! -name '*@.service' -exec basename '{}' ';' \
-maxdepth 1 -name '*.service' -exec basename '{}' ';' \
| sort \
> "$oldServiceFiles"
fi
@ -43,7 +43,7 @@ function systemdPostReload() {
touch "$newServiceFiles"
else
find "$newUserServicePath" \
-maxdepth 1 -name '*.service' \! -name '*@.service' -exec basename '{}' ';' \
-maxdepth 1 -name '*.service' -exec basename '{}' ';' \
| sort \
> "$newServiceFiles"
fi

View file

@ -100,8 +100,6 @@ let
settingsFormat.generate "user.conf" cfg.settings;
};
configHome = lib.removePrefix config.home.homeDirectory config.xdg.configHome;
in {
meta.maintainers = [ lib.maintainers.rycee ];
@ -337,8 +335,8 @@ in {
in ''
${pkgs.sd-switch}/bin/sd-switch \
''${DRY_RUN:+--dry-run} $VERBOSE_ARG ${timeoutArg} \
''${oldUnitsDir:+--old-units $oldUnitsDir} \
--new-units "$newUnitsDir"
''${oldGenPath:+--old-units $oldGenPath/home-files/.config/systemd/user} \
--new-units $newGenPath/home-files/.config/systemd/user
'';
};
@ -356,22 +354,8 @@ in {
warnEcho "Attempting to reload services anyway..."
fi
if [[ -v oldGenPath ]]; then
oldUnitsDir="$oldGenPath/home-files${configHome}/systemd/user"
if [[ ! -e $oldUnitsDir ]]; then
oldUnitsDir=
fi
fi
newUnitsDir="$newGenPath/home-files${configHome}/systemd/user"
if [[ ! -e $newUnitsDir ]]; then
newUnitsDir=${pkgs.emptyDirectory}
fi
${ensureRuntimeDir} \
${getAttr cfg.startServices cmd}
unset newUnitsDir oldUnitsDir
else
echo "User systemd daemon not running. Skipping reload."
fi

View file

@ -22,7 +22,7 @@ in {
lib.escapeShellArg cfg.backupFileExtension
}"}
${lib.optionalString cfg.verbose "export VERBOSE=1"}
exec ${usercfg.home.activationPackage}/activate
exec ${usercfg.home.activationPackage}/activate --driver-version 1
''
}
'') cfg.users);

View file

@ -13,6 +13,24 @@ let
in {
imports = [ ./common.nix ];
options.home-manager = {
enableLegacyProfileManagement = mkOption {
type = types.bool;
default = versionOlder config.system.stateVersion "24.05";
defaultText = lib.literalMD ''
- `true` for `system.stateVersion` < 24.05,
- `false` otherwise'';
description = ''
Whether to enable legacy profile (and garbage collection root)
management during activation. When enabled, the Home Manager activation
will produce a per-user `home-manager` Nix profile as well as a garbage
collection root, just like in the standalone installation of Home
Manager. Typically, this is not desired when Home Manager is embedded in
the system configuration.
'';
};
};
config = mkMerge [
{
home-manager = {
@ -26,12 +44,21 @@ in {
# Inherit glibcLocales setting from NixOS.
i18n.glibcLocales = lib.mkDefault config.i18n.glibcLocales;
# Legacy profile management is when the activation script generates GC
# root and home-manager profile. The modern way simply relies on the
# GC root that the system maintains, which should also protect the
# Home Manager activation package outputs.
home.activationGenerateGcRoot = cfg.enableLegacyProfileManagement;
}];
};
}
(mkIf (cfg.users != { }) {
systemd.services = mapAttrs' (_: usercfg:
let username = usercfg.home.username;
let
username = usercfg.home.username;
driverVersion =
if cfg.enableLegacyProfileManagement then "0" else "1";
in nameValuePair ("home-manager-${utils.escapeSystemdPath username}") {
description = "Home Manager environment for ${username}";
wantedBy = [ "multi-user.target" ];
@ -78,7 +105,7 @@ in {
| ${sed} -En '/^(${exportedSystemdVariables})=/s/^/export /p'
)"
exec "$1/activate"
exec "$1/activate" --driver-version ${driverVersion}
'';
in "${setupEnv} ${usercfg.home.activationPackage}";
};

View file

@ -189,7 +189,7 @@ in import nmtSrc {
./modules/programs/bemenu
./modules/programs/borgmatic
./modules/programs/boxxy
./modules/programs/firefox/firefox.nix
./modules/programs/firefox
./modules/programs/foot
./modules/programs/freetube
./modules/programs/fuzzel
@ -241,7 +241,6 @@ in import nmtSrc {
./modules/services/fnott
./modules/services/fusuma
./modules/services/git-sync
./modules/services/glance
./modules/services/gpg-agent
./modules/services/gromit-mpx
./modules/services/home-manager-auto-upgrade

View file

@ -11,6 +11,8 @@ let
tests = {
nixos-basics = runTest ./nixos/basics.nix;
nixos-legacy-profile-management =
runTest ./nixos/legacy-profile-management.nix;
standalone-flake-basics = runTest ./standalone/flake-basics.nix;
standalone-standard-basics = runTest ./standalone/standard-basics.nix;
};

View file

@ -7,7 +7,7 @@
nodes.machine = { ... }: {
imports = [ ../../../nixos ]; # Import the HM NixOS module.
virtualisation.memorySize = 2048;
system.stateVersion = "24.05";
users.users.alice = {
isNormalUser = true;
@ -16,14 +16,19 @@
uid = 1000;
};
home-manager.users.alice = { ... }: {
home.stateVersion = "24.05";
home.file.test.text = "testfile";
# Enable a light-weight systemd service.
services.pueue.enable = true;
# We focus on sd-switch since that hopefully will become the default in
# the future.
systemd.user.startServices = "sd-switch";
home-manager = {
enableLegacyProfileManagement = false;
users.alice = { ... }: {
home.stateVersion = "24.05";
home.file.test.text = "testfile";
# Enable a light-weight systemd service.
services.pueue.enable = true;
# We focus on sd-switch since that hopefully will become the default in
# the future.
systemd.user.startServices = "sd-switch";
};
};
};
@ -81,17 +86,13 @@
logout_alice()
with subtest("GC root and profile"):
# There should be a GC root and Home Manager profile and they should point
# to the same path in the Nix store.
gcroot = "/home/alice/.local/state/home-manager/gcroots/current-home"
gcrootTarget = machine.succeed(f"readlink {gcroot}")
with subtest("no GC root and profile"):
# There should be no GC root and Home Manager profile since we are not
# using legacy profile management.
hmState = "/home/alice/.local/state/home-manager"
machine.succeed(f"test ! -e {hmState}")
profile = "/home/alice/.local/state/nix/profiles"
profileTarget = machine.succeed(f"readlink {profile}/home-manager")
profile1Target = machine.succeed(f"readlink {profile}/{profileTarget}")
assert gcrootTarget == profile1Target, \
f"expected GC root and profile to point to same, but pointed to {gcrootTarget} and {profile1Target}"
hmProfile = "/home/alice/.local/state/nix/profiles/home-manager"
machine.succeed(f"test ! -e {hmProfile}")
'';
}

View file

@ -0,0 +1,46 @@
{ pkgs, ... }:
{
name = "nixos-legacy-profile-management";
meta.maintainers = [ pkgs.lib.maintainers.rycee ];
nodes.machine = { ... }: {
imports = [ ../../../nixos ]; # Import the HM NixOS module.
system.stateVersion = "23.11";
users.users.alice = { isNormalUser = true; };
home-manager.users.alice = { ... }: {
home.stateVersion = "23.11";
home.file.test.text = "testfile";
};
};
testScript = ''
start_all()
machine.wait_for_unit("home-manager-alice.service")
with subtest("Home Manager file"):
# The file should be linked with the expected content.
path = "/home/alice/test"
machine.succeed(f"test -L {path}")
actual = machine.succeed(f"cat {path}")
expected = "testfile"
assert actual == expected, f"expected {path} to contain {expected}, but got {actual}"
with subtest("GC root and profile"):
# There should be a GC root and Home Manager profile and they should point
# to the same path in the Nix store.
gcroot = "/home/alice/.local/state/home-manager/gcroots/current-home"
gcrootTarget = machine.succeed(f"readlink {gcroot}")
profile = "/home/alice/.local/state/nix/profiles"
profileTarget = machine.succeed(f"readlink {profile}/home-manager")
profile1Target = machine.succeed(f"readlink {profile}/{profileTarget}")
assert gcrootTarget == profile1Target, \
f"expected GC root and profile to point to same, but pointed to {gcrootTarget} and {profile1Target}"
'';
}

View file

@ -6,7 +6,7 @@
nodes.machine = { ... }: {
imports = [ "${pkgs.path}/nixos/modules/installer/cd-dvd/channel.nix" ];
virtualisation.memorySize = 3072;
virtualisation.memorySize = 2048;
nix = {
registry.home-manager.to = {
type = "path";
@ -88,7 +88,7 @@
} /home/alice/.config/home-manager/home.nix")
actual = succeed_as_alice("home-manager switch")
expected = "Starting units: pueued.service"
expected = "Started pueued.service - active"
assert expected in actual, \
f"expected home-manager switch to contain {expected}, but got {actual}"

View file

@ -85,7 +85,7 @@
} /home/alice/.config/home-manager/home.nix")
actual = succeed_as_alice("home-manager switch")
expected = "Starting units: pueued.service"
expected = "Started pueued.service - active"
assert expected in actual, \
f"expected home-manager switch to contain {expected}, but got {actual}"

View file

@ -10,7 +10,7 @@
};
test.stubs = {
atuin = { name = "atuin"; };
atuin = { };
bash-preexec = { };
};

View file

@ -4,7 +4,7 @@
programs.atuin.enable = true;
test.stubs = {
atuin = { name = "atuin"; };
atuin = { };
bash-preexec = { };
};

View file

@ -13,7 +13,7 @@
};
test.stubs = {
atuin = { name = "atuin"; };
atuin = { };
bash-preexec = { };
};

View file

@ -11,7 +11,7 @@
lib.mkForce (builtins.toFile "empty" "");
test.stubs = {
atuin = { name = "atuin"; };
atuin = { };
bash-preexec = { };
};

View file

@ -18,7 +18,7 @@
lib.mkForce (builtins.toFile "empty" "");
test.stubs = {
atuin = { name = "atuin"; };
atuin = { };
bash-preexec = { };
};

View file

@ -17,7 +17,7 @@
lib.mkForce (builtins.toFile "empty" "");
test.stubs = {
atuin = { name = "atuin"; };
atuin = { };
bash-preexec = { };
};

View file

@ -7,7 +7,7 @@
};
test.stubs = {
atuin = { name = "atuin"; };
atuin = { };
bash-preexec = { };
};

View file

@ -1,32 +1,27 @@
modulePath:
{ config, lib, ... }:
with lib;
{
imports = [ ./setup-firefox-mock-overlay.nix ];
let
firefoxMockOverlay = import ./setup-firefox-mock-overlay.nix modulePath;
in {
imports = [ firefoxMockOverlay ];
config = mkIf config.test.enableBig ({
config = lib.mkIf config.test.enableBig {
test.asserts.assertions.expected =
[ "Container id must be smaller than 4294967294 (2^32 - 2)" ];
} // setAttrByPath modulePath {
enable = true;
profiles.my-profile = {
isDefault = true;
id = 1;
programs.firefox = {
enable = true;
containers = {
"shopping" = {
id = 4294967294;
color = "blue";
icon = "circle";
profiles.my-profile = {
isDefault = true;
id = 1;
containers = {
"shopping" = {
id = 4294967294;
color = "blue";
icon = "circle";
};
};
};
};
});
};
}

View file

@ -0,0 +1,9 @@
{
firefox-profile-settings = ./profile-settings.nix;
firefox-state-version-19_09 = ./state-version-19_09.nix;
firefox-deprecated-native-messenger = ./deprecated-native-messenger.nix;
firefox-duplicate-profile-ids = ./duplicate-profile-ids.nix;
firefox-duplicate-container-ids = ./duplicate-container-ids.nix;
firefox-container-id-out-of-range = ./container-id-out-of-range.nix;
firefox-policies = ./policies.nix;
}

View file

@ -1,26 +1,21 @@
modulePath:
{ config, lib, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
{
imports = [ ./setup-firefox-mock-overlay.nix ];
moduleName = concatStringsSep "." modulePath;
config = lib.mkIf config.test.enableBig {
programs.firefox = {
enable = true;
enableGnomeExtensions = true;
};
firefoxMockOverlay = import ./setup-firefox-mock-overlay.nix modulePath;
in {
imports = [ firefoxMockOverlay ];
config = mkIf config.test.enableBig (setAttrByPath modulePath {
enable = true;
enableGnomeExtensions = true;
} // {
test.asserts.warnings.expected = [''
Using '${moduleName}.enableGnomeExtensions' has been deprecated and
Using 'programs.firefox.enableGnomeExtensions' has been deprecated and
will be removed in the future. Please change to overriding the package
configuration using '${moduleName}.package' instead. You can refer to
configuration using 'programs.firefox.package' instead. You can refer to
its example for how to do this.
''];
});
};
}

View file

@ -1,42 +1,35 @@
modulePath:
{ config, lib, ... }:
with lib;
{
imports = [ ./setup-firefox-mock-overlay.nix ];
let
cfg = getAttrFromPath modulePath config;
firefoxMockOverlay = import ./setup-firefox-mock-overlay.nix modulePath;
in {
imports = [ firefoxMockOverlay ];
config = mkIf config.test.enableBig ({
config = lib.mkIf config.test.enableBig {
test.asserts.assertions.expected = [''
Must not have a ${cfg.name} container with an existing ID but
Must not have a Firefox container with an existing ID but
- ID 9 is used by dangerous, shopping''];
} // setAttrByPath modulePath {
enable = true;
profiles = {
my-profile = {
isDefault = true;
id = 1;
programs.firefox = {
enable = true;
containers = {
"shopping" = {
id = 9;
color = "blue";
icon = "circle";
};
"dangerous" = {
id = 9;
color = "red";
icon = "circle";
profiles = {
my-profile = {
isDefault = true;
id = 1;
containers = {
"shopping" = {
id = 9;
color = "blue";
icon = "circle";
};
"dangerous" = {
id = 9;
color = "red";
icon = "circle";
};
};
};
};
};
});
};
}

View file

@ -1,30 +1,23 @@
modulePath:
{ config, lib, ... }:
with lib;
{
imports = [ ./setup-firefox-mock-overlay.nix ];
let
cfg = getAttrFromPath modulePath config;
firefoxMockOverlay = import ./setup-firefox-mock-overlay.nix modulePath;
in {
imports = [ firefoxMockOverlay ];
config = mkIf config.test.enableBig ({
config = lib.mkIf config.test.enableBig {
test.asserts.assertions.expected = [''
Must not have a ${cfg.name} profile with an existing ID but
Must not have a Firefox profile with an existing ID but
- ID 1 is used by first, second''];
} // setAttrByPath modulePath {
enable = true;
profiles = {
first = {
isDefault = true;
id = 1;
programs.firefox = {
enable = true;
profiles = {
first = {
isDefault = true;
id = 1;
};
second = { id = 1; };
};
second = { id = 1; };
};
});
};
}

View file

@ -1,11 +0,0 @@
let name = "firefox";
in builtins.mapAttrs (test: module: import module [ "programs" name ]) {
"${name}-profile-settings" = ./profile-settings.nix;
"${name}-state-version-19_09" = ./state-version-19_09.nix;
"${name}-deprecated-native-messenger" = ./deprecated-native-messenger.nix;
"${name}-duplicate-profile-ids" = ./duplicate-profile-ids.nix;
"${name}-duplicate-container-ids" = ./duplicate-container-ids.nix;
"${name}-container-id-out-of-range" = ./container-id-out-of-range.nix;
"${name}-policies" = ./policies.nix;
}

View file

@ -1,29 +1,22 @@
modulePath:
{ config, lib, pkgs, ... }:
with lib;
{
imports = [ ./setup-firefox-mock-overlay.nix ];
let
cfg = getAttrFromPath modulePath config;
firefoxMockOverlay = import ./setup-firefox-mock-overlay.nix modulePath;
in {
imports = [ firefoxMockOverlay ];
config = mkIf config.test.enableBig ({
config = lib.mkIf config.test.enableBig {
home.stateVersion = "23.05";
} // setAttrByPath modulePath {
enable = true;
policies = { BlockAboutConfig = true; };
package = pkgs.${cfg.wrappedPackageName}.override {
extraPolicies = { DownloadDirectory = "/foo"; };
programs.firefox = {
enable = true;
policies = { BlockAboutConfig = true; };
package = pkgs.firefox.override {
extraPolicies = { DownloadDirectory = "/foo"; };
};
};
}) // {
nmt.script = ''
jq=${lib.getExe pkgs.jq}
config_file="${cfg.finalPackage}/lib/${cfg.wrappedPackageName}/distribution/policies.json"
config_file="${config.programs.firefox.finalPackage}/lib/firefox/distribution/policies.json"
assertFileExists "$config_file"

View file

@ -1,184 +1,177 @@
modulePath:
{ config, lib, pkgs, ... }:
with lib;
{
imports = [ ./setup-firefox-mock-overlay.nix ];
let
config = lib.mkIf config.test.enableBig {
programs.firefox = {
enable = true;
profiles.basic.isDefault = true;
cfg = getAttrFromPath modulePath config;
firefoxMockOverlay = import ./setup-firefox-mock-overlay.nix modulePath;
in {
imports = [ firefoxMockOverlay ];
config = mkIf config.test.enableBig (setAttrByPath modulePath {
enable = true;
profiles.basic.isDefault = true;
profiles.test = {
id = 1;
settings = {
"general.smoothScroll" = false;
"browser.newtabpage.pinned" = [{
title = "NixOS";
url = "https://nixos.org";
}];
};
};
profiles.bookmarks = {
id = 2;
settings = { "general.smoothScroll" = false; };
bookmarks = [
{
toolbar = true;
bookmarks = [{
name = "Home Manager";
url = "https://wiki.nixos.org/wiki/Home_Manager";
profiles.test = {
id = 1;
settings = {
"general.smoothScroll" = false;
"browser.newtabpage.pinned" = [{
title = "NixOS";
url = "https://nixos.org";
}];
}
{
name = "wikipedia";
tags = [ "wiki" ];
keyword = "wiki";
url = "https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
}
{
name = "kernel.org";
url = "https://www.kernel.org";
}
{
name = "Nix sites";
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
tags = [ "wiki" "nix" ];
url = "https://wiki.nixos.org/";
}
{
name = "Nix sites";
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
url = "https://wiki.nixos.org/";
}
];
}
];
}
];
};
profiles.search = {
id = 3;
search = {
force = true;
default = "Google";
privateDefault = "DuckDuckGo";
order = [ "Nix Packages" "NixOS Wiki" ];
engines = {
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{
name = "type";
value = "packages";
}
{
name = "query";
value = "{searchTerms}";
}
];
}];
icon =
"/run/current-system/sw/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
definedAliases = [ "@np" ];
};
"NixOS Wiki" = {
urls = [{
template =
"https://wiki.nixos.org/index.php?search={searchTerms}";
}];
iconUpdateURL = "https://wiki.nixos.org/favicon.png";
updateInterval = 24 * 60 * 60 * 1000;
definedAliases = [ "@nw" ];
};
"Bing".metaData.hidden = true;
"Google".metaData.alias = "@g";
};
};
};
profiles.searchWithoutDefault = {
id = 4;
search = {
force = true;
order = [ "Google" "Nix Packages" ];
engines = {
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{
name = "type";
value = "packages";
}
{
name = "query";
value = "{searchTerms}";
}
];
profiles.bookmarks = {
id = 2;
settings = { "general.smoothScroll" = false; };
bookmarks = [
{
toolbar = true;
bookmarks = [{
name = "Home Manager";
url = "https://wiki.nixos.org/wiki/Home_Manager";
}];
}
{
name = "wikipedia";
tags = [ "wiki" ];
keyword = "wiki";
url =
"https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
}
{
name = "kernel.org";
url = "https://www.kernel.org";
}
{
name = "Nix sites";
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
tags = [ "wiki" "nix" ];
url = "https://wiki.nixos.org/";
}
{
name = "Nix sites";
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
url = "https://wiki.nixos.org/";
}
];
}
];
}
];
};
definedAliases = [ "@np" ];
profiles.search = {
id = 3;
search = {
force = true;
default = "Google";
privateDefault = "DuckDuckGo";
order = [ "Nix Packages" "NixOS Wiki" ];
engines = {
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{
name = "type";
value = "packages";
}
{
name = "query";
value = "{searchTerms}";
}
];
}];
icon =
"/run/current-system/sw/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
definedAliases = [ "@np" ];
};
"NixOS Wiki" = {
urls = [{
template =
"https://wiki.nixos.org/index.php?search={searchTerms}";
}];
iconUpdateURL = "https://wiki.nixos.org/favicon.png";
updateInterval = 24 * 60 * 60 * 1000;
definedAliases = [ "@nw" ];
};
"Bing".metaData.hidden = true;
"Google".metaData.alias = "@g";
};
};
};
profiles.searchWithoutDefault = {
id = 4;
search = {
force = true;
order = [ "Google" "Nix Packages" ];
engines = {
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{
name = "type";
value = "packages";
}
{
name = "query";
value = "{searchTerms}";
}
];
}];
definedAliases = [ "@np" ];
};
};
};
};
profiles.containers = {
id = 5;
containers = {
"shopping" = {
id = 6;
icon = "circle";
color = "yellow";
};
};
};
};
profiles.containers = {
id = 5;
containers = {
"shopping" = {
id = 6;
icon = "circle";
color = "yellow";
};
};
};
} // {
nmt.script = ''
assertFileRegex \
home-path/bin/${cfg.wrappedPackageName} \
home-path/bin/firefox \
MOZ_APP_LAUNCHER
assertDirectoryExists home-files/${cfg.configPath}/basic
assertDirectoryExists home-files/.mozilla/firefox/basic
assertFileContent \
home-files/${cfg.configPath}/test/user.js \
home-files/.mozilla/firefox/test/user.js \
${./profile-settings-expected-user.js}
assertFileContent \
home-files/${cfg.configPath}/containers/containers.json \
home-files/.mozilla/firefox/containers/containers.json \
${./profile-settings-expected-containers.json}
bookmarksUserJs=$(normalizeStorePaths \
home-files/${cfg.configPath}/bookmarks/user.js)
home-files/.mozilla/firefox/bookmarks/user.js)
assertFileContent \
$bookmarksUserJs \
@ -186,7 +179,7 @@ in {
bookmarksFile="$(sed -n \
'/browser.bookmarks.file/ {s|^.*\(/nix/store[^"]*\).*|\1|;p}' \
$TESTED/home-files/${cfg.configPath}/bookmarks/user.js)"
$TESTED/home-files/.mozilla/firefox/bookmarks/user.js)"
assertFileContent \
$bookmarksFile \
@ -204,12 +197,12 @@ in {
}
assertFirefoxSearchContent \
home-files/${cfg.configPath}/search/search.json.mozlz4 \
home-files/.mozilla/firefox/search/search.json.mozlz4 \
${./profile-settings-expected-search.json}
assertFirefoxSearchContent \
home-files/${cfg.configPath}/searchWithoutDefault/search.json.mozlz4 \
home-files/.mozilla/firefox/searchWithoutDefault/search.json.mozlz4 \
${./profile-settings-expected-search-without-default.json}
'';
});
};
}

View file

@ -1,24 +1,16 @@
modulePath:
{ config, lib, pkgs, ... }:
{ pkgs, ... }:
with lib;
let
cfg = getAttrFromPath modulePath config;
in {
{
nixpkgs.overlays = [
(self: super: {
"${cfg.wrappedPackageName}-unwrapped" =
pkgs.runCommandLocal "${cfg.wrappedPackageName}-0" {
meta.description = "I pretend to be ${cfg.name}";
passthru.gtk3 = null;
} ''
mkdir -p "$out"/{bin,lib}
touch "$out/bin/${cfg.wrappedPackageName}"
chmod 755 "$out/bin/${cfg.wrappedPackageName}"
'';
firefox-unwrapped = pkgs.runCommandLocal "firefox-0" {
meta.description = "I pretend to be Firefox";
passthru.gtk3 = null;
} ''
mkdir -p "$out"/{bin,lib}
touch "$out/bin/firefox"
chmod 755 "$out/bin/firefox"
'';
chrome-gnome-shell =
pkgs.runCommandLocal "dummy-chrome-gnome-shell" { } ''

View file

@ -1,24 +1,19 @@
modulePath:
{ config, lib, pkgs, ... }:
with lib;
let
{
imports = [ ./setup-firefox-mock-overlay.nix ];
cfg = getAttrFromPath modulePath config;
firefoxMockOverlay = import ./setup-firefox-mock-overlay.nix modulePath;
in {
imports = [ firefoxMockOverlay ];
config = lib.mkIf config.test.enableBig ({
config = lib.mkIf config.test.enableBig {
home.stateVersion = "19.09";
} // setAttrByPath modulePath { enable = true; } // {
programs.firefox.enable = true;
nmt.script = ''
assertFileRegex \
home-path/bin/${cfg.wrappedPackageName} \
home-path/bin/firefox \
MOZ_APP_LAUNCHER
'';
});
};
}

View file

@ -13,8 +13,9 @@
};
};
nixpkgs.overlays =
[ (self: super: { gnome-terminal = config.lib.test.mkStubPackage { }; }) ];
nixpkgs.overlays = [
(self: super: { gnome.gnome-terminal = config.lib.test.mkStubPackage { }; })
];
test.stubs.dconf = { };

View file

@ -49,7 +49,9 @@ with lib;
};
nixpkgs.overlays = [
(self: super: { gnome-terminal = config.lib.test.mkStubPackage { }; })
(self: super: {
gnome.gnome-terminal = config.lib.test.mkStubPackage { };
})
];
test.stubs.dconf = { };

View file

@ -1,14 +1,11 @@
{ pkgs, ... }:
{ ... }:
let
configDir =
if pkgs.stdenv.isDarwin then "Library/Application Support" else ".config";
in {
{
programs.jujutsu.enable = true;
test.stubs.jujutsu = { };
nmt.script = ''
assertPathNotExists 'home-files/${configDir}/jj/config.toml'
assertPathNotExists home-files/.config/jj/config.toml
'';
}

View file

@ -1,9 +1,6 @@
{ pkgs, config, ... }:
{ config, ... }:
let
configDir =
if pkgs.stdenv.isDarwin then "Library/Application Support" else ".config";
in {
{
programs.jujutsu = {
enable = true;
package = config.lib.test.mkStubPackage { };
@ -16,8 +13,9 @@ in {
};
nmt.script = ''
assertFileExists 'home-files/${configDir}/jj/config.toml'
assertFileContent 'home-files/${configDir}/jj/config.toml' \
assertFileExists home-files/.config/jj/config.toml
assertFileContent \
home-files/.config/jj/config.toml \
${
builtins.toFile "expected.toml" ''
[user]

View file

@ -2,5 +2,4 @@
khard_empty_config = ./empty_config.nix;
khard_basic_config = ./basic_config.nix;
khard_multiple_accounts = ./multiple_accounts.nix;
khard_dirty_path = ./dirty_path.nix;
}

View file

@ -1,22 +0,0 @@
{
accounts.contact = {
basePath = "/home/user/who/likes///";
accounts.forward = {
local.type = "filesystem";
khard = {
enable = true;
defaultCollection = "////slashes//a/lot";
};
};
};
programs.khard.enable = true;
test.stubs.khard = { };
nmt.script = ''
assertFileContent \
home-files/.config/khard/khard.conf \
${./dirty_path_expected}
'';
}

View file

@ -1,8 +0,0 @@
[addressbooks]
[[forward]]
path = /home/user/who/likes/forward/slashes/a/lot
[general]
default_action=list

View file

@ -2,7 +2,7 @@
let
shellIntegration = ''
function yy() {
function ya() {
local tmp="$(mktemp -t "yazi-cwd.XXXXX")"
yazi "$@" --cwd-file="$tmp"
if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then

View file

@ -1,6 +1,5 @@
{
yazi-settings = ./settings.nix;
yazi-init-lua-string = ./init-lua-string.nix;
yazi-bash-integration-enabled = ./bash-integration-enabled.nix;
yazi-zsh-integration-enabled = ./zsh-integration-enabled.nix;
yazi-fish-integration-enabled = ./fish-integration-enabled.nix;

View file

@ -2,7 +2,7 @@
let
shellIntegration = ''
function yy
function ya
set tmp (mktemp -t "yazi-cwd.XXXXX")
yazi $argv --cwd-file="$tmp"
if set cwd (cat -- "$tmp"); and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]

View file

@ -1,50 +0,0 @@
<div align="center">
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
</div>
<h3 align="center">
Example Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
</h3>
## Cooking up a new flavor
> [!NOTE]
> Please remove this section from your README before publishing.
1. [x] Fork this repository and rename it to `your-flavor-name.yazi`.
2. [ ] Copy the **parts you need to customize** from the [default theme.toml](https://github.com/sxyazi/yazi/blob/main/yazi-config/preset/theme.toml) as `./flavor.toml`, and change them to meet your preferences.
3. [ ] Find a `.tmTheme` file on GitHub that matches the color of your flavor, copy it and it's license file as `./tmtheme.xml`, and `LICENSE-tmtheme`.
4. [ ] Modify the content and preview image in the README to fit your flavor.
## 👀 Preview
<img src="preview.png" width="600" />
## 🎨 Installation
<!-- Please replace "username/example.yazi" with your repository name. -->
```bash
# Linux/macOS
git clone https://github.com/username/example.yazi.git ~/.config/yazi/flavors/example.yazi
# Windows
git clone https://github.com/username/example.yazi.git %AppData%\yazi\config\flavors\example.yazi
```
## ⚙️ Usage
Add the these lines to your `theme.toml` configuration file to use it:
<!-- Please replace "example" with your flavor name. -->
```toml
[flavor]
use = "example"
```
## 📜 License
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.

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