]> git.immae.eu Git - perso/Immae/Config/Nix.git/commitdiff
Add taskwarrior-web
authorIsmaël Bouya <ismael.bouya@normalesup.org>
Wed, 6 Mar 2019 15:43:44 +0000 (16:43 +0100)
committerIsmaël Bouya <ismael.bouya@normalesup.org>
Wed, 6 Mar 2019 15:43:44 +0000 (16:43 +0100)
Fixes https://git.immae.eu/mantisbt/view.php?id=67

nixops/modules/task/Gemfile.lock [new file with mode: 0644]
nixops/modules/task/default.nix
nixops/modules/task/fixes.patch [new file with mode: 0644]
nixops/modules/task/gemset.nix [new file with mode: 0644]
nixops/modules/task/taskwarrior-web.json [new file with mode: 0644]
nixops/modules/task/taskwarrior-web.nix [new file with mode: 0644]
nixops/modules/task/thin.patch [new file with mode: 0644]

diff --git a/nixops/modules/task/Gemfile.lock b/nixops/modules/task/Gemfile.lock
new file mode 100644 (file)
index 0000000..1b2f5ba
--- /dev/null
@@ -0,0 +1,139 @@
+PATH
+  remote: .
+  specs:
+    taskwarrior-web (1.1.12)
+      activesupport (~> 3)
+      json (~> 1.8)
+      parseconfig
+      rack-flash3
+      rinku
+      sinatra
+      sinatra-simple-navigation
+      vegas
+      versionomy
+
+GEM
+  remote: http://rubygems.org/
+  specs:
+    activesupport (3.2.22.5)
+      i18n (~> 0.6, >= 0.6.4)
+      multi_json (~> 1.0)
+    blockenspiel (0.5.0)
+    coderay (1.1.2)
+    concurrent-ruby (1.1.4)
+    daemons (1.3.1)
+    diff-lcs (1.3)
+    docile (1.3.1)
+    eventmachine (1.2.7)
+    ffi (1.10.0)
+    formatador (0.2.5)
+    growl (1.0.3)
+    guard (2.15.0)
+      formatador (>= 0.2.4)
+      listen (>= 2.7, < 4.0)
+      lumberjack (>= 1.0.12, < 2.0)
+      nenv (~> 0.1)
+      notiffany (~> 0.0)
+      pry (>= 0.9.12)
+      shellany (~> 0.0)
+      thor (>= 0.18.1)
+    guard-bundler (2.2.1)
+      bundler (>= 1.3.0, < 3)
+      guard (~> 2.2)
+      guard-compat (~> 1.1)
+    guard-compat (1.2.1)
+    guard-rspec (4.7.3)
+      guard (~> 2.1)
+      guard-compat (~> 1.1)
+      rspec (>= 2.99.0, < 4.0)
+    i18n (0.9.5)
+      concurrent-ruby (~> 1.0)
+    json (1.8.6)
+    listen (3.1.5)
+      rb-fsevent (~> 0.9, >= 0.9.4)
+      rb-inotify (~> 0.9, >= 0.9.7)
+      ruby_dep (~> 1.2)
+    lumberjack (1.0.13)
+    method_source (0.9.2)
+    mini_portile2 (2.4.0)
+    multi_json (1.13.1)
+    mustermann (1.0.3)
+    nenv (0.3.0)
+    nokogiri (1.10.1)
+      mini_portile2 (~> 2.4.0)
+    notiffany (0.1.1)
+      nenv (~> 0.1)
+      shellany (~> 0.0)
+    parseconfig (1.0.8)
+    pry (0.12.2)
+      coderay (~> 1.1.0)
+      method_source (~> 0.9.0)
+    rack (2.0.6)
+    rack-flash3 (1.0.5)
+      rack
+    rack-protection (2.0.5)
+      rack
+    rack-test (1.1.0)
+      rack (>= 1.0, < 3)
+    rake (10.5.0)
+    rb-fsevent (0.10.3)
+    rb-inotify (0.10.0)
+      ffi (~> 1.0)
+    rinku (2.0.5)
+    rspec (2.99.0)
+      rspec-core (~> 2.99.0)
+      rspec-expectations (~> 2.99.0)
+      rspec-mocks (~> 2.99.0)
+    rspec-core (2.99.2)
+    rspec-expectations (2.99.2)
+      diff-lcs (>= 1.1.3, < 2.0)
+    rspec-html-matchers (0.5.0)
+      nokogiri (~> 1)
+      rspec (~> 2, >= 2.11.0)
+    rspec-mocks (2.99.4)
+    ruby_dep (1.5.0)
+    shellany (0.0.1)
+    simple-navigation (4.0.5)
+      activesupport (>= 2.3.2)
+    simplecov (0.16.1)
+      docile (~> 1.1)
+      json (>= 1.8, < 3)
+      simplecov-html (~> 0.10.0)
+    simplecov-html (0.10.2)
+    sinatra (2.0.5)
+      mustermann (~> 1.0)
+      rack (~> 2.0)
+      rack-protection (= 2.0.5)
+      tilt (~> 2.0)
+    sinatra-simple-navigation (4.1.0)
+      simple-navigation (~> 4.0)
+      sinatra (>= 1.0, < 3.0)
+    thin (1.7.2)
+      daemons (~> 1.0, >= 1.0.9)
+      eventmachine (~> 1.0, >= 1.0.4)
+      rack (>= 1, < 3)
+    thor (0.20.3)
+    tilt (2.0.9)
+    vegas (0.1.11)
+      rack (>= 1.0.0)
+    versionomy (0.5.0)
+      blockenspiel (~> 0.5)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  growl
+  guard-bundler
+  guard-rspec
+  rack-test
+  rake (< 11)
+  rb-fsevent
+  rspec (~> 2)
+  rspec-html-matchers
+  simplecov
+  taskwarrior-web!
+  thin
+
+BUNDLED WITH
+   1.16.2
index 3dc329985de02b3f8e3c1397b406cdd04e916949..2fd61aab0c48b9a4b53b0230aed8b589495a55e1 100644 (file)
@@ -6,6 +6,81 @@ let
   user = config.services.taskserver.user;
   env = myconfig.env.tools.task;
   group = config.services.taskserver.group;
