How do I create my own ${pkgs.mypkg} based on a shell script?

How do I create a package around myscript with dependencies resolved?

I think part of my problem is vocabulary. Do I want to create a “package”, a “derivation”, a “flake”, a “module” or what should I really be wanting to do?

It sounds to me like I should build a “package” but Nixpkgs/Create and debug packages - NixOS Wiki is mostly about building from source or binaries. I want to create a package from a working bash script using other nix packages as dependencies.

Similarly, I’d like to create a opensubtitles-download wrapper around OpenSubtitlesDownload.py, which has a handful of runtime dependencies, so that I can:

nix-shell -p opensubtitles-download \
  --run OpenSubtitlesDownload.py some parameters

And now with 2 instances of a similar problem, I though I’d ask.

Details

Systemd/Timers - NixOS Wiki has this example:

systemd.services."hello-world" = {
  script = ''
    set -eu
    ${pkgs.coreutils}/bin/echo "Hello World"
  '';
  serviceConfig = {
    Type = "oneshot";
    User = "root";
  };
};

I really want to run my bash-based myscript that for the sake of argument has mailutils and btrfs-progs as dependencies. And bash and unix-utils for getopt too, apparently.

How do I package myscript so that I can run:

    ${pkgs.myscript}/bin/myscript "Hello World"

Which then implicitly loads the dependencies?

I have succeeded with:

  systemd.services."myscript" = {
    path = with pkgs; [ mailutils util-linux bash btrfs-progs ];
    script = ''
      set -eu
      /path/to/bin/myscript "Hello World"
    '';
    serviceConfig = {
      Type = "oneshot";
      User = "root";
    };
  };

Which smells to me like a hack because I’d prefer my configuration.nix didn’t know about the path to or dependencies for myscript.

I think it should be possible to build a package from this alone:

  • The name myscript
  • /path/to/myscript
  • Dependencies with pkgs; [ mailutils util-linux bash btrfs-progs ];
  • Perhaps an input specifying where to get pkgs from

And then my configuration.nix should now where to get mypkgs from so I can use mypkgs.myscript?

I don’t really know where to start.

Loosely, you can think of “package” and “derivation” as more/less the same thing.

(Whereas in Nix and NixOS, “module” more typically refers to code which makes up a system configuration, and “flake” is more like a package.json or cargo.toml file).

The writeShellApplication builder is probably going to be easiest. (c.f. the nixpkgs manual section

For example, here’s a shell script with some dependencies, myscript.sh:

#!/usr/bin/env bash

fortune | cowsay

The nix expression we want to run this would be:

writeShellApplication {
  name = "myscript";
  runtimeInputs = [ cowsay fortune ];
  text = ./myscript.sh;
}

Putting this in your configuration.nix might involve something like: let myscript = with pkgs; writeShellApplication { ... }; in ....

Or if you end up with a bunch of these scripts, a common convention is to put the package in a separate file like myscript.nix, with contents like { writeShellApplication, cowsay, fortune }: writeShellApplication { ... }, and then use this with let myscript = pkgs.callPackage ./myscript.nix {}; in ....

Thanks, this gets me part of the way (see git diff below)!

It also creates /nix/store/yqfn8857wim69am93575i06r7hd1ckz0-myscript.drv and its output.out.path /nix/store/8lv6lxczmr9a2sz5kmwlfm3qza59xqgl-myscript which looks like it is going in the right direction.

But I want more that that. I still want to be able to something similar to:

$ nix-shell -p myscript

And ideally I’d like to put some artifact in a repo somewhere so that I can use myscript easily on other machines, also ones that are only running nix-the-package-manager.

But that is for another post (I’ll update here when I’ve created it).

$ git diff
diff --git a/configuration.nix b/configuration.nix
index 17fd138..89b67ef 100644
--- a/configuration.nix
+++ b/configuration.nix
@@ -4,6 +4,12 @@
 
 { config, pkgs, ... }:
 
+let myscript = with pkgs; writeShellApplication {
+  name = "myscript";
+  runtimeInputs = [ bash cowsay fortune ];
+  text = /etc/nixos/myscript.sh;
+};
+in
 {
   imports =
     [ # Include the results of the hardware scan.
@@ -292,6 +298,21 @@
     };
   };
 
+  systemd.services."myscript" = {
+    script = "${myscript}/bin/myscript";
+    serviceConfig = {
+      Type = "oneshot";
+      User = "root";
+    };
+  };
+  # systemd.services."myscript" = builtins.trace {
+  #   script = "${myscript}";
+  #   serviceConfig = {
+  #     Type = "oneshot";
+  #     User = "root";
+  #   };
+  # } "foo";
+
   services.btrfs.autoScrub = {
     enable = true;
     interval = "monthly";

IMO, Nix flakes solve this pretty well: with nix flakes, it’s possible to use nix shell someFlake#package.

Without flakes… the most practical thing to do is to write some shell.nix file, and invoke nix-shell with that.

It’s possible to add myscript to nixpkgs such that literally nix-shell -p myscript, you’d define a nixpkgs overly with myscript. c.f. Overlays in the nixpkgs manual: Nixpkgs Manual

Ah, you beat me to it. I have just finished writing How do I run nix-shell with a package from my own new channel?, one question per post and all. And I also found overlays to work.

Yeah, flakes are on my todo list. The learning curve for NixOS is steep even for a professional software developer.

Thank you for your answers.