about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--machines/profpatsch/base-workstation.nix1
-rw-r--r--machines/profpatsch/shiki.nix8
-rw-r--r--modules/module-list.nix1
-rw-r--r--modules/services/guix.nix106
-rw-r--r--pkgs/games/gog/fetch-gog/default.nix29
-rw-r--r--pkgs/games/humblebundle/default.nix1
-rw-r--r--pkgs/games/humblebundle/fetch-humble-bundle/default.nix89
-rw-r--r--pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch121
-rw-r--r--pkgs/games/humblebundle/minimetro.nix15
-rw-r--r--pkgs/profpatsch/default.nix14
10 files changed, 360 insertions, 25 deletions
diff --git a/machines/profpatsch/base-workstation.nix b/machines/profpatsch/base-workstation.nix
index d1a14e70..82dee85d 100644
--- a/machines/profpatsch/base-workstation.nix
+++ b/machines/profpatsch/base-workstation.nix
@@ -119,6 +119,7 @@ in {
       # of utmost necessity for me to function
       basePkgs = [
         silver-searcher   # file content searcher, > ack > grep
+        lr                # list recursively, ls & find replacement
         dos2unix          # text file conversion
         manpages          # system manpages (not included by default)
         mkpasswd          # UNIX password creator
diff --git a/machines/profpatsch/shiki.nix b/machines/profpatsch/shiki.nix
index 6bc08b6a..4d66f81e 100644
--- a/machines/profpatsch/shiki.nix
+++ b/machines/profpatsch/shiki.nix
@@ -162,6 +162,7 @@ in {
         # myPkgs.fast-init             # fast-init of haskell projects
         gitAndTools.git-annex        # version controlled binary file storage
         gitAndTools.git-dit          # decentral issue tracking for git
+        gitAndTools.git-hub          # lightweight GitHub integration
 
         # TODO: move to user config
         go
@@ -172,7 +173,7 @@ in {
         pkgs.vuizvui.profpatsch.nix-http-serve # serve nix builds and rebuild on reloads
         pkgs.vuizvui.profpatsch.nman # open man pages in temporary nix shell
         pkgs.vuizvui.profpatsch.warpspeed    # trivial http file server
-        pkgs.vuizvui.profpatsch.nix-gen      # generate nix expressions
+        # pkgs.vuizvui.profpatsch.nix-gen      # generate nix expressions
         pkgs.vuizvui.profpatsch.watch-server # restart server on code change
         pkgs.vuizvui.profpatsch.until        # restart until cmd succeeds
         myPkgs.execlineb-with-builtins
@@ -235,6 +236,8 @@ in {
         redshift   # increases screen warmth at night (so i don’t have to feel cold)
         # pdfjam is the best CLI pdf modification suite
         (texlive.combine { inherit (texlive) scheme-small pdfjam; })
+        # move script/nix-cache-binary to here
+        cdb
       ];
     in systemPkgs ++ xPkgs ++ guiPkgs
     ++ programmingTools ++ documentation
@@ -253,6 +256,9 @@ in {
       LidSwitchIgnoreInhibited=no
     '';
 
+    # TMP
+
+    vuizvui.services.guix.enable = true;
     ###################
     # Graphical System
 
diff --git a/modules/module-list.nix b/modules/module-list.nix
index c75b8dae..df539e6e 100644
--- a/modules/module-list.nix
+++ b/modules/module-list.nix
@@ -11,6 +11,7 @@
   ./programs/fish/fasd.nix
   ./services/postfix
   ./services/starbound.nix
+  ./services/guix.nix
   ./system/iso.nix
   ./system/kernel/bfq
   ./system/kernel/rckernel.nix
diff --git a/modules/services/guix.nix b/modules/services/guix.nix
new file mode 100644
index 00000000..287ac619
--- /dev/null
+++ b/modules/services/guix.nix
@@ -0,0 +1,106 @@
+# ATTN: this is a WIP service, use at your own risk!
+{ config, lib, pkgs, ... }:
+# https://www.gnu.org/software/guix/manual/en/html_node/Binary-Installation.html
+
+let
+  guixBinaryTar = pkgs.fetchurl {
+    url = "https://alpha.gnu.org/gnu/guix/guix-binary-0.16.0.x86_64-linux.tar.xz";
+    sha256 = "049l0zim30cd0gyly2h3jaw4cshdk78h7xdb9ac173h72i13afbj";
+  };
+
+  #*/
+  guixInstallScriptIdempotent = pkgs.writeScript "guix-install.sh" ''
+    #!/bin/sh
+    set -euo pipefail
+
+    # extract guix
+    if ! test -e /gnu; then
+      echo "INFO: installing guix"
+
+      tmp=$(mktemp -d)
+      pushd $tmp >/dev/null
+      export PATH=${pkgs.xz}/bin:$PATH
+      ${pkgs.gnutar}/bin/tar xf ${guixBinaryTar}
+      mkdir -p /var
+      cp -r ./var/guix /var
+      cp -r ./gnu /
+      popd >/dev/null
+
+      # XXX
+      # change the mtime of all compiled guile files,
+      # because tar in this script somehow changes the mtime
+      # of extracted files to the current time, and nobody knows
+      # why. If the sources are newer than the .go files, guile
+      # will try to recompile everything.
+      find /gnu/store/ -ipath "*guile*ccache*/*.go" | xargs touch -m
+    fi
+
+    # install root user profile
+    if ! test -e /root/.config/guix/current; then
+      mkdir -p /root/.config/guix
+      ln -s /var/guix/profiles/per-user/root/current-guix \
+        /root/.config/guix/current
+    fi
+
+    echo INFO: finished installing guix!
+  '';
+
+  guixBuildGroup = "guixbuilders";
+
+  guixBuildUser = id: {
+    name = "guix-build-user-${toString id}";
+    createHome = false;
+    description = "Guix build user ${toString id}";
+    extraGroups = [ guixBuildGroup ];
+    isSystemUser = true;
+  };
+
+  guixBuildUsers = numberOfUsers:
+    builtins.listToAttrs
+      (map (user: {
+        name = user.name;
+        value = user;
+      }) (builtins.genList guixBuildUser numberOfUsers));
+in
+{
+  options = {
+    vuizvui.services.guix.enable =
+      lib.mkEnableOption "the guix daemon and init /gnu/store";
+  };
+
+  config = lib.mkIf config.vuizvui.services.guix.enable {
+    users.users = guixBuildUsers 10;
+    users.groups = { "${guixBuildGroup}" = {}; };
+
+    systemd.services.guix-install = {
+      serviceConfig = {
+        ExecStart = guixInstallScriptIdempotent;
+        Type = "oneshot";
+      };
+    };
+
+    systemd.services.guix-daemon = {
+      serviceConfig = {
+        ExecStart = "/var/guix/profiles/per-user/root/current-guix/bin/guix-daemon --build-users-group=${guixBuildGroup}";
+        Environment = "GUIX_LOCPATH=/var/guix/profiles/per-user/root/guix-profix/lib/locale";
+        RemainAfterExit = true;
+        StandardOutput = "syslog";
+        StandardError = "syslog";
+        TasksMax = 8192;
+      };
+      wantedBy = [ "multi-user.target" ];
+      after = [ "guix-install.service" ];
+      wants = [ "guix-install.service" ];
+    };
+
+    environment.shellInit = ''
+      export GUIX_PROFILE="$HOME/.config/guix/current"
+      source $GUIX_PROFILE/etc/profile
+      export GUIX_LOCPATH="${pkgs.glibcLocales}/lib/locale"
+      export INFOPATH="$GUIX_PROFILE/share/info:$INFOPATH"
+
+      guix archive --authorize < \
+        /root/.config/guix/current/share/guix/ci.guix.info.pub
+    '';
+  };
+}
diff --git a/pkgs/games/gog/fetch-gog/default.nix b/pkgs/games/gog/fetch-gog/default.nix
index d4a35a05..c1d350ce 100644
--- a/pkgs/games/gog/fetch-gog/default.nix
+++ b/pkgs/games/gog/fetch-gog/default.nix
@@ -147,14 +147,24 @@ let
       }
     '';
 
-  in runCommandCC "get-captcha" {
-    nativeBuildInputs = [ pkgconfig ];
+  in stdenv.mkDerivation {
+    name = "get-captcha";
+
+    dontUnpack = true;
+
+    nativeBuildInputs = [ pkgconfig (qt5.wrapQtAppsHook or null) ];
     buildInputs = [ qt5.qtbase qt5.qtwebengine ];
     preferLocalBuild = true;
-  } ''
-    g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \
-      -Wall -std=c++11 -o "$out" ${application}
-  '';
+
+    buildPhase = ''
+      g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \
+        -Wall -std=c++11 -o get-captcha ${application}
+    '';
+
+    installPhase = ''
+      install -vD get-captcha "$out/bin/get-captcha"
+    '';
+  };
 
   mkPyStr = str: "'${stdenv.lib.escape ["'" "\\"] (toString str)}'";
 
@@ -178,7 +188,7 @@ let
       def login(self):
         browser = mechanicalsoup.StatefulBrowser()
         response = browser.open(${mkPyStr authURL})
-        if "google.com/recaptcha" in response.text:
+        if "https://www.recaptcha.net/recaptcha" in response.text:
           token_url = self.login_with_captcha()
         else:
           browser.select_form('form[name="login"]')
@@ -190,7 +200,7 @@ let
 
           if 'code' not in query:
             sys.stderr.write(
-              "Unable to login with the provided GOG credentials."
+              "Unable to login with the provided GOG credentials.\n"
             )
             raise SystemExit(1)
 
@@ -210,7 +220,8 @@ let
 
       def login_with_captcha(self):
         sys.stderr.write("Solving a captcha is required to log in.\n")
-        sys.stderr.write("Please run " ${mkPyStr getCaptcha} " now.\n")
+        sys.stderr.write("Please run " ${mkPyStr getCaptcha}
+                         "/bin/get-captcha now.\n")
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sys.stderr.write("Waiting for connection")
         i = 0
diff --git a/pkgs/games/humblebundle/default.nix b/pkgs/games/humblebundle/default.nix
index 656c06db..8d712eb8 100644
--- a/pkgs/games/humblebundle/default.nix
+++ b/pkgs/games/humblebundle/default.nix
@@ -24,6 +24,7 @@ let
     jamestown = callPackage ./jamestown.nix {};
     liads = callPackage ./liads.nix {};
     megabytepunch = callPackage_i686 ./megabytepunch.nix {};
+    minimetro = callPackage ./minimetro.nix {};
     opus-magnum = callPackage ./opus-magnum.nix {};
     owlboy = callPackage ./owlboy.nix {};
     pico-8 = callPackage ./pico-8.nix {};
diff --git a/pkgs/games/humblebundle/fetch-humble-bundle/default.nix b/pkgs/games/humblebundle/fetch-humble-bundle/default.nix
index f6f161dc..1340bce8 100644
--- a/pkgs/games/humblebundle/fetch-humble-bundle/default.nix
+++ b/pkgs/games/humblebundle/fetch-humble-bundle/default.nix
@@ -1,13 +1,20 @@
-{ stdenv, curl, cacert, writeText, fetchFromGitHub, fetchpatch
-, python, pythonPackages
+{ stdenv, curl, cacert, writeText, writeScript, fetchFromGitHub, fetchpatch
+, python, python3, pythonPackages
 
 # Dependencies for the captcha solver
-, pkgconfig, qt5, runCommandCC
+, pkgconfig, qt5
 
 , email, password
 }:
 
-{ name ? null, machineName, downloadName ? "Download", suffix ? "humblebundle", md5 }: let
+{ name ? null
+, machineName
+, downloadName ? "Download"
+, suffix ? "humblebundle"
+, md5
+}:
+
+let
   cafile = "${cacert}/etc/ssl/certs/ca-bundle.crt";
 
   getCaptcha = let
@@ -77,13 +84,34 @@
       }
     '';
 
-  in runCommandCC "get-captcha" {
-    nativeBuildInputs = [ pkgconfig ];
+  in stdenv.mkDerivation {
+    name = "get-captcha";
+
+    dontUnpack = true;
+
+    nativeBuildInputs = [ pkgconfig (qt5.wrapQtAppsHook or null) ];
     buildInputs = [ qt5.qtbase qt5.qtwebengine ];
     preferLocalBuild = true;
-  } ''
-    g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \
-      -Wall -std=c++11 -o "$out" ${application}
+
+    buildPhase = ''
+      g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \
+        -Wall -std=c++11 -o get-captcha ${application}
+    '';
+
+    installPhase = ''
+      install -vD get-captcha "$out/bin/get-captcha"
+    '';
+  };
+
+  getGuard = writeScript "get-guard" ''
+    #!${python3.interpreter}
+    import socket
+    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+      sock.bind(('localhost', 18129))
+      sock.listen(1)
+      with sock.accept()[0] as conn:
+        guard = input("Guard code: ")
+        conn.sendall(guard.encode())
   '';
 
   humbleAPI = pythonPackages.buildPythonPackage rec {
@@ -97,6 +125,8 @@
       sha256 = "1kcg42nh7sbjabim1pbqx14468pypznjy7fx2bv7dicy0sqd9b8j";
     };
 
+    patches = [ ./guard-code.patch ];
+
     postPatch = ''
       sed -i -e '/^LOGIN_URL *=/s,/login,/processlogin,' humblebundle/client.py
       sed -i -e '/self\.supports_canonical.*data.*supports_canonical/d' \
@@ -138,7 +168,8 @@
 
     def login_with_captcha(hb):
       print >>sys.stderr, "Solving a captcha is required to log in."
-      print >>sys.stderr, "Please run " ${pyStr (toString getCaptcha)} " now."
+      print >>sys.stderr, "Please run " ${pyStr (toString getCaptcha)} \
+                          "/bin/get-captcha now."
       sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       print >>sys.stderr, "Waiting for connection",
       i = 0
@@ -153,13 +184,41 @@
       response = sock.recv(4096)
       sock.close()
       print >>sys.stderr, "Captcha solved correctly, logging in."
-      hb.login(${pyStr email}, ${pyStr password}, recaptcha_response=response)
+      api_login(hb, recaptcha_response=response)
+
+    def login_with_guard(hb, skip_code):
+      print >>sys.stderr, "A guard code has been sent to your email address."
+      print >>sys.stderr, "Please run " ${pyStr (toString getGuard)} " now."
+      sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+      print >>sys.stderr, "Waiting for connection",
+      # XXX: DRY!
+      i = 0
+      while sock.connect_ex(("127.0.0.1", 18129)) != 0:
+        time.sleep(0.1)
+        if i % 10 == 0:
+          sys.stderr.write('.')
+          sys.stderr.flush()
+        i += 1
+      print >>sys.stderr, " connected."
+      print >>sys.stderr, "Waiting for guard code..."
+      response = sock.recv(4096)
+      sock.close()
+      print >>sys.stderr, "Guard code supplied, logging in."
+      api_login(hb, captcha_skip_code=skip_code, guard_code=response)
+
+    def api_login(hb, recaptcha_response=None,
+                  captcha_skip_code=None, guard_code=None):
+      try:
+        hb.login(${pyStr email}, ${pyStr password},
+                 recaptcha_response=recaptcha_response,
+                 captcha_skip_code=captcha_skip_code, guard_code=guard_code)
+      except humblebundle.exceptions.HumbleCaptchaException:
+        login_with_captcha(hb)
+      except humblebundle.exceptions.HumbleGuardRequiredException as e:
+        login_with_guard(hb, e.captcha_skip_code)
 
     hb = humblebundle.HumbleApi()
-    try:
-      hb.login(${pyStr email}, ${pyStr password})
-    except humblebundle.exceptions.HumbleCaptchaException:
-      login_with_captcha(hb)
+    api_login(hb)
 
     products = dict(get_products(hb))
     dstruct = find_download(products)
diff --git a/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch b/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch
new file mode 100644
index 00000000..f17928ae
--- /dev/null
+++ b/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch
@@ -0,0 +1,121 @@
+diff --git a/humblebundle/client.py b/humblebundle/client.py
+index fbc31c9..44184a1 100644
+--- a/humblebundle/client.py
++++ b/humblebundle/client.py
+@@ -75,7 +75,9 @@ class HumbleApi(object):
+     """
+ 
+     @callback
+-    def login(self, username, password, authy_token=None, recaptcha_challenge=None, recaptcha_response=None,
++    def login(self, username, password, authy_token=None,
++              recaptcha_challenge=None, recaptcha_response=None,
++              guard_code=None, captcha_skip_code=None,
+               *args, **kwargs):
+         """
+         Login to the Humble Bundle API. The response sets the _simpleauth_sess cookie which is stored in the session
+@@ -87,6 +89,8 @@ class HumbleApi(object):
+         :type authy_token: integer or str
+         :param str recaptcha_challenge: (optional) The challenge signed by Humble Bundle's public key from reCAPTCHA
+         :param str recaptcha_response: (optional) The plaintext solved CAPTCHA
++        :param str guard_code: (optional) The guard code sent via email
++        :param str captcha_skip_code: (optional) A token to skip the CAPTCHA
+         :param list args: (optional) Extra positional args to pass to the request
+         :param dict kwargs: (optional) Extra keyword args to pass to the request. If a data dict is supplied a key
+                             collision with any of the above params will resolved in favor of the supplied param
+@@ -108,7 +112,9 @@ class HumbleApi(object):
+             'password': password,
+             'authy-token': authy_token,
+             'recaptcha_challenge_field': recaptcha_challenge,
+-            'recaptcha_response_field': recaptcha_response}
++            'recaptcha_response_field': recaptcha_response,
++            'guard': guard_code,
++            'captcha-skip-code': captcha_skip_code}
+         kwargs.setdefault('data', {}).update({k: v for k, v in default_data.items() if v is not None})
+ 
+         response = self._request('POST', LOGIN_URL, *args, **kwargs)
+diff --git a/humblebundle/exceptions.py b/humblebundle/exceptions.py
+index 9041219..fe4eeaf 100644
+--- a/humblebundle/exceptions.py
++++ b/humblebundle/exceptions.py
+@@ -9,7 +9,7 @@ __copyright__ = "Copyright 2014, Joel Pedraza"
+ __license__ = "MIT"
+ 
+ __all__ = ['HumbleException', 'HumbleResponseException', 'HumbleAuthenticationException', 'HumbleCredentialException',
+-           'HumbleCaptchaException', 'HumbleTwoFactorException', 'HumbleParseException']
++           'HumbleCaptchaException', 'HumbleTwoFactorException', 'HumbleGuardRequiredException', 'HumbleParseException']
+ 
+ from requests import RequestException
+ 
+@@ -38,6 +38,7 @@ class HumbleAuthenticationException(HumbleResponseException):
+     def __init__(self, *args, **kwargs):
+         self.captcha_required = kwargs.pop('captcha_required', None)
+         self.authy_required = kwargs.pop('authy_required', None)
++        self.captcha_skip_code = kwargs.pop('captcha_skip_code', None)
+         super(HumbleAuthenticationException, self).__init__(*args, **kwargs)
+ 
+ 
+@@ -62,6 +63,13 @@ class HumbleTwoFactorException(HumbleAuthenticationException):
+     pass
+ 
+ 
++class HumbleGuardRequiredException(HumbleAuthenticationException):
++    """
++    A guard code is required
++    """
++    pass
++
++
+ class HumbleParseException(HumbleResponseException):
+     """
+     An error occurred while parsing
+diff --git a/humblebundle/handlers.py b/humblebundle/handlers.py
+index 36fc6e1..a8acebf 100644
+--- a/humblebundle/handlers.py
++++ b/humblebundle/handlers.py
+@@ -64,29 +64,42 @@ def login_handler(client, response):
+     success = data.get('success', None)
+     if success is True:
+         return True
++    if data.get('goto', None) is not None:
++        return True
+ 
+     captcha_required = data.get('captcha_required')
+     authy_required = data.get('authy_required')
++    captcha_skip_code = data.get('skip_code', [None])[0]
++
++    guard = data.get('humble_guard_required', False)
++    if guard:
++        raise HumbleGuardRequiredException('Guard code required', request=response.request, response=response,
++                                           captcha_required=captcha_required, authy_required=authy_required,
++                                           captcha_skip_code=captcha_skip_code)
+ 
+     errors, error_msg = get_errors(data)
+     if errors:
+         captcha = errors.get('captcha')
+         if captcha:
+             raise HumbleCaptchaException(error_msg, request=response.request, response=response,
+-                                         captcha_required=captcha_required, authy_required=authy_required)
++                                         captcha_required=captcha_required, authy_required=authy_required,
++                                         captcha_skip_code=captcha_skip_code)
+ 
+         username = errors.get('username')
+         if username:
+             raise HumbleCredentialException(error_msg, request=response.request, response=response,
+-                                            captcha_required=captcha_required, authy_required=authy_required)
++                                            captcha_required=captcha_required, authy_required=authy_required,
++                                            captcha_skip_code=captcha_skip_code)
+ 
+         authy_token = errors.get("authy-token")
+         if authy_token:
+             raise HumbleTwoFactorException(error_msg, request=response.request, response=response,
+-                                           captcha_required=captcha_required, authy_required=authy_required)
++                                           captcha_required=captcha_required, authy_required=authy_required,
++                                           captcha_skip_code=captcha_skip_code)
+ 
+     raise HumbleAuthenticationException(error_msg, request=response.request, response=response,
+-                                        captcha_required=captcha_required, authy_required=authy_required)
++                                        captcha_required=captcha_required, authy_required=authy_required,
++                                        captcha_skip_code=captcha_skip_code)
+ 
+ 
+ def gamekeys_handler(client, response):
diff --git a/pkgs/games/humblebundle/minimetro.nix b/pkgs/games/humblebundle/minimetro.nix
new file mode 100644
index 00000000..a5c48775
--- /dev/null
+++ b/pkgs/games/humblebundle/minimetro.nix
@@ -0,0 +1,15 @@
+{ buildUnity, fetchHumbleBundle }:
+
+buildUnity rec {
+  name = "minimetro";
+  version = "39";
+  fullName = "Mini Metro";
+  saveDir = "Dinosaur Polo Club/Mini Metro";
+
+  src = fetchHumbleBundle {
+    name = "MiniMetro-release-39-linux.tar.gz";
+    machineName = "minimetro_linux";
+    downloadName = "Download";
+    md5 = "3e7afefbcc68b6295821394e31f5e48b";
+  };
+}
diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix
index 69977fff..dc746c2e 100644
--- a/pkgs/profpatsch/default.nix
+++ b/pkgs/profpatsch/default.nix
@@ -5,11 +5,25 @@ let
 
   # wrapper for execlineb that doesn’t need the execline commands
   # in PATH to work (making them appear like “builtins”)
+  # TODO: upstream into nixpkgs
+  # TODO: the grep could be nicer
   execlineb-with-builtins =
     let eldir = "${pkgs.execline}/bin";
     in pkgs.writeScriptBin "execlineb" ''
       #!${eldir}/execlineb -s0
+      # appends the execlineb bin dir to PATH if not yet in PATH
       ${eldir}/define eldir ${eldir}
+      ''${eldir}/ifelse
+      {
+        # since this is nix, we can grep for the execline drv hash in PATH
+        # to see whether it’s already in there
+        ''${eldir}/pipeline
+        { ${pkgs.coreutils}/bin/printenv PATH }
+        ${pkgs.gnugrep}/bin/grep --quiet "${eldir}"
+      }
+      # it’s there already
+      { ''${eldir}/execlineb $@ }
+      # not there yet, add it
       ''${eldir}/importas oldpath PATH
       ''${eldir}/export PATH "''${eldir}:''${oldpath}"
       ''${eldir}/execlineb $@