Casual Nixpkgs contributions

Referring to the discussion here How many people are paid to work on Nix/Nixpkgs? I open this topic to be able do to casual nixpkgs contributions. The idea is to lower the threshold to contribute to Nixpkgs for people like me who are not a dev, do not have a Github account, do not check out nixpkgs via git locally, are merely users, etc. etc etc but still want to share something so now and then.

Here is my first casual contribution. A sponsored PR (=someone who wants to shape it into an actual PR) would be appreciated.

{ stdenv, fetchFromGitHub, python3 }:

stdenv.mkDerivation rec {
  pname = "ff2mpv-host";
  version = "3.6";

  src = fetchFromGitHub {
    owner = "woodruffw";
    repo = "ff2mpv";
    rev = "v${version}";
    sha256 = "0dpqd4vis4c0pvdfpkgflc6ccdm98jils3jp0ncz1nvam4swsr1x";
  };

  buildInputs = [ python3 ];

  installPhase = ''
     runHook preInstall
     install -D ff2mpv.py $out/share/ff2mpv.py
     substitute ff2mpv.json $out/share/ff2mpv.json \
       --replace /home/william/scripts/ff2mpv $out/share/ff2mpv.py

     nativeMessagingPaths=(
       /lib/mozilla/native-messaging-hosts
    #   /etc/opt/chrome/native-messaging-hosts
    #   /etc/chromium/native-messaging-hosts
    #   /etc/vivaldi/native-messaging-hosts
     )

     for manifestDir in "''${nativeMessagingPaths[@]}"; do
       install -d $out$manifestDir
       ln -s $out/share/ff2mpv.json $out$manifestDir/
     done

    runHook postInstall
  '';

  meta = with stdenv.lib; {
    description = "Host app for the WebExtension ff2mpv";
    homepage = "https://github.com/woodruffw/ff2mpv";
    license = licenses.mit;
  };
}

3 Likes

You don’t have to set the maintainers field, considering the circumstances. Though I wonder: what you mean by “sponsor”?

Do not override phases, without runHook before and after it. Read: https://github.com/jtojnar/nixpkgs-hammering/blob/193d2108495b6c53c591f2f99de981153f218003/explanations/explicit-phases.md

Usually web extension’s json files are installed in several locations, Consider for instance:

You can use only the Firefox path if you wish.

Use pname = "ff2mpv and the name attribute will be derived automatically.

Does the executable eventually gets wrapped appropriately? For this simple derivation, you can use dontBuild = true and patchShebangs.

Though I wonder: what you mean by “sponsor”?

Presumably Sponsor (legislative) - Wikipedia

1 Like

Thanks for the feedback, I will process it and update the original message.
With sponsor I mean something similar as ‘non maintainer upload’ in Debian world. Someone who shapes it into a merge-able PR on behalf of the casual contributor (and can take credit if that matters)

Updated:

  • changed name+$version → pname
  • runHooks added
  • kept buildInputs python3 otherwise patchshebangs doesn’t do anything
  • kept it Firefox only, because the author only markets the extension for Firefox, and I did not test it in other browsers
  • made it look like the passff-host derivation
3 Likes

thanks both of you @kvtb and @doronbehar but wow, this looks awfully close to a Pull Request against nixpkgs minus the actual diff and all the goodies github brings.

Especially with hacktoberfest currently going on, these kind of additions make good first contributions.
@kvtb you should really think of filing a PR for this!

Apart from that, you can always start your own Nix User Repository ( NUR ) if you just want to give access to the derivations you’ve wrote. there is even a search for this at nur.nix-community.org which contains contributions of packages which may not fulfill the quality standards of nixpkgs.

6 Likes

