How to add service from another GitHub repository into NixOS configuration?

I currently have following for my servers configuration that I deploy with morph:

let
  pkgs = import (fetchTarball "https://github.com/knarkzel/nixpkgs/archive/043de04db8a6b0391b3fefaaade160514d866946.tar.gz") {};
  paste = import (pkgs.fetchFromGitHub {
    owner = "JJJollyjim";
    repo = "nixos-flask-example";
    rev = "master";
    sha256 = "";
  });
in {
  network.pkgs = pkgs;

  oddharaldxyz = {
    lib,
    name,
    modulesPath,
    ...
  }: {
    imports = [
      (modulesPath + "/virtualisation/openstack-config.nix")
    ];

    # Morph options
    deployment.targetUser = "root";
    deployment.targetHost = "oddharald.xyz";

    # Environment
    networking.hostName = name;
    system.stateVersion = "22.05";
    environment.systemPackages = with pkgs; [];

    # oddharald.xyz
    networking.firewall.allowedTCPPorts = [80];
    services.nginx = {
      enable = true;
      virtualHosts."oddharald.xyz" = {
        root = "/var/oddharald.xyz";
      };
    };

    # paste service
    paste.enable = true;
  };
}

However, when trying to add paste from here, I get following errors when deploying:

nixos ❯ morph deploy servers.nix switch
error: The option `paste' does not exist. Definition values:
       - In `/nix/store/0i7l09l9hlgp5jpk5yi55ybxj3z901qg-morph-1.7.0-lib/eval-machines.nix':
           {
             paste = {
               enable = true;
             };
           }
Error while running `nix-instantiate ..`: exit status 1

You forgot to import the paste module you’re fetching here.

Also, you want to fetch the paste source code, not import (i.e. evaluate) its default.nix here.

@@ -1,11 +1,11 @@
 let
   pkgs = import (fetchTarball "https://github.com/knarkzel/nixpkgs/archive/043de04db8a6b0391b3fefaaade160514d866946.tar.gz") {};
