diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md index 3e63ddced611..30c14de9539d 100644 --- a/nixos/doc/manual/release-notes/rl-2305.section.md +++ b/nixos/doc/manual/release-notes/rl-2305.section.md @@ -34,6 +34,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [fzf](https://github.com/junegunn/fzf), a command line fuzzyfinder. Available as [programs.fzf](#opt-programs.fzf.fuzzyCompletion). +- [gemstash](https://github.com/rubygems/gemstash), a RubyGems.org cache and private gem server. Available as [services.gemstash](#opt-services.gemstash.enable). + - [gmediarender](https://github.com/hzeller/gmrender-resurrect), a simple, headless UPnP/DLNA renderer. Available as [services.gmediarender](options.html#opt-services.gmediarender.enable). - [stevenblack-blocklist](https://github.com/StevenBlack/hosts), A unified hosts file with base extensions for blocking unwanted websites. Available as [networking.stevenblack](options.html#opt-networking.stevenblack.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index de390d801478..dc38c07b03f4 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -439,6 +439,7 @@ ./services/development/blackfire.nix ./services/development/bloop.nix ./services/development/distccd.nix + ./services/development/gemstash.nix ./services/development/hoogle.nix ./services/development/jupyter/default.nix ./services/development/jupyterhub/default.nix diff --git a/nixos/modules/services/development/gemstash.nix b/nixos/modules/services/development/gemstash.nix new file mode 100644 index 000000000000..eb7ccb98bde8 --- /dev/null +++ b/nixos/modules/services/development/gemstash.nix @@ -0,0 +1,103 @@ +{ lib, pkgs, config, ... }: +with lib; + +let + settingsFormat = pkgs.formats.yaml { }; + + # gemstash uses a yaml config where the keys are ruby symbols, + # which means they start with ':'. This would be annoying to use + # on the nix side, so we rewrite plain names instead. + prefixColon = s: listToAttrs (map + (attrName: { + name = ":${attrName}"; + value = + if isAttrs s.${attrName} + then prefixColon s."${attrName}" + else s."${attrName}"; + }) + (attrNames s)); + + # parse the port number out of the tcp://ip:port bind setting string + parseBindPort = bind: strings.toInt (last (strings.splitString ":" bind)); + + cfg = config.services.gemstash; +in +{ + options.services.gemstash = { + enable = mkEnableOption (lib.mdDoc "gemstash service"); + + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to open the firewall for the port in {option}`services.gemstash.bind`. + ''; + }; + + settings = mkOption { + default = {}; + description = lib.mdDoc '' + Configuration for Gemstash. The details can be found at in + [gemstash documentation](https://github.com/rubygems/gemstash/blob/master/man/gemstash-configuration.5.md). + Each key set here is automatically prefixed with ":" to match the gemstash expectations. + ''; + type = types.submodule { + freeformType = settingsFormat.type; + options = { + base_path = mkOption { + type = types.path; + default = "/var/lib/gemstash"; + description = lib.mdDoc "Path to store the gem files and the sqlite database. If left unchanged, the directory will be created."; + }; + bind = mkOption { + type = types.str; + default = "tcp://0.0.0.0:9292"; + description = lib.mdDoc "Host and port combination for the server to listen on."; + }; + db_adapter = mkOption { + type = types.nullOr (types.enum [ "sqlite3" "postgres" "mysql" "mysql2" ]); + default = null; + description = lib.mdDoc "Which database type to use. For choices other than sqlite3, the dbUrl has to be specified as well."; + }; + db_url = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc "The database to connect to when using postgres, mysql, or mysql2."; + }; + }; + }; + }; + }; + + config = + mkIf cfg.enable { + users = { + users.gemstash = { + group = "gemstash"; + isSystemUser = true; + }; + groups.gemstash = { }; + }; + + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ (parseBindPort cfg.settings.bind) ]; + + systemd.services.gemstash = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = mkMerge [ + { + ExecStart = "${pkgs.gemstash}/bin/gemstash start --no-daemonize --config-file ${settingsFormat.generate "gemstash.yaml" (prefixColon cfg.settings)}"; + NoNewPrivileges = true; + User = "gemstash"; + Group = "gemstash"; + PrivateTmp = true; + RestrictSUIDSGID = true; + LockPersonality = true; + } + (mkIf (cfg.settings.base_path == "/var/lib/gemstash") { + StateDirectory = "gemstash"; + }) + ]; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 28ea9272ffb7..91c7f27407c2 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -238,6 +238,7 @@ in { ft2-clone = handleTest ./ft2-clone.nix {}; mimir = handleTest ./mimir.nix {}; garage = handleTest ./garage {}; + gemstash = handleTest ./gemstash.nix {}; gerrit = handleTest ./gerrit.nix {}; geth = handleTest ./geth.nix {}; ghostunnel = handleTest ./ghostunnel.nix {}; diff --git a/nixos/tests/gemstash.nix b/nixos/tests/gemstash.nix new file mode 100644 index 000000000000..bc152e42e92e --- /dev/null +++ b/nixos/tests/gemstash.nix @@ -0,0 +1,51 @@ +{ system ? builtins.currentSystem, config ? { } +, pkgs ? import ../.. { inherit system config; } }: + +with import ../lib/testing-python.nix { inherit system pkgs; }; +with pkgs.lib; + +let common_meta = { maintainers = [ maintainers.viraptor ]; }; +in +{ + gemstash_works = makeTest { + name = "gemstash-works"; + meta = common_meta; + + nodes.machine = { config, pkgs, ... }: { + services.gemstash = { + enable = true; + }; + }; + + # gemstash responds to http requests + testScript = '' + machine.wait_for_unit("gemstash.service") + machine.wait_for_file("/var/lib/gemstash") + machine.wait_for_open_port(9292) + machine.succeed("curl http://localhost:9292") + ''; + }; + + gemstash_custom_port = makeTest { + name = "gemstash-custom-port"; + meta = common_meta; + + nodes.machine = { config, pkgs, ... }: { + services.gemstash = { + enable = true; + openFirewall = true; + settings = { + bind = "tcp://0.0.0.0:12345"; + }; + }; + }; + + # gemstash responds to http requests + testScript = '' + machine.wait_for_unit("gemstash.service") + machine.wait_for_file("/var/lib/gemstash") + machine.wait_for_open_port(12345) + machine.succeed("curl http://localhost:12345") + ''; + }; +} diff --git a/pkgs/development/tools/gemstash/default.nix b/pkgs/development/tools/gemstash/default.nix index 13ab213c15e0..ce8fde604ab5 100644 --- a/pkgs/development/tools/gemstash/default.nix +++ b/pkgs/development/tools/gemstash/default.nix @@ -1,11 +1,14 @@ -{ lib, bundlerApp, bundlerUpdateScript }: +{ lib, bundlerApp, bundlerUpdateScript, nixosTests }: bundlerApp rec { pname = "gemstash"; gemdir = ./.; exes = [ pname ]; - passthru.updateScript = bundlerUpdateScript pname; + passthru = { + updateScript = bundlerUpdateScript pname; + tests = { inherit (nixosTests) gemstash; }; + }; meta = with lib; { description = "A cache for RubyGems.org and a private gem server";