Looking closer, the license for this software is not the MIT license, but the MIT license with additional restrictions (https://github.com/woodruffw/ff2mpv/blob/ef1ad015871fc2b876e8c89b7a31ccb62c583cd6/LICENSE).

I know I offered to sponsor (in the sense of: convert to a PR on your behalf) your contributions earlier, but I’m not sure I find this particular one suitable, so I think I’ll pass in this particular case. Sorry!

1 Like

sadness, the license was updated after I wrote the first version of the derivation a few months a go.

Anyway, here’s another one, for SeaweedFS a distributed file system
Ideally this also comes with NixOS service definitions and options and such but I’ll leave that to those who know what they are doing. update: service definition here Casual Nixpkgs contributions - #11 by kvtb

So for nixpkgs, if someone sees the added value of SeaweedFS as new package: [code removed, PR created by raboof]

1 Like

Looks interesting - though if you see the added value that is good enough for me :wink: . Converted to PR at:
https://github.com/NixOS/nixpkgs/pull/102939

5 Likes

I notice it has been merged today, thanks for making this sponsor-based PR approach work, @raboof !

3 Likes

@kvtb This is a nice idea. Though, I fear the odds of effort dynamics prevent it from spreading.

Maybe a slight variant could work, though: sometimes somebody does a PR (that somebody can be me) but needs a little more tutoring to get it into shape (as far as even allowing direct push onto the PR branch).

Currently, I need tutoring on:

https://github.com/NixOS/nixpkgs/pull/110827

I left it in “draft”, but that’s not very expressive and concise.

Hi all,

apparently today I’m here exactly 1 year.
Let’s celebrate. I’ve managed to create a NixOS service for seaweedfs, the package of which the inclusion was done by @raboof

I’d say it’s 99% ready, but since I’ve never created a NixOS service before, I’m looking forward to feedback. I will update this post accordingly

{ config, lib, pkgs, ... }:
with lib;
let

  seaweedfs = pkgs.seaweedfs;

  user = "seaweedfs";
  group = "seaweedfs";
  cfg = config.services.seaweedfs;
  enabledVolumes = filterAttrs (_: v: v.enable) cfg.volumes;
  anyEnabled = cfg.master.enable || cfg.filer.enable || cfg.webdav.enable
    || enabledVolumes != { };

  mkCmdLineArguments = mapAttrsToList (option: value:
    if isBool value then
      "-${option}"
    else
      "-${option}=${
        if isList value then
          builtins.concatStringsSep "," value
        else
          toString value
      }");

  mkWeedExec = subcmd: options:
    (toString ([ "${seaweedfs}/bin/weed" subcmd ] ++ mkCmdLineArguments
      ((removeAttrs options [ "enable" "extraConfig" ])
        // (if options ? "extraConfig" then
          (removeAttrs options.extraConfig (builtins.attrNames options))
        else
          { }))));

  mkExtraConfigOption = subcmd:
    mkOption {
      default = { };
      type = with types; attrs;
      description = ''
        Additional configuration, see output of 'weed ${subcmd} --help' for attributes.
        Do not define settings for flags for which explicit configuration options exist, these will be ignored.
      '';
    };

  mkPortOption = defaultPort:
    mkOption {
      default = defaultPort;
      type = with types; uniq port;
      description = "Http listen port";
    };

  mkServerListOption = subcmd:
    mkOption {
      default = [ "localhost:${toString cfg.${subcmd}.port}" ];
      type = with types; listOf str;
      description = "List of ${subcmd} servers (host/ip:port)";
    };

  mkVolumeService = id: options:
    nameValuePair "seaweedfs-volume-${id}" {
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ]
        ++ optional cfg.master.enable "seaweedfs-master.service";
      description = "SeaweedFS volume - ${id}";
      unitConfig.ConditionPathIsDirectory = options.dir;
      serviceConfig = {
        User = user;
        Group = group;
        ExecStart = mkWeedExec "volume" options;
        KillSignal = "SIGTERM";
      };
    };

  mkVolumeOptions = { id, ... }: {
    options = {
      enable = mkEnableOption "SeaweedFS volume server";
      port = mkPortOption 8080;
      mserver = mkServerListOption "master";
      extraConfig = mkExtraConfigOption "volume";

      dir = mkOption {
        default = [ "/tmp" ];
        type = with types; listOf path;
        description = ''
          One or more directories to store data files.
          These must exist before the volume server service is started,
          and must be owned by ${user}:${group}.'';
      };
    };
  };

in {

  ###### interface

  options = {
    services.seaweedfs = {

      master = {
        enable = mkEnableOption "SeaweedFS master server";
        port = mkPortOption 9333;
        extraConfig = mkExtraConfigOption "master";

      };

      filer = {
        enable = mkEnableOption "SeaweedFS file server";
        port = mkPortOption 8888;
        master = mkServerListOption "master";
        extraConfig = mkExtraConfigOption "filer";

      };

      volumes = mkOption {
        default = { };
        type = with types; attrsOf (submodule mkVolumeOptions);
      };

      webdav = {
        enable = mkEnableOption "SeaweedFS webdav server";
        port = mkPortOption 7333;
        filer = mkServerListOption "filer";
        extraConfig = mkExtraConfigOption "webdav";

      };
    };
  };

  ###### implementation

  config = mkIf anyEnabled {
    environment.systemPackages = [ seaweedfs ];
    users.users.${user} = {
      description = "SeaweedFS user";
      isSystemUser = true;
      group = group;
    };
    users.groups.${group} = { };
    systemd.services = (mapAttrs' mkVolumeService enabledVolumes) // {
      seaweedfs-master = mkIf cfg.master.enable {
        wantedBy = [ "multi-user.target" ];
        after = [ "network.target" ];
        description = "SeaweedFS master";
        serviceConfig = rec {
          User = user;
          Group = group;
          StateDirectory = "seaweedfs/master";
          ExecStart = (mkWeedExec "master" cfg.master)
            + " -mdir=/var/lib/${StateDirectory}";
          KillSignal = "SIGTERM";
        };
      };

      seaweedfs-filer = mkIf cfg.filer.enable {
        wantedBy = [ "multi-user.target" ];
        after = [ "network.target" ]
          ++ optional cfg.master.enable "seaweedfs-master.service";
        description = "SeaweedFS filer";
        serviceConfig = rec {
          User = user;
          Group = group;
          WorkingDirectory = "/var/lib/${StateDirectory}";
          StateDirectory = "seaweedfs/filer";
          ExecStart = mkWeedExec "filer" cfg.filer;
          KillSignal = "SIGTERM";
        };
      };

      seaweedfs-webdav = mkIf cfg.webdav.enable {
        wantedBy = [ "multi-user.target" ];
        after = [ "network.target" ]
          ++ optional cfg.filer.enable "seaweedfs-filer.service";
        description = "SeaweedFS webdav";
        serviceConfig = {
          User = user;
          Group = group;
          ExecStart = mkWeedExec "webdav" cfg.webdav;
          KillSignal = "SIGTERM";
        };
      };
    };
  };
}