-  paste = import (pkgs.fetchFromGitHub {
+  paste = pkgs.fetchFromGitHub {
     owner = "JJJollyjim";
     repo = "nixos-flask-example";
     rev = "master";
     sha256 = "";
-  });
+  };
 in {
   network.pkgs = pkgs;
 
@@ -17,6 +17,7 @@ in {
   }: {
     imports = [
       (modulesPath + "/virtualisation/openstack-config.nix")
+      "${paste}/service.nix"
     ];
 
     # Morph options

Nice, that fixed the previous error. Now I get this:

nixos ❯ morph deploy servers.nix switch
error: path '/nix/store/50p2r8wpv05gw9bbrrnybcpk6h7a9h9s-source.drv' is not valid
(use '--show-trace' to show detailed location information)
Error while running `nix-instantiate ..`: exit status 1

Trace:

nixos ❯ morph deploy servers.nix switch --show-trace
error: path '/nix/store/50p2r8wpv05gw9bbrrnybcpk6h7a9h9s-source.drv' is not valid

       … while realising the context of path '/nix/store/26bs1l7blq130y3ddgw5az3j995rg15c-source/service.nix'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:376:99:

          375|           throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}"
          376|         else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args);
             |                                                                                                   ^
          377|

       … while evaluating 'isFunction'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/trivial.nix:448:16:

          447|   */
          448|   isFunction = f: builtins.isFunction f ||
             |                ^
          449|     (f ? __functor && isFunction (f.__functor f));

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:474:78:

          473|
          474|   applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
             |                                                                              ^
          475|     let

       … while evaluating 'applyModuleArgsIfFunction'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:474:39:

          473|
          474|   applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
             |                                       ^
          475|     let

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:376:59:

          375|           throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}"
          376|         else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args);
             |                                                           ^
          377|

       … while evaluating 'unifyModuleSyntax'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:443:34:

          442|      of ‘options’, ‘config’ and ‘imports’ attributes. */
          443|   unifyModuleSyntax = file: key: m:
             |                                  ^
          444|     let

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:376:14:

          375|           throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}"
          376|         else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args);
             |              ^
          377|

       … while evaluating 'loadModule'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:370:53:

          369|       # Like unifyModuleSyntax, but also imports paths and calls functions if necessary
          370|       loadModule = args: fallbackFile: fallbackKey: m:
             |                                                     ^
          371|         if isFunction m || isAttrs m then

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:411:22:

          410|           let
          411|             module = loadModule args parentFile "${parentKey}:anon-${toString n}" x;
             |                      ^
          412|             collectedImports = collectStructuredModules module._file module.key module.imports args;

       … while evaluating the attribute 'disabled'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:406:13:

          405|           collectResults = modules: {
          406|             disabled = concatLists (catAttrs "disabled" modules);
             |             ^
          407|             inherit modules;

       … while evaluating the attribute 'disabled'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:406:13:

          405|           collectResults = modules: {
          406|             disabled = concatLists (catAttrs "disabled" modules);
             |             ^
          407|             inherit modules;

       … while evaluating the attribute 'disabled'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:406:13:

          405|           collectResults = modules: {
          406|             disabled = concatLists (catAttrs "disabled" modules);
             |             ^
          407|             inherit modules;

       … while evaluating anonymous lambda

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:428:31:

          427|           disabledKeys = map moduleKey disabled;
          428|           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
             |                               ^
          429|         in map (attrs: attrs.module) (builtins.genericClosure {

       … from call site

       … while evaluating 'filterModules'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:424:36:

          423|       # modules recursively. It returns the final list of unique-by-key modules
          424|       filterModules = modulesPath: { disabled, modules }:
             |                                    ^
          425|         let

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:435:7:

          434|     in modulesPath: initialModules: args:
          435|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
             |       ^
          436|

       … while evaluating anonymous lambda

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:434:37:

          433|
          434|     in modulesPath: initialModules: args:
             |                                     ^
          435|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:280:25:

          279|       merged =
          280|         let collected = collectModules
             |                         ^
          281|           (specialArgs.modulesPath or "")

       … while evaluating 'reverseList'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/lists.nix:394:17:

          393|   */
          394|   reverseList = xs:
             |                 ^
          395|     let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:284:33:

          283|           ({ inherit lib options config specialArgs; } // specialArgs);
          284|         in mergeModules prefix (reverseList collected);
             |                                 ^
          285|

       … while evaluating 'byName'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:550:25:

          549|       */
          550|       byName = attr: f: modules:
             |                         ^
          551|         zipAttrsWith (n: concatLists)

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:567:21:

          566|       # an attrset 'name' => list of submodules that declare ‘name’.
          567|       declsByName = byName "options" (module: option:
             |                     ^
          568|           [{ inherit (module) _file; options = option; }]

       … while evaluating the attribute 'matchedOptions'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:646:14:

          645|     in {
          646|       inherit matchedOptions;
             |              ^
          647|

       … while evaluating 'mapAttrsRecursiveCond'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/attrsets.nix:393:36:

          392|   */
          393|   mapAttrsRecursiveCond = cond: f: set:
             |                                    ^
          394|     let

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/modules.nix:292:28:

          291|           # For definitions that have an associated option
          292|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                            ^
          293|

       … while evaluating 'isDerivation'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/attrsets.nix:427:18:

          426|   */
          427|   isDerivation = x: x.type or null == "derivation";
             |                  ^
          428|

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/options.nix:259:8:

          258|   scrubOptionValue = x:
          259|     if isDerivation x then
             |        ^
          260|       { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }

       … while evaluating 'scrubOptionValue'

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/options.nix:258:22:

          257|   */
          258|   scrubOptionValue = x:
             |                      ^
          259|     if isDerivation x then

       … from call site

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/options.nix:262:44:

          261|     else if isList x then map scrubOptionValue x
          262|     else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
             |                                            ^
          263|     else x;

       … while evaluating anonymous lambda

       at /nix/store/abhnrd57akqms4f06w50vzm3qnqr9zb3-source/lib/options.nix:262:41:

          261|     else if isList x then map scrubOptionValue x
          262|     else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
             |                                         ^
          263|     else x;

       … from call site
Error while running `nix-instantiate ..`: exit status 1

Right. I did not actually run the code I pasted earlier :sweat_smile:

Strange, I get an infinite recursion on my side (Nix 2.10.3). The infinite recursion comes from the fact the NixOS module system is trying to import the external modules before starting to evaluate pkgs. (I forgot about that when writing the above post, apologies)

You have to bootstrap this using a fetcher built into Nix (ie. part of builtins).

let
  paste = builtins.fetchTarball {
    url = "https://github.com/JJJollyjim/nixos-flask-example/archive/refs/heads/master.tar.gz";
    sha256 = "sha256:02j802chsba7q4fmd6mzk59x1gywdpm210x1m4l44c219bxapl4r";
  };

in {
  imports = [
    "${paste}/service.nix"
  ];

  services.paste.enable = true;
}

^ This does the trick on a test setup.

1 Like

Great. It works now. Here’s my final code:

let
  pkgs = import (fetchTarball "https://github.com/knarkzel/nixpkgs/archive/043de04db8a6b0391b3fefaaade160514d866946.tar.gz") {};
  paste = fetchTarball {
    url = "https://github.com/JJJollyjim/nixos-flask-example/archive/refs/heads/master.tar.gz";
    sha256 = "02j802chsba7q4fmd6mzk59x1gywdpm210x1m4l44c219bxapl4r";
  };
in {
  network.pkgs = pkgs;

  oddharaldxyz = {
    lib,
    name,
    modulesPath,
    ...
  }: {
    imports = [
      (modulesPath + "/virtualisation/openstack-config.nix")
      "${paste}/service.nix"
    ];

    # Morph options
    deployment.targetUser = "root";
    deployment.targetHost = "oddharald.xyz";

    # Environment
    networking.hostName = name;
    system.stateVersion = "22.05";

    # oddharald.xyz
    networking.firewall.allowedTCPPorts = [80 8080];
    services.nginx = {
      enable = true;
      virtualHosts."oddharald.xyz" = {
        root = "/var/oddharald.xyz";
      };
    };

    # paste service
    services.paste.enable = true;
  };
}
1 Like