From 3db43afcb4a755221530ff821ec4ac30eca77033 Mon Sep 17 00:00:00 2001 From: Robert Helgesson Date: Sat, 29 Jul 2023 19:56:00 +0200 Subject: [PATCH] home-manager: rework news command This new way of handling news should also work in Nix Flake setups. --- home-manager/build-news.nix | 54 +++++++++++++++ home-manager/home-manager | 111 +++++++++++++++++++------------ home-manager/home-manager.nix | 58 +--------------- home-manager/po/home-manager.pot | 47 ++++++------- modules/misc/news.nix | 12 ++++ modules/po/hm-modules.pot | 6 +- 6 files changed, 158 insertions(+), 130 deletions(-) create mode 100644 home-manager/build-news.nix diff --git a/home-manager/build-news.nix b/home-manager/build-news.nix new file mode 100644 index 000000000..251f56ae4 --- /dev/null +++ b/home-manager/build-news.nix @@ -0,0 +1,54 @@ +# Used by the home-manager tool to present news to the user. The content of this +# file is considered internal and the exported fields may change without +# warning. + +{ newsJsonFile, newsReadIdsFile ? null }: + +let + inherit (builtins) + concatStringsSep filter hasAttr isString length optionalString readFile + replaceStrings sort split; + + newsJson = builtins.fromJSON (builtins.readFile newsJsonFile); + + # Sorted and relevant entries. + relevantEntries = + sort (a: b: a.time > b.time) (filter (e: e.condition) newsJson.entries); + + newsReadIds = if newsReadIdsFile == null then + { } + else + let ids = filter isString (split "\n" (readFile newsReadIdsFile)); + in builtins.listToAttrs (map (id: { + name = id; + value = null; + }) ids); + + newsIsRead = entry: hasAttr entry.id newsReadIds; + + newsUnread = let pred = entry: entry.condition && !newsIsRead entry; + in filter pred relevantEntries; + + prettyTime = t: replaceStrings [ "T" "+00:00" ] [ " " "" ] t; + + layoutNews = entries: + let + mkTextEntry = entry: + let flag = if newsIsRead entry then "read" else "unread"; + in '' + * ${prettyTime entry.time} [${flag}] + + ${replaceStrings [ "\n" ] [ "\n " ] entry.message} + ''; + in concatStringsSep "\n\n" (map mkTextEntry entries); +in { + meta = { + numUnread = length newsUnread; + display = newsJson.display; + ids = concatStringsSep "\n" (map (e: e.id) newsJson.entries); + }; + news = { + all = layoutNews relevantEntries; + unread = layoutNews newsUnread; + }; +} diff --git a/home-manager/home-manager b/home-manager/home-manager index ea8546781..f02a2ce19 100644 --- a/home-manager/home-manager +++ b/home-manager/home-manager @@ -529,13 +529,16 @@ function doBuildFlake() { "${PASSTHROUGH_OPTS[@]}" } -# Presents news to the user. Takes as argument the path to a "news -# info" file as generated by `buildNews`. +# Presents news to the user as specified by the `news.display` option. function presentNews() { - local infoFile="$1" + local newsNixFile="$WORK_DIR/news.nix" + buildNews "$newsNixFile" - # shellcheck source=/dev/null - . "$infoFile" + local newsDisplay + newsDisplay="$(nix-instantiate --eval --expr "(import ${newsNixFile}).meta.display" | xargs)" + + local newsNumUnread + newsNumUnread="$(nix-instantiate --eval --expr "(import ${newsNixFile}).meta.numUnread" | xargs)" # shellcheck disable=2154 if [[ $newsNumUnread -eq 0 ]]; then @@ -601,12 +604,9 @@ function doBuild() { ${NO_OUT_LINK+--no-out-link} \ --attr activationPackage \ || return - - local newsInfo - newsInfo=$(buildNews) - - presentNews "$newsInfo" fi + + presentNews } function doSwitch() { @@ -632,12 +632,9 @@ function doSwitch() { --out-link "$generation" \ --attr activationPackage \ && "$generation/activate" || return - - local newsInfo - newsInfo=$(buildNews) - - presentNews "$newsInfo" fi + + presentNews } function doListGens() { @@ -718,62 +715,88 @@ function newsReadIdsFile() { echo "$path" } -# Builds news meta information to be sourced into this script. +# Builds the Home Manager news data file. # # Note, we suppress build output to remove unnecessary verbosity. We # put the output in the work directory to avoid the risk of an # unfortunately timed GC removing it. function buildNews() { - setFlakeAttribute + local newsNixFile="$1" + local newsJsonFile="$WORK_DIR/news.json" + if [[ -v FLAKE_CONFIG_URI ]]; then - # translators: Here "flake" is a noun that refers to the Nix Flakes feature. - _iError "Sorry, this command is not yet supported in flake setup" >&2 - exit 1 + # TODO: Use check=false to make it more likely that the build succeeds. + doBuildFlake \ + "$FLAKE_CONFIG_URI.config.news.json.output" \ + --quiet \ + --out-link "$newsJsonFile" \ + || return + else + doBuildAttr \ + --out-link "$newsJsonFile" \ + --arg check false \ + --attr config.news.json.output \ + > /dev/null \ + || return fi - local output - output="$WORK_DIR/news-info.sh" + local extraArgs=() - doBuildAttr \ - --out-link "$output" \ - --no-build-output \ - --quiet \ - --arg check false \ - --argstr newsReadIdsFile "$(newsReadIdsFile)" \ - --attr newsInfo \ - > /dev/null + for p in "${EXTRA_NIX_PATH[@]}"; do + extraArgs=("${extraArgs[@]}" "-I" "$p") + done - echo "$output" + local readIdsFile + readIdsFile=$(newsReadIdsFile) + + nix-instantiate \ + --no-build-output --strict \ + --eval '' \ + --arg newsJsonFile "$newsJsonFile" \ + --arg newsReadIdsFile "$readIdsFile" \ + "${extraArgs[@]}" \ + > "$newsNixFile" } function doShowNews() { setWorkDir + setFlakeAttribute - local infoFile - infoFile=$(buildNews) || return 1 + local newsNixFile="$WORK_DIR/news.nix" + buildNews "$newsNixFile" - # shellcheck source=/dev/null - . "$infoFile" + local readIdsFile + readIdsFile=$(newsReadIdsFile) - # shellcheck disable=2154 + local news + + # shellcheck disable=2154,2046 case $1 in --all) - ${PAGER:-less} "$newsFileAll" + news="$(nix-instantiate --quiet --eval --expr "(import ${newsNixFile}).news.all")" ;; --unread) - ${PAGER:-less} "$newsFileUnread" + news="$(nix-instantiate --quiet --eval --expr "(import ${newsNixFile}).news.unread")" ;; *) _i 'Unknown argument %s' "$1" return 1 esac - # shellcheck disable=2154 - if [[ -s "$newsUnreadIdsFile" ]]; then - local newsReadIdsFile - newsReadIdsFile="$(newsReadIdsFile)" - cat "$newsUnreadIdsFile" >> "$newsReadIdsFile" - fi + # Prints the news without surrounding quotes. + echo -e "${news:1:-1}" | ${PAGER:-less} + + local allIds + allIds="$(nix-instantiate --quiet --eval --expr "(import ${newsNixFile}).meta.ids")" + allIds="${allIds:1:-1}" # Trim surrounding quotes. + + local readIdsFileNew="$WORK_DIR/news-read-ids.new" + { + cat "$readIdsFile" + echo -e "$allIds" + } | sort | uniq > "$readIdsFileNew" + + mv -f "$readIdsFileNew" "$readIdsFile" } function doUninstall() { diff --git a/home-manager/home-manager.nix b/home-manager/home-manager.nix index 6f2f4b667..13a638fd8 100644 --- a/home-manager/home-manager.nix +++ b/home-manager/home-manager.nix @@ -15,60 +15,4 @@ let check = check; }; - newsReadIds = if newsReadIdsFile == null then - { } - else - let ids = splitString "\n" (fileContents newsReadIdsFile); - in builtins.listToAttrs (map (id: { - name = id; - value = null; - }) ids); - - newsIsRead = entry: builtins.hasAttr entry.id newsReadIds; - - newsFiltered = let pred = entry: entry.condition && !newsIsRead entry; - in filter pred env.newsEntries; - - newsNumUnread = length newsFiltered; - - newsFileUnread = pkgs.writeText "news-unread.txt" (concatMapStringsSep "\n\n" - (entry: - let - time = - replaceStrings [ "T" ] [ " " ] (removeSuffix "+00:00" entry.time); - in '' - * ${time} - - ${replaceStrings [ "\n" ] [ "\n " ] entry.message} - '') newsFiltered); - - newsFileAll = pkgs.writeText "news-all.txt" (concatMapStringsSep "\n\n" - (entry: - let - flag = if newsIsRead entry then "read" else "unread"; - time = - replaceStrings [ "T" ] [ " " ] (removeSuffix "+00:00" entry.time); - in '' - * ${time} [${flag}] - - ${replaceStrings [ "\n" ] [ "\n " ] entry.message} - '') env.newsEntries); - - # File where each line corresponds to an unread news entry - # identifier. If non-empty then the file ends in "\n". - newsUnreadIdsFile = pkgs.writeText "news-unread-ids" - (let text = concatMapStringsSep "\n" (entry: entry.id) newsFiltered; - in text + optionalString (text != "") "\n"); - - newsInfo = pkgs.writeText "news-info.sh" '' - local newsNumUnread=${toString newsNumUnread} - local newsDisplay="${env.newsDisplay}" - local newsFileAll="${newsFileAll}" - local newsFileUnread="${newsFileUnread}" - local newsUnreadIdsFile="${newsUnreadIdsFile}" - ''; - -in { - inherit (env) activationPackage; - inherit newsInfo; -} +in { inherit (env) activationPackage config; } diff --git a/home-manager/po/home-manager.pot b/home-manager/po/home-manager.pot index 2260e4e7d..df8aaf283 100644 --- a/home-manager/po/home-manager.pot +++ b/home-manager/po/home-manager.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Home Manager\n" "Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n" -"POT-Creation-Date: 2023-07-16 10:09+0200\n" +"POT-Creation-Date: 2023-07-30 09:08+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -78,11 +78,11 @@ msgid "Can't inspect options of a flake configuration" msgstr "" #: home-manager/home-manager:281 home-manager/home-manager:304 -#: home-manager/home-manager:1000 +#: home-manager/home-manager:1023 msgid "%s: unknown option '%s'" msgstr "" -#: home-manager/home-manager:286 home-manager/home-manager:1001 +#: home-manager/home-manager:286 home-manager/home-manager:1024 msgid "Run '%s --help' for usage help" msgstr "" @@ -124,7 +124,7 @@ msgstr "" msgid "Can't instantiate a flake configuration" msgstr "" -#: home-manager/home-manager:549 +#: home-manager/home-manager:552 msgid "" "There is %d unread and relevant news item.\n" "Read it by running the command \"%s news\"." @@ -134,77 +134,72 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: home-manager/home-manager:563 +#: home-manager/home-manager:566 msgid "Unknown \"news.display\" setting \"%s\"." msgstr "" -#: home-manager/home-manager:570 +#: home-manager/home-manager:573 #, sh-format msgid "Please set the $EDITOR environment variable" msgstr "" -#: home-manager/home-manager:585 +#: home-manager/home-manager:588 msgid "Cannot run build in read-only directory" msgstr "" -#: home-manager/home-manager:669 +#: home-manager/home-manager:666 msgid "No generation with ID %s" msgstr "" -#: home-manager/home-manager:671 +#: home-manager/home-manager:668 msgid "Cannot remove the current generation %s" msgstr "" -#: home-manager/home-manager:673 +#: home-manager/home-manager:670 msgid "Removing generation %s" msgstr "" -#: home-manager/home-manager:692 +#: home-manager/home-manager:689 msgid "No generations to expire" msgstr "" -#: home-manager/home-manager:703 +#: home-manager/home-manager:700 msgid "No home-manager packages seem to be installed." msgstr "" -#. translators: Here "flake" is a noun that refers to the Nix Flakes feature. -#: home-manager/home-manager:730 -msgid "Sorry, this command is not yet supported in flake setup" -msgstr "" - -#: home-manager/home-manager:767 +#: home-manager/home-manager:781 msgid "Unknown argument %s" msgstr "" -#: home-manager/home-manager:783 +#: home-manager/home-manager:805 msgid "This will remove Home Manager from your system." msgstr "" -#: home-manager/home-manager:786 +#: home-manager/home-manager:808 msgid "This is a dry run, nothing will actually be uninstalled." msgstr "" -#: home-manager/home-manager:790 +#: home-manager/home-manager:812 msgid "Really uninstall Home Manager?" msgstr "" -#: home-manager/home-manager:796 +#: home-manager/home-manager:818 msgid "Switching to empty Home Manager configuration..." msgstr "" -#: home-manager/home-manager:823 +#: home-manager/home-manager:846 msgid "Yay!" msgstr "" -#: home-manager/home-manager:828 +#: home-manager/home-manager:851 msgid "Home Manager is uninstalled but your home.nix is left untouched." msgstr "" -#: home-manager/home-manager:1040 +#: home-manager/home-manager:1063 msgid "expire-generations expects one argument, got %d." msgstr "" -#: home-manager/home-manager:1062 +#: home-manager/home-manager:1085 msgid "Unknown command: %s" msgstr "" diff --git a/modules/misc/news.nix b/modules/misc/news.nix index 61d186da0..0538d767b 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -85,10 +85,22 @@ in default = [ ]; description = "News entries."; }; + + json = { + output = mkOption { + internal = true; + type = types.package; + description = "The generated JSON file package."; + }; + }; }; }; config = { + news.json.output = pkgs.writeText "hm-news.json" (builtins.toJSON { + inherit (cfg) display entries; + }); + # Add news entries in chronological order (i.e., latest time # should be at the bottom of the list). The time should be # formatted as given in the output of diff --git a/modules/po/hm-modules.pot b/modules/po/hm-modules.pot index 2b4030b6c..8fdef9e6e 100644 --- a/modules/po/hm-modules.pot +++ b/modules/po/hm-modules.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Home Manager Modules\n" "Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n" -"POT-Creation-Date: 2023-07-16 10:09+0200\n" +"POT-Creation-Date: 2023-07-30 09:08+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -33,7 +33,7 @@ msgstr "" msgid "No change so reusing latest profile generation %s" msgstr "" -#: modules/home-environment.nix:634 +#: modules/home-environment.nix:626 msgid "" "Oops, Nix failed to install your new Home Manager profile!\n" "\n" @@ -49,7 +49,7 @@ msgid "" "Then try activating your Home Manager configuration again." msgstr "" -#: modules/home-environment.nix:667 +#: modules/home-environment.nix:659 msgid "Activating %s" msgstr ""