+  taskserver-user-certs = pkgs.runCommand "taskserver-user-certs" {} ''
+    mkdir -p $out/bin
+    cat > $out/bin/taskserver-user-certs <<"EOF"
+    #!/usr/bin/env bash
+
+    user=$1
+
+    silent_certtool() {
+      if ! output="$("${pkgs.gnutls.bin}/bin/certtool" "$@" 2>&1)"; then
+        echo "GNUTLS certtool invocation failed with output:" >&2
+        echo "$output" >&2
+      fi
+    }
+
+    silent_certtool -p \
+      --bits 4096 \
+      --outfile "${vardir}/userkeys/$user.key.pem"
+    ${pkgs.gnused}/bin/sed -i -n -e '/^-----BEGIN RSA PRIVATE KEY-----$/,$p' "${vardir}/userkeys/$user.key.pem"
+
+    silent_certtool -c \
+      --template "${pkgs.writeText "taskserver-ca.template" ''
+        tls_www_client
+        encryption_key
+        signing_key
+        expiration_days = 3650
+      ''}" \
+      --load-ca-certificate "${vardir}/keys/ca.cert" \
+      --load-ca-privkey "${vardir}/keys/ca.key" \
+      --load-privkey "${vardir}/userkeys/$user.key.pem" \
+      --outfile "${vardir}/userkeys/$user.cert.pem"
+    EOF
+    chmod a+x $out/bin/taskserver-user-certs
+    patchShebangs $out/bin/taskserver-user-certs
+    '';
+  taskwarrior-web = pkgs.callPackage ./taskwarrior-web.nix {
+    inherit (mylibs) fetchedGithub;
+    inherit env;
+  };
+  taskwebPages = let
+    uidPages = lib.attrsets.zipAttrs (
+      lib.lists.flatten
+        (lib.attrsets.mapAttrsToList (k: c: map (v: { "${v}" = k; }) c.uid) env.taskwarrior-web)
+      );
+    pages = lib.attrsets.mapAttrs (uid: items:
+      if lib.lists.length items == 1 then
+      ''
+        <html>
+        <head>
+          <meta http-equiv="refresh" content="0; url=/taskweb/${lib.lists.head items}/" />
+        </head>
+        <body></body>
+        </html>
+        ''
+      else
+      ''
+        <html>
+        <head>
+        <title>To-do list disponibles</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+        </head>
+        <body>
+          <ul>
+            ${builtins.concatStringsSep "\n" (map (item: "<li><a href='/taskweb/${item}'>${item}</a></li>") items)}
+          </ul>
+        </body>
+        </html>
+        ''
+    ) uidPages;
+  in
+    pkgs.runCommand "taskwerver-pages" {} ''
+      mkdir -p $out/
+      ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (k: v: "cp ${pkgs.writeText k v} $out/${k}.html") pages)}
+      echo "Please login" > $out/index.html
+      '';
 in {
   options.services.myTasks = {
     enable = lib.mkEnableOption "my tasks service";
@@ -13,7 +88,7 @@ in {
 
   config = lib.mkIf cfg.enable {
     security.acme.certs."eldiron".extraDomains.${fqdn} = null;
-    services.myWebsites.tools.modules = [ "proxy_fcgi" ];
+    services.myWebsites.tools.modules = [ "proxy_fcgi" "sed" ];
     services.myWebsites.tools.vhostConfs.task = {
       certName    = "eldiron";
       hosts       = [ "task.immae.eu" ];
@@ -34,7 +109,48 @@ in {
           SetEnv TASKD_LDAP_BASE     "${env.ldap.base}"
           SetEnv TASKD_LDAP_FILTER   "${env.ldap.search}"
         </Directory>
-        '' ];
+        ''
+        ''
+        <Macro Taskwarrior %{folderName}>
+          ProxyPass        "unix://${taskwarrior-web.socketsDir}/%{folderName}.sock|http://localhost-%{folderName}/"
+          ProxyPassReverse "unix://${taskwarrior-web.socketsDir}/%{folderName}.sock|http://localhost-%{folderName}/"
+          ProxyPassReverse http://${fqdn}/
+
+          SetOutputFilter Sed
+          OutputSed   "s|/ajax|/taskweb/%{folderName}/ajax|g"
+          OutputSed   "s|\([^x]\)/tasks|\1/taskweb/%{folderName}/tasks|g"
+          OutputSed   "s|\([^x]\)/projects|\1/taskweb/%{folderName}/projects|g"
+          OutputSed   "s|http://${fqdn}/|/taskweb/%{folderName}/|g"
+          OutputSed   "s|/img/relax.jpg|/taskweb/%{folderName}/img/relax.jpg|g"
+        </Macro>
+        ''
+        ''
+        Alias /taskweb ${taskwebPages}
+        <Directory "${taskwebPages}">
+          DirectoryIndex index.html
+          Require all granted
+        </Directory>
+
+        RewriteEngine on
+        RewriteRule ^/taskweb$ /taskweb/ [R=301,L]
+        RedirectMatch permanent ^/taskweb/([^/]+)$ /taskweb/$1/
+
+        RewriteCond %{LA-U:REMOTE_USER} !=""
+        RewriteCond ${taskwebPages}/%{LA-U:REMOTE_USER}.html -f
+        RewriteRule ^/taskweb/?$ ${taskwebPages}/%{LA-U:REMOTE_USER}.html [L]
+
+        <Location /taskweb/>
+          Use LDAPConnect
+          Require ldap-group cn=users,cn=taskwarrior,ou=services,dc=immae,dc=eu
+        </Location>
+        ''
+      ] ++ (lib.attrsets.mapAttrsToList (k: v: ''
+        <Location /taskweb/${k}/>
+          ${builtins.concatStringsSep "\n" (map (uid: "Require ldap-attribute   uid=${uid}") v.uid)}
+
+          Use Taskwarrior ${k}
+        </Location>
+        '') env.taskwarrior-web);
     };
     services.myPhpfpm.poolConfigs = {
       tasks = ''
@@ -69,47 +185,7 @@ in {
       '';
     };
 
-    users.users.${user}.packages = [
-      (pkgs.runCommand "taskserver-user-certs" {} ''
-        mkdir -p $out/bin
-        cat > $out/bin/taskserver-user-certs <<"EOF"
-        #!/usr/bin/env bash
-
-        user=$1
-
-        silent_certtool() {
-          if ! output="$("${pkgs.gnutls.bin}/bin/certtool" "$@" 2>&1)"; then
-            echo "GNUTLS certtool invocation failed with output:" >&2
-            echo "$output" >&2
-          fi
-        }
-
-        silent_certtool -p \
-          --bits 4096 \
-          --outfile "${vardir}/userkeys/$user.key.pem"
-        ${pkgs.gnused}/bin/sed -i -n -e '/^-----BEGIN RSA PRIVATE KEY-----$/,$p' "${vardir}/userkeys/$user.key.pem"
-
-        silent_certtool -c \
-          --template "${pkgs.writeText "taskserver-ca.template" ''
-            tls_www_client
-            encryption_key
-            signing_key
-            expiration_days = 3650
-          ''}" \
-          --load-ca-certificate "${vardir}/keys/ca.cert" \
-          --load-ca-privkey "${vardir}/keys/ca.key" \
-          --load-privkey "${vardir}/userkeys/$user.key.pem" \
-          --outfile "${vardir}/userkeys/$user.cert.pem"
-        EOF
-        chmod a+x $out/bin/taskserver-user-certs
-        patchShebangs $out/bin/taskserver-user-certs
-        '')
-    ];
-
-    systemd.services.taskserver-ca.postStart = ''
-      chown :${group} "${vardir}/keys/ca.key"
-      chmod g+r "${vardir}/keys/ca.key"
-      '';
+    users.users.${user}.packages = [ taskserver-user-certs ];
 
     system.activationScripts.taskserver = {
       deps = [ "users" ];
@@ -127,5 +203,67 @@ in {
       listenHost = "::";
       requestLimit = 104857600;
     };
+
+    system.activationScripts.taskwarrior-web = {
+      deps = [ "users" ];
+      text = ''
+        install -m 0755 -o ${user} -g ${group} -d ${taskwarrior-web.socketsDir}
+        install -m 0750 -o ${user} -g ${group} -d ${taskwarrior-web.varDir}
+        ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList
+          (k: v: "install -m 0750 -o ${user} -g ${group} -d ${taskwarrior-web.varDir}/${k}")
+          env.taskwarrior-web
+        )}
+        if [ ! -f ${vardir}/userkeys/taskwarrior-web.cert.pem ]; then
+          ${taskserver-user-certs}/bin/taskserver-user-certs taskwarrior-web
+          chown taskd:taskd ${vardir}/userkeys/taskwarrior-web.cert.pem ${vardir}/userkeys/taskwarrior-web.key.pem
+        fi
+      '';
+    };
+
+    systemd.services = (lib.attrsets.mapAttrs' (name: userConfig:
+      let
+        credentials = "${userConfig.org}/${name}/${userConfig.key}";
+        dateFormat = userConfig.date;
+        taskrc = pkgs.writeText "taskrc" ''
+          data.location=${taskwarrior-web.varDir}/${name}
+          taskd.certificate=${vardir}/userkeys/taskwarrior-web.cert.pem
+          taskd.key=${vardir}/userkeys/taskwarrior-web.key.pem
+          taskd.ca=${vardir}/keys/server.cert
+          taskd.server=${fqdn}:${toString config.services.taskserver.listenPort}
+          taskd.credentials=${credentials}
+          dateformat=${dateFormat}
+          '';
+      in lib.attrsets.nameValuePair "taskwarrior-web-${name}" {
+        description = "Taskwarrior webapp for ${name}";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        path = [ pkgs.taskwarrior ];
+
+        environment.TASKRC = taskrc;
+        environment.BUNDLE_PATH = "${taskwarrior-web.gems}/lib/ruby/gems/2.5.0";
+        environment.BUNDLE_GEMFILE = "${taskwarrior-web.gems.confFiles}/Gemfile";
+        environment.LC_ALL = "fr_FR.UTF-8";
+
+        script = ''
+          exec ${taskwarrior-web.gems}/lib/ruby/gems/2.5.0/bin/bundle exec thin start -R config.ru -S ${taskwarrior-web.socketsDir}/${name}.sock
+        '';
+
+        serviceConfig = {
+          User = user;
+          PrivateTmp = true;
+          Restart = "always";
+          TimeoutSec = 60;
+          Type = "simple";
+          WorkingDirectory = taskwarrior-web.rubyRoot;
+        };
+
+        unitConfig.RequiresMountsFor = taskwarrior-web.varDir;
+      }) env.taskwarrior-web) // {
+        taskserver-ca.postStart = ''
+          chown :${group} "${vardir}/keys/ca.key"
+          chmod g+r "${vardir}/keys/ca.key"
+        '';
+      };
+
   };
 }
