Skip to content

Add dnf Home Manager module for toolbox package management#3

Draft
thrix-bot wants to merge 1 commit intomainfrom
dnf-module
Draft

Add dnf Home Manager module for toolbox package management#3
thrix-bot wants to merge 1 commit intomainfrom
dnf-module

Conversation

@thrix-bot
Copy link
Copy Markdown
Collaborator

@thrix-bot thrix-bot commented Apr 2, 2026

Summary

  • Add a reusable Home Manager module (modules/dnf.nix) that declaratively manages DNF packages inside Fedora toolbox containers
  • dnf.install accepts anything dnf install supports: package names, @groups, paths, URLs, provides
  • dnf.releaseInstall allows per-Fedora-release items keyed by VERSION_ID
  • dnf.upgradeAll optionally runs dnf -y upgrade on every activation
  • Uses gum spin for spinner output when available, falls back to plain echo
  • DNF's built-in idempotency handles already-installed items gracefully

Assisted-by: Claude Code

Summary by Sourcery

Add a reusable Home Manager module for managing DNF packages inside Fedora toolbox containers and wire it into the flake and home configuration.

New Features:

  • Introduce a dnf Home Manager module that declaratively configures DNF package installation and upgrades inside toolbox containers.
  • Allow per-release DNF package configuration keyed by Fedora VERSION_ID.
  • Expose a dnf configuration block in home.nix for enabling the module and specifying install behavior.

Enhancements:

  • Integrate the new dnf module into the flake’s homeManagerModules set and Home Manager module lists so it can be reused across configurations.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 2, 2026

Reviewer's Guide

Adds a reusable Home Manager module for managing DNF packages inside Fedora toolbox containers, wires it into the flake and home configuration, and provides options for declarative installs, per-release installs, and optional upgrades with spinner-based UX.

Sequence diagram for dnf activation in toolbox containers

sequenceDiagram
  participant User
  participant HomeManager
  participant DnfModule
  participant HomeActivation
  participant installScript as ShellScript
  participant ToolboxEnv
  participant dnf as Dnf

  User->>HomeManager: apply configuration
  HomeManager->>DnfModule: read dnf options
  DnfModule->>HomeActivation: define dnfInstall entryAfter reloadSystemd
  HomeManager->>HomeActivation: run dnfInstall script
  HomeActivation->>installScript: execute installScript

  installScript->>ToolboxEnv: check /run/.toolboxenv
  alt not in toolbox
    installScript-->>HomeActivation: exit
  else in toolbox
    installScript->>ToolboxEnv: read /etc/os-release VERSION_ID
    installScript->>installScript: build INSTALL from install and releaseInstall
    alt gum available
      installScript->>dnf: gum spin dnf -y install INSTALL
    else gum not available
      installScript->>dnf: dnf -y install INSTALL
    end
    alt upgradeAll true
      alt gum available
        installScript->>dnf: gum spin dnf -y upgrade
      else gum not available
        installScript->>dnf: dnf -y upgrade
      end
    end
  end
Loading

Class diagram for the new dnf Home Manager module

classDiagram
  class DnfModule {
    +bool enable
    +list~string~ install
    +attrs~list~string~~ releaseInstall
    +bool upgradeAll
  }

  class HomeActivation {
    +string dnfInstall
  }

  class Lib {
    +mkEnableOption(description)
    +mkOption(args)
    +mkIf(condition)
    +hm_dag_entryAfter(dependencies, script)
    +concatStringsSep(sep, items)
    +concatStrings(items)
    +mapAttrsToList(fn, attrs)
    +optionalString(condition, value)
  }

  class Config {
    +DnfModule dnf
    +HomeActivation home_activation
  }

  Config --> DnfModule : contains
  Config --> HomeActivation : configures
  DnfModule ..> Lib : uses
  HomeActivation ..> DnfModule : derived from
  HomeActivation ..> Lib : uses
Loading

File-Level Changes

Change Details Files
Introduce reusable Home Manager module for DNF package management in Fedora toolbox containers.
  • Define dnf options (enable, install, releaseInstall, upgradeAll) with types, defaults, examples, and descriptions.
  • Implement activation script that only runs inside toolbox (/run/.toolboxenv), detects Fedora release via /etc/os-release, and builds the install list from global and per-release items.
  • Add helper _dnf_run function that uses gum spin when available, falling back to colored echo and direct command execution.
  • Execute dnf -y install with the computed item list and optionally dnf -y upgrade when upgradeAll is enabled.
  • Hook activation script into home.activation as a DAG entry after reloadSystemd when dnf is enabled.
modules/dnf.nix
Wire the new DNF module into the flake and enable it in the main Home Manager configuration.
  • Expose the dnf module via the flake's homeManagerModules attribute.
  • Include the dnf module in the Home Manager modules list for both supported system configurations.
  • Add a dnf configuration block in home.nix with enable=true and empty install list plus commented examples for releaseInstall and upgradeAll.
flake.nix
home.nix

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Add a reusable Home Manager module that declaratively manages DNF
packages inside Fedora toolbox containers. The module provides:

- `dnf.install` — items to install via `dnf install` (packages, groups,
  paths, URLs, provides)