Here’s an example how to use it:

...
  services.seaweedfs.master.enable = true;
  services.seaweedfs.master.extraConfig.resumeState = true;
  services.seaweedfs.filer.enable = true;
  services.seaweedfs.webdav.enable = true;
  services.seaweedfs.webdav.extraConfig = {
    disk = "hdd";
    cacheCapacityMB = 100;
  };
  services.seaweedfs.volumes = {
    default = {
      enable = true;
      dir = [ "/seaweed-volume-sda" "/mnt/backup/seaweedfs/volume-sdb" ];
      #  mserver = [ "localhost:${config.services.seaweedfs.master.port}" ];
    };
  };

  networking.firewall.allowedTCPPorts = [
    config.services.seaweedfs.master.port # HTTP
    19333 # gRPC
    config.services.seaweedfs.volumes.default.port
    18080
    config.services.seaweedfs.filer.port
    18888
  ];
...
1 Like

:tada:

Cool! I’m afraid I don’t have any experience with NixOS services, so I’m afraid I can’t really be of help here.

While building I see:

warning: either libtermcap or libreadline is missing - building without readline

Is that OK? Submitted as gnucap: init at 20210107 by raboof · Pull Request #117039 · NixOS/nixpkgs · GitHub

Hi!

I’m curious, why don’t you just create a dummy GitHub account so we can review and eventually merge your contributions in a easier way ?

IMHO, reviewing PRs on discourse is not really the most appropriate place, and it gives more work to other contributors.

5 Likes

A guess but maybe the whole idea was to avoid the review part. Guessing this since review can take a lot of energy and time. Now, if someone is actually looking for that package, they could take the declaration and use it, and if they are really into it, maybe even submit a MR.

Thus, thanks for sharing the pkg definition @kvtb !

I’ve packaged couple of small python packages not yet in nixpkgs, and this far mainly for some quick and dirty trials, and I’ve been thinking, if I should share them somehow. E.g. on a separate “hope you find this useful repo”.

But a review is happening, it’s just using an inferior tool. The only reason for doing it here is a better “eyeballs to PR” ratio, but that’s hardly fair either since there is nothing that makes what has been posted here more special than your “regular” PR.

I would strongly advise OP to just create a GH account and go through the regular channels.

4 Likes

I don’t think you should be guessing at the intentions of OP here. The only thing you know is that they do not have a github account, that they wanted to contribute a derivation to the community, and that they’ve blessed it for someone else to include in nixpkgs.

That should be good enough to warrant a thanks.

If you don’t want to see it, or deal with it, that’s fine, but I suggest you mute the thread instead of making a in my opinion quite rude accusation on the motive behind it.

I don’t know why OP chooses to contribute here rather than on github, but I can come up with multiple reasons that are perfectly valid.

I don’t think OP should expect someone to come and file PR’s for them, but they’re not demanding anything. If no one steps up, that’s fine too, this is still a contribution to the commons, which might prove useful to someone in the future.

The thread is hurting no one

4 Likes

I don’t think you should be guessing at the intentions of OP here. That should be good enough to warrant a thanks.

I don’t assume anything and contributions are generally a good thing - OP for sure deserves thanks for their contributions.

If you don’t want to see it, or deal with it, that’s fine, but I suggest you mute the thread instead of making a in my opinion quite rude accusation on the motive behind it.

I absolutely did not accuse anyone of anything.

My point is that if OP (or anyone else for that matter) wants to make contributions of this kind, the best way to do so is by following our existing procedures. It ensures a proper review flow, it allows us to make use of the tools we already have that are meant for this purpose and generally imposes a lower load on the people who will be acting on this (ie reviewers).

Of course, if OP and others want to continue and there are people in this thread who are willing to entertain that, sure, by all means. I’m not in a position to tell them not to. But keep in mind, that doing it here might be easy for them but it’s harder for reviewers, so if they want to make the process as smooth as possible for everyone involved, the proper way to do so is (currently) opening a PR on GH.

4 Likes

I was simply looking for a low-threshold way to contribute some of my work. I’m not a developer (the fact that I only respond three months after this discussion says enough). I also run NixOS Stable 23.11 and I don’t use a local checkout of nixpkgs. I don’t even know how to contribute a full patch without a local checkout. And I only started using flakes a few months ago, because it was not mentioned in the 23.11 manual, just to indicate how non-advanced I am as a ‘developer’

Anway, the derivations posted here are very small. A real NixOS developer could (if they want) get it merged with low effort, as raboof has shown with seaweedfs and gnucap. But, I’m not expecting anything. Hopefully the derivations posted here are seen as unconditional gifts, not as as a nuisance.

4 Likes