diff --git a/nixops/modules/task/fixes.patch b/nixops/modules/task/fixes.patch
new file mode 100644 (file)
index 0000000..851f9f0
--- /dev/null
@@ -0,0 +1,56 @@
+diff --git a/lib/taskwarrior-web/helpers.rb b/lib/taskwarrior-web/helpers.rb
+index 212aed7..94c57df 100644
+--- a/lib/taskwarrior-web/helpers.rb
++++ b/lib/taskwarrior-web/helpers.rb
+@@ -1,6 +1,8 @@
+ require 'active_support/core_ext/date/calculations'
+ module TaskwarriorWeb::App::Helpers
++  include ERB::Util
++
+   def format_date(timestamp)
+     format = TaskwarriorWeb::Config.dateformat || '%-m/%-d/%Y'
+     Time.parse(timestamp).localtime.strftime(format)
+diff --git a/lib/taskwarrior-web/services/builder/base.rb b/lib/taskwarrior-web/services/builder/base.rb
+index 58d246e..8f716ac 100644
+--- a/lib/taskwarrior-web/services/builder/base.rb
++++ b/lib/taskwarrior-web/services/builder/base.rb
+@@ -10,7 +10,7 @@ module TaskwarriorWeb::CommandBuilder::Base
+     :complete => ':id done',
+     :annotate => ':id annotate',
+     :denotate => ':id denotate',
+-    :projects => '_projects',
++    :projects => '_unique project',
+     :tags => '_tags',
+     :sync => 'sync'
+   }
+diff --git a/lib/taskwarrior-web/views/tasks/_form.erb b/lib/taskwarrior-web/views/tasks/_form.erb
+index 789e7a1..fa08698 100644
+--- a/lib/taskwarrior-web/views/tasks/_form.erb
++++ b/lib/taskwarrior-web/views/tasks/_form.erb
+@@ -1,14 +1,14 @@
+ <div class="control-group">
+   <label for="task-description" class="control-label">Description</label>
+   <div class="controls">
+-    <input type="text" required="required" id="task-description" name="task[description]" value="<%= @task.description unless @task.nil? %>" />
++    <input type="text" required="required" id="task-description" name="task[description]" value="<%=h @task.description unless @task.nil? %>" />
+   </div>
+ </div>
+ <div class="control-group">
+   <label for="task-project" class="control-label">Project</label>
+   <div class="controls">
+-    <input type="text" id="task-project" name="task[project]" value="<%= @task.project unless @task.nil? %>" autocomplete="off" />
++    <input type="text" id="task-project" name="task[project]" value="<%=h @task.project unless @task.nil? %>" autocomplete="off" />
+   </div>
+ </div>
+@@ -45,7 +45,7 @@
+ <div class="control-group">
+   <label for="task-tags" class="control-label">Tags</label>
+   <div class="controls">
+-    <input type="text" id="task-tags" name="task[tags]" value="<%= @task.tags.join(', ') unless @task.nil? %>" autocomplete="off" />
++    <input type="text" id="task-tags" name="task[tags]" value="<%=h @task.tags.join(', ') unless @task.nil? %>" autocomplete="off" />
+     <span class="help-block">Enter tags separated by commas or spaces (e.g. <em>each, word will,be a tag</em>)</span>
+   </div>
+ </div>
diff --git a/nixops/modules/task/gemset.nix b/nixops/modules/task/gemset.nix
new file mode 100644 (file)
index 0000000..35d13c6
--- /dev/null
@@ -0,0 +1,567 @@
+{
+  activesupport = {
+    dependencies = ["i18n" "multi_json"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0fyxqkkws4px4lzkbcqzp0bwai7nn7jk4p0bgfy0dny9cwm0qc9r";
+      type = "gem";
+    };
+    version = "3.2.22.5";
+  };
+  blockenspiel = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1h701s45n5qprvcpc7fnr45n88p56x07pznkxqnhz1dbdbhb7xx8";
+      type = "gem";
+    };
+    version = "0.5.0";
+  };
+  coderay = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "15vav4bhcc2x3jmi3izb11l4d9f3xv8hp2fszb7iqmpsccv1pz4y";
+      type = "gem";
+    };
+    version = "1.1.2";
+  };
+  concurrent-ruby = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1ixcx9pfissxrga53jbdpza85qd5f6b5nq1sfqa9rnfq82qnlbp1";
+      type = "gem";
+    };
+    version = "1.1.4";
+  };
+  daemons = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0l5gai3vd4g7aqff0k1mp41j9zcsvm2rbwmqn115a325k9r7pf4w";
+      type = "gem";
+    };
+    version = "1.3.1";
+  };
+  diff-lcs = {
+    groups = ["default" "development" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "18w22bjz424gzafv6nzv98h0aqkwz3d9xhm7cbr1wfbyas8zayza";
+      type = "gem";
+    };
+    version = "1.3";
+  };
+  docile = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "04d2izkna3ahfn6fwq4xrcafa715d3bbqczxm16fq40fqy87xn17";
+      type = "gem";
+    };
+    version = "1.3.1";
+  };
+  eventmachine = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0wh9aqb0skz80fhfn66lbpr4f86ya2z5rx6gm5xlfhd05bj1ch4r";
+      type = "gem";
+    };
+    version = "1.2.7";
+  };
+  ffi = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0j8pzj8raxbir5w5k6s7a042sb5k02pg0f8s4na1r5lan901j00p";
+      type = "gem";
+    };
+    version = "1.10.0";
+  };
+  formatador = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1gc26phrwlmlqrmz4bagq1wd5b7g64avpx0ghxr9xdxcvmlii0l0";
+      type = "gem";
+    };
+    version = "0.2.5";
+  };
+  growl = {
+    groups = ["local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0s0y7maljnalpbv2q1j5j5hvb4wcc31y9af0n7x1q2l0fzxgc9n9";
+      type = "gem";
+    };
+    version = "1.0.3";
+  };
+  guard = {
+    dependencies = ["formatador" "listen" "lumberjack" "nenv" "notiffany" "pry" "shellany" "thor"];
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0h84ja6qvii3hx86w9l4vjpbgl4m8ma8fbawwp7s8l791cgkdcmk";
+      type = "gem";
+    };
+    version = "2.15.0";
+  };
+  guard-bundler = {
+    dependencies = ["guard" "guard-compat"];
+    groups = ["local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0lji8f8w7y4prmpr2lqmlljvkqgkgnlsiwqgwvq7b1y3sxlsvy62";
+      type = "gem";
+    };
+    version = "2.2.1";
+  };
+  guard-compat = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1zj6sr1k8w59mmi27rsii0v8xyy2rnsi09nqvwpgj1q10yq1mlis";
+      type = "gem";
+    };
+    version = "1.2.1";
+  };
+  guard-rspec = {
+    dependencies = ["guard" "guard-compat" "rspec"];
+    groups = ["local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1jkm5xp90gm4c5s51pmf92i9hc10gslwwic6mvk72g0yplya0yx4";
+      type = "gem";
+    };
+    version = "4.7.3";
+  };
+  i18n = {
+    dependencies = ["concurrent-ruby"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "038qvz7kd3cfxk8bvagqhakx68pfbnmghpdkx7573wbf0maqp9a3";
+      type = "gem";
+    };
+    version = "0.9.5";
+  };
+  json = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0qmj7fypgb9vag723w1a49qihxrcf5shzars106ynw2zk352gbv5";
+      type = "gem";
+    };
+    version = "1.8.6";
+  };
+  listen = {
+    dependencies = ["rb-fsevent" "rb-inotify" "ruby_dep"];
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "01v5mrnfqm6sgm8xn2v5swxsn1wlmq7rzh2i48d4jzjsc7qvb6mx";
+      type = "gem";
+    };
+    version = "3.1.5";
+  };
+  lumberjack = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "06im7gcg42x77yhz2w5da2ly9xz0n0c36y5ks7xs53v0l9g0vf5n";
+      type = "gem";
+    };
+    version = "1.0.13";
+  };
+  method_source = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1pviwzvdqd90gn6y7illcdd9adapw8fczml933p5vl739dkvl3lq";
+      type = "gem";
+    };
+    version = "0.9.2";
+  };
+  mini_portile2 = {
+    groups = ["default" "development"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "15zplpfw3knqifj9bpf604rb3wc1vhq6363pd6lvhayng8wql5vy";
+      type = "gem";
+    };
+    version = "2.4.0";
+  };
+  multi_json = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1rl0qy4inf1mp8mybfk56dfga0mvx97zwpmq5xmiwl5r770171nv";
+      type = "gem";
+    };
+    version = "1.13.1";
+  };
+  mustermann = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0lycgkmnyy0bf29nnd2zql5a6pcf8sp69g9v4xw0gcfcxgpwp7i1";
+      type = "gem";
+    };
+    version = "1.0.3";
+  };
+  nenv = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0r97jzknll9bhd8yyg2bngnnkj8rjhal667n7d32h8h7ny7nvpnr";
+      type = "gem";
+    };
+    version = "0.3.0";
+  };
+  nokogiri = {
+    dependencies = ["mini_portile2"];
+    groups = ["default" "development"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "09zll7c6j7xr6wyvh5mm5ncj6pkryp70ybcsxdbw1nyphx5dh184";
+      type = "gem";
+    };
+    version = "1.10.1";
+  };
+  notiffany = {
+    dependencies = ["nenv" "shellany"];
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0x838fa5il0dd9zbm3lxkpbfxcf5fxv9556mayc2mxsdl5ghv8nx";
+      type = "gem";
+    };
+    version = "0.1.1";
+  };
+  parseconfig = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0br2g9k6zc4ygah52aa8cwvpnnkszia29bnvnr8bhpk3rdzi2vmq";
+      type = "gem";
+    };
+    version = "1.0.8";
+  };
+  pry = {
+    dependencies = ["coderay" "method_source"];
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "00rm71x0r1jdycwbs83lf9l6p494m99asakbvqxh8rz7zwnlzg69";
+      type = "gem";
+    };
+    version = "0.12.2";
+  };
+  rack = {
+    groups = ["default" "development"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1pcgv8dv4vkaczzlix8q3j68capwhk420cddzijwqgi2qb4lm1zm";
+      type = "gem";
+    };
+    version = "2.0.6";
+  };
+  rack-flash3 = {
+    dependencies = ["rack"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0rim9afrns6s8zc4apiymncysyvijpdg18k57kdpz66p55jf4mqz";
+      type = "gem";
+    };
+    version = "1.0.5";
+  };
+  rack-protection = {
+    dependencies = ["rack"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "15167q25rmxipqwi6hjqj3i1byi9iwl3xq9b7mdar7qiz39pmjsk";
+      type = "gem";
+    };
+    version = "2.0.5";
+  };
+  rack-test = {
+    dependencies = ["rack"];
+    groups = ["development"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0rh8h376mx71ci5yklnpqqn118z3bl67nnv5k801qaqn1zs62h8m";
+      type = "gem";
+    };
+    version = "1.1.0";
+  };
+  rake = {
+    groups = ["development"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0jcabbgnjc788chx31sihc5pgbqnlc1c75wakmqlbjdm8jns2m9b";
+      type = "gem";
+    };
+    version = "10.5.0";
+  };
+  rb-fsevent = {
+    groups = ["local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1lm1k7wpz69jx7jrc92w3ggczkjyjbfziq5mg62vjnxmzs383xx8";
+      type = "gem";
+    };
+    version = "0.10.3";
+  };
+  rb-inotify = {
+    dependencies = ["ffi"];
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1fs7hxm9g6ywv2yih83b879klhc4fs8i0p9166z795qmd77dk0a4";
+      type = "gem";
+    };
+    version = "0.10.0";
+  };
+  rinku = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1smkk299v18brk98gqbdnqrfwm3143kikl30scidqb5j3pzlbz91";
+      type = "gem";
+    };
+    version = "2.0.5";
+  };
+  rspec = {
+    dependencies = ["rspec-core" "rspec-expectations" "rspec-mocks"];
+    groups = ["development" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "14q3hxvngk4ks8h41yw50d5fqbf2dhzwi9rz5ccxvh5a53ak2as3";
+      type = "gem";
+    };
+    version = "2.99.0";
+  };
+  rspec-core = {
+    groups = ["default" "development" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1wwz21lcz2lwd2jcp2pvq7n1677v23acf7wxsyszp8msb47mw38i";
+      type = "gem";
+    };
+    version = "2.99.2";
+  };
+  rspec-expectations = {
+    dependencies = ["diff-lcs"];
+    groups = ["default" "development" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "11a5pph3anp4xr591dnlcy8xfkdf54qi2lvg4ykpqhxk37si1py3";
+      type = "gem";
+    };
+    version = "2.99.2";
+  };
+  rspec-html-matchers = {
+    dependencies = ["nokogiri" "rspec"];
+    groups = ["development"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "148grzvk0cvh24avhl0shjzz7ldhj138svf48pc5h1fdsb0pnqcv";
+      type = "gem";
+    };
+    version = "0.5.0";
+  };
+  rspec-mocks = {
+    groups = ["default" "development" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0bzhqahbiswq41nqi6y2dka1k42n0hl14jb6bldb206zp4hikz8r";
+      type = "gem";
+    };
+    version = "2.99.4";
+  };
+  ruby_dep = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1c1bkl97i9mkcvkn1jks346ksnvnnp84cs22gwl0vd7radybrgy5";
+      type = "gem";
+    };
+    version = "1.5.0";
+  };
+  shellany = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1ryyzrj1kxmnpdzhlv4ys3dnl2r5r3d2rs2jwzbnd1v96a8pl4hf";
+      type = "gem";
+    };
+    version = "0.0.1";
+  };
+  simple-navigation = {
+    dependencies = ["activesupport"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "08a2s18an3br3xj5j86r33q0hrkai0y157xg67h1khdskb08yylk";
+      type = "gem";
+    };
+    version = "4.0.5";
+  };
+  simplecov = {
+    dependencies = ["docile" "json" "simplecov-html"];
+    groups = ["local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1sfyfgf7zrp2n42v7rswkqgk3bbwk1bnsphm24y7laxv3f8z0947";
+      type = "gem";
+    };
+    version = "0.16.1";
+  };
+  simplecov-html = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1lihraa4rgxk8wbfl77fy9sf0ypk31iivly8vl3w04srd7i0clzn";
+      type = "gem";
+    };
+    version = "0.10.2";
+  };
+  sinatra = {
+    dependencies = ["mustermann" "rack" "rack-protection" "tilt"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1gasgn5f15myv08k10i16p326pchxjsy37pgqfw0xm66kcc5d7ry";
+      type = "gem";
+    };
+    version = "2.0.5";
+  };
+  sinatra-simple-navigation = {
+    dependencies = ["simple-navigation" "sinatra"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1gishxd23qw6bwsk7fkagkfc7ihqyvvvb98j9bmwc6wwpsfs4prs";
+      type = "gem";
+    };
+    version = "4.1.0";
+  };
+  taskwarrior-web = {
+    dependencies = ["activesupport" "json" "parseconfig" "rack-flash3" "rinku" "sinatra" "sinatra-simple-navigation" "vegas" "versionomy"];
+    groups = ["default"];
+    platforms = [];
+    bundledByPath = true;
+    path = ./.;
+    source = {
+      path = ./.;
+      type = "path";
+    };
+    version = "1.1.12";
+  };
+  thin = {
+    dependencies = ["daemons" "eventmachine" "rack"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0nagbf9pwy1vg09k6j4xqhbjjzrg5dwzvkn4ffvlj76fsn6vv61f";
+      type = "gem";
+    };
+    version = "1.7.2";
+  };
+  thor = {
+    groups = ["default" "local"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "1yhrnp9x8qcy5vc7g438amd5j9sw83ih7c30dr6g6slgw9zj3g29";
+      type = "gem";
+    };
+    version = "0.20.3";
+  };
+  tilt = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0ca4k0clwf0rkvy7726x4nxpjxkpv67w043i39saxgldxd97zmwz";
+      type = "gem";
+    };
+    version = "2.0.9";
+  };
+  vegas = {
+    dependencies = ["rack"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0kzv0v1zb8vvm188q4pqwahb6468bmiamn6wpsbiq6r5i69s1bs5";
+      type = "gem";
+    };
+    version = "0.1.11";
+  };
+  versionomy = {
+    dependencies = ["blockenspiel"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["http://rubygems.org"];
+      sha256 = "0i0l4pzrl1vyp4lpg2cxhgkk56spki3lld943d6h7168fj8qyv33";
+      type = "gem";
+    };
+    version = "0.5.0";
+  };
+}
diff --git a/nixops/modules/task/taskwarrior-web.json b/nixops/modules/task/taskwarrior-web.json
new file mode 100644 (file)
index 0000000..70f396d
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "tag": "a79cfe2-master",
+  "meta": {
+    "name": "taskwarrior-web",
+    "url": "https://github.com/theunraveler/taskwarrior-web",
+    "branch": "master"
+  },
+  "github": {
+    "owner": "theunraveler",
+    "repo": "taskwarrior-web",
+    "rev": "a79cfe2b42791b62364118e58b21b892fff6ded8",
+    "sha256": "028rb4wry2an19707bvy4n305f3s0hipg214224p1m0mb61c3cq4",
+    "fetchSubmodules": true
+  }
+}
diff --git a/nixops/modules/task/taskwarrior-web.nix b/nixops/modules/task/taskwarrior-web.nix
new file mode 100644 (file)
index 0000000..49d6946
--- /dev/null
@@ -0,0 +1,24 @@
+{ env, ruby_2_5, bundlerEnv, defaultGemConfig, fetchedGithub, stdenv, writeText, pkgs }:
+let
+  varDir = "/var/lib/taskwarrior-web";
+  socketsDir = "/run/taskwarrior-web";
+  rubyRoot = stdenv.mkDerivation (fetchedGithub ./taskwarrior-web.json // rec {
+    phases = [ "unpackPhase" "patchPhase" "installPhase" ];
+    patches = [ ./fixes.patch ./thin.patch ];
+    installPhase = ''
+      cp -a . $out
+      cp ${./Gemfile.lock} $out/Gemfile.lock
+      '';
+  });
+  gems = bundlerEnv {
+    name = "taskwarrior-web-env";
+    ruby = ruby_2_5;
+    pname = "taskwarrior-web";
+    gemset = ./gemset.nix;
+    gemdir = rubyRoot.out;
+    groups = [ "default" "local" "development" ];
+  };
+in
+  {
+    inherit gems varDir socketsDir rubyRoot;
+  }
diff --git a/nixops/modules/task/thin.patch b/nixops/modules/task/thin.patch
new file mode 100644 (file)
index 0000000..a7df3e3
--- /dev/null
@@ -0,0 +1,23 @@
+diff --git a/Gemfile b/Gemfile
+index 8544e15..9c4279c 100644
+--- a/Gemfile
++++ b/Gemfile
+@@ -3,6 +3,8 @@ source "http://rubygems.org"
+ # Specify your gem's dependencies in taskwarrior-web.gemspec
+ gemspec
++gem 'thin'
++
+ group :local do
+   gem 'rb-fsevent', :require => false
+   gem 'growl', :require => false
+diff --git a/config.ru b/config.ru
+index c3050c6..52387d8 100644
+--- a/config.ru
++++ b/config.ru
+@@ -1,5 +1,4 @@
+ require File.join(File.dirname(__FILE__), 'lib', 'taskwarrior-web')
+-disable :run
+ TaskwarriorWeb::App.set({ :environment => :production })
+ run TaskwarriorWeb::App