- `dnf.releaseInstall` — per-Fedora-release items keyed by `VERSION_ID`
- `dnf.upgradeAll` — optional full `dnf -y upgrade` on every activation

The activation script uses `gum spin` for spinner output when available,
falling back to plain echo. DNF's built-in idempotency handles
already-installed items gracefully.

Assisted-by: Claude Code
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
@thrix-bot
Copy link
Copy Markdown
Collaborator Author

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The shell fragment in installScript builds INSTALL as a space-separated string and then expands it unquoted ($INSTALL), which can lead to word-splitting issues for items with spaces or special characters; consider using a bash array for the install list instead of a plain string.
  • Since this module is intended to run only inside toolbox, you might want to move the /run/.toolboxenv check into a Nix-level condition (e.g. via mkIf) or at least add explicit checks/early exits for missing sudo/dnf binaries to fail more clearly when used in other environments.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The shell fragment in `installScript` builds `INSTALL` as a space-separated string and then expands it unquoted (`$INSTALL`), which can lead to word-splitting issues for items with spaces or special characters; consider using a bash array for the install list instead of a plain string.
- Since this module is intended to run only inside toolbox, you might want to move the `/run/.toolboxenv` check into a Nix-level condition (e.g. via `mkIf`) or at least add explicit checks/early exits for missing `sudo`/`dnf` binaries to fail more clearly when used in other environments.

## Individual Comments

### Comment 1
<location path="modules/dnf.nix" line_range="33-41" />
<code_context>
+
+    # Install items (dnf handles already-installed items gracefully)
+    if [ -n "$INSTALL" ]; then
+      _dnf_run "DNF: installing $INSTALL" /usr/bin/sudo dnf -y install $INSTALL
+    fi
+
+    # Upgrade all packages
+    ${lib.optionalString cfg.upgradeAll ''
+      _dnf_run "DNF: upgrading all packages" /usr/bin/sudo dnf -y upgrade
+    ''}
+  '';
</code_context>
<issue_to_address>
**suggestion:** Hardcoding `/usr/bin/sudo` may break on images without sudo or with it in a different path.

In minimal or toolbox containers `sudo` may be missing or not at `/usr/bin/sudo`, causing `_dnf_run` to fail even though `dnf` is present. Consider either detecting `sudo` and falling back to direct `dnf` when absent, or just calling `sudo dnf ...` and relying on `$PATH` instead of a hardcoded path.

```suggestion
    # Install items (dnf handles already-installed items gracefully)
    if [ -n "$INSTALL" ]; then
      if command -v sudo &>/dev/null; then
        _dnf_run "DNF: installing $INSTALL" sudo dnf -y install $INSTALL
      else
        _dnf_run "DNF: installing $INSTALL" dnf -y install $INSTALL
      fi
    fi

    # Upgrade all packages
    ${lib.optionalString cfg.upgradeAll ''
      if command -v sudo &>/dev/null; then
        _dnf_run "DNF: upgrading all packages" sudo dnf -y upgrade
      else
        _dnf_run "DNF: upgrading all packages" dnf -y upgrade
      fi
    ''}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread modules/dnf.nix
Comment on lines +33 to +41
# Install items (dnf handles already-installed items gracefully)
if [ -n "$INSTALL" ]; then
_dnf_run "DNF: installing $INSTALL" /usr/bin/sudo dnf -y install $INSTALL
fi

# Upgrade all packages
${lib.optionalString cfg.upgradeAll ''
_dnf_run "DNF: upgrading all packages" /usr/bin/sudo dnf -y upgrade
''}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Hardcoding /usr/bin/sudo may break on images without sudo or with it in a different path.

In minimal or toolbox containers sudo may be missing or not at /usr/bin/sudo, causing _dnf_run to fail even though dnf is present. Consider either detecting sudo and falling back to direct dnf when absent, or just calling sudo dnf ... and relying on $PATH instead of a hardcoded path.

Suggested change
# Install items (dnf handles already-installed items gracefully)
if [ -n "$INSTALL" ]; then
_dnf_run "DNF: installing $INSTALL" /usr/bin/sudo dnf -y install $INSTALL
fi
# Upgrade all packages
${lib.optionalString cfg.upgradeAll ''
_dnf_run "DNF: upgrading all packages" /usr/bin/sudo dnf -y upgrade
''}
# Install items (dnf handles already-installed items gracefully)
if [ -n "$INSTALL" ]; then
if command -v sudo &>/dev/null; then
_dnf_run "DNF: installing $INSTALL" sudo dnf -y install $INSTALL
else
_dnf_run "DNF: installing $INSTALL" dnf -y install $INSTALL
fi
fi
# Upgrade all packages
${lib.optionalString cfg.upgradeAll ''
if command -v sudo &>/dev/null; then
_dnf_run "DNF: upgrading all packages" sudo dnf -y upgrade
else
_dnf_run "DNF: upgrading all packages" dnf -y upgrade
fi
''}

Copy link
Copy Markdown
Owner

@thrix thrix Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sourcery-ai well, tbh if we want to make it right, we should rather check fi the user is not root, and if not require sudo ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants