summaryrefslogtreecommitdiffstats
path: root/krebs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2016-01-14 16:39:34 +0100
committertv <tv@krebsco.de>2016-01-14 16:39:34 +0100
commit2ae2bdc586177f674a987e354940765ad86cbaa2 (patch)
tree0ba9baf9ff358a9eb2bb1111090d513b93268df7 /krebs
parent780ddec4ab268fbfd43ea9bf511ed4b3b0d35783 (diff)
parent49b6fd9c87678893ed47794b116660700994b1bc (diff)
Merge remote-tracking branch 'gum/master'
Diffstat (limited to 'krebs')
-rw-r--r--krebs/3modules/Reaktor.nix27
-rw-r--r--krebs/3modules/buildbot/master.nix385
-rw-r--r--krebs/3modules/buildbot/slave.nix186
-rw-r--r--krebs/3modules/default.nix2
-rw-r--r--krebs/3modules/makefu/default.nix11
-rw-r--r--krebs/3modules/shared/default.nix1
-rw-r--r--krebs/5pkgs/Reaktor/plugins.nix120
-rw-r--r--krebs/5pkgs/Reaktor/scripts/random-emoji.sh6
-rw-r--r--krebs/5pkgs/Reaktor/scripts/random-issue.sh20
-rw-r--r--krebs/5pkgs/Reaktor/scripts/sed-plugin.py53
-rw-r--r--krebs/5pkgs/Reaktor/scripts/shack-correct.sh6
-rw-r--r--krebs/5pkgs/cacpanel/default.nix4
-rw-r--r--krebs/5pkgs/default.nix6
-rw-r--r--krebs/5pkgs/snapraid/default.nix33
-rw-r--r--krebs/5pkgs/test/infest-cac-centos7/default.nix39
-rwxr-xr-xkrebs/5pkgs/test/infest-cac-centos7/notes150
-rw-r--r--krebs/Zhosts/bobby11
-rw-r--r--krebs/Zhosts/kebsco11
-rw-r--r--krebs/default.nix20
19 files changed, 1046 insertions, 45 deletions
diff --git a/krebs/3modules/Reaktor.nix b/krebs/3modules/Reaktor.nix
index 0fca52203..92400139c 100644
--- a/krebs/3modules/Reaktor.nix
+++ b/krebs/3modules/Reaktor.nix
@@ -9,6 +9,7 @@ let
${cfg.overrideConfig}
'' else ""}
## Extra Config
+ ${concatStringsSep "\n" (map (plug: plug.config) cfg.plugins)}
${cfg.extraConfig}
'';
cfg = config.krebs.Reaktor;
@@ -35,7 +36,6 @@ let
'';
};
-
overrideConfig = mkOption {
default = null;
type = types.nullOr types.str;
@@ -44,6 +44,9 @@ let
Reaktor default cfg can be retrieved via `reaktor get-config`
'';
};
+ plugins = mkOption {
+ default = [pkgs.ReaktorPlugins.nixos-version];
+ };
extraConfig = mkOption {
default = "";
type = types.string;
@@ -51,6 +54,14 @@ let
configuration appended to the default or overridden configuration
'';
};
+
+ workdir = mkOption {
+ default = "/var/lib/Reaktor";
+ type = types.str;
+ description = ''
+ Reaktor working directory
+ '';
+ };
extraEnviron = mkOption {
default = {};
type = types.attrsOf types.str;
@@ -59,12 +70,17 @@ let
REAKTOR_HOST
REAKTOR_PORT
REAKTOR_STATEDIR
- REAKTOR_CHANNELS
debug and nickname can be set separately via the Reaktor api
'';
};
-
+ channels = mkOption {
+ default = [ "#krebs" ];
+ type = types.listOf types.str;
+ description = ''
+ Channels the Reaktor should connect to at startup.
+ '';
+ };
debug = mkOption {
default = false;
description = ''
@@ -79,7 +95,7 @@ let
name = "Reaktor";
uid = genid name;
description = "Reaktor user";
- home = "/var/lib/Reaktor";
+ home = cfg.workdir;
createHome = true;
};
@@ -101,6 +117,9 @@ let
GIT_SSL_CAINFO = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
REAKTOR_NICKNAME = cfg.nickname;
REAKTOR_DEBUG = (if cfg.debug then "True" else "False");
+ REAKTOR_CHANNELS = lib.concatStringsSep "," cfg.channels;
+ state_dir = cfg.workdir;
+
} // cfg.extraEnviron;
serviceConfig= {
ExecStartPre = pkgs.writeScript "Reaktor-init" ''
diff --git a/krebs/3modules/buildbot/master.nix b/krebs/3modules/buildbot/master.nix
new file mode 100644
index 000000000..74385a433
--- /dev/null
+++ b/krebs/3modules/buildbot/master.nix
@@ -0,0 +1,385 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+ buildbot = pkgs.buildbot;
+ buildbot-master-config = pkgs.writeText "buildbot-master.cfg" ''
+ # -*- python -*-
+ from buildbot.plugins import *
+ import re
+ import json
+ c = BuildmasterConfig = {}
+
+ c['slaves'] = []
+ slaves = json.loads('${builtins.toJSON cfg.slaves}')
+ slavenames = [ s for s in slaves ]
+ for k,v in slaves.items():
+ c['slaves'].append(buildslave.BuildSlave(k, v))
+
+ # TODO: configure protocols?
+ c['protocols'] = {'pb': {'port': 9989}}
+
+ ####### Build Inputs
+ c['change_source'] = cs = []
+
+ ${ concatStringsSep "\n"
+ (mapAttrsToList (n: v: ''
+ #### Change_Source: Begin of ${n}
+ ${v}
+ #### Change_Source: End of ${n}
+ '') cfg.change_source )}
+
+ ####### Build Scheduler
+ c['schedulers'] = sched = []
+
+ ${ concatStringsSep "\n"
+ (mapAttrsToList (n: v: ''
+ #### Schedulers: Begin of ${n}
+ ${v}
+ #### Schedulers: End of ${n}
+ '') cfg.scheduler )}
+
+ ###### Builder
+ c['builders'] = bu = []
+
+ # Builder Pre: Begin
+ ${cfg.builder_pre}
+ # Builder Pre: End
+
+ ${ concatStringsSep "\n"
+ (mapAttrsToList (n: v: ''
+ #### Builder: Begin of ${n}
+ ${v}
+ #### Builder: End of ${n}
+ '') cfg.builder )}
+
+
+ ####### Status
+ c['status'] = st = []
+
+ # If you want to configure this url, override with extraConfig
+ c['buildbotURL'] = "http://${config.networking.hostName}:${toString cfg.web.port}/"
+
+ ${optionalString (cfg.web.enable) ''
+ from buildbot.status import html
+ from buildbot.status.web import authz, auth
+ authz_cfg=authz.Authz(
+ auth=auth.BasicAuth([ ("${cfg.web.username}","${cfg.web.password}") ]),
+ # TODO: configure harder
+ gracefulShutdown = False,
+ forceBuild = 'auth',
+ forceAllBuilds = 'auth',
+ pingBuilder = False,
+ stopBuild = 'auth',
+ stopAllBuilds = 'auth',
+ cancelPendingBuild = 'auth'
+ )
+ # TODO: configure krebs.nginx
+ st.append(html.WebStatus(http_port=${toString cfg.web.port}, authz=authz_cfg))
+ ''}
+
+ ${optionalString (cfg.irc.enable) ''
+ from buildbot.status import words
+ irc = words.IRC("${cfg.irc.server}", "${cfg.irc.nick}",
+ channels=${builtins.toJSON cfg.irc.channels},
+ notify_events={
+ 'success': 1,
+ 'failure': 1,
+ 'exception': 1,
+ 'successToFailure': 1,
+ 'failureToSuccess': 1,
+ }${optionalString cfg.irc.allowForce ",allowForce=True"})
+ c['status'].append(irc)
+ ''}
+
+ ${ concatStringsSep "\n"
+ (mapAttrsToList (n: v: ''
+ #### Status: Begin of ${n}
+ ${v}
+ #### Status: End of ${n}
+ '') cfg.status )}
+
+ ####### PROJECT IDENTITY
+ c['title'] = "${cfg.title}"
+ c['titleURL'] = "http://krebsco.de"
+
+
+ ####### DB URL
+ # TODO: configure
+ c['db'] = {
+ 'db_url' : "sqlite:///state.sqlite",
+ }
+ ${cfg.extraConfig}
+ '';
+
+ cfg = config.krebs.buildbot.master;
+
+ api = {
+ enable = mkEnableOption "Buildbot Master";
+ title = mkOption {
+ default = "Buildbot CI";
+ type = types.str;
+ description = ''
+ Title of the Buildbot Installation
+ '';
+ };
+ workDir = mkOption {
+ default = "/var/lib/buildbot/master";
+ type = types.str;
+ description = ''
+ Path to build bot master directory.
+ Will be created on startup.
+ '';
+ };
+
+ secrets = mkOption {
+ default = [];
+ type = types.listOf types.str;
+ example = [ "cac.json" ];
+ description = ''
+ List of all the secrets in <secrets> which should be copied into the
+ buildbot master directory.
+ '';
+ };
+
+ slaves = mkOption {
+ default = {};
+ type = types.attrsOf types.str;
+ description = ''
+ Attrset of slavenames with their passwords
+ slavename = slavepassword
+ '';
+ };
+
+ change_source = mkOption {
+ default = {};
+ type = types.attrsOf types.str;
+ example = {
+ stockholm = ''
+ cs.append(changes.GitPoller(
+ 'http://cgit.gum/stockholm',
+ workdir='stockholm-poller', branch='master',
+ project='stockholm',
+ pollinterval=120))
+ '';
+ };
+ description = ''
+ Attrset of all the change_sources which should be configured.
+ It will be directly included into the master configuration.
+
+ At the end an change object should be appended to <literal>cs</literal>
+ '';
+ };
+
+ scheduler = mkOption {
+ default = {};
+ type = types.attrsOf types.str;
+ example = {
+ force-scheduler = ''
+ sched.append(schedulers.ForceScheduler(
+ name="force",
+ builderNames=["full-tests"]))
+ '';
+ };
+ description = ''
+ Attrset of all the schedulers which should be configured.
+ It will be directly included into the master configuration.
+
+ At the end an change object should be appended to <literal>sched</literal>
+ '';
+ };
+
+ builder_pre = mkOption {
+ default = "";
+ type = types.lines;
+ example = ''
+ grab_repo = steps.Git(repourl=stockholm_repo, mode='incremental')
+ '';
+ description = ''
+ some code before the builders are being assembled.
+ can be used to define functions used by multiple builders
+ '';
+ };
+
+ builder = mkOption {
+ default = {};
+ type = types.attrsOf types.str;
+ example = {
+ fast-test = ''
+ '';
+ };
+ description = ''
+ Attrset of all the builder which should be configured.
+ It will be directly included into the master configuration.
+
+ At the end an change object should be appended to <literal>bu</literal>
+ '';
+ };
+
+ status = mkOption {
+ default = {};
+ type = types.attrsOf types.str;
+ description = ''
+ Attrset of all the extra status which should be configured.
+ It will be directly included into the master configuration.
+
+ At the end an change object should be appended to <literal>st</literal>
+
+ Right now IRC and Web status can be configured by setting
+ <literal>buildbot.master.irc.enable</literal> and
+ <literal>buildbot.master.web.enable</literal>
+ '';
+ };
+
+ # Configurable Stati
+ web = mkOption {
+ default = {};
+ type = types.submodule ({ config2, ... }: {
+ options = {
+ enable = mkEnableOption "Buildbot Master Web Status";
+ username = mkOption {
+ default = "krebs";
+ type = types.str;
+ description = ''
+ username for web authentication
+ '';
+ };
+ hostname = mkOption {
+ default = config.networking.hostName;
+ type = types.str;
+ description = ''
+ web interface Hostname
+ '';
+ };
+ password = mkOption {
+ default = "bob";
+ type = types.str;
+ description = ''
+ password for web authentication
+ '';
+ };
+ port = mkOption {
+ default = 8010;
+ type = types.int;
+ description = ''
+ port for buildbot web status
+ '';
+ };
+ };
+ });
+ };
+
+ irc = mkOption {
+ default = {};
+ type = types.submodule ({ config, ... }: {
+ options = {
+ enable = mkEnableOption "Buildbot Master IRC Status";
+ channels = mkOption {
+ default = [ "nix-buildbot-meetup" ];
+ type = with types; listOf str;
+ description = ''
+ irc channels the bot should connect to
+ '';
+ };
+ allowForce = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Determines if builds can be forced via IRC
+ '';
+ };
+ nick = mkOption {
+ default = "nix-buildbot";
+ type = types.str;
+ description = ''
+ nickname for IRC
+ '';
+ };
+ server = mkOption {
+ default = "irc.freenode.net";
+ type = types.str;
+ description = ''
+ Buildbot Status IRC Server to connect to
+ '';
+ };
+ };
+ });
+ };
+
+ extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ extra config appended to the generated master.cfg
+ '';
+ };
+ };
+
+ imp = {
+
+ users.extraUsers.buildbotMaster = {
+ uid = genid "buildbotMaster";
+ description = "Buildbot Master";
+ home = cfg.workDir;
+ createHome = false;
+ };
+
+ users.extraGroups.buildbotMaster = {
+ gid = 672626386;
+ };
+
+ systemd.services.buildbotMaster = {
+ description = "Buildbot Master";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ # TODO: add extra dependencies to master like svn and cvs
+ path = [ pkgs.git ];
+ environment = {
+ SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+ };
+ serviceConfig = let
+ workdir="${lib.shell.escape cfg.workDir}";
+ secretsdir="${lib.shell.escape (toString <secrets>)}";
+ in {
+ PermissionsStartOnly = true;
+ Type = "forking";
+ PIDFile = "${workdir}/twistd.pid";
+ # TODO: maybe also prepare buildbot.tac?
+ ExecStartPre = pkgs.writeScript "buildbot-master-init" ''
+ #!/bin/sh
+ set -efux
+ if [ ! -e ${workdir} ];then
+ mkdir -p ${workdir}
+ ${buildbot}/bin/buildbot create-master -r -l 10 -f ${workdir}
+ fi
+ # always override the master.cfg
+ cp ${buildbot-master-config} ${workdir}/master.cfg
+
+ # copy secrets
+ ${ concatMapStringsSep "\n"
+ (f: "cp ${secretsdir}/${f} ${workdir}/${f}" ) cfg.secrets }
+ # sanity
+ ${buildbot}/bin/buildbot checkconfig ${workdir}
+
+ # TODO: maybe upgrade? not sure about this
+ # normally we should write buildbot.tac by our own
+ # ${buildbot}/bin/buildbot upgrade-master ${workdir}
+
+ chmod 700 -R ${workdir}
+ chown buildbotMaster:buildbotMaster -R ${workdir}
+ '';
+ ExecStart = "${buildbot}/bin/buildbot start ${workdir}";
+ ExecStop = "${buildbot}/bin/buildbot stop ${workdir}";
+ ExecReload = "${buildbot}/bin/buildbot reconfig ${workdir}";
+ PrivateTmp = "true";
+ User = "buildbotMaster";
+ Restart = "always";
+ RestartSec = "10";
+ };
+ };
+ };
+in
+{
+ options.krebs.buildbot.master = api;
+ config = mkIf cfg.enable imp;
+}
diff --git a/krebs/3modules/buildbot/slave.nix b/krebs/3modules/buildbot/slave.nix
new file mode 100644
index 000000000..0e7796d8a
--- /dev/null
+++ b/krebs/3modules/buildbot/slave.nix
@@ -0,0 +1,186 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+ buildbot-slave-init = pkgs.writeText "buildbot-slave.tac" ''
+ import os
+
+ from buildslave.bot import BuildSlave
+ from twisted.application import service
+
+ basedir = '${cfg.workDir}'
+ rotateLength = 10000000
+ maxRotatedFiles = 10
+
+ application = service.Application('buildslave')
+
+ from twisted.python.logfile import LogFile
+ from twisted.python.log import ILogObserver, FileLogObserver
+ logfile = LogFile.fromFullPath(os.path.join(basedir, "twistd.log"), rotateLength=rotateLength,
+ maxRotatedFiles=maxRotatedFiles)
+ application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
+
+ buildmaster_host = '${cfg.masterhost}'
+ # TODO: masterport?
+ port = 9989
+ slavename = '${cfg.username}'
+ passwd = '${cfg.password}'
+ keepalive = 600
+ usepty = 0
+ umask = None
+ maxdelay = 300
+ allow_shutdown = None
+
+ ${cfg.extraConfig}
+
+ s = BuildSlave(buildmaster_host, port, slavename, passwd, basedir,
+ keepalive, usepty, umask=umask, maxdelay=maxdelay,
+ allow_shutdown=allow_shutdown)
+ s.setServiceParent(application)
+ '';
+ default-packages = [ pkgs.git pkgs.bash ];
+ cfg = config.krebs.buildbot.slave;
+
+ api = {
+ enable = mkEnableOption "Buildbot Slave";
+
+ workDir = mkOption {
+ default = "/var/lib/buildbot/slave";
+ type = types.str;
+ description = ''
+ Path to build bot slave directory.
+ Will be created on startup.
+ '';
+ };
+
+ masterhost = mkOption {
+ default = "localhost";
+ type = types.str;
+ description = ''
+ Hostname/IP of the buildbot master
+ '';
+ };
+
+ username = mkOption {
+ type = types.str;
+ description = ''
+ slavename used to authenticate with master
+ '';
+ };
+
+ password = mkOption {
+ type = types.str;
+ description = ''
+ slave password used to authenticate with master
+ '';
+ };
+
+ contact = mkOption {
+ default = "nix slave <buildslave@${config.networking.hostName}>";
+ type = types.str;
+ description = ''
+ contact to be announced by buildslave
+ '';
+ };
+
+ description = mkOption {
+ default = "Nix Generated BuildSlave";
+ type = types.str;
+ description = ''
+ description for hostto be announced by buildslave
+ '';
+ };
+
+ packages = mkOption {
+ default = [ pkgs.git ];
+ type = with types; listOf package;
+ description = ''
+ packages which should be in path for buildslave
+ '';
+ };
+
+ extraEnviron = mkOption {
+ default = {};
+ example = {
+ NIX_PATH = "nixpkgs=/path/to/my/nixpkgs";
+ };
+ type = types.attrsOf types.str;
+ description = ''
+ extra environment variables to be provided to the buildslave service
+ if you need nixpkgs, e.g. for running nix-shell you can set NIX_PATH here.
+ '';
+ };
+
+ extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ example = ''
+ port = 443
+ keepalive = 600
+ '';
+ description = ''
+ extra config evaluated before calling BuildSlave init in .tac file
+ '';
+ };
+ };
+
+ imp = {
+
+ users.extraUsers.buildbotSlave = {
+ uid = genid "buildbotSlave";
+ description = "Buildbot Slave";
+ home = cfg.workDir;
+ createHome = false;
+ };
+
+ users.extraGroups.buildbotSlave = {
+ gid = 1408105834;
+ };
+
+ systemd.services."buildbotSlave-${cfg.username}-${cfg.masterhost}" = {
+ description = "Buildbot Slave for ${cfg.username}@${cfg.masterhost}";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ path = default-packages ++ cfg.packages;
+
+ environment = {
+ SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+ NIX_REMOTE="daemon";
+ } // cfg.extraEnviron;
+
+ serviceConfig = let
+ workdir = "${lib.shell.escape cfg.workDir}";
+ contact = "${lib.shell.escape cfg.contact}";
+ description = "${lib.shell.escape cfg.description}";
+ buildbot = pkgs.buildbot-slave;
+ # TODO:make this
+ in {
+ PermissionsStartOnly = true;
+ Type = "forking";
+ PIDFile = "${workdir}/twistd.pid";
+ # TODO: maybe also prepare buildbot.tac?
+ ExecStartPre = pkgs.writeScript "buildbot-master-init" ''
+ #!/bin/sh
+ set -efux
+ mkdir -p ${workdir}/info
+ cp ${buildbot-slave-init} ${workdir}/buildbot.tac
+ echo ${contact} > ${workdir}/info/admin
+ echo ${description} > ${workdir}/info/host
+
+ chown buildbotSlave:buildbotSlave -R ${workdir}
+ chmod 700 -R ${workdir}
+ '';
+ ExecStart = "${buildbot}/bin/buildslave start ${workdir}";
+ ExecStop = "${buildbot}/bin/buildslave stop ${workdir}";
+ PrivateTmp = "true";
+ User = "buildbotSlave";
+ Restart = "always";
+ RestartSec = "10";
+ };
+ };
+ };
+in
+{
+ options.krebs.buildbot.slave = api;
+ config = mkIf cfg.enable imp;
+}
diff --git a/krebs/3modules/default.nix b/krebs/3modules/default.nix
index deccecc48..ba1f425d9 100644
--- a/krebs/3modules/default.nix
+++ b/krebs/3modules/default.nix
@@ -10,6 +10,8 @@ let
./backup.nix
./bepasty-server.nix
./build.nix
+ ./buildbot/master.nix
+ ./buildbot/slave.nix
./current.nix
./exim-retiolum.nix
./exim-smarthost.nix
diff --git a/krebs/3modules/makefu/default.nix b/krebs/3modules/makefu/default.nix
index 1970a0777..31516d591 100644
--- a/krebs/3modules/makefu/default.nix
+++ b/krebs/3modules/makefu/default.nix
@@ -83,6 +83,9 @@ with lib;
'';
};
};
+ ssh.privkey.path = <secrets/ssh_host_ed25519_key>;
+ ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHDM0E608d/6rGzXqGbNSuMb2RlCojCJSiiz6QcPOC2G root@pornocauster";
+
};
vbob = {
@@ -108,6 +111,8 @@ with lib;
'';
};
};
+ ssh.privkey.path = <secrets/ssh_host_ed25519_key>;
+ ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICPLTMl+thSq77cjYa2XF7lz5fA7JMftrLo8Dy/OBXSg root@nixos";
};
flap = rec {
cores = 1;
@@ -238,6 +243,8 @@ with lib;
'';
};
};
+ ssh.privkey.path = <secrets/ssh_host_ed25519_key>;
+ ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH4Tjx9qK6uWtxT1HCpeC0XvDZKO/kaPygyKatpAqU6I root@wry";
};
filepimp = rec {
cores = 1;
@@ -287,6 +294,8 @@ with lib;
'';
};
};
+ ssh.privkey.path = <secrets/ssh_host_ed25519_key>;
+ ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIujMZ3ZFxKpWeB/cjfKfYRr77+VRZk0Eik+92t03NoA root@servarch";
};
gum = rec {
cores = 1;
@@ -327,6 +336,8 @@ with lib;
'';
};
};
+ ssh.privkey.path = <secrets/ssh_host_ed25519_key>;
+ ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIcxWFEPzke/Sdd9qNX6rSJgXal8NmINYajpFCxXfYdj root@gum";
};
};
users = addNames rec {
diff --git a/krebs/3modules/shared/default.nix b/krebs/3modules/shared/default.nix
index b332676c6..518e46587 100644
--- a/krebs/3modules/shared/default.nix
+++ b/krebs/3modules/shared/default.nix
@@ -7,6 +7,7 @@ let
"test-arch"
"test-centos6"
"test-centos7"
+ "test-all-krebs-modules"
] (name: {
inherit name;
cores = 1;
diff --git a/krebs/5pkgs/Reaktor/plugins.nix b/krebs/5pkgs/Reaktor/plugins.nix
new file mode 100644
index 000000000..7490be4ca
--- /dev/null
+++ b/krebs/5pkgs/Reaktor/plugins.nix
@@ -0,0 +1,120 @@
+{ stdenv, lib, pkgs, makeWrapper }:
+
+rec {
+ # Begin API
+ buildBaseReaktorPlugin = { name
+ , config # python extra configuration for plugin
+ , phases ? []
+ , ... } @ attrs:
+ stdenv.mkDerivation (attrs // {
+ name = "Reaktor-plugin-" + name;
+ isReaktorPlugin = true;
+ });
+
+ buildSimpleReaktorPlugin = name: { script
+ , path ? []
+ , env ? {}
+ , append_rule ? false # append the rule instead of insert
+ , pattern ? ""
+ , ... } @ attrs:
+ let
+ path_env = { "PATH" = lib.makeSearchPath "bin" (path ++ [ pkgs.coreutils ]); };
+ src_dir = pkgs.substituteAll ( {
+ inherit name;
+ dir = "bin";
+ isExecutable = true;
+ src = script;
+ });
+ src_file = "${src_dir}/bin/${name}";
+ config = ''
+ public_commands.${if append_rule then "append(" else "insert(0," }{
+ 'capname' : "${name}",
+ 'pattern' : ${if pattern == "" then
+ ''indirect_pattern.format("${name}")'' else
+ ''"${pattern}"'' },
+ 'argv' : ["${src_file}"],
+ 'env' : ${builtins.toJSON (path_env // env)} })
+ '';
+ config_file = pkgs.writeText "plugin.py" config;
+ in buildBaseReaktorPlugin (attrs // rec {
+ inherit name config;
+
+ phases = [ "installPhase" ];
+ buildInputs = [ makeWrapper ];
+ installPhase = ''
+ mkdir -p $out/bin $out/etc/Reaktor
+ ln -s ${src_file} $out/bin
+ wrapProgram $out/bin/${name} \
+ --prefix PATH : ${path_env.PATH}
+ ln -s ${config_file} $out/etc/Reaktor/plugin.py
+ '';
+
+ });
+ # End API
+
+ # Begin Plugins
+ random-emoji = buildSimpleReaktorPlugin "emoji" {
+ path = with pkgs; [ gnused gnugrep xmlstarlet curl ];
+ script = ./scripts/random-emoji.sh;
+ };
+
+ sed-plugin = buildSimpleReaktorPlugin "sed-plugin" {
+ path = [ pkgs.gnused pkgs.python3 ];
+ # only support s///gi the plugin needs to see every msg
+ # TODO: this will eat up the last regex, fix Reaktor to support fallthru
+ append_rule = true;
+ pattern = "^(?P<args>.*)$$";
+ script = ./scripts/sed-plugin.py;
+ };
+
+ shack-correct = buildSimpleReaktorPlugin "shack-correct" {
+ path = [ pkgs.gnused ];
+ pattern = "^(?P<args>.*Shack.*)$$";
+ script = ./scripts/shack-correct.sh;
+ };
+
+ nixos-version = buildSimpleReaktorPlugin "nixos-version" {
+ script = pkgs.writeScript "nixos-version" ''
+ #! /bin/sh
+ . /etc/os-release
+ echo "$PRETTY_NAME"
+ '';
+ };
+ stockholm-issue = buildSimpleReaktorPlugin "stockholm-issue" {
+ script = ./scripts/random-issue.sh;
+ path = with pkgs; [ git gnused lentil ];
+ env = { "origin" = "http://cgit.gum/stockholm"; };
+ };
+
+ titlebot =
+ let
+ pypkgs = pkgs.python3Packages;
+ titlebot_cmds = pypkgs.buildPythonPackage {
+ name = "titlebot_cmds";
+ propagatedBuildInputs = with pypkgs; [ setuptools ];
+ src = pkgs.fetchurl {
+ url = "https://github.com/makefu/reaktor-titlebot/archive/2.1.0.tar.gz";
+ sha256 = "0wvf09wmk8b52f9j65qrw81nwrhs9pfhijwrlkzp5l7l2q8cjkp6";
+ };
+ };
+ in buildBaseReaktorPlugin rec {
+ name = "titlebot";
+ phases = [ "installPhase" ];
+ installPhase = ''
+ mkdir -p $out
+ ln -s ${titlebot_cmds}/* $out
+ '';
+ config = ''
+ def titlebot_cmd(cmd):
+ from os import environ
+ return { 'capname': None,
+ 'env': { 'TITLEDB':
+ environ['state_dir']+'/suggestions.json' },
+ 'pattern': '^\\.' + cmd + '\\s*(?:\\s+(?P<args>.*))?$$',
+ 'argv': [ '${titlebot_cmds}/bin/' + cmd ] }
+ for i in ['up','help','list','top','new']:
+ public_commands.insert(0,titlebot_cmd(i))
+ commands.insert(0,titlebot_cmd('clear'))
+ '';
+ };
+}
diff --git a/krebs/5pkgs/Reaktor/scripts/random-emoji.sh b/krebs/5pkgs/Reaktor/scripts/random-emoji.sh
new file mode 100644
index 000000000..386aa68b9
--- /dev/null
+++ b/krebs/5pkgs/Reaktor/scripts/random-emoji.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+curl http://emojicons.com/random -s | \
+ grep data-text | \
+ sed -n 's/.*>\(.*\)<\/textarea>/\1/p' | \
+ head -n 1 | \
+ xmlstarlet unesc
diff --git a/krebs/5pkgs/Reaktor/scripts/random-issue.sh b/krebs/5pkgs/Reaktor/scripts/random-issue.sh
new file mode 100644
index 000000000..5c47c6156
--- /dev/null
+++ b/krebs/5pkgs/Reaktor/scripts/random-issue.sh
@@ -0,0 +1,20 @@
+#! /bin/sh
+set -eu
+# requires env:
+# $state_dir
+# $origin
+
+# in PATH: git,lentil,coreutils
+subdir=`echo "$1" | tr -dc "[:alnum:]"`
+name=`echo "$origin" | tr -dc "[:alnum:]"`
+track="$state_dir/$name-checkout"
+(if test -e "$track" ;then
+ cd "$track"
+ git fetch origin master
+ git reset --hard origin/master
+else
+ git clone "$origin" "$track"
+fi) >&2
+
+cd "$track"
+lentil "${subdir:-.}" -f csv | sed 1d | shuf | head -1
diff --git a/krebs/5pkgs/Reaktor/scripts/sed-plugin.py b/krebs/5pkgs/Reaktor/scripts/sed-plugin.py
new file mode 100644
index 000000000..8103c9585
--- /dev/null
+++ b/krebs/5pkgs/Reaktor/scripts/sed-plugin.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+
+# Usage:
+# _from=krebs state_dir=. python sed-plugin.py 'dick butt'
+# _from=krebs state_dir=. python sed-plugin.py 's/t/l/g'
+## dick bull
+import shelve
+from os import environ
+from os.path import join
+from sys import argv
+d = shelve.open(join(environ['state_dir'],'sed-plugin.shelve'),writeback=True)
+usr = environ['_from']
+import re
+
+def is_regex(line):
+ myre = re.compile(r'^s/((?:\\/|[^/])+)/((?:\\/|[^/])*)/([ig]*)$')
+ return myre.match(line)
+
+line = argv[1]
+m = is_regex(line)
+
+if m:
+ f,t,flagstr = m.groups()
+ fn = f.replace('\/','/')
+ tn = t.replace('\/','/')
+ flags = 0
+ count = 1
+ if flagstr:
+ if 'i' in flagstr:
+ flags = re.IGNORECASE
+ if 'g' in flagstr: